Skip to content

Commit

Permalink
Improve ProductTabVulnerabilitiesView #95
Browse files Browse the repository at this point in the history
Signed-off-by: tdruez <[email protected]>
  • Loading branch information
tdruez committed Aug 30, 2024
1 parent 1f6ed29 commit ec9d975
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 80 deletions.
2 changes: 1 addition & 1 deletion component_catalog/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ class Meta:

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.filters["max_score"].extra["widget"] = DropDownRightWidget()
self.filters["max_score"].extra["widget"] = DropDownRightWidget(anchor=self.anchor)

def filter_by_score_range(self, queryset, name, value):
if value in vulnerability_score_ranges:
Expand Down
17 changes: 17 additions & 0 deletions dejacode/static/css/dejacode_bootstrap.css
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,23 @@ table.vulnerabilities-table .column-summary {
width: 320px;
}

/* -- Dependency tab -- */
#tab_dependencies .column-for_package {
width: 250px;
}
#tab_dependencies .column-resolved_to_package {
width: 250px;
}
#tab_dependencies .column-is_runtime {
width: 100px;
}
#tab_dependencies .column-column-is_optional {
width: 100px;
}
#tab_dependencies .column-column-is_resolved {
width: 88px;
}

/* -- Package Details -- */
textarea.licenseexpressionwidget {
height: 62px;
Expand Down
1 change: 1 addition & 0 deletions dje/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def __init__(self, *args, **kwargs):

self.dynamic_qs = kwargs.pop("dynamic_qs", True)
self.parent_qs_cache = {}
self.anchor = kwargs.pop("anchor", None)

super().__init__(*args, **kwargs)

Expand Down
4 changes: 2 additions & 2 deletions dje/templates/includes/object_list_table_header.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
{% if header.sort %}
{% with query_no_sort=filter.get_query_no_sort %}
{% if filter.form.sort.data and header.field_name in filter.form.sort.data.0 %}
<a href="?{% if query_no_sort %}{{ query_no_sort }}&{% endif %}sort={% if '-' not in filter.form.sort.data.0 %}-{% endif %}{{ header.field_name }}" class="sort active" aria-label="Sort"><i class="fas fa-sort-{% if '-' not in filter.form.sort.data.0 %}up{% else %}down{% endif %}"></i></a>
<a href="?{% if query_no_sort %}{{ query_no_sort }}&{% endif %}sort={% if '-' not in filter.form.sort.data.0 %}-{% endif %}{{ header.field_name }}{% if tab_id %}#{{ tab_id }}{% endif %}" class="sort active" aria-label="Sort"><i class="fas fa-sort-{% if '-' not in filter.form.sort.data.0 %}up{% else %}down{% endif %}"></i></a>
{% else %}
<a href="?{% if query_no_sort %}{{ query_no_sort }}&{% endif %}sort={{ header.field_name }}" class="sort" aria-label="Sort"><i class="fas fa-sort"></i></a>
<a href="?{% if query_no_sort %}{{ query_no_sort }}&{% endif %}sort={{ header.field_name }}{% if tab_id %}#{{ tab_id }}{% endif %}" class="sort" aria-label="Sort"><i class="fas fa-sort"></i></a>
{% endif %}
{% endwith %}
{% endif %}
Expand Down
1 change: 1 addition & 0 deletions dje/tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def test_permissions_get_all_tabsets(self):
"notice",
"license",
"owner",
"vulnerabilities",
"dependencies",
"activity",
"imports",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,7 @@
{% spaceless %}
{% include 'tabs/pagination.html' %}
<table class="table table-bordered table-hover table-md text-break">
<thead>
<tr>
<th style="min-width: 250px">
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.for_package }}">
{% trans 'For package' %}
</span>
<div class="float-end">
{{ filterset.form.for_package }}
</div>
</th>
<th style="min-width: 250px">
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.resolved_to_package }}">
{% trans 'Resolved to package' %}
</span>
<div class="float-end">
{{ filterset.form.resolved_to_package }}
</div>
</th>
<th>
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.declared_dependency }}">
{% trans 'Declared dependency' %}
</span>
</th>
<th>
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.scope }}">
{% trans 'Scope' %}
</span>
</th>
<th>
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.extracted_requirement }}">
{% trans 'Extracted requirement' %}
</span>
</th>
<th style="min-width: 100px">
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.is_runtime }}">
{% trans 'Runtime' %}
</span>
<div class="float-end">
{{ filterset.form.is_runtime }}
</div>
</th>
<th style="min-width: 100px">
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.is_optional }}">
{% trans 'Optional' %}
</span>
<div class="float-end">
{{ filterset.form.is_optional }}
</div>
</th>
<th style="min-width: 88px">
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ help_texts.is_resolved }}">
{% trans 'Pinned' %}
</span>
<div class="float-end">
{{ filterset.form.is_resolved }}
</div>
</th>
</tr>
</thead>
{% include 'includes/object_list_table_header.html' %}
<tbody class="text-break">
{% for dependency in page_obj.object_list %}
<tr class="{% cycle 'odd' '' %}">
Expand Down Expand Up @@ -134,7 +76,7 @@
<td colspan="10">
No results.
{% if filterset.is_active %}
<a href="#" hx-get="{{ request.path }}?all=true#dependencies" hx-target="{{ tab_id_html }}">
<a href="#" hx-get="{{ request.path }}?all=true#{{ tab_id }}" hx-target="{{ tab_id_html }}">
Clear search and filters
</a>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{% load i18n %}
{% include 'tabs/pagination.html' %}
{{ table_headers }}
<table class="table table-bordered table-hover table-md text-break">
{% include 'includes/object_list_table_header.html' %}
<tbody>
Expand Down Expand Up @@ -49,6 +48,17 @@
</ul>
</td>
</tr>
{% empty %}
<tr>
<td colspan="10">
No results.
{% if filterset.is_active %}
<a href="#" hx-get="{{ request.path }}?all=true#{{ tab_id }}" hx-target="{{ tab_id_html }}">
Clear search and filters
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
4 changes: 2 additions & 2 deletions product_portfolio/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_product_portfolio_detail_view_tab_inventory_and_hierarchy_availability(
ProductComponent.objects.create(
product=self.product1, component=self.component1, dataspace=self.dataspace
)
with self.assertNumQueries(29):
with self.assertNumQueries(30):
response = self.client.get(url)
self.assertContains(response, expected1)
self.assertContains(response, expected2)
Expand All @@ -158,7 +158,7 @@ def test_product_portfolio_detail_view_tab_inventory_availability(self):
ProductPackage.objects.create(
product=self.product1, package=self.package1, dataspace=self.dataspace
)
with self.assertNumQueries(26):
with self.assertNumQueries(27):
response = self.client.get(url)
self.assertContains(response, expected)

Expand Down
35 changes: 21 additions & 14 deletions product_portfolio/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,17 +998,28 @@ class ProductTabDependenciesView(
LoginRequiredMixin,
BaseProductView,
PreviousNextPaginationMixin,
TableHeaderMixin,
TabContentView,
):
template_name = "product_portfolio/tabs/tab_dependencies.html"
paginate_by = 50
table_model = ProductDependency
filterset_class = DependencyFilterSet
query_dict_page_param = "dependencies-page"
tab_id = "dependencies"
table_headers = (
Header("for_package", _("For package"), filter="for_package"),
Header("resolved_to_package", _("Resolved to package"), filter="resolved_to_package"),
Header("declared_dependency", _("Declared dependency")),
Header("scope", _("Scope")),
Header("extracted_requirement", _("Extracted requirement")),
Header("is_runtime", _("Runtime"), filter="is_runtime"),
Header("is_optional", _("Optional"), filter="is_optional"),
Header("is_resolved", _("Resolved"), filter="is_resolved"),
)

def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
product = self.object

for_package_qs = Package.objects.only_rendering_fields().with_vulnerability_count()
resolved_to_package_qs = (
Package.objects.only_rendering_fields()
Expand All @@ -1020,14 +1031,16 @@ def get_context_data(self, **kwargs):
Prefetch("resolved_to_package", resolved_to_package_qs),
)

filter_dependency = DependencyFilterSet(
self.filterset = self.filterset_class(
self.request.GET,
queryset=dependency_qs,
dataspace=product.dataspace,
prefix=self.tab_id,
)

filtered_and_ordered_qs = filter_dependency.qs.order_by(
context_data = super().get_context_data(**kwargs)

filtered_and_ordered_qs = self.filterset.qs.order_by(
"for_package__type",
"for_package__namespace",
"for_package__name",
Expand All @@ -1039,19 +1052,12 @@ def get_context_data(self, **kwargs):
page_number = self.request.GET.get(self.query_dict_page_param)
page_obj = paginator.get_page(page_number)

help_texts = {
field.name: field.help_text
for field in ProductDependency._meta.get_fields()
if hasattr(field, "help_text")
}

context_data.update(
{
"filterset": filter_dependency,
"filterset": self.filterset,
"page_obj": page_obj,
"total_count": product.dependencies.count(),
"search_query": self.request.GET.get("dependencies-q", ""),
"help_texts": help_texts,
}
)

Expand All @@ -1074,9 +1080,9 @@ class ProductTabVulnerabilitiesView(
TableHeaderMixin,
TabContentView,
):
# TODO: Remove duplication + check queries: assertMax
# TODO: check queries: assertMax
template_name = "product_portfolio/tabs/tab_vulnerabilities.html"
paginate_by = 5
paginate_by = 50
query_dict_page_param = "vulnerabilities-page"
tab_id = "vulnerabilities"
table_model = Vulnerability
Expand Down Expand Up @@ -1108,6 +1114,7 @@ def get_context_data(self, **kwargs):
queryset=vulnerability_qs,
dataspace=product.dataspace,
prefix=self.tab_id,
anchor=f"#{self.tab_id}",
)

# The self.filterset needs to be set before calling super()
Expand Down

0 comments on commit ec9d975

Please sign in to comment.