mirror of
https://github.com/fspc/BikeShed-1.git
synced 2025-02-28 08:43:23 -05:00
BikeCsvImporter: import bikes, first step (main code, shop_id)
This commit is contained in:
parent
904b824c8b
commit
5564a1931f
113
app/models/bike_csv_importer.rb
Normal file
113
app/models/bike_csv_importer.rb
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
require 'csv'
|
||||||
|
|
||||||
|
class BikeCsvImporter
|
||||||
|
attr_reader :file
|
||||||
|
|
||||||
|
def initialize(file)
|
||||||
|
@file = file
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
result = {imported_count: 0, skipped_shop_ids: []}
|
||||||
|
fetch do |bike_hash|
|
||||||
|
bike = import_bike bike_hash
|
||||||
|
if bike.try :persisted?
|
||||||
|
result[:imported_count] += 1
|
||||||
|
else
|
||||||
|
result[:skipped_shop_ids].push bike.try(:shop_id) || bike_hash.values.first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def analyze(fields = [])
|
||||||
|
fields = fields.map &:downcase
|
||||||
|
grouped = {}
|
||||||
|
fetch do |bike_hash|
|
||||||
|
bike_hash.each do |key, value|
|
||||||
|
next if fields.any? && !fields.include?(key)
|
||||||
|
grouped[key] ||= {}
|
||||||
|
grouped[key][value] ||= 0
|
||||||
|
grouped[key][value] += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
grouped
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch
|
||||||
|
CSV.foreach(file).each_with_index do |row, i|
|
||||||
|
if i.zero?
|
||||||
|
parse_header row
|
||||||
|
else
|
||||||
|
yield parse_bike(row)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_header(row)
|
||||||
|
@header = row.map(&:downcase)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_bike(row)
|
||||||
|
@header.zip(row).to_h
|
||||||
|
end
|
||||||
|
|
||||||
|
# + Velocipede Number -> Bikes.shop_id
|
||||||
|
# Program -> Bikes.bike_purpose_id
|
||||||
|
#
|
||||||
|
# Gone -> If "Yes", set 'gone' to true, then create a Log entry like the following:
|
||||||
|
# id | loggable_id | loggable_type | logger_id | logger_type | context | start_date | end_date | description | log_action_id | log_action_type | created_at | updated_at
|
||||||
|
# 18 | 1 | Bike | 4 | User | | 2017-02-03 23:27:00 | 2017-02-03 23:27:00 | Gone | 5 | ActsAsLoggable::BikeAction | 2017-02-03 23:27:36.8387 | 2017-02-03 23:27:36.8387
|
||||||
|
# See https://github.com/spacemunkay/BikeShed/blob/master/app/components/bike_logs.rb#L12-L18 for example. Use user_id 1 for current_user_id (1 should be the admin ID I think). Use "Date Out" column for start_date & end_date. Set action_id to "COMPLETED".
|
||||||
|
#
|
||||||
|
# Date In -> Create a bike log entry with start_date & end_date with same value as "Date In". Set action_id to "AQUIRED"
|
||||||
|
# Date Out -> Should be the start_date & end_date value for "Gone" column mentioned above.
|
||||||
|
# Price -> Bikes.value
|
||||||
|
# Make -> Bikes.bike_brand_id
|
||||||
|
# Model -> Bikes.bike_model_id
|
||||||
|
# to Whom -> ignore
|
||||||
|
# Zip Code -> ignore
|
||||||
|
# Comment -> Create a bike log entry with action_id "NOTE". The log 'description' should be the value of 'Comment'.
|
||||||
|
#
|
||||||
|
# Data at the end of the CSV seems to be missing a lot of fields. If any field is empty, the the value can be "UNDETERMINED" if applicable, or ignored. Any other dates beside "Date In/Out" can be current date.
|
||||||
|
#
|
||||||
|
# I realize the log entry stuff is likely complicated and time consuming. At a minimum, the most important columns to import are the following: Velocipede Number, Program, Gone, Make, Model. To avoid creating the log entry for "Gone", we would instead just set 'gone' to true.
|
||||||
|
def import_bike(bike_hash)
|
||||||
|
bike = Bike.new bike_attrs(bike_hash)
|
||||||
|
bike.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def bike_attrs(bike_hash)
|
||||||
|
%i{ shop_id bike_purpose gone }.each_with_object({}) do |field, memo|
|
||||||
|
memo[field] = send :"bike_attr_#{ field }", bike_hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bike_attr_shop_id(bike_hash)
|
||||||
|
bike_hash['velocipede number'].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def bike_attr_bike_purpose(bike_hash)
|
||||||
|
# TODO bike_hash['program']
|
||||||
|
end
|
||||||
|
|
||||||
|
def bike_attr_gone(bike_hash)
|
||||||
|
%w{ yes yeah y }.include? clean_value(bike_hash['gone']).try :downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_value(value)
|
||||||
|
value_or_nil strip_value(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def strip_value(value)
|
||||||
|
value.try(:strip).try(:gsub, /\n|\r/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def value_or_nil(value)
|
||||||
|
return value unless ['?', 'n/a', 'missing', 'unknown', ''].include? value.try(:downcase)
|
||||||
|
end
|
||||||
|
end
|
@ -1,67 +0,0 @@
|
|||||||
require 'csv'
|
|
||||||
|
|
||||||
class BikesCsvImporter
|
|
||||||
attr_reader :file
|
|
||||||
|
|
||||||
def initialize(file)
|
|
||||||
@file = file
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
fetch do |bike_hash|
|
|
||||||
import_bike bike_hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def analyze(fields = [])
|
|
||||||
fields = fields.map &:downcase
|
|
||||||
grouped = {}
|
|
||||||
fetch do |bike_hash|
|
|
||||||
bike_hash.each do |key, value|
|
|
||||||
next if fields.any? && !fields.include?(key.downcase)
|
|
||||||
grouped[key] ||= {}
|
|
||||||
grouped[key][value] ||= 0
|
|
||||||
grouped[key][value] += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
grouped
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def fetch
|
|
||||||
CSV.foreach(file).each_with_index do |row, i|
|
|
||||||
if i.zero?
|
|
||||||
parse_header row
|
|
||||||
else
|
|
||||||
yield parse_bike(row)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_header(row)
|
|
||||||
@header = row
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_bike(row)
|
|
||||||
@header.zip(row).to_h
|
|
||||||
end
|
|
||||||
|
|
||||||
def import_bike(bike_hash)
|
|
||||||
# TODO
|
|
||||||
end
|
|
||||||
|
|
||||||
def clean_value(value)
|
|
||||||
value_or_nil strip_value(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def strip_value(value)
|
|
||||||
value.try(:strip).try(:gsub, /\n|\r/, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def value_or_nil(value)
|
|
||||||
return value unless ['?', 'n/a', 'missing', ''].include? value.try(:downcase)
|
|
||||||
end
|
|
||||||
end
|
|
@ -5,7 +5,7 @@ namespace :import do
|
|||||||
file = args[:file]
|
file = args[:file]
|
||||||
next puts "Usage: rake #{t.name}[$csv_file_path]" unless file
|
next puts "Usage: rake #{t.name}[$csv_file_path]" unless file
|
||||||
next puts "File #{file} does not exist or is unreachable" unless File.readable? file
|
next puts "File #{file} does not exist or is unreachable" unless File.readable? file
|
||||||
pp BikesCsvImporter.new(file).run
|
pp BikeCsvImporter.new(file).run
|
||||||
end
|
end
|
||||||
|
|
||||||
# Analyze a single field from CSV file
|
# Analyze a single field from CSV file
|
||||||
@ -13,7 +13,7 @@ namespace :import do
|
|||||||
file, field = args.values_at :file, :field
|
file, field = args.values_at :file, :field
|
||||||
next puts "Usage: rake #{t.name}[$csv_file_path[,\"$field_name\"]]" unless file
|
next puts "Usage: rake #{t.name}[$csv_file_path[,\"$field_name\"]]" unless file
|
||||||
next puts "File #{file} does not exist or is unreachable" unless File.readable? file
|
next puts "File #{file} does not exist or is unreachable" unless File.readable? file
|
||||||
pp BikesCsvImporter.new(file).analyze field ? [field] : []
|
pp BikeCsvImporter.new(file).analyze field ? [field] : []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user