Skip to content

Commit 2139d24

Browse files
committed
Add generating html format
1 parent 0369043 commit 2139d24

File tree

4 files changed

+178
-13
lines changed

4 files changed

+178
-13
lines changed

src/fosslight_reuse/_constant.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,67 @@
88
OSS_PKG_INFO_FILES = ["oss-pkg-info.yaml", "oss-pkg-info.yml", r"oss-package*.info", "requirement.txt",
99
"requirements.txt", "package.json", "pom.xml", "build.gradle", "podfile.lock", "cartfile.resolved",
1010
"pubspec.yaml", "package.resolved", "go.mod", r"fosslight-sbom-info*.yaml"]
11+
12+
HTML_FORMAT_PREFIX = """
13+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd">
14+
<html lang="ko">
15+
<head>
16+
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
17+
<style>
18+
.marker{background-color: Yellow;}
19+
</style>
20+
<title>FOSSLight Reuse</title>
21+
</head>
22+
<body>
23+
<table cellspacing="0" cellpadding="0" border="0" style="font-family:'arial,sans-serif'">
24+
<tr>
25+
<td colspan="3" height="28" style="background:#f9f9f9"></td>
26+
</tr>
27+
<tr>
28+
<td width="60" style="background:#f9f9f9"></td>
29+
<td>
30+
<table cellspacing="0" cellpadding="0" border="0">
31+
<tr>
32+
<td colspan="3" height="25" style="background:#c00c3f">
33+
<h1 style="margin:0;padding-left:5px;font-size:14px;color:white">FOSSLight Reuse Lint</h1>
34+
</td>
35+
</tr>
36+
<tr>
37+
<td width="1" style="background:#ddd"></td>
38+
<td style="padding:40px;">
39+
<div style="padding-bottom:10px;font-size:16px;font-weight:bold;">"""
40+
41+
HTML_COMPLIANCE_SUFFIX = """
42+
</div>
43+
<p style="font-size:12px;">
44+
"""
45+
46+
HTML_CELL_PREFIX = """
47+
<h2 style="margin:20px 0 0;padding:10px;font-size:16px;">« Files without License or Copyright »</h2>
48+
<table cellspacing="0" cellpadding="0" width="100%" border="1" style="font-size:12px;border-color:#ddd;">
49+
<tr>
50+
<th style="padding:5px;background:#f0f0f0;">File</th>
51+
<th style="padding:5px;background:#f0f0f0;">License</th>
52+
<th style="padding:5px;background:#f0f0f0;">Copyright</th>
53+
</tr>"""
54+
55+
HTML_FORMAT_SUFFIX = """
56+
</table>
57+
<br/>
58+
</td>
59+
<td width="1" style="background:#ddd"></td>
60+
</tr>
61+
<tr>
62+
<td colspan="3" height="1" style="background:#ddd"></td>
63+
</tr>
64+
</table>
65+
</td>
66+
<td width="60" style="background:#f9f9f9"></td>
67+
</tr>
68+
<tr>
69+
<td colspan="3" height="28" style="background:#f9f9f9"></td>
70+
</tr>
71+
</table>
72+
</body>
73+
</html>
74+
"""

src/fosslight_reuse/_fosslight_reuse.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ def run_lint(target_path, disable, output_file_name, format='', need_log_file=Tr
342342
lic_present_files_in_yaml,
343343
cop_present_files_in_yaml)
344344

345-
success, exit_code = write_result_file(result_file, output_extension, _exit_code, result_item, _result_log)
345+
success, exit_code = write_result_file(result_file, output_extension, _exit_code,
346+
result_item, _result_log, project, path_to_find)
346347
if success:
347348
logger.warning(f"Created file name: {result_file}\n")
348349
else:

src/fosslight_reuse/_result.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
import xml.etree.ElementTree as ET
1010
import logging
1111
import fosslight_util.constant as constant
12-
from fosslight_util.output_format import check_output_format
1312
from pathlib import Path
13+
from reuse.project import Project
14+
from fosslight_reuse._result_html import result_for_html
15+
from fosslight_util.output_format import check_output_format
16+
1417

1518
CUSTOMIZED_FORMAT_FOR_REUSE = {'html': '.html', 'xml': '.xml', 'yaml': '.yaml'}
1619
RULE_LINK = "https://oss.lge.com/guide/process/osc_process/1-identification/copyright_license_rule.html"
@@ -27,6 +30,7 @@ def __init__(self):
2730
self._oss_pkg_files = []
2831
self._detected_licenses = []
2932
self._count_total_files = ""
33+
self._count_without_both = ""
3034
self._count_without_lic = ""
3135
self._count_without_cop = ""
3236
self._files_without_both = []
@@ -49,7 +53,7 @@ def compliant_result(self, value):
4953
if value:
5054
self._compliant_result = "OK"
5155
else:
52-
self._compliant_result = "Not OK"
56+
self._compliant_result = "Not-OK"
5357

5458
@property
5559
def oss_pkg_files(self):
@@ -59,7 +63,7 @@ def get_print_yaml(self):
5963
result_item = {}
6064
result_item["Compliant"] = self._compliant_result
6165
result_summary_item = {}
62-
result_summary_item["Open Source Package File"] = self._oss_pkg_files
66+
result_summary_item["Open Source Package File"] = is_list_empty(self._oss_pkg_files)
6367
result_summary_item["Detected Licenses"] = is_list_empty(self._detected_licenses)
6468
result_summary_item["Files without license / total"] = f"{self._count_without_lic} / {self._count_total_files}"
6569
result_summary_item["Files without copyright / total"] = f"{self._count_without_cop} / {self._count_total_files}"
@@ -133,7 +137,7 @@ def result_for_xml(result_item: ResultItem):
133137
return _root_xml_item
134138

135139

136-
def write_result_xml(result_file: str, exit_code: int, result_item: ResultItem, _result_log: str) -> None:
140+
def write_result_xml(result_file: str, exit_code: int, result_item: ResultItem, _result_log: str):
137141
success = False
138142
# Create a new XML file with the results
139143
try:
@@ -148,19 +152,22 @@ def write_result_xml(result_file: str, exit_code: int, result_item: ResultItem,
148152
return success, exit_code
149153

150154

151-
def write_result_html(result_file: str, exit_code: int, _result_log: str) -> None:
155+
def write_result_html(result_file: str, exit_code: int, result_item: ResultItem, project: Project, path_to_find):
152156
success = False
153157
try:
154158
output_dir = os.path.dirname(result_file)
155159
Path(output_dir).mkdir(parents=True, exist_ok=True)
160+
html_result = result_for_html(result_item, project, path_to_find)
161+
with open(result_file, 'w') as f:
162+
f.write(html_result)
156163
success = True
157164
except Exception as ex:
158165
logger.error(f"Error_to_write_html: {ex}")
159166
exit_code = os.EX_IOERR
160167
return success, exit_code
161168

162169

163-
def write_result_yaml(result_file: str, exit_code: int, result_item: ResultItem) -> None:
170+
def write_result_yaml(result_file: str, exit_code: int, result_item: ResultItem):
164171
success = False
165172
try:
166173
output_dir = os.path.dirname(result_file)
@@ -206,7 +213,7 @@ def create_result_file(output_file_name, format='', _start_time=""):
206213

207214

208215
def result_for_summary(oss_pkg_info_files, license_missing_files, copyright_missing_files,
209-
report, _result_log, _check_only_file_mode, file_to_check_list, error_items,
216+
prj_report, _result_log, _check_only_file_mode, file_to_check_list, error_items,
210217
excluded_files, lic_present_files_in_yaml, cop_present_files_in_yaml):
211218
reuse_compliant = False
212219
detected_lic = []
@@ -216,9 +223,9 @@ def result_for_summary(oss_pkg_info_files, license_missing_files, copyright_miss
216223
if _check_only_file_mode:
217224
file_total = len(file_to_check_list)
218225
else:
219-
file_total = len(report.file_reports)
226+
file_total = len(prj_report.file_reports)
220227
# Get detected License
221-
for i, lic in enumerate(sorted(report.used_licenses)):
228+
for i, lic in enumerate(sorted(prj_report.used_licenses)):
222229
detected_lic.append(lic)
223230

224231
# Subtract license or copyright presenting file and excluded file
@@ -245,6 +252,7 @@ def result_for_summary(oss_pkg_info_files, license_missing_files, copyright_miss
245252
result_item._oss_pkg_files = oss_pkg_info_files
246253
result_item._detected_licenses = detected_lic
247254
result_item._count_total_files = file_total
255+
result_item._count_without_both = str(len(missing_both_files))
248256
result_item._count_without_lic = str(len(license_missing_files) + len(missing_both_files))
249257
result_item._count_without_cop = str(len(copyright_missing_files) + len(missing_both_files))
250258
result_item._files_without_both = sorted(missing_both_files)
@@ -259,12 +267,12 @@ def result_for_summary(oss_pkg_info_files, license_missing_files, copyright_miss
259267
return result_item
260268

261269

262-
def write_result_file(result_file, output_extension, exit_code, result_item, _result_log):
270+
def write_result_file(result_file, output_extension, exit_code, result_item, _result_log, project, path_to_find):
263271
success = False
264272
if output_extension == ".yaml" or output_extension == "":
265273
success, exit_code = write_result_yaml(result_file, exit_code, result_item)
266274
elif output_extension == ".html":
267-
success, exit_code = write_result_html(result_file, exit_code, _result_log)
275+
success, exit_code = write_result_html(result_file, exit_code, result_item, project, path_to_find)
268276
elif output_extension == ".xml":
269277
success, exit_code = write_result_xml(result_file, exit_code, result_item, _result_log)
270278
else:
@@ -273,6 +281,6 @@ def write_result_file(result_file, output_extension, exit_code, result_item, _re
273281
if success:
274282
# Print yaml result
275283
yaml_result = result_item.get_print_yaml()
276-
str_yaml_result = yaml.safe_dump(yaml_result, allow_unicode=True, sort_keys=True)
284+
str_yaml_result = yaml.safe_dump(yaml_result, allow_unicode=True, sort_keys=False)
277285
logger.info(str_yaml_result)
278286
return success, exit_code
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2022 LG Electronics Inc.
4+
# SPDX-License-Identifier: GPL-3.0-only
5+
import os
6+
from reuse import report
7+
from reuse.project import Project
8+
from fosslight_reuse._constant import HTML_FORMAT_PREFIX, HTML_CELL_PREFIX, HTML_FORMAT_SUFFIX, HTML_COMPLIANCE_SUFFIX
9+
10+
11+
def get_html_summary(result_item):
12+
pkg_file_str = ""
13+
detected_lic_str = ""
14+
if result_item._oss_pkg_files:
15+
for pkg_file in result_item._oss_pkg_files:
16+
pkg_file_str += f"<br>&nbsp;&nbsp;&nbsp; - {pkg_file}"
17+
else:
18+
pkg_file_str = 'N/A'
19+
20+
if result_item._detected_licenses:
21+
for detected_lic in result_item._detected_licenses:
22+
detected_lic_str += f"<br>&nbsp;&nbsp;&nbsp; - {detected_lic}"
23+
else:
24+
detected_lic_str = 'N/A'
25+
26+
html_lint_str = f"""
27+
- Open Source Package file: {pkg_file_str}</br>
28+
- Detected licenses: {detected_lic_str}</br>
29+
- Files without copyright / total: {result_item._count_without_cop} / {result_item._count_total_files}</br>
30+
- Files without license / total: {result_item._count_without_lic} / {result_item._count_total_files}</br></p>
31+
"""
32+
return html_lint_str
33+
34+
35+
def get_html_compliance(result_item):
36+
return f"Compliant: {result_item.compliant_result}{HTML_COMPLIANCE_SUFFIX}"
37+
38+
39+
def get_num_of_not_compliant(result_item):
40+
return int(result_item._count_without_lic) + int(result_item._count_without_cop)
41+
42+
43+
def get_file_report(project, path_to_find, file):
44+
file_abs_path = os.path.abspath(os.path.join(path_to_find, file))
45+
file_rep = report.FileReport.generate(project, file_abs_path)
46+
return file_rep
47+
48+
49+
def get_html_cell(result_item, project: Project, path_to_find):
50+
cell_str = ""
51+
for both_file in result_item._files_without_both:
52+
cell_str += f"""<tr>
53+
<td style="padding:5px;">{both_file}</td>
54+
<td style="padding:5px;"></td>
55+
<td style="padding:5px;"></td>
56+
</tr>"""
57+
58+
for file in result_item._files_without_lic:
59+
file_rep = get_file_report(project, path_to_find, file)
60+
cop_text = file_rep.spdxfile.copyright.replace("SPDX-FileCopyrightText: ", "")
61+
cell_str += f"""<tr>
62+
<td style="padding:5px;">{file}</td>
63+
<td style="padding:5px;"></td>
64+
<td style="padding:5px;">{cop_text}</td>
65+
</tr>"""
66+
67+
for file in result_item._files_without_cop:
68+
file_rep = get_file_report(project, path_to_find, file)
69+
lic_in_file = ', '.join(file_rep.spdxfile.licenses_in_file)
70+
cell_str += f"""<tr>
71+
<td style="padding:5px;">{file}</td>
72+
<td style="padding:5px;">{lic_in_file}</td>
73+
<td style="padding:5px;"></td>
74+
</tr>"""
75+
return cell_str
76+
77+
78+
def result_for_html(result_item, project: Project, path_to_find):
79+
compliance_str = get_html_compliance(result_item)
80+
summary_str = get_html_summary(result_item)
81+
cell_contents_str = ""
82+
83+
if get_num_of_not_compliant(result_item) <= 100:
84+
cell_contents_str = get_html_cell(result_item, project, path_to_find)
85+
html_in_str = f"{HTML_FORMAT_PREFIX}{compliance_str}{summary_str}{HTML_CELL_PREFIX}{cell_contents_str}{HTML_FORMAT_SUFFIX}"
86+
else:
87+
cell_contents_str = "<b> &#9888; There are so many incompliant files, so result can't be printed in html. </b>"
88+
html_in_str = f"{HTML_FORMAT_PREFIX}{compliance_str}{summary_str}{cell_contents_str}{HTML_FORMAT_SUFFIX}"
89+
90+
# if result_item.execution_error:
91+
# TODO: How do handle for execution_error in html format?
92+
return html_in_str

0 commit comments

Comments
 (0)