Coverage for src/mesh/models/log_models.py: 87%
40 statements
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
1from enum import Enum, unique
2from typing import Any, Self
4from django.conf import settings
5from django.db import models
7# from django.http import HttpRequest
8from django.utils.translation import gettext_lazy as _
10from .base_models import BaseChangeTrackingModel
13@unique
14class LogType(Enum):
15 # Message automatically created following user actions.
16 GENERATED = "generated"
17 # Message explicitely entered by an user.
18 MANUAL = "manual"
21LOG_TYPES = [lt.value for lt in LogType]
22LOG_TYPE_CHOICES = [
23 (LogType.GENERATED.value, _("Auto generated")),
24 (LogType.MANUAL.value, _("Manual entry")),
25]
28class ModelLog(BaseChangeTrackingModel):
29 """
30 Base class to create a simple log table attached to an entity.
32 TODO: Always store the message text ? For auto generated messages (ex: new version),
33 store a unique message code which could enable message lookup somewhere else ?
34 """
36 attached_to: None | models.ForeignKey = None
37 # Log content (French)
38 content = models.TextField(blank=True, null=True)
39 # Translated content (English)
40 content_en = models.TextField(blank=True, null=True)
41 # Log message code (little helper that can fasten message browsing) - Optional
42 code = models.CharField(max_length=128, null=True, blank=True)
43 # Log type - Automatically generated or explicite user entry
44 type = models.CharField(
45 verbose_name=_("Message type"),
46 max_length=32,
47 choices=LOG_TYPE_CHOICES,
48 default=LogType.GENERATED.value,
49 )
50 impersonated_by = models.ForeignKey(
51 settings.AUTH_USER_MODEL,
52 on_delete=models.SET_NULL,
53 blank=True,
54 null=True,
55 editable=False,
56 related_name="+",
57 )
58 significant = models.BooleanField(verbose_name=_("Significant log"), default=False)
60 class Meta:
61 abstract = True
63 @classmethod
64 def add_message(
65 cls,
66 attached_to: models.Model,
67 content: str | None = None,
68 content_en: str | None = None,
69 user=None,
70 # impersonate_data=None,
71 # request: HttpRequest | None = None,
72 type: str = "",
73 code: str = "",
74 significant: bool = False,
75 ) -> Self:
76 """
77 Adds an entry to the model log.
78 Automatically checks if the request is performed with active impersonate mode.
79 """
80 kwargs: dict[str, Any] = {"attached_to": attached_to, "significant": significant}
81 if content: 81 ↛ 83line 81 didn't jump to line 83 because the condition on line 81 was always true
82 kwargs["content"] = content
83 if content_en: 83 ↛ 85line 83 didn't jump to line 85 because the condition on line 83 was always true
84 kwargs["content_en"] = content_en
85 if type and type in LOG_TYPES: 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true
86 kwargs["type"] = type
87 if code:
88 kwargs["code"] = code
90 new_message = cls(**kwargs)
92 if user.impersonate_data: 92 ↛ 93line 92 didn't jump to line 93 because the condition on line 92 was never true
93 new_message.impersonated_by_id = user.impersonate_data.source.pk # type: ignore
95 if user.is_authenticated: 95 ↛ 100line 95 didn't jump to line 100 because the condition on line 95 was always true
96 new_message._user = user # type: ignore
97 # if request.user.is_authenticated:
98 # new_message._user = request.user # type: ignore
100 new_message.save()
102 return new_message