from django.db import models from django.conf import settings from django.utils.translation import gettext_lazy as _ from multiselectfield import MultiSelectField from .likelihood_choice import LikelihoodChoice # --------------------------------------------------------------------------- # Risk # --------------------------------------------------------------------------- class Risk(models.Model): class Meta: verbose_name = _("Risk") verbose_name_plural = _("Risks") STATUS_CHOICES = [ ("open", _("Open")), ("in_progress", _("In Progress")), ("closed", _("Closed")), ("review_required", _("Review required")), ] IMPACT_CHOICES = [ (1, _("Very Low (< 1,000 € – minor operational impact)")), (2, _("Low (1,000–5,000 € – local impact)")), (3, _("High (5,000–15,000 € – team-level impact)")), (4, _("Severe (50,000–100,000 € – regional impact)")), (5, _("Critical (> 100,000 € – existential threat)")), ] CIA_CHOICES = [ ("1", _("Confidentiality")), ("2", _("Integrity")), ("3", _("Availability")), ] # Basic information title = models.CharField(_("Title"), max_length=255) description = models.TextField(_("Description"), max_length=225, blank=True, null=True) asset = models.CharField(_("Asset"), max_length=255, blank=True, null=True) process = models.CharField(_("Process"), max_length=255, blank=True, null=True) category = models.CharField(_("Category"), max_length=255, blank=True, null=True) created_at = models.DateTimeField(_("Created at"), auto_now_add=True) updated_at = models.DateTimeField(_("Updated at"), auto_now=True) effects = models.TextField(_("Effects"), blank=True, null=True) status = models.CharField( _("Status"), max_length=20, choices=STATUS_CHOICES, default="open", db_index=True, ) # CIA Protection Goals cia = MultiSelectField(choices=CIA_CHOICES, max_length=100, blank=True, null=True) # Risk evaluation before controls likelihood = models.ForeignKey( LikelihoodChoice, on_delete=models.PROTECT, verbose_name=_("Likelihood"), related_name="risks", ) impact = models.IntegerField(choices=IMPACT_CHOICES, default=1) # Calculated fields score = models.IntegerField(editable=False) level = models.CharField(max_length=50, editable=False) # Ownership owner = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="owned_risks" ) # Reminder / follow-up date follow_up = models.DateField(blank=True, null=True) def save(self, *args, **kwargs): # Mark for review if likelihood/impact changed if self.pk: old = Risk.objects.get(pk=self.pk) if old.likelihood != self.likelihood or old.impact != self.impact: self.review_required = True self.status = "review_required" # Calculate risk score and level self.score = self.likelihood.value * self.impact if self.score <= 4: self.level = "Low" elif self.score <= 8: self.level = "Medium" elif self.score <= 12: self.level = "High" else: self.level = "Critical" super().save(*args, **kwargs) def __str__(self): return f"{self.title} (Score: {self.score}, Level: {self.level})"