ArgumentError on CSV output - ruby-on-rails

i'm getting the following error when trying to generate a CSV:
ArgumentError in ProductsController#schedulecsv
wrong number of arguments (0 for 1)
My Products controller is set up as follows:
def schedulecsv
products = Product.find(:all)
filename ="schedule_#{Date.today.strftime('%d%b%y')}"
csv_data = CSV.generate do |csv|
csv << Product.csv_header
products.each do |p|
csv << p.to_csv
end
end
send_data csv_data,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=#{filename}.csv"
end
Does anyone have any pointers here? Driving me bonkers!
Thanks!

From source of csv.rb place in /usr/lib/ruby/(version of your ruby gem)/csv.rb (on my machine)
Here is source code of CSV class's generate method
def CSV.generate(path, fs = nil, rs = nil, &block)
open_writer(path, 'w', fs, rs, &block)
end
generate method require filename as parameter.it will make file with given name,but You are calling CSV.generate filename was missed
so you have to passed name of file in generate call!
filename ="schedule_#{Date.today.strftime('%d%b%y')}"
CSV.generate filename do |csv|
csv << Product.csv_header
products.each do |p|
csv << p.to_csv
end
end

Related

How to refresh page after send_data in rails 6

Hello devs i'm generating a csv in my controllers but everytime the user clicks on download, it has to refresh manually, how can i avoid this?
#archivo_csv = CSV.generate(:encoding => 'windows-1252') do |csv|
csv << ["indicator","count"]
#indicator[1..#indicator.size].each_with_index do |indicator, i|
csv << ["#{indicator[0]}", indicator[1]]
indicator[2].each do |date|
csv << [" #{date[0]}", date[1]]
end
indicator[3].each do |status|
csv << [" #{status[0]}", status[1]]
end
end
send_data #archivo_csv, :filename => "Indicadores Generales.csv"
end

How can I dry up code to export different CSVs from my action controller and application record in rails 5?

I have an app whose sole purpose is to seed data files and add the data to different CSVs which are zipped and exported by the user. My application controller is filled with lines that all look like this:
def export_tips
#appointments = Appointment.order('service_id')
send_data #appointments.to_csv_tips, filename: 'tips.csv'
end
def export_ticketpayments
#appointments = Appointment.order('service_id')
send_data #appointments.to_csv_ticketpayments, filename: 'ticketspaymentitems.csv'
end
def export_batchmanifest
#batchmanifests = Batchmanifest.all
send_data #batchmanifests.to_csv_batchmanifest, filename: "batch_manifest-#{Date.today}.csv"
end
def export_pets
#clients = Client.all
send_data #clients.to_csv_pets, filename: 'pets.csv'
end
def export_clients
#clients = Client.all
send_data #clients.to_csv_clients, filename: 'clients.csv'
end
I have it in the application controller because I used it in multiple different areas including creating single CSV exports and creating complex zip files with multiple zips and CSVs inside.
Some things that I have tried to cleanup the code include:
Different variables of this:
def csv_export (model, filename)
#model.pluralize = (model.titleize).all
send_data #model.pluralize.filename, filename: filename
end
Having each one in its own controller (could not access them from different views and other controllers easily)
I also tried to figure out how to create my own module, but was unable to do so.
My application record is just as bad with repeated lines simply meant to export the CSVs:
def self.to_csv_appointments
attributes = %w[appointment_id location_id employee_id client_id child_id notes
has_specific_employee start_time end_time]
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |appointment|
csv << attributes.map { |attr| appointment.send(attr) }
end
end
end
def self.to_csv_appointmentservices
attributes = %w[appointment_id service_id price duration]
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |appointment|
csv << attributes.map { |attr| appointment.send(attr) }
end
end
end
def self.to_csv_tickets
attributes = %w[ticket_id location_id client_id ticket_status employee_id
employee_id start_time]
headers = %w[ticket_id location_id client_id status employee_id
closed_by_employee_id closed_at]
CSV.generate(headers: true) do |csv|
csv << headers
all.each do |appointment|
csv << attributes.map { |attr| appointment.send(attr) }
end
end
end
For the application record, I have tried similar methods as those listed for the application controller, but to no avail. Again, I use the code in application record instead of in the individual model files because I need to access these in multiple parts of the site.
The code from the application controller is used mostly in the static controller and buttons on the view files. I need the ability to create the file sets, as listed below, but also allow the user to export just one CSV.
Examples from static controller to built the zip files:
def create_appointments_zip
file_stream = Zip::OutputStream.write_buffer do |zip|
#appointments = Appointment.order('service_id')
zip.put_next_entry "appointment_manifest.csv"; zip << File.binread("#{Rails.root}/app/assets/csvs/appointment_manifest.csv")
zip.put_next_entry "appointments.csv"; zip << #appointments.to_csv_appointments
zip.put_next_entry "appointment_services.csv"; zip << #appointments.to_csv_appointmentservices
zip.put_next_entry "appointment_statuses.csv"; zip << #appointments.to_csv_appointmentstatuses
end
file_stream.rewind
File.open("#{Rails.root}/app/assets/csvs/appointments.zip", 'wb') do |file|
file.write(file_stream.read)
end
end
def export_salonset
create_appointments_zip
create_tickets_zip
create_inventory_zip
create_memberships_zip
file_stream = Zip::OutputStream.write_buffer do |zip|
#saloncategories = Saloncategory.all
#salonservices = Salonservice.all
#clients = Client.all
#locations = Location.all
#salonpricings = Salonpricing.all
#staffs = Staff.order("location_id")
zip.put_next_entry "batch_manifest.csv"; zip << File.binread("#{Rails.root}/app/assets/csvs/batch_manifest_simple_salon.csv")
zip.put_next_entry "categories.csv"; zip << #saloncategories.to_csv_saloncategories
zip.put_next_entry "clients.csv"; zip << #clients.to_csv_clients
zip.put_next_entry "employees.csv"; zip << #staffs.to_csv_staff
zip.put_next_entry "locations.csv"; zip << #locations.to_csv_locations
zip.put_next_entry "pricings.csv"; zip << #salonpricings.to_csv_pricings
zip.put_next_entry "services.csv"; zip << #salonservices.to_csv_salonservices
zip.put_next_entry "appointments.zip"; zip << File.binread("#{Rails.root}/app/assets/csvs/appointments.zip")
zip.put_next_entry "inventories.zip"; zip << File.binread("#{Rails.root}/app/assets/csvs/inventories.zip")
zip.put_next_entry "tickets.zip"; zip << File.binread("#{Rails.root}/app/assets/csvs/tickets.zip")
zip.put_next_entry "addonmappings.csv"; zip << File.binread("#{Rails.root}/app/assets/csvs/addonmappings.csv")
end
file_stream.rewind
respond_to do |format|
format.zip do
send_data file_stream.read, filename: "salon_set.zip"
end
end
file_stream.rewind
File.open("#{Rails.root}/app/assets/csvs/salon_set.zip", 'wb') do |file|
file.write(file_stream.read)
end
end
Link to my repository, if that is helpful
https://github.com/atayl16/data-wizard/blob/master/app/controllers/application_controller.rb
https://github.com/atayl16/data-wizard/blob/master/app/models/application_record.rb
I know there must be a better way than writing these same lines over and over. The code works, my site works (amazingly), but I would be embarrassed for any seasoned developer to see the repository without laughing. Any help is appreciated!
In this end, I ended up using metaprogramming to clean this up. Here is an example in which I excluded some items from the array for brevity:
["bundle", "attendee", "location", "membership", "client", "staff"].each do |new_method|
define_method("#{new_method.pluralize}") do
instance_variable_set("##{new_method.pluralize}", new_method.camelcase.constantize.all)
instance_var = instance_variable_get("##{new_method.pluralize}")
send_data instance_var.public_send("to_csv_#{new_method.pluralize}"), filename: "#{new_method.pluralize}.csv"
end
end
I was able to remove 30 methods from my newly created export controller. Here is the code after pushing up the changes https://github.com/atayl16/data-wizard/blob/0011b6cf8c1fe967d73a569fa573cedc52cb8c72/app/controllers/export_controller.rb

Rails: send_data(csv) putting object id in csv file rather than csv info

I have a block in a rails controller that looks like this:
CSV.generate do |csv|
csv << ["Name", "Other Field"]
csv << [#app.name, #app.other_field]
send_data csv, filename: 'test.csv'
end
But then the contents of the downloaded file are simply this:
#<CSV:0x007fe518414bc8>
What am I doing wrong?
CSV.generate returns the generated CSV string, for example:
str = CSV.generate { |csv| csv << %w[a b c]; csv << [1,2,3] }
puts str
gives you:
a,b,c
1,2,3
as output.
You're passing a CSV instance to send_data and it is trying to convert that CSV instance to a string by, apparently, calling to_s on it.
You want to say something more like this:
csv_string = CSV.generate do |csv|
csv << ["Name", "Other Field"]
csv << [#app.name, #app.other_field]
end
send_data csv_string, filename: 'test.csv'

Active admin CSV export custom query scoped

Am using active admin Export CSV option. Its returning all the values related to the particular table.
I want the reports only for a particular month.
Can anyone help?
you can write own csv exporter
collection_action :download_report, :method => :get do
users = User.where('created_at >= ?', Date.today - 1.month)
csv = CSV.generate( encoding: 'Windows-1251' ) do |csv|
# add headers
csv < [ #Some header ]
# add data
users.each do |user|
csv << [ user.created_at ]
end
end
# send file to user
send_data csv.encode('Windows-1251'), type: 'text/csv; charset=windows-1251; header=present', disposition: "attachment; filename=report.csv"
end
action_item only: :index do
link_to('csv report'), params.merge(:action => :download_report))
end
index :download_links => false do
# off standard download link
end
this is just example for you. Your code can be another
for generation csv file use this code where you want
# generate csv file of photo
def self.generate_csv
header = []
csv_fname = "#{CSV_FILE_PATH}/images.csv"
options = {headers: :first_row}
photo_columns = column_names - ["id", "updated_at"]
photo_columns.map{|col| col == "created_at" ? header << "ScrapeDate" : header << col.classify}
CSV.open(csv_fname, "w", options ) do |csv|
csv << header if File.exist?(csv_fname) && File.size(csv_fname) == 0
find_each(batch_size: 5000) do |photo|
csv << photo.attributes.values_at(*photo_columns)
end
end
end
in above code which column you don't want subtract that cols from actual cols, for example column_names - ["id", "updated_at"] here column_names return actual cols array and which cols we don't need we subtract them.

Rails 3.1 active record query to an array of arrays for CSV export via FastCSV

I'm attempting to DRY up a method I've been using for a few months:
def export(imagery_requests)
csv_string = FasterCSV.generate do |csv|
imagery_requests.each do |ir|
csv << [ir.id, ir.service_name, ir.description, ir.first_name, ir.last_name, ir.email,
ir.phone_contact, ir.region, ir.imagery_type, ir.file_type, ir.pixel_type,
ir.total_images, ir.tile_size, ir.progress, ir.expected_date, ir.high_priority,
ir.priority_justification, ir.raw_data_location, ir.service_overviews,
ir.is_def, ir.isc_def, ir.special_instructions, ir.navigational_path,
ir.fyqueue, ir.created_at, ir.updated_at]
end
end
# send it to the browser with proper headers
send_data csv_string,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=requests_as_of-#{Time.now.strftime("%Y%m%d")}.csv"
end
I figured it would be a LOT better if instead of specifying EVERY column manually, I did something like this:
def export(imagery_requests)
csv_string = FasterCSV.generate do |csv|
line = []
imagery_requests.each do |ir|
csv << ir.attributes.values.each do |i|
line << i
end
end
end
# send it to the browser with proper headers
send_data csv_string,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=requests_as_of-#{Time.now.strftime("%Y%m%d")}.csv"
end
That should be creating an array of arrays. It works just fine in the Rails console. But in the production environment, it just produces garbage output. I'd much rather make this method extensible so I can add more fields to the ImageryRequest model at a later time. Am I going about this all wrong?
I'm guessing that it probably works in the console when you do it for just one imagery_request, yes?
But when you do multiple it fails?
Again I'm guessing that's because you never reset line to be an empty array again. So you're continually filling a single array.
Try the simple way first, to check it works, then start going all << on it then:
csv_string = FasterCSV.generate do |csv|
imagery_requests.each do |ir|
csv << ir.attributes.values.clone
end
end
PS - in the past I've even used clone on my line-by-line array, just to be sure I wasn't doing anything untoward with persisted stuff...

Resources