summaryrefslogtreecommitdiff
path: root/archaeological_context_records
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_context_records')
-rw-r--r--archaeological_context_records/admin.py1
-rw-r--r--archaeological_context_records/forms.py92
-rw-r--r--archaeological_context_records/migrations/0124_contextrecord_periods.py19
-rw-r--r--archaeological_context_records/migrations/0125_datings_refactoring.py62
-rw-r--r--archaeological_context_records/migrations/0126_migrate_periods_and_datings.py19
-rw-r--r--archaeological_context_records/models.py112
-rw-r--r--archaeological_context_records/serializers.py7
-rw-r--r--archaeological_context_records/templates/ishtar/sheet_contextrecord.html18
-rw-r--r--archaeological_context_records/tests.py62
-rw-r--r--archaeological_context_records/urls.py8
-rw-r--r--archaeological_context_records/views.py43
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