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
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import json
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 _
13ADDITION = 1
14CHANGE = 2
15DELETION = 3
17ACTION_FLAG_CHOICES = (
18 (ADDITION, _("Addition")),
19 (CHANGE, _("Change")),
20 (DELETION, _("Deletion")),
21)
24class LogEntryManager(models.Manager):
25 use_in_migrations = True
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 )
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)
76 objects = LogEntryManager()
78 class Meta:
79 verbose_name = _("log entry")
80 verbose_name_plural = _("log entries")
81 db_table = "django_admin_log"
82 ordering = ["-action_time"]
84 def __repr__(self):
85 return str(self.action_time)
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}
98 return gettext("LogEntry Object")
100 def is_addition(self):
101 return self.action_flag == ADDITION
103 def is_change(self):
104 return self.action_flag == CHANGE
106 def is_deletion(self):
107 return self.action_flag == DELETION
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."))
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 )
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 )
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
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)
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