Skip to content

Commit f427d6f

Browse files
committed
Implement progress logging in path_match #688
Signed-off-by: Thomas Druez <tdruez@nexb.com>
1 parent a7a9018 commit f427d6f

4 files changed

Lines changed: 83 additions & 11 deletions

File tree

scanpipe/pipelines/develop_to_deploy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def steps(cls):
4444
cls.path_match,
4545
)
4646

47-
purldb_match_extensions = [".jar", ".war"]
47+
purldb_match_extensions = [".jar", ".war", ".zip"]
4848

4949
def get_inputs(self):
5050
"""Locate the `from` and `to` archives."""

scanpipe/pipes/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,30 @@ def remove_prefix(text, prefix):
288288
prefix_len = len(prefix)
289289
return text[prefix_len:]
290290
return text
291+
292+
293+
def get_progress_percentage(current_index, total_count):
294+
"""
295+
Return the percentage of progress given the current index and total count of
296+
objects.
297+
"""
298+
if current_index < 0 or current_index >= total_count:
299+
raise ValueError("current_index must be between 0 and total_count - 1")
300+
301+
progress = current_index / total_count * 100
302+
return progress
303+
304+
305+
def log_progress(log_func, current_index, total_count, last_percent, increment_percent):
306+
"""
307+
Log progress updates every `increment_percent` percentage points, given the
308+
current index and total count of objects.
309+
Return the latest percent logged.
310+
"""
311+
progress_percentage = int(get_progress_percentage(current_index, total_count))
312+
if progress_percentage >= last_percent + increment_percent:
313+
last_percent = progress_percentage
314+
log_func(
315+
f"Progress: {progress_percentage}% ({current_index:,d}/{total_count:,d})"
316+
)
317+
return last_percent

scanpipe/pipes/d2d.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ def checksum_match(project, checksum_field, logger=None):
4848
"""Match using checksum."""
4949
project_files = project.codebaseresources.files().not_empty()
5050
from_resources = project_files.from_codebase().has_value(checksum_field)
51-
to_resources = project_files.to_codebase().has_value(checksum_field)
51+
to_resources = (
52+
project_files.to_codebase().has_value(checksum_field).has_no_relation()
53+
)
5254

5355
if logger:
5456
resource_count = to_resources.count()
5557
logger(
56-
f"Matching {resource_count} to/ resources using {checksum_field} "
58+
f"Matching {resource_count:,d} to/ resources using {checksum_field} "
5759
f"against from/ codebase"
5860
)
5961

@@ -80,7 +82,8 @@ def java_to_class_match(project, logger=None):
8082

8183
to_resources_dot_class = to_resources.filter(name__endswith=to_extension)
8284
if logger:
83-
logger(f"Matching {to_resources_dot_class.count()} .class resources to .java")
85+
count = to_resources_dot_class.count()
86+
logger(f"Matching {count:,d} .class resources to .java")
8487

8588
for to_resource in to_resources_dot_class:
8689
qualified_class = to_resource.path.split("-extract/")[-1]
@@ -105,22 +108,29 @@ def java_to_class_match(project, logger=None):
105108

106109
def path_match(project, logger=None):
107110
"""Match using path similarities."""
108-
project_files = project.codebaseresources.files().only("path")
111+
project_files = project.codebaseresources.files().not_empty().only("path")
109112
from_resources = project_files.from_codebase()
110113
to_resources = project_files.to_codebase().has_no_relation()
114+
resource_count = to_resources.count()
111115

112116
if logger:
113-
resource_count = to_resources.count()
114117
logger(
115-
f"Matching {resource_count} to/ resources using path match "
118+
f"Matching {resource_count:,d} to/ resources using path match "
116119
f"against from/ codebase"
117120
)
118121

119-
for to_resource in to_resources:
122+
resource_iterator = to_resources.iterator(chunk_size=2000)
123+
last_percent = 0
124+
for resource_index, to_resource in enumerate(resource_iterator):
125+
last_percent = pipes.log_progress(
126+
logger, resource_index, resource_count, last_percent, increment_percent=5
127+
)
128+
120129
path_parts = Path(to_resource.path.lstrip("/")).parts
121130
path_parts_len = len(path_parts)
122-
for index in range(1, path_parts_len):
123-
current_parts = path_parts[index:]
131+
132+
for path_parts_index in range(1, path_parts_len):
133+
current_parts = path_parts[path_parts_index:]
124134
current_path = "/".join(current_parts)
125135
# The slash "/" prefix matters during the match as we do not want to
126136
# match on filenames sharing the same ending.
@@ -157,7 +167,9 @@ def purldb_match(project, extensions, logger=None):
157167
if logger:
158168
resource_count = to_resources.count()
159169
extensions_str = ", ".join(extensions)
160-
logger(f"Matching {resource_count} {extensions_str} resources against PurlDB")
170+
logger(
171+
f"Matching {resource_count:,d} {extensions_str} resources against PurlDB"
172+
)
161173

162174
for resource in to_resources:
163175
if results := purldb.match_by_sha1(sha1=resource.sha1):

scanpipe/tests/pipes/test_pipes.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# Visit https://github.com/nexB/scancode.io for support and download.
2222

2323
import datetime
24+
import io
2425
from pathlib import Path
2526
from unittest import mock
2627

@@ -203,3 +204,35 @@ def test_scanpipe_add_resource_to_package(self):
203204
# resource.
204205
scancode.add_resource_to_package(package1.package_uid, resource1, project1)
205206
self.assertEqual(len(resource1.for_packages), 1)
207+
208+
def test_scanpipe_get_progress_percentage(self):
209+
self.assertEqual(0.0, pipes.get_progress_percentage(0, 10))
210+
self.assertEqual(50.0, pipes.get_progress_percentage(5, 10))
211+
self.assertEqual(90.0, pipes.get_progress_percentage(9, 10))
212+
self.assertEqual(60.0, pipes.get_progress_percentage(3, 5))
213+
214+
with self.assertRaises(ValueError):
215+
pipes.get_progress_percentage(10, 1)
216+
217+
def test_scanpipe_log_progress(self):
218+
buffer = io.StringIO()
219+
last_percent = pipes.log_progress(
220+
log_func=buffer.write,
221+
current_index=1,
222+
total_count=10,
223+
last_percent=0,
224+
increment_percent=5,
225+
)
226+
self.assertEqual(10, last_percent)
227+
self.assertEqual("Progress: 10% (1/10)", buffer.getvalue())
228+
229+
buffer = io.StringIO()
230+
last_percent = pipes.log_progress(
231+
log_func=buffer.write,
232+
current_index=20,
233+
total_count=100,
234+
last_percent=15,
235+
increment_percent=5,
236+
)
237+
self.assertEqual(20, last_percent)
238+
self.assertEqual("Progress: 20% (20/100)", buffer.getvalue())

0 commit comments

Comments
 (0)