Rails 4 - Import CSV is not working - ruby-on-rails

In rails 4.2.4, I am trying to extract the data from .csv file and save it to the database. But right now extracted row from the file is in wrong format so that value is not getting save.
require 'csv'
filename = "#{asset.document.path}"
if File.exist?(filename)
file = File.open(filename)
if file
CSV::parse(file)[1..-1].each do |row|
User.create_data(row, admin)
end
end
end
def create_data(row, admin)
usr = User.new
usr.name = row[0] if row[0]
usr.email = row[1] if row[1]
usr.password = row[2] if row[2]
user.save
end
Generated row's data is like ["Sannidhi\tsannidhi#gmail.com\tsannidhi123#\t"]. From this row I am not getting each values separately Eg: row[0], row[1] & row[2] to assign for related database fields.
How can I solve this CSV import issue? Please help me.

Try this:
CSV::parse(file)[1..-1].each do |row|
row.shift.split("\t") unless row.blank?
User.create_data(row, admin)
end
After this, you should be able to access:
row[0] #=> "Sannidhi"
row[1]
row[2]

You CSV file uses tabs as column separators. You can pass your own column separator to CSV as a col_sep option. Even though other 2 answers will do the job, let csv do its job on its own:
CSV::parse(file, col_sep: "\t")[1..-1].each do |row|
User.create_data(row, admin)
end
Also, consider using headers option to use the first line of the file as a header instead of [1..-1]:
CSV::parse(file, col_sep: "\t", headers: 'first_row').each do |row|
User.create_data(row, admin)
end

CSV stands for Comma Separated Value. It seems that your file is separated by spaces instead. Replace the tabs in your file by commas.

Related

How to check header exist before import data in Ruby CSV?

I want to write header only 1 time in first row when import data to csv in ruby, but the header is written many time on output file.
job_datas.each do |job_data|
#company_job = job data coverted etc....
save_job_to_csv(#company_job)
end
def save_job_to_csv(job_data)
filepath = "tmp/jobs/jobs.csv"
CSV.open(filepath, "a", :headers => true) do |csv|
if csv.blank?
csv << CompanyJob.attribute_names
end
csv << job_data.attributes.values
end
end
Any one can give me solution? Thank you so much!
You are calling save_job_to_csv the method for each job_data and pushing header every time csv << CompanyJob.attribute_names
filepath = "tmp/jobs/jobs.csv"
CSV.open(filepath, "a", :headers => true) do |csv|
# push header once
csv << CompanyJob.attribute_names
# push every job record
job_datas.each do |job_data|
#company_job = job data coverted etc....
csv << #company_job.attributes.values
end
end
The above script can be created wrapped a method but if you like to write a separate method that just saves the CSV, then you need to refactor the script when you first prepare an array of values holding header and pass it to a method that just saves to CSV.
You could do something similar to this:
def save_job_to_csv(job_data)
filepath = "tmp/jobs/jobs.csv"
unless File.file?(filepath)
File.open(filepath, 'w') do |file|
file.puts(job_data.attribute_names.join(','))
end
end
CSV.open(filepath, "a", :headers => true) do |csv|
csv << job_data.attributes.values
end
end
It just checks beforehand if the file exists and if not it adds the header. If you want tabs as column separators, you just have to change the value for the join function and add the col_sep parameter to CSV.open():
file.puts(job_data.attribute_names.join("\t"))
CSV.open(filepath, "a", :headers => true, col_sep: "\t") do |csv|

Ruby Rails Remove Comments from CSV

I'm new to rails and am trying to process a CSV file, some files will have comments at the start of the CSV file, comments are marked with #. If there a way I can delete these rows? I don't have to just ignore them as I want to save the file without comments.
sample file:
#-----------------------
# report --------------
#-----------------------
Date, transctions
20100923, 34
20200110, 56
Thanks.
The CSV library has a skip_lines options:
When setting an object responding to match, every line matching it is considered a comment and ignored during parsing. When set to a String, it is first converted to a Regexp. When set to nil no line is considered a comment. If the passed object does not respond to match, ArgumentError is thrown.
This should work for you:
CSV.foreach(file, skip_lines: /^#/, headers: true) do |row|
# ...
end
/^#/ matches lines starting with #.
Adding something to #Stefan answer (all credit goes to him for the skip_lines tip), assuming your csv file is input.csv :
require "csv"
CSV.open("output.csv", "wb") do |output_csv|
CSV.foreach("input.csv", skip_lines: /^#/, headers: true) do |row|
# ...
output_csv << row
end
end
This way you will end with a file output.csv without those comments.
EDIT:
If you want also the header, you can do:
CSV.open("output.csv", "wb") do |output_csv|
CSV.foreach("input.csv", skip_lines: /^#/, headers: true).with_index(0) do |row, i|
output_csv << row.headers if i == 0
puts row
output_csv << row
end
end
...It's not as clean as I want but fits your needs ;)

Ruby csv - delete row if column is empty

Trying to delete rows from the csv file here with Ruby without success.
How can I tell that all rows, where column "newprice" is empty, should be deleted?
require 'csv'
guests = CSV.table('new.csv', headers:true)
guests.each do |guest_row|
p guests.to_s
end
price = CSV.foreach('new.csv', headers:true) do |row|
puts row['newprice']
end
guests.delete_if('newprice' = '')
File.open('new_output.csv', 'w') do |f|
f.write(guests.to_csv)
end
Thanks!
Almost there. The table method changes the headers to symbols, and delete_if takes a block, the same way as each and open.
require 'csv'
guests = CSV.table('test.csv', headers:true)
guests.each do |guest_row|
p guest_row.to_s
end
guests.delete_if do |row|
row[:newprice].nil?
end
File.open('test1.csv', 'w') do |f|
f.write(guests.to_csv)
end

Run method during CSV upload

I have a simple CSV uploader below that is going row by row and creating a new record (event). I also have the unidecoder gem in use and would like to call the to_ascii method on a field (the description field) that is in every record being created by the CSV uploader. It sounds like it should be simple, but I'm not familiar with iterating through a CSV file.
The uploader:
def self.import(file)
CSV.foreach(file.path, headers: true, encoding: "windows-1252:utf-8") do |row|
Event.create! row.to_hash
end
end
Correct way of implementing this:
def self.import(file)
CSV.foreach(file.path, headers: true, encoding: "windows-1252:utf-8") do |row|
description = row[2]
row[2] = description.to_ascii
Event.create! row.to_hash
end
end
Thanks!
Try this:
CSV.foreach(file.path, headers: true, encoding: "windows-1252:utf-8") do |row|
unless row[1].blank?
description = row[1] # Change 1 for the correct column
row[1] = description.to_ascii
end
Event.create! row
end
If the description is not blank (empty), extract and update the value (description) and then save it.
row is an Array of you comma separated values, for example a CSV file like name, description, address, the row[1] have the value of the description.

How to solve when parsing CSV file return empty values?

I am using ruby 1.9.2 and Rails 3.0.7. I have written ruby back-end script that will create one CSV file on every 15 min interval using cron job.
Back-end ruby script:
CSV.open("count.csv", 'wb',:col_sep=>',') do |csv|
# header row
csv << ['id', 'count']
models = Model.all
models.each do |obj|
csv << [ obj.id, obj.get_count]
end
end
From above script CSV file(count.csv) created successfully. In Rails app,
CSV.foreach("count.csv", :quote_char => '"', :col_sep =>',', :row_sep =>:auto, :headers => true) do |row|
count = row["count"].to_i if row["id"].to_i == #id
end
I need to parse count value from that CSV file. but problem is when the time of cron execution, I unable to get count value from that CSV file return zero for all record and after execution finish I can get value of count. But I need count value always whether the cron execution stop or start, Can any one help me to resolve or any suggestion ? Thanks in advance.
models = Model.all
models.each do |obj|
csv_string << [ obj.id, obj.get_count]
end
CSV.open("count.csv", 'wb',:col_sep=>',') do |csv|
# header row
csv << ['id', 'count']
csv << csv_string
end

Resources