Class: Import

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/import.rb

Overview

Since:

  • 3.2.0

Direct Known Subclasses

IssueImport

Constant Summary collapse

DATE_FORMATS =
[
  '%Y-%m-%d',
  '%d/%m/%Y',
  '%m/%d/%Y',
  '%d.%m.%Y',
  '%d-%m-%Y'
]

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Import

Returns a new instance of Import



38
39
40
41
# File 'app/models/import.rb', line 38

def initialize(*args)
  super
  self.settings ||= {}
end

Instance Method Details

#add_callback(position, name, *args) ⇒ Object

Adds a callback that will be called after the item at given position is imported

Since:

  • 3.4.0



142
143
144
145
146
147
# File 'app/models/import.rb', line 142

def add_callback(position, name, *args)
  settings['callbacks'] ||= {}
  settings['callbacks'][position.to_i] ||= []
  settings['callbacks'][position.to_i] << [name, args]
  save!
end

#columns_options(default = nil) ⇒ Object

Returns the headers as an array that can be used for select options



94
95
96
97
# File 'app/models/import.rb', line 94

def columns_options(default=nil)
  i = -1
  headers.map {|h| [h, i+=1]}
end

#do_callbacks(position, object) ⇒ Object

Executes the callbacks for the given object

Since:

  • 3.4.0



150
151
152
153
154
155
156
157
# File 'app/models/import.rb', line 150

def do_callbacks(position, object)
  if callbacks = (settings['callbacks'] || {}).delete(position)
    callbacks.each do |name, args|
      send "#{name}_callback", object, *args
    end
    save!
  end
end

#file=(arg) ⇒ Object



43
44
45
46
47
48
# File 'app/models/import.rb', line 43

def file=(arg)
  return unless arg.present? && arg.size > 0

  self.filename = generate_filename
  Redmine::Utils.save_upload(arg, filepath)
end

#file_exists?Boolean

Returns true if the file to import exists

Returns:

  • (Boolean)


88
89
90
# File 'app/models/import.rb', line 88

def file_exists?
  filepath.present? && File.exists?(filepath)
end

#filepathObject

Returns the full path of the file to import It is stored in tmp/imports with a random hex as filename



79
80
81
82
83
84
85
# File 'app/models/import.rb', line 79

def filepath
  if filename.present? && filename =~ /\A[0-9a-f]+\z/
    File.join(Rails.root, "tmp", "imports", filename)
  else
    nil
  end
end

#first_rows(count = 4) ⇒ Object

Returns the count first rows of the file (including headers)



122
123
124
125
126
127
128
129
# File 'app/models/import.rb', line 122

def first_rows(count=4)
  rows = []
  read_rows do |row|
    rows << row
    break if rows.size >= count
  end
  rows
end

#headersObject

Returns an array of headers



132
133
134
# File 'app/models/import.rb', line 132

def headers
  first_rows(1).first || []
end

#mappingObject

Returns the mapping options



137
138
139
# File 'app/models/import.rb', line 137

def mapping
  settings['mapping'] || {}
end

#parse_fileObject

Parses the file to import and updates the total number of items



100
101
102
103
104
105
# File 'app/models/import.rb', line 100

def parse_file
  count = 0
  read_items {|row, i| count=i}
  update_attribute :total_items, count
  count
end

#read_itemsObject

Reads the items to import and yields the given block for each item



108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/models/import.rb', line 108

def read_items
  i = 0
  headers = true
  read_rows do |row|
    if i == 0 && headers
      headers = false
      next
    end
    i+= 1
    yield row, i if block_given?
  end
end

#run(options = {}) ⇒ Object

Imports items and returns the position of the last processed item



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'app/models/import.rb', line 160

def run(options={})
  max_items = options[:max_items]
  max_time = options[:max_time]
  current = 0
  imported = 0
  resume_after = items.maximum(:position) || 0
  interrupted = false
  started_on = Time.now

  read_items do |row, position|
    if (max_items && imported >= max_items) || (max_time && Time.now >= started_on + max_time)
      interrupted = true
      break
    end
    if position > resume_after
      item = items.build
      item.position = position

      if object = build_object(row, item)
        if object.save
          item.obj_id = object.id
        else
          item.message = object.errors.full_messages.join("\n")
        end
      end

      item.save!
      imported += 1

      do_callbacks(item.position, object)
    end
    current = position
  end

  if imported == 0 || interrupted == false
    if total_items.nil?
      update_attribute :total_items, current
    end
    update_attribute :finished, true
    remove_file
  end

  current
end

#saved_itemsObject



209
210
211
# File 'app/models/import.rb', line 209

def saved_items
  items.where("obj_id IS NOT NULL")
end

#set_default_settingsObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'app/models/import.rb', line 50

def set_default_settings
  separator = lu(user, :general_csv_separator)
  if file_exists?
    begin
      content = File.read(filepath, 256)
      separator = [',', ';'].sort_by {|sep| content.count(sep) }.last
    rescue Exception => e
    end
  end
  wrapper = '"'
  encoding = lu(user, :general_csv_encoding)

  date_format = lu(user, "date.formats.default", :default => "foo")
  date_format = DATE_FORMATS.first unless DATE_FORMATS.include?(date_format)

  self.settings.merge!(
    'separator' => separator,
    'wrapper' => wrapper,
    'encoding' => encoding,
    'date_format' => date_format
  )
end

#to_paramObject



73
74
75
# File 'app/models/import.rb', line 73

def to_param
  filename
end

#unsaved_itemsObject



205
206
207
# File 'app/models/import.rb', line 205

def unsaved_items
  items.where(:obj_id => nil)
end