Create change log entry on status updates

This commit is contained in:
2020-09-29 22:19:43 +02:00
parent 96d6dc72fb
commit 5237d81551
6 changed files with 102 additions and 56 deletions

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-09-29 10:11
# Generated by Django 1.11.29 on 2020-09-29 20:15
from __future__ import unicode_literals
import dav_events.models.eventchange
@@ -23,7 +23,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
('operation', models.CharField(choices=[('update', 'update')], max_length=20)),
('operation', models.CharField(choices=[('update', 'Update'), ('set_flag', 'Raise Flag'), ('unset_flag', 'Lower Flag')], max_length=20)),
('content', models.TextField()),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='changes', to='dav_events.Event')),
('user', models.ForeignKey(default=dav_events.models.eventchange.get_system_user_id, on_delete=models.SET(dav_events.roles.get_ghost_user), related_name='+', to=settings.AUTH_USER_MODEL)),

View File

@@ -306,11 +306,13 @@ class Event(models.Model):
signals.event_created.send(sender=self.__class__, event=self)
self.workflow.update_status('draft', self.editor)
else:
change = EventChange(event=self, user=self.editor, operation='update', content=self.diff(original))
change = EventChange(event=self, user=self.editor, operation=EventChange.UPDATE,
content=self.diff(original))
change.save()
if not implicit_update:
logger.info('Event updated: %s', self)
signals.event_updated.send(sender=self.__class__, event=self, diff=self.diff(original, fmt='human_readable'), user=self.editor)
signals.event_updated.send(sender=self.__class__, event=self, user=self.editor,
diff=self.diff(original, fmt='human_readable'))
def diff(self, event, fmt='json'):
if fmt == 'human_readable':
@@ -326,16 +328,14 @@ class Event(models.Model):
for field in fields:
field_name = field.name
from_value = getattr(event, field_name)
if (isinstance(from_value, datetime.datetime) or
isinstance(from_value, datetime.date) or
isinstance(from_value, datetime.time) or
isinstance(from_value, Country)):
try:
json.dumps(from_value)
except TypeError:
from_value = str(from_value)
to_value = getattr(self, field_name)
if (isinstance(to_value, datetime.datetime) or
isinstance(to_value, datetime.date) or
isinstance(to_value, datetime.time) or
isinstance(to_value, Country)):
try:
json.dumps(to_value)
except TypeError:
to_value = str(to_value)
if from_value != to_value:
change = {

View File

@@ -7,10 +7,6 @@ from django.utils.encoding import python_2_unicode_compatible
from . import get_ghost_user, get_system_user
CHANGE_OPERATIONS = (
('update', 'update'),
)
def get_system_user_id():
return get_system_user().id
@@ -18,6 +14,15 @@ def get_system_user_id():
@python_2_unicode_compatible
class EventChange(models.Model):
UPDATE = 'update'
RAISE_FLAG = 'set_flag'
LOWER_FLAG = 'unset_flag'
OPERATION_CHOICES = (
(UPDATE, 'Update'),
(RAISE_FLAG, 'Raise Flag'),
(LOWER_FLAG, 'Lower Flag'),
)
event = models.ForeignKey('dav_events.Event', related_name='changes')
timestamp = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
@@ -25,7 +30,7 @@ class EventChange(models.Model):
on_delete=models.SET(get_ghost_user),
related_name='+')
operation = models.CharField(max_length=20, choices=CHANGE_OPERATIONS)
operation = models.CharField(max_length=20, choices=OPERATION_CHOICES)
content = models.TextField()
class Meta:

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import json
from django import template
from django.utils.html import format_html
@@ -5,6 +6,7 @@ from django.utils.safestring import mark_safe
from django.utils import timezone
from django.utils.translation import ugettext as _
from ..models.eventchange import EventChange
from ..models.eventstatus import EventStatus, get_or_create_event_status
register = template.Library()
@@ -38,24 +40,37 @@ def render_event_status(event, show_void=True):
@register.simple_tag
def render_event_changelog(event):
change_templ = u'<li class="list-group-item">\n' \
u'\t<p class="list-group-item-heading">{timestamp}' \
u'\t<p class="list-group-item-heading">' \
u'<span class="glyphicon glyphicon-{icon}"></span>' \
u' {timestamp}' \
u' - ' \
u' {user}</p>\n' \
u'\t{content}\n' \
u'</li>\n'
subchange_templ = u'<li class="list-group-item">\n' \
update_sub_templ = u'<li class="list-group-item">\n' \
u'\t{field}:{separator1}\n' \
u'\t<span style="background-color: #ffe0e0;">{refer}</span>\n' \
u'\t{separator2}\n' \
u'\t<span style="background-color: #e0ffe0;">{current}</span>\n' \
u'</li>\n'
raise_flag_templ = u'<span class="text-success glyphicon glyphicon-plus"></span>' \
u' <span class="label label-{bcontext}">{label}</span>' \
u' <span class="text-success glyphicon glyphicon-plus"></span>'
lower_flag_templ = u'<span class="text-danger glyphicon glyphicon-minus"></span>' \
u' <del><span class="label label-{bcontext}">{label}</span></del>' \
u' <span class="text-danger glyphicon glyphicon-minus"></span>'
if event.changes.exists():
html = u'<ul class="list-group">\n'
for change in event.changes.all():
username = change.user.get_full_name()
if not username:
username = change.user
if change.operation == EventChange.UPDATE:
icon = u'pencil'
content_html = u'<ul class="list-group">'
subchanges = json.loads(change.content)
for subchange in subchanges:
@@ -70,18 +85,36 @@ def render_event_changelog(event):
else:
separator1 = u' '
separator2 = u' -&gt; '
content_html += format_html(subchange_templ,
content_html += format_html(update_sub_templ,
field=field_label,
separator1=mark_safe(separator1),
refer=subchange['refer'],
separator2=mark_safe(separator2),
current=subchange['current'])
content_html += u'</ul>'
elif change.operation == EventChange.RAISE_FLAG:
icon = u'flag'
status = get_or_create_event_status(change.content)
content_html = format_html(raise_flag_templ,
bcontext=status.bootstrap_context,
label=status.label)
elif change.operation == EventChange.LOWER_FLAG:
icon = u'flag'
status = get_or_create_event_status(change.content)
content_html = format_html(lower_flag_templ,
bcontext=status.bootstrap_context,
label=status.label)
else:
icon = u'question-sign'
content_html = format_html(u'{content}', content=change.content)
html += format_html(change_templ,
icon=icon,
timestamp=timezone.localtime(change.timestamp).strftime('%Y-%m-%d %H:%M:%S %Z'),
user=username,
content=mark_safe(content_html))
html += u'</ul>\n'
else:
html = _(u'No entries')
html = _(u'Keine Einträge')
return mark_safe(html)

View File

@@ -4,6 +4,7 @@ import datetime
import json
from django.test import TestCase
from ..models.eventchange import EventChange
from .generic import EventMixin
TEST_EVENT_DATA = {
@@ -21,12 +22,6 @@ TEST_EVENT_DATA = {
class EventsTestCase(EventMixin, TestCase):
def test_empty_changelog(self):
data = TEST_EVENT_DATA
event = self.create_event_by_model(data)
event.sport = 'M'
self.assertFalse(event.changes.exists())
def test_changelog(self):
data = TEST_EVENT_DATA
event = self.create_event_by_model(data)
@@ -44,19 +39,29 @@ class EventsTestCase(EventMixin, TestCase):
event.save()
changes = event.changes
self.assertEqual(changes.count(), 3)
self.assertEqual(changes.count(), 4)
subchanges = json.loads(changes.get(pk=1).content)
change = changes.get(pk=1)
self.assertEqual(change.operation, EventChange.RAISE_FLAG)
self.assertEqual(change.content, 'draft')
change = changes.get(pk=2)
self.assertEqual(change.operation, EventChange.UPDATE)
subchanges = json.loads(change.content)
self.assertEqual(len(subchanges), 3)
self.assertIn({'field': 'alt_first_day', 'refer': None, 'current': '2019-03-02'}, subchanges)
self.assertIn({'field': 'sport', 'refer': 'W', 'current': 'M'}, subchanges)
self.assertIn({'field': 'ski_lift', 'refer': False, 'current': True}, subchanges)
subchanges = json.loads(changes.get(pk=2).content)
change = changes.get(pk=3)
self.assertEqual(change.operation, EventChange.UPDATE)
subchanges = json.loads(change.content)
self.assertEqual(len(subchanges), 1)
self.assertIn({'field': 'country', 'refer': 'DE', 'current': 'FR'}, subchanges)
subchanges = json.loads(changes.get(pk=3).content)
change = changes.get(pk=4)
self.assertEqual(change.operation, EventChange.UPDATE)
subchanges = json.loads(change.content)
self.assertEqual(len(subchanges), 2)
self.assertIn({'field': 'trainer_familyname', 'refer': 'Weißalles', 'current': 'Weißalles-Ömlaut'}, subchanges)
self.assertIn({'field': 'max_participants', 'refer': 0, 'current': 8}, subchanges)

View File

@@ -9,6 +9,7 @@ from django.utils.translation import ugettext_lazy as _
from . import emails
from . import signals
from .models.eventchange import EventChange
from .models.eventflag import EventFlag
from .models.eventstatus import get_or_create_event_status
from .roles import get_users_by_role, has_role
@@ -50,6 +51,8 @@ class BasicWorkflow(object):
kwargs['status'] = status
flag = EventFlag(**kwargs)
flag.save()
change = EventChange(event=event, user=flag.user, operation=EventChange.RAISE_FLAG, content=status.code)
change.save()
logger.info('Flagging status \'%s\' for %s', status.code, event)
return flag