导出到XLS作为没有gem的电子邮件附件

时间:2014-04-18 16:46:55

标签: ruby-on-rails ruby xls

我发现了一个很棒的railscast,它展示了如何在没有gem的情况下生成XLS,但是可以找到更深入的珍贵文档。 http://railscasts.com/episodes/362-exporting-csv-and-excel?view=asciicast

我被要求创建一个以XLS电子表格形式直接发送到电子邮件的指标列表,但是被告知slug太大而无法添加gem。

佣金任务:

#scheduler.rake
namespace :scheduler do
...
desc "Email client metrics - 100pm every Sunday"
task :email_client_metrics => :environment do
  # run every Sunday
  Notifications.send_metrics_email('name1@company.com').deliver
  Notifications.send_metrics_email('name2@company.com').deliver
end

该任务调用通知邮件程序:

#app/mailers/notifications.rb
class Notifications < ActionMailer::Base
...
def send_metrics_email(email_addr)
  @email = email_addr
  #rather than splitting on @, we are looking for a specific name to avoid mistakes.
  if @email =~ /name1/
    login = "name1"
  elsif @email =~ /name2/
    login = "name2"
  end
  @user = User.find_by_login(login)
  @factor_clients,@active_count,@update_count,@closed_per_month,@incorrect = @user.get_factors_and_counts_for_metrics

  mail(:to => @email, :subject => "Weekly Client Metrics")
end

邮件程序调用用户模型中的方法:

#user.rb
class User < ActiveRecord::Base
...
def get_factors_and_counts_for_metrics()
  ret_factors,ret_actives,ret_updates,ret_cls_per_mo,ret_incorrect = [],[],[],[],[]
  factors.includes(:factor_clients).where(:status => "a").find_each { |f| ret_factors << f }

  ret_factors = ret_factors.sort_by { |f| f.company }

  ret_factors.each do |f|
    ret_actives << f.factor_clients.where(:status => ['a','u']).count
    ret_updates << f.factor_clients.where("status IN (?) AND (reason_8821 NOT IN (?) OR reason_8821 is ?)",['a'],'current',nil).count
    ret_cls_per_mo << f.factor_clients.joins(:client_status_changes).where("factor_clients.status IN (?) AND client_status_changes.change_type IN (?) AND client_status_changes.change_time BETWEEN (?) AND (?)", ['c'],['a2c','r2c','ia2c','e2c','u2c','o2c'],Time.now.beginning_of_month,Time.now.end_of_month).count
    ret_incorrect << f.factor_clients.where(:status => 'e').count
  end

  return [ret_factors,ret_actives,ret_updates,ret_cls_per_mo,ret_incorrect]
end

当然,所有这些都会呈现邮件模板:

<div>
  Hello <%= @user.first_name %>,<br />
  <p>
    Here is the breakdown of your client metrics.
  </p>
</div>

<p>
<div>
  <table style="font-size: 12; margin-right: auto; margin-left: auto; width: 90%;"border=1 cellpadding=2 cellspacing=0>
    <tr>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">Account Name</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">Active</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">Need Updated 8821</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">% Outstanding</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">Closed This Month</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">Total Incorrect</th>
    </tr>
  <% shade = "dark" %>
  <% @factor_clients.zip(@active_count,@update_count,@closed_per_month,@incorrect).each do |f,a,u,c,i| %>
    <% if shade == "background: #dddddd;"
         shade = "background: #ffffff;"
       else
         shade = "background: #dddddd;"
       end %>

    <tr style="<%= shade %>">
    <% if f %>
      <td><%= f.company %></td>
      <td><%= a %></td>
      <td><%= u %></td>
      <td><% if a == 0 || u == 0 then %><%= 0 %><% else %><%= ((u.to_f)/(a.to_f) * 100).to_i %><% end %> %</td>
      <td><%= c %></td>
      <td><%= i %></td>
    <% end %>
    </tr>
  <% end %>
    <% if @active_count.any? && @update_count.any? %>
    <tr>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);">SUM</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);"><%= @active_count.sum %></th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);"><%= @update_count.sum %></th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);"><%= (((@update_count.sum.to_f)/(@active_count.sum.to_f)) * 100).to_i %> %</th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);"><%= @closed_per_month.sum %></th>
      <th style="background: #8B8B8B; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E78CC, endColorstr=#0C89E6); background: -webkit-gradient(linear, left top, left bottom, from(#1E78CC), to(#0C89E6)); background: -moz-linear-gradient(top,  #1E78CC,  #0C89E6);"><%= @incorrect.sum %></th>
    </tr>
    <% end %>
  </table>
</div>
</p>

我认为棘手的部分是必要的信息从各种包含和连接查询汇总在一起,所有信息都是从用户模型中单独启动的,并通过zip()合并 - 所以我不会立即看到一种说出@variable.to_xls的方法。此外,电子邮件是通过rake任务生成的,因此我无法弄清楚如何/在何时/何时为附件生成XLS。

邮件程序成功生成电子邮件中可见的列表。如何使用邮件程序生成的列表并将其转换为相同电子邮件的XLS附件,同样不使用gem?

Ruby 1.8.7, Rails 3.0.20

1 个答案:

答案 0 :(得分:0)

通过抨击墙壁并搜索类似问题找到解决方案来找到解决方案。

我在通知邮件中添加了一个块...

#app/mailers/notifications.rb
class Notifications < ActionMailer::Base
...
def send_metrics_email(email_addr)
  @email = email_addr
  #rather than splitting on @, we are looking for a specific name to avoid mistakes.
  if @email =~ /name1/
    login = "name1"
  elsif @email =~ /name2/
    login = "name2"
  end
  @user = User.find_by_login(login)
  @factor_clients,@active_count,@update_count,@closed_per_month,@incorrect = @user.get_factors_and_counts_for_metrics

  mail(:to => @email, :subject => "Weekly Client Metrics") do |format|
    format.html { render }
    format.xls { attachments["#{login}#{Time.now}.xls"] = @user.to_csv(:col_sep => "\t") }
  end
end

通过@user.to_csv自动创建XLS文件作为附件。我添加了to_csv方法,该方法基本上是之前的get_factors_and_counts_for_metrics方法,但已修改。

我必须将代码从示例CSV.generate更改为FCSV.generate,并在config / application.rb中添加一些行

#app/models/user.rb
class User < ActiveRecord::Base
...
def to_csv(options = {})
  FCSV.generate(options) do |csv|
    csv << ["Account Name","Active","Need Updated 8821","% Outstanding","Closed This Month","Total Incorrect"]
    ret_factors = []
    factors.includes(:factor_clients).where(:status => "a").find_each { |f| ret_factors << f }
    ret_factors = ret_factors.sort_by { |f| f.company }
    ret_factors.each do |f|
      f_c = f.company
      a = f.factor_clients.where(:status => ['a','u']).count
      u = f.factor_clients.where("status IN (?) AND (reason_8821 NOT IN (?) OR reason_8821 is ?)",['a'],'current',nil).count
      if a == 0 || u == 0 
        a_u_per = 0
      else
        a_u_per = ((u.to_f)/(a.to_f) * 100).to_i
      end
      c = f.factor_clients.joins(:client_status_changes).where("factor_clients.status IN (?) AND client_status_changes.change_type IN (?) AND client_status_changes.change_time BETWEEN (?) AND (?)", ['c'],['a2c','r2c','ia2c','e2c','u2c','o2c'],Time.now.beginning_of_month,Time.now.end_of_month).count
      e = f.factor_clients.where(:status => 'e').count
      csv << [f_c,a,u,"%#{a_u_per}",c,e]
    end
  end
end

相应的配置改变..

#config/application.rb
require File.expand_path('../boot', __FILE__)

if RUBY_VERSION < "1.9"
  require "rubygems"
  require "faster_csv"
  #CSV = FCSV
else
  require "csv"
end
require 'rails/all'

将mime类型添加到initializers / mime_types.rb:Mime::Type.register "application/xls", :xls

我现在在电子邮件中有XLS附件。