Class: Effective::CSVImporter

Inherits:
Object
  • Object
show all
Defined in:
app/models/effective/csv_importer.rb

Constant Summary collapse

A =
0
B =
1
C =
2
D =
3
E =
4
F =
5
G =
6
H =
7
I =
8
J =
9
K =
10
L =
11
M =
12
N =
13
O =
14
P =
15
Q =
16
R =
17
S =
18
T =
19
U =
20
V =
21
W =
22
X =
23
Y =
24
Z =
25
AA =
26
AB =
27
AC =
28
AD =
29
AE =
30
AF =
31
AG =
32
AH =
33
AI =
34
AJ =
35
AK =
36
AL =
37
AM =
38
AN =
39
AO =
40
AP =
41
AQ =
42
AR =
43
AS =
44
AT =
45

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(csv_file = default_csv_file(), header: true) ⇒ CSVImporter

Returns a new instance of CSVImporter.



12
13
14
15
16
17
# File 'app/models/effective/csv_importer.rb', line 12

def initialize(csv_file = default_csv_file(), header: true)
  @has_header_row = header

  @csv_file = csv_file
  raise "#{@csv_file} does not exist" unless File.exists?(@csv_file)
end

Instance Attribute Details

#csv_file ⇒ Object (readonly)

Returns the value of attribute csv_file.



5
6
7
# File 'app/models/effective/csv_importer.rb', line 5

def csv_file
  @csv_file
end

#current_row ⇒ Object (readonly)

Returns the value of attribute current_row.



5
6
7
# File 'app/models/effective/csv_importer.rb', line 5

def current_row
  @current_row
end

#last_row ⇒ Object (readonly)

Returns the value of attribute last_row.



5
6
7
# File 'app/models/effective/csv_importer.rb', line 5

def last_row
  @last_row
end

Instance Method Details

#after_import ⇒ Object



29
# File 'app/models/effective/csv_importer.rb', line 29

def after_import ; end

#assign_columns(obj, only: [], except: []) ⇒ Object

Takes an object and loops through all columns assigning the current row values



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'app/models/effective/csv_importer.rb', line 111

def assign_columns(obj, only: [], except: [])
  assigns = (
    if only.present?
      only = Array(only)
      columns.keep_if { |key, _| only.include?(key) }
    elsif except.present?
      except = Array(except)
      columns.delete_if { |key, _| except.include?(key) }
    end
  )

  (assigns || columns).each do |column, _|
    obj.send("#{column}=", col(column)) if obj.respond_to?(column)
  end

  obj
end

#before_import ⇒ Object

Override me if you need some before/after hooks



28
# File 'app/models/effective/csv_importer.rb', line 28

def before_import ; end

#columns ⇒ Object



19
20
21
# File 'app/models/effective/csv_importer.rb', line 19

def columns
  raise "Please define a method 'def columns' returning a Hash of {id: A, name: B}"
end

#error(message) ⇒ Object



133
134
135
136
# File 'app/models/effective/csv_importer.rb', line 133

def error(message)
  @errors_count += 1
  puts "\n#{colorize('Error', :red)} (.csv line #{@current_row_number}) #{message}"
end

#find(attributes) ⇒ Object



70
71
72
# File 'app/models/effective/csv_importer.rb', line 70

def find(attributes)
  where(attributes).first
end

#find!(attributes) ⇒ Object



74
75
76
# File 'app/models/effective/csv_importer.rb', line 74

def find!(attributes)
  find(attributes).presence || raise("csv row with #{attributes} not found")
end

#import! ⇒ Object

This runs through each row and calls process_row() on it



32
33
34
35
36
37
38
39
40
41
42
# File 'app/models/effective/csv_importer.rb', line 32

def import!
  log "Importing #{csv_file.split('/').last.sub('.csv', '')}"

  @errors_count = 0

  before_import()
  with_each_row { process_row }
  after_import()

  log "Import complete (#{@errors_count} errors in #{@has_header_row ? @current_row_number-1 : @current_row_number} rows)"
end

#log(message) ⇒ Object



129
130
131
# File 'app/models/effective/csv_importer.rb', line 129

def log(message)
  puts "\n#{message}";
end

#normalize(column, value) ⇒ Object

Normalize the value based on column name



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'app/models/effective/csv_importer.rb', line 79

def normalize(column, value)
  column = column.to_s
  value = value.to_s

  if column.ends_with?('?')  # Boolean
    ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
  elsif column.ends_with?('_at')  # DateTime
    parse_datetime(column, value)
  elsif column.ends_with?('_on')  # Date
    parse_datetime(column, value).beginning_of_day
  elsif column.ends_with?('_to_i')
    value.to_i
  elsif column.ends_with?('_to_f')
    value.to_f
  elsif column.ends_with?('_to_s')
    value.to_s
  elsif column.ends_with?('_to_a')
    if ['[]', '{}'].include?(value)
      []
    elsif value.starts_with?('{') && value.ends_with?('}')
      YAML::load(value).keys.select { |str| str.to_s.present? }
    else
      YAML::load(value).to_a.select { |str| str.to_s.present? }
    end
  elsif column == 'id' || column.ends_with?('_id')
    value.present? ? value.to_i : nil
  else
    value.presence
  end
end

#process_row ⇒ Object



23
24
25
# File 'app/models/effective/csv_importer.rb', line 23

def process_row
  raise "Please define a method 'def process_row' to process your row"
end

#rows ⇒ Object

Returns an Array of Arrays, with each row run through normalize



45
46
47
48
49
50
51
# File 'app/models/effective/csv_importer.rb', line 45

def rows
  @rows ||= [].tap do |rows|
    CSV.foreach(csv_file, headers: @has_header_row) do |row|
      rows << columns.map { |column, index| normalize(column, row[index].try(:strip).presence) }
    end
  end
end

#warn(message) ⇒ Object



138
139
140
# File 'app/models/effective/csv_importer.rb', line 138

def warn(message)
  puts "\n#{colorize('Warning', :yellow)} (.csv line #{@current_row_number}) #{message}"
end

#where(attributes) ⇒ Object

UserStudentInfosImporter.new().where(id: 3, title: 'thing') Returns an Array of Hashes, representing any row that matches the selector



55
56
57
58
59
60
61
62
63
64
# File 'app/models/effective/csv_importer.rb', line 55

def where(attributes)
  raise 'expected a Hash of attributes' unless attributes.kind_of?(Hash)
  attributes.each { |column, _| raise "unknown column :#{column}" unless columns.key?(column) }

  rows.map do |row|
    if attributes.all? { |column, value| row[columns[column]] == value }
      columns.inject({}) { |retval, (column, index)| retval[column] = row[index]; retval }
    end
  end.compact
end

#where!(attributes) ⇒ Object



66
67
68
# File 'app/models/effective/csv_importer.rb', line 66

def where!(attributes)
  where(attributes).presence || raise("csv row with #{attributes} not found")
end