Skip to content

Commit ea13990

Browse files
committed
Merging latest master in
Merge branch 'master' into mkumykov-multi-image
2 parents 918e695 + 084b557 commit ea13990

File tree

1 file changed

+70
-6
lines changed

1 file changed

+70
-6
lines changed

examples/client/file_hierarchy_report.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,78 @@ def trim_version_report(version_report, reduced_path_set):
157157
reduced_aggregate_bom_view_entries = [e for e in aggregate_bom_view_entries if f"{e['producerProject']['id']}:{e['producerReleases'][0]['id']}" in deduplicated]
158158
version_report['aggregateBomViewEntries'] = reduced_aggregate_bom_view_entries
159159

160-
def write_output_file(version_report, output_file):
160+
'''
161+
162+
CSV output details
163+
164+
component name = aggregateBomViewEntries[].producerProject.name
165+
version name = aggregateBomViewEntries[].producerReleases[0].version
166+
license = licenses[].licenseDisplay
167+
file path = extract from detailedFileBomViewEntries
168+
match type = aggregateBomViewEntries[].matchTypes
169+
review status = aggregateBomViewEntries[].reviewSummary.reviewStatus
170+
171+
'''
172+
def get_csv_fieldnames():
173+
return ['component name', 'version name', 'license', 'match type', 'review status']
174+
175+
def get_csv_data(version_report, keep_dupes):
176+
csv_data = list()
177+
components = list()
178+
for bom_view_entry in version_report['aggregateBomViewEntries']:
179+
entry = dict()
180+
entry['component name'] = bom_view_entry['producerProject']['name']
181+
entry['version name'] = bom_view_entry['producerReleases'][0]['version']
182+
entry['license'] = bom_view_entry['licenses'][0]['licenseDisplay'].replace(' AND ',';').replace('(','').replace(')','')
183+
pid = bom_view_entry['producerProject']['id']
184+
vid = bom_view_entry['producerReleases'][0]['id']
185+
#path_list = [p['path'] for p in version_report['detailedFileBomViewEntries'] if p['projectId'] == pid and p['versionId'] == vid]
186+
#entry['file path'] = ';'.join(path_list)
187+
entry['match type'] = ';'.join(bom_view_entry['matchTypes'])
188+
entry['review status'] = bom_view_entry['reviewSummary']['reviewStatus']
189+
190+
# Only add if this component was not previously added.
191+
composite_key = pid + vid
192+
if composite_key not in components:
193+
csv_data.append(entry)
194+
components.append(composite_key)
195+
if keep_dupes:
196+
return csv_data
197+
else:
198+
return remove_duplicates(csv_data)
199+
200+
def remove_duplicates(data):
201+
# Put data into buckets by version
202+
buckets = dict()
203+
for row in data:
204+
name = row['component name'].lower()
205+
version = row['version name']
206+
if not version in buckets:
207+
buckets[version] = [row]
208+
else:
209+
buckets[version].append(row)
210+
# Run reduction process for component names that start with existing component name
211+
# This process will ignore case in component names
212+
for set in buckets.values():
213+
set.sort(key = lambda d: d['component name'].lower())
214+
for row in set:
215+
index = set.index(row)
216+
name = row['component name'].lower()
217+
while index + 1 < len(set) and set[index+1]['component name'].lower().startswith(name):
218+
set.pop(index+1)
219+
reduced_data = list()
220+
for b in buckets.values():
221+
reduced_data.extend(b)
222+
return reduced_data
223+
224+
def write_output_file(version_report, output_file, keep_dupes):
161225
if output_file.lower().endswith(".csv"):
162226
logging.info(f"Writing CSV output into {output_file}")
163-
field_names = list(version_report['aggregateBomViewEntries'][0].keys())
227+
field_names = get_csv_fieldnames()
164228
with open(output_file, "w") as f:
165-
writer = csv.DictWriter(f, fieldnames = field_names, extrasaction = 'ignore') # TODO
229+
writer = csv.DictWriter(f, fieldnames = field_names, extrasaction = 'ignore',quoting=csv.QUOTE_ALL) # TODO
166230
writer.writeheader()
167-
writer.writerows(version_report['aggregateBomViewEntries'])
168-
231+
writer.writerows(get_csv_data(version_report, keep_dupes))
169232
return
170233
# If it's neither, then .json
171234
if not output_file.lower().endswith(".json"):
@@ -183,6 +246,7 @@ def parse_command_args():
183246
parser.add_argument("-pn", "--project-name", required=True, help="Project Name")
184247
parser.add_argument("-pv", "--project-version-name", required=True, help="Project Version Name")
185248
parser.add_argument("-o", "--output-file", required=False, help="File name to write output. File extension determines format .json and .csv, json is the default.")
249+
parser.add_argument("-kd", "--keep-dupes", action='store_true', help="Do not reduce CVS data by fuzzy matching component names")
186250
parser.add_argument("-kh", "--keep_hierarchy", action='store_true', help="Set to keep all entries in the sources report. Will not remove components found under others.")
187251
parser.add_argument("--report-retries", metavar="", type=int, default=RETRY_LIMIT, help="Retries for receiving the generated BlackDuck report. Generating copyright report tends to take longer minutes.")
188252
parser.add_argument("--report-timeout", metavar="", type=int, default=RETRY_TIMER, help="Wait time between subsequent download attempts.")
@@ -230,7 +294,7 @@ def main():
230294
trim_version_report(version_report, reduced_path_set)
231295
logging.info(f"Truncated dataset contains {len(version_report['aggregateBomViewEntries'])} bom entries and {len(version_report['detailedFileBomViewEntries'])} file view entries")
232296

233-
write_output_file(version_report, output_file)
297+
write_output_file(version_report, output_file, args.keep_dupes)
234298

235299
# Combine component data with selected file data
236300
# Output result with CSV anf JSON as options.

0 commit comments

Comments
 (0)