Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a progress field on applied controls #1443

Merged
merged 5 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions backend/core/migrations/0050_appliedcontrol_progress_field.py
Original file line number Diff line number Diff line change
@@ -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",
),
),
]
13 changes: 12 additions & 1 deletion backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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
Comment on lines +1877 to +1878
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve status handling in the save method.

Consider these improvements:

  1. Use the Status enum instead of string literal for better type safety
  2. Handle the case when status changes from "active" to another status
-        if self.status == "active":
+        if self.status == self.Status.ACTIVE:
             self.progress_field = 100
+        elif self.status != self.Status.ACTIVE and self._state.adding is False:
+            # Only update progress if this is not a new instance
+            original = type(self).objects.get(pk=self.pk)
+            if original.status == self.Status.ACTIVE:
+                # Reset progress when moving from active to another status
+                self.progress_field = 0

Committable suggestion skipped: line range outside the PR's diff.

super(AppliedControl, self).save(*args, **kwargs)

@property
Expand Down
2 changes: 2 additions & 0 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,7 @@ class AppliedControlViewSet(BaseModelViewSet):
"risk_scenarios_e",
"requirement_assessments",
"evidences",
"progress_field",
]
search_fields = ["name", "description", "risk_scenarios", "requirement_assessments"]

Expand Down Expand Up @@ -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"]:
Expand Down
1 change: 1 addition & 0 deletions frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,7 @@
"greenZoneRadius": "Green zone radius",
"yellowZoneRadius": "Yellow zone radius",
"redZoneRadius": "Red zone radius",
"progressField": "Progress",
"recap": "Recap",
"sectionMoved": "Section moved here"
}
1 change: 1 addition & 0 deletions frontend/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -139,6 +140,14 @@
cacheLock={cacheLocks['cost']}
bind:cachedValue={formDataCache['cost']}
/>
<Score
{form}
label={m.progress()}
field="progress_field"
fullDonut
min_score={0}
max_score={100}
/>
{/if}

{#if duplicate}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Loading