Skip to content

Commit 1c4fb5e

Browse files
authored
Add a new AboutCode tab in Package details view #42 (#119)
Signed-off-by: tdruez <tdruez@nexb.com>
1 parent b339542 commit 1c4fb5e

7 files changed

Lines changed: 65 additions & 11 deletions

File tree

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ Release notes
7272
- Always display the full Package URL in the UI view including the "pkg:" prefix.
7373
https://github.com/nexB/dejacode/issues/115
7474

75+
- Add a new AboutCode tab in Package details view.
76+
https://github.com/nexB/dejacode/issues/42
77+
7578
### Version 5.0.1
7679

7780
- Improve the stability of the "Check for new Package versions" feature.

component_catalog/models.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,9 +2016,15 @@ def size_formatted(self):
20162016

20172017
@cached_property
20182018
def component(self):
2019-
"""Return the Component instance if 1 and only 1 Component is assigned to this Package."""
2020-
with suppress(ObjectDoesNotExist, MultipleObjectsReturned):
2021-
return self.component_set.get()
2019+
"""
2020+
Return the Component instance if 1 and only 1 Component is assigned to this
2021+
Package.
2022+
Using ``component_set.all()`` to benefit from prefetch_related when it was
2023+
applied to the Package QuerySet.
2024+
"""
2025+
component_set = self.component_set.all()
2026+
if len(component_set) == 1:
2027+
return component_set[0]
20222028

20232029
def set_values_from_component(self, component, user):
20242030
changed_fields = set_fields_from_object(
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div class="alert alert-primary m-0 mb-3" role="alert">
2+
<p class="m-0">
3+
This tab renders a preview of the AboutCode files: .ABOUT and .NOTICE,
4+
exactly as it will appear in the generated .yml files.<br>
5+
<a href="{{ object.get_about_files_url }}" class="fw-bold">Click here to download</a>
6+
those files as a zip archive.
7+
</p>
8+
</div>
9+
10+
<dl class="row mb-0">
11+
<dt class="col-sm-1 text-end pt-2 pe-0">.ABOUT</dt>
12+
<dd class="col-sm-11">
13+
<pre class="pre-bg-body-tertiary mb-1">{{ values.about_content }}</pre>
14+
</dd>
15+
{% if values.notice_content %}
16+
<dt class="col-sm-1 text-end pt-2 pe-0">.NOTICE</dt>
17+
<dd class="col-sm-11">
18+
<pre class="pre-bg-body-tertiary mb-1">{{ values.notice_content }}</pre>
19+
</dd>
20+
{% endif %}
21+
</dl>

component_catalog/tests/test_views.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,15 @@ def test_package_details_view_add_package_links(self):
13421342
for expected in expecteds:
13431343
self.assertContains(response, expected)
13441344

1345+
def test_package_details_view_aboutcode_tab(self):
1346+
details_url = self.package1.get_absolute_url()
1347+
self.client.login(username=self.super_user.username, password="secret")
1348+
response = self.client.get(details_url)
1349+
self.assertContains(response, 'id="tab_aboutcode-tab"')
1350+
self.assertContains(response, 'id="tab_aboutcode"')
1351+
self.assertContains(response, "This tab renders a preview of the AboutCode files")
1352+
self.assertContains(response, "about_resource: package1")
1353+
13451354
def test_package_list_view_add_to_product(self):
13461355
user = create_user("user", self.dataspace)
13471356
self.client.login(username=user.username, password="secret")

component_catalog/views.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from django.utils.formats import date_format
3535
from django.utils.html import escape
3636
from django.utils.html import format_html
37+
from django.utils.text import normalize_newlines
3738
from django.utils.translation import gettext
3839
from django.utils.translation import gettext_lazy as _
3940
from django.views.decorators.csrf import csrf_exempt
@@ -1147,8 +1148,8 @@ class PackageDetailsView(
11471148
AcceptAnonymousMixin,
11481149
AddPackagePermissionMixin,
11491150
TabVulnerabilityMixin,
1150-
ObjectDetailsView,
11511151
AddToProductFormMixin,
1152+
ObjectDetailsView,
11521153
):
11531154
model = Package
11541155
slug_url_kwarg = "uuid"
@@ -1247,6 +1248,9 @@ class PackageDetailsView(
12471248
"vulnerabilities": {
12481249
"verbose_name": "Vulnerabilities",
12491250
},
1251+
"aboutcode": {
1252+
"verbose_name": "AboutCode",
1253+
},
12501254
"history": {
12511255
"fields": [
12521256
"created_date",
@@ -1266,8 +1270,7 @@ def get_context_data(self, **kwargs):
12661270
has_change_package_permission = user.has_perm("component_catalog.change_package")
12671271
context_data["has_change_package_permission"] = has_change_package_permission
12681272

1269-
# License data is required for the Scan tab "scan to package"
1270-
# license expression fields
1273+
# License data is required for the Scan tab "scan to package" license expression fields
12711274
client_data = getattr(self.request, "client_data", {})
12721275
include_all_licenses = all(
12731276
[
@@ -1280,9 +1283,9 @@ def get_context_data(self, **kwargs):
12801283
all_licenses = License.objects.scope(user.dataspace).filter(is_active=True)
12811284
add_client_data(self.request, license_data=all_licenses.data_for_expression_builder())
12821285

1283-
if self.request.user.has_perm("component_catalog.change_component"):
1286+
if user.has_perm("component_catalog.change_component"):
12841287
context_data["add_to_component_form"] = AddToComponentForm(
1285-
self.request.user, initial={self.model._meta.model_name: self.object}
1288+
user, initial={self.model._meta.model_name: self.object}
12861289
)
12871290

12881291
return context_data
@@ -1447,6 +1450,15 @@ def tab_purldb(self):
14471450
}
14481451
return {"fields": [(None, tab_context, None, template)]}
14491452

1453+
def tab_aboutcode(self):
1454+
template = "component_catalog/tabs/tab_aboutcode.html"
1455+
context = {
1456+
"about_content": self.object.as_about_yaml(),
1457+
"notice_content": normalize_newlines(self.object.notice_text),
1458+
}
1459+
1460+
return {"fields": [(None, context, None, template)]}
1461+
14501462
def get_vulnerabilities_tab_fields(self, vulnerabilities):
14511463
dataspace = self.object.dataspace
14521464
fields = []

dje/models.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,13 +1127,15 @@ def clean_extra_spaces_in_identifier_fields(self):
11271127
setattr(self, field_name, " ".join(field_value.split()))
11281128

11291129
def mark_all_notifications_as_read(self, user):
1130-
unread_notifications = Notification.objects.unread().filter(
1130+
unread_notifications_qs = Notification.objects.unread().filter(
11311131
action_object_content_type__model=self._meta.model_name,
11321132
action_object_object_id=self.id,
11331133
recipient=user,
11341134
)
1135-
if unread_notifications:
1136-
unread_notifications.update(unread=False)
1135+
# Trigger a single UPDATE query on the "unread" Notification.
1136+
# Even if the QS is empty, this is faster than checking is the QS contains
1137+
# entries first.
1138+
unread_notifications_qs.update(unread=False)
11371139

11381140

11391141
class HistoryDateFieldsMixin(models.Model):

dje/tests/test_permissions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def test_permissions_get_all_tabsets(self):
116116
"scan",
117117
"purldb",
118118
"vulnerabilities",
119+
"aboutcode",
119120
"history",
120121
],
121122
"license": [

0 commit comments

Comments
 (0)