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