From b3c334d076cb2cf2aef513e440fe8e044d3b008e Mon Sep 17 00:00:00 2001 From: melinoix Date: Thu, 30 Jan 2025 18:40:19 +0100 Subject: [PATCH] feat: add a progress field on applied controls (#1443) * Add a progress field on applied controls * changed save model function & regionalize * inverse colors and add a validator on the progress field model --- .../0050_appliedcontrol_progress_field.py | 29 +++++++++++++++++++ backend/core/models.py | 13 ++++++++- backend/core/views.py | 2 ++ frontend/messages/en.json | 1 + frontend/messages/fr.json | 1 + .../ModelForm/AppliedControlPolicyForm.svelte | 9 ++++++ frontend/src/lib/utils/crud.ts | 3 +- frontend/src/lib/utils/schemas.ts | 3 +- 8 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 backend/core/migrations/0050_appliedcontrol_progress_field.py diff --git a/backend/core/migrations/0050_appliedcontrol_progress_field.py b/backend/core/migrations/0050_appliedcontrol_progress_field.py new file mode 100644 index 000000000..da4387c02 --- /dev/null +++ b/backend/core/migrations/0050_appliedcontrol_progress_field.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.5 on 2025-01-30 11:40 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0049_complianceassessment_show_documentation_score_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="appliedcontrol", + name="progress_field", + field=models.IntegerField( + default=0, + validators=[ + django.core.validators.MinValueValidator( + 0, message="Progress cannot be less than 0" + ), + django.core.validators.MaxValueValidator( + 100, message="Progress cannot be more than 100" + ), + ], + verbose_name="Progress Field", + ), + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index f2e88bfdc..ddf385973 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -12,7 +12,7 @@ from django.contrib.auth import get_user_model from django.core import serializers from django.core.exceptions import ValidationError -from django.core.validators import MaxValueValidator, RegexValidator +from django.core.validators import MaxValueValidator, RegexValidator, MinValueValidator from django.db import models, transaction from django.db.models import Q from django.forms.models import model_to_dict @@ -1854,6 +1854,15 @@ class Status(models.TextChoices): verbose_name=_("Cost"), ) + progress_field = models.IntegerField( + default=0, + verbose_name=_("Progress Field"), + validators=[ + MinValueValidator(0, message="Progress cannot be less than 0"), + MaxValueValidator(100, message="Progress cannot be more than 100"), + ], + ) + fields_to_check = ["name"] class Meta: @@ -1865,6 +1874,8 @@ def save(self, *args, **kwargs): self.category = self.reference_control.category if self.reference_control and self.csf_function is None: self.csf_function = self.reference_control.csf_function + if self.status == "active": + self.progress_field = 100 super(AppliedControl, self).save(*args, **kwargs) @property diff --git a/backend/core/views.py b/backend/core/views.py index bbcae6668..548ccb6b1 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -1055,6 +1055,7 @@ class AppliedControlViewSet(BaseModelViewSet): "risk_scenarios_e", "requirement_assessments", "evidences", + "progress_field", ] search_fields = ["name", "description", "risk_scenarios", "requirement_assessments"] @@ -1408,6 +1409,7 @@ def duplicate(self, request, pk): link=applied_control.link, effort=applied_control.effort, cost=applied_control.cost, + progress_field=applied_control.progress_field, ) duplicate_applied_control.owner.set(applied_control.owner.all()) if data["duplicate_evidences"]: diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 05df2f7c7..3fad2617f 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -1142,6 +1142,7 @@ "greenZoneRadius": "Green zone radius", "yellowZoneRadius": "Yellow zone radius", "redZoneRadius": "Red zone radius", + "progressField": "Progress", "recap": "Recap", "sectionMoved": "Section moved here" } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index fb55f3a60..f1479cad4 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -1141,6 +1141,7 @@ "greenZoneRadius": "Rayon de la zone verte", "yellowZoneRadius": "Rayon de la zone jaune", "redZoneRadius": "Rayon de la zone rouge", + "progressField": "Progrès", "recap": "Recap", "sectionMoved": "Section déplacée ici" } diff --git a/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte b/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte index ff1e2355a..3f24a8851 100644 --- a/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte @@ -4,6 +4,7 @@ import Checkbox from '$lib/components/Forms/Checkbox.svelte'; import TextField from '$lib/components/Forms/TextField.svelte'; import NumberField from '$lib/components/Forms/NumberField.svelte'; + import Score from '$lib/components/Forms/Score.svelte'; import { getOptions } from '$lib/utils/crud'; import type { SuperValidated } from 'sveltekit-superforms'; import type { ModelInfo, CacheLock } from '$lib/utils/types'; @@ -139,6 +140,14 @@ cacheLock={cacheLocks['cost']} bind:cachedValue={formDataCache['cost']} /> + {/if} {#if duplicate} diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index 1711c4b71..c5cedc9c3 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -263,7 +263,8 @@ export const URL_MODEL_MAP: ModelMap = { { field: 'eta', type: 'date' }, { field: 'owner' }, { field: 'expiry_date', type: 'date' }, - { field: 'link' } + { field: 'link' }, + { field: 'progress_field' } ], foreignKeyFields: [ { field: 'reference_control', urlModel: 'reference-controls' }, diff --git a/frontend/src/lib/utils/schemas.ts b/frontend/src/lib/utils/schemas.ts index d55fe9b2e..662b39c5f 100644 --- a/frontend/src/lib/utils/schemas.ts +++ b/frontend/src/lib/utils/schemas.ts @@ -153,7 +153,8 @@ export const AppliedControlSchema = z.object({ cost: z.number().multipleOf(0.000001).optional().nullable(), folder: z.string(), reference_control: z.string().optional().nullable(), - owner: z.string().uuid().optional().array().optional() + owner: z.string().uuid().optional().array().optional(), + progress_field: z.number().optional().default(0) }); export const AppliedControlDuplicateSchema = z.object({