lets try to display the changeset contents nicely

This commit is contained in:
Laura Klünder 2024-08-26 16:31:19 +02:00
parent a7a5ec93e6
commit c9d73e9daf
2 changed files with 106 additions and 212 deletions

View file

@ -124,44 +124,7 @@ class ChangeSet(models.Model):
Wrap Objects
"""
def fill_changes_cache(self):
return
"""
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
return # todo: remove
"""
Analyse Changes

View file

@ -1,6 +1,7 @@
from itertools import chain
from operator import itemgetter
from django.apps import apps
from django.conf import settings
from django.contrib import messages
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.views.base import sidebar_view
from c3nav.editor.wrappers import is_created_pk
from c3nav.mapdata.fields import I18nField
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug
@ -193,81 +195,35 @@ def changeset_detail(request, pk):
changed_objects_data = []
added_redirects = {}
removed_redirects = {}
for changed_object in changeset.changed_objects.get(LocationRedirect, {}).values():
if changed_object.is_created == changed_object.deleted:
continue
obj = objects[LocationRedirect][changed_object.obj_pk]
redirect_list = (removed_redirects if changed_object.deleted else added_redirects)
redirect_list.setdefault(obj.target_id, []).append(obj.slug)
redirect_changed_objects = []
for pk in set(added_redirects.keys()) | set(removed_redirects.keys()):
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 model == LocationRedirect:
continue
for pk, changed_object in changed_objects.items():
if pk in objects[model]:
obj = objects[model][pk]
obj_desc = format_lazy(_('{model} #{id}'), model=obj.__class__._meta.verbose_name, id=pk)
if is_created_pk(pk):
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
# added_redirects = {}
# removed_redirects = {}
# for changed_object in changeset.changed_objects.get(LocationRedirect, {}).values():
# if changed_object.is_created == changed_object.deleted:
# continue
# obj = objects[LocationRedirect][changed_object.obj_pk]
# redirect_list = (removed_redirects if changed_object.deleted else added_redirects)
# redirect_list.setdefault(obj.target_id, []).append(obj.slug)
#
# redirect_changed_objects = []
# todo: display redirects nicely
for changed_object in changeset.changes.changed_objects:
model = apps.get_model("mapdata", changed_object.obj.model)
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,
'model': model,
'model_title': model._meta.verbose_name,
'pk': changed_object.obj.id,
'desc': changed_object.repr,
'title': 'TITLE',
'changes': changes,
'edit_url': edit_url,
'edit_url': None,
'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)
form_fields = get_editor_form(model)._meta.fields
if changed_object.is_created:
if changed_object.created:
changes.append({
'icon': 'plus',
'class': 'success',
@ -276,8 +232,7 @@ def changeset_detail(request, pk):
})
update_changes = []
for name, value in changed_object.updated_fields.items():
for name, value in changed_object.fields.items():
change_data = {
'icon': 'option-vertical',
'class': 'muted',
@ -287,7 +242,7 @@ def changeset_detail(request, pk):
'icon': 'map-marker',
'class': 'info',
'empty': True,
'title': _('created geometry') if changed_object.is_created else _('edited geometry'),
'title': _('created geometry') if changed_object.created else _('edited geometry'),
'order': (8,),
})
elif name == 'data':
@ -295,44 +250,43 @@ def changeset_detail(request, pk):
'icon': 'signal',
'class': 'info',
'empty': True,
'title': _('created scan data') if changed_object.is_created else _('edited scan data'),
'title': _('created scan data') if changed_object.created else _('edited scan data'),
'order': (9,),
})
else:
if '__i18n__' in name:
orig_name, i18n, lang = name.split('__')
lang_info = get_language_info(lang)
field = model._meta.get_field(orig_name)
field_title = format_lazy(_('{field_name} ({lang})'),
field_name=field.verbose_name,
lang=lang_info['name_translated'])
field_value = str(value)
if field_value:
getattr(obj, field.attname)[lang] = field_value
else:
getattr(obj, field.attname).pop(lang, None)
change_data.update({
'order': (4, tuple(code for code, title in settings.LANGUAGES).index(lang)),
})
else:
field = model._meta.get_field(name)
field_title = field.verbose_name
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,
if isinstance(field, I18nField):
for lang, subvalue in value.items():
sub_change_data = change_data.copy()
lang_info = get_language_info(lang)
field_title = format_lazy(_('{field_name} ({lang})'),
field_name=field.verbose_name,
lang=lang_info['name_translated'])
if subvalue == '' or subvalue is None:
sub_change_data.update({
'empty': True,
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
})
else:
field_value = field.to_python(value)
if name in unique_collisions:
sub_change_data.update({
'title': field_title,
'value': subvalue,
})
sub_change_data.update({
'order': (4, tuple(code for code, title in settings.LANGUAGES).index(lang)),
})
update_changes.append(sub_change_data)
else:
if value == '' or value is None:
change_data.update({
'unique_collision': field.name in unique_collisions,
'empty': True,
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
})
else:
change_data.update({
'title': field_title,
'value': value,
})
order = 5
if name == 'slug':
@ -342,46 +296,24 @@ def changeset_detail(request, pk):
change_data.update({
'order': (order, form_fields.index(name) if order else 1),
})
if field_value == '' or field_value is None:
change_data.update({
'empty': True,
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
})
else:
change_data.update({
'title': field_title,
'value': field_value,
})
update_changes.append(change_data)
changes.extend(sorted(update_changes, key=itemgetter('order')))
for m2m_mode in ('m2m_added', 'm2m_removed'):
m2m_list = getattr(changed_object, m2m_mode).items()
for name, values in sorted(m2m_list, key=lambda nv: form_fields.index(nv[0])):
for name, m2m_changes in changed_object.m2m_changes.items():
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, ()):
for item in m2m_changes.added:
changes.append({
'icon': 'chevron-right',
'class': 'info',
'title': _('Redirect slugs'),
'value': slug,
'title': field.verbose_name,
'value': item,
})
for slug in removed_redirects.get(obj.pk, ()):
for item in m2m_changes.removed:
changes.append({
'icon': 'chevron-left',
'class': 'info',
'title': _('Redirect slugs'),
'value': slug,
'title': field.verbose_name,
'value': item,
})
if changed_object.deleted:
@ -390,10 +322,9 @@ def changeset_detail(request, pk):
'class': 'danger',
'empty': True,
'title': _('deleted'),
'order': (9,),
})
changed_objects_data = sorted(changed_objects_data, key=itemgetter('order'))
changed_objects_data.append(changed_object_data)
cache.set(cache_key, changed_objects_data, 300)
ctx['changed_objects'] = changed_objects_data