Skip to content

Commit eff3494

Browse files
committed
integrating the clarity-compliance in UI
Signed-off-by: NucleonGodX <racerpro41@gmail.com>
1 parent a018711 commit eff3494

File tree

7 files changed

+118
-0
lines changed

7 files changed

+118
-0
lines changed

scanpipe/pipes/license_clarity.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
from pathlib import Path
4343

44+
from django.conf import settings
4445
from django.core.exceptions import ValidationError
4546

4647
import saneyaml
@@ -163,3 +164,27 @@ def load_clarity_thresholds_from_file(file_path):
163164
return load_clarity_thresholds_from_yaml(yaml_content)
164165
except (OSError, UnicodeDecodeError) as e:
165166
raise ValidationError(f"Error reading file {file_path}: {e}")
167+
168+
169+
def get_project_clarity_thresholds(project):
170+
"""
171+
Get clarity thresholds for a project, checking multiple sources.
172+
173+
Returns:
174+
ClarityThresholdsPolicy or None: Policy object if thresholds are configured
175+
176+
"""
177+
if hasattr(project, "get_input_policies_file"):
178+
policies_file = project.get_input_policies_file()
179+
if policies_file:
180+
policy = load_clarity_thresholds_from_file(policies_file)
181+
if policy:
182+
return policy
183+
184+
global_policies_file = getattr(settings, "SCANCODEIO_POLICIES_FILE", None)
185+
if global_policies_file:
186+
policy = load_clarity_thresholds_from_file(global_policies_file)
187+
if policy:
188+
return policy
189+
190+
return None

scanpipe/pipes/scancode.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from scanpipe.models import DiscoveredDependency
5353
from scanpipe.models import DiscoveredPackage
5454
from scanpipe.pipes import flag
55+
from scanpipe.pipes.license_clarity import get_project_clarity_thresholds
5556

5657
logger = logging.getLogger("scanpipe.pipes")
5758

@@ -931,7 +932,10 @@ def make_results_summary(project, scan_results_location):
931932
Extract selected sections of the Scan results, such as the `summary`
932933
`license_clarity_score`, and `license_matches` related data.
933934
The `key_files` are also collected and injected in the `summary` output.
935+
Additionally, store clarity_compliance_alert in project's extra_data.
934936
"""
937+
import json
938+
935939
from scanpipe.api.serializers import CodebaseResourceSerializer
936940
from scanpipe.api.serializers import DiscoveredPackageSerializer
937941

@@ -964,4 +968,16 @@ def make_results_summary(project, scan_results_location):
964968
DiscoveredPackageSerializer(package).data for package in key_files_packages_qs
965969
]
966970

971+
clarity_score = summary.get("license_clarity_score", {}).get("score")
972+
if clarity_score is not None:
973+
clarity_policy = get_project_clarity_thresholds(project)
974+
if clarity_policy:
975+
alert = clarity_policy.get_alert_for_score(clarity_score)
976+
summary["clarity_compliance_alert"] = alert
977+
978+
extra_data = project.extra_data or {}
979+
extra_data["clarity_compliance_alert"] = alert
980+
project.extra_data = extra_data
981+
project.save(update_fields=["extra_data"])
982+
967983
return summary
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{% if clarity_compliance_alert %}
2+
<div class="column is-half">
3+
<nav id="clarity-compliance-panel" class="panel
4+
{% if clarity_compliance_alert == 'error' %}is-danger
5+
{% elif clarity_compliance_alert == 'warning' %}is-warning
6+
{% elif clarity_compliance_alert == 'ok' %}is-success
7+
{% else %}is-light{% endif %}">
8+
<p class="panel-heading">
9+
Clarity Compliance
10+
</p>
11+
<div class="panel-block">
12+
<span class="tag is-rounded
13+
{% if clarity_compliance_alert == 'error' %}is-danger
14+
{% elif clarity_compliance_alert == 'warning' %}is-warning
15+
{% elif clarity_compliance_alert == 'ok' %}is-success
16+
{% else %}is-light{% endif %}">
17+
{{ clarity_compliance_alert|title }}
18+
</span>
19+
{% if clarity_compliance_score %}
20+
<span class="ml-2">Score: {{ clarity_compliance_score }}</span>
21+
{% endif %}
22+
<span class="ml-2">
23+
{% if clarity_compliance_alert == "ok" %}
24+
License clarity is sufficient.
25+
{% elif clarity_compliance_alert == "warning" %}
26+
License clarity is borderline. Please review.
27+
{% elif clarity_compliance_alert == "error" %}
28+
License clarity is insufficient! Immediate attention required.
29+
{% else %}
30+
No clarity compliance data available.
31+
{% endif %}
32+
</span>
33+
</div>
34+
</nav>
35+
</div>
36+
{% endif %}

scanpipe/templates/scanpipe/project_detail.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
{% if policies_enabled %}
121121
<div class="columns">
122122
<div hx-get="{% url 'project_compliance_panel' project.slug %}" hx-trigger="load" hx-swap="outerHTML"></div>
123+
<div hx-get="{% url 'clarity_compliance_panel' project.slug %}" hx-trigger="load" hx-swap="outerHTML"></div>
123124
</div>
124125
{% endif %}
125126

scanpipe/tests/test_views.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,24 @@ def test_scanpipe_views_project_compliance_panel_view(self, mock_policies_enable
10081008
expected = f"/project/{self.project1.slug}/packages/?compliance_alert=error"
10091009
self.assertContains(response, expected)
10101010

1011+
@mock.patch.object(Project, "policies_enabled", new_callable=mock.PropertyMock)
1012+
def test_scanpipe_views_project_clarity_compliance_panel_view(
1013+
self, mock_policies_enabled
1014+
):
1015+
url = reverse("clarity_compliance_panel", args=[self.project1.slug])
1016+
self.project1.extra_data = {"clarity_compliance_alert": "error"}
1017+
self.project1.save(update_fields=["extra_data"])
1018+
1019+
mock_policies_enabled.return_value = False
1020+
response = self.client.get(url)
1021+
self.assertEqual(404, response.status_code)
1022+
1023+
mock_policies_enabled.return_value = True
1024+
response = self.client.get(url)
1025+
self.assertContains(response, "Clarity Compliance")
1026+
self.assertContains(response, "Error")
1027+
self.assertContains(response, "License clarity is insufficient")
1028+
10111029
def test_scanpipe_views_pipeline_help_view(self):
10121030
url = reverse("pipeline_help", args=["not_existing_pipeline"])
10131031
response = self.client.get(url)

scanpipe/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@
221221
views.ProjectCompliancePanelView.as_view(),
222222
name="project_compliance_panel",
223223
),
224+
path(
225+
"projects/<slug:slug>/clarity_compliance_panel/",
226+
views.ClarityCompliancePanelView.as_view(),
227+
name="clarity_compliance_panel",
228+
),
224229
path(
225230
"project/<slug:slug>/",
226231
views.ProjectDetailView.as_view(),

scanpipe/views.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,23 @@ def get_context_data(self, **kwargs):
12121212
return context
12131213

12141214

1215+
class ClarityCompliancePanelView(ConditionalLoginRequired, generic.DetailView):
1216+
model = Project
1217+
template_name = "scanpipe/panels/clarity_compliance.html"
1218+
1219+
def get_context_data(self, **kwargs):
1220+
context = super().get_context_data(**kwargs)
1221+
project = self.object
1222+
1223+
if not project.policies_enabled:
1224+
raise Http404
1225+
1226+
extra_data = project.extra_data or {}
1227+
context["clarity_compliance_alert"] = extra_data.get("clarity_compliance_alert")
1228+
1229+
return context
1230+
1231+
12151232
class ProjectArchiveView(ConditionalLoginRequired, SingleObjectMixin, FormView):
12161233
model = Project
12171234
http_method_names = ["post"]

0 commit comments

Comments
 (0)