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.py23
-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.py86
-rw-r--r--archaeological_context_records/templates/ishtar/sheet_contextrecord.html9
-rw-r--r--archaeological_context_records/views.py2
8 files changed, 200 insertions, 21 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..df074c768 100644
--- a/archaeological_context_records/forms.py
+++ b/archaeological_context_records/forms.py
@@ -179,6 +179,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 +195,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 +306,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,
@@ -527,23 +534,18 @@ class DatingForm(ManageOldType, forms.Form):
]
-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 +561,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 +579,7 @@ class RecordFormInterpretation(CustomForm, ManageOldType):
FieldType("identification", models.IdentificationType, True),
FieldType('cultural_attribution', models.CulturalAttributionType,
True),
+ FieldType("period", Period, is_multiple=True),
]
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..b12782e3a 100644
--- a/archaeological_context_records/models.py
+++ b/archaeological_context_records/models.py
@@ -30,7 +30,9 @@ 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 (
@@ -101,12 +103,17 @@ 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
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 +161,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():
@@ -294,6 +300,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 +743,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 +844,8 @@ class ContextRecord(
("site", "archaeological_site__pk"),
("file", "operation__associated_file__pk"),
]
- HISTORICAL_M2M = ["datings", "documentations", "excavation_technics", "identifications"]
+ # HISTORICAL_M2M = ["datings", "documentations", "excavation_technics", "identifications"]
+ HISTORICAL_M2M = ["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 +972,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)
@@ -1467,6 +1523,22 @@ for attr in ContextRecord.HISTORICAL_M2M:
)
+class ContextRecordDating(BaseDating):
+ SERIALIZE_EXCLUDE = ["context_record"]
+ CURRENT_MODEL = ContextRecord
+
+ 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")
+
+
class RelationType(GeneralRelationType):
class Meta:
verbose_name = _("Relation type")
diff --git a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html
index 4bd770fd0..869d6edd5 100644
--- a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html
+++ b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html
@@ -55,7 +55,7 @@
<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 %}
@@ -214,10 +214,12 @@
{% if display_datations %}
<div class="tab-pane fade" id="{{window_id}}-datations"
role="tabpanel" aria-labelledby="{{window_id}}-datations-tab">
- {% if dating_list %}
- <h3>{% trans "Datations" %}</h3>
+ {% if dating_list or item.periods_count %}
+ <h3>{% trans "Periods / Datings" %}</h3>
{% endif %}
+ {% field_flex_multiple_obj _("Periods") item 'periods' %}
{% include "ishtar/blocks/sheet_dating_list.html" %}
+ {% 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 +229,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 %}
diff --git a/archaeological_context_records/views.py b/archaeological_context_records/views.py
index 437a2824e..69f995932 100644
--- a/archaeological_context_records/views.py
+++ b/archaeological_context_records/views.py
@@ -115,7 +115,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 +129,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),
]