structure du projet + docker, back: mise en place BD et apps, front: début de dev pour le header et mise en place du thème et css global (override des variables bootstrap)

This commit is contained in:
2026-06-01 15:21:47 +02:00
parent b3c027794c
commit e8e6122a45
111 changed files with 6778 additions and 1 deletions
+9
View File
@@ -0,0 +1,9 @@
from .gdpr_event import GDPR_Event
from .global_variable import GlobalVariable
from .trackable_model import TrackableModel
__all__ = [
"GDPR_Event",
"GlobalVariable",
"TrackableModel",
]
+51
View File
@@ -0,0 +1,51 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class GDPR_Event(models.Model):
"""
Model to log GDPR-related events for auditing purposes.
"""
EVENT_TYPES = [
("DATA_EXPORT", _("Export de données")),
("DATA_ANONYMIZATION", _("Anonymisation de données")),
("CONSENT_WITHDRAWAL", _("Consentement retiré")),
]
EVENT_REASON = [
("USER_REQUEST", _("Demande utilisateur")),
("RETENTION_POLICY", _("Politique de conservation")),
]
event_type = models.CharField(
max_length=50, choices=EVENT_TYPES, verbose_name=_("type d'événement")
)
subject_reference = models.CharField(
max_length=255,
verbose_name=_("objet concerné"),
help_text=_("Objet concerné par l'événement : Class:id"),
)
fields_touched = models.JSONField(
blank=True,
verbose_name=_("champs affectés"),
help_text=_("Liste des champs affectés par l'événement."),
)
reason = models.CharField(
max_length=50, verbose_name=_("raison"), choices=EVENT_REASON
)
done_at = models.DateTimeField(auto_now_add=True, verbose_name=_("date et heure"))
done_by = models.ForeignKey(
"users.User",
null=True,
blank=True,
on_delete=models.SET_NULL,
verbose_name=_("Effectué par"),
)
class Meta:
verbose_name = _("Événement RGPD")
verbose_name_plural = _("Événements RGPD")
def __str__(self):
return f"{self.get_event_type_display()} - {self.done_at} - {self.subject_reference}"
@@ -0,0 +1,23 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from api.core.models.trackable_model import TrackableModel
class GlobalVariable(TrackableModel):
"""
Model to store global key-value pairs for application-wide settings.
"""
key = models.CharField(max_length=255, unique=True, verbose_name=_("clé"))
description = models.TextField(
blank=True, default="", verbose_name=_("description")
)
value = models.JSONField(verbose_name=_("valeur"), null=True, blank=True)
class Meta:
verbose_name = _("Paramètre global")
verbose_name_plural = _("Paramètres globaux")
def __str__(self):
return f"{self.key}: {self.value}"
@@ -0,0 +1,95 @@
from django.utils import timezone
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _
class TrackableModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("créé le"))
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
blank=True,
on_delete=models.PROTECT,
related_name="+",
verbose_name=_("créé par"),
)
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("modifié le"))
updated_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
blank=True,
on_delete=models.PROTECT,
related_name="+",
verbose_name=_("modifié par"),
)
obsolete = models.BooleanField(default=False, verbose_name=_("obsolète"))
obsolete_at = models.DateTimeField(
null=True, blank=True, verbose_name=_("obsolète le")
)
class Meta:
abstract = True
def soft_delete(self, cascade_m2m=True):
"""
Soft delete of trackable models. Objct becomes obsolete and obsolescence date is indicated.
"""
if cascade_m2m:
self._cascade_soft_delete_m2m()
self.obsolete = True
self.obsolete_at = timezone.now()
self.save(update_fields=["obsolete", "obsolete_at"])
def _cascade_soft_delete_m2m(self):
"""
Propagation of soft delete to M2M fields.
"""
for field in self._meta.get_fields():
if field.many_to_many and not field.auto_created:
through_model = field.remote_field.through
if issubclass(through_model, TrackableModel):
field_name = None
for through_field in through_model._meta.get_fields():
if through_field.related_model == self.__class__:
field_name = through_field.name
break
if field_name:
filter_kwargs = {field_name: self}
for relation in through_model.objects.filter(
**filter_kwargs, obsolete=False
):
relation.soft_delete(cascade_m2m=False)
def restore(self, cascade_m2m=True):
"""
Restore an object that was soft deleted.
"""
if cascade_m2m:
self._cascade_restore_m2m()
self.obsolete = False
self.obsolete_at = None
self.save(update_fields=["obsolete", "obsolete_at"])
def _cascade_restore_m2m(self):
for field in self._meta.get_fields():
if field.many_to_many and not field.auto_created:
through_model = field.remote_field.through
if issubclass(through_model, TrackableModel):
field_name = None
for through_field in through_model._meta.get_fields():
if through_field.related_model == self.__class__:
field_name = through_field.name
break
if field_name:
filter_kwargs = {field_name: self}
for relation in through_model.objects.filter(
**filter_kwargs, obsolete=True
):
relation.restore(cascade_m2m=False)