diff --git a/backend/ebios_rm/helpers.py b/backend/ebios_rm/helpers.py new file mode 100644 index 000000000..efd2d7779 --- /dev/null +++ b/backend/ebios_rm/helpers.py @@ -0,0 +1,87 @@ +from django.db.models.query import QuerySet +import math + + +def ecosystem_radar_chart_data(stakeholders_queryset: QuerySet): + qs = stakeholders_queryset + + def get_exposure_segment_id(value): + if value < 3: + return 1 + if value >= 3 and value < 7: + return 2 + if value >= 7 and value <= 9: + return 3 + if value > 9: + return 4 + return 0 + + def get_reliability_cluster(value): + if value < 4: + return "clst1" + if value >= 4 and value < 6: + return "clst2" + if value >= 6 and value <= 7: + return "clst3" + if value > 7: + return "clst4" + return 1 + + """ + // data format: f1-f4 (fiabilité cyber = maturité x confiance ) to get the clusters and colors + // x,y, z + // x: criticité calculée avec cap à 5,5 + // y: the angle (output of dict to make sure they end up on the right quadrant, min: 45, max:-45) -> done on BE + // z: the size of item (exposition = dependence x penetration) based on a dict, -> done on BE + // label: name of the 3rd party entity + Angles start at 56,25 (45+45/4) and end at -45-45/4 = 303,75 + """ + + c_data = {"clst1": [], "clst2": [], "clst3": [], "clst4": []} + r_data = {"clst1": [], "clst2": [], "clst3": [], "clst4": []} + angle_offsets = {"client": 135, "partner": 225, "supplier": 45} + + cnt_c_not_displayed = 0 + cnt_r_not_displayed = 0 + for sh in qs: + # current + c_reliability = sh.current_maturity * sh.current_trust + c_exposure = sh.current_dependency * sh.current_penetration + c_exposure_val = get_exposure_segment_id(c_exposure) * 4 + + c_criticality = ( + math.floor(sh.current_criticality * 100) / 100.0 + if sh.current_criticality <= 5 + else 5.25 + ) + + angle = angle_offsets[sh.category] + ( + get_exposure_segment_id(c_exposure) * (45 / 4) + ) + + vector = [c_criticality, angle, c_exposure_val, str(sh)] + + cluser_id = get_reliability_cluster(c_reliability) + c_data[cluser_id].append(vector) + + # residual + r_reliability = sh.residual_maturity * sh.residual_trust + r_exposure = sh.residual_dependency * sh.residual_penetration + r_exposure_val = get_exposure_segment_id(r_exposure) * 4 + + r_criticality = ( + math.floor(sh.residual_criticality * 100) / 100.0 + if sh.residual_criticality <= 5 + else 5.25 + ) + + angle = angle_offsets[sh.category] + ( + get_exposure_segment_id(r_exposure) * (45 / 4) + ) + + vector = [r_criticality, angle, r_exposure_val, str(sh)] + + cluser_id = get_reliability_cluster(r_reliability) + r_data[cluser_id].append(vector) + + return {"current": c_data, "residual": r_data} diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py index 8477fab8b..bbb46688b 100644 --- a/backend/ebios_rm/models.py +++ b/backend/ebios_rm/models.py @@ -477,7 +477,7 @@ def get_scope(self): return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study) def __str__(self): - return f"{self.entity.name} - {self.get_category_display()}" + return f"{self.entity.name}-{self.get_category_display()}" def save(self, *args, **kwargs): self.folder = self.ebios_rm_study.folder diff --git a/backend/ebios_rm/views.py b/backend/ebios_rm/views.py index 7f5786329..d1e78f9ab 100644 --- a/backend/ebios_rm/views.py +++ b/backend/ebios_rm/views.py @@ -1,7 +1,7 @@ import django_filters as df from core.serializers import RiskMatrixReadSerializer from core.views import BaseModelViewSet as AbstractBaseModelViewSet -from core.serializers import RiskMatrixReadSerializer +from .helpers import ecosystem_radar_chart_data from .models import ( EbiosRMStudy, FearedEvent, @@ -19,8 +19,6 @@ LONG_CACHE_TTL = 60 # mn -import math - class BaseModelViewSet(AbstractBaseModelViewSet): serializers_module = "ebios_rm.serializers" @@ -86,6 +84,12 @@ def update_workshop_step_status(self, request, pk, workshop, step): ) return Response(EbiosRMStudyReadSerializer(ebios_rm_study).data) + @action(detail=True, name="Get ecosystem radar chart data") + def ecosystem_chart_data(self, request, pk): + return Response( + ecosystem_radar_chart_data(Stakeholder.objects.filter(ebios_rm_study=pk)) + ) + class FearedEventViewSet(BaseModelViewSet): model = FearedEvent @@ -161,89 +165,7 @@ def category(self, request): @action(detail=False, name="Get chart data") def chart_data(self, request): - def get_exposure_segment_id(value): - if value < 3: - return 1 - if value >= 3 and value < 7: - return 2 - if value >= 7 and value <= 9: - return 3 - if value > 9: - return 4 - return 0 - - def get_reliability_cluster(value): - if value < 4: - return "clst1" - if value >= 4 and value < 6: - return "clst2" - if value >= 6 and value <= 7: - return "clst3" - if value > 7: - return "clst4" - return 1 - - """ - // data format: f1-f4 (fiabilité cyber = maturité x confiance ) to get the clusters and colors - // x,y, z - // x: criticité calculée avec cap à 5,5 - // y: the angle (output of dict to make sure they end up on the right quadrant, min: 45, max:-45) -> done on BE - // z: the size of item (exposition = dependence x penetration) based on a dict, -> done on BE - // label: name of the 3rd party entity - Angles start at 56,25 (45+45/4) and end at -45-45/4 = 303,75 - """ - - # we can add a filter on the Stakeholder concerned by the ebios study here - qs = Stakeholder.objects.all() - - c_data = {"clst1": [], "clst2": [], "clst3": [], "clst4": []} - r_data = {"clst1": [], "clst2": [], "clst3": [], "clst4": []} - angle_offsets = {"client": 135, "partner": 225, "supplier": 45} - - cnt_c_not_displayed = 0 - cnt_r_not_displayed = 0 - for sh in qs: - # current - c_reliability = sh.current_maturity * sh.current_trust - c_exposure = sh.current_dependency * sh.current_penetration - c_exposure_val = get_exposure_segment_id(c_exposure) * 4 - - c_criticality = ( - math.floor(sh.current_criticality * 100) / 100.0 - if sh.current_criticality <= 5 - else 5.25 - ) - - angle = angle_offsets[sh.category] + ( - get_exposure_segment_id(c_exposure) * (45 / 4) - ) - - vector = [c_criticality, angle, c_exposure_val, str(sh)] - - cluser_id = get_reliability_cluster(c_reliability) - c_data[cluser_id].append(vector) - - # residual - r_reliability = sh.residual_maturity * sh.residual_trust - r_exposure = sh.residual_dependency * sh.residual_penetration - r_exposure_val = get_exposure_segment_id(r_exposure) * 4 - - r_criticality = ( - math.floor(sh.residual_criticality * 100) / 100.0 - if sh.residual_criticality <= 5 - else 5.25 - ) - - angle = angle_offsets[sh.category] + ( - get_exposure_segment_id(r_exposure) * (45 / 4) - ) - - vector = [r_criticality, angle, r_exposure_val, str(sh)] - - cluser_id = get_reliability_cluster(r_reliability) - r_data[cluser_id].append(vector) - - return Response({"current": c_data, "residual": r_data}) + return Response(ecosystem_radar_chart_data(Stakeholder.objects.all())) class StrategicScenarioViewSet(BaseModelViewSet): diff --git a/frontend/messages/en.json b/frontend/messages/en.json index a94439d61..422da66de 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -1019,8 +1019,11 @@ "markAsInProgress": "Mark as in progress", "riskAnalyses": "Risk analyses", "client": "Client", + "clients": "Clients", "partner": "Partner", + "partners": "Partners", "supplier": "Supplier", + "suppliers": "Suppliers", "referenceEntity": "Reference entity", "referenceEntitySemiColon": "Reference entity:", "moderate": "Moderate", @@ -1067,5 +1070,8 @@ "operationalScenarioThreatsHelpText": "Unitary actions carried out by a risk source on a critical support asset as part of an operational scenario", "operationalScenarioIsSelectedHelpText": "Selection of the operational scenario for the study", "attackPathStakeholdersHelpText": "Stakeholders involved in the attack path", - "attackPathIsSelectedHelpText": "Selection of the attack path for the study" + "attackPathIsSelectedHelpText": "Selection of the attack path for the study", + "ecosystemRadar": "Ecosystem radar", + "criticalitySemiColon": "Criticality:", + "cyberFiability": "Cyber fiability" } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 164389a7a..f4a655fee 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -1019,8 +1019,11 @@ "markAsInProgress": "Reprendre", "riskAnalyses": "Analyses de risques", "client": "Client", + "clients": "Clients", "partner": "Partenaire", + "partners": "Partenaires", "supplier": "Fournisseur", + "suppliers": "Fournisseurs", "referenceEntity": "Entité de référence", "referenceEntitySemiColon": "Entité de référence :", "moderate": "Modérée", @@ -1067,5 +1070,8 @@ "operationalScenarioThreatsHelpText": "Actions élémentaires réalisées par une source de risque sur un bien support critique dans le cadre d'un scénario opérationnel", "operationalScenarioIsSelectedHelpText": "Sélection du scénario opérationnel pour l'étude", "attackPathStakeholdersHelpText": "Parties prenantes impactées par le chemin d'attaque", - "attackPathIsSelectedHelpText": "Sélection du chemin d'attaque pour l'étude" + "attackPathIsSelectedHelpText": "Sélection du chemin d'attaque pour l'étude", + "ecosystemRadar": "Radar de l'écosystème", + "criticalitySemiColon": "Criticité :", + "cyberFiability": "Fiabilité cyber" } diff --git a/frontend/src/lib/components/Chart/EcosystemRadarChart.svelte b/frontend/src/lib/components/Chart/EcosystemRadarChart.svelte index df867d697..dbe9b1cb9 100644 --- a/frontend/src/lib/components/Chart/EcosystemRadarChart.svelte +++ b/frontend/src/lib/components/Chart/EcosystemRadarChart.svelte @@ -1,8 +1,7 @@ - -
- - - -
-
+
+ + + + {m.ecosystemRadar()} + +
+
+ + +
+
+
+
+
+ +
+ + + +
+
+
diff --git a/frontend/src/routes/(app)/(internal)/experimental/ecosystem/+page.svelte b/frontend/src/routes/(app)/(internal)/experimental/ecosystem/+page.svelte index d7cc6d15f..37a66b7c2 100644 --- a/frontend/src/routes/(app)/(internal)/experimental/ecosystem/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/experimental/ecosystem/+page.svelte @@ -2,15 +2,12 @@ import type { PageData } from './$types'; export let data: PageData; import EcosystemRadarChart from '$lib/components/Chart/EcosystemRadarChart.svelte'; + import * as m from '$paraglide/messages.js';
- - + +