Skip to content

Commit 2f7943f

Browse files
Improve LicenseDetections UI
Signed-off-by: Ayan Sinha Mahapatra <ayansmahapatra@gmail.com>
1 parent 3cb1a2c commit 2f7943f

File tree

12 files changed

+117
-60
lines changed

12 files changed

+117
-60
lines changed

scanpipe/filters.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,8 @@ class Meta:
772772
class LicenseFilterSet(FilterSetUtilsMixin, django_filters.FilterSet):
773773
dropdown_widget_fields = [
774774
"compliance_alert",
775+
"license_expression",
776+
"license_expression_spdx",
775777
]
776778

777779
search = DiscoveredLicenseSearchFilter(
@@ -788,6 +790,7 @@ class LicenseFilterSet(FilterSetUtilsMixin, django_filters.FilterSet):
788790
],
789791
)
790792
license_expression = ParentAllValuesFilter()
793+
license_expression_spdx = ParentAllValuesFilter()
791794
compliance_alert = django_filters.ChoiceFilter(
792795
choices=[(EMPTY_VAR, "None")] + CodebaseResource.Compliance.choices,
793796
)

scanpipe/models.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,16 @@ def license_detections_count(self):
12341234
"""Return the number of license detections in this project."""
12351235
return self.discoveredlicenses.count()
12361236

1237+
@cached_property
1238+
def package_compliance_alert_count(self):
1239+
"""Return the number of packages related to this project which have."""
1240+
return self.discoveredpackages.has_compliance_alert().count()
1241+
1242+
@cached_property
1243+
def license_compliance_alert_count(self):
1244+
"""Return the number of packages related to this project which have."""
1245+
return self.discoveredlicenses.has_compliance_alert().count()
1246+
12371247
@cached_property
12381248
def message_count(self):
12391249
"""Return the number of messages related to this project."""
@@ -2156,6 +2166,17 @@ class Compliance(models.TextChoices):
21562166
class Meta:
21572167
abstract = True
21582168

2169+
@property
2170+
def has_compliance_alert(self):
2171+
"""
2172+
Returns True if this instance has a compliance alert of `ERROR`
2173+
for it's respective license_expression fields.
2174+
"""
2175+
if self.compliance_alert == self.Compliance.ERROR:
2176+
return True
2177+
2178+
return False
2179+
21592180
@classmethod
21602181
def from_db(cls, db, field_names, values):
21612182
"""
@@ -2710,8 +2731,16 @@ def vulnerable(self):
27102731
return self.filter(~Q(affected_by_vulnerabilities__in=EMPTY_VALUES))
27112732

27122733

2734+
class ComplianceAlertQuerySetMixin:
2735+
def has_compliance_alert(self):
2736+
return self.filter(Q(compliance_alert__exact=CodebaseResource.Compliance.ERROR))
2737+
2738+
27132739
class DiscoveredPackageQuerySet(
2714-
VulnerabilityQuerySetMixin, PackageURLQuerySetMixin, ProjectRelatedQuerySet
2740+
VulnerabilityQuerySetMixin,
2741+
ComplianceAlertQuerySetMixin,
2742+
PackageURLQuerySetMixin,
2743+
ProjectRelatedQuerySet,
27152744
):
27162745
def order_by_purl(self):
27172746
"""Order by Package URL fields."""
@@ -3485,7 +3514,10 @@ def as_spdx(self):
34853514
)
34863515

34873516

3488-
class DiscoveredLicenseQuerySet(ProjectRelatedQuerySet):
3517+
class DiscoveredLicenseQuerySet(
3518+
ComplianceAlertQuerySetMixin,
3519+
ProjectRelatedQuerySet,
3520+
):
34893521
def order_by_count_and_expression(self):
34903522
"""Order by detection count and license expression (identifer) fields."""
34913523
return self.order_by("-detection_count", "identifier")
@@ -3550,7 +3582,6 @@ class DiscoveredLicense(
35503582
"""
35513583
A project's Discovered Licenses are the unique License Detection objects
35523584
discovered in the code under analysis.
3553-
35543585
"""
35553586

35563587
license_expression_field = "license_expression"

scanpipe/templates/scanpipe/includes/project_summary_level.html

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
<i class="fa-solid fa-bug is-size-6"></i>
1515
</a>
1616
{% endif %}
17+
{% if project.package_compliance_alert_count %}
18+
<a href="{% url 'project_packages' project.slug %}?compliance_alert=error" class="has-text-danger is-size-5 ml-2">
19+
{{ project.package_compliance_alert_count|intcomma }}
20+
<i class="fa-solid fa-scale-balanced is-size-6"></i>
21+
</a>
22+
{% endif %}
1723
{% else %}
1824
<span>0</span>
1925
{% endif %}
@@ -40,20 +46,6 @@
4046
</p>
4147
</div>
4248
</div>
43-
<div class="level-item has-text-centered">
44-
<div>
45-
<p class="heading">License Detections</p>
46-
<p class="{{ title_class }} is-flex is-align-items-center is-justify-content-center">
47-
{% if project.license_detections_count %}
48-
<a href="{% url 'project_licenses' project.slug %}">
49-
{{ project.license_detections_count|intcomma }}
50-
</a>
51-
{% else %}
52-
<span>0</span>
53-
{% endif %}
54-
</p>
55-
</div>
56-
</div>
5749
<div class="level-item has-text-centered">
5850
<div>
5951
<p class="heading">Resources</p>

scanpipe/templates/scanpipe/license_detection_list.html

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,28 @@
2424
<td style="min-width: 300px;" title="{{ license_detection.identifier }}">
2525
{# CAUTION: Avoid relying on get_absolute_url to prevent unnecessary query triggers #}
2626
<a href="{% url 'license_detail' project.slug license_detection.identifier %}">{{ license_detection.identifier }}</a>
27+
{% if license_detection.has_compliance_alert %}
28+
<a href="{% url 'license_detail' project.slug license_detection.identifier %}#detection">
29+
<i class="fa-solid fa-scale-balanced fa-sm has-text-danger" title="License Compliance Error"></i>
30+
</a>
31+
{% endif %}
2732
</td>
2833
<td>
29-
<a href="?type={{ license_detection.license_expression }}" class="is-black-link">{{ license_detection.license_expression }}</a>
34+
<a href="?license_expression={{ license_detection.license_expression }}" class="is-black-link">{{ license_detection.license_expression }}</a>
3035
</td>
3136
<td>
32-
<a href="?type={{ license_detection.license_expression_spdx }}" class="is-black-link">{{ license_detection.license_expression_spdx }}</a>
37+
<a href="?license_expression_spdx={{ license_detection.license_expression_spdx }}" class="is-black-link">{{ license_detection.license_expression_spdx }}</a>
3338
</td>
3439
<td>
3540
{{ license_detection.detection_count }}
3641
</td>
3742
{% if display_compliance_alert %}
3843
<td>
39-
<a href="?compliance_alert={{ package.compliance_alert }}" class="is-black-link">
40-
{{ package.compliance_alert }}
44+
<a href="?compliance_alert={{ license_detection.compliance_alert }}" class="is-black-link">
45+
{{ license_detection.compliance_alert }}
4146
</a>
4247
</td>
43-
{% endif %}
48+
{% endif %}
4449
</tr>
4550
{% empty %}
4651
<tr>

scanpipe/templates/scanpipe/package_list.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
<i class="fa-solid fa-bug fa-sm has-text-danger" title="Vulnerabilities"></i>
3131
</a>
3232
{% endif %}
33+
{% if package.has_compliance_alert %}
34+
<a href="{% url 'package_detail' project.slug package.uuid %}#terms">
35+
<i class="fa-solid fa-scale-balanced fa-sm has-text-danger" title="License Compliance Error"></i>
36+
</a>
37+
{% endif %}
3338
</td>
3439
<td style="min-width: 300px; max-width: 400px;">
3540
<a href="?declared_license_expression={{ package.declared_license_expression }}" class="is-black-link">
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{% load humanize %}
2+
{% if license_detection_summary %}
3+
<div class="column is-half">
4+
<nav class="panel is-info">
5+
<p class="panel-heading py-2 is-size-6">
6+
Top 10 Unique License detections
7+
</p>
8+
{% for license_expression, count in license_detection_summary.items %}
9+
<a class="panel-block is-align-items-flex-start break-word" href="{{ project_licenses_url }}?license_expression={{ license_expression|default:'_EMPTY_' }}" target="_blank">
10+
{{ license_expression|default:'<i>No licenses</i>' }}
11+
<span class="tag is-rounded ml-1">{{ count|intcomma }}</span>
12+
{% if license_expression in expressions_with_compliance_alert %}
13+
&nbsp; <span class="fa-solid fa-scale-balanced has-text-danger" title="License Compliance Error"></span>
14+
{% endif %}
15+
</a>
16+
{% endfor %}
17+
</nav>
18+
</div>
19+
{% endif %}

scanpipe/templates/scanpipe/panels/resource_license_summary.html

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

scanpipe/templates/scanpipe/project_detail.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104

105105
<div class="columns">
106106
<div hx-get="{% url 'project_resource_status_summary' project.slug %}" hx-trigger="load" hx-swap="outerHTML"></div>
107-
<div hx-get="{% url 'project_resource_license_summary' project.slug %}" hx-trigger="load" hx-swap="outerHTML"></div>
107+
<div hx-get="{% url 'project_license_detection_summary' project.slug %}" hx-trigger="load" hx-swap="outerHTML"></div>
108108
</div>
109109

110110
{% if project.extra_data %}

scanpipe/templates/scanpipe/tabset/tab_packages.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
<i class="fa-solid fa-bug fa-sm has-text-danger" title="Vulnerabilities"></i>
1919
</a>
2020
{% endif %}
21+
{% if package.has_compliance_alert %}
22+
<a href="{% url 'package_detail' project.slug package.uuid %}#detection">
23+
<i class="fa-solid fa-scale-balanced fa-sm has-text-danger" title="License Compliance Error"></i>
24+
</a>
25+
{% endif %}
2126
</td>
2227
<td>
2328
{{ package.declared_license_expression }}

scanpipe/tests/test_views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ def test_scanpipe_views_project_views(self):
683683
with self.assertNumQueries(8):
684684
self.client.get(url)
685685

686-
with self.assertNumQueries(14):
686+
with self.assertNumQueries(13):
687687
self.client.get(self.project1.get_absolute_url())
688688

689689
@mock.patch("scanpipe.models.Run.execute_task_async")
@@ -915,7 +915,7 @@ def test_scanpipe_views_codebase_resource_views(self):
915915
with self.assertNumQueries(7):
916916
self.client.get(url)
917917

918-
with self.assertNumQueries(7):
918+
with self.assertNumQueries(8):
919919
self.client.get(resource1.get_absolute_url())
920920

921921
def test_scanpipe_views_discovered_package_views(self):

0 commit comments

Comments
 (0)