Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/contrib/admin/models.py: 42%

77 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import json 

2 

3from django.conf import settings 

4from django.contrib.admin.utils import quote 

5from django.contrib.contenttypes.models import ContentType 

6from django.db import models 

7from django.urls import NoReverseMatch, reverse 

8from django.utils import timezone 

9from django.utils.text import get_text_list 

10from django.utils.translation import gettext 

11from django.utils.translation import gettext_lazy as _ 

12 

13ADDITION = 1 

14CHANGE = 2 

15DELETION = 3 

16 

17ACTION_FLAG_CHOICES = ( 

18 (ADDITION, _("Addition")), 

19 (CHANGE, _("Change")), 

20 (DELETION, _("Deletion")), 

21) 

22 

23 

24class LogEntryManager(models.Manager): 

25 use_in_migrations = True 

26 

27 def log_action( 

28 self, 

29 user_id, 

30 content_type_id, 

31 object_id, 

32 object_repr, 

33 action_flag, 

34 change_message="", 

35 ): 

36 if isinstance(change_message, list): 

37 change_message = json.dumps(change_message) 

38 return self.model.objects.create( 

39 user_id=user_id, 

40 content_type_id=content_type_id, 

41 object_id=str(object_id), 

42 object_repr=object_repr[:200], 

43 action_flag=action_flag, 

44 change_message=change_message, 

45 ) 

46 

47 

48class LogEntry(models.Model): 

49 action_time = models.DateTimeField( 

50 _("action time"), 

51 default=timezone.now, 

52 editable=False, 

53 ) 

54 user = models.ForeignKey( 

55 settings.AUTH_USER_MODEL, 

56 models.CASCADE, 

57 verbose_name=_("user"), 

58 ) 

59 content_type = models.ForeignKey( 

60 ContentType, 

61 models.SET_NULL, 

62 verbose_name=_("content type"), 

63 blank=True, 

64 null=True, 

65 ) 

66 object_id = models.TextField(_("object id"), blank=True, null=True) 

67 # Translators: 'repr' means representation 

68 # (https://docs.python.org/library/functions.html#repr) 

69 object_repr = models.CharField(_("object repr"), max_length=200) 

70 action_flag = models.PositiveSmallIntegerField( 

71 _("action flag"), choices=ACTION_FLAG_CHOICES 

72 ) 

73 # change_message is either a string or a JSON structure 

74 change_message = models.TextField(_("change message"), blank=True) 

75 

76 objects = LogEntryManager() 

77 

78 class Meta: 

79 verbose_name = _("log entry") 

80 verbose_name_plural = _("log entries") 

81 db_table = "django_admin_log" 

82 ordering = ["-action_time"] 

83 

84 def __repr__(self): 

85 return str(self.action_time) 

86 

87 def __str__(self): 

88 if self.is_addition(): 

89 return gettext("Added “%(object)s”.") % {"object": self.object_repr} 

90 elif self.is_change(): 

91 return gettext("Changed “%(object)s” — %(changes)s") % { 

92 "object": self.object_repr, 

93 "changes": self.get_change_message(), 

94 } 

95 elif self.is_deletion(): 

96 return gettext("Deleted “%(object)s.”") % {"object": self.object_repr} 

97 

98 return gettext("LogEntry Object") 

99 

100 def is_addition(self): 

101 return self.action_flag == ADDITION 

102 

103 def is_change(self): 

104 return self.action_flag == CHANGE 

105 

106 def is_deletion(self): 

107 return self.action_flag == DELETION 

108 

109 def get_change_message(self): 

110 """ 

111 If self.change_message is a JSON structure, interpret it as a change 

112 string, properly translated. 

113 """ 

114 if self.change_message and self.change_message[0] == "[": 

115 try: 

116 change_message = json.loads(self.change_message) 

117 except json.JSONDecodeError: 

118 return self.change_message 

119 messages = [] 

120 for sub_message in change_message: 

121 if "added" in sub_message: 

122 if sub_message["added"]: 

123 sub_message["added"]["name"] = gettext( 

124 sub_message["added"]["name"] 

125 ) 

126 messages.append( 

127 gettext("Added {name} “{object}”.").format( 

128 **sub_message["added"] 

129 ) 

130 ) 

131 else: 

132 messages.append(gettext("Added.")) 

133 

134 elif "changed" in sub_message: 

135 sub_message["changed"]["fields"] = get_text_list( 

136 [ 

137 gettext(field_name) 

138 for field_name in sub_message["changed"]["fields"] 

139 ], 

140 gettext("and"), 

141 ) 

142 if "name" in sub_message["changed"]: 

143 sub_message["changed"]["name"] = gettext( 

144 sub_message["changed"]["name"] 

145 ) 

146 messages.append( 

147 gettext("Changed {fields} for {name} “{object}”.").format( 

148 **sub_message["changed"] 

149 ) 

150 ) 

151 else: 

152 messages.append( 

153 gettext("Changed {fields}.").format( 

154 **sub_message["changed"] 

155 ) 

156 ) 

157 

158 elif "deleted" in sub_message: 

159 sub_message["deleted"]["name"] = gettext( 

160 sub_message["deleted"]["name"] 

161 ) 

162 messages.append( 

163 gettext("Deleted {name} “{object}”.").format( 

164 **sub_message["deleted"] 

165 ) 

166 ) 

167 

168 change_message = " ".join(msg[0].upper() + msg[1:] for msg in messages) 

169 return change_message or gettext("No fields changed.") 

170 else: 

171 return self.change_message 

172 

173 def get_edited_object(self): 

174 """Return the edited object represented by this log entry.""" 

175 return self.content_type.get_object_for_this_type(pk=self.object_id) 

176 

177 def get_admin_url(self): 

178 """ 

179 Return the admin URL to edit the object represented by this log entry. 

180 """ 

181 if self.content_type and self.object_id: 

182 url_name = "admin:%s_%s_change" % ( 

183 self.content_type.app_label, 

184 self.content_type.model, 

185 ) 

186 try: 

187 return reverse(url_name, args=(quote(self.object_id),)) 

188 except NoReverseMatch: 

189 pass 

190 return None