Skip to content

Commit ea2c8ce

Browse files
authored
UI enhancements (#1267)
* Render the JSON tab fields as YML by default Signed-off-by: tdruez <tdruez@nexb.com> * Remove unused template Signed-off-by: tdruez <tdruez@nexb.com> * Do not display the "Show all/Hide" button if smaller the max height Signed-off-by: tdruez <tdruez@nexb.com> * Remove the line return in log strings Signed-off-by: tdruez <tdruez@nexb.com> * Upgrade multiple libraries to their latest version Signed-off-by: tdruez <tdruez@nexb.com> --------- Signed-off-by: tdruez <tdruez@nexb.com>
1 parent f7b9579 commit ea2c8ce

File tree

8 files changed

+95
-112
lines changed

8 files changed

+95
-112
lines changed

scancodeio/static/main.js

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -60,50 +60,61 @@ function setupCloseModalButtons() {
6060

6161
// Tabs
6262

63-
function setupTabs() {
64-
const $tabLinks = getAll('.tabs a');
65-
66-
function activateTab($tabLink) {
67-
const activeLink = document.querySelector('.tabs .is-active');
68-
const activeTabContent = document.querySelector('.tab-content.is-active');
69-
const targetId = $tabLink.dataset.target;
70-
const targetTabContent = document.getElementById(targetId);
71-
72-
activeLink.classList.remove('is-active');
73-
$tabLink.parentNode.classList.add('is-active');
74-
if (activeTabContent) activeTabContent.classList.remove('is-active');
75-
if (targetTabContent) targetTabContent.classList.add('is-active');
63+
function activateTab(tabLink) {
64+
const tabsContainer = tabLink.closest('.tabs');
65+
if (!tabsContainer) return; // Safety check
66+
67+
const tabs = tabsContainer.querySelectorAll('li');
68+
const tabContents = tabsContainer.parentNode.querySelectorAll('.tab-content');
69+
70+
// Deactivate all tabs
71+
tabs.forEach(item => item.classList.remove('is-active'));
72+
// Deactivate all tab contents
73+
tabContents.forEach(content => content.classList.remove('is-active'));
74+
75+
tabLink.parentNode.classList.add('is-active');
76+
const targetId = tabLink.getAttribute('data-target');
77+
const targetContent = tabsContainer.parentNode.querySelector(`#${targetId}`);
78+
if (targetContent) {
79+
targetContent.classList.add('is-active');
80+
}
7681

77-
// Set the active tab in the URL hash. The "tab-" prefix is removed to avoid
78-
// un-wanted scrolling to the related "id" element
82+
// Conditionally update the URL hash
83+
const storeInHash = !tabsContainer.classList.contains('disable-hash-storage');
84+
if (storeInHash) {
7985
document.location.hash = targetId.replace('tab-', '');
8086
}
87+
}
8188

82-
// Activate the related tab using the current URL hash
83-
function activateTabFromHash() {
84-
let tabLink;
89+
function activateTabFromHash() {
90+
const hashValue = document.location.hash.slice(1); // Remove the '#' from the hash
91+
if (!hashValue) return;
8592

86-
if (document.location.hash !== "") {
87-
let tabName = document.location.hash.slice(1);
88-
tabLink = document.querySelector(`a[data-target="tab-${tabName}"]`);
89-
}
90-
else if ($tabLinks.length) {
91-
tabLink = $tabLinks[0];
92-
}
93-
if (tabLink) activateTab(tabLink);
93+
const tabLink = document.querySelector(`a[data-target="tab-${hashValue}"]`);
94+
if (tabLink) {
95+
activateTab(tabLink);
9496
}
97+
}
9598

96-
$tabLinks.forEach(function ($el) {
97-
$el.addEventListener('click', function () {
98-
activateTab($el)
99+
function setupTabs() {
100+
const tabsContainers = document.querySelectorAll('.tabs');
101+
102+
tabsContainers.forEach(tabsContainer => {
103+
const tabLinks = tabsContainer.querySelectorAll('a[data-target]');
104+
105+
tabLinks.forEach(tabLink => {
106+
tabLink.addEventListener('click', (event) => {
107+
event.preventDefault(); // Prevent the default behavior of the anchor tag
108+
activateTab(tabLink);
109+
});
99110
});
100111
});
101112

102113
// Activate the related tab if hash is present in the URL on page loading
103-
activateTabFromHash();
114+
activateTabFromHash();
104115
// Enable tab history navigation (using previous/next browser button for example)
105116
// by detecting URL hash changes.
106-
window.addEventListener("hashchange", () => {activateTabFromHash()});
117+
window.addEventListener("hashchange", activateTabFromHash);
107118
}
108119

109120
// Menu
@@ -147,12 +158,18 @@ function setupHighlightControls() {
147158
const $highlightShows = getAll(".is-more-show");
148159

149160
$highlightShows.forEach(function ($el) {
150-
$el.addEventListener("click", function () {
151-
let text = $el.querySelector("strong").textContent;
152-
let newText = text === "Show all" ? "Hide" : "Show all";
153-
$el.querySelector("strong").textContent = newText;
154-
$el.parentNode.classList.toggle("is-more-clipped");
155-
});
161+
const parentDiv = $el.parentNode;
162+
163+
if (parentDiv.scrollHeight <= 250) {
164+
$el.style.display = "none";
165+
} else {
166+
$el.addEventListener("click", function () {
167+
let text = $el.querySelector("strong").textContent;
168+
let newText = text === "Show all" ? "Hide" : "Show all";
169+
$el.querySelector("strong").textContent = newText;
170+
$el.parentNode.classList.toggle("is-more-clipped");
171+
});
172+
}
156173
});
157174
}
158175

scanpipe/migrations/0031_scancode_toolkit_v32_data_updates.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def compute_package_declared_license_expression_spdx(apps, schema_editor):
2525
).only("declared_license_expression")
2626

2727
object_count = queryset.count()
28-
logger.info(f"\nCompute declared_license_expression_spdx for {object_count:,} packages.")
28+
logger.info(f"Compute declared_license_expression_spdx for {object_count:,} packages.")
2929

3030
chunk_size = 2000
3131
iterator = queryset.iterator(chunk_size=chunk_size)
@@ -65,7 +65,7 @@ def compute_resource_detected_license_expression(apps, schema_editor):
6565
)
6666

6767
object_count = queryset.count()
68-
logger.info(f"\nCompute detected_license_expression for {object_count:,} resources.")
68+
logger.info(f"Compute detected_license_expression for {object_count:,} resources.")
6969

7070
chunk_size = 2000
7171
iterator = queryset.iterator(chunk_size=chunk_size)
@@ -168,7 +168,7 @@ def compute_resource_license_detections(apps, schema_editor):
168168
queryset = CodebaseResource.objects.filter(~Q(licenses=[])).only("licenses")
169169

170170
object_count = queryset.count()
171-
logger.info(f"\nCompute license_detections for {object_count:,} resources.")
171+
logger.info(f"Compute license_detections for {object_count:,} resources.")
172172

173173
chunk_size = 2000
174174
iterator = queryset.iterator(chunk_size=chunk_size)

scanpipe/migrations/0055_discoveredpackage_datafile_paths.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def update_package_datasource_ids(apps, schema_editor):
1818

1919
object_count = queryset.count()
2020
if object_count:
21-
logger.info(f"\nCompute datasource_ids for {object_count:,} packages.")
21+
logger.info(f"Compute datasource_ids for {object_count:,} packages.")
2222

2323
chunk_size = 2000
2424
iterator = queryset.iterator(chunk_size=chunk_size)

scanpipe/templates/scanpipe/base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
</style>
8383
{% block extrahead %}{% endblock %}
8484
</head>
85-
<body>
85+
<body class="pb-5">
8686
{% block content %}{% endblock %}
8787
{% include 'scanpipe/modals/search_syntax_modal.html' %}
8888
<script src="{% static 'main.js' %}" crossorigin="anonymous"></script>

scanpipe/templates/scanpipe/modals/search_syntax_modal.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
</header>
88
<div class="modal-card-body p-2 pb-4">
99
<div class="content">
10-
<div class="tabs is-boxed mb-4">
10+
<div class="tabs disable-hash-storage is-boxed mb-4">
1111
<ul class="m-0">
1212
<li class="is-active">
13-
<a data-target="tab-syntax" >Syntax</a>
13+
<a data-target="tab-syntax">Syntax</a>
1414
</li>
1515
<li>
1616
<a data-target="tab-fields">Fields</a>

scanpipe/templates/scanpipe/tabset/tab_detections.html

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

scanpipe/views.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,13 @@ def get_fields_data(self, fields):
306306
return fields_data
307307

308308
def get_field_value(self, field_name, render_func=None):
309-
"""Return the formatted value for the given `field_name` of the object."""
309+
"""
310+
Return the formatted value of the specified `field_name` from the object.
311+
312+
By default, JSON types (list and dict) are rendered as YAML.
313+
If a `render_func` is provided, it will take precedence and be used for
314+
rendering the value.
315+
"""
310316
field_value = getattr(self.object, field_name, None)
311317

312318
if field_value and render_func:
@@ -315,12 +321,9 @@ def get_field_value(self, field_name, render_func=None):
315321
if isinstance(field_value, Manager):
316322
return list(field_value.all())
317323

318-
list_fields = ["datafile_paths", "datasource_ids"]
319-
320-
if isinstance(field_value, list):
321-
if field_name not in list_fields:
322-
with suppress(TypeError):
323-
field_value = "\n".join(field_value)
324+
if isinstance(field_value, (list, dict)):
325+
with suppress(Exception):
326+
field_value = render_as_yaml(field_value)
324327

325328
return field_value
326329

@@ -1706,14 +1709,14 @@ class CodebaseResourceDetailsView(
17061709
"field_name": "detected_license_expression_spdx",
17071710
"label": "Detected license expression (SPDX)",
17081711
},
1709-
{"field_name": "license_detections", "render_func": render_as_yaml},
1710-
{"field_name": "license_clues", "render_func": render_as_yaml},
1712+
"license_detections",
1713+
"license_clues",
17111714
"percentage_of_license_text",
1712-
{"field_name": "copyrights", "render_func": render_as_yaml},
1713-
{"field_name": "holders", "render_func": render_as_yaml},
1714-
{"field_name": "authors", "render_func": render_as_yaml},
1715-
{"field_name": "emails", "render_func": render_as_yaml},
1716-
{"field_name": "urls", "render_func": render_as_yaml},
1715+
"copyrights",
1716+
"holders",
1717+
"authors",
1718+
"emails",
1719+
"urls",
17171720
],
17181721
"icon_class": "fa-solid fa-search",
17191722
},
@@ -1728,9 +1731,7 @@ class CodebaseResourceDetailsView(
17281731
"template": "scanpipe/tabset/tab_relations.html",
17291732
},
17301733
"extra_data": {
1731-
"fields": [
1732-
{"field_name": "extra_data", "render_func": render_as_yaml},
1733-
],
1734+
"fields": ["extra_data"],
17341735
"verbose_name": "Extra",
17351736
"icon_class": "fa-solid fa-plus-square",
17361737
},
@@ -1879,7 +1880,7 @@ class DiscoveredPackageDetailsView(
18791880
{"field_name": "sha256", "label": "SHA256"},
18801881
{"field_name": "sha512", "label": "SHA512"},
18811882
"file_references",
1882-
{"field_name": "parties", "render_func": render_as_yaml},
1883+
"parties",
18831884
"missing_resources",
18841885
"modified_resources",
18851886
"package_uid",
@@ -1904,11 +1905,8 @@ class DiscoveredPackageDetailsView(
19041905
"copyright",
19051906
"holder",
19061907
"notice_text",
1907-
{"field_name": "license_detections", "render_func": render_as_yaml},
1908-
{
1909-
"field_name": "other_license_detections",
1910-
"render_func": render_as_yaml,
1911-
},
1908+
"license_detections",
1909+
"other_license_detections",
19121910
],
19131911
"icon_class": "fa-solid fa-file-contract",
19141912
},
@@ -1923,14 +1921,14 @@ class DiscoveredPackageDetailsView(
19231921
"template": "scanpipe/tabset/tab_dependencies.html",
19241922
},
19251923
"vulnerabilities": {
1926-
"fields": ["affected_by_vulnerabilities"],
1924+
"fields": [
1925+
{"field_name": "affected_by_vulnerabilities", "render_func": list},
1926+
],
19271927
"icon_class": "fa-solid fa-bug",
19281928
"template": "scanpipe/tabset/tab_vulnerabilities.html",
19291929
},
19301930
"extra_data": {
1931-
"fields": [
1932-
{"field_name": "extra_data", "render_func": render_as_yaml},
1933-
],
1931+
"fields": ["extra_data"],
19341932
"verbose_name": "Extra",
19351933
"icon_class": "fa-solid fa-plus-square",
19361934
},
@@ -2052,7 +2050,9 @@ class DiscoveredDependencyDetailsView(
20522050
"icon_class": "fa-solid fa-info-circle",
20532051
},
20542052
"vulnerabilities": {
2055-
"fields": ["affected_by_vulnerabilities"],
2053+
"fields": [
2054+
{"field_name": "affected_by_vulnerabilities", "render_func": list},
2055+
],
20562056
"icon_class": "fa-solid fa-bug",
20572057
"template": "scanpipe/tabset/tab_vulnerabilities.html",
20582058
},

setup.cfg

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ install_requires =
5454
# Django related
5555
Django==5.0.6
5656
django-environ==0.11.2
57-
django-crispy-forms==2.1
57+
django-crispy-forms==2.2
5858
crispy-bootstrap3==2024.1
5959
django-filter==24.2
6060
djangorestframework==3.15.1
@@ -66,7 +66,7 @@ install_requires =
6666
# Task queue
6767
rq==1.16.2
6868
django-rq==2.10.2
69-
redis==5.0.5
69+
redis==5.0.6
7070
# WSGI server
7171
gunicorn==22.0.0
7272
# Docker
@@ -85,13 +85,13 @@ install_requires =
8585
aboutcode-toolkit==10.1.0
8686
# Utilities
8787
XlsxWriter==3.2.0
88-
openpyxl==3.1.3
88+
openpyxl==3.1.4
8989
requests==2.32.3
9090
gitpython==3.1.43
9191
# Profiling
9292
pyinstrument==4.6.2
9393
# CycloneDX
94-
cyclonedx-python-lib==7.4.0
94+
cyclonedx-python-lib==7.4.1
9595
jsonschema==4.22.0
9696
# Font Awesome
9797
fontawesomefree==6.5.1
@@ -108,13 +108,13 @@ install_requires =
108108
[options.extras_require]
109109
dev =
110110
# Validation
111-
flake8==7.0.0
111+
flake8==7.1.0
112112
black==24.4.2
113113
isort==5.13.2
114114
doc8==1.1.1
115115
pydocstyle==6.3.0
116116
# Security analyzer
117-
bandit==1.7.8
117+
bandit==1.7.9
118118
# Debug
119119
django-debug-toolbar==4.4.2
120120
# Documentation

0 commit comments

Comments
 (0)