Skip to content

Commit c845968

Browse files
committed
Log "Import ScanCode scan results" as ScanCodeProject #241
Signed-off-by: tdruez <[email protected]>
1 parent 2e12517 commit c845968

File tree

9 files changed

+73
-31
lines changed

9 files changed

+73
-31
lines changed

component_catalog/templates/component_catalog/includes/scan_status.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<div class="progress-bar {% if has_errors %}bg-warning{% else %}bg-success{% endif %}" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
44
{% elif status == 'failure' or status == "stopped" or status == "stale" %}
55
<div class="progress-bar bg-danger" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
6+
{% elif status == 'warning' %}
7+
<div class="progress-bar bg-warning" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
68
{% elif status == 'running' %}
79
<div class="progress-bar" role="progressbar" style="width: 40%" aria-label="Scan progress" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
810
{% elif status == 'not_started' or status == 'queued' %}

product_portfolio/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ class ImportFromScanForm(forms.Form):
543543
extensions=["json"],
544544
)
545545
create_codebase_resources = forms.BooleanField(
546-
label=_('Create Codebase Resources (from <code>"files"</code>)'),
546+
label=_('Create Codebase Resources (from "files" resources)'),
547547
required=False,
548548
initial=False,
549549
help_text=_(

product_portfolio/importers.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from product_portfolio.models import ProductItemPurpose
4646
from product_portfolio.models import ProductPackage
4747
from product_portfolio.models import ProductRelationStatus
48+
from product_portfolio.models import ScanCodeProject
4849

4950

5051
class CleanProductMixin(ComponentRelatedFieldImportMixin):
@@ -394,6 +395,7 @@ def __init__(
394395
self.created_counts = {}
395396
self.warnings = []
396397
self.product_packages_by_id = {}
398+
self.scancode_project = None
397399

398400
self.resource_additional_fields = [
399401
"programming_language",
@@ -408,22 +410,21 @@ def __init__(
408410
]
409411

410412
def save(self):
413+
self.create_scancode_project()
411414
self.load_data_from_file()
412415
self.validate_headers()
413416
self.import_packages()
414417
if self.create_codebase_resources:
415418
self.import_codebase_resources()
416-
419+
self.update_scancode_project()
417420
return self.warnings, self.created_counts
418421

419422
def load_data_from_file(self):
420-
with self.upload_file.open() as f:
421-
file_content = f.read()
422-
423423
try:
424-
self.data = json.loads(file_content)
425-
except json.JSONDecodeError:
426-
raise ValidationError("The file content is not proper JSON.")
424+
with self.upload_file.open("r", encoding="utf-8") as f:
425+
self.data = json.load(f)
426+
except json.JSONDecodeError as e:
427+
raise ValidationError(f"Invalid JSON file: {e}")
427428

428429
def validate_headers(self):
429430
"""
@@ -604,6 +605,37 @@ def import_codebase_resources(self):
604605
if codebase_resources_count:
605606
self.created_counts["Codebase Resources"] = codebase_resources_count
606607

608+
def create_scancode_project(self):
609+
"""Create a ScanCodeProject entry to log this import on the Product."""
610+
self.scancode_project = ScanCodeProject.objects.create(
611+
product=self.product,
612+
dataspace=self.product.dataspace,
613+
type=ScanCodeProject.ProjectType.IMPORT_SCAN_RESULTS,
614+
input_file=self.upload_file,
615+
created_by=self.user,
616+
status=ScanCodeProject.Status.IMPORT_STARTED,
617+
)
618+
619+
def update_scancode_project(self):
620+
"""Update the ScanCodeProject entry with import results."""
621+
if self.created_counts:
622+
status = ScanCodeProject.Status.SUCCESS
623+
import_log = [
624+
f"- Imported {count} {label.lower()}"
625+
for label, count in self.created_counts.items()
626+
]
627+
else:
628+
status = ScanCodeProject.Status.WARNING
629+
import_log = ["Nothing imported."]
630+
631+
if self.warnings:
632+
import_log.extend(self.warnings)
633+
634+
self.scancode_project.update(
635+
status=status,
636+
import_log=import_log,
637+
)
638+
607639

608640
class ImportPackageFromScanCodeIO:
609641
"""
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 5.1.6 on 2025-02-21 22:16
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('product_portfolio', '0011_alter_product_owner'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='scancodeproject',
15+
name='status',
16+
field=models.CharField(choices=[('submitted', 'Submitted'), ('importing', 'Import Started'), ('success', 'Completed'), ('failure', 'Failure'), ('warning', 'Warning')], db_index=True, max_length=10),
17+
),
18+
migrations.AlterField(
19+
model_name='scancodeproject',
20+
name='type',
21+
field=models.CharField(choices=[('IMPORT_FROM_MANIFEST', 'Import Package manifests'), ('LOAD_SBOMS', 'Import SBOM'), ('PULL_FROM_SCANCODEIO', 'Import ScanCode.io project'), ('IMPROVE_FROM_PURLDB', 'Improve from PurlDB'), ('IMPORT_SCAN_RESULTS', 'Import ScanCode scan results')], db_index=True, help_text='The type of import, for the ProjectType choices.', max_length=50),
22+
),
23+
]

product_portfolio/migrations/0012_alter_scancodeproject_type.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

product_portfolio/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1457,19 +1457,21 @@ def in_progress(self):
14571457

14581458

14591459
class ScanCodeProject(HistoryFieldsMixin, DataspacedModel):
1460-
"""Wrap a ScanCode.io Project."""
1460+
"""Wrap Product imports, such as a ScanCode.io Project."""
14611461

14621462
class ProjectType(models.TextChoices):
14631463
IMPORT_FROM_MANIFEST = "IMPORT_FROM_MANIFEST", _("Import Package manifests")
14641464
LOAD_SBOMS = "LOAD_SBOMS", _("Import SBOM")
14651465
PULL_FROM_SCANCODEIO = "PULL_FROM_SCANCODEIO", _("Import ScanCode.io project")
14661466
IMPROVE_FROM_PURLDB = "IMPROVE_FROM_PURLDB", _("Improve from PurlDB")
1467+
IMPORT_SCAN_RESULTS = "IMPORT_SCAN_RESULTS", _("Import ScanCode scan results")
14671468

14681469
class Status(models.TextChoices):
14691470
SUBMITTED = "submitted"
14701471
IMPORT_STARTED = "importing"
14711472
SUCCESS = "success", _("Completed")
14721473
FAILURE = "failure"
1474+
WARNING = "warning"
14731475

14741476
uuid = models.UUIDField(
14751477
_("UUID"),

product_portfolio/tests/test_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def test_api_product_endpoint_import_from_scan_action(self):
435435
}
436436
response = self.client.post(url, data)
437437
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code)
438-
expected = ["The file content is not proper JSON."]
438+
expected = ["Invalid JSON file: Expecting value: line 1 column 1 (char 0)"]
439439
self.assertEqual(expected, response.data)
440440

441441
scan_input_location = self.testfiles_path / "import_from_scan.json"

product_portfolio/tests/test_views.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3090,15 +3090,16 @@ def test_product_portfolio_load_sbom_view(self, mock_submit):
30903090
expected = "Import SBOM"
30913091
self.assertContains(response, expected)
30923092

3093-
data = {"input_file": ContentFile("{}", name="file.json")}
3093+
data = {"input_file": ContentFile('{"data": "data"}', name="file.json")}
30943094
response = self.client.post(url, data=data, follow=True)
30953095
expected = "SBOM file submitted to ScanCode.io for inspection."
30963096
self.assertContains(response, expected)
30973097
self.assertEqual(1, ScanCodeProject.objects.count())
30983098

3099+
data = {"input_file": ContentFile('{"data": "data"}', name="file2.json")}
30993100
with override_settings(CLAMD_ENABLED=True):
31003101
with mock.patch("dje.fields.SmartFileField.scan_file_for_virus") as scan:
3101-
self.client.post(url, data=data, follow=True)
3102+
response = self.client.post(url, data=data, follow=True)
31023103
scan.assert_called_once()
31033104

31043105
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.submit_project")

product_portfolio/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2016,7 +2016,7 @@ def import_from_scan_view(request, dataspace, name, version=""):
20162016
messages.success(request, format_html(msg))
20172017
if warnings:
20182018
messages.warning(request, format_html("<br>".join(warnings)))
2019-
return redirect(product)
2019+
return redirect(f"{product.get_absolute_url()}#imports")
20202020
else:
20212021
form = form_class(request.user)
20222022

0 commit comments

Comments
 (0)