Coverage for src / mesh / models / orm / log_models.py: 96%
49 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-22 07:20 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-22 07:20 +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)
61 html_class = models.CharField(max_length=128, null=True, blank=True)
63 class Meta:
64 abstract = True
65 ordering = ["-date_created"]
67 @classmethod
68 def add_message(
69 cls,
70 attached_to: models.Model,
71 content: str | None = None,
72 content_en: str | None = None,
73 user=None,
74 # request: HttpRequest | None = None,
75 type: str = "",
76 code: str = "",
77 significant: bool = False,
78 date: "timezone.datetime | None" = None,
79 html_class: str | None = None,
80 round_number: int | None = None,
81 ) -> Self:
82 """
83 Adds an entry to the model log.
84 Automatically checks if the request is performed with active impersonate mode.
85 """
86 kwargs: dict[str, Any] = {"attached_to": attached_to, "significant": significant}
87 if content:
88 kwargs["content"] = content
89 if content_en:
90 kwargs["content_en"] = content_en
91 if type and type in LOG_TYPES:
92 kwargs["type"] = type
93 if code:
94 kwargs["code"] = code
95 if html_class:
96 kwargs["html_class"] = html_class
97 if round_number:
98 kwargs["round_number"] = round_number
100 new_message = cls(**kwargs)
102 if user:
103 if user.impersonate_data:
104 new_message.impersonated_by_id = user.impersonate_data.source.pk # type: ignore
106 if user.is_authenticated:
107 new_message._user = user # type: ignore
108 # if request.user.is_authenticated:
109 # new_message._user = request.user # type: ignore
111 if date:
112 new_message.override_saved_date(
113 date_created=date,
114 date_last_modified=date,
115 last_modified_by_user=user,
116 created_by_user=user,
117 )
118 new_message.save()
120 return new_message