Skip to content

Commit 973d7aa

Browse files
Improve license detection and summary related views
Signed-off-by: Ayan Sinha Mahapatra <[email protected]>
1 parent 225c216 commit 973d7aa

9 files changed

+245
-56
lines changed

scanpipe/filters.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ class ResourceFilterSet(FilterSetUtilsMixin, django_filters.FilterSet):
492492
dropdown_widget_fields = [
493493
"status",
494494
"type",
495+
"programming_language",
495496
"tag",
496497
"compliance_alert",
497498
"in_package",
@@ -521,6 +522,7 @@ class ResourceFilterSet(FilterSetUtilsMixin, django_filters.FilterSet):
521522
"related_from__from_resource__path",
522523
],
523524
)
525+
programming_language = django_filters.AllValuesFilter()
524526
compliance_alert = django_filters.ChoiceFilter(
525527
choices=[(EMPTY_VAR, "None")] + CodebaseResource.Compliance.choices,
526528
)
@@ -577,8 +579,8 @@ class Meta:
577579

578580
def __init__(self, *args, **kwargs):
579581
super().__init__(*args, **kwargs)
580-
license_expression_filer = self.filters["detected_license_expression"]
581-
license_expression_filer.extra["widget"] = HasValueDropdownWidget()
582+
license_expression_filter = self.filters["detected_license_expression"]
583+
license_expression_filter.extra["widget"] = HasValueDropdownWidget()
582584

583585
@classmethod
584586
def filter_for_lookup(cls, field, lookup_type):

scanpipe/models.py

+25-8
Original file line numberDiff line numberDiff line change
@@ -1365,14 +1365,28 @@ def license_detections_count(self):
13651365

13661366
@cached_property
13671367
def package_compliance_alert_count(self):
1368-
"""Return the number of packages related to this project which have."""
1368+
"""
1369+
Return the number of packages related to this project which have
1370+
a license compliance error alert.
1371+
"""
13691372
return self.discoveredpackages.has_compliance_alert().count()
13701373

13711374
@cached_property
13721375
def license_compliance_alert_count(self):
1373-
"""Return the number of packages related to this project which have."""
1376+
"""
1377+
Return the number of license detections related to this project
1378+
which have a license compliance error alert.
1379+
"""
13741380
return self.discoveredlicenses.has_compliance_alert().count()
13751381

1382+
@cached_property
1383+
def resource_compliance_alert_count(self):
1384+
"""
1385+
Return the number of codebase resources related to this project which have
1386+
a license compliance error alert.
1387+
"""
1388+
return self.codebaseresources.has_compliance_alert().count()
1389+
13761390
@cached_property
13771391
def message_count(self):
13781392
"""Return the number of messages related to this project."""
@@ -2042,7 +2056,15 @@ def convert_glob_to_django_regex(glob_pattern):
20422056
return escaped_pattern
20432057

20442058

2045-
class CodebaseResourceQuerySet(ProjectRelatedQuerySet):
2059+
class ComplianceAlertQuerySetMixin:
2060+
def has_compliance_alert(self):
2061+
return self.filter(Q(compliance_alert__exact=CodebaseResource.Compliance.ERROR))
2062+
2063+
2064+
class CodebaseResourceQuerySet(
2065+
ComplianceAlertQuerySetMixin,
2066+
ProjectRelatedQuerySet,
2067+
):
20462068
def prefetch_for_serializer(self):
20472069
"""
20482070
Optimized prefetching for a QuerySet to be consumed by the
@@ -2945,11 +2967,6 @@ def vulnerable(self):
29452967
return self.filter(~Q(affected_by_vulnerabilities__in=EMPTY_VALUES))
29462968

29472969

2948-
class ComplianceAlertQuerySetMixin:
2949-
def has_compliance_alert(self):
2950-
return self.filter(Q(compliance_alert__exact=CodebaseResource.Compliance.ERROR))
2951-
2952-
29532970
class DiscoveredPackageQuerySet(
29542971
VulnerabilityQuerySetMixin,
29552972
ComplianceAlertQuerySetMixin,

scanpipe/templates/scanpipe/includes/project_summary_level.html

+7-1
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,17 @@
4949
<div class="level-item has-text-centered">
5050
<div>
5151
<p class="heading">Resources</p>
52-
<p class="{{ title_class }}">
52+
<p class="{{ title_class }} is-flex is-align-items-center is-justify-content-center">
5353
{% if project.resource_count %}
5454
<a href="{{ project_resources_url }}">
5555
{{ project.resource_count|intcomma }}
5656
</a>
57+
{% if project.resource_compliance_alert_count %}
58+
<a href="{% url 'project_resources' project.slug %}?compliance_alert=error" class="has-text-danger is-size-5 ml-2">
59+
{{ project.resource_compliance_alert_count|intcomma }}
60+
<i class="fa-solid fa-scale-balanced is-size-6"></i>
61+
</a>
62+
{% endif %}
5763
{% else %}
5864
<span>0</span>
5965
{% endif %}

scanpipe/templates/scanpipe/license_detection_list.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<a href="{% url 'license_detail' project.slug license_detection.identifier %}">{{ license_detection.identifier }}</a>
2727
{% if license_detection.has_compliance_alert %}
2828
<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>
29+
<i class="fa-solid fa-scale-balanced fa-sm has-text-danger" title="License Compliance Alerts"></i>
3030
</a>
3131
{% endif %}
3232
</td>

scanpipe/templates/scanpipe/panels/license_detections_summary.html

+15-3
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,29 @@
33
<div class="column is-half">
44
<nav class="panel is-info">
55
<p class="panel-heading py-2 is-size-6">
6-
Top 10 Unique License detections
6+
Unique License detections
77
</p>
88
{% 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">
9+
<a class="panel-block is-align-items-flex-start break-word is-flex is-align-items-center" href="{{ project_licenses_url }}?license_expression={{ license_expression|default:'_EMPTY_' }}" target="_blank">
1010
{{ license_expression|default:'<i>No licenses</i>' }}
1111
<span class="tag is-rounded ml-1">{{ count|intcomma }}</span>
1212
{% 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>
13+
&nbsp; <span class="fa-solid fa-scale-balanced has-text-danger" title="License Compliance Alerts"></span>
1414
{% endif %}
1515
</a>
1616
{% endfor %}
17+
{% if total_counts.all %}
18+
<a class="panel-block is-align-items-flex-start break-word is-flex is-align-items-center" href="{{ project_licenses_url }}" target="_blank">
19+
See all License Detections
20+
<span class="tag is-rounded ml-1">{{ total_counts.all|intcomma }}</span>
21+
{% if total_counts.with_compliance_error %}
22+
<span class="has-text-danger is-size-6 ml-2">
23+
<i class="fa-solid fa-scale-balanced fa-sm"></i>
24+
{{ total_counts.with_compliance_error|intcomma }}
25+
</span>
26+
{% endif %}
27+
</a>
28+
{% endif %}
1729
</nav>
1830
</div>
1931
{% endif %}

scanpipe/templates/scanpipe/panels/scan_summary_panel.html

+147-15
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,80 @@
66
</div>
77
<div class="panel-block p-0">
88
<table class="table is-fullwidth border-bottom-radius">
9-
{% for field_label, values in scan_summary.items %}
10-
<tr>
11-
<th class="is-narrow">
12-
{{ field_label }}
13-
</th>
14-
<td>
15-
<ul>
16-
{% for entry in values %}
17-
{% if entry.value %}
9+
<tr>
10+
<th class="is-narrow">
11+
Declared license
12+
</th>
13+
<td>
14+
<ul>
15+
{% for entry in scan_summary.declared_license_expression %}
16+
{% if entry.value %}
17+
<li>
18+
{{ entry.value }}
19+
{% if entry.count %}
20+
<span class="tag is-rounded">
21+
{{ entry.count|intcomma }}
22+
</span>
23+
{% endif %}
24+
</li>
25+
{% endif %}
26+
{% endfor %}
27+
</ul>
28+
</td>
29+
</tr>
30+
<tr>
31+
<th class="is-narrow">
32+
Declared holder
33+
</th>
34+
<td>
35+
<ul>
36+
{% for entry in scan_summary.declared_holder %}
37+
{% if entry.value %}
38+
<li>
39+
{{ entry.value }}
40+
{% if entry.count %}
41+
<span class="tag is-rounded">
42+
{{ entry.count|intcomma }}
43+
</span>
44+
{% endif %}
45+
</li>
46+
{% endif %}
47+
{% endfor %}
48+
</ul>
49+
</td>
50+
</tr>
51+
<tr>
52+
<th class="is-narrow">
53+
Primary language
54+
</th>
55+
<td>
56+
<ul>
57+
{% for entry in scan_summary.primary_language %}
58+
{% if entry.value %}
59+
<li>
60+
<a href="{% url 'project_resources' project.slug %}?programming_language={{ entry.value }}" target="_blank">
61+
{{ entry.value }}
62+
{% if entry.count %}
63+
<span class="tag is-rounded">
64+
{{ entry.count|intcomma }}
65+
</span>
66+
{% endif %}
67+
</a>
68+
</li>
69+
{% endif %}
70+
{% endfor %}
71+
</ul>
72+
</td>
73+
</tr>
74+
<tr>
75+
<th class="is-narrow">
76+
Other licenses
77+
</th>
78+
<td>
79+
<ul>
80+
{% for entry in scan_summary.other_license_expressions %}
81+
{% if entry.value %}
82+
<a href="{% url 'project_licenses' project.slug %}?license_expression={{ entry.value }}" target="_blank">
1883
<li>
1984
{{ entry.value }}
2085
{% if entry.count %}
@@ -23,12 +88,79 @@
2388
</span>
2489
{% endif %}
2590
</li>
26-
{% endif %}
27-
{% endfor %}
28-
</ul>
29-
</td>
30-
</tr>
31-
{% endfor %}
91+
</a>
92+
{% endif %}
93+
{% endfor %}
94+
</ul>
95+
</td>
96+
</tr>
97+
<tr>
98+
<th class="is-narrow">
99+
Other holders
100+
</th>
101+
<td>
102+
<ul>
103+
{% for entry in scan_summary.other_holders %}
104+
{% if entry.value %}
105+
<li>
106+
{{ entry.value }}
107+
{% if entry.count %}
108+
<span class="tag is-rounded">
109+
{{ entry.count|intcomma }}
110+
</span>
111+
{% endif %}
112+
</li>
113+
{% endif %}
114+
{% endfor %}
115+
</ul>
116+
</td>
117+
</tr>
118+
<tr>
119+
<th class="is-narrow">
120+
Other languages
121+
</th>
122+
<td>
123+
<ul>
124+
{% for entry in scan_summary.other_languages %}
125+
{% if entry.value %}
126+
<a href="{% url 'project_resources' project.slug %}?programming_language={{ entry.value }}" target="_blank">
127+
<li>
128+
{{ entry.value }}
129+
{% if entry.count %}
130+
<span class="tag is-rounded">
131+
{{ entry.count|intcomma }}
132+
</span>
133+
{% endif %}
134+
</li>
135+
</a>
136+
{% endif %}
137+
{% endfor %}
138+
</ul>
139+
</td>
140+
</tr>
141+
<tr>
142+
<th class="is-narrow">
143+
Key Files
144+
</th>
145+
<td>
146+
<ul>
147+
{% for path, license in scan_summary.key_file_licenses.items %}
148+
{% if path %}
149+
<a href="{% url 'resource_detail' project.slug path %}#detection" target="_blank">
150+
<li>
151+
{{ path }}
152+
{% if license %}
153+
<span class="tag is-rounded">
154+
{{ license }}
155+
</span>
156+
{% endif %}
157+
</li>
158+
</a>
159+
{% endif %}
160+
{% endfor %}
161+
</ul>
162+
</td>
163+
</tr>
32164
</table>
33165
</div>
34166
</article>

scanpipe/templates/scanpipe/tabset/tab_license_detections.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
{{ match.license_expression }}
2020
</td>
2121
<td class="break-all">
22-
{{ match.from_file }}
22+
<a href="{% url 'resource_detail' project.slug match.from_file %}#detection">{{ match.from_file }}</a>
2323
</td>
2424
<td class="break-all">
2525
{{ match.rule_url }}
@@ -73,7 +73,7 @@
7373
{% for file_region in tab_data.fields.file_regions.value %}
7474
<tr>
7575
<td class="break-all">
76-
{{ file_region.path }}
76+
<a href="{% url 'resource_detail' project.slug file_region.path %}#detection">{{ file_region.path }}</a>
7777
</td>
7878
<td class="break-all">
7979
{{ file_region.start_line }}

scanpipe/tests/test_views.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -475,16 +475,17 @@ def test_scanpipe_views_project_details_get_scan_summary_data(self):
475475

476476
scan_summary = self.data / "scancode" / "is-npm-1.0.0_scan_package_summary.json"
477477
scan_summary_json = json.loads(scan_summary.read_text())
478-
scan_summary_data = get_scan_summary_data(scan_summary_json)
478+
scan_summary_data = get_scan_summary_data(self.project1, scan_summary_json)
479479

480-
self.assertEqual(6, len(scan_summary_data))
480+
self.assertEqual(7, len(scan_summary_data))
481481
expected = [
482-
"Declared license",
483-
"Declared holder",
484-
"Primary language",
485-
"Other licenses",
486-
"Other holders",
487-
"Other languages",
482+
"declared_license_expression",
483+
"declared_holder",
484+
"primary_language",
485+
"other_license_expressions",
486+
"other_holders",
487+
"other_languages",
488+
"key_file_licenses",
488489
]
489490
self.assertEqual(expected, list(scan_summary_data.keys()))
490491

@@ -956,7 +957,7 @@ def test_scanpipe_views_codebase_resource_views(self):
956957
package1.add_resources([resource1, resource2])
957958

958959
url = reverse("project_resources", args=[self.project1.slug])
959-
with self.assertNumQueries(8):
960+
with self.assertNumQueries(9):
960961
self.client.get(url)
961962

962963
with self.assertNumQueries(8):

0 commit comments

Comments
 (0)