diff options
-rw-r--r-- | archaeological_context_records/models.py | 43 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 16 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/sheet_find.html | 10 | ||||
-rw-r--r-- | archaeological_finds/tests.py | 39 | ||||
-rw-r--r-- | ishtar_common/models.py | 38 | ||||
-rw-r--r-- | ishtar_common/templatetags/window_field.py | 5 | ||||
-rw-r--r-- | ishtar_common/utils.py | 14 |
7 files changed, 102 insertions, 63 deletions
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 5ba631dba..030f817d9 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -29,7 +29,7 @@ from django.utils.translation import ugettext_lazy as _, pgettext, \ activate, pgettext_lazy, deactivate from django.utils.text import slugify -from ishtar_common.utils import cached_label_changed +from ishtar_common.utils import cached_label_changed, HISTORY_M2M_SPLIT from ishtar_common.models import Document, GeneralType, \ BaseHistorizedItem, HistoricalRecords, OwnPerms, ShortMenuItem, \ @@ -104,25 +104,30 @@ class Dating(models.Model): 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 val == '': - val = None - elif key in ("period", "dating_type", "quality"): - field = cls._meta.get_field(key) - q = field.related_model.objects.filter(txt_idx=val) - if q.count(): - val = q.all()[0] - else: # do not exist anymore in db + def history_decompress(cls, full_value, create=False): + if not full_value: + return [] + full_res = [] + for value in full_value.split(HISTORY_M2M_SPLIT): + res = {} + for idx, val in enumerate(value.split(cls.SEP)): + key = cls.HISTORY_ATTR[idx] + if val == '': val = None - elif key in ("start_date", "end_date"): - val = int(val) - res[key] = val - return res + elif key in ("period", "dating_type", "quality"): + field = cls._meta.get_field(key) + q = field.related_model.objects.filter(txt_idx=val) + if q.count(): + val = q.all()[0] + else: # do not exist anymore in db + val = None + elif key in ("start_date", "end_date"): + val = int(val) + res[key] = val + if create: + res = cls.objects.create(**res) + full_res.append(res) + return full_res @classmethod def is_identical(cls, dating_1, dating_2): diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index f8cc34e61..9e377d1f6 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -32,7 +32,7 @@ from django.utils.translation import ugettext_lazy as _, pgettext_lazy, \ from ishtar_common.data_importer import post_importer_action, ImporterError from ishtar_common.utils import cached_label_changed, post_save_point, \ - m2m_historization_changed, HISTORY_M2M_SPLIT + m2m_historization_changed from ishtar_common.alternative_configs import ALTERNATE_CONFIGS @@ -41,7 +41,7 @@ from ishtar_common.models import Document, GeneralType, \ HistoricalRecords, OwnPerms, Person, Basket, post_save_cache, \ ValueGetter, get_current_profile, IshtarSiteProfile, PRIVATE_FIELDS, \ SpatialReferenceSystem, BulkUpdatedItem, ExternalIdManager, QuickAction, \ - MainItem, document_attached_changed + MainItem, document_attached_changed, HistoryModel from archaeological_operations.models import AdministrativeAct, Operation from archaeological_context_records.models import ContextRecord, Dating @@ -1199,7 +1199,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, help_text=_(u"Related treatments when no new find is created")) cached_label = models.TextField(_(u"Cached name"), null=True, blank=True, db_index=True) - history = HistoricalRecords() + history = HistoricalRecords(bases=[HistoryModel]) BASKET_MODEL = FindBasket class Meta: @@ -1233,16 +1233,6 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, return u" ; ".join([unicode(dating) for dating in self.datings.all()]) @property - def dating_list(self): - if self.historical_datings: - return [ - Dating.history_decompress(v) - for v in self.historical_datings.split(HISTORY_M2M_SPLIT) - ] - else: - return self.datings.all() - - @property def excavation_ids(self): return u" - ".join( [base_find['excavation_id'] diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html index 2d234b2d9..ac2df607d 100644 --- a/archaeological_finds/templates/ishtar/sheet_find.html +++ b/archaeological_finds/templates/ishtar/sheet_find.html @@ -19,7 +19,7 @@ {% with permission_view_document=permission_view_document %} {% with permission_view_own_document=permission_view_own_document %} -{% with display_identification=item.integrities.count|or_:item.remarkabilities.count|or_:item.conservatory_state|or_:item.conservatory_comment|or_:item.alterations.count|or_:item.alteration_causes.count|or_:item.preservation_to_considers.count|or_:item.appraisal_date|or_:item.treatment_emergency|or_:item.insurance_value|or_:item.estimated_value|or_:item.datings.count|or_:item.dating_comment %} +{% with display_datings=item.integrities.count|or_:item.remarkabilities.count|or_:item.conservatory_state|or_:item.conservatory_comment|or_:item.alterations.count|or_:item.alteration_causes.count|or_:item.preservation_to_considers.count|or_:item.appraisal_date|or_:item.treatment_emergency|or_:item.insurance_value|or_:item.estimated_value|or_:item.historical_datings|or_:item.datings.count|or_:item.dating_comment %} {% with display_warehouse_treatments=item.container|or_:item.container_ref|or_:item.upstream_treatment|or_:item.downstream_treatment|or_:item.treatments.count %} {% with can_view_documents=permission_view_own_document|or_:permission_view_document %} {% with display_documents=can_view_documents|and_:item.documents.count %} @@ -39,7 +39,7 @@ {% trans "Identification / Description / Dimensions" %} </a> </li> - {% if display_identification %} + {% if display_datings %} <li class="nav-item"> <a class="nav-link" id="{{window_id}}-preservation-tab" data-toggle="tab" href="#{{window_id}}-preservation" role="tab" @@ -180,7 +180,7 @@ {% include "ishtar/blocks/sheet_creation_section.html" %} </div> </div> - {% if display_identification %} + {% if display_datings %} <div class="tab-pane fade" id="{{window_id}}-preservation" role="tabpanel" aria-labelledby="{{window_id}}-preservation-tab"> {% if item.integrities.count or item.remarkabilities.count or item.conservatory_state or item.conservatory_comment or item.alterations.count or item.alteration_causes.count or item.preservation_to_considers.count or item.appraisal_date or item.treatment_emergency or item.insurance_value or item.estimated_value %} @@ -202,7 +202,6 @@ {% if item.historical_datings or item.datings.count or item.dating_comment %} <h3>{% trans "Dating" %}</h3> - {% if item.datings.count %} <table id='{{window_id}}-datings' class="table table-striped"> <tr> <th>{% trans "Period" %}</th> @@ -212,7 +211,7 @@ <th>{% trans "Quality" %}</th> <th>{% trans "Precise dating" %}</th> </tr> - {% for dating in item.dating_list %} + {% for dating in item|m2m_listing:'datings' %} <tr> <td> {{dating.period}} @@ -235,7 +234,6 @@ </tr> {% endfor %} </table> - {% endif %} {% field_flex_full "Comment on dating" item.dating_comment "<pre>" "</pre>" %} {% endif %} </div> diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index cefeb1649..231ac30ad 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -861,15 +861,19 @@ class FindHistoryTest(FindInit, TestCase): def test_m2m_history_save(self): find = self.finds[0] user = self.get_default_user() - nb_hist = find.history.count() ceram = models.MaterialType.objects.get(txt_idx='ceramic').pk glass = models.MaterialType.objects.get(txt_idx='glass').pk + + find = models.Find.objects.get(pk=find.pk) + nb_hist = find.history.count() + find.label = "hop hop hop1" + find.history_modifier = user + find._force_history = True + find.save() find.material_types.add(ceram) find.material_types.add(glass) d1_txt, d2_txt = self._add_datings(find) - - find = models.Find.objects.get(pk=find.pk) self.assertIn( find.historical_material_types, [HISTORY_M2M_SPLIT.join(['ceramic', 'glass']), # order do not @@ -880,20 +884,16 @@ class FindHistoryTest(FindInit, TestCase): HISTORY_M2M_SPLIT.join([d2_txt, d1_txt])]) # matter historical_material_types = find.historical_material_types - find.label = "hop hop hop1" - find.history_modifier = user - find._force_history = True - find.save() find = models.Find.objects.get(pk=find.pk) - find.material_types.remove(ceram) - find.datings.clear() find.label = "hop hop hop2" find.history_modifier = user if hasattr(find, 'skip_history_when_saving'): delattr(find, 'skip_history_when_saving') find._force_history = True find.save() + find.material_types.remove(ceram) + find.datings.clear() find = models.Find.objects.get(pk=find.pk) self.assertEqual(find.historical_material_types, 'glass') @@ -908,33 +908,33 @@ class FindHistoryTest(FindInit, TestCase): c = Client() user = self.get_default_user() find = self.finds[0] - - find = models.Find.objects.get(pk=find.pk) ceram = models.MaterialType.objects.get(txt_idx='ceramic').pk glass = models.MaterialType.objects.get(txt_idx='glass').pk - find.material_types.add(ceram) - find.material_types.add(glass) - self._add_datings(find) + + find = models.Find.objects.get(pk=find.pk) find.history_modifier = user find.label = "hop hop hop1" find._force_history = True if hasattr(find, 'skip_history_when_saving'): delattr(find, 'skip_history_when_saving') find.save() + find.material_types.add(ceram) + find.material_types.add(glass) + self._add_datings(find) find = models.Find.objects.get(pk=find.pk) - find.material_types.remove(ceram) find.history_modifier = user find.label = "hop hop hop2" - find.datings.clear() find._force_history = True if hasattr(find, 'skip_history_when_saving'): delattr(find, 'skip_history_when_saving') find.save() + find.datings.clear() + find.material_types.remove(ceram) find = models.Find.objects.get(pk=find.pk) - history_date = find.history.all()[1].history_date.strftime( - '%Y-%m-%dT%H:%M:%S.%f') + history_date = find.history.order_by('-history_date').all()[ + 1].history_date.strftime('%Y-%m-%dT%H:%M:%S.%f') c.login(username=self.username, password=self.password) response = c.get(reverse('show-find', @@ -957,7 +957,8 @@ class FindHistoryTest(FindInit, TestCase): content = response.content.decode('utf-8') self.assertIn( models.MaterialType.objects.get(txt_idx='ceramic').label, content) - self.assertIn("5001", content) + self.assertIn("5001", content, msg=u"5001 not found in historical " + u"sheet") self.assertIn( Period.objects.get(txt_idx='neolithic').label, content) diff --git a/ishtar_common/models.py b/ishtar_common/models.py index c7223c898..e5e173654 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -148,6 +148,18 @@ class ValueGetter(object): return values +class HistoryModel(models.Model): + class Meta: + abstract = True + + def m2m_listing(self, key): + hist_value = getattr(self, "historical_" + key, None) + if not hist_value: + return + related_model = getattr(self, key).model + return related_model.history_decompress(hist_value) + + class HistoricalRecords(BaseHistoricalRecords): def create_historical_record(self, instance, type): try: @@ -523,9 +535,9 @@ class GeneralType(Cached, models.Model): return self.txt_idx @classmethod - def history_decompress(cls, value=""): + def history_decompress(cls, value, create=False): if not value: - value = "" + return [] res = [] for txt_idx in value.split(HISTORY_M2M_SPLIT): try: @@ -1571,12 +1583,27 @@ class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData, if not new_item: raise HistoryError(u"The date to rollback to doesn't exist.") try: - for f in self._meta.fields: - k = f.name + field_keys = [f.name for f in self._meta.fields] + for k in field_keys: if k != 'id' and hasattr(self, k): if not hasattr(new_item, k): k = k + "_id" setattr(self, k, getattr(new_item, k)) + + # M2M history + if k.startwith('historical_') \ + and k[len('historical_'):] in field_keys: + value = getattr(new_item, k) + if not value: + continue + base_k = k[len('historical_'):] + base_field = self._meta.get_field(base_k) + base_field.clear() + new_values = base_field.related_model.history_decompress( + value, create=True + ) + for val in new_values: + base_field.add(val) try: self.history_modifier = User.objects.get( pk=new_item.history_modifier_id) @@ -1591,6 +1618,9 @@ class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData, for historized_item in to_del: historized_item.delete() + def m2m_listing(self, key): + return getattr(self, key).all() + def values(self): values = {} for f in self._meta.fields: diff --git a/ishtar_common/templatetags/window_field.py b/ishtar_common/templatetags/window_field.py index 3af9ed634..df72690c4 100644 --- a/ishtar_common/templatetags/window_field.py +++ b/ishtar_common/templatetags/window_field.py @@ -166,3 +166,8 @@ def field_flex_detail(context, caption, item, small=False): if small: size = 2 return field_detail(context, caption, item, size=size) + + +@register.filter +def m2m_listing(item, key): + return item.m2m_listing(key) diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 94e71c655..56c6f669f 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -938,12 +938,22 @@ def m2m_historization_changed(sender, **kwargs): obj = kwargs.get('instance', None) if not obj: return + hist_values = {} 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 + hist_values['historical_' + attr] = HISTORY_M2M_SPLIT.join(values) + for key in hist_values: + setattr(obj, key, hist_values[key]) + # force resave of last history record + if hasattr(obj, 'skip_history_when_saving'): + delattr(obj, 'skip_history_when_saving') + obj._force_history = True + q = obj.history.order_by("-history_date") + if q.count(): + last = q.all()[0] + last.delete() obj.save() |