diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-01-02 10:18:44 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-01-11 17:30:46 +0100 |
commit | 066a782e440b64f7584475347c1b762c89e0cab5 (patch) | |
tree | e5d0f38c898f24c420702682ded8d12ada2dbc3e | |
parent | e1ca97e099fab92f48471f34214bd0c5233dff5f (diff) | |
download | Ishtar-066a782e440b64f7584475347c1b762c89e0cab5.tar.bz2 Ishtar-066a782e440b64f7584475347c1b762c89e0cab5.zip |
Work on M2M historization
-rw-r--r-- | archaeological_context_records/models.py | 35 | ||||
-rw-r--r-- | archaeological_finds/migrations/0055_auto_20181227_1643.py | 75 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 20 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/sheet_find.html | 2 | ||||
-rw-r--r-- | ishtar_common/models.py | 19 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/blocks/window_field_flex_historized_multiple.html | 8 | ||||
-rw-r--r-- | ishtar_common/templatetags/window_field.py | 29 | ||||
-rw-r--r-- | ishtar_common/utils.py | 18 |
8 files changed, 200 insertions, 6 deletions
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 82527acb1..cce36236c 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -17,12 +17,12 @@ # See the file COPYING for details. -import time +import datetime from django.conf import settings from django.contrib.gis.db import models from django.core.urlresolvers import reverse -from django.db import connection, transaction +from django.db import connection from django.db.models import Q from django.db.models.signals import post_delete, post_save, m2m_changed from django.utils.translation import ugettext_lazy as _, pgettext, \ @@ -84,6 +84,37 @@ class Dating(models.Model): return unicode(self.period) return u"%s (%s-%s)" % (self.period, start_date, end_date) + SEP = u"$|£|$" + HISTORY_ATTR = ["period", "start_date", "end_date", "dating_type", + "quality", "precise_dating"] + + def history_compress(self): + values = [] + for attr in self.HISTORY_ATTR: + val = getattr(self, attr) + if hasattr(val, 'history_compress'): + values.append(val.history_compress()) + elif hasattr(val, 'isoformat'): + values.append(val.isoformat()) + else: + values.append(unicode(val)) + return self.SEP.join(values) + + @classmethod + def history_decompress(cls, value): + if not value: + value = "" + res = {} + for idx, val in enumerate(value.split(cls.SEP)): + key = cls.HISTORY_ATTR[idx] + if key in ("period", "dating_type", "quality"): + field = cls._meta.get_field(key) + val = field.to.model.objects.get(txt_idx=val) + elif key in ("start_date", "end_date"): + val = datetime.datetime.fromisoformat(val) + res[key] = val + return res + @classmethod def is_identical(cls, dating_1, dating_2): """ diff --git a/archaeological_finds/migrations/0055_auto_20181227_1643.py b/archaeological_finds/migrations/0055_auto_20181227_1643.py new file mode 100644 index 000000000..094fbaf12 --- /dev/null +++ b/archaeological_finds/migrations/0055_auto_20181227_1643.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-12-27 16:43 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_finds', '0054_migrate_main_image'), + ] + + operations = [ + migrations.AddField( + model_name='find', + name='historical_communicabilities', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='find', + name='historical_datings', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='find', + name='historical_integrities', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='find', + name='historical_material_types', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='find', + name='historical_object_types', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='find', + name='historical_remarkabilities', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='historicalfind', + name='historical_communicabilities', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='historicalfind', + name='historical_datings', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='historicalfind', + name='historical_integrities', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='historicalfind', + name='historical_material_types', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='historicalfind', + name='historical_object_types', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='historicalfind', + name='historical_remarkabilities', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 33ec2df0b..c7aec5dee 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -31,7 +31,8 @@ from django.utils.translation import ugettext_lazy as _, pgettext_lazy, \ activate, deactivate from ishtar_common.data_importer import post_importer_action, ImporterError -from ishtar_common.utils import cached_label_changed, post_save_point +from ishtar_common.utils import cached_label_changed, post_save_point, \ + m2m_historization_changed from ishtar_common.alternative_configs import ALTERNATE_CONFIGS @@ -1044,6 +1045,10 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, ('warehouse', 'container__location__pk'), ('site', 'base_finds__context_record__archaeological_site__pk') ] + HISTORICAL_M2M = [ + 'material_types', 'datings', 'object_types', 'integrities', + 'remarkabilities', 'communicabilities', + ] objects = ExternalIdManager() # fields @@ -1069,6 +1074,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, MaterialType, verbose_name=_(u"Material types"), related_name='finds', blank=True ) + historical_material_types = models.TextField(blank=True, null=True) material_type_quality = models.ForeignKey( MaterialTypeQualityType, verbose_name=_(u"Material type quality"), related_name='finds', @@ -1088,6 +1094,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, verbose_name=_(u"Downstream treatment"), on_delete=models.SET_NULL) datings = models.ManyToManyField(Dating, verbose_name=_(u"Dating"), related_name='find') + historical_datings = models.TextField(blank=True, null=True) container = models.ForeignKey( "archaeological_warehouse.Container", verbose_name=_(u"Container"), blank=True, null=True, @@ -1103,6 +1110,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, ObjectType, verbose_name=_(u"Object types"), related_name='find', blank=True ) + historical_object_types = models.TextField(blank=True, null=True) object_type_quality = models.ForeignKey( ObjectTypeQualityType, verbose_name=_(u"Object type quality"), related_name='finds', @@ -1111,12 +1119,15 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, integrities = models.ManyToManyField( IntegrityType, verbose_name=_(u"Integrity / interest"), related_name='find', blank=True) + historical_integrities = models.TextField(blank=True, null=True) remarkabilities = models.ManyToManyField( RemarkabilityType, verbose_name=_(u"Remarkability"), related_name='find', blank=True) + historical_remarkabilities = models.TextField(blank=True, null=True) communicabilities = models.ManyToManyField( CommunicabilityType, verbose_name=_(u"Communicability"), related_name='find', blank=True) + historical_communicabilities = models.TextField(blank=True, null=True) min_number_of_individuals = models.IntegerField( _(u"Minimum number of individuals (MNI)"), blank=True, null=True) length = models.FloatField(_(u"Length (cm)"), blank=True, null=True) @@ -1951,7 +1962,8 @@ def base_find_find_changed(sender, **kwargs): obj = kwargs.get('instance', None) if not obj: return - # recalculate complete id and external id + obj.skip_history_when_saving = True + # recalculate cached_label, complete id and external id obj.save() @@ -1960,6 +1972,10 @@ m2m_changed.connect(base_find_find_changed, sender=Find.base_finds.through) m2m_changed.connect(document_attached_changed, sender=Find.documents.through) +for attr in Find.HISTORICAL_M2M: + m2m_changed.connect(m2m_historization_changed, + sender=getattr(Find, attr).through) + class Property(LightHistorizedItem): find = models.ForeignKey(Find, verbose_name=_(u"Find")) diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html index cfbefa306..1ff5e21f4 100644 --- a/archaeological_finds/templates/ishtar/sheet_find.html +++ b/archaeological_finds/templates/ishtar/sheet_find.html @@ -140,7 +140,7 @@ <div class='row'> {% field_flex_full "Description" item.description "<pre>" "</pre>" %} {% field_flex "Is complete?" item.is_complete %} - {% field_flex_multiple "Material types" item.material_types %} + {% field_flex_multiple_obj "Material types" item 'material_types' %} {% field_flex "Material type quality" item.material_type_quality %} {% field_flex_multiple "Object types" item.object_types %} {% field_flex "Object type quality" item.object_type_quality %} diff --git a/ishtar_common/models.py b/ishtar_common/models.py index e1318ef62..2915c4997 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -69,7 +69,7 @@ from ishtar_common.models_imports import ImporterModel, ImporterType, \ Import, TargetKeyGroup from ishtar_common.utils import get_cache, disable_for_loaddata, create_slug, \ get_all_field_names, merge_tsvectors, cached_label_changed, \ - generate_relation_graph + generate_relation_graph, HISTORY_M2M_SPLIT __all__ = [ 'ImporterModel', 'ImporterType', 'ImporterDefault', 'ImporterDefaultValues', @@ -507,6 +507,21 @@ class GeneralType(Cached, models.Model): def natural_key(self): return (self.txt_idx,) + def history_compress(self): + return self.txt_idx + + @classmethod + def history_decompress(cls, value=""): + if not value: + value = "" + res = [] + for txt_idx in value.split(HISTORY_M2M_SPLIT): + try: + res.append(cls.objects.get(txt_idx=txt_idx)) + except cls.DoesNotExist: + continue + return res + @property def explicit_label(self): return u"{} ({})".format(self.label, self._meta.verbose_name) @@ -1433,6 +1448,8 @@ class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData, IS_BASKET = False EXTERNAL_ID_KEY = '' EXTERNAL_ID_DEPENDENCIES = [] + HISTORICAL_M2M = [] + history_modifier = models.ForeignKey( User, related_name='+', on_delete=models.SET_NULL, verbose_name=_(u"Last editor"), blank=True, null=True) diff --git a/ishtar_common/templates/ishtar/blocks/window_field_flex_historized_multiple.html b/ishtar_common/templates/ishtar/blocks/window_field_flex_historized_multiple.html new file mode 100644 index 000000000..a68bd0cb4 --- /dev/null +++ b/ishtar_common/templates/ishtar/blocks/window_field_flex_historized_multiple.html @@ -0,0 +1,8 @@ +{% load i18n %}{% if data %} +<dl class="col-12 {% if size == 2 %}col-lg-6{% else %}col-md-6 col-lg-4{% endif %} d-flex flex-wrap row"> + <dt class="col-5">{% trans caption %}</dt> + <dd class="col-7">{% for d in data %} + {% if forloop.counter0 %} ; {% endif %}{{ d }} + {% endfor %}</dd> +</dl> +{% endif %} diff --git a/ishtar_common/templatetags/window_field.py b/ishtar_common/templatetags/window_field.py index a5bae3b72..30a711ed9 100644 --- a/ishtar_common/templatetags/window_field.py +++ b/ishtar_common/templatetags/window_field.py @@ -95,6 +95,27 @@ def field_multiple(caption, data, li=False, size=None): return {'caption': caption, 'data': data, 'li': li, "size": size} +from django.template import Library, loader, Context + + +@register.simple_tag +def field_multiple_obj(caption, item, attr, li=False, size=None): + data = getattr(item, attr) + if not hasattr(item, '_step') or not hasattr(item, 'historical_' + attr): + t = loader.get_template('ishtar/blocks/window_field_flex_multiple.html') + return t.render( + {'caption': caption, 'data': data, 'li': li, "size": size} + ) + rel_model = data.model + data = getattr(item, 'historical_' + attr) + data = rel_model.history_decompress(data) + t = loader.get_template( + 'ishtar/blocks/window_field_flex_historized_multiple.html') + return t.render( + {'caption': caption, 'data': data, 'li': li, "size": size} + ) + + @register.inclusion_tag('ishtar/blocks/window_field_multiple.html') def field_li_multiple(caption, data): return field_multiple(caption, data, li=True) @@ -108,6 +129,14 @@ def field_flex_multiple(caption, data, small=False): return field_multiple(caption, data, size=size) +@register.simple_tag +def field_flex_multiple_obj(caption, item, attr, small=False): + size = None + if small: + size = 2 + return field_multiple_obj(caption, item, attr, size=size) + + @register.inclusion_tag('ishtar/blocks/window_field_flex_multiple_full.html') def field_flex_multiple_full(caption, data, small=False): size = None diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index ff67fc470..94e71c655 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -929,3 +929,21 @@ def get_urls_for_model(model, views, own=False, autocomplete=False, ] return urls + + +HISTORY_M2M_SPLIT = u"*||*" + + +def m2m_historization_changed(sender, **kwargs): + obj = kwargs.get('instance', None) + if not obj: + return + for attr in obj.HISTORICAL_M2M: + values = [] + for value in getattr(obj, attr).all(): + if not hasattr(value, "history_compress"): + continue + values.append(value.history_compress()) + setattr(obj, 'historical_' + attr, HISTORY_M2M_SPLIT.join(values)) + obj.skip_history_when_saving = True + obj.save() |