In my to_csv function I am wanting to print the name of a particular course, then print under that the name of a module in that course and then each task of that module.
e.g. (multiple headers to allow for easy csv import of same file)
Course_name #header
Course1 #course name
Module_name #header
Module1 #module name
Tasks #header
Task1 #task name
Taskdescription #description of task
Taskresult #expected result of task
Task2 #task name
Taskdescription #description of task
Taskresult #expected result of task
Module2 #module name
Tasks #header
Task1 #task name
Taskdescription #description of task
Taskresult #expected result of task
In my course model I have:(comments are only in stackoverflow, not in actual code)
def to_csv(params)
CSV.generate do |csv|
csv << %w{ Course_name }
csv << [ self.name ]
csv << %w{ Module_name }
Mod.all.each do |mod| #load all mods
if mod.course_id == self.id do #match mod to their course and only print those if they match
csv << [ mod.name ]
Task.all.each do |task| #load all tasks
if task.mod_id == mod.id do #match task to their module and only print those if they match
csv << [ task.name]
csv << [ task.description ]
csv << [ task.expectedresult ]
end
end
end
end
end
When I download the CSV file, i only get:
Course_name
course1
Module_name
so the the program is being caught at if mod.course_id == self.id do
not Mod.all.each do |mod| because when I only had that line in it printed every module.
Am I using the right logic check for model or am I missing something else.
Any help on this would be appreciated, thanks.
EDIT:
I tried using
Mod.all.each do |mod|
if 1 == 1 do
csv << [ mod.name ]
which printed each module name, so the if function works.
Doing:
Mod.all.each do |mod|
csv << [ mod.name ]
csv << [ mod.id ]
csv << [ self.id ]
results in
module1 #mod name
1 #mod id
1 #course id
module2 #mod name
2 #mod id
1 #course id
If I use .to_i for both mod.id and self.id that would set both to int 1, for module 1, which should allow the if function to pass. but it does not.
I think you should remove do from the if blocks (it's a valid syntax in Elixir, not in Ruby)
if mod.course_id == self.id
Related
I am trying to export data to CSV based on the current case_main "show" page the export button is on. I believe I need something similar to the following, but I can't get any permutation of it to work. Any attempt at referencing self.id or :id results in an error or nil.
Billedtime.where(case_main_id: self.id).each do |time|
Using Billedtime.all.each do |time| lets the code run, but it obviously grabs everything instead of only billedtimes for the current case_main.
case_main has two one-to-manys, billedtimes and lineitems. I am trying to combine billedtimes + lineitems for a particular case_main into a single .CSV export.
Here is the code:
Model:
def self.to_csv
desired_columns = ["Client Code",
"Case Name" ,
"Date",
"Description",
"Hours",
"Charge",
]
CSV.generate(headers: true) do |csv|
# Header columns
csv << desired_columns
# Data columns
Billedtime.where(case_main_id: self.id).each do |time|
csv <<
[
time.case_main.client.client_code,
time.case_main.case_name,
time.billedtime_date,
time.billedtime_description,
time.billedtime_hours,
time.billedtime_total
]
end
Lineitem.where(case_main_id: self.id).each do |line|
csv <<
[
line.case_main.client.client_code,
line.case_main.case_name,
line.lineitem_date.to_date,
line.lineitem_description,
' ',
line.lineitem_total
]
end
end
end
Controller:
def show
#case_mains = CaseMain.where(:id => #case_main.id).limit(5)
respond_to do |format|
format.html
format.csv { send_data #case_mains.to_csv, filename: "case-#{Date.today}.csv"}
end
end
View:
<%= link_to "Export Billed Items", case_main_path(format: "csv") %>
Thanks!
The issue is that your model doesn't poses an id attribute, so self.id doesn't do the job. Instead you want to grab the current scope using all and iterate through the collection using each or find_each. To prevent an 1+N query I've also added the needed includes call.
def self.to_csv
CSV.generate(headers: true) do |csv|
csv << [
"Client Code",
"Case Name",
"Date",
"Description",
"Hours",
"Charge"
]
all.includes(:client, :billedtimes, :lineitems).find_each do |case_main|
case_main.billedtimes.each do |billedtime|
csv << [
case_main.client.client_code,
case_main.case_name,
billedtime.billedtime_date,
billedtime.billedtime_description,
billedtime.billedtime_hours,
billedtime.billedtime_total
]
end
case_main.lineitems.each do |lineitem|
csv << [
case_main.client.client_code,
case_main.case_name,
lineitem.lineitem_date.to_date,
lineitem.lineitem_description,
' ',
line.lineitem_total
]
end
end
end
end
You might think, I don't want to export all records. However all (without receiver) operates on the current scope, so when you use it in the context CaseMain.where(id: #case_main.id).limit(5).to_csv you will grab the collection CaseMain.where(id: #case_main.id).limit(5).all.
Is there a way to achieve exporting the entire active records in a rails application to csv where each relation is a sheet of csv or there is another way to export full db data.Any suggestions?
Try like this you can get datas from your tables. after you can split it by sheet
models = ActiveRecord::Base.connection.tables
models.shift
models.shift
models.map do |model_name|
model_name = model_name.split("")
model_name.pop
model_name = model_name.join("")
model_name.camelize.constantize.all.map do |data|
puts data
end
end
def backup
models = ActiveRecord::Base.connection.tables
all_data = Hash.new
models.map do |model_name|
table_data = []
model_name = model_name.split("")
model_name.pop
model_name = model_name.join("")
model_name.camelize.constantize.all.map do |data|
table_data.push(data)
end
all_data[model_name.camelize] = table_data
end
send_data export_csv(all_data), filename: "Backup - #{Date.today}.csv" and return
end
def export_csv(data)
csvfile = CSV.generate(headers: true) do |csv|
data.each do |key, value|
csv << [key]
attributes = key.camelize.constantize.column_names
csv << attributes
value.each do |val|
csv << val.attributes.values_at(*attributes)
end
csv << ['eot']
end
end
return csvfile
end
I found the solution and a way to export all tables inside a single csv file.
I'm trying to filter through all venues, and add any without an external_join_url to the CSV I've created.
require 'csv'
namespace :create_dump do
desc "Erase and fill database"
task :venue => :environment do
#venues = Venue.where(external_join_url: nil)
CSV.open("venues_without_join_url.csv", "w") do |csv|
csv << ["Venue Title"]
#venues.each do |venue|
csv << venue.title
end
end
end
end
When I attempt to do so, I get the error:
NoMethodError: undefined method `map' for "5 Pancras Square":String
I get this means I'm trying to map a string, but can't see what part I'm creating the string. I've tried different ways of assigning #venues (creating an array and shovelling into it) to no avail.
csv << expects an array of strings that's meant to be the fields of a row in the csv file
require 'csv'
namespace :create_dump do
desc 'Erase and fill database'
task venue: :environment do
#venues = Venue.where(external_join_url: nil)
CSV.open('venues_without_join_url.csv', 'w') do |csv|
csv << ['Venue Title']
#venues.each do |venue|
csv << [venue.title]
end
end
end
end
I am using a rake task to take data from one csv, call the shopify api using that data, and save the response to another CSV. The problem is I have no error handler in place so that if the shopify api cannot find the resource I provided, the whole task gets aborted. What is the best way to handle the error so that if the resource is not found in Shopify, simply skip it and proceed to the next row?
The line calling the shopify API in the code below is:
variant = ShopifyAPI::Variant.find(vid)
namespace :replace do
desc "replace variant id with variant sku"
task :sku => :environment do
file="db/master-list-3-28.csv"
newFile = Rails.root.join('lib/assets', 'newFile.csv')
CSV.open(newFile, "a+") do |csv|
CSV.foreach(file) do |row|
msku, namespace, key, valueType, value = row
valueArray = value.split('|')
newValueString = ""
valueArray.each_with_index do |v, index|
recArray = v.split('*')
handle = recArray[0]
vid = recArray[1]
newValueString << handle
newValueString << "*"
# use api call to retrieve variant sku using handle and vid
#replace vid with sku and save to csv
variant = ShopifyAPI::Variant.find(vid)
sleep 1
# puts variant.sku
newValueString << variant.sku
if index < 2
newValueString << "|"
end
end
#end of value save the newvaluestring to new csv
csv << [newValueString]
end
end
end
end
Here's a simple way to get it done:
begin
variant = ShopifyAPI::Variant.find(vid)
rescue
next
end
If an exception is raised the stuff in rescue happens.
I am trying to scrape list of restaurants for my zip code from Deliveroo.co.uk
I need to add a way to figure out whether a restaurant is open or closed... from the website its very clear, but I just need to update my code to reflect this.
How do I go about doing this? I need to create something like a 'status' variable and then set each restaurant to 'open' or 'closed'.
Here is the website I'm trying to scrape from: https://deliveroo.co.uk/restaurants/london/maida-vale?postcode=W92DE&time=1800&day=today
And my code is below.
thanks.
require 'open-uri'
require 'nokogiri'
require 'csv'
# Store URL to be scraped
url = "https://deliveroo.co.uk/restaurants/london/maida-vale?postcode=W92DE"
# Parse the page with Nokogiri
page = Nokogiri::HTML(open(url))
# Display output onto the screen
name =[]
page.css('span.list-item-title.restaurant-name').each do |line|
name << line.text
end
category = []
page.css('span.restaurant-detail.detail-cat').each do |line|
category << line.text
end
delivery_time = []
page.css('span.restaurant-detail.detail-time').each do |line|
delivery_time << line.text
end
distance = []
page.css('span.restaurant-detail.detail-distance').each do |line|
distance << line.text
end
status = []
# Write data to CSV file
CSV.open("deliveroo.csv", "w") do |file|
file << ["Name", "Category", "Delivery Time", "Distance", "Status"]
name.length.times do |i|
file << [name[i], category[i], delivery_time[i], distance[i]]
end
end
end
We need to check li.restaurant--details have / have not class unavailable for close / open restaurant.
status = []
page.css('li.restaurant--details').each do |line|
if line.attr("class").include? "unavailable"
sts = "closed"
else
sts = "open"
end
status << sts
end
Btw, you should remove white space when get restaurant_name, etc ...
page.css('span.list-item-title.restaurant-name').each do |line|
name << line.text.strip
end
You can refer my code at here: https://gist.github.com/vinhnglx/4eaeb2e8511dd1454f42