Ruby on Rails Mongoid to csv - ruby-on-rails

I would like to ask if any one know how correctly transfer Mongoid data in to CSV document?
I got a model Record and I want every row from Record to become row in CSV document. I got 90 columns (keys) in the Record and I want to exclude some of the them from CSV document but I do not wont manually type every key which I want to be on CSV document. My code is
#all_rows = Record.all
CSV.open(Rails.root.join('public', 'downloads', "file.csv"), "w") do |csv|
#allrows.all.each do |record|
csv << record
end
But it does not work I am getting error
undefined method `map' for #<Record:0x007f9cd9e242f8>
if i adding record.to_s
i am gating document full of records like this #<Record:0x007f801ba60d68>
If any one can please help me to fix it! Thank you!

You are using << method on csv (documentation), which expects to be called with array as an argument. That is why it tries to perform map method on your record.
Solution for your problem is adding array of attributes instead of record object. There is method attributes that will return hash with all attributes.
ignored_attributes = ["attribute_you_dont_want", "another_attribute"]
#all_rows = Record.all
CSV.open(Rails.root.join('public', 'downloads', "file.csv"), "w") do |csv|
#all_rows.each do |record|
csv << record.attributes.delete_if{ |attr, value| ignored_attributes.include?(attr) }.values
end
end
Note that I wrote #all_rows.each, you shouldn't call all method again.
This code will perform delete_if method on attributes hash and will remove any attributes with names included in ignored_attributes array. delete_if returns hash on which you can call values method to return only array of values.

Related

New line in csv cell (rails)

I'd like multiple pieces of data on different lines within the same CSV cell like this:
"String" 2-15-2021 05:26pm
"String ..."
"String..."
I have tried the following and ended up with \n in the cell and not an actual new line, like this "2-15-2021 05:26pm \nHi, it's ...".
["\n", time, text.body].join("\n")
[time, text.body, "\n"].join("\n")
[time, text.body].join("\n")
The input data is an array of hashes. The output of a row is a hash with keys and values, one of the values is a list of strings (or this can be a list of lists of string, I am playing with what I can get to work). The list of strings is where I am trying to add line breaks.
I am using this to create the csv:
CSV.open("data.csv", "wb") do |csv|
csv << list.first.keys
list.each do |hash|
csv << hash.values
end
end
I ended up needing a list of strings that I could then join and add new lines onto.
values = []
values.push("#{time}, #{text.body}")
# And then in the hash for the csv, setting the value for that column like this:
{ message: values.join("\n\n")}

How to manipulate a CSV object in ruby?

I want to export some ActiveRecords in CSV format. After check some tutorials, I found this:
def export_as_csv(equipments)
attributes = %w[id title description category_id]
CSV.generate(headers: true) do |csv|
csv << attributes
equipments.each do |equipment|
csv << equipment.attributes.values_at(*attributes)
end
return csv
end
end
The problem is, I want to manipulate all in memory in my tests(i.e. I don't want to save the file in the disk). So, when I receive this csv object as return value, how I can iterate through rows and columns? I came from Python and so I tried:
csv = exporter.export_as_csv(equipments)
for row in csv:
foo(row)
But obviously didn't work. Also, the equipments are surely not nil.
CSV.generate returns string formatted according csv rules.
So the most obvious way is to parse it and iterate, like:
csv = exporter.expor_as_csv(equipments)
CSV.parse(csv).each do |line|
# line => ['a', 'b', 'c']
end
After some videos, I found that the return was the problem. Returning the CSV I was receiving a CSV object, and not the CSV itself.

In Rails, how do I export to CSV while translating values of a specific column?

Currently, I'm using Rails and able to export, but there are values within the DB that are in a numeric format, and I need them to be translated into an alphanumeric format. I have the translations, but I don't know how to do it while exporting to CSV
Here's my current snippet of code to export to CSV
def self.to_csv(mycolumns)
CSV.generate() do |csv|
csv << mycolumns
all.each do |ccts|
csv << ccts.attributes.values_at(*mycolumns)
end
end
end
So my initial thought was that I could go into each ccts and edit them, but I don't know how to access the value within the hash and alter it. And it's only for a specific column. For instance, if this table was for fruits, and one of the column names was Name. If I wanted to change a value of 0041 into Apple, but only within the Name column, I'm just not sure how to accomplish this.
The csv export code is very compact, especially this line:
csv << ccts.attributes.values_at(*mycolumns)
That makes it difficult to think about how to change it.
First think how you would export your value if it was a single column. It may look something like:
if column_name == :name
lookup_fruit_name(ccts.name)
else
ccts[column_name]
end
Now you need all the values of a ccts inside an array, so it can be sent to csv:
values = mycolums.map do |column_name|
if column_name == :name
lookup_fruit_name(ccts.name)
else
ccts[column_name]
end
end
csv << values
Then just place this inside the inner loop of your original export method.
If you think more functional, you just write an instance method that gets your value and does a conversion depending on the column:
def csv_value_for(column_name)
if column_name == :name
lookup_fruit_name( self.name )
else
self[column_name]
end
end
Then you can use it like this:
csv << mycolumns.map{|col| ccts.csv_value_for(col) }

Should I symbolize keys?

1) I am grabbing some records for the DB in HAML to display, and the attributes method on each row returns a hash. The hash's keys are strings. Should I turn those keys into symbols? I am not sure the call to symbolize_keys is worth it. I.e.,
%td #{app['comment']}
or
%td #{app[:comment]
2) I am trying to symbolize the array of hashes I return, but it is not working:
rows = Comment.all(:order => 'created DESC')
result = rows.each_with_object([]) do |row, comments|
comments << row.attributes.symbolize_keys
end
Is it not actually pushing the symbolized hash into the comments array? I also tried symbolize_keys!, and that did not help. What am I doing wrong?
Since you're using Rails, you have access to HashWithIndifferentAccess so you can bypass your "strings or symbols" issue quite easily by allow both:
h = HashWithIndifferentAccess.new(some_model.attributes)
puts h['id'] # Gives you some_model.id
puts h[:id] # Also gives you some_model.id
Your each_with_object approach:
result = rows.each_with_object([]) do |row, comments|
comments << row.attributes.symbolize_keys
end
should work fine so I think your problem with that lies elsewhere.
Do you have a reason for using ActiveRecord::Base#attributes[your_attribute] instead of ActiveRecord::Base#your_attribute directly? You didn't mention a reason.
ActiveRecord::Base automatically sets up accessors for your database fields:
object = Model.new
object.your_column = "foo" # Writer
object.your_column # Reader
You should be able to use the reader in your views instead of accessing the value through ActiveRecord::Base#attributes.
Update:
I'm not sure if this is what confuses you.
Comment.find(:all) already retrieves all columns values for those rows in your database and stores them in your Comment objects (which we assign to #comments below). The values are already stored in your Comment objects, so you may already use them in your views as you please.
In your controller, if you have:
def index
#comments = Commend.find(:all) # Fetch columns and rows.
end
you can do this in your HAML view:
- #comments.each do |comment| # Iterate through array of Comment objects
%tr
%td= comment.comment # Use value for "comment" column.
you can add hook, which symbolizes keys after model load:
class YourModel < ApplicationRecord
after_initialize do |rec|
attributes["some_json_field"].symbolize_keys! if attributes.key? "some_json_field"
end
end

Rails import from csv to model

I have a csv file with dump data of table and I would like to import it directly into my database using rails.
I am currently having this code:
csv_text = File.read("public/csv_fetch/#{model.table_name}.csv")
ActiveRecord::Base.connection.execute("TRUNCATE TABLE #{model.table_name}")
puts "\nUpdating table #{model.table_name}"
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
row = row.to_hash.with_indifferent_access
ActiveRecord::Base.record_timestamps = false
model.create!(row.to_hash.symbolize_keys)
end
with help from here..
Consider my Sample csv:
id,code,created_at,updated_at,hashcode
10,00001,2012-04-12 06:07:26,2012-04-12 06:07:26,
2,00002,0000-00-00 00:00:00,0000-00-00 00:00:00,temphashcode
13,00007,0000-00-00 00:00:00,0000-00-00 00:00:00,temphashcode
43,00011,0000-00-00 00:00:00,0000-00-00 00:00:00,temphashcode
5,00012,0000-00-00 00:00:00,0000-00-00 00:00:00,temphashcode
But problem with this code is :
It is generating `id' as autoincrement 1,2,3,.. instead of what in
csv file.
The timestamps for records where there is 0000-00-00 00:00:00 defaults to null automatically and throws error as the column created_at cannot be null...
Is there any way I can do it in generic way to import from csv to models?
or would i have to write custom code for each model to manipulate the attributes in each row manually??
for question1, I suggest you output the row.to_hash.symbolize_keys, e.g.
# ...
csv.each do |row|
#...
hash = row.to_hash.symbolize_keys
Rails.logger.info "hash: #{hash.inspect}"
model.create!(hash)
end
to see if the "id" is assigned.
for Question2, I don't think it's a good idea to store "0000-00-00" instead of nil for the date.
providing fields like 'id' and for timestamps fields too manually solved it...
model.id = row[:id]
and similar for created_at,updated_at if these exists in model..

Resources