diff options
Diffstat (limited to 'archaeological_context_records')
11 files changed, 337 insertions, 106 deletions
diff --git a/archaeological_context_records/admin.py b/archaeological_context_records/admin.py index 62f4c12e2..8a0e254ae 100644 --- a/archaeological_context_records/admin.py +++ b/archaeological_context_records/admin.py @@ -54,7 +54,6 @@ class ContextRecordAdmin(HistorizedObjectAdmin, MainGeoDataItem): model = models.ContextRecord readonly_fields = HistorizedObjectAdmin.readonly_fields + [ "cached_label", - "datings", ] exclude = ["documents", "main_image"] diff --git a/archaeological_context_records/forms.py b/archaeological_context_records/forms.py index 4ecb40386..9f7dfa0b4 100644 --- a/archaeological_context_records/forms.py +++ b/archaeological_context_records/forms.py @@ -43,7 +43,6 @@ from archaeological_context_records import models from ishtar_common.forms import ( FinalForm, - FormSet, reverse_lazy, get_form_selection, ManageOldType, @@ -179,6 +178,9 @@ class RecordSelect(GeoItemSelect, PeriodSelect): filling = forms.CharField(label=_("Filling")) interpretation = forms.CharField(label=_("Interpretation")) parcel = forms.CharField(label=_("Parcel")) + periods = forms.ChoiceField( + label=_("Periods"), choices=[], required=False + ) has_finds = forms.NullBooleanField(label=_("Has finds")) cr_relation_types = forms.ChoiceField( label=_("Search within relations"), choices=[] @@ -192,6 +194,7 @@ class RecordSelect(GeoItemSelect, PeriodSelect): TYPES = PeriodSelect.TYPES + [ FieldType('area', Area), + FieldType("periods", Period), FieldType('cultural_attributions', models.CulturalAttributionType), FieldType("unit", models.Unit), FieldType("cr_relation_types", models.RelationType), @@ -302,7 +305,10 @@ class RecordFormGeneral(CustomForm, ManageOldType): form_admin_name = _("Context record - 020 - General") form_slug = "contextrecord-020-general" file_upload = True - base_models = ["documentation", "excavation_technic", "structure", "texture", "color", "inclusion"] + base_models = [ + "documentation", "excavation_technic", "structure", "texture", "color", + "inclusion" + ] associated_models = { "archaeological_site": ArchaeologicalSite, "parcel": Parcel, @@ -505,45 +511,18 @@ class RecordFormGeneral(CustomForm, ManageOldType): return cleaned_data -class DatingForm(ManageOldType, forms.Form): - form_label = _("Dating") - base_model = "dating" - associated_models = { - "dating_type": models.DatingType, - "quality": models.DatingQuality, - "period": models.Period, - } - period = forms.ChoiceField(label=_("Chronological period"), choices=[]) - start_date = forms.IntegerField(label=_("Start date"), required=False) - end_date = forms.IntegerField(label=_("End date"), required=False) - quality = forms.ChoiceField(label=_("Quality"), required=False, choices=[]) - dating_type = forms.ChoiceField(label=_("Dating type"), required=False, choices=[]) - precise_dating = forms.CharField(label=_("Precise on this dating"), required=False) - - TYPES = [ - FieldType("dating_type", models.DatingType), - FieldType("quality", models.DatingQuality), - FieldType("period", models.Period), - ] - - -DatingFormSet = formset_factory(DatingForm, can_delete=True, formset=FormSet) -DatingFormSet.form_label = _("Dating") -DatingFormSet.form_admin_name = _("Context record - 030 - Dating") -DatingFormSet.form_slug = "contextrecord-030-datings" - - class RecordFormInterpretation(CustomForm, ManageOldType): HEADERS = {} form_label = _("Interpretation") form_admin_name = _("Context record - 040 - Interpretation") form_slug = "contextrecord-040-interpretation" - base_models = ["cultural_attribution", "identification"] + base_models = ["cultural_attribution", "identification", "period"] associated_models = { "activity": models.ActivityType, "identification": models.IdentificationType, 'cultural_attribution': models.CulturalAttributionType, + "period": Period, } interpretation = forms.CharField( label=_("Interpretation"), widget=forms.Textarea, required=False @@ -559,6 +538,10 @@ class RecordFormInterpretation(CustomForm, ManageOldType): taq_estimated = forms.IntegerField(label=_("Estimated TAQ"), required=False) tpq = forms.IntegerField(label=_("TPQ"), required=False) tpq_estimated = forms.IntegerField(label=_("Estimated TPQ"), required=False) + period = widgets.Select2MultipleField( + label=_("Periods"), + required=False, + ) cultural_attribution = forms.MultipleChoiceField( label=_("Cultural attributions"), choices=[], widget=widgets.Select2Multiple, @@ -573,6 +556,7 @@ class RecordFormInterpretation(CustomForm, ManageOldType): FieldType("identification", models.IdentificationType, True), FieldType('cultural_attribution', models.CulturalAttributionType, True), + FieldType("period", Period, is_multiple=True), ] @@ -914,3 +898,49 @@ class QAContextRecordFormMulti(QAForm): def _set_qa_relation_type(self, item, __): pass + + +class QADating(ManageOldType, forms.Form): + form_label = _("Dating") + associated_models = { + "dating_type_id": models.DatingType, + "quality_id": models.DatingQuality, + "period_id": models.Period, + } + pk = forms.IntegerField(required=False, widget=forms.HiddenInput) + reference = forms.CharField( + label=_("Reference"), validators=[validators.MaxLengthValidator(400)], + required=False + ) + period_id = forms.ChoiceField(label=_("Chronological period"), choices=[], + required=False) + start_date = forms.IntegerField(label=_("Start date"), required=False) + end_date = forms.IntegerField(label=_("End date"), required=False) + quality_id = forms.ChoiceField(label=_("Quality"), required=False, choices=[]) + dating_type_id = forms.ChoiceField(label=_("Dating type"), required=False, choices=[]) + precise_dating = forms.CharField(label=_("Precise on this dating"), required=False, + widget=forms.Textarea) + + TYPES = [ + FieldType("dating_type_id", models.DatingType), + FieldType("quality_id", models.DatingQuality), + FieldType("period_id", models.Period), + ] + + def clean(self): + cleaned_data = self.cleaned_data + if any(1 for k in self.cleaned_data if self.cleaned_data[k]): + return cleaned_data + raise forms.ValidationError(_("No data provided.")) + + def save(self, model, item): + data = copy(self.cleaned_data) + data[model.CURRENT_MODEL_ATTR + "_id"] = item.pk + for attr in ['period_id', 'quality_id', 'dating_type_id']: + if not data.get(attr, None): + data[attr] = None + if data.get("pk", None): + pk = data.pop("pk") + model.objects.filter(pk=pk).update(**data) + return + model.objects.create(**data) diff --git a/archaeological_context_records/migrations/0124_contextrecord_periods.py b/archaeological_context_records/migrations/0124_contextrecord_periods.py new file mode 100644 index 000000000..72877aa2b --- /dev/null +++ b/archaeological_context_records/migrations/0124_contextrecord_periods.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.19 on 2025-10-28 15:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_operations', '0123_add_timezone_django_v4'), + ('archaeological_context_records', '0123_add_timezone_django_v4'), + ] + + operations = [ + migrations.AddField( + model_name='contextrecord', + name='periods', + field=models.ManyToManyField(blank=True, to='archaeological_operations.period', verbose_name='Periods'), + ), + ] diff --git a/archaeological_context_records/migrations/0125_datings_refactoring.py b/archaeological_context_records/migrations/0125_datings_refactoring.py new file mode 100644 index 000000000..585939420 --- /dev/null +++ b/archaeological_context_records/migrations/0125_datings_refactoring.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.19 on 2025-10-29 10:53 + +from django.db import migrations, models +import django.db.models.deletion +import ishtar_common.models_common +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_operations', '0123_add_timezone_django_v4'), + ('archaeological_context_records', '0124_contextrecord_periods'), + ] + + operations = [ + migrations.AlterModelOptions( + name='dating', + options={'verbose_name': 'Dating - deprecated', 'verbose_name_plural': 'Datings - deprecated'}, + ), + migrations.RenameField( + model_name='contextrecord', + old_name='datings', + new_name='datings_old', + ), + migrations.AddField( + model_name='dating', + name='external_id', + field=models.TextField(blank=True, default='', verbose_name='External ID'), + ), + migrations.AddField( + model_name='dating', + name='reference', + field=models.TextField(blank=True, default='', verbose_name='Reference'), + ), + migrations.AlterField( + model_name='dating', + name='period', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='archaeological_operations.period', verbose_name='Chronological period'), + ), + migrations.CreateModel( + name='ContextRecordDating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.UUIDField(default=uuid.uuid4)), + ('reference', models.TextField(blank=True, default='', verbose_name='Reference')), + ('external_id', models.TextField(blank=True, default='', verbose_name='External ID')), + ('start_date', models.IntegerField(blank=True, null=True, verbose_name='Start date')), + ('end_date', models.IntegerField(blank=True, null=True, verbose_name='End date')), + ('precise_dating', models.TextField(blank=True, default='', verbose_name='Precise on this dating')), + ('context_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='datings', to='archaeological_context_records.contextrecord', verbose_name='Context record')), + ('dating_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_context_records.datingtype', verbose_name='Dating type')), + ('period', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='archaeological_operations.period', verbose_name='Chronological period')), + ('quality', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_context_records.datingquality', verbose_name='Quality')), + ], + options={ + 'verbose_name': 'Context record dating', + 'verbose_name_plural': 'Context record datings', + }, + bases=(models.Model, ishtar_common.models_common.SerializeItem), + ), + ] diff --git a/archaeological_context_records/migrations/0126_migrate_periods_and_datings.py b/archaeological_context_records/migrations/0126_migrate_periods_and_datings.py new file mode 100644 index 000000000..3b04c7ad1 --- /dev/null +++ b/archaeological_context_records/migrations/0126_migrate_periods_and_datings.py @@ -0,0 +1,19 @@ +from django.db import migrations +from ishtar_common.utils_migrations import migrate_dating_periods + + +def _migrate_datings_periods(apps, __): + model_dating = apps.get_model("archaeological_context_records", "contextrecorddating") + model = apps.get_model("archaeological_context_records", "contextrecord") + migrate_dating_periods(apps, model_dating, model, "context_record") + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_context_records', '0125_datings_refactoring'), + ] + + operations = [ + migrations.RunPython(_migrate_datings_periods) + ] diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 29deae5ee..63e6a773d 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -30,12 +30,15 @@ from django.db.models import Q from django.db.models.signals import post_delete, post_save, m2m_changed from django.urls import reverse, reverse_lazy -from ishtar_common.utils import gettext_lazy as _, pgettext_lazy, pgettext +from ishtar_common.utils import get_generated_id, gettext_lazy as _, pgettext_lazy, \ + pgettext + from django.utils.text import slugify from ishtar_common.utils import ( cached_label_changed, m2m_historization_changed, + related_historization_changed, post_save_geo, SearchAltName, ) @@ -101,12 +104,18 @@ post_save.connect(post_save_cache, sender=DatingQuality) post_delete.connect(post_save_cache, sender=DatingQuality) -class Dating(models.Model, SerializeItem): +class BaseDating(models.Model, SerializeItem): SLUG = "dating" SERIALIZE_EXCLUDE = ["find", "context_record"] + CURRENT_MODEL = None + CURRENT_MODEL_ATTR = None uuid = models.UUIDField(default=uuid.uuid4) + reference = models.TextField(_("Reference"), blank=True, default="") + external_id = models.TextField(_("External ID"), blank=True, default="") period = models.ForeignKey( - Period, verbose_name=_("Chronological period"), on_delete=models.PROTECT + Period, verbose_name=_("Chronological period"), on_delete=models.PROTECT, + blank=True, + null=True, ) start_date = models.IntegerField(_("Start date"), blank=True, null=True) end_date = models.IntegerField(_("End date"), blank=True, null=True) @@ -154,8 +163,7 @@ class Dating(models.Model, SerializeItem): } class Meta: - verbose_name = _("Dating") - verbose_name_plural = _("Datings") + abstract = True def __str__(self): if self.precise_dating and self.precise_dating.strip(): @@ -171,6 +179,8 @@ class Dating(models.Model, SerializeItem): def get_values(self, prefix="", no_values=False, filtr=None, **kwargs): values = {} + if not filtr or prefix + "reference" in filtr: + values[prefix + "reference"] = self.reference if not filtr or prefix + "period" in filtr: values[prefix + "period"] = str(self.period) if not filtr or prefix + "start_date" in filtr: @@ -188,6 +198,7 @@ class Dating(models.Model, SerializeItem): return values HISTORY_ATTR = [ + "reference", "period", "start_date", "end_date", @@ -198,7 +209,8 @@ class Dating(models.Model, SerializeItem): def history_compress(self): values = {} - for attr in self.HISTORY_ATTR: + attrs = self.HISTORY_ATTR + [self.CURRENT_MODEL_ATTR + "_id"] + for attr in attrs: val = getattr(self, attr) if hasattr(val, "history_compress"): val = val.history_compress() @@ -244,6 +256,7 @@ class Dating(models.Model, SerializeItem): attribute is identical """ for attr in [ + "reference", "period", "start_date", "end_date", @@ -283,6 +296,7 @@ class Dating(models.Model, SerializeItem): for dating in obj.datings.order_by("pk").all(): key = ( dating.period.pk, + dating.reference, dating.start_date, dating.end_date, dating.dating_type, @@ -294,6 +308,50 @@ class Dating(models.Model, SerializeItem): continue dating.delete() + @property + def q_parent(self): + if not self.pk or not self.CURRENT_MODEL: + return + if getattr(self, "__q_parent", None): + return self.__q_parent + q = self.CURRENT_MODEL.objects.filter(datings__pk=self.pk) + if q.count(): + self.__q_parent = q + return q + + @property + def parent_external_id(self): + if not self.pk or not self.q_parent: + return "" + return self.q_parent.all()[0].external_id + + @property + def auto_id(self): + if not self.pk or not self.q_parent: + return 0 + parent_pk = self.q_parent.all()[0].pk + attr = "context_record" if self.CURRENT_MODEL == ContextRecord else "find" + for idx, dating_pk in enumerate( + self.__class__.objects.filter(**{attr: parent_pk}).values_list( + "pk", flat=True).all()).all(): + if dating_pk == self.pk: + return idx + 1 + return 0 + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + if not self.pk: + return + external_id = get_generated_id("dating_external_id", self) + if external_id != self.external_id: + self.__class__.objects.filter(pk=self.pk).update(external_id=external_id) + + +class Dating(BaseDating): + class Meta: + verbose_name = _("Dating - deprecated") + verbose_name_plural = _("Datings - deprecated") + class Unit(GeneralType): order = models.IntegerField(_("Order")) @@ -693,6 +751,10 @@ class ContextRecord( pgettext_lazy("key for text search", "excavation-technique"), "excavation_technics__label__iexact", ), + "periods": SearchAltName( + pgettext_lazy("key for text search", "period"), + "periods__label__iexact", + ), "cultural_attributions": SearchAltName( pgettext_lazy("key for text search", "cultural-attribution"), "cultural_attributions__label__iexact", @@ -790,7 +852,8 @@ class ContextRecord( ("site", "archaeological_site__pk"), ("file", "operation__associated_file__pk"), ] - HISTORICAL_M2M = ["datings", "documentations", "excavation_technics", "identifications"] + HISTORICAL_M2M = ["periods", "datings", "documentations", "excavation_technics", + "identifications"] CACHED_LABELS = ["cached_label", "cached_periods", "cached_related_context_records"] DOWN_MODEL_UPDATE = ["base_finds"] GET_VALUES_EXTRA = ValueGetter.GET_VALUES_EXTRA + ["context_record"] @@ -917,7 +980,8 @@ class ContextRecord( default="", help_text=_("A short description of the location of the context record"), ) - datings = models.ManyToManyField(Dating, related_name="context_records") + datings_old = models.ManyToManyField(Dating, related_name="context_records") + periods = models.ManyToManyField(Period, verbose_name=_("Periods"), blank=True) documentations = models.ManyToManyField(DocumentationType, blank=True) structures = models.ManyToManyField(StructureType, blank=True) textures = models.ManyToManyField(TextureType, blank=True) @@ -1312,7 +1376,7 @@ class ContextRecord( return self.full_label() def _generate_cached_periods(self): - return " & ".join(dating.period.label for dating in self.datings.all()) + return " & ".join(period.label for period in self.periods.all()) def _generate_cached_related_context_records(self): return self.detailed_related_context_records() @@ -1461,11 +1525,35 @@ post_save.connect(context_record_post_save, sender=ContextRecord) m2m_changed.connect(document_attached_changed, sender=ContextRecord.documents.through) m2m_changed.connect(geodata_attached_changed, sender=ContextRecord.geodata.through) -for attr in ContextRecord.HISTORICAL_M2M: - m2m_changed.connect( - m2m_historization_changed, sender=getattr(ContextRecord, attr).through + +class ContextRecordDating(BaseDating): + SERIALIZE_EXCLUDE = ["context_record"] + CURRENT_MODEL = ContextRecord + CURRENT_MODEL_ATTR = "context_record" + + context_record = models.ForeignKey( + ContextRecord, + verbose_name=_("Context record"), + related_name="datings", + on_delete=models.CASCADE, ) + class Meta: + verbose_name = _("Context record dating") + verbose_name_plural = _("Context record datings") + + +for attr in ContextRecord.HISTORICAL_M2M: + if attr == "datings": + model = ContextRecordDating + post_save.connect(related_historization_changed, sender=model) + post_delete.connect(related_historization_changed, sender=model) + else: + model = getattr(ContextRecord, attr).through + m2m_changed.connect( + m2m_historization_changed, sender=model + ) + class RelationType(GeneralRelationType): class Meta: diff --git a/archaeological_context_records/serializers.py b/archaeological_context_records/serializers.py index 2bccc63f7..0314de918 100644 --- a/archaeological_context_records/serializers.py +++ b/archaeological_context_records/serializers.py @@ -8,7 +8,7 @@ from archaeological_finds.serializers import ( generate_warehouse_queryset as finds_generate_warehouse_queryset, ) -CR_MODEL_LIST = [models.Dating, models.ContextRecord, models.RecordRelations] +CR_MODEL_LIST = [models.ContextRecord, models.ContextRecordDating, models.RecordRelations] # TODO: associated documents @@ -136,9 +136,8 @@ def cr_serialization( cr_ids = list( result_queryset[models.ContextRecord.__name__].values_list("id", flat=True) ) - result_queryset[models.Dating.__name__] = models.Dating.objects.filter( - Q(context_records__id__in=cr_ids) | Q(find__id__in=list(find_ids)) - ) + result_queryset[models.ContextRecordDating.__name__] = \ + models.ContextRecordDating.objects.filter(context_record_id__in=cr_ids) if get_queryset: return result_queryset diff --git a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html index 4bd770fd0..bd766f9f6 100644 --- a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html +++ b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html @@ -28,6 +28,7 @@ {% with display_interpretation=item|safe_or:"identifications.count"|or_:item.interpretation|or_:item.activity %} {% with display_datations=dating_list|or_:item.taq|or_:item.taq_estimated|or_:item.tpq|or_:item.tpq_estimated|or_:has_cultural_attributions %} {% with can_view_finds=permission_view_own_find|or_:permission_view_find %} +{% with can_change=permission_change_own_find|or_:permission_change_find %} {% with has_finds=item|safe_or:"base_finds.count"|safe_and_not:"base_finds_not_available" %} {% with display_finds=has_finds|and_:can_view_finds %} {% with display_data=item.data %} @@ -50,12 +51,12 @@ </a> </li> {% endif %} - {% if display_datations %} + {% if display_datations or can_change %} <li class="nav-item"> <a class="nav-link" id="{{window_id}}-datations-tab" data-toggle="tab" href="#{{window_id}}-datations" role="tab" aria-controls="{{window_id}}-datations" aria-selected="false"> - {% trans "Datations" %} + {% trans "Periods / Datings" %} </a> </li> {% endif %} @@ -211,13 +212,15 @@ </div> {% endif %} - {% if display_datations %} + {% if display_datations or can_change %} <div class="tab-pane fade" id="{{window_id}}-datations" role="tabpanel" aria-labelledby="{{window_id}}-datations-tab"> - {% if dating_list %} - <h3>{% trans "Datations" %}</h3> - {% endif %} + <h3>{% trans "Periods / Datings" %}</h3> + {% field_flex_multiple_obj _("Periods") item 'periods' %} + {% with url_dating="context-record-dating" %} {% include "ishtar/blocks/sheet_dating_list.html" %} + {% endwith %} + {% if item.cultural_attributions_count or item.taq or item.taq_estimated or item.tpq or item.tpq_estimated or datings_comment %} <h3>{% trans "Dating complements" %}</h3> <div class='row'> {% field_flex_multiple_obj "Cultural attributions" item 'cultural_attributions' %} @@ -227,6 +230,7 @@ {% field_flex "Estimated TPQ" item.tpq_estimated %} {% field_flex_full "Comment on datings" item.datings_comment "<pre>" "</pre>" has_image %} </div> + {% endif %} </div> {% endif %} @@ -367,6 +371,6 @@ {% endif %} </div> -{% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} +{% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endblock %} diff --git a/archaeological_context_records/tests.py b/archaeological_context_records/tests.py index 852cd3aa7..ea29ce938 100644 --- a/archaeological_context_records/tests.py +++ b/archaeological_context_records/tests.py @@ -162,30 +162,14 @@ class ImportContextRecordTest(ImportTest, TestCase): impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) - # Dating is not in models that can be created but force new is - # set for a column that references Dating impt.importation() - self.assertEqual(len(impt.errors), 5) + + self.assertEqual(len(impt.errors), 4) self.assertTrue( "doesn't exist in the database." in impt.errors[0]["error"] or "n'existe pas dans la base" in impt.errors[0]["error"] ) - # retry with only Dating (no context record) - for cr in models.ContextRecord.objects.all(): - cr.delete() - mcc, form = self.init_context_record_import() - mcc.created_models.clear() - dat_model, c = ImporterModel.objects.get_or_create( - klass="archaeological_context_records.models.Dating", - defaults={"name": "Dating"}, - ) - mcc.created_models.add(dat_model) - impt = form.save(self.ishtar_user) - impt.initialize() - self.init_cr_targetkey(impt) - impt.importation() - current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, 0) @@ -199,30 +183,12 @@ class ImportContextRecordTest(ImportTest, TestCase): klass="archaeological_context_records.models.ContextRecord" ) ) - mcc.created_models.add(dat_model) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) impt.importation() current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, 4) - """ - - # add a context record model - for cr in models.ContextRecord.objects.all(): - cr.delete() - mcc, form = self.init_context_record_import() - mcc.created_models.clear() - mcc.created_models.add(ImporterModel.objects.get( - klass='archaeological_context_records.models.ContextRecord' - )) - impt = form.save(self.ishtar_user) - impt.initialize() - self.init_cr_targetkey(impt) - impt.importation() - current_nb = models.ContextRecord.objects.count() - self.assertEqual(current_nb, 4) - """ class ContextRecordInit(OperationInitTest): @@ -282,10 +248,10 @@ class SerializationTest(GenericSerializationTest, ContextRecordInit, TestCase): ope2 = self.create_operation()[1] cr = self.create_context_record(data={"label": "CR 1", "operation": ope1})[0] cr2 = self.create_context_record(data={"label": "CR 2", "operation": ope2})[1] - dating = models.Dating.objects.create( + models.ContextRecordDating.objects.create( period=models.Period.objects.all()[0], + context_record=cr ) - cr.datings.add(dating) rlt = models.RelationType.objects.create( label="Test", txt_idx="test", symmetrical=False ) @@ -560,16 +526,20 @@ class ContextRecordTest(ContextRecordInit, TestCase): def test_redundant_dating_clean(self): obj = self.context_records[0] - values = {"period": models.Period.objects.all()[0]} + values = { + "period": models.Period.objects.all()[0], + "context_record": obj + } values_2 = { "period": models.Period.objects.all()[0], "quality": models.DatingQuality.objects.all()[0], + "context_record": obj } - obj.datings.add(models.Dating.objects.create(**values)) - obj.datings.add(models.Dating.objects.create(**values)) - obj.datings.add(models.Dating.objects.create(**values_2)) - obj.datings.add(models.Dating.objects.create(**values_2)) + models.ContextRecordDating.objects.create(**values) + models.ContextRecordDating.objects.create(**values) + models.ContextRecordDating.objects.create(**values_2) + models.ContextRecordDating.objects.create(**values_2) self.assertEqual(obj.datings.count(), 4) obj.fix() self.assertEqual(obj.datings.count(), 2) @@ -898,8 +868,7 @@ class ContextRecordSearchTest(ContextRecordInit, TestCase, SearchText): neo = models.Period.objects.get(txt_idx="neolithic") final_neo = models.Period.objects.get(txt_idx="final-neolithic") recent_neo = models.Period.objects.get(txt_idx="recent-neolithic") - dating = models.Dating.objects.create(period=final_neo) - cr.datings.add(dating) + models.ContextRecordDating.objects.create(period=final_neo, context_record=cr) search = {"datings__period": final_neo.pk} @@ -1664,9 +1633,6 @@ class ContextRecordWizardCreationTest(WizardTest, ContextRecordInit, TestCase): def post_wizard(self): self.assertEqual(models.ContextRecord.objects.count(), self.cr_nb + 2) - # identical datings, only one should be finaly save - cr = models.ContextRecord.objects.order_by("-pk")[0] - self.assertEqual(cr.datings.count(), 1) class ContextRecordRelationTest(ContextRecordInit, TestCase): diff --git a/archaeological_context_records/urls.py b/archaeological_context_records/urls.py index bb06e9945..ec529deeb 100644 --- a/archaeological_context_records/urls.py +++ b/archaeological_context_records/urls.py @@ -162,6 +162,14 @@ urlpatterns = [ name="context-record-relation-modify", ), re_path( + r"^context-record-dating/(?P<pk>.+)/$", + check_permissions(["archaeological_context_records.change_contextrecord", + "archaeological_context_records.change_own_contextrecord"])( + views.context_record_dating_add + ), + name="context-record-dating-add", + ), + re_path( r"^operation-qa-contextrecord/(?P<pks>[0-9]+)/$", check_permissions(["archaeological_context_records.add_contextrecord"])( views.QAOperationContextRecordView.as_view() diff --git a/archaeological_context_records/views.py b/archaeological_context_records/views.py index 437a2824e..8c222f395 100644 --- a/archaeological_context_records/views.py +++ b/archaeological_context_records/views.py @@ -19,9 +19,10 @@ import json +from django.core.exceptions import PermissionDenied from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect, Http404 -from django.shortcuts import redirect +from django.shortcuts import redirect, render from django.urls import reverse from ishtar_common.utils import gettext_lazy as _ from django.views.generic import RedirectView @@ -115,7 +116,6 @@ record_search_wizard = wizards.RecordSearch.as_view( record_creation_steps = [ ("selec-record_creation", forms.OperationRecordFormSelection), ("general-record_creation", forms.RecordFormGeneral), - ("datings-record_creation", forms.DatingFormSet), ("interpretation-record_creation", forms.RecordFormInterpretation), ("final-record_creation", forms.FinalForm), ] @@ -130,7 +130,6 @@ record_modification_steps = [ ("selec-record_modification", forms.RecordFormSelection), ("operation-record_modification", forms.OperationFormSelection), ("general-record_modification", forms.RecordFormGeneral), - ("datings-record_modification", forms.DatingFormSet), ("interpretation-record_modification", forms.RecordFormInterpretation), ("final-record_modification", forms.FinalForm), ] @@ -196,6 +195,44 @@ context_record_modify_relations = get_relation_modify( ) +def get_dating_form(model, dating_model, url_name, action='add'): + def _dating_add(request, pk, current_right=None): + try: + item = model.objects.get(pk=pk) + except model.DoesNotExist: + raise Http404() + if "_own_" in current_right: + if not request.user.has_perm(current_right, item): + raise PermissionDenied() + elif current_right: + if not request.user.has_perm(current_right): + raise PermissionDenied() + if request.method == 'POST': + form = forms.QADating(request.POST) + if form.is_valid(): + form.save(dating_model, item) + return redirect(reverse(url_name, args=[pk])) + else: + form = forms.QADating() + button_name = _("Add") if action == "add" else _("Modify") + icon = "fa fa-plus" if action == "add" else "fa fa-pencil" + return render( + request, + "ishtar/forms/qa_form.html", { + "page_name": _("Dating"), + "icon": icon, + "action_name": button_name, + "form": form, + "url": reverse(url_name, args=[pk]) + }) + return _dating_add + + +context_record_dating_add = get_dating_form( + models.ContextRecord, models.ContextRecordDating, "context-record-dating-add" +) + + class GenerateRelationImage(IshtarMixin, LoginRequiredMixin, RedirectView): upper_model = models.Operation model = models.ContextRecord |
