table_importer 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/table_importer/excel.rb +3 -2
- data/lib/table_importer/source.rb +63 -56
- data/lib/table_importer/version.rb +1 -1
- data/spec/files/excel/mediaprofiler.xls +0 -0
- data/spec/sources/excel_spec.rb +89 -66
- data/spec/spec_helper.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4846a1f4ca1046589631a63ca0dd6a5663e1f8df
|
4
|
+
data.tar.gz: a7e213f005b115da0a057cd4287712a4e49576d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1afd54cd4806f1506a9374d8e8d1701e25df2f88dea9fe22d4bb08c7d00767cae3a2226a352728ddcb8868a367332932f222957819bacf8cec3074d7346f4a05
|
7
|
+
data.tar.gz: 9fc08b213c24acd5c46d8d9223f90cf3cf40ae77e4108deb569b8e5d47fd9c48497e345bf4718d984d39049d4bc7bb1cd3aea47948ac435c34d2092199dd1094
|
data/lib/table_importer/excel.rb
CHANGED
@@ -44,11 +44,11 @@ module TableImporter
|
|
44
44
|
|
45
45
|
def get_preview_lines(start_point = 0, end_point = 10)
|
46
46
|
begin
|
47
|
+
@headers = @mapping.present? && @mapping != false ? convert_headers : @headers
|
47
48
|
lines = clean_chunks([get_lines(start_point, end_point)], @compulsory_headers)[0][:lines]
|
48
49
|
if lines.first.nil?
|
49
50
|
get_preview_lines(start_point+10, end_point+10)
|
50
51
|
else
|
51
|
-
@headers = @mapping.present? ? convert_headers : @headers
|
52
52
|
lines[0..8]
|
53
53
|
end
|
54
54
|
rescue SystemStackError
|
@@ -69,8 +69,9 @@ module TableImporter
|
|
69
69
|
def convert_headers
|
70
70
|
new_headers = @headers_present ? @file.row(1) : default_headers
|
71
71
|
new_headers = default_headers(new_headers.count)
|
72
|
+
return new_headers unless @mapping
|
72
73
|
@mapping.each do |key, value|
|
73
|
-
if value.to_i.to_s == value
|
74
|
+
if value.to_i.to_s == value.to_s
|
74
75
|
new_headers[value.to_i] = key.to_sym
|
75
76
|
end
|
76
77
|
end
|
@@ -75,23 +75,7 @@ module TableImporter
|
|
75
75
|
result = []
|
76
76
|
empty_headers = chunks.first.first.keys
|
77
77
|
chunks.each do |chunk|
|
78
|
-
new_chunk =
|
79
|
-
chunk.each_with_index do |line, index|
|
80
|
-
line, line_empty = line_empty?(line)
|
81
|
-
no_compulsory_headers, missing_header = check_compulsory_headers?(line, compulsory_headers)
|
82
|
-
if line_empty || no_compulsory_headers
|
83
|
-
new_chunk[:errors] << format_error(line, line_empty, no_compulsory_headers, compulsory_headers, missing_header)
|
84
|
-
else
|
85
|
-
if delete_empty_columns
|
86
|
-
line.each do |key, value|
|
87
|
-
if value.present? && value.to_s.gsub(/[^A-Za-z0-9]/, '').present?
|
88
|
-
empty_headers.delete(clean_item(key).to_sym)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
new_chunk[:lines] << line
|
93
|
-
end
|
94
|
-
end
|
78
|
+
new_chunk, empty_headers = process_line(chunk, empty_headers, compulsory_headers, delete_empty_columns)
|
95
79
|
result << new_chunk unless new_chunk[:lines] == [] && new_chunk[:errors] == []
|
96
80
|
end
|
97
81
|
if delete_empty_columns
|
@@ -100,64 +84,87 @@ module TableImporter
|
|
100
84
|
result
|
101
85
|
end
|
102
86
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
87
|
+
private
|
88
|
+
|
89
|
+
def process_line(chunk, empty_headers, compulsory_headers, delete_empty_columns)
|
90
|
+
new_chunk = {:lines => [], :errors => []}
|
91
|
+
chunk.each_with_index do |line, index|
|
92
|
+
line, line_empty = line_empty?(line)
|
93
|
+
no_compulsory_headers, missing_header = check_compulsory_headers?(line, compulsory_headers) unless line_empty || compulsory_headers.blank?
|
94
|
+
if line_empty || no_compulsory_headers
|
95
|
+
new_chunk[:errors] << format_error(line, line_empty, no_compulsory_headers, compulsory_headers, missing_header)
|
96
|
+
else
|
97
|
+
check_empty_headers(line, empty_headers) if delete_empty_columns
|
98
|
+
new_chunk[:lines] << line
|
99
|
+
end
|
107
100
|
end
|
101
|
+
return new_chunk, empty_headers
|
102
|
+
end
|
108
103
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
104
|
+
def check_empty_headers(line, empty_headers)
|
105
|
+
line.each do |key, value|
|
106
|
+
if value.present? && value.to_s.gsub(/[^A-Za-z0-9]/, '').present?
|
107
|
+
empty_headers.delete(clean_item(key).to_sym)
|
113
108
|
end
|
114
|
-
map
|
115
109
|
end
|
110
|
+
end
|
116
111
|
|
117
|
-
|
118
|
-
|
119
|
-
|
112
|
+
def line_empty?(line)
|
113
|
+
line = clean_line(line)
|
114
|
+
return line, line.all?{ |item_key, item_value| line_item_is_garbage?(item_value)} && line.all?{ |item_key, item_value| line_item_is_garbage?(item_key)}
|
115
|
+
end
|
120
116
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
return true, "email" if line[:email].nil? || !line[:email].to_s.match(/@\S/)
|
126
|
-
end
|
127
|
-
return true, "email" if !line.values.any?{ |value| /@\S/ =~ value.to_s }
|
128
|
-
end
|
129
|
-
# here perform other checks for other compulsory headers we might have.
|
130
|
-
return false
|
117
|
+
def clean_line(line)
|
118
|
+
map = {}
|
119
|
+
line.each_pair do |key,value|
|
120
|
+
map[clean_item(key).to_sym] = clean_item(value)
|
131
121
|
end
|
122
|
+
map
|
123
|
+
end
|
132
124
|
|
133
|
-
|
134
|
-
|
135
|
-
|
125
|
+
def clean_item(item)
|
126
|
+
item.to_s.delete("\u0000").to_s.delete("\x00")
|
127
|
+
end
|
128
|
+
|
129
|
+
def check_compulsory_headers?(line, compulsory_headers)
|
130
|
+
if compulsory_headers.key?(:email)
|
131
|
+
if line.key?(:email)
|
132
|
+
line[:email] = clean_email(line[:email])
|
133
|
+
return true, "email" if line[:email].nil? || !line[:email].to_s.match(/@\S/)
|
136
134
|
end
|
135
|
+
return true, "email" if !line.values.any?{ |value| /@\S/ =~ value.to_s }
|
137
136
|
end
|
137
|
+
# here perform other checks for other compulsory headers we might have.
|
138
|
+
return false
|
139
|
+
end
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
+
def clean_email(email)
|
142
|
+
if email
|
143
|
+
email.to_s.gsub(/\A[^A-Za-z0-9]/, '').reverse.gsub(/\A[^A-Za-z0-9]/, '').reverse
|
141
144
|
end
|
145
|
+
end
|
142
146
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
147
|
+
def line_item_is_garbage?(item_value)
|
148
|
+
item_value.blank?
|
149
|
+
end
|
147
150
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
151
|
+
def format_error(line, line_empty, no_compulsory_headers, compulsory_headers, missing_header)
|
152
|
+
message = line_empty ? "it did not have any content" : " it did not contain this/these required headers: #{missing_header}"
|
153
|
+
{:level => :error, :message => "The following line was not imported because #{message}.", :data => {:line => line, :line_empty => line_empty, :headers => no_compulsory_headers}}
|
154
|
+
end
|
155
|
+
|
156
|
+
def remove_empty_columns(chunks, headers)
|
157
|
+
chunks.each do |chunk|
|
158
|
+
if chunk[:lines].present?
|
159
|
+
headers.each do |header|
|
160
|
+
chunk[:lines][0][header] = "empty_column"
|
154
161
|
end
|
155
162
|
end
|
156
|
-
chunks
|
157
163
|
end
|
164
|
+
chunks
|
165
|
+
end
|
158
166
|
end
|
159
167
|
end
|
160
|
-
|
161
168
|
require 'table_importer/csv'
|
162
169
|
require 'table_importer/copy_and_paste'
|
163
170
|
require 'table_importer/excel'
|
Binary file
|
data/spec/sources/excel_spec.rb
CHANGED
@@ -5,105 +5,111 @@ require 'roo'
|
|
5
5
|
describe TableImporter::Source do
|
6
6
|
|
7
7
|
context 'when source is an xls file with headers' do
|
8
|
+
context 'when mapping has not been set' do
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
before(:each) do
|
11
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/with_headers.xls"].join), :headers_present => true, :user_headers => nil, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
it "gets the preview lines" do
|
15
|
+
@source.get_preview_lines.count.should eql(5)
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
it "has the correct type" do
|
19
|
+
@source.get_type.should eql("xls")
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
after(:each) do
|
23
|
+
@source = nil
|
24
|
+
end
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
-
source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/with_headers.xls"].join), :headers_present => true, :headers => {"first_name"=>"", "last_name"=>"", "salutation"=>"", "tag_list"=>"", "email"=>"0", "organization"=>"", "url"=>"", "phone"=>"", "job_title"=>"", "second_url"=>"", "notes"=>"", "twitter_username"=>"", "skype_username"=>"", "pinterest_username"=>"", "instagram_username"=>"", "facebook_username"=>"", "last_name_prefix"=>"", "second_email"=>"", "phone_mobile"=>"", "street"=>"", "street_number"=>"", "zipcode"=>"", "city"=>"", "country"=>""}, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
28
|
-
source.get_chunks(2).count.should eql(4)
|
29
|
-
end
|
27
|
+
context 'when mapping has been set' do
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
(last_chunk[:lines].count + last_chunk[:errors].count).should eql(2)
|
35
|
-
end
|
29
|
+
before(:each) do
|
30
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/with_headers.xls"].join), :headers_present => true, :headers_present => {"first_name"=>"", "last_name"=>"", "salutation"=>"", "tag_list"=>"", "email"=>"0", "organization"=>"", "url"=>"", "phone"=>"", "job_title"=>"", "second_url"=>"", "notes"=>"", "twitter_username"=>"", "skype_username"=>"", "pinterest_username"=>"", "instagram_username"=>"", "facebook_username"=>"", "last_name_prefix"=>"", "second_email"=>"", "phone_mobile"=>"", "street"=>"", "street_number"=>"", "zipcode"=>"", "city"=>"", "country"=>""}, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
it "has the correct headers" do
|
34
|
+
@source.get_headers.should eql([:ExternalEmailAddress, :Name, :FirstName, :LastName, :StreetAddress, :City, :StateorProvince, :PostalCode, :Phone, :MobilePhone, :Pager, :HomePhone, :Company, :Title, :OtherTelephone, :Department, :CountryOrRegion, :Fax, :Initials, :Notes, :Office, :Manager])
|
35
|
+
end
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
it "has the correct number of lines" do
|
38
|
+
@source.get_chunks(1).count.should eql(6)
|
39
|
+
end
|
44
40
|
|
45
|
-
|
46
|
-
|
41
|
+
it "has the correct number of chunks" do
|
42
|
+
@source.get_chunks(2).count.should eql(4)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "does not have extra spaces in the final chunk" do
|
46
|
+
last_chunk = @source.get_chunks(4).last
|
47
|
+
(last_chunk[:lines].count + last_chunk[:errors].count).should eql(2)
|
48
|
+
end
|
49
|
+
|
50
|
+
after(:each) do
|
51
|
+
@source = nil
|
52
|
+
end
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
56
|
+
|
57
|
+
|
50
58
|
context 'when source is an xls file without headers' do
|
59
|
+
context 'when mapping has not been set' do
|
60
|
+
before(:each) do
|
61
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/without_headers.xls"].join), :headers_present => false, :user_headers => nil, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
62
|
+
end
|
51
63
|
|
52
|
-
|
53
|
-
|
54
|
-
|
64
|
+
it "has the correct number of columns" do
|
65
|
+
@source.get_headers.count.should eql(100)
|
66
|
+
end
|
55
67
|
|
56
|
-
|
57
|
-
|
68
|
+
after(:each) do
|
69
|
+
@source = nil
|
70
|
+
end
|
58
71
|
end
|
59
72
|
|
60
|
-
|
61
|
-
@source.get_headers.count.should eql(100)
|
62
|
-
end
|
73
|
+
context 'when mapping has been set' do
|
63
74
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
75
|
+
before(:each) do
|
76
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/without_headers.xls"].join), :headers_present => false, :user_headers => {"first_name"=>"", "last_name"=>"", "salutation"=>"", "tag_list"=>"", "email"=>"0", "organization"=>"", "url"=>"", "phone"=>"", "job_title"=>"", "second_url"=>"", "notes"=>"", "twitter_username"=>"", "skype_username"=>"", "pinterest_username"=>"", "instagram_username"=>"", "facebook_username"=>"", "last_name_prefix"=>"", "second_email"=>"", "phone_mobile"=>"", "street"=>"", "street_number"=>"", "zipcode"=>"", "city"=>"", "country"=>""}, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
77
|
+
end
|
68
78
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
79
|
+
it "has the correct number of lines" do
|
80
|
+
@source.get_chunks(1).count.should eql(6)
|
81
|
+
end
|
73
82
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
(last_chunk[:lines].count + last_chunk[:errors].count).should eql(3)
|
78
|
-
end
|
83
|
+
it "has the correct number of chunks" do
|
84
|
+
@source.get_chunks(2).count.should eql(4)
|
85
|
+
end
|
79
86
|
|
80
|
-
|
81
|
-
|
87
|
+
it "does not have extra spaces in the final chunk" do
|
88
|
+
last_chunk = @source.get_chunks(4).last
|
89
|
+
(last_chunk[:lines].count + last_chunk[:errors].count).should eql(2)
|
90
|
+
end
|
91
|
+
|
92
|
+
after(:each) do
|
93
|
+
@source = nil
|
94
|
+
end
|
82
95
|
end
|
83
96
|
end
|
84
97
|
|
85
98
|
context 'when source is an edge-case xls file without headers' do
|
86
99
|
before(:each) do
|
87
|
-
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/edge_cases.xls"].join), :headers_present => false, :
|
88
|
-
end
|
89
|
-
|
90
|
-
it "creates a source object" do
|
91
|
-
TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/edge_cases.xls"].join), :headers_present => false, :headers => nil, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
100
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/edge_cases.xls"].join), :headers_present => false, :user_headers => {"first_name"=>"", "last_name"=>"", "salutation"=>"", "tag_list"=>"", "email"=>"0", "organization"=>"", "url"=>"", "phone"=>"", "job_title"=>"", "second_url"=>"", "notes"=>"", "twitter_username"=>"", "skype_username"=>"", "pinterest_username"=>"", "instagram_username"=>"", "facebook_username"=>"", "last_name_prefix"=>"", "second_email"=>"", "phone_mobile"=>"", "street"=>"", "street_number"=>"", "zipcode"=>"", "city"=>"", "country"=>""}, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
92
101
|
end
|
93
102
|
|
94
103
|
it "has the correct number of lines" do
|
95
|
-
source
|
96
|
-
source.get_chunks(1).count.should eql(13)
|
104
|
+
@source.get_chunks(1).count.should eql(13)
|
97
105
|
end
|
98
106
|
|
99
107
|
it "has the correct number of chunks" do
|
100
|
-
source
|
101
|
-
source.get_chunks(4).count.should eql(4)
|
108
|
+
@source.get_chunks(4).count.should eql(4)
|
102
109
|
end
|
103
110
|
|
104
111
|
it "does not have extra spaces in the final chunk" do
|
105
|
-
|
106
|
-
last_chunk = source.get_chunks(4).last
|
112
|
+
last_chunk = @source.get_chunks(4).last
|
107
113
|
(last_chunk[:lines].count + last_chunk[:errors].count).should eql(1)
|
108
114
|
end
|
109
115
|
|
@@ -115,7 +121,7 @@ describe TableImporter::Source do
|
|
115
121
|
context 'when source has empty lines' do
|
116
122
|
|
117
123
|
before(:each) do
|
118
|
-
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/empty_lines.xlsx"].join), :headers_present => false, :
|
124
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/empty_lines.xlsx"].join), :headers_present => false, :user_headers => nil, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
119
125
|
end
|
120
126
|
|
121
127
|
it "does not throw an error" do
|
@@ -130,7 +136,7 @@ describe TableImporter::Source do
|
|
130
136
|
context 'when source has 20 empty lines at the beginning' do
|
131
137
|
|
132
138
|
before(:each) do
|
133
|
-
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/empty_lines_at_start.xlsx"].join), :headers_present => true, :
|
139
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/empty_lines_at_start.xlsx"].join), :headers_present => true, :user_headers => nil, :user_headers => nil, :type => "xls", :column_separator => "", :record_separator => "", :compulsory_headers => {:email => true}})
|
134
140
|
end
|
135
141
|
|
136
142
|
it "does not throw an error" do
|
@@ -146,10 +152,27 @@ describe TableImporter::Source do
|
|
146
152
|
|
147
153
|
it 'raises an error when creating a source object' do
|
148
154
|
begin
|
149
|
-
TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/no_content.xlsx"].join), :
|
155
|
+
TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/no_content.xlsx"].join), :headers_present => "false", :type => "xls", :column_separator => "", :record_separator => ""})
|
150
156
|
rescue TableImporter::EmptyFileImportError => e
|
151
157
|
e.message
|
152
158
|
end
|
153
159
|
end
|
154
160
|
end
|
161
|
+
|
162
|
+
context 'mediaprofiler' do
|
163
|
+
|
164
|
+
before(:each) do
|
165
|
+
@source = TableImporter::Source.new({:content => File.open([Dir.pwd, "/spec/files/excel/mediaprofiler.xls"].join), :headers_present => "true", :type => "xls", :column_separator => "", :record_separator => "",
|
166
|
+
:user_headers => {:first_name=>0, :last_name_prefix=>1, :last_name=>2, :organization=>3, :email=>5, :second_email=>6, :phone=>7, :phone_mobile=>8, :twitter_username=>9, :url=>10, :street=>11, :street_number=>12, :zipcode=>13, :country=>18}
|
167
|
+
})
|
168
|
+
end
|
169
|
+
|
170
|
+
it "has correct mapping" do
|
171
|
+
@source.get_preview_lines.first.keys.first.should == :first_name
|
172
|
+
end
|
173
|
+
|
174
|
+
after(:each) do
|
175
|
+
@source = nil
|
176
|
+
end
|
177
|
+
end
|
155
178
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_importer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dowse
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: spreadsheet
|
@@ -169,6 +169,7 @@ files:
|
|
169
169
|
- spec/files/excel/edge_cases.xls
|
170
170
|
- spec/files/excel/empty_lines.xlsx
|
171
171
|
- spec/files/excel/empty_lines_at_start.xlsx
|
172
|
+
- spec/files/excel/mediaprofiler.xls
|
172
173
|
- spec/files/excel/no_content.xlsx
|
173
174
|
- spec/files/excel/with_headers.xls
|
174
175
|
- spec/files/excel/without_headers.xls
|
@@ -213,6 +214,7 @@ test_files:
|
|
213
214
|
- spec/files/excel/edge_cases.xls
|
214
215
|
- spec/files/excel/empty_lines.xlsx
|
215
216
|
- spec/files/excel/empty_lines_at_start.xlsx
|
217
|
+
- spec/files/excel/mediaprofiler.xls
|
216
218
|
- spec/files/excel/no_content.xlsx
|
217
219
|
- spec/files/excel/with_headers.xls
|
218
220
|
- spec/files/excel/without_headers.xls
|