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

1from enum import Enum, unique 

2from typing import Any, Self 

3 

4from django.conf import settings 

5from django.db import models 

6 

7# from django.http import HttpRequest 

8from django.utils.translation import gettext_lazy as _ 

9 

10from .base_models import BaseChangeTrackingModel 

11 

12 

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" 

19 

20 

21LOG_TYPES = [lt.value for lt in LogType] 

22LOG_TYPE_CHOICES = [ 

23 (LogType.GENERATED.value, _("Auto generated")), 

24 (LogType.MANUAL.value, _("Manual entry")), 

25] 

26 

27 

28class ModelLog(BaseChangeTrackingModel): 

29 """ 

30 Base class to create a simple log table attached to an entity. 

31 

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 """ 

35 

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) 

59 

60 class Meta: 

61 abstract = True 

62 

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 

89 

90 new_message = cls(**kwargs) 

91 

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 

94 

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 

99 

100 new_message.save() 

101 

102 return new_message