3 changed files with 328 additions and 0 deletions
@ -0,0 +1,70 @@
|
||||
require "csv" |
||||
|
||||
class BulkUpload::Sales::Year2022::CsvParser |
||||
MIN_COLUMNS = 125 |
||||
MAX_COLUMNS = 126 |
||||
|
||||
attr_reader :path |
||||
|
||||
def initialize(path:) |
||||
@path = path |
||||
end |
||||
|
||||
def row_offset |
||||
with_headers? ? 5 : 0 |
||||
end |
||||
|
||||
def col_offset |
||||
with_headers? ? 1 : 0 |
||||
end |
||||
|
||||
def cols |
||||
@cols ||= ("A".."DV").to_a |
||||
end |
||||
|
||||
def row_parsers |
||||
@row_parsers ||= body_rows.map do |row| |
||||
stripped_row = row[col_offset..] |
||||
headers = ("field_1".."field_125").to_a |
||||
hash = Hash[headers.zip(stripped_row)] |
||||
|
||||
BulkUpload::Lettings::Year2022::RowParser.new(hash) |
||||
end |
||||
end |
||||
|
||||
def body_rows |
||||
rows[row_offset..] |
||||
end |
||||
|
||||
def rows |
||||
@rows ||= CSV.parse(normalised_string, row_sep:) |
||||
end |
||||
|
||||
def column_for_field(field) |
||||
cols[headers.find_index(field) + col_offset] |
||||
end |
||||
|
||||
private |
||||
|
||||
def headers |
||||
@headers ||= ("field_1".."field_125").to_a |
||||
end |
||||
|
||||
def with_headers? |
||||
rows[0][0]&.match?(/\D+/) |
||||
end |
||||
|
||||
def row_sep |
||||
"\n" |
||||
end |
||||
|
||||
def normalised_string |
||||
return @normalised_string if @normalised_string |
||||
|
||||
@normalised_string = File.read(path, encoding: "bom|utf-8") |
||||
@normalised_string.gsub!("\r\n", "\n") |
||||
@normalised_string.scrub!("") |
||||
|
||||
@normalised_string |
||||
end |
||||
end |
||||
@ -0,0 +1,97 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe BulkUpload::Sales::Year2022::CsvParser do |
||||
subject(:service) { described_class.new(path:) } |
||||
|
||||
let(:path) { file_fixture("2022_23_sales_bulk_upload.csv") } |
||||
|
||||
context "when parsing csv with headers" do |
||||
it "returns correct offsets" do |
||||
expect(service.row_offset).to eq(5) |
||||
expect(service.col_offset).to eq(1) |
||||
end |
||||
|
||||
it "parses csv correctly" do |
||||
expect(service.row_parsers[0].field_7.to_i).to eq(30) |
||||
end |
||||
end |
||||
|
||||
context "when parsing csv without headers" do |
||||
let(:file) { Tempfile.new } |
||||
let(:path) { file.path } |
||||
let(:log) { build(:sales_log, :completed) } |
||||
|
||||
before do |
||||
file.write(BulkUpload::LogToCsv.new(log:, col_offset: 0).to_2022_sales_csv_row) |
||||
file.rewind |
||||
end |
||||
|
||||
it "returns correct offsets" do |
||||
expect(service.row_offset).to eq(0) |
||||
expect(service.col_offset).to eq(0) |
||||
end |
||||
|
||||
it "parses csv correctly" do |
||||
expect(service.row_parsers[0].field_7.to_i).to eql(log.age1) |
||||
end |
||||
end |
||||
|
||||
context "when parsing with BOM aka byte order mark" do |
||||
let(:file) { Tempfile.new } |
||||
let(:path) { file.path } |
||||
let(:log) { build(:sales_log, :completed) } |
||||
let(:bom) { "\uFEFF" } |
||||
|
||||
before do |
||||
file.write(bom) |
||||
file.write(BulkUpload::LogToCsv.new(log:, col_offset: 0).to_2022_sales_csv_row) |
||||
file.close |
||||
end |
||||
|
||||
it "parses csv correctly" do |
||||
expect(service.row_parsers[0].field_7.to_i).to eql(log.age1) |
||||
end |
||||
end |
||||
|
||||
context "when an invalid byte sequence" do |
||||
let(:file) { Tempfile.new } |
||||
let(:path) { file.path } |
||||
let(:log) { build(:sales_log, :completed) } |
||||
let(:invalid_sequence) { "\x81" } |
||||
|
||||
before do |
||||
file.write(invalid_sequence) |
||||
file.write(BulkUpload::LogToCsv.new(log:, col_offset: 0).to_2022_sales_csv_row) |
||||
file.close |
||||
end |
||||
|
||||
it "parses csv correctly" do |
||||
expect(service.row_parsers[0].field_7.to_i).to eql(log.age1) |
||||
end |
||||
end |
||||
|
||||
describe "#column_for_field", aggregate_failures: true do |
||||
context "when headers present" do |
||||
it "returns correct column" do |
||||
expect(service.column_for_field("field_1")).to eql("B") |
||||
expect(service.column_for_field("field_125")).to eql("DV") |
||||
end |
||||
end |
||||
|
||||
context "when no headers" do |
||||
let(:file) { Tempfile.new } |
||||
let(:path) { file.path } |
||||
let(:log) { build(:sales_log, :completed) } |
||||
|
||||
before do |
||||
file.write(BulkUpload::LogToCsv.new(log:, col_offset: 0).to_2022_sales_csv_row) |
||||
file.rewind |
||||
end |
||||
|
||||
it "returns correct column" do |
||||
expect(service.column_for_field("field_1")).to eql("A") |
||||
expect(service.column_for_field("field_125")).to eql("DU") |
||||
end |
||||
end |
||||
end |
||||
end |
||||
Loading…
Reference in new issue