I found a great railscast that shows how to generate XLS without a gem, but can find precious little documentation that goes more in depth.
http://railscasts.com/episodes/362-exporting-csv-and-excel?view=asciicast
I've been asked to create a metrics list that is direct-to-email in the form of an XLS spreadsheet, but have been told that the slug is too large to add a gem.
The rake task:
#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
The task calls the notifications mailer:
#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
The mailer calls to a method in the user model:
#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
And, of course, all of this renders the mailer template:
<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>
I believe the tricky part will be that the necessary info is pooled together from various includes and joins queries, all initiated separately from the user model, and combined via zip()
- so I don't immediately see a way to just say @variable.to_xls
. Also, the e-mail is generated via rake task, so I haven't been able to figure out how/where/when to generate the XLS for the attachment.
The mailer successfully generates a list visible in the e-mail. How do I take that list generated by the mailer and turn it into an XLS attachment for the same e-mail, again, without using a gem?
Ruby 1.8.7,
Rails 3.0.20