Importing CSV file into Rails sqlite3 database - ruby-on-rails

I have created Rails database using the following schema:
ActiveRecord::Schema.define(:version => 20090807141407) do
create_table "trunks", :force => true do |t|
t.integer "npa"
t.integer "nxxFrom"
t.integer "nxxTo"
t.string "trnk"
t.datetime "created_at"
t.datetime "updated_at"
end
end
In my CSV file, I only have the first four columns (npa, nxxFrom, nxxTo, and trnk).
How can I import the CSV data while also updating the last two columns?
Thanks always

To use the csv module which is part of the standard Ruby library:
require 'csv'
# row will be an array with the fields in the order they appear in the file
CSV.open('myfile.csv', 'r') do |row|
# assuming the fields in the CSV file are in order npa, nxxFrom, nxxTo, trnk
# create and save a Trunk model for each row
Trunk.create!(:npa => row[0], :nxxFrom => row[1], :nxxTo => row[2], :trnk => row[3])
end
I haven't used fastercsv but looking at its documentation the approach seems the same.

Use the FasterCSV gem.
A nice example is at http://espndev.com/blog/csv-imports-using-fastercsv/

The last two columns will be updated by ActiveRecord. If they exist in the schema then ActiveRecord will add a value for the created_at and updated_at upon creation of the object. Any modifications to the object will cause the updated_at column to be auto updated.
So when importing from a CSV just map the first 4 columns and save the object. When you retrieve the object you will see the other two values have also been set automatically.

Related

Importing info into existing table Rails

I'm trying to import CSV data into an already existing Rails table.
My CSV has columns of lec_exam, location, meeting_days, begin_time, and end_time. My table looks as follows:
create_table "courses", force: true do |t|
t.string "lec_exam"
t.string "location"
t.string "meeting_days"
t.time "begin_time"
t.time "end_time"
t.datetime "created_at"
t.datetime "updated_at"
t.string "status"
end
The status (i.e. taken vs open) field is something I want to update based on the current time versus the presence of a course occurring at that time.
Every time I import the CSV data the last column (end_time) does not get properly imported because each course has an end_time of nil, when a simple glance at the CSV shows otherwise.
I have tried
CSV.foreach(file.path, headers: true) do |row|
#course_hash = row.to_hash # exclude the price field
#course = Course.where(id: course_hash["id"])
row = Course.create!({
:lec_exam => row[0],
:location => row[1],
:meeting_days => row[2],
:begin_time => row[3],
:end_time => row[4]
})
as well as the to_hash method. Any help towards a solution would be great. Thanks!
To import a CSV data into an existing Rails table add:
require 'csv'
csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
Moulding.create!(row.to_hash)
end
In the rake task, or in a controller action.
Source: Ruby on Rails - Import Data from a CSV file
Hope this helps!
Solved.
Model had an unneccessary
attr_accessor:
tag. Bleh.

Sort by date span

Let's say we have the following model.
create_table :meetings do |t|
t.datetime :started_at
t.datetime: ended_at
end
class Meeting < ActiveRecord::base
end
How would I order a meetings_result, so that the longest meeting is the first meeting in the collection and the shortest meeting the last.
Something like
Meeting.order(longest(started_at..ended_at))
Obviously that doesn't work.
How would I achieve this, preferably without using raw SQL?
I don't think you can do it without using raw SQL.
Using Raw SQL:
Meeting.order('(ended_at - start_at) DESC')
(works with PostGreSQL)
No SQL? Two options come to mind. Create an array of hashes and sort it there, or add another column in the db and sort on that.
# How many records in the meetings table? This array of hashes could get huge.
meetings_array = []
Meeting.all.each do |meeting|
meetings_array << {id: meeting.id, started_at: meeting.started_at, ended_at: meeting.ended_at , duration: meeting.ended_at - meeting.started_at }
end
meetings_array.sort_by { |hsh| hsh[:duration] }
Or, create another column:
# Is it worth adding another column?
create_table :meetings do |t|
t.datetime :started_at
t.datetime :ended_at
t.datetime :duration
end
Update this column whenever you have both started_at and ended_at. Then you can:
Meeting.order("duration")

How do I get only unique results from two dissimilar arrays?

This might seem like a duplicate question, but I can't find any information on this. I want to show the results from a remotely acquired json array excluding certain results by comparing them to a local table. I have a gallery model with:
t.integer :smugmug_id
t.string :smugmug_key
t.integer :category_id
t.string :category_name
t.string :description
t.integer :highlight_id
t.string :highlight_key
t.string :highlight_type
t.string :keywords
t.string :nicename
t.integer :subcategory_id
t.string :subcategory_name
t.string :title
t.string :url
The data for this model gets populated by a rake task that connects to the smugmug api (json) and stores the data locally. I'm trying to create a view that shows all the smugmug galleries that are not stored locally.
Here's what I've tried so far, but it's not excluding the locally stored galleries like I thought it would.
def self.not_stored
smugmug_list = Smug::Client.new.albums(heavy = true)
gallery_list = Gallery.select(:smugmug_id)
smugmug_list.each do |smugmug|
smugmug unless gallery_list.include? smugmug.id
end
end
Hopefully this makes sense. I'm getting a json array of galleries, and I want to display that array excluding results where the album id matches the smugmug_id of any of my locally stored records.
Quick edit: I'm using an adaptation of this gem to connect to the smugmug api.
Just use the difference operator.
General Example:
ruby-1.9.2-p136 :001 > [3,2,1] - [2,1]
=> [3]
So you would have:
smugmug_list.collect{|e| e.id} - gallery_list
Enumerable#collect will turn the smugmug_list into a list of id's. From there, you can do the difference operator, which will return all the id's of all the smugmug galleries that are not stored locally.
Another option to maintain the list of galleries:
smugmug_list.select{|e|!gallery_list.include?(e.id)}

How can we delete rows from a join table by using ActiveRecord?

create_table "tags_pages", :id => false do |t|
t.integer "tag_id", "page_id"
end
add_index "tags_pages", "tag_id"
add_index "tags_pages", "page_id"
How activerecord works on this table ? I want to insert and delete new rows. Sorry if it is a noob question.
Let's suppose you have one page and one tag.
# This will add a "tags_pages" entry, linking one page to one tag
page.tags << tag
# This will delete the appropriate "tags_pages" entry
page.tags.delete(tag)
You can also delete all the tags linked to one page with the clear method.
page.tags.clear

Unknown attribute error on CSV upload

I am trying to upload a CSV file into an existing database and receiving the following
error: ActiveRecord::RecordInvalid in UploadsController#import! Validation failed: Email has already been taken.
Controller:
class UploadsController < ApplicationController
def index
#uploads = Upload.all
end
def import
Upload.import(params[:file])
redirect_to uploads_path, notice: "Employee data imported!"
end
end
Model:
class Upload < ActiveRecord::Base
require 'csv'
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Employee.create! row.to_hash
end
end
end
Table:
create_table "employees", force: :cascade do |t|
t.string "last_name"
t.string "first_name"
t.string "employee_code"
t.string "email"
t.string "level"
t.string "dept"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I checked and and my database headers for employees do match my first row of the CSV file (beginning row 1, col 1). Any thoughts?
The keys of the hash you supply to Employee.create! (the CSV headers in this case) need to match exactly with the names of attributes on your model. Your problem is probably caused by one or more of the following:
There is a column where the header is not all lowercase. E.g., the CSV column name may be "Email" or "EMAIL" but should be "email".
There is a column where the header has spaces in it. E.g., the CSV column name may be "first name" or "last name" but should be "first_name" or "last_name", respectively.
There is a column in the CSV in which the column header is misspelled or otherwise doesn't belong to your table schema in the db at all. (Though you have said all of your CSV column headers match the database table columns, maybe check again if the problem is not solved by the first two bullets.)
Any one of these will cause the ActiveModel::UnknownAttributeError you are seeing. If the problem is being caused by one or multiple of the above then you can obviously solve the problem by editing your CSV file to make sure every header is lowercase with underscores instead of spaces. Alternatively, you could pass in the :header_converters => :symbol option to CSV.foreach like this:
CSV.foreach(file.path, headers: true, header_converters: :symbol) do |row|
Employee.create! row.to_hash
end
This option will transform your headers by converting them to lowercase, replacing spaces with underscores, and then converting them to symbols. This may be a more reliable option if you have a lot of column headers to tidy up, or if you will frequently receive the files with "slightly bad" headers suffering from one of the problems described above.
EDIT
I see you updated your question with more detail about the error. Rails is telling you that there is a column with header "last_name" in your CSV but there is no such column in your database (i.e. no such attribute on your Employee model). As Muhamad suggested, you could create a migration to add the last_name column to Employee. Can you verify that your employees table in the database has this column?

Resources