diff --git a/backend/ebios_rm/migrations/0004_remove_roto_pertinence_alter_roto_feared_events.py b/backend/ebios_rm/migrations/0004_remove_roto_pertinence_alter_roto_feared_events.py new file mode 100644 index 000000000..954fe19f8 --- /dev/null +++ b/backend/ebios_rm/migrations/0004_remove_roto_pertinence_alter_roto_feared_events.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.4 on 2024-12-12 18:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("ebios_rm", "0003_remove_ebiosrmstudy_risk_assessments"), + ] + + operations = [ + migrations.RemoveField( + model_name="roto", + name="pertinence", + ), + migrations.AlterField( + model_name="roto", + name="feared_events", + field=models.ManyToManyField( + blank=True, + related_name="ro_to_couples", + to="ebios_rm.fearedevent", + verbose_name="Feared events", + ), + ), + ] diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py index 83ad19ea1..dbe56bfb8 100644 --- a/backend/ebios_rm/models.py +++ b/backend/ebios_rm/models.py @@ -194,7 +194,10 @@ class Pertinence(models.IntegerChoices): on_delete=models.CASCADE, ) feared_events = models.ManyToManyField( - FearedEvent, verbose_name=_("Feared events"), related_name="ro_to_couples" + FearedEvent, + verbose_name=_("Feared events"), + related_name="ro_to_couples", + blank=True, ) risk_origin = models.CharField( @@ -211,11 +214,6 @@ class Pertinence(models.IntegerChoices): choices=Resources.choices, default=Resources.UNDEFINED, ) - pertinence = models.PositiveSmallIntegerField( - verbose_name=_("Pertinence"), - choices=Pertinence.choices, - default=Pertinence.UNDEFINED, - ) activity = models.PositiveSmallIntegerField( verbose_name=_("Activity"), default=0, validators=[MaxValueValidator(4)] ) @@ -234,6 +232,20 @@ def save(self, *args, **kwargs): self.folder = self.ebios_rm_study.folder super().save(*args, **kwargs) + @property + def get_pertinence(self): + PERTINENCE_MATRIX = [ + [1, 1, 2, 2], + [1, 2, 3, 3], + [2, 3, 3, 4], + [2, 3, 4, 4], + ] + if self.motivation == 0 or self.resources == 0: + return self.Pertinence(self.Pertinence.UNDEFINED).label + return self.Pertinence( + PERTINENCE_MATRIX[self.motivation - 1][self.resources - 1] + ).label + class Stakeholder(AbstractBaseModel, FolderMixin): class Category(models.TextChoices): diff --git a/backend/ebios_rm/serializers.py b/backend/ebios_rm/serializers.py index f301dc1cc..9330fa7e5 100644 --- a/backend/ebios_rm/serializers.py +++ b/backend/ebios_rm/serializers.py @@ -94,9 +94,9 @@ class RoToReadSerializer(BaseModelSerializer): folder = FieldsRelatedField() feared_events = FieldsRelatedField(["folder", "id"], many=True) - pertinence = serializers.CharField(source="get_pertinence_display") motivation = serializers.CharField(source="get_motivation_display") resources = serializers.CharField(source="get_resources_display") + pertinence = serializers.CharField(source="get_pertinence") class Meta: model = RoTo diff --git a/backend/ebios_rm/views.py b/backend/ebios_rm/views.py index 71ddb5d7d..6d35d48a1 100644 --- a/backend/ebios_rm/views.py +++ b/backend/ebios_rm/views.py @@ -69,9 +69,7 @@ def likelihood(self, request, pk): class FearedEventViewSet(BaseModelViewSet): model = FearedEvent - filterset_fields = [ - "ebios_rm_study", - ] + filterset_fields = ["ebios_rm_study", "ro_to_couples"] @action(detail=True, name="Get risk matrix", url_path="risk-matrix") def risk_matrix(self, request, pk=None): @@ -112,10 +110,6 @@ def motivation(self, request): def resources(self, request): return Response(dict(RoTo.Resources.choices)) - @action(detail=False, name="Get pertinence choices") - def pertinence(self, request): - return Response(dict(RoTo.Pertinence.choices)) - class StakeholderViewSet(BaseModelViewSet): model = Stakeholder diff --git a/documentation/architecture/data-model.md b/documentation/architecture/data-model.md index 20766a703..59d68c962 100644 --- a/documentation/architecture/data-model.md +++ b/documentation/architecture/data-model.md @@ -1210,7 +1210,7 @@ The object risk_origin_target_objective (workshop 2) contains the following fiel - target objective (text) - motivation (--/1 very low/2 low/3 significant/4 strong) (--/très peu/peu/assez/fortement motivé) - resources (--/1 limited/2 significant/3 important/4 unlimited) (--/limitées/significatives/importantes/illimitées) -- pertinence (--/1 Irrelevant/2 partially relevant/3 fairly relevant/4 highly relevant) (--/peu pertinent/moyennement pertient/plutôt pertinent/très pertinent) +- pertinence (--/1 Irrelevant/2 partially relevant/3 fairly relevant/4 highly relevant) (--/peu pertinent/moyennement pertient/plutôt pertinent/très pertinent) -> calculated - activity (--/1/2/3/4) - selected - justification @@ -1267,7 +1267,7 @@ The frontend for risk study shall propose the following steps: ```mermaid erDiagram DOMAIN ||--o{ EBIOS_RM_STUDY : contains - DOMAIN ||--o{ STAKEHOLDER : contains + DOMAIN ||--o{ STAKEHOLDER : contains DOMAIN ||--o{ OPERATIONAL_SCENARIO : contains DOMAIN ||--o{ FEARED_EVENT : contains DOMAIN ||--o{ RO_TO : contains @@ -1289,7 +1289,7 @@ erDiagram EBIOS_RM_STUDY }o--o| ENTITY : studies EBIOS_RM_STUDY }o--o{ COMPLIANCE_ASSESSMENT: leverages EBIOS_RM_STUDY }o--|| RISK_MATRIX : leverages - EBIOS_RM_STUDY }o--o{ RISK_ASSESSMENT : generates + EBIOS_RM_STUDY |o--o{ RISK_ASSESSMENT : generates ATTACK_PATH }o--|| RO_TO : derives RO_TO }o--o{ FEARED_EVENT : corresponds_to OPERATIONAL_SCENARIO }o--|{ ATTACK_PATH : derives @@ -1326,7 +1326,6 @@ erDiagram string target_objective int motivation int resources - int pertinence int activity bool selected string justification diff --git a/frontend/messages/en.json b/frontend/messages/en.json index a7a851212..e8019b094 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -981,6 +981,10 @@ "attackPaths": "Attack paths", "currentCriticality": "Current criticality", "residualCriticality": "Residual criticality", + "notSelected": "Not selected", + "identifyRoTo": "Identify RO/TO", + "evaluateRoTo": "Evaluate RO/TO", + "selectRoTo": "Select RO/TO", "resetPasswordHere": "You can reset your password here.", "resetPassword": "Reset password", "ebiosRm": "Ebios RM", diff --git a/frontend/src/lib/components/Forms/ModelForm.svelte b/frontend/src/lib/components/Forms/ModelForm.svelte index e30978a25..8ca71675b 100644 --- a/frontend/src/lib/components/Forms/ModelForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm.svelte @@ -267,7 +267,7 @@ {:else if URLModel === 'feared-events'} {:else if URLModel === 'ro-to'} - + {:else if URLModel === 'stakeholders'} {:else if URLModel === 'attack-paths'} diff --git a/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte b/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte index eee24fba6..a78db0a56 100644 --- a/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte @@ -8,12 +8,27 @@ import { getOptions } from '$lib/utils/crud'; import TextArea from '../TextArea.svelte'; import NumberField from '../NumberField.svelte'; + import { page } from '$app/stores'; export let form: SuperValidated; export let model: ModelInfo; export let cacheLocks: Record = {}; export let formDataCache: Record = {}; export let initialData: Record = {}; + export let context: string; + + const activityBackground = context === 'edit' ? 'bg-white' : 'bg-surface-100-800-token'; + + let activeActivity: string | null = null; + $page.url.searchParams.forEach((value, key) => { + if (key === 'activity' && value === 'one') { + activeActivity = 'one'; + } else if (key === 'activity' && value === 'two') { + activeActivity = 'two'; + } else if (key === 'activity' && value === 'three') { + activeActivity = 'three'; + } + });