Axlsx Rails. Generate .xlsx file and respond filename as json/html - ruby-on-rails

I am generating xlsx files with axlsx_rails gem.
After collecting user input I am posting data to /basic_report_post.xlsx
Controller action looks like
def basic_report_post
#config = params[:config]
#data = params[:data]
#filename = "#{Rails.root}/public/test.xlsx"
respond_to do |format|
format.xlsx {
render xlsx: 'basic_report_post'
}
end
end
View file for this action basic_report_post.xlsx.axlsx
wb = xlsx_package.workbook
wb.add_worksheet(name: 'Data1') do |s|
# Drawing columns
end
xlsx_package.serialize #filename
My problem is that I am getting response data(in post success action) that is raw .xlsx file.
But I need somehow respond #filename (format json/html) to download it after.

It is possible to use the axlsx_rails template renderer to create a string and save it to file:
def basic_report_post
#config = params[:config]
#data = params[:data]
#filename = "#{Rails.root}/public/test.xlsx"
File.open(#filename, 'w') do |f|
f.write render_to_string(handlers: [:axlsx], formats: [:xlsx], template: 'path/to/template')
end
render json: {name: #filename}
end
Then you can use the template to serve the file directly if need be.

After some experiments with respond_to I move .xlsx generation logic to view helper.
So I have included BasicReportHelper in controller.
basic_report_helper.rb
module BasicReportsHelper
def generate_basic_report(filename)
p = Axlsx::Package.new
wb = p.workbook
wb.add_worksheet(:name => "Basic Worksheet") do |sheet|
# Drawing here
end
p.serialize filename
end
end
Changed post call to /basic_report_post.json and changed action to
def basic_report_post
#config = params[:config]
#data = params[:data]
#filename = "#{Rails.root}/public/test.xlsx"
generate_basic_report(#filename)
respond_to do |format|
format.json {
render json: {name: #filename}
}
end
end

Related

Export xlsx file as a link in a mail using axlsx-rails

I'm using Rails 4.2, sidekiq, rubyzip, axlsx and axlsx-rails
What I need to do:
I need to export file in a mail, but as a link and not as attachment.
I want an email send with a url to the download for it
Problem:
I don't know how to do it with this gem, in the git documentation there's nothing about it and every url I use don't work. I need to know how to create a download link for it and not as attachment to the mail
Code:
controller:
def export
export_args = {account_id: current_account.id}
ExportReportJob.perform_later(export_args)
redirect_to action: :index
end
job:
def perform(export_args)
begin
export_failed = false
account = Admin::Account.find_by(id: export_args[:account_id])
account.connect_to_target_db
#accounts = Admin::Account.active_accounts
file_name = "#{Time.now.strftime("%d_%m_%Y_at_%I_%M%p")}_export.xlsx"
dir_path = "#{Rails.root}/public/exports"
FileUtils.mkdir_p(dir_path) unless File.directory?(dir_path)
file_location = "#{absolute_path}/#{file_name}"
Admin::Mailer.export(account.pi, #accounts, file_location).deliver
rescue => e
export_failed = true
Airbrake.notify(e, environment_name: Rails.env, error_message: "export account failed #{e.message}")
ensure
ActiveRecord::Base.clear_active_connections!
end
end
template:
wb = xlsx_package.workbook
wb.add_worksheet(name: "Accounts") do |sheet|
sheet.add_row ["Account id", "Account name", "Organization"]
accounts.each do |account|
sheet.add_row [account.id, accoount.name, account.organization_id]
end
end
xlsx_package.to_stream.read
mailer:
def export(member, accounts, file_location)
#member = member
#title = "Export"
#header = subject = "Your Export is Ready"
#link = file_location
xlsx = render_to_string(layout: false, handlers: [:xlsx], template: "export.xlsx.axlsx", locals: {accounts: accounts})
# attachment = Base64.encode64(xlsx)
# attachments["export_#{DateTime.now.strftime("%d_%m_%Y_%HH%MM")}.xlsx"] = {mime_type: Mime::XLSX, content: attachment, encoding: 'base64'}
# ^ this is not what I want, I want to get the url for it and insert to #link
mail(:to => member.email, :subject => subject) do |format|
format.html
end
end
mail template:
= render partial: 'header'
= h_tag 'The export you requested has been completed'
= button 'Download the file', #link, { download: true }
= p_tag '(the file download will expire after <b>2</b> days)'
= render partial: 'footer'

Remove pagination from axlsx result set

I am using AXLSX to generate xlsx files from a table displayed on a website when a user performs a search using Ransack. My problem is that the results are paginated on the website however I want the spreadsheet to display the full set of results rather than 15 records as is currently the case.
Controller
def index
#search = Campaign.search(params[:q])
#campaigns = #search.result.page(params[:page]||1)
respond_to do |format|
format.html
format.xlsx{ render xlsx: 'Campaigns' }
end
end
Link
= link_to '<i class="glyphicon glyphicon-cloud-download"></i>'.html_safe, admin_campaigns_path(format: :xlsx), title: "Download Campaign XLS", rel: 'tooltip'
Campaigns.xlsx.axlsx
require 'axlsx'
xlsx_package = Axlsx::Package.new
wb = xlsx_package.workbook
wb.add_worksheet(name: "Campaigns") do |sheet|
style1 = sheet.styles.add_style(:bg_color => "EF0920", :fg_color => "FFFFFF", :b => "true")
sheet.add_row ["Header", "Header", "Header", "Header","Header","Header","Header","Header","Header","Header","Header","Header"], :style => style1
#campaigns.each do |campaign|
sheet.add_row [campaign.data,campaign.data,campaign.data,campaign.data,campaign.data,ccampaign.data,campaign.data,campaign.data,campaign.data,campaign.data,campaign.data]
end
end
I was thinking of either changing the call to campaigns to something else and removing the pagination but not sure how to do this.
Figured it out:
def index
#search = Campaign.search(params[:q])
#campaigns = #search.result
respond_to do |format|
format.html{#campaigns = #search.result.page(params[:page]||1)}
format.xlsx{ render xlsx: 'Campaigns' }
end
end

I want to import all my table data to xml in rails

controller.rb
def xml_print
byebug
#month = params[:month]
#year = params[:year]
#maps = SalaryMapSap.all
#salaryslip = Salaryslip.limit(50)
respond_to do |format|
format.xml { send_data render_to_string(:index), :filename => 'mydoc.xml', :type=>"application/xml", :disposition => 'attachment' }
# #salary_components = SalaryComponent.all
#maps = SalaryMapSap.all
end
end
routes.rb
resources :salaryslip_components do
collection do
post :xml_print
end
end
new.html.erb
<%= bootstrap_form_for(:salaryslip_component, url: { action: 'xml_print',format: 'xml',month: #month,year: #year}) do |f| %>
I want to generate an XML Data in rails,for that i have created one form,in that two drop downs are there where i would select month and year and on bottom there would be submit button,if i click on it then all my data would be downloaded as an XML.I created index XML builder file.But my data is not generating in XML ,error is coming,that action could not be found.I also mentioned this in controller's action.
format.xml { send_data render_to_string(:index), :filename => 'mydoc.xml', :type=>"application/xml", :disposition => 'attachment' }
So please help me.

Rails 3.2 delayed job a pdf email

I have a page where the user can select multiple costprojects to create a single pdf (including S3 attachments). It works fine - except if the user selects quite a few, then app will time out (30 sec).
So, I would like to create the pdf and email it in the background using gem 'delayed_job_active_record'.
This works without delayed job:
def pdfemail
#costprojects = Costproject.find(params[:costproject_ids])
pdf = CombinePDF.new
#costprojects.each do |costproject|
#costproject = costproject
pdf2 = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
costproject.attachments.each do |attachment|
pdf << CombinePDF.parse( Net::HTTP.get( URI.parse( attachment.attach.url ) ) )
end
end
useremail = current_user.email
CostpdfMailer.costpdf_email(useremail,pdf).deliver
redirect_to :back
flash[:notice] = 'An Email containing a PDF has been sent to you!'
end
This was my first try that didn't work - I added:
handle_asynchronously :pdfemail
My second try:
def pdfemail
#costprojects = Costproject.find(params[:costproject_ids])
CostprojectsController.delay.pdfemail2(#costprojects)
end
def self.pdfemail2(costprojects)
#costprojects = costprojects
pdf = CombinePDF.new
#costprojects.each do |costproject|
#costproject = costproject
pdf2 = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
costproject.attachments.each do |attachment|
pdf << CombinePDF.parse( Net::HTTP.get( URI.parse( attachment.attach.url ) ) )
end
end
useremail = current_user.email
CostpdfMailer.costpdf_email(useremail,pdf).deliver
redirect_to :back
flash[:notice] = 'An Email containing a PDF has been sent to you!'
end
With the 2nd try, I get:
undefined method `render_to_string' for CostprojectsController:Class
The same render_to_string worked when it was just pdfemail.
3rd try:
pdf2 = CostprojectsController.new.render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
With the 3rd try, #costproject isn't getting passed to costprojects/viewproject.pdf.erb
You can't handle a controller action asynchronously.
And you can't call controller methods like render, render_to_string and flash[:notice] from within a class method self.pdfemail since those methods are instance methods on the controller.
You'll have to separate out the pdf email logic from the controller response logic, like this:
def pdfemail
pdf_template = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
PdfMailer.new.pdf_email params[:costproject_ids], current_user.email, pdf_template
redirect_to :back
flash[:notice] = 'An Email containing a PDF has been sent to you!'
end
Then in another file:
class PdfMailer
def pdf_email(costproject_ids, useremail, pdf_template)
#costprojects = Costproject.find(costproject_ids)
pdf = CombinePDF.new
#costprojects.each do |costproject|
#costproject = costproject
# This part needs work, you can't use render_to_string here.
# You'll have to generate this pdf some other way.
pdf2 = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
costproject.attachments.each do |attachment|
pdf_response = Net::HTTP.get URI.parse(attachment.attach.url)
pdf << CombinePDF.parse(pdf_response)
end
end
CostpdfMailer.costpdf_email(useremail, pdf).deliver
end
handle_asynchronously :pdf_email
end
The PdfMailer class does not use any controller instance methods such as render, render_to_string, or flash, that has to be taken care of in the controller since you can't handle controller action asynchronously. Consequently you have to pass in the data you need, in this case the costproject_ids, useremail from current_user.email.
Inside the PdfMailer class you'll have to generate the pdf in that loop with some other method than the controller's render_to_string. There's plenty of other tools for this here: https://www.ruby-toolbox.com/categories/pdf_generation.
On your 3rd try: it isn't working because you're instantiating a new controller which doesn't have the #costproject instance variable set in it.

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.

Resources