forked from GamesDoneQuick/donation-tracker
-
Notifications
You must be signed in to change notification settings - Fork 1
/
logutil.py
202 lines (170 loc) · 6.27 KB
/
logutil.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
from django.contrib.admin import models
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_text
from django.utils.text import get_text_list
from django.utils.translation import ugettext as _
def get_change_message(fields):
"""
Create a change message for *fields* (a sequence of field names).
"""
return _('Changed %s.') % get_text_list(fields, _('and'))
def addition(request, object):
"""
Log that an object has been successfully added.
"""
models.LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=ContentType.objects.get_for_model(object).pk,
object_id=object.pk,
object_repr=force_text(object),
action_flag=models.ADDITION
)
def change(request, object, message_or_fields):
"""
Log that an object has been successfully changed.
The argument *message_or_fields* must be a sequence of modified field names
or a custom change message.
"""
if isinstance(message_or_fields, str):
message = message_or_fields
else:
message = get_change_message(message_or_fields)
models.LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=ContentType.objects.get_for_model(object).pk,
object_id=object.pk,
object_repr=force_text(object),
action_flag=models.CHANGE,
change_message=message
)
def deletion(request, object, object_repr=None):
"""
Log that an object will be deleted.
"""
models.LogEntry.objects.log_action(
user_id=request.user.id,
content_type_id=ContentType.objects.get_for_model(object).pk,
object_id=object.pk,
object_repr=object_repr or force_text(object),
action_flag=models.DELETION
)
def in_bulk(request, added, changed, deleted):
"""
Log all *added*, *changed* and *deleted* instances.
Note that, while *added* and *deleted* are sequences of instances,
*changed* must be a sequence of tuples *(instance, message_or_fields)*,
where *message_or_fields* is a sequence of modified field names
or a custom change message.
"""
for instance in added:
addition(request, instance)
for instance, fields in changed:
if fields:
change(request, instance, fields)
for instance in deleted:
deletion(request, instance)
class AdminLogMixin(object):
"""
Class based views mixin that adds simple wrappers to
the three functions above.
"""
def log_addition(self, instance):
"""
Log that an object has been successfully added.
"""
addition(self.request, instance)
def log_change(self, instance, message_or_fields):
"""
Log that an object has been successfully changed.
"""
change(self.request, instance, message_or_fields)
def log_deletion(self, instance, instance_repr=None):
"""
Log that an object will be deleted.
"""
deletion(self.request, instance, instance_repr)
def logall(self, added, changed, deleted):
in_bulk(self.request, added, changed, deleted)
class AdminLogger(AdminLogMixin):
"""
A more generic Python object that can be used as a logger
taking the request in the constructor.
"""
def __init__(self, request):
self.request = request
class AdminLogCollector(object):
"""
A class to collect logs that will be reported later.
It can be useful, for example, when you need to add log entries
in forms (e.g. in a custom admin page) without the need to pass a
request argument::
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.collector = AdminLogCollector()
def save(self):
... add some instance
self.collector.added(instance)
If you have a formset of forms like the above, it is easy to
collect all logs::
class MyBaseFormSet(BaseFormSet):
def save(self):
collectors = []
for form in self.forms:
form.save()
collectors.append(form.collector)
# collect changes for all forms
self.collector = sum(collectors, AdminLogCollector())
# return the number of forms that did something
return len(filter(None, collectors))
In the view you can actually save all collected log entries::
formset.collector.logall(request)
"""
def __init__(self, added=None, changed=None, deleted=None, logger=None):
self._added = set() if added is None else set(added)
self._changed = set() if changed is None else set(changed)
self._deleted = set() if deleted is None else set(deleted)
self._logger = logger or in_bulk
self._done = False
def __add__(self, other):
added, changed, deleted = other.get_collected()
return self.__class__(
self._added.union(added),
self._changed.union(changed),
self._deleted.union(deleted),
logger=self._logger
)
def __repr__(self):
return repr(self.get_collected())
def __bool__(self):
return any(self.get_collected())
def added(self, instance):
"""
Collect an addition log.
"""
self._added.add(instance)
def changed(self, instance, message_or_fields):
"""
Collect a change log.
"""
if not isinstance(message_or_fields, str):
message_or_fields = tuple(message_or_fields)
self._changed.add((instance, message_or_fields))
def deleted(self, instance):
"""
Collect a deletion log.
"""
self._deleted.add(instance)
def get_collected(self):
"""
Return a tuple *(additions, changes, deletions)*
representing all the collected logs.
"""
return self._added, self._changed, self._deleted
def logall(self, request, redo=False):
"""
Actually save all log entries using the given *request*.
"""
if redo or not self._done:
self._logger(request, self._added, self._changed, self._deleted)
self._done = True