lets try to display the changeset contents nicely
This commit is contained in:
parent
a7a5ec93e6
commit
c9d73e9daf
2 changed files with 106 additions and 212 deletions
|
@ -124,44 +124,7 @@ class ChangeSet(models.Model):
|
||||||
Wrap Objects
|
Wrap Objects
|
||||||
"""
|
"""
|
||||||
def fill_changes_cache(self):
|
def fill_changes_cache(self):
|
||||||
return
|
return # todo: remove
|
||||||
"""
|
|
||||||
Get all changed objects and fill this ChangeSet's changes cache.
|
|
||||||
Only executable once, if something is changed later the cache will be automatically updated.
|
|
||||||
This method gets called automatically when the cache is needed.
|
|
||||||
Only call it if you need to set include_deleted_created to True.
|
|
||||||
:rtype: True if the method was executed, else False
|
|
||||||
"""
|
|
||||||
if self.changed_objects is not None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self.pk is None:
|
|
||||||
self.changed_objects = {}
|
|
||||||
return False
|
|
||||||
|
|
||||||
cache_key = self.cache_key_by_changes + ':cache'
|
|
||||||
|
|
||||||
cached_cache = cache.get(cache_key)
|
|
||||||
if cached_cache is not None:
|
|
||||||
(self.changed_objects, self.created_objects, self.updated_existing,
|
|
||||||
self.deleted_existing, self.m2m_added, self.m2m_removed) = cached_cache
|
|
||||||
return True
|
|
||||||
|
|
||||||
self.changed_objects = {}
|
|
||||||
for change in self.changed_objects_set.all():
|
|
||||||
change.update_changeset_cache()
|
|
||||||
|
|
||||||
if self.state != 'applied' and not self._cleaning_changes:
|
|
||||||
self._cleaning_changes = True
|
|
||||||
try:
|
|
||||||
self._clean_changes()
|
|
||||||
finally:
|
|
||||||
self._cleaning_changes = False
|
|
||||||
|
|
||||||
cache.set(cache_key, (self.changed_objects, self.created_objects, self.updated_existing,
|
|
||||||
self.deleted_existing, self.m2m_added, self.m2m_removed), 300)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Analyse Changes
|
Analyse Changes
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
@ -16,6 +17,7 @@ from c3nav.editor.forms import ChangeSetForm, RejectForm, get_editor_form
|
||||||
from c3nav.editor.models import ChangeSet
|
from c3nav.editor.models import ChangeSet
|
||||||
from c3nav.editor.views.base import sidebar_view
|
from c3nav.editor.views.base import sidebar_view
|
||||||
from c3nav.editor.wrappers import is_created_pk
|
from c3nav.editor.wrappers import is_created_pk
|
||||||
|
from c3nav.mapdata.fields import I18nField
|
||||||
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug
|
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug
|
||||||
|
|
||||||
|
|
||||||
|
@ -193,156 +195,90 @@ def changeset_detail(request, pk):
|
||||||
|
|
||||||
changed_objects_data = []
|
changed_objects_data = []
|
||||||
|
|
||||||
added_redirects = {}
|
# added_redirects = {}
|
||||||
removed_redirects = {}
|
# removed_redirects = {}
|
||||||
for changed_object in changeset.changed_objects.get(LocationRedirect, {}).values():
|
# for changed_object in changeset.changed_objects.get(LocationRedirect, {}).values():
|
||||||
if changed_object.is_created == changed_object.deleted:
|
# if changed_object.is_created == changed_object.deleted:
|
||||||
continue
|
# continue
|
||||||
obj = objects[LocationRedirect][changed_object.obj_pk]
|
# obj = objects[LocationRedirect][changed_object.obj_pk]
|
||||||
redirect_list = (removed_redirects if changed_object.deleted else added_redirects)
|
# redirect_list = (removed_redirects if changed_object.deleted else added_redirects)
|
||||||
redirect_list.setdefault(obj.target_id, []).append(obj.slug)
|
# redirect_list.setdefault(obj.target_id, []).append(obj.slug)
|
||||||
|
#
|
||||||
|
# redirect_changed_objects = []
|
||||||
|
# todo: display redirects nicely
|
||||||
|
|
||||||
redirect_changed_objects = []
|
for changed_object in changeset.changes.changed_objects:
|
||||||
|
model = apps.get_model("mapdata", changed_object.obj.model)
|
||||||
|
changes = []
|
||||||
|
changed_object_data = {
|
||||||
|
'model': model,
|
||||||
|
'model_title': model._meta.verbose_name,
|
||||||
|
'pk': changed_object.obj.id,
|
||||||
|
'desc': changed_object.repr,
|
||||||
|
'title': 'TITLE',
|
||||||
|
'changes': changes,
|
||||||
|
'edit_url': None,
|
||||||
|
'deleted': changed_object.deleted,
|
||||||
|
}
|
||||||
|
|
||||||
for pk in set(added_redirects.keys()) | set(removed_redirects.keys()):
|
form_fields = get_editor_form(model)._meta.fields
|
||||||
obj = objects[LocationSlug][pk]
|
|
||||||
model = obj.__class__
|
|
||||||
try:
|
|
||||||
changeset.changed_objects[model][pk]
|
|
||||||
except KeyError:
|
|
||||||
redirect_changed_objects.append((model, {pk: changeset.get_changed_object(obj)}))
|
|
||||||
|
|
||||||
for model, changed_objects in chain(changeset.changed_objects.items(), redirect_changed_objects):
|
if changed_object.created:
|
||||||
if model == LocationRedirect:
|
changes.append({
|
||||||
continue
|
'icon': 'plus',
|
||||||
|
'class': 'success',
|
||||||
|
'empty': True,
|
||||||
|
'title': _('created'),
|
||||||
|
})
|
||||||
|
|
||||||
for pk, changed_object in changed_objects.items():
|
update_changes = []
|
||||||
if pk in objects[model]:
|
for name, value in changed_object.fields.items():
|
||||||
obj = objects[model][pk]
|
change_data = {
|
||||||
obj_desc = format_lazy(_('{model} #{id}'), model=obj.__class__._meta.verbose_name, id=pk)
|
'icon': 'option-vertical',
|
||||||
if is_created_pk(pk):
|
'class': 'muted',
|
||||||
obj_still_exists = pk in changeset.created_objects.get(obj.__class__, ())
|
|
||||||
else:
|
|
||||||
obj_still_exists = pk not in changeset.deleted_existing.get(obj.__class__, ())
|
|
||||||
else:
|
|
||||||
obj = changed_object.obj._obj
|
|
||||||
obj_still_exists = False
|
|
||||||
if changed_object.deleted:
|
|
||||||
obj_desc = format_lazy(_('{model} #{id}'), model=obj.__class__._meta.verbose_name, id=pk)
|
|
||||||
|
|
||||||
else:
|
|
||||||
obj_desc = format_lazy(_('{model} #{id} (deleted outside this changeset)'),
|
|
||||||
model=obj.__class__._meta.verbose_name, id=pk)
|
|
||||||
|
|
||||||
edit_url = None
|
|
||||||
if obj_still_exists and can_edit and not isinstance(obj, LocationRedirect):
|
|
||||||
reverse_kwargs = {'pk': obj.pk}
|
|
||||||
if hasattr(obj, 'space_id'):
|
|
||||||
reverse_kwargs['space'] = obj.space_id
|
|
||||||
elif hasattr(obj, 'level_id'):
|
|
||||||
reverse_kwargs['level'] = obj.level_id
|
|
||||||
try:
|
|
||||||
edit_url = reverse('editor.' + obj.__class__._meta.default_related_name + '.edit',
|
|
||||||
kwargs=reverse_kwargs)
|
|
||||||
except NoReverseMatch:
|
|
||||||
pass
|
|
||||||
|
|
||||||
changes = []
|
|
||||||
missing_dependencies = changed_object.get_missing_dependencies()
|
|
||||||
unique_collisions = changed_object.get_unique_collisions()
|
|
||||||
changed_object_data = {
|
|
||||||
'model': obj.__class__,
|
|
||||||
'model_title': obj.__class__._meta.verbose_name,
|
|
||||||
'pk': changed_object.pk,
|
|
||||||
'desc': obj_desc,
|
|
||||||
'title': obj.title if getattr(obj, 'titles', None) else None,
|
|
||||||
'changes': changes,
|
|
||||||
'edit_url': edit_url,
|
|
||||||
'deleted': changed_object.deleted,
|
|
||||||
'missing_dependencies': missing_dependencies,
|
|
||||||
'unique_collisions': unique_collisions,
|
|
||||||
'order': (changed_object.deleted and changed_object.is_created, not changed_object.is_created),
|
|
||||||
}
|
}
|
||||||
changed_objects_data.append(changed_object_data)
|
if name == 'geometry':
|
||||||
|
change_data.update({
|
||||||
form_fields = get_editor_form(model)._meta.fields
|
'icon': 'map-marker',
|
||||||
|
'class': 'info',
|
||||||
if changed_object.is_created:
|
|
||||||
changes.append({
|
|
||||||
'icon': 'plus',
|
|
||||||
'class': 'success',
|
|
||||||
'empty': True,
|
'empty': True,
|
||||||
'title': _('created'),
|
'title': _('created geometry') if changed_object.created else _('edited geometry'),
|
||||||
|
'order': (8,),
|
||||||
})
|
})
|
||||||
|
elif name == 'data':
|
||||||
update_changes = []
|
change_data.update({
|
||||||
|
'icon': 'signal',
|
||||||
for name, value in changed_object.updated_fields.items():
|
'class': 'info',
|
||||||
change_data = {
|
'empty': True,
|
||||||
'icon': 'option-vertical',
|
'title': _('created scan data') if changed_object.created else _('edited scan data'),
|
||||||
'class': 'muted',
|
'order': (9,),
|
||||||
}
|
})
|
||||||
if name == 'geometry':
|
else:
|
||||||
change_data.update({
|
field = model._meta.get_field(name)
|
||||||
'icon': 'map-marker',
|
field_title = field.verbose_name
|
||||||
'class': 'info',
|
if isinstance(field, I18nField):
|
||||||
'empty': True,
|
for lang, subvalue in value.items():
|
||||||
'title': _('created geometry') if changed_object.is_created else _('edited geometry'),
|
sub_change_data = change_data.copy()
|
||||||
'order': (8,),
|
|
||||||
})
|
|
||||||
elif name == 'data':
|
|
||||||
change_data.update({
|
|
||||||
'icon': 'signal',
|
|
||||||
'class': 'info',
|
|
||||||
'empty': True,
|
|
||||||
'title': _('created scan data') if changed_object.is_created else _('edited scan data'),
|
|
||||||
'order': (9,),
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
if '__i18n__' in name:
|
|
||||||
orig_name, i18n, lang = name.split('__')
|
|
||||||
lang_info = get_language_info(lang)
|
lang_info = get_language_info(lang)
|
||||||
field = model._meta.get_field(orig_name)
|
|
||||||
field_title = format_lazy(_('{field_name} ({lang})'),
|
field_title = format_lazy(_('{field_name} ({lang})'),
|
||||||
field_name=field.verbose_name,
|
field_name=field.verbose_name,
|
||||||
lang=lang_info['name_translated'])
|
lang=lang_info['name_translated'])
|
||||||
field_value = str(value)
|
if subvalue == '' or subvalue is None:
|
||||||
if field_value:
|
sub_change_data.update({
|
||||||
getattr(obj, field.attname)[lang] = field_value
|
'empty': True,
|
||||||
|
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
|
||||||
|
})
|
||||||
else:
|
else:
|
||||||
getattr(obj, field.attname).pop(lang, None)
|
sub_change_data.update({
|
||||||
change_data.update({
|
'title': field_title,
|
||||||
|
'value': subvalue,
|
||||||
|
})
|
||||||
|
sub_change_data.update({
|
||||||
'order': (4, tuple(code for code, title in settings.LANGUAGES).index(lang)),
|
'order': (4, tuple(code for code, title in settings.LANGUAGES).index(lang)),
|
||||||
})
|
})
|
||||||
else:
|
update_changes.append(sub_change_data)
|
||||||
field = model._meta.get_field(name)
|
else:
|
||||||
field_title = field.verbose_name
|
if value == '' or value is None:
|
||||||
if field.related_model is not None:
|
|
||||||
if value is None:
|
|
||||||
field_value = None
|
|
||||||
else:
|
|
||||||
if issubclass(field.related_model, User):
|
|
||||||
field_value = objects[field.related_model][value].username
|
|
||||||
else:
|
|
||||||
field_value = objects[field.related_model][value].title
|
|
||||||
change_data.update({
|
|
||||||
'missing_dependency': field.name in missing_dependencies,
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
field_value = field.to_python(value)
|
|
||||||
if name in unique_collisions:
|
|
||||||
change_data.update({
|
|
||||||
'unique_collision': field.name in unique_collisions,
|
|
||||||
})
|
|
||||||
order = 5
|
|
||||||
if name == 'slug':
|
|
||||||
order = 1
|
|
||||||
if name not in form_fields:
|
|
||||||
order = 0
|
|
||||||
change_data.update({
|
|
||||||
'order': (order, form_fields.index(name) if order else 1),
|
|
||||||
})
|
|
||||||
if field_value == '' or field_value is None:
|
|
||||||
change_data.update({
|
change_data.update({
|
||||||
'empty': True,
|
'empty': True,
|
||||||
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
|
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
|
||||||
|
@ -350,50 +286,45 @@ def changeset_detail(request, pk):
|
||||||
else:
|
else:
|
||||||
change_data.update({
|
change_data.update({
|
||||||
'title': field_title,
|
'title': field_title,
|
||||||
'value': field_value,
|
'value': value,
|
||||||
})
|
})
|
||||||
update_changes.append(change_data)
|
order = 5
|
||||||
|
if name == 'slug':
|
||||||
changes.extend(sorted(update_changes, key=itemgetter('order')))
|
order = 1
|
||||||
|
if name not in form_fields:
|
||||||
for m2m_mode in ('m2m_added', 'm2m_removed'):
|
order = 0
|
||||||
m2m_list = getattr(changed_object, m2m_mode).items()
|
change_data.update({
|
||||||
for name, values in sorted(m2m_list, key=lambda nv: form_fields.index(nv[0])):
|
'order': (order, form_fields.index(name) if order else 1),
|
||||||
field = model._meta.get_field(name)
|
|
||||||
for value in values:
|
|
||||||
changes.append({
|
|
||||||
'icon': 'chevron-right' if m2m_mode == 'm2m_added' else 'chevron-left',
|
|
||||||
'class': 'info',
|
|
||||||
'title': field.verbose_name,
|
|
||||||
'value': objects[field.related_model][value].title,
|
|
||||||
})
|
|
||||||
|
|
||||||
if isinstance(obj, LocationSlug):
|
|
||||||
for slug in added_redirects.get(obj.pk, ()):
|
|
||||||
changes.append({
|
|
||||||
'icon': 'chevron-right',
|
|
||||||
'class': 'info',
|
|
||||||
'title': _('Redirect slugs'),
|
|
||||||
'value': slug,
|
|
||||||
})
|
|
||||||
for slug in removed_redirects.get(obj.pk, ()):
|
|
||||||
changes.append({
|
|
||||||
'icon': 'chevron-left',
|
|
||||||
'class': 'info',
|
|
||||||
'title': _('Redirect slugs'),
|
|
||||||
'value': slug,
|
|
||||||
})
|
})
|
||||||
|
update_changes.append(change_data)
|
||||||
|
changes.extend(sorted(update_changes, key=itemgetter('order')))
|
||||||
|
|
||||||
if changed_object.deleted:
|
for name, m2m_changes in changed_object.m2m_changes.items():
|
||||||
|
field = model._meta.get_field(name)
|
||||||
|
for item in m2m_changes.added:
|
||||||
changes.append({
|
changes.append({
|
||||||
'icon': 'minus',
|
'icon': 'chevron-right',
|
||||||
'class': 'danger',
|
'class': 'info',
|
||||||
'empty': True,
|
'title': field.verbose_name,
|
||||||
'title': _('deleted'),
|
'value': item,
|
||||||
'order': (9,),
|
})
|
||||||
|
for item in m2m_changes.removed:
|
||||||
|
changes.append({
|
||||||
|
'icon': 'chevron-left',
|
||||||
|
'class': 'info',
|
||||||
|
'title': field.verbose_name,
|
||||||
|
'value': item,
|
||||||
})
|
})
|
||||||
|
|
||||||
changed_objects_data = sorted(changed_objects_data, key=itemgetter('order'))
|
if changed_object.deleted:
|
||||||
|
changes.append({
|
||||||
|
'icon': 'minus',
|
||||||
|
'class': 'danger',
|
||||||
|
'empty': True,
|
||||||
|
'title': _('deleted'),
|
||||||
|
})
|
||||||
|
|
||||||
|
changed_objects_data.append(changed_object_data)
|
||||||
|
|
||||||
cache.set(cache_key, changed_objects_data, 300)
|
cache.set(cache_key, changed_objects_data, 300)
|
||||||
ctx['changed_objects'] = changed_objects_data
|
ctx['changed_objects'] = changed_objects_data
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue