team-3/src/c3nav/editor/views/changes.py
2017-06-25 17:59:18 +02:00

254 lines
10 KiB
Python

import json
from operator import itemgetter
from django.conf import settings
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.formats import date_format
from django.utils.translation import ugettext_lazy as _
from c3nav.editor.models import ChangeSet
from c3nav.editor.utils import is_created_pk
from c3nav.editor.views.base import sidebar_view
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug
@sidebar_view
def changeset_detail(request, pk, show_history=False):
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=show_history)
if show_history:
return render(request, 'editor/changeset_history.html', ctx)
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)
def group_changes(changeset, can_edit=False, show_history=False):
changeset.parse_changes(get_history=show_history)
objects = changeset.get_objects()
if show_history:
grouped_changes = []
for obj in objects:
if is_created_pk(obj.pk):
obj.titles = {}
grouped_changes = [] if show_history else {}
changes = []
last_obj = None
for change in changeset.changes_qs:
pk = change.obj_pk
obj = objects[change.model_class][pk]
if change.model_class == LocationRedirect:
if change.action not in ('create', 'delete'):
continue
change.action = 'm2m_add' if change.action == 'create' else 'm2m_remove'
change.field_name = 'redirects'
change.field_value = obj.slug
pk = obj.target_id
obj = objects[LocationSlug][pk]
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):
if show_history:
obj_desc = _('%s (created)') % obj_desc
obj_still_exists = pk in changeset.created_objects[obj.__class__]
else:
obj_still_exists = pk not in changeset.deleted_existing.get(obj.__class__, ())
edit_url = None
if obj_still_exists and can_edit:
reverse_kwargs = {'pk': obj.pk}
if hasattr(obj, 'level_id'):
reverse_kwargs['level'] = obj.level_id
elif hasattr(obj, 'space_id'):
reverse_kwargs['space'] = obj.space_id
edit_url = reverse('editor.' + obj.__class__._meta.default_related_name + '.edit',
kwargs=reverse_kwargs)
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
form = changeset.wrap(type(obj)).EditorForm
change_data = {
'pk': change.pk,
'author': change.author,
'discarded': change.discarded_by_id is not None,
'apply_problem': change.check_apply_problem(),
'has_no_effect': change.check_has_no_effect(),
}
if show_history:
change_data.update({
'created': _('created at %(datetime)s') % {'datetime': date_format(change.created, 'DATETIME_FORMAT')},
})
if not show_history or change.action == 'delete' and can_edit:
change_data.update({
'can_restore': change.can_restore,
})
changes.append(change_data)
if change.action == 'create':
change_data.update({
'icon': 'plus',
'class': 'success',
'title': _('created'),
'order': (0, ),
})
elif change.action == 'delete':
change_data.update({
'icon': 'minus',
'class': 'danger',
'title': _('deleted'),
'order': (9, ),
})
elif change.action == 'update':
change_data.update({
'icon': 'option-vertical',
'class': 'muted',
})
if change.field_name == 'geometry':
change_data.update({
'icon': 'map-marker',
'class': 'info',
'title': _('edited geometry'),
'order': (8, ),
})
else:
if change.field_name.startswith('title_'):
lang = change.field_name[6:]
field_title = _('Title (%(lang)s)') % {'lang': dict(settings.LANGUAGES).get(lang, lang)}
field_value = str(json.loads(change.field_value))
if field_value:
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 = change.field
field_title = field.verbose_name
field_value = field.to_python(json.loads(change.field_value))
if field.related_model is not None:
field_value = objects[field.related_model][field_value].title
order = 5
if change.field_name == 'slug':
order = 1
if change.field_name not in form._meta.fields:
order = 0
change_data.update({
'order': (order, form._meta.fields.index(change.field_name) if order else 1),
})
if not field_value:
change_data.update({
'title': _('remove %(field_title)s') % {'field_title': field_title},
})
else:
change_data.update({
'title': field_title,
'value': field_value,
})
elif change.action == 'restore':
change_data.update({
'icon': 'share-alt',
'class': 'muted',
})
if change.field_name == 'geometry':
change_data.update({
'icon': 'map-marker',
'title': _('reverted geometry'),
})
else:
if change.field_name.startswith('title_'):
lang = change.field_name[6:]
field_title = _('Title (%(lang)s)') % {'lang': dict(settings.LANGUAGES).get(lang, lang)}
else:
field = change.field
field_title = field.verbose_name
model = getattr(field, 'related_model', None)
if model is not None:
change_data.update({
'value': objects[model][json.loads(change.field_value)].title
})
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',
'class': 'info',
})
if change.field_name == 'redirects':
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, form._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': changeset.pk,
'changeset': changeset,
'desc': desc,
'grouped_changes': grouped_changes,
}
return ctx