Skip to content

Commit

Permalink
Display Product inventory count on the Product list view #81 (#116)
Browse files Browse the repository at this point in the history
* Display Product inventory count on the Product list view #81

Signed-off-by: tdruez <tdruez@nexb.com>

* Optimize the filters QuerySet of the Product list #81

Signed-off-by: tdruez <tdruez@nexb.com>

* Optimize the filters QuerySet of several DataspacedFilterSet #81

Signed-off-by: tdruez <tdruez@nexb.com>

* Set a max-width for the column-grouping CSS class #81

Signed-off-by: tdruez <tdruez@nexb.com>

* Add unit test amd changelog entry #81

Signed-off-by: tdruez <tdruez@nexb.com>

* Add ordering by inventory count #81

Signed-off-by: tdruez <tdruez@nexb.com>

---------

Signed-off-by: tdruez <tdruez@nexb.com>
  • Loading branch information
tdruez committed May 14, 2024
1 parent 8827373 commit 58f4ec2
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ Release notes
Existing Keywords are suggested for consistency but any values is now allowed.
https://github.com/nexB/dejacode/issues/48

- Display Product inventory count on the Product list view.
https://github.com/nexB/dejacode/issues/81

### Version 5.0.1

- Improve the stability of the "Check for new Package versions" feature.
Expand Down
6 changes: 3 additions & 3 deletions component_catalog/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class ComponentFilterSet(DataspacedFilterSet):
label=_("License"),
field_name="licenses__key",
to_field_name="key",
queryset=License.objects.all().only("key", "short_name", "dataspace"),
queryset=License.objects.only("key", "short_name", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search licenses",
),
Expand All @@ -80,7 +80,7 @@ class ComponentFilterSet(DataspacedFilterSet):
label=_("Keyword"),
to_field_name="label",
lookup_expr="contains",
queryset=ComponentKeyword.objects.all().only("label", "dataspace"),
queryset=ComponentKeyword.objects.only("label", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search keywords",
),
Expand Down Expand Up @@ -183,7 +183,7 @@ class PackageFilterSet(DataspacedFilterSet):
label=_("License"),
field_name="licenses__key",
to_field_name="key",
queryset=License.objects.all().only("key", "short_name", "dataspace"),
queryset=License.objects.only("key", "short_name", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search licenses",
),
Expand Down
7 changes: 7 additions & 0 deletions dejacode/static/css/dejacode_bootstrap.css
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ dd.reduced pre {
z-index: 1;
font-size: 0.875rem;
}
th.column-grouping {
width: 36px;
max-width: 36px;
}
th.column-selection {
width: 27px;
max-width: 27px;
Expand Down Expand Up @@ -336,6 +340,9 @@ table.products-table .column-license_expression {
table.products-table .column-owner {
min-width: 75px;
}
table.products-table .column-productinventoryitem_count {
max-width:80px;
}

/* -- Package List -- */
table.packages-table .column-usage_policy {
Expand Down
4 changes: 2 additions & 2 deletions license_library/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class LicenseFilterSet(DataspacedFilterSet):
label=_("Category"),
field_name="category__label",
to_field_name="label",
queryset=LicenseCategory.objects.all(),
queryset=LicenseCategory.objects.only("label", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search categories",
),
Expand All @@ -80,7 +80,7 @@ class LicenseFilterSet(DataspacedFilterSet):
label=_("License profile"),
field_name="license_profile__name",
to_field_name="name",
queryset=LicenseProfile.objects.all(),
queryset=LicenseProfile.objects.only("name", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search license profiles",
),
Expand Down
7 changes: 4 additions & 3 deletions product_portfolio/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class ProductFilterSet(DataspacedFilterSet):
"primary_language",
"owner",
"configuration_status",
"productinventoryitem_count",
],
field_labels={
"primary_language": "Language",
Expand All @@ -67,7 +68,7 @@ class ProductFilterSet(DataspacedFilterSet):
label=_("Configuration status"),
field_name="configuration_status__label",
to_field_name="label",
queryset=ProductStatus.objects.all(),
queryset=ProductStatus.objects.only("label", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search=False,
search_placeholder="Search configuration status",
Expand All @@ -84,7 +85,7 @@ class ProductFilterSet(DataspacedFilterSet):
label=_("License"),
field_name="licenses__key",
to_field_name="key",
queryset=License.objects.all(),
queryset=License.objects.only("key", "short_name", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search licenses",
),
Expand All @@ -93,7 +94,7 @@ class ProductFilterSet(DataspacedFilterSet):
label=_("Keyword"),
to_field_name="label",
lookup_expr="contains",
queryset=ComponentKeyword.objects.all().only("label", "dataspace"),
queryset=ComponentKeyword.objects.only("label", "dataspace__id"),
widget=BootstrapSelectMultipleWidget(
search_placeholder="Search keywords",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
{% endif %}
</td>
<td>{{ product.configuration_status|default_if_none:'' }}</td>
<td>
{% if product.productinventoryitem_count %}
<a href="{% inject_preserved_filters product.get_absolute_url %}#inventory">{{ product.productinventoryitem_count }}</a>
{% else %}
0
{% endif %}
</td>
<td>
<ul class="fa-ul ms-4 mb-0">
{% for keyword in product.keywords %}
Expand Down
29 changes: 28 additions & 1 deletion product_portfolio/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,33 @@ def test_product_portfolio_list_view_search(self):
self.assertEqual(1, len(response.context_data["object_list"]))
self.assertIn(self.product1, response.context_data["object_list"])

def test_product_portfolio_list_view_inventory_count(self):
self.client.login(username="nexb_user", password="secret")
url = resolve_url("product_portfolio:product_list")

response = self.client.get(url)
self.assertContains(response, "<td>0</td>", html=True)
product1 = response.context_data["object_list"][0]
self.assertEqual(0, product1.productinventoryitem_count)

ProductComponent.objects.create(
product=self.product1, component=self.component1, dataspace=self.dataspace
)
response = self.client.get(url)
expected = f'<a href="{self.product1.get_absolute_url()}#inventory">1</a>'
self.assertContains(response, expected, html=True)
product1 = response.context_data["object_list"][0]
self.assertEqual(1, product1.productinventoryitem_count)

ProductPackage.objects.create(
product=self.product1, package=self.package1, dataspace=self.dataspace
)
response = self.client.get(url)
expected = f'<a href="{self.product1.get_absolute_url()}#inventory">2</a>'
self.assertContains(response, expected, html=True)
product1 = response.context_data["object_list"][0]
self.assertEqual(2, product1.productinventoryitem_count)

def test_product_portfolio_product_tree_comparison_view_proper(self):
self.client.login(username="nexb_user", password="secret")
url = resolve_url(
Expand Down Expand Up @@ -2775,7 +2802,7 @@ def test_product_portfolio_load_sbom_view(self, mock_submit):
scan.assert_called_once()

@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.submit_project")
def test_product_portfolio_mport_manifest_view(self, mock_submit):
def test_product_portfolio_import_manifest_view(self, mock_submit):
mock_submit.return_value = None
self.client.login(username=self.super_user.username, password="secret")
url = self.product1.get_import_manifests_url()
Expand Down
9 changes: 9 additions & 0 deletions product_portfolio/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from django.core.paginator import Paginator
from django.db import models
from django.db import transaction
from django.db.models import Count
from django.db.models.functions import Lower
from django.forms import modelformset_factory
from django.http import Http404
Expand Down Expand Up @@ -147,6 +148,7 @@ class ProductListView(
Header("primary_language", "Language", filter="primary_language"),
Header("owner", "Owner"),
Header("configuration_status", "Status", filter="configuration_status"),
Header("productinventoryitem_count", "Inventory", help_text="Inventory count"),
Header("keywords", "Keywords", filter="keywords"),
)

Expand Down Expand Up @@ -175,6 +177,13 @@ def get_queryset(self):
.prefetch_related(
"licenses__usage_policy",
)
.annotate(
productinventoryitem_count=Count("productinventoryitem"),
)
.order_by(
"name",
"version",
)
)

def get_extra_add_urls(self):
Expand Down

0 comments on commit 58f4ec2

Please sign in to comment.