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 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

View file

@ -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,81 +195,35 @@ 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 = [] # redirect_changed_objects = []
# todo: display redirects nicely
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
for changed_object in changeset.changes.changed_objects:
model = apps.get_model("mapdata", changed_object.obj.model)
changes = [] changes = []
missing_dependencies = changed_object.get_missing_dependencies()
unique_collisions = changed_object.get_unique_collisions()
changed_object_data = { changed_object_data = {
'model': obj.__class__, 'model': model,
'model_title': obj.__class__._meta.verbose_name, 'model_title': model._meta.verbose_name,
'pk': changed_object.pk, 'pk': changed_object.obj.id,
'desc': obj_desc, 'desc': changed_object.repr,
'title': obj.title if getattr(obj, 'titles', None) else None, 'title': 'TITLE',
'changes': changes, 'changes': changes,
'edit_url': edit_url, 'edit_url': None,
'deleted': changed_object.deleted, '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 form_fields = get_editor_form(model)._meta.fields
if changed_object.is_created: if changed_object.created:
changes.append({ changes.append({
'icon': 'plus', 'icon': 'plus',
'class': 'success', 'class': 'success',
@ -276,8 +232,7 @@ def changeset_detail(request, pk):
}) })
update_changes = [] update_changes = []
for name, value in changed_object.fields.items():
for name, value in changed_object.updated_fields.items():
change_data = { change_data = {
'icon': 'option-vertical', 'icon': 'option-vertical',
'class': 'muted', 'class': 'muted',
@ -287,7 +242,7 @@ def changeset_detail(request, pk):
'icon': 'map-marker', 'icon': 'map-marker',
'class': 'info', 'class': 'info',
'empty': True, '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,), 'order': (8,),
}) })
elif name == 'data': elif name == 'data':
@ -295,44 +250,43 @@ def changeset_detail(request, pk):
'icon': 'signal', 'icon': 'signal',
'class': 'info', 'class': 'info',
'empty': True, '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,), '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: else:
field = model._meta.get_field(name) field = model._meta.get_field(name)
field_title = field.verbose_name field_title = field.verbose_name
if field.related_model is not None: if isinstance(field, I18nField):
if value is None: for lang, subvalue in value.items():
field_value = None sub_change_data = change_data.copy()
else: lang_info = get_language_info(lang)
if issubclass(field.related_model, User): field_title = format_lazy(_('{field_name} ({lang})'),
field_value = objects[field.related_model][value].username field_name=field.verbose_name,
else: lang=lang_info['name_translated'])
field_value = objects[field.related_model][value].title if subvalue == '' or subvalue is None:
change_data.update({ sub_change_data.update({
'missing_dependency': field.name in missing_dependencies, 'empty': True,
'title': format_lazy(_('remove {field_title}'), field_title=field_title),
}) })
else: else:
field_value = field.to_python(value) sub_change_data.update({
if name in unique_collisions: '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({ 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 order = 5
if name == 'slug': if name == 'slug':
@ -342,46 +296,24 @@ def changeset_detail(request, pk):
change_data.update({ change_data.update({
'order': (order, form_fields.index(name) if order else 1), '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) update_changes.append(change_data)
changes.extend(sorted(update_changes, key=itemgetter('order'))) changes.extend(sorted(update_changes, key=itemgetter('order')))
for m2m_mode in ('m2m_added', 'm2m_removed'): for name, m2m_changes in changed_object.m2m_changes.items():
m2m_list = getattr(changed_object, m2m_mode).items()
for name, values in sorted(m2m_list, key=lambda nv: form_fields.index(nv[0])):
field = model._meta.get_field(name) field = model._meta.get_field(name)
for value in values: for item in m2m_changes.added:
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({ changes.append({
'icon': 'chevron-right', 'icon': 'chevron-right',
'class': 'info', 'class': 'info',
'title': _('Redirect slugs'), 'title': field.verbose_name,
'value': slug, 'value': item,
}) })
for slug in removed_redirects.get(obj.pk, ()): for item in m2m_changes.removed:
changes.append({ changes.append({
'icon': 'chevron-left', 'icon': 'chevron-left',
'class': 'info', 'class': 'info',
'title': _('Redirect slugs'), 'title': field.verbose_name,
'value': slug, 'value': item,
}) })
if changed_object.deleted: if changed_object.deleted:
@ -390,10 +322,9 @@ def changeset_detail(request, pk):
'class': 'danger', 'class': 'danger',
'empty': True, 'empty': True,
'title': _('deleted'), '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) cache.set(cache_key, changed_objects_data, 300)
ctx['changed_objects'] = changed_objects_data ctx['changed_objects'] = changed_objects_data