Rails to excel with associations - ruby-on-rails

i'am new on rails , i've spent my day looking for solution to export my data into excel file.
I've tryed to_xls, simple_xlxs and other gems, also i've tryed to render xml template , but i've no success.
So i have associations between 2 models:
Call model:
class Call < ActiveRecord::Base
belongs_to :number
has_many :results
end
and my result model:
class Result < ActiveRecord::Base
belongs_to :call
end
So i need to genrate excel tables with my OWN headers in table calle'd as i want.
And also i wan't that in this excel file will be columns from my associeted model
What can i do?
Thanks

# install in a gem file or in the terminal to test
# gem install spreadsheet
require 'spreadsheet'
Spreadsheet.client_encoding = 'UTF-8'
book = Spreadsheet::Workbook.new
sheet1 = book.create_worksheet :name => 'test'
money_format = Spreadsheet::Format.new :number_format => "#,##0.00 [$€-407]"
date_format = Spreadsheet::Format.new :number_format => 'MM.DD.YYYY'
# set default column formats
sheet1.column(1).default_format = money_format
sheet1.column(2).default_format = date_format
# depending on your data you obviously have to create a loop that fits the format of your data
sheet1.row(0).push "just text", 5.98, DateTime.now
book.write 'sample.xls'
The above code example writes the data to columns you create. You can have your info in csv style. So if you're returning objects you can just get values for each object and join the array with ',' separation and loop through.

Related

how to prepare csv data using associated model with missing values in rails

I'm trying to prepare a csv file which'll be generated from multiple tables.
So I've my model setup like bellow
# Student
# attributes
:id, :name
class Student < ActiveRecord::Base
has_many :answers
end
# Question model
# attributes
:id, :question
class Question < ActiveRecord::Base
has_many :answers
end
# Answer model
# attributes
:id, :question_id, :student_id, :answer
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :student
end
Now I want to prepare a csv file.The header of the csv file will be the actual question of the Question model sorted(by id) and then prepare the csv data accordingly from Answer modoel .Now student might not answers all the questions.So I need to set n/a if a student doesn't answer a particular question. Offcourse the questions are sorted by id and the answer is also sorted by question_id. The output will be.
what_is_you_name, how_old_are_you, what_is_your_hobby
monsur, 18, playing football
ranjon, n/a, gardening
n/a, n/a, running
I query the Answer model.Because some of the Student skipped few question therefor there are no answer object present that's why the answer is in wrong position.
what_is_you_name, how_old_are_you, what_is_your_hobby
monsur, 18, playing football
ranjon, gardening
alex,running
So I need to set n/a if the particular student skipped a question .
I can't figure it out how to solve this problem.
You could overwrite attribute ie. if Answer model has field age you could do sth like:
def age
self.age || "n/a"
end
This should return age if it's present or "n/a" if it's nil.
If this does not solve your problem please provide a way how do you create CSV.
Ok after your explanation i came up with sth like this:
Create default array default = ['n/a', 'n/a', 'n/a']
You should add :question_id to .pluck like here:
answers_arr = student.answers.order(question_id: :asc).pluck(:question_id, :answer) it gives you sth like [[1, "18"], [2, "playing football"]]
Replace values in default array with values from pluck:
answers_arr.each { |e| default[e[0] -1] = e[1] } so default should look like ["18", "playing football", "n/a"]
EDIT
Here is a code from the comment below that solved problem:
response = []
all_response = []
qs_id = [1,2.3]
answers.each do |answer|
qs_ids.each do |q_id|
if answer.question_id == q_id
response << answer.answer
else
response << 'n/a'
end
if qs_ids.last = q_id
all_response << response
end
end
end

Rails & ActiveRecord complex query: rendering a collection of has_many through objects while substituting one column

I have a List model. A List has_many :lists_movies and has_many :movies through :lists_movies and has_many :trailers through :movies. ListsMovie has a list_id, movie_id, and title columns.
I want to render all the trailers of movies in a given list, populating all the trailer data AND the list_movie.title. What is the best way to accomplish this?
If I just wanted to render the trailers, I could go:
#trailers = #list.trailers
#trailers.each do |t|
t.description
t.youtube_id
end
But I also want the title from the list_movie like:
#trailers.each do |t|
list_movie.title
t.description
t.youtube_id
end
Basically I'm trying to iterate and get data from 2 different objects.. but having trouble on which collections I should be loading.
Try writing out the query in English: "For each list, I want to write down the list_movie title, and each trailer for that movie", which, in ruby, now easily translates to:
#list.lists_movies.each do |list_movie|
# list_movie.title
list_movie.movie.trailers.each do |trailer|
# trailer.description
# trailer.youtube_id
end
end

Use RAKE Task to move CSV to POSTGRES relational DB

I have the following models.
class Lesson < ActiveRecord::Base
has_many :books
has_many :vocabularies
has_many :sentenses
end
class Book < ActiveRecord::Base
belongs_to :lesson
end
class Vocabulary < ActiveRecord::Base
belongs_to :lesson
end
class Sentense < ActiveRecord::Base
belongs_to :lesson
end
With the following table schema:
Table Lesson [lesson_number, lesson_name]
Table Books [lesson_id, page_start, page_finish]
Table Vocabulary [lesson_id, word, meaning]
Table Sentences [lesson_id, sentence, sentence meaning]
And I have a CSV file with 15,000 lessons. The CSV file uses the same structure of 2 books, 10 vocabulary, 2 sentences consistently throughout all lesson plans.
My thoughts are to start like this.
namespace :import_csv do
desc "IMPORT Lessons"
task :lessons => :environment do
CSV.foreach('CcyTbl.csv') do |row|
lesson_name_id = row[0]
lesson_name = row[1]
Lesson.create(lesson_name_id: lesson_name_id, lesson_name: lesson_name)
end
end
desc "IMPORT BOOKS"
task :books => :environment do
CSV.foreach('CcyTbl.csv') do |row|
lesson_name_id = row[0]
book_name = row[3]
book_start_pg = row[7]
book_end_pg = row[8]
Lesson.create(lesson_name_id: lesson_name_id, book_name: book_name, book_end_pg: book_end_pg)
end
end
That much seems straight forward but I am struggling with:
How to handle null values.
Some lessons have two books
(Think column 3 has book1 and book2 is column 9 and sometimes book2 is null)
Lessons might have 5-10 vocabulary words
(Column 10 vocabulary 1, column 11 vocabulary 1 meaning, column 12 vocabulary, etc)
What is the best way to import the data in this CSV into their respective tables? Does it make more sense to create multiple rake tasks to do each portion or can it be done in one go?
UPDATE
Here is a link to a sample of the header row and first row of data.
(It is a bit too long to share a picture.)
You may want to create a data object that makes it easier to work with the CSV data. Decoupling the CSV format with the model creation will make the whole process simpler:
csv = CSV.new(body, headers: true, header_converters: :symbol, converters: :all)
data = csv.to_a.map {|row| row.to_hash }
See CSV reference.
Now we have an easy way to access each field.
data.each do |d|
lesson = Lesson.create!(d[:join], ...)
book = Book.create!(lesson: lesson, page_start:..)
end
BTW & FWIW,
class Sentense < ActiveRecord::Base
should be
class Sentence < ActiveRecord::Base

How can I save array of hashes into db?

I used nokogiri to parse a xml document into array of hashes:
helpers/countries.helper
module CountriesHelper
def parse
#countries = ['australia', 'canada', 'france']
#countries.inject([]) do |memo, country|
File.open("public/#{country}.xml") do |f|
xml = Nokogiri::XML(f)
path = "//country/stores/"
memo << xml.xpath(path).map do |x|
{ 'country' => x.parent['country'],
'store' => x['store']}
end
end
end
# [{"country"=>"australia", "store"=>"store1"}, {"country"=>"france", "store"=>"store2"}]
How can I save this array of hashes format into my database? Lets say I have two models Country and Store.
You can serialize an attribute, which means saving it as a particular type of object.
#in your model
serialize :store_hashes, Array
The field should be a text field in the database. I don't know whether or not that's a good idea in this particular instance - i suspect it isn't. But that's how you save an array of hashes to the database.
http://apidock.com/rails/ActiveRecord/Base/serialize/class
You can store your array of hashes in a text field in your database.
Something like this in your migration file:
create_table "your_table", force: true do |t|
t.text "your_column_name"
end
Or, if you already have the table in the database and just want to add the new column to the table:
class Migration0001
def change
add_column :your_table, :your_column_name, :text
end
end
Just so you know, if you want to save a Hash object in your database and if you define the column type as :text, then Rails should be able to serialize it properly, and you won't need to use serialize explicitly in your model.
But, in your case, it's an Array of Hash, so it's an Array object that needs to be saved in the database, so you need to serialize the field in your model:
serialize :your_column_name, Array
That way, you can save an Array of Hashes in the database. Hope this helps.
Assuming country has many stores. Storing the hash in database would make very little sense (in my opinion). Storing in individual tables would make much more sense and easy for querying.
module CountriesHelper
def parse
#countries = ['australia', 'canada', 'france']
#countries.inject([]) do |memo, country|
File.open("public/#{country}.xml") do |f|
xml = Nokogiri::XML(f)
path = "//country/stores/"
memo << xml.xpath(path).map do |x|
{ 'country' => x.parent['country'],
'store' => x['store']}
country = Country.find_by_name(x.parent['country'])
if country.nil?
country = Country.create(name: x.parent['country'])
end
country.stores.create(name: x['store'])
end
end
end
Database transactions are meant to be invoked from Model; you can refactor later.
class Country < ActiveRecord::Base
has_many :stores
end
class Store < ActiveRecord::Base
belongs_to :country
end

RoR Method to search for data in a table and then update another column on another table based on that data

I have a program i am making changes to for my work. I know what i must do but i am having problems getting the code to work. I come from a Java and C background.
i have two tables one table called customprojecschedule_lines has a project_id,workorder_base_id, and other various columns column.
The other table called customschedule has an id, workorder column and various other columns.
I have a method and variable called work order.
I am trying to get an SQL statement that will do that like this:
class Customschedule < ActiveRecord::Base
set_table_name "customschedules"
after_create :build_customprojectschedule_lines
has_many :customprojectschedule_lines, :dependent => :destroy
has_one :projectschedule_cost
delegate :est_cost, :act_cost, :to => :projectschedule_cost, :allow_nil => true
attr_accessor :workorder_base, :lots
def workorder
customschedule.where(:id => customprojectschedule_lines.product_id)
end
def workorder=(wo)
#workorder_base = wo
customprojectschedule_lines.each do |pl|
pl.update_attributes({:workorder_base_id => wo})
end
end
def build_customprojectschedule_lines
lines = #lots.split(',').inject([]) do |lines, lot_id|
line = customprojectschedule_lines.find_or_initialize_by_workorder_lot_id(lot_id)
if line.new_record?
p workorder_base
line.workorder_base_id = #workorder_base
line.line_no = lot_id
line.workorder_split_id = 0
end
lines << line
end
customprojectschedule_lines.replace(lines)
end
Basically what i would like is that whenever a user enters a workorder on the form number goes into the database gets the stored values with that record and then retrieves the ID(from that record) and puts that same id in my other table.
However, i keep getting this error:
undefined local variable or method `customschedule' for #
<Customschedule:0x00000005542040>
Before when i was trying things i kept getting a weird looking select statement saying that Customschedule ID was null.
We use oracle here.
This is my first time posting on here so please let me know if i need anything else.
Any help is greatly appreciated.
I think all you need is to upcase the first letter in this line
customschedule.where(:id => customprojectschedule_lines.product_id)
change it to
Customschedule.where(:id => customprojectschedule_lines.product_id)

Resources