Commit 950b4433 by Ivan Lan

增加 import 测试

parent bb4f3054
...@@ -2,7 +2,7 @@ PATH ...@@ -2,7 +2,7 @@ PATH
remote: . remote: .
specs: specs:
tallty_import_export (0.1.0) tallty_import_export (0.1.0)
activesupport (~> 6.0.3) activesupport
caxlsx caxlsx
roo roo
roo-xls roo-xls
......
...@@ -8,7 +8,10 @@ module TalltyImportExport ...@@ -8,7 +8,10 @@ module TalltyImportExport
autoload :Common autoload :Common
autoload :Exportable autoload :Exportable
autoload :Export
autoload :Importable autoload :Importable
autoload :Import
autoload :Context
class Error < StandardError; end class Error < StandardError; end
end end
...@@ -3,7 +3,7 @@ module TalltyImportExport ...@@ -3,7 +3,7 @@ module TalltyImportExport
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do |base| included do |base|
base.include(Common) # base.include(Common)
base.const_set('Export', Class.new(TalltyImportExport::Export)) base.const_set('Export', Class.new(TalltyImportExport::Export))
end end
......
class Import module TalltyImportExport
attr_reader :klass, :content, :headers, :primary_keys class Import
require 'roo'
attr_reader :klass, :context, :headers, :primary_keys
def initialize klass def initialize klass
@klass = klass @klass = klass
@content = Context.new({}) @context = Context.new({})
end end
# key: 属性的英文名
# name: 属性的中文名
# attr_type: 属性的类型
# format: excel是否需要特定的格式,目前主要是类似于身份证号,可以用string
# convert: 导入时候,把excel的内容转换成导入时候代码逻辑需要的内容
# primary_key: 是否是主键
# key: 属性的英文名 def import_xlsx xlsx_file, associations, options={}
# name: 属性的中文名 # 先处理获取出来Excel每行的数据, line_info
# attr_type: 属性的类型 options = options.with_indifferent_access
# format: excel是否需要特定的格式,目前主要是类似于身份证号,可以用string
# convert: 导入时候,把excel的内容转换成导入时候代码逻辑需要的内容
# primary_key: 是否是主键
def import_xlsx xlsx_file, associations, options={} self.headers = options.delete(:headers) || import_headers
# 先处理获取出来Excel每行的数据, line_info @primary_keys = options.delete(:primary_keys) || headers.map { |header| header[:primary_key] ? header[:key].to_sym : nil }.compact
options = options.with_indifferent_access
excel_hash = headers.reduce({}) do |h, header|
h[header[:key]] = header[:name]
h
end
file_path = xlsx_file.is_a?(String) ? xlsx_file : xlsx_file.path
xlsx = ::Roo::Excelx.new(file_path)
xlsx.each_with_pagename do |_sheetname, sheet|
sheet.each(**excel_hash).with_index do |line_info, index|
next if index == 0
self.headers = options.delete(:headers) || import_headers # 转换处理导入的数据格式
self.primary_keys = options.delete(:primary_keys) || headers.map { |header| header[:primary_key] ? header[:key].to_sym : nil }.compact line_info = convert_data(line_info)
excel_hash = headers.reduce({}) do |h, header| # 处理每行对于导入的动作,处理line_info
h[header[:key]] = header[:name] import_record(line_info, associations)
h context.last_line_info = line_info
end
end
end end
file_path = xlsx_file.is_a?(String) ? xlsx_file : xlsx_file.path def convert_data line_info
xlsx = Roo::Excelx.new(file_path) line_info.with_indifferent_access.reduce({}) do |h, (k, v)|
xlsx.each_with_pagename do |_sheetname, sheet| header = headers.find do |_header|
sheet.each(**excel_hash).with_index do |line_info, index| _header[:key].to_sym == k.to_sym
next if index == 0 end
# header[:convert] = handle_xxx
# handle_xxx(val, processing_line_info, raw_line_info)
val = header[:convert] ? send(header[:convert], v, h, line_info) : v
if header[:json]
h[header[:json]] ||= {}
h[header[:json]][k] = val
else
h[k.to_sym] = val
end
h
end.with_indifferent_access
end
# 转换处理导入的数据格式 def import_headers
line_info = convert_data(line_info) klass.try(:headers) || klass.try(:model_headers)
end
# 处理每行对于导入的动作,处理line_info def headers= val
import_record(line_info, associations) @headers = val.map { |header| header.with_indifferent_access }
end
end end
end
def convert_data line_info def skip val, processing_line_info, raw_line_info
line_info.with_indifferent_access.reduce({}) do |h, (k, v)| # do nothing there, use for header[:convert]
header = headers.find do |_header| end
_header[:key].to_sym == k.to_sym
end ### 这个方法是可以由复杂业务进行重载的 ###
val = header[:convert] ? send(header[:convert], v) : v def import_record line_info, associations
if header[:json] if primary_keys.present?
h[header[:json]] ||= {} _record = associations.find_or_initialize_by(line_info.clone.extract!(*primary_keys))
h[header[:json]][k] = val _record.update!(line_info.clone.except!(*primary_keys))
else else
h[k.to_sym] = val associations.create!(line_info)
end end
h
end.with_indifferent_access
end
def import_headers
klass.try(:headers) || kless.try(:model_headers)
end
### 这个方法是可以由复杂业务进行重载的 ###
def import_record line_info, associations
if primary_keys.present?
_record = associations.find_or_initialize_by(line_info.clone.extract!(*primary_keys))
_record.update!(line_info.clone.except!(*primary_keys))
else
associations.create!(line_info)
end end
end end
end end
...@@ -3,13 +3,13 @@ module TalltyImportExport ...@@ -3,13 +3,13 @@ module TalltyImportExport
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do |base| included do |base|
base.include(Common) # base.include(Common)
base.const_set('Import', Class.new(TalltyImportExport::Import)) base.const_set('Import', Class.new(TalltyImportExport::Import))
end end
module ClassMethods module ClassMethods
def import_instance def import_instance
Import.new(self) const_get('Import').new(self)
end end
def import_xlsx *args def import_xlsx *args
......
RSpec.describe TalltyImportExport::Importable do
before do
class Test
include TalltyImportExport::Importable
class << self
def model_headers
[
{ key: 'name', name: '名称' },
{ key: 'code', name: '学号', convert: :handle_code },
{ key: 'sub_code', name: '副学号', convert: :skip},
{ key: 'meta1', name: 'meta字段1', json: :meta },
{ key: 'meta2', name: 'meta字段2', json: :meta },
]
end
end
class Import
def import_record line_info, associations
associations << line_info
end
def handle_code code, processing_line_info, raw_line_info
"#{code}_#{raw_line_info['sub_code']}"
end
end
end
end
it "has a version number" do
# 名称 学号 副学号 meta字段1 meta字段2
# 1 20070101 1 meta1 metaA
# 2 20070102 2 meta2 metaB
# 3 20070103 3 meta3 metaC
# 4 20070104 4 meta4 metaD
# 5 20070105 5 meta5 metaE
@result = []
Test.import_xlsx('./spec/test1.xlsx', @result)
expect(@result).to eq([
{"name"=>1, "code"=>"20070101_1", "sub_code"=>nil, "meta"=>{"meta1"=>"meta1", "meta2"=>"metaA"}},
{"name"=>2, "code"=>"20070102_2", "sub_code"=>nil, "meta"=>{"meta1"=>"meta2", "meta2"=>"metaB"}},
{"name"=>3, "code"=>"20070103_3", "sub_code"=>nil, "meta"=>{"meta1"=>"meta3", "meta2"=>"metaC"}},
{"name"=>4, "code"=>"20070104_4", "sub_code"=>nil, "meta"=>{"meta1"=>"meta4", "meta2"=>"metaD"}},
{"name"=>5, "code"=>"20070105_5", "sub_code"=>nil, "meta"=>{"meta1"=>"meta5", "meta2"=>"metaE"}},
])
end
end
...@@ -2,8 +2,4 @@ RSpec.describe TalltyImportExport do ...@@ -2,8 +2,4 @@ RSpec.describe TalltyImportExport do
it "has a version number" do it "has a version number" do
expect(TalltyImportExport::VERSION).not_to be nil expect(TalltyImportExport::VERSION).not_to be nil
end end
it "does something useful" do
expect(false).to eq(true)
end
end end
File added
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment