ISO-27001-Risk-Management/risks/utils.py
Kevin Heyer ab01841cf2 Add risk status and notification preferences
- Introduced a new `status` field to the `Risk` model with choices for "open", "in_progress", "closed", and "review_required".
- Created a `NotificationPreference` model to manage user notification settings for various events related to risks, controls, residual risks, reviews, users, and incidents.
- Updated the admin interface to include `NotificationPreference` inline with the `User` admin.
- Enhanced signal handlers to send notifications based on user preferences for created, updated, and deleted events for users, risks, controls, and incidents.
- Modified the `check_risk_followups` utility function to update risk status and create notifications for follow-ups.
- Updated serializers and views to accommodate the new `status` field and improved risk listing functionality.
- Added a new section in the risk detail template to display related incidents.
- Removed the unused statistics view from URLs.
2025-09-10 11:54:08 +02:00

58 lines
No EOL
2.2 KiB
Python

from django.contrib.auth import get_user_model
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from .models import AuditLog, Notification, Risk, ResidualRisk
def model_diff(old, new, fields=None):
"""
Compare two model instances and return a dict of changed fields.
- old: previous model instance (from DB)
- new: updated model instance (unsaved)
- fields: optional list of fields to check
"""
changes = {}
opts = new._meta
if fields is None:
fields = [f.name for f in opts.fields]
for field_name in fields:
old_value = getattr(old, field_name, None)
new_value = getattr(new, field_name, None)
if old_value != new_value:
changes[field_name] = {"old": old_value, "new": new_value}
return changes
def check_risk_followups():
"""
Check if follow ups need attention and create notifications.
Ensures no duplicate notifications per risk per day
"""
today = now().date()
risks = Risk.objects.filter(follow_up__lte=today).select_related("owner")
for risk in risks:
# Risk-Status auf review_required setzen (nicht überschreiben, wenn bereits closed)
if risk.status != "closed" and risk.status != "review_required":
Risk.objects.filter(pk=risk.pk).update(status="review_required")
# ResidualRisk-Objekt sicherstellen und Review-Flag setzen
resid, created = ResidualRisk.objects.get_or_create(risk=risk)
if not resid.review_required:
resid.review_required = True
resid.save()
# Notification an Stakeholder
message = _("Follow-up reached: review required for risk '{t}'").format(t=risk.title)
notification, created = Notification.objects.get_or_create(
user=risk.owner,
message=message,
defaults={"read": False, "sent": False},
)
if created:
AuditLog.objects.create(
user=None, action="create", model="Notification", object_id=notification.pk,
changes={"message": notification.message, "user": risk.owner.username if risk.owner else None},
)