add changeset_history
This commit is contained in:
parent
37ff5e54a9
commit
cf538fa6a5
3 changed files with 120 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
if str(pk) != str(request.changeset.pk):
|
||||
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
|
||||
else:
|
||||
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=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
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue