Coverage for src / mesh / models / orm / log_models.py: 95%
44 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
1from enum import Enum, unique
2from typing import TYPE_CHECKING, Any, Self
4from django.db import models
6# from django.http import HttpRequest
7from django.utils.translation import gettext_lazy as _
9from mesh.models.orm.base_models import BaseChangeTrackingModel
10from mesh.models.orm.user_models import User
12if TYPE_CHECKING:
13 from django.utils import timezone
16@unique
17class LogType(Enum):
18 # Message automatically created following user actions.
19 GENERATED = "generated"
20 # Message explicitely entered by an user.
21 MANUAL = "manual"
24LOG_TYPES = [lt.value for lt in LogType]
25LOG_TYPE_CHOICES = [
26 (LogType.GENERATED.value, _("Auto generated")),
27 (LogType.MANUAL.value, _("Manual entry")),
28]
31class ModelLog(BaseChangeTrackingModel):
32 """
33 Base class to create a simple log table attached to an entity.
35 TODO: Always store the message text ? For auto generated messages (ex: new version),
36 store a unique message code which could enable message lookup somewhere else ?
37 """
39 attached_to: None | models.ForeignKey = None
40 # Log content (French)
41 content = models.TextField(blank=True, null=True)
42 # Translated content (English)
43 content_en = models.TextField(blank=True, null=True)
44 # Log message code (little helper that can fasten message browsing) - Optional
45 code = models.CharField(max_length=128, null=True, blank=True)
46 # Log type - Automatically generated or explicite user entry
47 type = models.CharField(
48 verbose_name=_("Message type"),
49 max_length=32,
50 choices=LOG_TYPE_CHOICES,
51 default=LogType.GENERATED.value,
52 )
53 impersonated_by = models.ForeignKey(
54 User,
55 on_delete=models.SET_NULL,
56 null=True,
57 editable=False,
58 related_name="+",
59 )
60 significant = models.BooleanField(verbose_name=_("Significant log"), default=False)
62 class Meta:
63 abstract = True
64 ordering = ["-date_created"]
66 @classmethod
67 def add_message(
68 cls,
69 attached_to: models.Model,
70 content: str | None = None,
71 content_en: str | None = None,
72 user=None,
73 # impersonate_data=None,
74 # request: HttpRequest | None = None,
75 type: str = "",
76 code: str = "",
77 significant: bool = False,
78 date: "timezone.datetime | None" = None,
79 ) -> Self:
80 """
81 Adds an entry to the model log.
82 Automatically checks if the request is performed with active impersonate mode.
83 """
84 kwargs: dict[str, Any] = {"attached_to": attached_to, "significant": significant}
85 if content:
86 kwargs["content"] = content
87 if content_en:
88 kwargs["content_en"] = content_en
89 if type and type in LOG_TYPES:
90 kwargs["type"] = type
91 if code:
92 kwargs["code"] = code
94 new_message = cls(**kwargs)
96 if user:
97 if user.impersonate_data:
98 new_message.impersonated_by_id = user.impersonate_data.source.pk # type: ignore
100 if user.is_authenticated:
101 new_message._user = user # type: ignore
102 # if request.user.is_authenticated:
103 # new_message._user = request.user # type: ignore
105 if date:
106 new_message.override_saved_date(
107 date_created=date,
108 date_last_modified=date,
109 last_modified_by_user=user,
110 created_by_user=user,
111 )
112 new_message.save()
114 return new_message