Today I need to import from a CSV file about 14500 records. Doing it the old way isn't a great idea, the server will be overloaded and the result may be unesxpected!
Here Loops take the field, let's figure out how with an example.
In Lipsiadmin create Loops is very simple, in the root of your project:
$ script/generate loopYou will also find a "simple_loop.rb" file that contains a loop with a debug line to test the loop.
The config file contains some variable, the most important is poll_period, change it with the second you want your loop to start.
global: logger: stdout poll_period: 30 workers_engine: fork
Now in the console you can start your loop in foreground with:
$ script/loops -a$ script/loops -hSo we are able to make a loop, let's see a good example on how to use it.
In my case i need to import a list of accounts, so create an account_import model
$ script/generate model account_importwe add to the migration: timestamps log and completed_at
class CreateAccountImports < ActiveRecord::Migration def self.up create_table :account_imports do |t| t.text :log t.datetime :completed_at t.timestamps end end def self.down drop_table :account_imports end end
then we generate the lipsiadmin backend page
$ script/generate backend_page account_importand add to the model an attachment, remember to generate the Lipsiadmin attachment if you did't do it yet.
class AccountImport < ActiveRecord::Base has_one_attachment :client, :dependent => :destroy end
and modify the "app/viwe/backend/account_imports/_form" with the file field:
... -tab :general do %table -unless @account_import.new_record? File Name: =link_to @account_import.client.attached_file_name, @account_import.client.url ="( #{number_to_human_size(@account_import.client.attached_file_size)} )" .box=file_field_tag "account_import[client_attributes][file]"
remember that for file upload you need to add multipart => true to new and edit pages
-form_tag({:action => :create}, :method => :post, :multipart => true) do
=render :partial => "form"The upload part is complete, now we need to add the part that take the CSV file, parse it and create the accounts.
Guess who do this? Loops
Create a new file named "acount_import_loop.rb" in "app/loops"
class AccountImportLoop < Lipsiadmin::Loops::Base def run # we search for the first record that isn't completed if import = AccountImport.first( :conditions => "completed_at IS NULL") import.update_attributes( :log => "Import Running...") # update the log file = "#{Rails.root}/public" + import.client.url # set file variable with uploaded file position csv = FasterCSV.read(file, { :col_sep => ";", :skip_blanks => true }) # I used FasterCSV for parsing import_log = "Import of #{csv.size} Client Started" import_log << "---------------------------------------" # the cycle that import accounts csv.each_with_index do |row, index| account = Account.find_by_client_code(row[0].strip) || Account.new account.client_code = row[0].strip .... # Row matching if !account.save import_log << "Import Client #{index+1}/#{csv.size} FAILED" end end import_log << "---------------------------------------" import_log << "Import of #{csv.size} Clients Done" import.update_attributes( :log => import_log, :completed_at => Time.now ) end end end
Add this loop to configuration file "/config/loop.yml"
....
loops:
account_import_loop:
workers_number: 1The part that do the import is complete, we can test it by creating a new AccountImport record.
To test the loop start it manually with:
$ script/loops -aIn production we can't start the loop manually, so we can create a task to do the job. Create a file in "/lib/taks/import.rake"
namespace :loop do desc "Start import loop" task :start => :stop do #start all loops in background as daemons exec "#{RAILS_ROOT}/script/loops -a -d" end desc "Stop import loop" task :stop do system "#{RAILS_ROOT}/script/loops -s" puts "Stop Loop... DONE" end end
Tip: When you try to stop the loop the program wait one cycle, the one defined in the poll period, so if you set an high poll, for example 3600 you need to wait that period before the loop stop.
That's all! We have a loop started by a rake task, that search if there is a database record with an uploaded CSV of clients every 30 seconds and create or update Accounts definitions.
