cirandas.net

ref: master

script/import_data


#!/usr/bin/env ruby

require 'rubygems'
require "#{File.dirname(__FILE__)}/../config/environment"

def parse_opts
  puts "please provide the yaml configuration file" if ARGV.blank?
  $config_file = ARGV[0]
  $config = YAML.load File.read($config_file)

  $options = $config['options']
  $models_options = $config['models_options']

  $sources = $config['sources']

  $targets = $config['targets']

  $mutations = {}

  $renews = $config['renews']

  $record_ids = {}
  $id_records = {}

  $temp_files = []
end

class UploadedData
  attr_accessor :path, :content_type, :size
  def original_filename
    File.basename self.path
  end
end

class ActiveRecord::Base
  yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"

  def self.yaml_new klass, tag, val
    attributes = val.delete 'attributes'
    attributes = YAML.load attributes
    id = attributes.delete 'id'
    type = klass.name
    base_type = klass.base_class.name

    # target
    target_type = $targets[type] || $targets[base_type] || {}
    if target_id = target_type[id]
      record = klass.find target_id
    elsif target_other = target_type['other']
      record = klass.find target_other
    else
      record = klass.new
    end

    # renew
    renew_type = $renews[base_type] || {}

    # id <-> record
    $record_ids[record] = id
    $id_records[type] ||= {}
    $id_records[base_type] ||= {}
    $id_records[type][id] = record
    $id_records[base_type][id] = record

    # embeded_file
    embeded_file = val.delete 'embeded_file'
    if $options['embed_files'] and embeded_file
      embeded_file = YAML.load embeded_file 

      u = UploadedData.new; u.size = attributes['size']; u.path = attributes['filename']; u.content_type = attributes['content_type']
      File.open(u.path, "wb"){ |file| file.write embeded_file }
      record.uploaded_data = u

      $temp_files << u.path
    end

    # attributes
    attributes.each do |attr, value|
      # target
      if target_attr = target_type[attr] || $targets[attr]
        if not (new_value = target_attr[value]).nil?
          value = new_value
        elsif not (other_value = target_attr['other']).nil?
          value = other_value
        end
      end

      # renew
      if renew_attr = renew_type[attr]
        $mutations[record] ||= []

        renew_base_type = renew_attr['base_type']
        if renew_association = renew_attr['association']
          vdup = value.dup rescue value #why can't value be used?
          $mutations[record] << (proc do
            association = $id_records[renew_base_type][vdup] rescue nil
            record.send "#{renew_association}=", association rescue nil
          end)
        end

        value = nil
      end

      record[attr] = value
    end

    val.each do |method, value|
      # RECURSION
      value = YAML.load value
      val[method] = value

      # Fill parent
      value.to_a.each do |value|
        klass = value.class
        type = klass.name
        base_type = klass.base_class.name
        target_type = $targets[type] || $targets[base_type] || {}

        if belongs_to_association = target_type['belongs_to']
          value.send "#{belongs_to_association}=", record
        end
      end
    end

    record
  end

  def call_save
    self.class.record_timestamps = false

    method = if ($models_options[self.class.base_class.name]['save_without_callbacks'] rescue nil) then 'create_without_callbacks' else 'create_without_timestamps' end
    begin
      self.send method
    rescue Exception => e
      puts "Could not save: #{self.inspect}"
      raise
    end

    if $options['embed_files'] and self.respond_to? :full_filename
      raise "Could not save data of: #{self.inspect}" unless File.exists? self.full_filename
    end
  end

end

def apply_renews
  $mutations.each do |record, mutations|
    mutations.each{ |mutation| mutation.call }
  end
end

def run
  parse_opts

  ActiveRecord::Base.transaction do
    YAML.load STDIN.read

    # renew ids with associated records
    apply_renews

    # save top sources
    $sources.to_a.each do |source|
      record = $id_records[source['type']][source['id']]
      record.call_save
      pp record
    end

    # save others
    $record_ids.keys.each do |record|
      next if not record.new_record?
      record.call_save
      pp record
    end

    # delete temporary dump on Rails.root
    $temp_files.each do |filename|
      File.delete filename rescue nil
    end

    #raise 'test before!'
  end
end

run