Coverage for src / mesh / models / orm / log_models.py: 95%
44 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-11 09:47 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-11 09:47 +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 # request: HttpRequest | None = None,
74 type: str = "",
75 code: str = "",
76 significant: bool = False,
77 date: "timezone.datetime | None" = None,
78 ) -> Self:
79 """
80 Adds an entry to the model log.
81 Automatically checks if the request is performed with active impersonate mode.
82 """
83 kwargs: dict[str, Any] = {"attached_to": attached_to, "significant": significant}
84 if content:
85 kwargs["content"] = content
86 if content_en:
87 kwargs["content_en"] = content_en
88 if type and type in LOG_TYPES:
89 kwargs["type"] = type
90 if code:
91 kwargs["code"] = code
93 new_message = cls(**kwargs)
95 if user:
96 if user.impersonate_data:
97 new_message.impersonated_by_id = user.impersonate_data.source.pk # type: ignore
99 if user.is_authenticated:
100 new_message._user = user # type: ignore
101 # if request.user.is_authenticated:
102 # new_message._user = request.user # type: ignore
104 if date:
105 new_message.override_saved_date(
106 date_created=date,
107 date_last_modified=date,
108 last_modified_by_user=user,
109 created_by_user=user,
110 )
111 new_message.save()
113 return new_message