Controller
format.csv { send_data #find_customer.to_csv, :filename => "customer" + ".csv" }
format.xls { send_data #find_customer.to_csv(col_sep: "\t"), filename: 'customer.xls'}
Model
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << ["SHW#", "LeadId", "Fullname", "Address", "City", "State", "Zip", "Phone", "Email", "Created-At"]
all.each do |customer|
csv << [customer.id, customer.leadid, "#{customer.first_name} #{customer.last_name}", customer.customer_address, customer.customer_city, customer.customer_state, customer.customer_zip, customer.customer_phone, customer.email, customer.created_at.strftime("%m/%d/%Y")]
end
end
end
How to pass extra parameters in to_csv method. I want to pass start_date and end_date in to_csv method so How can I ?
Update 1
If I pass
format.xls { send_data #find_customer.to_csv(col_sep: "\t", :start_date => "date"), filename: 'customer.xls'}
Then getting error like : Unknown option start_date
Try this:
format.csv { send_data #find_customer.to_csv({}, '2014-05-08', '2014-05-10'), :filename => "customer" + ".csv" }
model
def self.to_csv(options = {}, start_date = '', end_date = '')
CSV.generate(options) do |csv|
csv << ["SHW#", "LeadId", "Fullname", "Address", "City", "State", "Zip", "Phone", "Email", "Created-At", "Start", "End"]
all.each do |customer|
csv << [customer.id, customer.leadid, "#{customer.first_name} #{customer.last_name}", customer.customer_address, customer.customer_city, customer.customer_state, customer.customer_zip, customer.customer_phone, customer.email, customer.created_at.strftime("%m/%d/%Y"), start_date, end_date]
end
end
end
Related
I have an array of objects. I am trying to create CSV data and allow the user to download that file but I get the following error:
Undefined method 'first_name' for Hash:0x007f946fc76590
employee_csv_data.each do |obj|
csv << attributes.map{ |attr| obj.send(attr) }
end
end
end
This is the button that allows a user to download the CSV:
<%= link_to "Download Employee CSV", download_employee_csv_path %>
Controller:
def download_employee_csv
employee_csv_data = []
employees.each do |employee|
employee_csv_data << {
first_name: employee[:first_name],
last_name: employee[:last_name],
email: employee_email,
phone1: employee[:phone1],
gender: employee[:gender],
veteran: employee[:veteran].to_s,
dob: employee[:dob],
core_score: service_score,
performance_rank: rank,
industry_modules_passed: industry_modules_passed
}
end
respond_to do |format|
format.html
format.csv { send_data Employer.to_csv(employee_csv_data), filename: "download_employee_csv.csv" }
end
end
employee_csv_data:
=> [{:first_name=>"Christopher",
:last_name=>"Pelnar",
:email=>"pelnar#gmail.com",
:phone1=>"4072422433",
:gender=>"male",
:veteran=>"true",
:dob=>"1988-09-09",
:core_score=>"No Score",
:performance_rank=>"No Rank",
:industry_modules_passed=>"No Industry Modules Passed"},
{:first_name=>"chris",
:last_name=>"pelnar",
:email=>"chris#gmail.com",
:phone1=>"4072422433",
:gender=>"male",
:veteran=>"true",
:dob=>"1998-09-09",
:core_score=>"729",
:performance_rank=>"Good",
:industry_modules_passed=>"Entry-Service, Entry-Tech"}]
Model:
def self.to_csv(employee_csv_data)
attributes = %w(first_name last_name email phone gender veteran dob core_score performance_rank industry_modules_passed)
CSV.generate(headers: true) do |csv|
csv << attributes
employee_csv_data.each do |obj|
csv << attributes.map{ |attr| obj.send(attr) }
end
end
end
When I click the button, it takes me to the blank HTML page without any problem. When I add .csv to the filename in the URL on that page I get the error.
It looks like it's an array of Hashes. To access properties of a hash in Ruby you need to use brackets. Try updating your code to this:
csv << attributes.map{ |attr| obj.send([], attr) }
or more concisely:
csv << attributes.map{ |attr| obj[attr] }
One more thing, in the example you provided, the keys in the hash are symbols which means you may need to convert your attributes to symbols when trying to access them, like this:
csv << attributes.map{ |attr| obj[attr.to_sym] }
I adapted #Ctpelnar1988's answer to determine the attributes dynamically and allow each array item to have different columns:
def array_of_hashes_to_csv(array)
array_keys = array.map(&:keys).flatten.uniq
CSV.generate(headers: true) do |csv|
csv << array_keys
array.each do |obj|
csv << array_keys.map{ |attr| obj[attr] }
end
end
end
Example:
puts array_of_hashes_to_csv([
{attr_a: 1, attr_b: 2},
{attr_a: 3, attr_c: 4}
])
attr_a,attr_b,attr_c
1,2,
3,,4
In the more specific "employee_csv_data" context, I think it'd look like this:
def self.to_csv(employee_csv_data)
attributes = employee_csv_data.map(&:keys).flatten.uniq
CSV.generate(headers: true) do |csv|
csv << attributes
employee_csv_data.each do |obj|
csv << attributes.map { |attr| obj[attr] }
end
end
end
I'm developing an ecommerce app and I have a csv export feature which exports all product details like name, price, etc. Each product is in one row with a column for each product attribute. I want to add a column to the file which will contain the url of each product. The reason I want this is so I can use this as a product feed that can be submitted to various shopping sites.
Here is my export code in the controller. How do I add a column called route to this? I don't have a route column in the model.
#controller
def productlist
#listings = Listing.all
respond_to do |format|
format.html
format.csv { send_data #listings.to_csv(#listings) }
end
end
#model
def self.to_csv(listings)
wanted_columns = [:sku, :name, :designer_or_brand, :description, :price, :saleprice, :inventory, :category]
CSV.generate do |csv|
csv << ['Product_ID', 'Product_title', 'Designer_or_Brand', 'Description', 'Price', 'SalePrice', 'Quantity_in_stock', 'Category'] + [:Image, :Image2, :Image3, :Image4]
listings.each do |listing|
attrs = listing.attributes.with_indifferent_access.values_at(*wanted_columns)
attrs.push(listing.image.url, listing.image2.try(:url), listing.image3.try(:url), listing.image4.try(:url))
csv << attrs
end
end
end
def self.to_csv(listings)
wanted_columns = [:sku, :name, :designer_or_brand, :description, :price,
:saleprice, :inventory, :category]
header = %w(Product_ID Product_title Designer_or_Brand Description Price
SalePrice Quantity_in_stock Category Image Image2 Image3 Image4 ProductUrl)
CSV.generate do |csv|
csv << header
listings.each do |listing|
attrs = listing.attributes.with_indifferent_access.values_at(*wanted_columns)
<< listing.image.url << listing.image2.try(:url)
<< listing.image3.try(:url) << listing.image4.try(:url)
<< Rails.application.routes.url_helpers.product_url(listing.Product_ID)
csv << attrs
end
end
end
Actually the only difference is last item of array: Rails.application.routes.url_helpers.product_url(listing.Product_ID), where product_url is your route to product#show
I've got below code:
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << %w{ id email title first_name last_name position work_phone company state industry mobile origin terms events roles booths }
all.each do |user|
events = '', roles = '', booths = ''
events = user.events.first.name.to_s if user.events.present?
roles = user.roles.first.name.to_s if user.roles.present?
booths = user.booths.first.name.to_s if user.booths.present?
csv << user.attributes.values_at("id", "email", "title", "first_name", "last_name", "position", "work_phone", "company", "state", "industry", "mobile", "origin", "terms")
csv << events
csv << roles
csv << booths
end
end
end
I want to be able to generate csv and add those values in the extra columns but I'm getting undefined method 'map' for "admin":String error.
Is there a way to append this to the csv on the same row?
CSV#<< says :
The primary write method for wrapped Strings and IOs, row (an Array or CSV::Row) is converted to CSV and appended to the data source. When a CSV::Row is passed, only the row’s fields() are appended to the output.
But you are passing stirngs. see below :
csv << events # string
csv << roles # string
csv << booths # string
Tried to replicate the erro :
require 'csv'
a = CSV.generate("") do |csv|
csv << "foo"
end
# `<<': undefined method `map' for "foo":String (NoMethodError)
Here is a fix :
require 'csv'
a = CSV.generate("") do |csv|
csv << ["foo"] # just wrapped the string into an Array as doc is saying.
end
a # => "foo\n"
Write your code as :
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << %w{ id email title first_name last_name position work_phone company state industry mobile origin terms events roles booths }
all.each do |user|
ary = %w[events,roles,booths].map do |item|
user.send(item).first.name if user.send(item).present?
end
row = user.attributes.values_at("id", "email", "title", "first_name", "last_name", "position", "work_phone", "company", "state", "industry", "mobile", "origin", "terms")
row.push(*ary)
csv << row
end
end
end
When you append to csv it's expecting an array that represents a row or a CSV::Row object. First, build the array, then append that to csv as follows:
row = user.attributes.values_at("id", "email", "title", "first_name", "last_name", "position", "work_phone", "company", "state", "industry", "mobile", "origin", "terms")
row << events
row << roles
row << booths
csv << row
I would like to create one CSV file containing two models with comma gem in my ruby 3.2.8 application. Maybe the answer to the question is trivial, but this is the first time I use this gem. I know how create the file it based on a model, but I don' t know how make matches of two.
I have a views\participants\index with :
<%= link_to 'Download CSV', '/participants.csv' %>
the controller :
def index
#participants = Participant.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #participants }
format.csv { send_data #participants.to_comma }
end
end
participant Model:
require 'comma'
class Participant < ActiveRecord::Base
comma do
id
token
end
end
and field Model:
require 'comma'
class Field < ActiveRecord::Base
comma do
name
value
id_participant
end
end
in the db i have:
Participant1 = ["id" => 1 , "token" => "a"]
Participant2 = ["id" => 2 , "token" => "b"]
Field1= ["id_participant" => 1, "name" => "p1_exams1", "value" =>5]
Field2= ["id_participant" => 1, "name" => "p1_exams2", "value" =>3]
Field3= ["id_participant" => 2, "name" => "p2_exams1", "value" =>2]
Field4= ["id_participant" => 2, "name" => "p2_exams2", "value" =>3]
I would like to have a file like this:
id token
1 a
id_p name value
1 p1_c1_exams1 5
1 p1_c1_exams2 3
id token
2 b
id_p name value
2 p1_c1_exams1 2
2 p1_c1_exams2 3
I tried with this in controller:
def index
#participants = Participant.all
#fields = Field.all
require 'csv'
csv_string = CSV.generate do |csv|
#participants.each do |p|
csv << ["id","token","last_ip_address","start_date","last_transition_date","completion_date","completed","total_time_survey","created_at"]
csv << [ p.id, p.token , p.last_ip_address, p.start_date, p.last_transition_date, p.completion_date, p.completed, p.total_time_survey, p.created_at]
#fields.each do |f|
if String(f.id_participant) == String(p.id)
csv << ["id","name","value","id_participant","id_survey","created_at"]
csv << [f.id,f.name, f.insert_value, f.id_participant, f.id_survey, f.created_at]
end
end
end
end
respond_to do |format|
format.html # index.html.erb
format.json { render json: #participants }
format.csv { send_data csv_string,
:type => "text/csv; charset=iso-8859-1; header=present",
:disposition => "attachment; filename=Database.csv" }
end
end
You can also use the fastercsv for this
i think this will help u what i am understanding that u have has many relationship between Participant and Field regarding this i have write some piece of code u can customize it as ur need
#participants = Participant.all
csv_string = FasterCSV.generate do |csv|
#participants.each do |i|
csv << ["id","token"]
csv << [ i.id, i.token ]
i.fields.each do |j|
csv << ["id_p","name", "value"]
csv << [i.id,j.name, j.value]
end
end
end
send_data csv_string,
:type => "text/csv; charset=iso-8859-1; header=present",
:disposition => "attachment; filename=anyName.csv"
I currently have some code which iv used to export a table from the data I have
require 'fastercsv'
def dump_csv
#users = User.find(:all, :order => "lastname ASC")
#outfile = "members_" + Time.now.strftime("%m-%d-%Y") + ".csv"
csv_data = FasterCSV.generate do |csv|
csv << [
"Last Name",
"First Name",
"Username",
"Email",
"Company",
"Phone",
"Fax",
"Address",
"City",
"State",
"Zip Code"
]
#users.each do |user|
csv << [
user.lastname,
user.firstname,
user.username,
user.email,
user.company,
user.phone,
user.fax,
user.address + " " + user.cb_addresstwo,
user.city,
user.state,
user.zip
]
end
end
send_data csv_data,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=#{#outfile}"
flash[:notice] = "Export complete!"
end
my question is how do I call it from my view and will this work with will_pagination. I know FasterCVS creates tables using the ActiveRecord so will_paginiation wont be of any use when trying to organize the table.
I don't understand why you are talking about will_paginate...
But if you want to send data or send a file from a controller, you should look at the methods send_data and send_file :
http://api.rubyonrails.org/classes/ActionController/Streaming.html
Thanks for your help. I saw your link and came up with this:
#lists = Project.find(:all, :order=> (params[:sort] + ' ' + params[:direction]), :conditions => ["name || description LIKE ?", "%#{params[:selection]}%"])
csv_string = FasterCSV.generate do |csv|
csv << ["Status","Name","Summary","Description","Creator","Comment","Contact Information","Created Date","Updated Date"]
#lists.each do |project|
csv << [project.status, project.name, project.summary, project.description, project.creator, project.statusreason, project.contactinfo, project.created_at, project.updated_at]
end
end
filename = Time.now.strftime("%Y%m%d") + ".csv"
send_data(csv_string,
:type => 'text/csv; charset=utf-8; header=present',
:filename => filename)
end