Skip to content

Commit c7871d1

Browse files
Dp 2398 tind spread validate header (#43)
* validating header for tind spreadsheet validator * rubocop %w instead of quotes in array * Added some comments for header validation description for tind spread validator * Changed regular expression for tind spreadsheet header validation
1 parent 9e3de46 commit c7871d1

4 files changed

Lines changed: 93 additions & 17 deletions

File tree

app/lib/tind_spread/tind_batch.rb

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@ def format_errors
2727
end
2828

2929
def attachments(attachment_name)
30-
if @all_errors.empty?
31-
{ "#{attachment_name}.csv" => @csv.to_s }
32-
elsif @csv.count("\n") > 1
33-
{ "#{attachment_name}.csv" => @csv.to_s, "ERRORREPORT_#{attachment_name}.csv" => @errors_csv.to_s,
34-
"ERRORREPORT_#{attachment_name}.txt" => format_errors }
35-
else
36-
{ "ERRORREPORT_#{attachment_name}.csv" => @errors_csv.to_s,
37-
"ERRORREPORT_#{attachment_name}.txt" => format_errors }
30+
return { "ERRORREPORT_#{attachment_name}.txt" => format_errors } if @all_errors.key?(1)
31+
32+
return { "#{attachment_name}.csv" => @csv.to_s } if @all_errors.empty?
33+
34+
if @csv.count("\n") > 1
35+
return { "#{attachment_name}.csv" => @csv.to_s,
36+
"ERRORREPORT_#{attachment_name}.csv" => @errors_csv.to_s,
37+
"ERRORREPORT_#{attachment_name}.txt" => format_errors }
3838
end
39+
40+
{ "ERRORREPORT_#{attachment_name}.csv" => @errors_csv.to_s,
41+
"ERRORREPORT_#{attachment_name}.txt" => format_errors }
3942
end
4043

4144
# rubocop:disable Metrics/AbcSize
@@ -65,19 +68,37 @@ def create_rows(all_rows)
6568
end
6669
# rubocop:enable Metrics/MethodLength
6770

71+
def validate_header_row(headers)
72+
header_errors = []
73+
headers.each do |header|
74+
header_errors << "Invalid header name: #{header.gsub(/^\d+:/, '')}" unless TindSpread::TindValidation.valid_header?(header)
75+
end
76+
header_errors
77+
end
78+
79+
# rubocop:disable Metrics/MethodLength
6880
def run
6981
t = TindSpread::SpreadTool.new(@xlsx_path, @extension, @form_info[:directory])
7082
all_rows = t.spread
83+
@all_errors = {}
84+
85+
# Validate header row
86+
header_errors = validate_header_row(all_rows.first.keys)
87+
@all_errors[1] = header_errors if header_errors.any?
88+
89+
return send_email if header_errors.any?
90+
7191
@csv = TindSpread::MakeBatch.make_header(t.header(all_rows.first.keys), @form_info).encode('UTF-8')
7292
@errors_csv = TindSpread::MakeBatch.make_header(t.header(all_rows.first.keys), @form_info, remove_filename: false).encode('UTF-8')
73-
@all_errors = {}
93+
7494
create_rows(all_rows)
7595
@csv.to_s.gsub!("\xEF\xBB\xBF".force_encoding('UTF-8'), '')
7696
@errors_csv.to_s.gsub!("\xEF\xBB\xBF".force_encoding('UTF-8'), '')
7797
# File.write('output.csv', @csv)
7898
# File.write('errors.csv', @errors_csv)
7999
send_email
80100
end
101+
# rubocop:enable Metrics/MethodLength
81102
# rubocop:enable Metrics/AbcSize
82103
end
83104
end

app/lib/tind_spread/tind_validation.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
require 'net/http'
22
require 'open-uri'
33
module TindSpread
4+
# rubocop:disable Metrics/ModuleLength
45
module TindValidation
56

7+
# validates the header row
8+
# should be 3 digits for field, 2 for indicator (can be _, number), one digit or number for subfield
9+
# optionally can have a ('-' followed by a number). This is used to group columns into similar fields
10+
# the header row can also be just "Filename" or "FFT". The program will create the proper fields for those
11+
def self.valid_header?(str)
12+
str.match?(/\d{3}[_|\d]{2}[a-zA-Z0-9](-\d+)?$/) || str.match?(/Filename|FFT/i)
13+
end
14+
615
# runs a set of validations against a single row.
716
# Row should be an array of hashes, key being the column header for the row.
817
# rubocop:disable Metrics/MethodLength
@@ -28,6 +37,7 @@ def self.validate_row(row)
2837

2938
# private
3039
class << self
40+
3141
private
3242

3343
def filename_error(row, errors)
@@ -149,4 +159,5 @@ def valid_500__3?(key, row)
149159

150160
end
151161
end
162+
# rubocop:enable Metrics/ModuleLength
152163
end

spec/lib/tind_spread/tind_batch_spec.rb

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
let(:args) { { directory:, '982__a': 'test' } }
1616
let(:tind_batch) { described_class.new(args, xlsx, extension, email) }
1717
let(:spread_tool) { instance_double(TindSpread::SpreadTool) }
18-
let(:all_rows) { [{ 'Header1' => 'Data1', 'Header2' => 'Data2' }, { 'Header1' => 'Data3', 'Header2' => 'Data4' }] }
18+
let(:all_rows) { [{ '001__a' => 'Data1', '245__a' => 'Data2' }, { '001__a' => 'Data3', '245__a' => 'Data4' }] }
1919

2020
before do
2121
allow(TindSpread::SpreadTool).to receive(:new).with(xlsx, extension, directory).and_return(spread_tool)
2222
allow(spread_tool).to receive(:spread).and_return(all_rows)
23-
allow(spread_tool).to receive(:header).with(any_args).and_return(%w[Header1 Header2])
24-
allow(TindSpread::MakeBatch).to receive(:make_header).with(any_args).and_return("Header1,Header2\n")
23+
allow(spread_tool).to receive(:header).with(any_args).and_return(%w[001__a 245__a])
24+
allow(TindSpread::MakeBatch).to receive(:make_header).with(any_args).and_return("001__a,245__a\n")
2525
allow(TindSpread::MakeBatch).to receive(:add_row).with(any_args).and_return("Data1,Data2\n")
2626
allow(TindSpread::TindValidation).to receive(:validate_row).with(any_args).and_return([])
2727
# rubocop:disable RSpec/MessageChain
@@ -49,15 +49,15 @@
4949
describe '#send_email' do
5050
it 'sends an email with the correct attachments' do
5151
tind_batch.instance_variable_set(:@all_errors, {})
52-
tind_batch.instance_variable_set(:@csv, "Header1,Header2\nData1,Data2\n")
53-
tind_batch.instance_variable_set(:@errors_csv, "Header1,Header2\n")
52+
tind_batch.instance_variable_set(:@csv, "001__a,245__a\nData1,Data2\n")
53+
tind_batch.instance_variable_set(:@errors_csv, "001__a,245__a\n")
5454
allow(Time).to receive(:current).and_return(Time.parse('2023-10-01 12:00:00 UTC'))
5555
attachment_name = 'test_2023-10-01'
5656
expect(RequestMailer).to receive(:tind_spread_email).with(
5757
email,
5858
'Tind batch load for test',
5959
'No errors found',
60-
{ "#{attachment_name}.csv" => "Header1,Header2\nData1,Data2\n" }
60+
{ "#{attachment_name}.csv" => "001__a,245__a\nData1,Data2\n" }
6161
).and_return(double(deliver_now: true))
6262
tind_batch.send_email
6363
end
@@ -74,12 +74,34 @@
7474
end
7575
end
7676

77+
describe '#validate_header_row' do
78+
it 'returns an empty array for valid headers' do
79+
headers = %w[001__a 245__a 500__3]
80+
errors = tind_batch.validate_header_row(headers)
81+
expect(errors).to be_empty
82+
end
83+
84+
it 'returns error messages for invalid headers' do
85+
headers = %w[Header1 Header2]
86+
errors = tind_batch.validate_header_row(headers)
87+
expect(errors).to include('Invalid header name: Header1')
88+
expect(errors).to include('Invalid header name: Header2')
89+
end
90+
91+
it 'returns errors only for invalid headers in a mixed list' do
92+
headers = %w[001__a InvalidHeader 245__a]
93+
errors = tind_batch.validate_header_row(headers)
94+
expect(errors).to include('Invalid header name: InvalidHeader')
95+
expect(errors.length).to eq(1)
96+
end
97+
end
98+
7799
describe '#run' do
78100
it 'runs the batch process' do
79101
allow(tind_batch).to receive(:send_email)
80102
tind_batch.run
81-
expect(tind_batch.instance_variable_get(:@csv)).to eq("Header1,Header2\nData1,Data2\nData1,Data2\n")
82-
expect(tind_batch.instance_variable_get(:@errors_csv)).to eq("Header1,Header2\n")
103+
expect(tind_batch.instance_variable_get(:@csv)).to eq("001__a,245__a\nData1,Data2\nData1,Data2\n")
104+
expect(tind_batch.instance_variable_get(:@errors_csv)).to eq("001__a,245__a\n")
83105
end
84106
end
85107
end

spec/lib/tind_spread/tind_validation_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,26 @@
108108
expect(described_class.send(:corresponding_6?, '800__6', row)).to be false
109109
end
110110
end
111+
112+
describe '.valid_header?' do
113+
it 'returns true for a valid header with standard format' do
114+
expect(described_class.valid_header?('001__a')).to be true
115+
expect(described_class.valid_header?('245__a')).to be true
116+
expect(described_class.valid_header?('Filename')).to be true
117+
expect(described_class.valid_header?('100_1a')).to be true
118+
end
119+
120+
it 'returns true for a valid header with suffix format' do
121+
expect(described_class.valid_header?('001__a-1')).to be true
122+
expect(described_class.valid_header?('245__a-2')).to be true
123+
expect(described_class.valid_header?('500__3-5')).to be true
124+
end
125+
126+
it 'returns false for invalid headers' do
127+
expect(described_class.valid_header?('abc')).to be false
128+
expect(described_class.valid_header?('12__a')).to be false
129+
expect(described_class.valid_header?('001__a-')).to be false
130+
expect(described_class.valid_header?('001__a-ab')).to be false
131+
end
132+
end
111133
end

0 commit comments

Comments
 (0)