Skip to content

Commit f499419

Browse files
committed
Fixed #482 - Support excel output for inventory
* add encoding error handling (i.e. errors='replace') Signed-off-by: Chin Yeung Li <[email protected]>
1 parent ef8f9e9 commit f499419

File tree

8 files changed

+34
-41
lines changed

8 files changed

+34
-41
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
* Add Dockerfile to run aboutcode with docker
1414
* Add new option to choose extract license from ScanCode LicenseDB or DJC License Library
1515
* Add ability to transform Excel formatted file
16-
* Generate attribution directly from CSV/JSON/Excel file
17-
* Generate ABOUT files with excel as an input
16+
* Support Excel file format for `inventory`, `gen` and `attrib`
1817

1918
2021-04-02
2019
Release 6.0.0

src/attributecode/cmd.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def validate_extensions(ctx, param, value, extensions=tuple(('.csv', '.json',)))
139139

140140

141141
@about.command(cls=AboutCommand,
142-
short_help='Collect the inventory of .ABOUT files to a CSV or JSON file.')
142+
short_help='Collect the inventory of .ABOUT files to a CSV/JSON/Excel file.')
143143

144144
@click.argument('location',
145145
required=True,
@@ -156,7 +156,7 @@ def validate_extensions(ctx, param, value, extensions=tuple(('.csv', '.json',)))
156156
is_flag=False,
157157
default='csv',
158158
show_default=True,
159-
type=click.Choice(['json', 'csv']),
159+
type=click.Choice(['json', 'csv', 'excel']),
160160
help='Set OUTPUT inventory file format.')
161161

162162
@click.option('-q', '--quiet',
@@ -170,11 +170,11 @@ def validate_extensions(ctx, param, value, extensions=tuple(('.csv', '.json',)))
170170
@click.help_option('-h', '--help')
171171
def inventory(location, output, format, quiet, verbose): # NOQA
172172
"""
173-
Collect the inventory of ABOUT file data as CSV or JSON.
173+
Collect the inventory of .ABOUT files to a CSV/JSON/Excel file.
174174
175175
LOCATION: Path to an ABOUT file or a directory with ABOUT files.
176176
177-
OUTPUT: Path to the JSON or CSV inventory file to create.
177+
OUTPUT: Path to the CSV/JSON/Excel inventory file to create.
178178
"""
179179
if not quiet:
180180
print_version()
@@ -184,8 +184,8 @@ def inventory(location, output, format, quiet, verbose): # NOQA
184184
# accept zipped ABOUT files as input
185185
location = extract_zip(location)
186186
errors, abouts = collect_inventory(location)
187-
write_errors = write_output(abouts=abouts, location=output, format=format)
188-
errors.extend(write_errors)
187+
write_output(abouts=abouts, location=output, format=format)
188+
189189
errors = unique(errors)
190190
errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log')
191191
if not quiet:

src/attributecode/model.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from attributecode import Error
5050
from attributecode import saneyaml
5151
from attributecode import util
52+
from attributecode.transform import write_excel
5253
from attributecode.util import add_unc
5354
from attributecode.util import boolean_fields
5455
from attributecode.util import copy_license_notice_files
@@ -986,7 +987,7 @@ def load(self, location):
986987
errors = []
987988
try:
988989
loc = add_unc(loc)
989-
with io.open(loc, encoding='utf-8') as txt:
990+
with io.open(loc, encoding='utf-8', errors='replace') as txt:
990991
input_text = txt.read()
991992
# The 'Yes' and 'No' will be converted to 'True' and 'False' in the yaml.load()
992993
# Therefore, we need to wrap the original value in quote to prevent
@@ -1513,34 +1514,28 @@ def write_output(abouts, location, format): # NOQA
15131514
about_dicts = about_object_to_list_of_dictionary(abouts)
15141515
location = add_unc(location)
15151516
if format == 'csv':
1516-
errors = save_as_csv(location, about_dicts, get_field_names(abouts))
1517+
save_as_csv(location, about_dicts, get_field_names(abouts))
1518+
elif format == 'json':
1519+
save_as_json(location, about_dicts)
15171520
else:
1518-
errors = save_as_json(location, about_dicts)
1519-
return errors
1520-
1521+
save_as_excel(location, about_dicts)
15211522

15221523
def save_as_json(location, about_dicts):
15231524
with io.open(location, mode='w') as output_file:
15241525
data = util.format_about_dict_for_json_output(about_dicts)
15251526
output_file.write(json.dumps(data, indent=2))
1526-
return []
1527-
15281527

15291528
def save_as_csv(location, about_dicts, field_names):
1530-
errors = []
15311529
with io.open(location, mode='w', encoding='utf-8', newline='') as output_file:
15321530
writer = csv.DictWriter(output_file, field_names)
15331531
writer.writeheader()
1534-
csv_formatted_list = util.format_about_dict_for_csv_output(about_dicts)
1532+
csv_formatted_list = util.format_about_dict_output(about_dicts)
15351533
for row in csv_formatted_list:
1536-
# See https://github.com/dejacode/about-code-tool/issues/167
1537-
try:
1538-
writer.writerow(row)
1539-
except Exception as e:
1540-
msg = u'Generation skipped for ' + row['about_file_path'] + u' : ' + str(e)
1541-
errors.append(Error(CRITICAL, msg))
1542-
return errors
1534+
writer.writerow(row)
15431535

1536+
def save_as_excel(location, about_dicts):
1537+
formatted_list = util.format_about_dict_output(about_dicts)
1538+
write_excel(location, formatted_list)
15441539

15451540
def pre_process_and_fetch_license_dict(abouts, api_url=None, api_key=None, scancode=False, reference=None):
15461541
"""

src/attributecode/util.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,8 @@ def ungroup_licenses(licenses):
486486

487487

488488
# FIXME: add docstring
489-
def format_about_dict_for_csv_output(about_dictionary_list):
490-
csv_formatted_list = []
489+
def format_about_dict_output(about_dictionary_list):
490+
formatted_list = []
491491
for element in about_dictionary_list:
492492
row_list = dict()
493493
for key in element:
@@ -498,8 +498,8 @@ def format_about_dict_for_csv_output(about_dictionary_list):
498498
row_list[key] = u'\n'.join((element[key].keys()))
499499
else:
500500
row_list[key] = element[key]
501-
csv_formatted_list.append(row_list)
502-
return csv_formatted_list
501+
formatted_list.append(row_list)
502+
return formatted_list
503503

504504

505505
# FIXME: add docstring

tests/test_model.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -850,14 +850,13 @@ def test_load_can_load_unicode(self):
850850
assert errors == a.errors
851851
assert 'Copyright (c) 2012, Domen Kožar' == a.copyright.value
852852

853-
def test_load_has_errors_for_non_unicode(self):
853+
def test_load_non_unicode(self):
854854
test_file = get_test_loc('test_model/unicode/not-unicode.ABOUT')
855855
a = model.About()
856856
a.load(test_file)
857857
err = a.errors[0]
858858
assert CRITICAL == err.severity
859859
assert 'Cannot load invalid ABOUT file' in err.message
860-
assert 'UnicodeDecodeError' in err.message
861860

862861
def test_as_dict_load_dict_ignores_empties(self):
863862
test = {
@@ -1206,8 +1205,7 @@ def test_collect_inventory_does_not_convert_lf_to_crlf_from_directory(self):
12061205
location = get_test_loc('test_model/crlf/about.ABOUT')
12071206
result = get_temp_file()
12081207
errors, abouts = model.collect_inventory(location)
1209-
errors2 = model.write_output(abouts, result, format='csv')
1210-
errors.extend(errors2)
1208+
model.write_output(abouts, result, format='csv')
12111209
assert all(e.severity == INFO for e in errors)
12121210

12131211
expected = get_test_loc('test_model/crlf/expected.csv')

tests/test_util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ def test_load_csv_does_convert_column_names_to_lowercase(self):
330330
result = util.load_csv(test_file)
331331
assert expected == result
332332

333-
def test_format_about_dict_for_csv_output(self):
333+
def test_format_about_dict_output(self):
334334
about = [dict([
335335
(u'about_file_path', u'/input/about1.ABOUT'),
336336
(u'about_resource', [u'test.c']),
@@ -345,7 +345,7 @@ def test_format_about_dict_for_csv_output(self):
345345
(u'license_expression', u'mit AND bsd-new'),
346346
(u'license_key', u'mit\nbsd-new')])]
347347

348-
output = util.format_about_dict_for_csv_output(about)
348+
output = util.format_about_dict_output(about)
349349
assert output == expected
350350

351351
def test_load_csv_microsoft_utf_8(self):

tests/testdata/test_cmd/help/about_help.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Commands:
1919
report errors and warnings.
2020
collect-redist-src Collect redistributable sources.
2121
gen Generate .ABOUT files from an inventory as CSV/JSON/Excel.
22-
inventory Collect the inventory of .ABOUT files to a CSV or JSON
22+
inventory Collect the inventory of .ABOUT files to a CSV/JSON/Excel
2323
file.
2424
transform Transform a CSV/JSON/Excel by applying renamings, filters
2525
and checks.
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
Usage: about inventory [OPTIONS] LOCATION OUTPUT
22

3-
Collect the inventory of ABOUT file data as CSV or JSON.
3+
Collect the inventory of .ABOUT files to a CSV/JSON/Excel file.
44

55
LOCATION: Path to an ABOUT file or a directory with ABOUT files.
66

7-
OUTPUT: Path to the JSON or CSV inventory file to create.
7+
OUTPUT: Path to the CSV/JSON/Excel inventory file to create.
88

99
Options:
10-
-f, --format [json|csv] Set OUTPUT inventory file format. [default: csv]
11-
-q, --quiet Do not print error or warning messages.
12-
--verbose Show all error and warning messages.
13-
-h, --help Show this message and exit.
10+
-f, --format [json|csv|excel] Set OUTPUT inventory file format. [default:
11+
csv]
12+
-q, --quiet Do not print error or warning messages.
13+
--verbose Show all error and warning messages.
14+
-h, --help Show this message and exit.

0 commit comments

Comments
 (0)