add changeset_history

This commit is contained in:
Laura Klünder 2017-06-20 15:39:22 +02:00
parent 37ff5e54a9
commit cf538fa6a5
3 changed files with 120 additions and 35 deletions

View file

@ -33,6 +33,7 @@ class ChangeSet(models.Model):
super().__init__(*args, **kwargs)
self.default_author = None
self.parsed = False
self.changes_qs = None
self.ever_created_objects = {}
self.created_objects = {}
self.updated_existing = {}
@ -41,12 +42,21 @@ class ChangeSet(models.Model):
self.m2m_removed = {}
self._last_change_pk = 0
def parse_changes(self):
if self.pk is None or self.parsed:
@property
def relevant_changes(self):
return self.changes.filter(discarded_by__isnull=True).exclude(action='restore')
def parse_changes(self, get_history=False):
if self.pk is None or self.changes_qs is not None:
return
for change in self.changes.all():
if get_history:
self.changes_qs = self.changes.all()
else:
self.changes_qs = self.relevant_changes
for change in self.changes_qs:
self._parse_change(change)
self.parsed = True
def _parse_change(self, change):
self._last_change_pk = change.pk

View file

@ -1,7 +1,7 @@
from django.apps import apps
from django.conf.urls import url
from c3nav.editor.views.changes import changeset_detail
from c3nav.editor.views.changes import changeset_detail, changeset_history
from c3nav.editor.views.edit import edit, level_detail, list_objects, main_index, space_detail
from c3nav.editor.views.login import login_view, logout_view
@ -37,6 +37,7 @@ urlpatterns = [
url(r'^levels/(?P<on_top_of>c?[0-9]+)/levels_on_top/create$', edit, name='editor.levels_on_top.create',
kwargs={'model': 'Level'}),
url(r'^changesets/(?P<pk>[0-9]+)/$', changeset_detail, name='editor.changesets.detail'),
url(r'^changesets/(?P<pk>[0-9]+)/history$', changeset_history, name='editor.changesets.history'),
url(r'^login$', login_view, name='editor.login'),
url(r'^logout$', logout_view, name='editor.logout'),
]

View file

@ -1,4 +1,5 @@
import json
from operator import itemgetter
from django.conf import settings
from django.shortcuts import get_object_or_404, redirect, render
@ -14,14 +15,50 @@ from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug
@sidebar_view
def changeset_detail(request, pk):
can_edit = True
changeset = request.changeset
if str(pk) != str(request.changeset.pk):
can_edit = False
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
else:
changeset = request.changeset
ctx = group_changes(changeset, can_edit=can_edit, show_history=False)
if request.method == 'POST':
if request.POST.get('delete') == '1':
if request.POST.get('delete_confirm') == '1':
changeset.delete()
return redirect(reverse('editor.index'))
ctx.update({
'model_title': ChangeSet._meta.verbose_name,
'obj_title': changeset.title,
})
return render(request, 'editor/delete.html', ctx)
return render(request, 'editor/changeset.html', ctx)
@sidebar_view
def changeset_history(request, pk):
can_edit = True
changeset = request.changeset
if str(pk) != str(request.changeset.pk):
can_edit = False
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
ctx = group_changes(changeset, can_edit=can_edit, show_history=True)
return render(request, 'editor/changeset.html', ctx)
def group_changes(changeset, can_edit=False, show_history=False):
changeset.parse_changes(get_history=show_history)
changes_list = changeset.changes_qs
# collect pks of relevant objects
object_pks = {}
for change in changeset.changes.all():
for change in changes_list:
object_pks.setdefault(change.model_class, set()).add(change.obj_pk)
model = None
if change.action == 'update':
@ -41,7 +78,7 @@ def changeset_detail(request, pk):
objects = {}
for model, pks in object_pks.items():
created_pks = set(pk for pk in pks if is_created_pk(pk))
existing_pks = pks-created_pks
existing_pks = pks - created_pks
model_objects = {}
if existing_pks:
for obj in model.objects.filter(pk__in=existing_pks):
@ -51,13 +88,14 @@ def changeset_detail(request, pk):
if created_pks:
for pk in created_pks:
model_objects[pk] = changeset.get_created_object(model, pk, allow_deleted=True)._obj
model_objects[pk].titles = {}
if show_history:
model_objects[pk].titles = {}
objects[model] = model_objects
grouped_changes = []
grouped_changes = [] if show_history else {}
changes = []
last_obj = None
for change in changeset.changes.all():
for change in changes_list:
pk = change.obj_pk
obj = objects[change.model_class][pk]
if change.model_class == LocationRedirect:
@ -69,7 +107,10 @@ def changeset_detail(request, pk):
pk = obj.target_id
obj = objects[LocationSlug][pk]
if obj != last_obj:
if obj != last_obj and not show_history and pk in grouped_changes:
# noinspection PyTypeChecker
changes = grouped_changes[pk]['changes']
elif obj != last_obj:
changes = []
obj_desc = _('%(model)s #%(id)s') % {'model': obj.__class__._meta.verbose_name, 'id': pk}
if is_created_pk(pk):
@ -79,22 +120,29 @@ def changeset_detail(request, pk):
obj_still_exists = pk not in changeset.deleted_existing[obj.__class__]
edit_url = None
if obj_still_exists and changeset == request.changeset:
if obj_still_exists and can_edit:
reverse_kwargs = {'pk': obj.pk}
if hasattr(obj, 'level'):
reverse_kwargs['level'] = obj.level_id
elif hasattr(obj, 'space'):
reverse_kwargs['space'] = obj.space_id
edit_url = reverse('editor.'+obj.__class__._meta.default_related_name+'.edit', kwargs=reverse_kwargs)
edit_url = reverse('editor.' + obj.__class__._meta.default_related_name + '.edit',
kwargs=reverse_kwargs)
grouped_changes.append({
change_group = {
'model': obj.__class__,
'model_title': obj.__class__._meta.verbose_name,
'obj': obj_desc,
'obj_title': obj.title if obj.titles else None,
'changes': changes,
'edit_url': edit_url,
})
}
if show_history:
grouped_changes.append(change_group)
else:
change_group['order'] = (0, int(pk[1:])) if is_created_pk(pk) else (1, int(pk))
grouped_changes[pk] = change_group
last_obj = obj
change_data = {
@ -108,12 +156,14 @@ def changeset_detail(request, pk):
'icon': 'plus',
'class': 'success',
'title': _('created'),
'order': (0, ),
})
elif change.action == 'delete':
change_data.update({
'icon': 'minus',
'class': 'danger',
'title': _('deleted')
'title': _('deleted'),
'order': (9, ),
})
elif change.action == 'update':
change_data.update({
@ -125,6 +175,7 @@ def changeset_detail(request, pk):
'icon': 'map-marker',
'class': 'info',
'title': _('edited geometry'),
'order': (8, ),
})
else:
if change.field_name.startswith('title_'):
@ -135,6 +186,9 @@ def changeset_detail(request, pk):
obj.titles[lang] = field_value
else:
obj.titles.pop(lang, None)
change_data.update({
'order': (4, tuple(code for code, title in settings.LANGUAGES).index(lang)),
})
else:
field = obj.__class__._meta.get_field(change.field_name)
field_title = field.verbose_name
@ -142,15 +196,40 @@ def changeset_detail(request, pk):
model = getattr(field, 'related_model', None)
if model is not None:
field_value = objects[model][field_value].title
order = 5
if change.field_name == 'slug':
order = 1
if change.field_name not in obj.__class__.EditorForm._meta.fields:
order = 0
change_data.update({
'order': (order,
obj.__class__.EditorForm._meta.fields.index(change.field_name) if order else 1),
})
if not field_value:
change_data.update({
'title': _('unset %(field_title)s') % {'field_title': field_title},
'title': _('remove %(field_title)s') % {'field_title': field_title},
})
else:
change_data.update({
'title': field_title,
'value': field_value,
})
elif change.action == 'revert':
change_data.update({
'icon': 'share-alt',
'class': 'muted',
})
if change.field_name == 'geometry':
change_data.update({
'icon': 'map-marker',
'title': _('reverted geometry'),
})
else:
field = obj.__class__._meta.get_field(change.field_name)
field_title = field.verbose_name
change_data.update({
'title': _('reverted %(field_title)s') % {'field_title': field_title},
})
elif change.action in ('m2m_add', 'm2m_remove'):
change_data.update({
'icon': 'chevron-right' if change.action == 'm2m_add' else 'chevron-left',
@ -160,40 +239,35 @@ def changeset_detail(request, pk):
change_data.update({
'title': _('Redirecting slugs'),
'value': change.field_value,
'order': (6, -1, (change.action == 'm2m_remove')),
})
else:
field = obj.__class__._meta.get_field(change.field_name)
change_data.update({
'title': field.verbose_name,
'value': objects[field.related_model][json.loads(change.field_value)].title,
'order': (6, obj.__class__.EditorForm._meta.fields.index(change.field_name),
(change.action == 'm2m_remove')),
})
else:
change_data.update({
'title': '???',
'order': (10, )
})
if changeset.author:
desc = _('created at %(datetime)s by') % {'datetime': date_format(changeset.created, 'DATETIME_FORMAT')}
else:
desc = _('created at %(datetime)s') % {'datetime': date_format(changeset.created, 'DATETIME_FORMAT')}
if not show_history:
grouped_changes = sorted(grouped_changes.values(), key=itemgetter('order'))
for group in grouped_changes:
group['changes'] = sorted(group['changes'], key=itemgetter('order'))
ctx = {
'pk': pk,
'pk': changeset.pk,
'changeset': changeset,
'desc': desc,
'grouped_changes': grouped_changes,
}
if request.method == 'POST':
if request.POST.get('delete') == '1':
if request.POST.get('delete_confirm') == '1':
changeset.delete()
return redirect(reverse('editor.index'))
ctx.update({
'model_title': ChangeSet._meta.verbose_name,
'obj_title': changeset.title,
})
return render(request, 'editor/delete.html', ctx)
return render(request, 'editor/changeset.html', ctx)
return ctx