XLS report in ROR - ruby-on-rails

I want to generate a Excel and PDF report using ruby on rails , but I have 24,278 entries to display in both reports so it is showing an error . How to solve this error .
Controller Code - (for excel)
def from_date_wise_xls
#start = params[:day]
#end = params[:to_date]
#present = params[:present]
#employee_attendances = EmployeeAttendance.where(present: #present,day: #start..#end)
respond_to do |format|
format.xls {render template: 'employee_attendances/from_date_wise_xls.xls.erb'}
end
end
(For PDF) -
def from_date_wise_pdf
#start = params[:day]
#end = params[:to_date]
#present = params[:present]
#employee_attendances = EmployeeAttendance.where(present: #present,day: #start..#end)
respond_to do |format|
format.html
format.pdf do
render :pdf => 'from_date_wise_pdf',
layout: '/layouts/pdf.html.erb',
:template => 'employee_attendances/from_date_wise_pdf.pdf.erb',
:orientation => 'Landscape', # default , Landscape
:page_height => 1000,
:dpi => '300',
:margin => {:top => 10, # default 10 (mm)
:bottom => 10,
:left => 20,
:right => 20},
:show_as_html => params[:debug].present?
end
end
end

Related

Rails gem axlsx gem multiple sheet

Is it possible to export in xlsx with multiple sheet using gem axlsx. If not please suggest me the other ways.
I want to export the values of #search, #searchg, #clients, #overtimes in different sheets.
#search = Operation.where(:date => params[:from_date].to_datetime..(params[:to_date].to_datetime + 1.day))
#searchg = Guard.where(:status => true).where(:induction_date => params[:from_date].to_datetime..(params[:to_date].to_datetime + 1.day))
#searcht = Guard.where(:status => false).where(:terminate_date => params[:from_date].to_datetime..(params[:to_date].to_datetime + 1.day))
#clients = Client.where(:status => false).where(:terminate_date => params[:from_date].to_datetime..(params[:to_date].to_datetime + 1.day))
#overtimes = Overtime.where(:date => params[:from_date].to_datetime..(params[:to_date].to_datetime + 1.day))
respond_to do |format|
format.js
format.xlsx {render xlsx: 'report', filename: "Operation_reports_#{params[:from_date]}_#{params[:to_date]}.xlsx"}
end
Yep, axlsx supports multiple sheets. In fact, you can see it in the very first code snippet in its readme:
Axlsx::Package.new do |p|
p.workbook.add_worksheet(:name => "Pie Chart") do |sheet|
sheet.add_row ["Simple Pie Chart"]
%w(first second third).each { |label| sheet.add_row [label, rand(24)+1] }
sheet.add_chart(Axlsx::Pie3DChart, :start_at => [0,5], :end_at => [10, 20], :title => "example 3: Pie Chart") do |chart|
chart.add_series :data => sheet["B2:B4"], :labels => sheet["A2:A4"], :colors => ['FF0000', '00FF00', '0000FF']
end
end
p.serialize('simple.xlsx')
end

How to call same method in Ruby On Rails

I want to make PDF and Excel report code using same method and want to pass parameters only one time ,but it is showing an error
NoMethodError (undefined method `[]' for nil:NilClass) .
Controller Code -
def print_application_report
#from = params[:salary][:from_date]
#to = params[:salary][:to_date]
#company = params[:employee][:company_id]
#company_location = params[:employee][:company_location_id]
#department = params[:employee][:department_id]
#employees = Employee.where(company_id: #company.to_i,company_location_id: #company_location.to_i,department_id: #department).pluck(:id)
#travel_requests = TravelRequest.where(application_date: #from.to_date..#to.to_date,employee_id: #employees)
respond_to do |format|
format.js
format.xls {render template: 'travel_requests/application_datewise_report_xls.xls.erb'}
format.html
format.pdf do
render pdf: 'application_datewise_report_pdf',
layout: 'pdf.html',
orientation: 'Landscape',
template: 'travel_requests/application_datewise_report_pdf.pdf.erb',
# show_as_html: params[:debug].present?,
:page_height => 1000,
:dpi => '300',
:margin => {:top => 10, # default 10 (mm)
:bottom => 10,
:left => 20,
:right => 20},
:show_as_html => params[:debug].present?
end
end
end
you can check the condition in your controller with the terniary operator and check for the presence of params[:salary] and params[:employee],
Controller:
def print_application_report
#from = params[:salary] ? params[:salary][:from_date] : params[:from_date]
#to = params[:salary] ? params[:salary][:to_date] : params[:to_date]
#company = params[:employee] ? params[:employee][:company_id] : params[:company_id]
#company_location = params[:employee] ? params[:employee][:company_location_id] : params[:company_location_id]
#department = params[:employee] ? params[:employee][:department_id] : params[:department_id]
#employees = Employee.where(company_id: #company.to_i,company_location_id: #company_location.to_i,department_id: #department).pluck(:id)
#travel_requests = TravelRequest.where(application_date: #from.to_date..#to.to_date,employee_id: #employees)
respond_to do |format|
format.js
format.xls {render template: 'travel_requests/application_datewise_report_xls.xls.erb'}
format.html
format.pdf do
render pdf: 'application_datewise_report_pdf',
layout: 'pdf.html',
orientation: 'Landscape',
template: 'travel_requests/application_datewise_report_pdf.pdf.erb',
# show_as_html: params[:debug].present?,
:page_height => 1000,
:dpi => '300',
:margin => {:top => 10, # default 10 (mm)
:bottom => 10,
:left => 20,
:right => 20},
:show_as_html => params[:debug].present?
end
end
end
Explanation:
The change I made is,
#from = params[:salary]
to,
#from = params[:salary] ? params[:salary][:from_date] : params[:from_date]
the meaning of params[:salary] ? params[:salary][:from_date] : params[:from_date] is,
if there is params[:salary], then take params[:salary][:from_date], if params[:salary] is not there, then take params[:from_date]
This line can also be written as,
if params[:salary]
#from = params[:salary][:from_date]
else
#from = params[:from_date]
end
Without the actual line of error, I am making a guess here.
you are sending params as:
print_application_report_travel_requests_path(from_date: params[:salary][:from_date],to_date: params[:salary][:to_date], company_id: params[:employee][:company_id]....
Here value of param from_date is params[:salary][:from_date] but you are sending the param directly, not enclosed in salary key. so all of your params will be available directly inside params hash.
you can try changing like this.
#from = params[:from_date]
#to = params[:to_date]
#company = params[:company_id]
#company_location = params[:company_location_id]
#department = params[:department_id]

download report/invoice as pdf format using payday gemfile in rails

My code is
class pdfController < ApplicationController
def index
#posts = Post.all
respond_to do |format|
format.html
format.pdf do
pdf = Prawn::Document.new
send_data pdf.render, :filename => "report.pdf", :type => "application/pdf", :disposition => "inline"
end
end
end
end
Now the problem is, pdf file downloaded. But I am getting an empty file, there is no report in that downloaded file.
And the code#posts = Post.all Post is a table, I can get the data from Post to downloaded pdf file.
As suggested in this post: http://adamalbrecht.com/2014/01/14/generate-clean-testable-pdf-reports-in-rails-with-prawn/
You can inherit Prawn::Document class for an easy configuration of your table border color and font size.
class PdfReport < Prawn::Document
# Often-Used Constants
TABLE_ROW_COLORS = ["FFFFFF","DDDDDD"]
TABLE_FONT_SIZE = 9
TABLE_BORDER_STYLE = :grid
def initialize(default_prawn_options={})
super(default_prawn_options)
font_size 10
end
def header(title=nil)
text "My Awesome Invoice!!", size: 18, style: :bold, align: :center
if title
text title, size: 14, style: :bold_italic, align: :center
end
end
def footer
# ...
end
# ... More helpers
end
Now, the above class is handful to use at other places by inheriting to some other classes, for example: posts reports, comments reports, etc. For your posts reports, We could do something like this:
class PostSummaryReportPdf < PdfReport
TABLE_WIDTHS = [20, 100, 30, 60]
TABLE_HEADERS = ["ID", "Name", "Date", "User"]
def initialize(posts=[])
super()
#posts = posts
header "Posts' Invoice Summary Report"
display_event_table
footer
end
private
def display_event_table
if table_data.empty?
text "No Events Found"
else
table table_data,
headers: TABLE_HEADERS,
column_widths: TABLE_WIDTHS,
row_colors: TABLE_ROW_COLORS,
font_size: TABLE_FONT_SIZE
end
end
def table_data
#table_data ||= #posts.map { |p| [p.id, p.name, p.created_at.strftime("%m/%d/%y"), p.created_by.try(:full_name)] }
end
end
Everything is now up and ready to be used in DRY-way within your controllers.
class pdfController < ApplicationController
def index
#posts = Post.all
respond_to do |format|
format.html
format.pdf do
pdf = PostSummaryReportPdf.new(#posts)
send_data pdf.render, :filename => "report.pdf", :type => "application/pdf", :disposition => "inline"
end
end
end
end
I hope that helps.

Rails: passing objects between model and controller

Is there a best practice for managing objects between model and controller in Rails? I've got code that clumsily hands files from controller to model and back like so:
In document_controller.rb:
tmp_file = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
pdf = Prawn::Document.generate(tmp_file.path(), :page_size => "A4", :skip_page_creation => true) do |posting|
#document.process(posting)
send_data posting.render, :filename => "#{#document.id}", :type => "application/pdf"
end
And in document.rb:
def process(pdf)
pdf.start_new_page
pdf.image self.user.logo.path, :width => pdf.bounds.width
pdf.fill_color '#444444'
pdf.image self.component.image_snapshot.path, :width => pdf.bounds.width
pdf.move_down 20
pdf.indent 20 do
pdf.text self.component.name, :size => 24
end
box_start = pdf.cursor
pdf.reflow_column_box([20, box_start-15], :width => pdf.bounds.width-20) do
format_for_prawn(pdf, self.component.body, self.user.colour1)
end
end
How can I rewrite this code so I don't have to clumsily pass the pdf/posting object around? It feels very un-Rails-like.
Thanks for your input.
EDIT: worse example.
Again in the controller:
tempfile = Magick::Image.read(#document.component.image_newsletter.path).first
overlay = Magick::Image.read(#document.user.logo.path).first.resize_to_fit(tempfile.columns)
rec = Magick::Draw.new
rec.stroke = "##{#document.user.colour1}"
rec.fill = "##{#document.user.colour1}"
rec.rectangle 0, 0, tempfile.columns, 5
lank = tempfile.extent(tempfile.columns, tempfile.rows+overlay.rows, 0 ,0)
final = lank.composite(overlay, Magick::SouthGravity, 0, 0, Magick::OverCompositeOp)
rec.draw(final)
send_data final.to_blob, :filename => "#{#document.user.name}-#{#document.id}.jpg", :type => "image/jpg"
This is in the controller because otherwise send_data can't access final. Any help?
Thanks again.
EDIT
You can create extension methods for inbuilt Class by
#lib/ext/prawn/document.rb
class Prawn::Document
def process(pdf)
pdf = self if pdf.blank?
pdf.start_new_page
pdf.image self.user.logo.path, :width => pdf.bounds.width
pdf.fill_color '#444444'
pdf.image self.component.image_snapshot.path, :width => pdf.bounds.width
pdf.move_down 20
pdf.indent 20 do
pdf.text self.component.name, :size => 24
end
box_start = pdf.cursor
pdf.reflow_column_box([20, box_start-15], :width => pdf.bounds.width-20) do
format_for_prawn(pdf, self.component.body, self.user.colour1)
end
end
end
And in your controller
tmp_file = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
pdf = Prawn::Document.generate(tmp_file.path(), :page_size => "A4", :skip_page_creation => true) do |posting|
posting.process
send_data posting.render, :filename => "#{#document.id}", :type => "application/pdf"
end
To load this class, you will need to require it in your config/application.rb file or in an initializer.
require 'ext/prawn/document'
To get that messy controller code into the model I've done this:
def process_media
temp = "#{::Rails.root}/public/temporary/#{temp_file_name}"
tempfile = Magick::Image.read(self.component.image_newsletter.path).first
overlay = Magick::Image.read(self.user.logo.path).first.resize_to_fit(tempfile.columns)
rec = Magick::Draw.new
rec.stroke = "##{self.user.colour1}"
rec.fill = "##{self.user.colour1}"
rec.rectangle 0, 0, tempfile.columns, 5
lank = tempfile.extent(tempfile.columns, tempfile.rows+overlay.rows, 0 ,0)
final = lank.composite(overlay, Magick::SouthGravity, 0, 0, Magick::OverCompositeOp)
rec.draw(final)
final.to_blob
final.write("#{temp}.jpg")
end
And I'm sending the actual file with send_file from the controller. Hope that helps someone.

How I can dynamically generate url ( for generating xls report )?

Hello!
I have this trouble: I'm searching reports by date and in html view everything is alright, BUT when I'm rendering xls view error appear, because it didn't receive params, so I need to pass them in URL for xls link_to generator.
My controller:
def show
#website = Website.find(params[:id])
if params[:report] && params[:report][:start_date] && params[:report][:end_date]
#search_by_created_at
#performance_reports = #website.performance_reports.where("created_at between ? and ?", params[:report][:start_date].to_date, params[:report][:end_date].to_date)
else
#performance_reports = #website.performance_reports
end
respond_to do |format|
format.html # index.html.erb
format.xls
format.xml { render :xml => #performance_reports }
end
end
and my generated url looks like:
http://127.0.0.1:3000/websites/25/performance_reports/show?utf8=%E2%9C%93&report[end_date]=07%2F09%2F2012&report[start_date]=04%2F09%2F2012&commit=Run+Report
mine xls url is generated like this:
<%= link_to url_for(:format => 'xls') do%>
<%= image_tag("excel.png", :id => "analytics",:size => '21x23')%> <b>Export</b>
<% end %>
result:
http://127.0.0.1:3000/websites/25/performance_reports/show
Any help will be appreciated.
xls in not available by default.
Add this:
gem "spreadsheet"
gem "to_xls", :git => "https://github.com/dblock/to_xls.git", :branch => "to-xls-on-models"
Register the Excel MIME type in config/initializers/mime_types.rb by adding this:
Mime::Type.register "application/vnd.ms-excel", :xls
Add an as_xls method to model that you want to export for the fields you want.
For example for a User model you might have:
def as_xls(options = {})
{
"Id" => id.to_s,
"Name" => name,
"E-Mail" => email,
"Joined" => created_at,
"Last Signed In" => last_sign_in_at,
"Sign In Count" => sign_in_count
}
end
Add code to the controller:
def index
#users = User.all
respond_to do |format|
format.html
format.xls { send_data #users.to_xls, content_type: 'application/vnd.ms-excel', filename: 'users.xls' }
end
end
Provide a link:
= link_to 'Export', users_path(request.parameters.merge({:format => :xls}))
All code should have a test. You could do something like this:
describe "GET index.xls" do
it "creates an Excel spreadsheet with all users" do
user = Fabricate :user
get :index, :format => :xls
response.headers['Content-Type'].should == "application/vnd.ms-excel"
s = Spreadsheet.open(StringIO.new(response.body))
s.worksheets.count.should == 1
w = s.worksheet(0)
w.should_not be_nil
w.row(0)[0].should == "Id"
w.row(1)[0].should == user.id.to_s
w.row(0)[1].should == "Name"
w.row(1)[1].should == user.name
end
end

Resources