summaryrefslogtreecommitdiff
path: root/archaeological_operations
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_operations')
-rw-r--r--archaeological_operations/admin.py9
-rw-r--r--archaeological_operations/forms.py198
-rw-r--r--archaeological_operations/migrations/0125_archaeologicalsite_actors.py19
-rw-r--r--archaeological_operations/migrations/0126_archaeologicalsite_heritage_relations_datings.py207
-rw-r--r--archaeological_operations/migrations/0127_data_migration_current_states.py23
-rw-r--r--archaeological_operations/migrations/0128_add_editors.py39
-rw-r--r--archaeological_operations/migrations/0129_data_migration.json396
-rw-r--r--archaeological_operations/migrations/0129_data_migration_heritageinterest_heritageenvprotection_relsite.py24
-rw-r--r--archaeological_operations/models.py267
-rw-r--r--archaeological_operations/templates/ishtar/sheet_operation.html25
-rw-r--r--archaeological_operations/templates/ishtar/sheet_site.html63
-rw-r--r--archaeological_operations/tests.py46
-rw-r--r--archaeological_operations/urls.py35
-rw-r--r--archaeological_operations/views.py32
14 files changed, 1229 insertions, 154 deletions
diff --git a/archaeological_operations/admin.py b/archaeological_operations/admin.py
index 429315513..82c5c7f90 100644
--- a/archaeological_operations/admin.py
+++ b/archaeological_operations/admin.py
@@ -141,6 +141,7 @@ class RecordRelationsAdmin(admin.ModelAdmin):
admin_site.register(models.RecordRelations, RecordRelationsAdmin)
+admin_site.register(models.SiteRecordRelations, RecordRelationsAdmin)
class RelationTypeAdmin(GeneralTypeAdmin):
@@ -150,6 +151,7 @@ class RelationTypeAdmin(GeneralTypeAdmin):
admin_site.register(models.RelationType, RelationTypeAdmin)
+admin_site.register(models.SiteRelationType, RelationTypeAdmin)
class RecordQualityTypeAdmin(GeneralTypeAdmin):
@@ -210,7 +212,10 @@ admin_site.register(models.CulturalAttributionType,
CulturalAttributionTypeAdmin)
-general_models = [models.RemainType, models.NatureOfSiteType, models.InterpretationLevelType,
- models.SiteCurrentStatusType, models.SiteDiscoveryStatusType, models.SiteType]
+general_models = [
+ models.RemainType, models.NatureOfSiteType, models.InterpretationLevelType,
+ models.SiteCurrentStatusType, models.SiteDiscoveryStatusType, models.SiteType,
+ models.HeritageInterestType, models.HeritageAndEnvironmentalProtectionType
+]
for model in general_models:
admin_site.register(model, GeneralTypeAdmin)
diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py
index ba59f4419..3e94e3963 100644
--- a/archaeological_operations/forms.py
+++ b/archaeological_operations/forms.py
@@ -31,7 +31,6 @@ from django.core import validators
from django.db.models import Max
from django.forms.formsets import formset_factory, DELETION_FIELD_NAME, \
TOTAL_FORM_COUNT
-from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from ishtar_common.utils import gettext_lazy as _, pgettext_lazy, parse_parcels
@@ -42,13 +41,11 @@ from ishtar_common import widgets
from ishtar_common.forms import FinalForm, FormSet, \
reverse_lazy, get_data_from_formset, QAForm, CustomFormSearch,\
ManageOldType, IshtarForm, CustomForm, FieldType, FormHeader, \
- GeoItemSelect, LockForm, MultiSearchForm, DocumentItemSelect
+ GeoItemSelect, LockForm, MultiSearchForm, DocumentItemSelect, DatingSelect
from ishtar_common.forms_common import TownFormSet, get_town_field, TownForm
from ishtar_common.models import valid_id, valid_ids, Person, Town, \
DocumentTemplate, Organization, get_current_profile, \
- person_type_pks_lazy, person_type_pk_lazy, organization_type_pks_lazy, \
- organization_type_pk_lazy, SpatialReferenceSystem, Area, \
- get_sra_agent_label, get_sra_agent_head_scientist_label, get_operator_label
+ SpatialReferenceSystem, Area, QualifiedBiographicalNote
from ishtar_common.wizards import MultiValueDict
from .widgets import ParcelWidget, SelectParcelWidget, OAWidget
@@ -435,6 +432,32 @@ RecordRelationsFormSet.form_admin_name = _("Operation - Relations")
RecordRelationsFormSet.form_slug = "operation-relations"
+class SiteRecordRelationsForm(RecordRelationsForm):
+ current_model = models.SiteRelationType
+ current_related_model = models.ArchaeologicalSite
+ associated_models = {
+ "right_record": models.ArchaeologicalSite,
+ "relation_type": models.SiteRelationType,
+ }
+ ERROR_MISSING = _("You should select an archaeological site and a relation type.")
+ ERROR_SAME = _("An archaeological site cannot be related to himself.")
+
+ right_record = forms.IntegerField(
+ label=_("Archaeological site"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-archaeologicalsite'),
+ associated_model=models.ArchaeologicalSite),
+ validators=[valid_id(models.ArchaeologicalSite)], required=False)
+
+
+SiteRecordRelationsFormSet = formset_factory(
+ SiteRecordRelationsForm, can_delete=True, formset=RecordRelationsFormSetBase
+)
+SiteRecordRelationsFormSet.form_label = _("Archaeological site - Relations")
+RecordRelationsFormSet.form_admin_name = _("Archaeological site - Relations")
+RecordRelationsFormSet.form_slug = "archaeologicalsite-recordrelations"
+
+
class OpeSiteRelationsForm(ManageOldType):
associated_models = {'right_record': models.ArchaeologicalSite}
@@ -449,6 +472,7 @@ class OpeSiteRelationsForm(ManageOldType):
super().__init__(*args, **kwargs)
self.fields["right_record"].label = get_current_profile().get_site_label()
+
OpeSiteRelationsFormSet = formset_factory(
OpeSiteRelationsForm, can_delete=True, formset=RecordRelationsFormSetBase,
extra=3
@@ -631,6 +655,7 @@ class OperationFormMultiSelection(LockForm, MultiSearchForm):
class OperationCodeInput(forms.TextInput):
"""Manage auto complete when changing year in form"""
+
def render(self, *args, **kwargs):
name, value = args
base_name = '-'.join(name.split('-')[:-1])
@@ -1176,7 +1201,7 @@ class OperationDeletionForm(FinalForm):
#########
-class SiteSelect(GeoItemSelect):
+class SiteSelect(GeoItemSelect, DatingSelect):
_model = models.ArchaeologicalSite
form_admin_name = _("Archaeological site - 001 - Search")
form_slug = "archaeological_site-001-search"
@@ -1189,23 +1214,48 @@ class SiteSelect(GeoItemSelect):
name = forms.CharField(label=_("Name"), max_length=200, required=False)
other_reference = forms.CharField(label=_("Other reference"),
max_length=200, required=False)
- types = widgets.Select2MultipleField(label=_("Types"), required=False)
+ types = widgets.Select2SimpleField(label=_("Types"), required=False,
+ modal="modal-advanced-search")
+ heritage_interest = forms.ChoiceField(label=_("Heritage interests"), choices=[],
+ required=False)
+ actors = forms.IntegerField(
+ label=_("Actors"), required=False,
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-qualifiedbiographicalnote')))
+ collaborators = forms.IntegerField(
+ label=_("Collaborators"), required=False,
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-person')))
+ protection_id = forms.CharField(label=_("Protection ID"), required=False)
+ protection_date = DateField(label=_("Protection date"), required=False)
+ heritage_environmental_protections = forms.ChoiceField(
+ label=_("Heritage and environmental protections"), choices=[],
+ required=False)
+ details_on_protection = forms.CharField(label=_("Details on protection"),
+ required=False)
discoverer = forms.IntegerField(
- widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person'), associated_model=Person),
+ widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person'),
+ associated_model=Person),
label=_("Discoverer"))
- periods = forms.ChoiceField(label=_("Periods"), choices=[], required=False)
- remains = forms.ChoiceField(label=_("Remains"), choices=[], required=False)
- cultural_attributions = forms.ChoiceField(
- label=_("Cultural attribution"), choices=[], required=False)
- discovery_status = forms.ChoiceField(label=_("Discovery status"), choices=[], required=False)
- current_status = forms.ChoiceField(label=_("Current status"), choices=[], required=False)
- nature_of_site = forms.ChoiceField(label=_("Nature of site"), choices=[], required=False)
- interpretation_level = forms.ChoiceField(label=_("Interpretation level"), choices=[], required=False)
+ periods = widgets.Select2SimpleField(label=_("Periods"), required=False,
+ modal="modal-advanced-search")
+ remains = widgets.Select2SimpleField(label=_("Remains"), required=False,
+ modal="modal-advanced-search")
+ cultural_attributions = widgets.Select2SimpleField(
+ label=_("Cultural attributions"), required=False, modal="modal-advanced-search")
+ discovery_status = forms.ChoiceField(label=_("Discovery status"), choices=[],
+ required=False)
+ current_states = forms.ChoiceField(label=_("Current states"), required=False)
+ nature_of_site = forms.ChoiceField(label=_("Nature of site"), choices=[],
+ required=False)
+ interpretation_level = forms.ChoiceField(label=_("Interpretation level"),
+ choices=[], required=False)
+
towns = get_town_field()
towns__areas = forms.ChoiceField(label=_("Areas"), choices=[])
- description = forms.CharField(label=_("Description"), max_length=200, required=False)
- public_description = forms.CharField(label=_("Public description"), max_length=200, required=False)
- comment = forms.CharField(label=_("Comment"), max_length=200, required=False)
+ description = forms.CharField(label=_("Description"), required=False)
+ public_description = forms.CharField(label=_("Public description"), required=False)
+ comment = forms.CharField(label=_("Comment"), required=False)
top_operation = forms.IntegerField(
label=_("Top operation"), required=False,
widget=widgets.JQueryAutoComplete(
@@ -1244,17 +1294,21 @@ class SiteSelect(GeoItemSelect):
discovery_area = forms.CharField(
label=_("Discovery area"), max_length=200,
required=False)
+
TYPES = [
FieldType('periods', models.Period),
FieldType('remains', models.RemainType),
- FieldType("types", models.SiteType, is_multiple=True),
- FieldType('current_status', models.SiteCurrentStatusType),
+ FieldType("types", models.SiteType),
+ FieldType('current_states', models.SiteCurrentStatusType),
FieldType('discovery_status', models.SiteDiscoveryStatusType),
FieldType('cultural_attributions', models.CulturalAttributionType),
FieldType('nature_of_site', models.NatureOfSiteType),
FieldType('interpretation_level', models.InterpretationLevelType),
FieldType('towns__areas', Area),
- ] + GeoItemSelect.TYPES
+ FieldType('heritage_interest', models.HeritageInterestType),
+ FieldType('heritage_environmental_protections',
+ models.HeritageAndEnvironmentalProtectionType),
+ ] + GeoItemSelect.TYPES + DatingSelect.TYPES
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -1268,6 +1322,7 @@ class SiteSelect(GeoItemSelect):
'affmar_number',
'drassm_number',
))
+ self._reorder_period_fields("comment")
class SiteFormSelection(LockForm, CustomFormSearch):
@@ -1312,41 +1367,79 @@ class SiteForm(CustomForm, ManageOldType):
form_admin_name = _("Archaeological site - 010 - General")
form_slug = "archaeological_site-010-general"
associated_models = {
- 'period': models.Period, 'remain': models.RemainType,
- 'spatial_reference_system': SpatialReferenceSystem,
- 'cultural_attribution': models.CulturalAttributionType,
- 'collaborator': Person,
- 'discoverer': Person,
- "nature_of_site": models.NatureOfSiteType,
- "interpretation_level": models.InterpretationLevelType,
+ "actor": QualifiedBiographicalNote,
+ "collaborator": Person,
+ "cultural_attribution": models.CulturalAttributionType,
+ "current_state": models.SiteCurrentStatusType,
+ "discoverer": Person,
"discovery_status": models.SiteDiscoveryStatusType,
- "current_status": models.SiteCurrentStatusType,
+ "editor": models.Author,
+ "heritage_interest": models.HeritageInterestType,
+ "heritage_environmental_protection": models.HeritageAndEnvironmentalProtectionType,
+ "interpretation_level": models.InterpretationLevelType,
+ "nature_of_site": models.NatureOfSiteType,
+ "period": models.Period,
+ "remain": models.RemainType,
"town": Town,
"type": models.SiteType,
}
- base_models = ["period", "remain", "collaborator", "cultural_attribution", "town", "type"]
-
+ extra_form_modals = ["author", "qualifiedbiographicalnote", "biographicalnote",
+ "person", "organization"]
+ base_models = [
+ "actor", "collaborator", "cultural_attribution", "current_state", "editor",
+ "heritage_interest", "heritage_environmental_protection", "period", "remain",
+ "town", "type"
+ ]
pk = forms.IntegerField(required=False, widget=forms.HiddenInput)
+ HEADERS['reference'] = FormHeader(_("General"))
reference = forms.CharField(label=_("Reference"), max_length=200)
- name = forms.CharField(label=_("Name"), max_length=200, required=False)
other_reference = forms.CharField(label=_("Other reference"),
required=False)
+ name = forms.CharField(label=_("Name"), max_length=200, required=False)
+ heritage_interest = forms.MultipleChoiceField(
+ label=_("Heritage interest"), choices=[], widget=widgets.Select2Multiple,
+ required=False)
+ actor = widgets.Select2MultipleField(
+ model=QualifiedBiographicalNote, label=_("Actors"), required=False,
+ remote=True, new=True, remote_filter='qualification_type__S-A')
collaborator = widgets.Select2MultipleField(
- model=Person, label=_("Collaborators"), required=False, remote=True)
+ model=Person, label=_("Collaborators"), required=False,
+ remote=True, new=True)
description = forms.CharField(label=_("Description"), widget=forms.Textarea,
required=False)
- public_description = forms.CharField(label=_("Public description"), widget=forms.Textarea,
- required=False)
+ public_description = forms.CharField(
+ label=_("Public description"), widget=forms.Textarea,
+ required=False
+ )
comment = forms.CharField(label=_("Comment"), widget=forms.Textarea,
required=False)
+ HEADERS['protection_id'] = FormHeader(_("Protection"))
+ protection_id = forms.CharField(label=_("Protection ID"), required=False)
+ protection_date = DateField(label=_("Protection date"), required=False)
+ heritage_environmental_protection = forms.MultipleChoiceField(
+ label=_("Heritage and environmental protections"), choices=[],
+ widget=widgets.Select2Multiple, required=False)
+ details_on_protection = forms.CharField(
+ label=_("Details on protection"), widget=forms.Textarea,
+ required=False
+ )
HEADERS['type'] = FormHeader(_("Scientific"))
type = forms.MultipleChoiceField(
label=_("Types"), choices=[], widget=widgets.Select2Multiple,
required=False)
- nature_of_site = forms.ChoiceField(choices=[], label=_("Nature of site"), required=False)
- interpretation_level = forms.ChoiceField(choices=[], label=_("Interpretation level"), required=False)
- discovery_status = forms.ChoiceField(choices=[], label=_("Discovery status"), required=False)
- current_status = forms.ChoiceField(choices=[], label=_("Current status"), required=False)
+ nature_of_site = forms.ChoiceField(
+ choices=[], label=_("Nature of site"), required=False
+ )
+ interpretation_level = forms.ChoiceField(
+ choices=[], label=_("Interpretation level"), required=False
+ )
+ discovery_status = forms.ChoiceField(
+ choices=[], label=_("Discovery status"), required=False
+ )
+ current_state = forms.MultipleChoiceField(
+ label=_("Current states"), choices=[], widget=widgets.Select2Multiple,
+ required=False
+ )
period = forms.MultipleChoiceField(
label=_("Periods"), choices=[], widget=widgets.Select2Multiple,
required=False)
@@ -1359,7 +1452,8 @@ class SiteForm(CustomForm, ManageOldType):
required=False)
discoverer = forms.IntegerField(
widget=widgets.JQueryAutoComplete(
- reverse_lazy('autocomplete-person-permissive'), associated_model=Person),
+ reverse_lazy('autocomplete-person-permissive'), associated_model=Person,
+ new=True),
label=_("Discoverer"), required=False)
HEADERS['town'] = FormHeader(_("Localization"))
town = widgets.Select2MultipleField(
@@ -1378,22 +1472,32 @@ class SiteForm(CustomForm, ManageOldType):
widget=forms.Textarea, required=False
)
+ HEADERS["editor"] = FormHeader(_("Sheet"))
+ editor = widgets.Select2MultipleField(
+ label=_("Editors"), required=False,
+ model=models.Author, remote=True, new=True
+ )
+
TYPES = [
FieldType('type', models.SiteType, True),
FieldType('period', models.Period, True),
FieldType('remain', models.RemainType, True),
FieldType('cultural_attribution',
models.CulturalAttributionType, True),
+ FieldType('heritage_interest', models.HeritageInterestType, True),
+ FieldType('heritage_environmental_protection',
+ models.HeritageAndEnvironmentalProtectionType, True),
FieldType('nature_of_site', models.NatureOfSiteType),
FieldType('interpretation_level', models.InterpretationLevelType),
- FieldType('current_status', models.SiteCurrentStatusType),
+ FieldType('current_state', models.SiteCurrentStatusType, True),
FieldType('discovery_status', models.SiteDiscoveryStatusType),
]
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- if 'collaborator' in self.fields:
- self.fields['collaborator'].widget.attrs['full-width'] = True
+ OPTIONS_PERMISSIONS = [
+ # field name, permission, options
+ ("actor", ("ishtar_common.add_qualifiedbiographicalnote",), {"new": True}),
+ ("discoverer", ("ishtar_common.add_person",), {"new": True}),
+ ("editor", ("ishtar_common.add_author",), {"new": True}),
+ ]
def clean_reference(self):
reference = self.cleaned_data['reference']
diff --git a/archaeological_operations/migrations/0125_archaeologicalsite_actors.py b/archaeological_operations/migrations/0125_archaeologicalsite_actors.py
new file mode 100644
index 000000000..ac1d91587
--- /dev/null
+++ b/archaeological_operations/migrations/0125_archaeologicalsite_actors.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.19 on 2026-03-24 09:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0274_qualifiedbiographicalnote'),
+ ('archaeological_operations', '0124_archaeologicalsite_cached_types'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='actors',
+ field=models.ManyToManyField(blank=True, related_name='sites', to='ishtar_common.qualifiedbiographicalnote', verbose_name='Actors'),
+ ),
+ ]
diff --git a/archaeological_operations/migrations/0126_archaeologicalsite_heritage_relations_datings.py b/archaeological_operations/migrations/0126_archaeologicalsite_heritage_relations_datings.py
new file mode 100644
index 000000000..baa407d6f
--- /dev/null
+++ b/archaeological_operations/migrations/0126_archaeologicalsite_heritage_relations_datings.py
@@ -0,0 +1,207 @@
+# Generated by Django 4.2.19 on 2026-03-27 12:03
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models_common
+import re
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0275_authortype_parent'),
+ ('archaeological_context_records', '0126_migrate_periods_and_datings'),
+ ('archaeological_operations', '0125_archaeologicalsite_actors'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='cached_current_states',
+ field=models.TextField(blank=True, default='', help_text='Generated automatically - do not edit', verbose_name='Cached current states label'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='cached_heritage_environmental_protections',
+ field=models.TextField(blank=True, default='', help_text='Generated automatically - do not edit', verbose_name='Cached heritage and environmental protections label'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='cached_heritage_interests',
+ field=models.TextField(blank=True, default='', help_text='Generated automatically - do not edit', verbose_name='Cached heritage interests label'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='current_states',
+ field=models.ManyToManyField(blank=True, to='archaeological_operations.sitecurrentstatustype', verbose_name='Current status'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='details_on_protection',
+ field=models.TextField(blank=True, default='', verbose_name='Details on protection'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='sites', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='protection_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Protection date'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='protection_id',
+ field=models.TextField(blank=True, default='', verbose_name='Protection ID'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='cached_current_states',
+ field=models.TextField(blank=True, default='', help_text='Generated automatically - do not edit', verbose_name='Cached current states label'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='cached_heritage_environmental_protections',
+ field=models.TextField(blank=True, default='', help_text='Generated automatically - do not edit', verbose_name='Cached heritage and environmental protections label'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='cached_heritage_interests',
+ field=models.TextField(blank=True, default='', help_text='Generated automatically - do not edit', verbose_name='Cached heritage interests label'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='details_on_protection',
+ field=models.TextField(blank=True, default='', verbose_name='Details on protection'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='protection_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Protection date'),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='protection_id',
+ field=models.TextField(blank=True, default='', verbose_name='Protection ID'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='current_status',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites_deprecated', to='archaeological_operations.sitecurrentstatustype', verbose_name='Current status - deprecated'),
+ ),
+ migrations.AlterField(
+ model_name='historicalarchaeologicalsite',
+ name='current_status',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_operations.sitecurrentstatustype', verbose_name='Current status - deprecated'),
+ ),
+ migrations.CreateModel(
+ name='SiteRelationType',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('label', models.TextField(verbose_name='Label')),
+ ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), 'Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.', 'invalid')], verbose_name='Textual ID')),
+ ('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
+ ('available', models.BooleanField(default=True, verbose_name='Available')),
+ ('order', models.IntegerField(default=1, verbose_name='Order')),
+ ('symmetrical', models.BooleanField(verbose_name='Symmetrical')),
+ ('tiny_label', models.CharField(blank=True, max_length=50, null=True, verbose_name='Tiny label')),
+ ('logical_relation', models.CharField(blank=True, choices=[('above', 'Above'), ('below', 'Below'), ('equal', 'Equal'), ('include', 'Include'), ('included', 'Is included')], max_length=10, null=True, verbose_name='Logical relation')),
+ ('inverse_relation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_operations.siterelationtype', verbose_name='Inverse relation')),
+ ],
+ options={
+ 'verbose_name': 'Site relation type',
+ 'verbose_name_plural': 'Site relation types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='SiteRecordRelations',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('timestamp_geo', models.IntegerField(blank=True, null=True, verbose_name='Timestamp geo')),
+ ('timestamp_label', models.IntegerField(blank=True, null=True, verbose_name='Timestamp label')),
+ ('imports', models.ManyToManyField(blank=True, related_name='imported_%(app_label)s_%(class)s', to='ishtar_common.import', verbose_name='Created by imports')),
+ ('imports_updated', models.ManyToManyField(blank=True, related_name='import_updated_%(app_label)s_%(class)s', to='ishtar_common.import', verbose_name='Updated by imports')),
+ ('left_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='right_relations', to='archaeological_operations.archaeologicalsite')),
+ ('relation_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='archaeological_operations.siterelationtype')),
+ ('right_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='left_relations', to='archaeological_operations.archaeologicalsite')),
+ ],
+ options={
+ 'verbose_name': 'Site record relation',
+ 'verbose_name_plural': 'Site record relations',
+ 'ordering': ('left_record__cached_label', 'relation_type', 'right_record__cached_label'),
+ 'permissions': [('view_siterelation', 'Can view all Site relations')],
+ },
+ ),
+ migrations.CreateModel(
+ name='SiteDating',
+ 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')),
+ ('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')),
+ ('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='datings', to='archaeological_operations.archaeologicalsite', verbose_name='Site')),
+ ],
+ options={
+ 'verbose_name': 'Site dating',
+ 'verbose_name_plural': 'Site datings',
+ },
+ bases=(models.Model, ishtar_common.models_common.SerializeItem),
+ ),
+ migrations.CreateModel(
+ name='HeritageInterestType',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('label', models.TextField(verbose_name='Label')),
+ ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), 'Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.', 'invalid')], verbose_name='Textual ID')),
+ ('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
+ ('available', models.BooleanField(default=True, verbose_name='Available')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_operations.heritageinteresttype', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Heritage interest type',
+ 'verbose_name_plural': 'Heritage interest types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='HeritageAndEnvironmentalProtectionType',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('label', models.TextField(verbose_name='Label')),
+ ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), 'Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.', 'invalid')], verbose_name='Textual ID')),
+ ('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
+ ('available', models.BooleanField(default=True, verbose_name='Available')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_operations.heritageandenvironmentalprotectiontype', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Heritage and environmental protection type',
+ 'verbose_name_plural': 'Heritage and environmental protection types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='heritage_environmental_protections',
+ field=models.ManyToManyField(blank=True, to='archaeological_operations.heritageandenvironmentalprotectiontype', verbose_name='Heritage and environmental protections'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='heritage_interests',
+ field=models.ManyToManyField(blank=True, to='archaeological_operations.heritageinteresttype', verbose_name='Heritage interests'),
+ ),
+ ]
diff --git a/archaeological_operations/migrations/0127_data_migration_current_states.py b/archaeological_operations/migrations/0127_data_migration_current_states.py
new file mode 100644
index 000000000..fe1aa5c25
--- /dev/null
+++ b/archaeological_operations/migrations/0127_data_migration_current_states.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2026-03-27 12:06
+
+from django.db import migrations
+
+
+def migrate_current_states(apps, __):
+ ArchaeologicalSite = apps.get_model("archaeological_operations", "archaeologicalsite")
+ if not hasattr(ArchaeologicalSite, "current_status"):
+ return
+ q = ArchaeologicalSite.objects.filter(current_status__isnull=False)
+ for site in q.all():
+ site.current_states.add(site.current_status)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0126_archaeologicalsite_heritage_relations_datings'),
+ ]
+
+ operations = [
+ migrations.RunPython(migrate_current_states)
+ ]
diff --git a/archaeological_operations/migrations/0128_add_editors.py b/archaeological_operations/migrations/0128_add_editors.py
new file mode 100644
index 000000000..d26c47f74
--- /dev/null
+++ b/archaeological_operations/migrations/0128_add_editors.py
@@ -0,0 +1,39 @@
+# Generated by Django 4.2.21 on 2026-04-02 06:36
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0276_add_editors'),
+ ('archaeological_operations', '0127_data_migration_current_states'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='administrativeact',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='parcel',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='parcelowner',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AlterField(
+ model_name='archaeologicalsite',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ ]
diff --git a/archaeological_operations/migrations/0129_data_migration.json b/archaeological_operations/migrations/0129_data_migration.json
new file mode 100644
index 000000000..f1e341c88
--- /dev/null
+++ b/archaeological_operations/migrations/0129_data_migration.json
@@ -0,0 +1,396 @@
+[
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Protections et labels patrimoniaux",
+ "txt_idx": "protections-et-labels-patrimoniaux",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 100
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Protections environnementales",
+ "txt_idx": "protections-environnementales",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 300
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Inscrit MH",
+ "txt_idx": "inscrit-mh",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 110
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Class\u00e9 MH",
+ "txt_idx": "classe-mh",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 120
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "P\u00e9rim\u00e8tre MH",
+ "txt_idx": "Perimetre-mh",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 130
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Site Patrimonial remarquable",
+ "txt_idx": "site-patrimonial-remarquable",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 140
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Label ACR ",
+ "txt_idx": "label-acr",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 150
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Label PIR",
+ "txt_idx": "label-pir",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 160
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Jardin remarquable ",
+ "txt_idx": "jardin-remarquable",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 200
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Maison des Illustres ",
+ "txt_idx": "maison-des-illustres",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-et-labels-patrimoniaux"
+ ],
+ "order": 220
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Natura 2000",
+ "txt_idx": "natura-2000",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-environnementales"
+ ],
+ "order": 320
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Site inscrit ",
+ "txt_idx": "site-inscrit",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-environnementales"
+ ],
+ "order": 340
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "Site class\u00e9 ",
+ "txt_idx": "site-classe",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "protections-environnementales"
+ ],
+ "order": 360
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "PVAP",
+ "txt_idx": "pvap",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "site-patrimonial-remarquable"
+ ],
+ "order": 10
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageandenvironmentalprotectiontype",
+ "fields": {
+ "label": "PSMV",
+ "txt_idx": "psmv",
+ "comment": "",
+ "available": true,
+ "parent": [
+ "site-patrimonial-remarquable"
+ ],
+ "order": 20
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageinteresttype",
+ "fields": {
+ "label": "Peu int\u00e9ressant",
+ "txt_idx": "peu-interessant",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 10
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageinteresttype",
+ "fields": {
+ "label": "Int\u00e9ressant",
+ "txt_idx": "interessant",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 20
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageinteresttype",
+ "fields": {
+ "label": "Tr\u00e8s int\u00e9ressant",
+ "txt_idx": "tres-interessant",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 30
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageinteresttype",
+ "fields": {
+ "label": "Remarquable",
+ "txt_idx": "remarquable",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 40
+ }
+ },
+ {
+ "model": "archaeological_operations.heritageinteresttype",
+ "fields": {
+ "label": "\u00c0 prot\u00e9ger",
+ "txt_idx": "a-proteger",
+ "comment": "",
+ "available": true,
+ "parent": null,
+ "order": 50
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Voisin de",
+ "txt_idx": "voisin-de",
+ "comment": "",
+ "available": true,
+ "order": 30,
+ "symmetrical": true,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Relation indirecte",
+ "txt_idx": "relation-indirecte",
+ "comment": "",
+ "available": true,
+ "order": 40,
+ "symmetrical": true,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Parent de",
+ "txt_idx": "parent-de",
+ "comment": "",
+ "available": true,
+ "order": 10,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Enfant de",
+ "txt_idx": "enfant-de",
+ "comment": "",
+ "available": true,
+ "order": 20,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Ant\u00e9rieure \u00e0",
+ "txt_idx": "anterieure-a",
+ "comment": "",
+ "available": true,
+ "order": 50,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Post\u00e9rieure \u00e0",
+ "txt_idx": "posterieure-a",
+ "comment": "",
+ "available": true,
+ "order": 60,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Parent de",
+ "txt_idx": "parent-de",
+ "comment": "",
+ "available": true,
+ "order": 10,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": [
+ "enfant-de"
+ ],
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Enfant de",
+ "txt_idx": "enfant-de",
+ "comment": "",
+ "available": true,
+ "order": 20,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": [
+ "parent-de"
+ ],
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Ant\u00e9rieure \u00e0",
+ "txt_idx": "anterieure-a",
+ "comment": "",
+ "available": true,
+ "order": 50,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": [
+ "posterieure-a"
+ ],
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_operations.siterelationtype",
+ "fields": {
+ "label": "Post\u00e9rieure \u00e0",
+ "txt_idx": "posterieure-a",
+ "comment": "",
+ "available": true,
+ "order": 60,
+ "symmetrical": false,
+ "tiny_label": null,
+ "inverse_relation": [
+ "anterieure-a"
+ ],
+ "logical_relation": null
+ }
+ }
+]
diff --git a/archaeological_operations/migrations/0129_data_migration_heritageinterest_heritageenvprotection_relsite.py b/archaeological_operations/migrations/0129_data_migration_heritageinterest_heritageenvprotection_relsite.py
new file mode 100644
index 000000000..59a884794
--- /dev/null
+++ b/archaeological_operations/migrations/0129_data_migration_heritageinterest_heritageenvprotection_relsite.py
@@ -0,0 +1,24 @@
+# Generated by Django 2.2.24 on 2024-02-10 12:07
+
+import os
+
+from django.db import migrations
+from django.core.management import call_command
+
+
+def load_data(apps, __):
+ QualifiedBiographicalNoteType = apps.get_model("archaeological_operations", "HeritageInterestType")
+ if not QualifiedBiographicalNoteType.objects.count():
+ json_path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1] + ["0129_data_migration.json"])
+ call_command("loaddata", json_path)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0128_add_editors'),
+ ]
+
+ operations = [
+ migrations.RunPython(load_data)
+ ]
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py
index 609f7b229..fc3a8a097 100644
--- a/archaeological_operations/models.py
+++ b/archaeological_operations/models.py
@@ -36,10 +36,15 @@ from django.db.models.signals import m2m_changed, post_save, post_delete, pre_de
from django.forms import ValidationError
from django.urls import reverse, reverse_lazy
from ishtar_common.data_importer import post_importer_action
-from ishtar_common.utils import gettext_lazy as _, pgettext_lazy, get_generated_id
+from ishtar_common.utils import (
+ gettext_lazy as _, pgettext_lazy, get_generated_id,
+ related_historization_changed
+)
from ishtar_common.models import (
Area,
+ Author,
+ BaseDating,
BaseHistorizedItem,
DashboardFormItem,
Document,
@@ -59,6 +64,8 @@ from ishtar_common.models import (
PersonType,
post_delete_record_relation,
post_save_cache,
+ QualifiedBiographicalNote,
+ RecordRelationManager,
RelationItem,
SourceType,
Town,
@@ -209,6 +216,32 @@ post_save.connect(post_save_cache, sender=CulturalAttributionType)
post_delete.connect(post_save_cache, sender=CulturalAttributionType)
+class HeritageInterestType(HierarchicalType):
+ order = models.IntegerField(_("Order"), default=10)
+
+ class Meta:
+ verbose_name = _("Heritage interest type")
+ verbose_name_plural = _("Heritage interest types")
+ ordering = ("order", "label")
+
+
+post_save.connect(post_save_cache, sender=HeritageInterestType)
+post_delete.connect(post_save_cache, sender=HeritageInterestType)
+
+
+class HeritageAndEnvironmentalProtectionType(HierarchicalType):
+ order = models.IntegerField(_("Order"), default=10)
+
+ class Meta:
+ verbose_name = _("Heritage and environmental protection type")
+ verbose_name_plural = _("Heritage and environmental protection types")
+ ordering = ("order", "label")
+
+
+post_save.connect(post_save_cache, sender=HeritageAndEnvironmentalProtectionType)
+post_delete.connect(post_save_cache, sender=HeritageAndEnvironmentalProtectionType)
+
+
class SiteType(OrderedHierarchicalType):
class Meta:
verbose_name = _("Site type")
@@ -393,9 +426,12 @@ class ArchaeologicalSite(
("towns__areas__label", _("Area")),
("towns__areas__parent__label", _("Extended area")),
("discovery_status__label", _("Discovery status")),
- ("current_status__label", _("Current status")),
+ ("current_states__label", _("Current states")),
("nature_of_site__label", _("Nature of site")),
("interpretation_level__label", _("Interpretation level")),
+ ("heritage_interests__label", _("Heritage interest")),
+ ("heritage_environmental_protections__label",
+ _("Heritage and environmental protections")),
("documents__source_type__label", _("Associated document type")),
("last_modified__FILTERyear", _("Modification (year)")),
]
@@ -434,11 +470,13 @@ class ArchaeologicalSite(
"cultural_attributions",
"towns",
"collaborators",
- "types"
+ "types",
+ "heritage_interests",
+ "heritage_environmental_protections"
]
- DATED_FIELDS = BaseHistorizedItem.DATED_FIELDS + ["sinking_date"]
- NUMBER_FIELDS = GeographicTownItem.NUMBER_FIELDS[:]
+ DATED_FIELDS = BaseHistorizedItem.DATED_FIELDS + ["sinking_date", "protection_date"]
+ NUMBER_FIELDS = GeographicTownItem.NUMBER_FIELDS[:] + BaseDating.NUMBER_FIELDS
EXTRA_REQUEST_KEYS = {
"towns_label": "towns",
@@ -448,6 +486,7 @@ class ArchaeologicalSite(
"collaborators__pk": "collaborators__pk", # dynamic_table_documents
"discoverer_id": "discoverer_id", # dynamic_table_documents
"types__label": "types__label",
+ "editors__person_id": "editors__person_id", # dynamic_table_documents
}
# alternative names of fields for searches
@@ -478,9 +517,37 @@ class ArchaeologicalSite(
pgettext_lazy("key for text search", "discovery-status"),
"discovery_status__label__iexact"
),
- "current_status": SearchAltName(
+ "current_states": SearchAltName(
pgettext_lazy("key for text search", "current-status"),
- "current_status__label__iexact"
+ "current_states__label__iexact"
+ ),
+ "actors": SearchAltName(
+ pgettext_lazy("key for text search", "actors"),
+ "actors__cached_label__iexact"
+ ),
+ "collaborators": SearchAltName(
+ pgettext_lazy("key for text search", "collaborators"),
+ "collaborators__cached_label__iexact"
+ ),
+ "protection_id": SearchAltName(
+ pgettext_lazy("key for text search", "protection-id"),
+ "protection_id__iexact"
+ ),
+ "protection_date": SearchAltName(
+ pgettext_lazy("key for text search", "protection-date"),
+ "protection_date"
+ ),
+ "details_on_protection": SearchAltName(
+ pgettext_lazy("key for text search", "protection-details"),
+ "details_on_protection__iexact"
+ ),
+ "heritage_interest": SearchAltName(
+ pgettext_lazy("key for text search", "heritage-interest"),
+ "heritage_interests__label__iexact"
+ ),
+ "heritage_environmental_protections": SearchAltName(
+ pgettext_lazy("key for text search", "heritage-environmental-protection"),
+ "heritage_environmental_protections__label__iexact"
),
"nature_of_site": SearchAltName(
pgettext_lazy("key for text search", "nature"),
@@ -568,6 +635,7 @@ class ArchaeologicalSite(
ALT_NAMES.update(DocumentItem.ALT_NAMES)
ALT_NAMES.update(GeoItem.ALT_NAMES)
ALT_NAMES.update(Imported.ALT_NAMES)
+ ALT_NAMES.update(BaseDating.ALT_NAMES)
DEFAULT_SEARCH_FORM = ("archaeological_operations.forms", "SiteSelect")
@@ -580,13 +648,19 @@ class ArchaeologicalSite(
RELATIVE_SESSION_NAMES = [
("operation", "operations__pk"),
]
- HISTORICAL_M2M = ["periods", "remains", "towns", "cultural_attributions", "types"]
+ HISTORICAL_M2M = [
+ "datings", "periods", "remains", "towns", "cultural_attributions", "types",
+ "heritage_interests", "heritage_environmental_protections"
+ ]
CACHED_LABELS = [
"cached_label",
"cached_towns_label",
"cached_periods",
"cached_remains",
"cached_types",
+ "cached_current_states",
+ "cached_heritage_interests",
+ "cached_heritage_environmental_protections"
]
DOWN_MODEL_UPDATE = ["context_records"]
@@ -653,17 +727,25 @@ class ArchaeologicalSite(
Town, verbose_name=_("Towns"), related_name="sites", blank=True
)
current_status = models.ForeignKey(
+ SiteCurrentStatusType, verbose_name=_("Current status - deprecated"),
+ on_delete=models.SET_NULL, blank=True, null=True, related_name="sites_deprecated"
+ )
+ current_states = models.ManyToManyField(
SiteCurrentStatusType, verbose_name=_("Current status"),
- on_delete=models.SET_NULL, blank=True, null=True
+ blank=True
)
discovery_status = models.ForeignKey(
SiteDiscoveryStatusType, verbose_name=_("Discovery status"),
on_delete=models.SET_NULL, blank=True, null=True
)
- discoverer = models.ForeignKey(Person, verbose_name=_("Discoverer"), on_delete=models.SET_NULL,
- blank=True, null=True, related_name="site_discovered")
- nature_of_site = models.ForeignKey(NatureOfSiteType, verbose_name=_("Nature of site"),
- on_delete=models.SET_NULL, blank=True, null=True)
+ discoverer = models.ForeignKey(
+ Person, verbose_name=_("Discoverer"), on_delete=models.SET_NULL,
+ blank=True, null=True, related_name="site_discovered"
+ )
+ nature_of_site = models.ForeignKey(
+ NatureOfSiteType, verbose_name=_("Nature of site"),
+ on_delete=models.SET_NULL, blank=True, null=True
+ )
interpretation_level = models.ForeignKey(
InterpretationLevelType, verbose_name=_("Interpretation level"),
on_delete=models.SET_NULL, blank=True, null=True
@@ -684,6 +766,18 @@ class ArchaeologicalSite(
verbose_name=_("Collaborators"),
related_name="site_collaborator",
)
+ # heritage
+ heritage_interests = models.ManyToManyField(
+ HeritageInterestType, blank=True, verbose_name=_("Heritage interests")
+ )
+ protection_id = models.TextField(_("Protection ID"), blank=True, default="")
+ heritage_environmental_protections = models.ManyToManyField(
+ HeritageAndEnvironmentalProtectionType, blank=True,
+ verbose_name=_("Heritage and environmental protections")
+ )
+ details_on_protection = models.TextField(_("Details on protection"), blank=True,
+ default="")
+ protection_date = models.DateField(_("Protection date"), null=True, blank=True)
# underwater
shipwreck_name = models.TextField(_("Shipwreck name"), blank=True, default="")
oceanographic_service_localisation = models.TextField(
@@ -699,6 +793,10 @@ class ArchaeologicalSite(
_("DRASSM number"), max_length=100, null=True, blank=True
)
+ actors = models.ManyToManyField(
+ QualifiedBiographicalNote, related_name="sites", verbose_name=_("Actors"),
+ blank=True
+ )
documents = models.ManyToManyField(
Document, related_name="sites", verbose_name=_("Documents"), blank=True
)
@@ -734,6 +832,24 @@ class ArchaeologicalSite(
default="",
help_text=_("Generated automatically - do not edit"),
)
+ cached_current_states = models.TextField(
+ _("Cached current states label"),
+ blank=True,
+ default="",
+ help_text=_("Generated automatically - do not edit"),
+ )
+ cached_heritage_interests = models.TextField(
+ _("Cached heritage interests label"),
+ blank=True,
+ default="",
+ help_text=_("Generated automatically - do not edit"),
+ )
+ cached_heritage_environmental_protections = models.TextField(
+ _("Cached heritage and environmental protections label"),
+ blank=True,
+ default="",
+ help_text=_("Generated automatically - do not edit"),
+ )
history = HistoricalRecords(bases=[HistoryModel])
@@ -831,6 +947,26 @@ class ArchaeologicalSite(
if can_edit_site and not is_locked:
actions += [
(
+ reverse("site-dating-add", args=[self.pk]),
+ _("Add dating"),
+ "fa fa-plus",
+ _("dating"),
+ "",
+ True,
+ ),
+ ]
+ actions += [
+ (
+ reverse("site-relations-modify", args=[self.pk]),
+ _("Modify sites relations"),
+ "fa fa-retweet",
+ _("sites"),
+ "",
+ True,
+ ),
+ ]
+ actions += [
+ (
reverse("site-operation-relations-modify", args=[self.pk, window_id]),
_("Modify site-operation relations"),
"fa fa-retweet",
@@ -953,22 +1089,23 @@ class ArchaeologicalSite(
return self.towns_label() or "-"
def _generate_cached_remains(self):
- q = self.remains
- if not self.remains.exists():
- return "-"
- return " & ".join(list(q.values_list("label", flat=True)))
+ return self._regenerate_many_to_many_for_cache("remains")
def _generate_cached_periods(self):
- q = self.periods
- if not self.periods.exists():
- return "-"
- return " & ".join(list(q.values_list("label", flat=True)))
+ return self._regenerate_many_to_many_for_cache("periods")
def _generate_cached_types(self):
- q = self.types
- if not self.types.exists():
- return "-"
- return " & ".join(list(q.values_list("label", flat=True)))
+ return self._regenerate_many_to_many_for_cache("types")
+
+ def _generate_cached_current_states(self):
+ return self._regenerate_many_to_many_for_cache("current_states")
+
+ def _generate_cached_heritage_interests(self):
+ return self._regenerate_many_to_many_for_cache("heritage_interests")
+
+ def _generate_cached_heritage_environmental_protections(self):
+ return self._regenerate_many_to_many_for_cache(
+ "heritage_environmental_protections")
def natural_key(self):
return (self.reference,)
@@ -1067,10 +1204,66 @@ m2m_changed.connect(geodata_attached_changed, sender=ArchaeologicalSite.geodata.
m2m_changed.connect(geotown_attached_changed, sender=ArchaeologicalSite.towns.through)
+class SiteDating(BaseDating):
+ SERIALIZE_EXCLUDE = ["site"]
+ CURRENT_MODEL = ArchaeologicalSite
+ CURRENT_MODEL_ATTR = "site"
+
+ site = models.ForeignKey(
+ ArchaeologicalSite,
+ verbose_name=_("Site"),
+ related_name="datings",
+ on_delete=models.CASCADE,
+ )
+
+ class Meta:
+ verbose_name = _("Site dating")
+ verbose_name_plural = _("Site datings")
+
+
for attr in ArchaeologicalSite.HISTORICAL_M2M:
- m2m_changed.connect(
- m2m_historization_changed, sender=getattr(ArchaeologicalSite, attr).through
+ if attr == "datings":
+ model = SiteDating
+ post_save.connect(related_historization_changed, sender=SiteDating)
+ post_delete.connect(related_historization_changed, sender=SiteDating)
+ else:
+ m2m_changed.connect(
+ m2m_historization_changed, sender=getattr(ArchaeologicalSite, attr).through
+ )
+
+
+class SiteRelationType(GeneralRelationType):
+ class Meta:
+ verbose_name = _("Site relation type")
+ verbose_name_plural = _("Site relation types")
+ ordering = ("order", "label")
+
+
+class SiteRecordRelations(GeneralRecordRelations):
+ MAIN_ATTR = "left_record"
+ left_record = models.ForeignKey(
+ ArchaeologicalSite, related_name="right_relations", on_delete=models.CASCADE
+ )
+ right_record = models.ForeignKey(
+ ArchaeologicalSite, related_name="left_relations", on_delete=models.CASCADE
)
+ relation_type = models.ForeignKey(SiteRelationType, on_delete=models.PROTECT)
+ objects = RecordRelationManager()
+
+ class Meta:
+ verbose_name = _("Site record relation")
+ verbose_name_plural = _("Site record relations")
+ ordering = (
+ "left_record__cached_label",
+ "relation_type",
+ "right_record__cached_label",
+ )
+ permissions = [
+ ("view_siterelation", "Can view all Site relations"),
+ ]
+
+
+post_delete.connect(post_delete_record_relation, sender=SiteRecordRelations)
def get_values_town_related(item, prefix, values, filtr=None):
@@ -1271,6 +1464,7 @@ class Operation(
"collaborators__pk": "collaborators__pk", # dynamic_table_documents
"cira_rapporteur__pk": "cira_rapporteur__pk", # dynamic_table_documents
"operator__pk": "operator__pk", # dynamic_table_documents
+ "editors__person_id": "editors__person_id", # dynamic_table_documents
}
COL_LABELS = {
@@ -2810,15 +3004,6 @@ class RelationType(GeneralRelationType):
ordering = ("order", "label")
-class OperationRecordRelationManager(models.Manager):
- def get_by_natural_key(self, left_record, right_record, relation_type):
- return self.get(
- left_record__uuid=left_record,
- right_record__uuid=right_record,
- relation_type__txt_idx=relation_type,
- )
-
-
class RecordRelations(GeneralRecordRelations):
MAIN_ATTR = "left_record"
left_record = models.ForeignKey(
@@ -2828,7 +3013,7 @@ class RecordRelations(GeneralRecordRelations):
Operation, related_name="left_relations", on_delete=models.CASCADE
)
relation_type = models.ForeignKey(RelationType, on_delete=models.PROTECT)
- objects = OperationRecordRelationManager()
+ objects = RecordRelationManager()
class Meta:
verbose_name = _("Operation record relation")
@@ -2842,13 +3027,6 @@ class RecordRelations(GeneralRecordRelations):
("view_operationrelation", "Can view all Operation relations"),
]
- def natural_key(self):
- return (
- self.left_record.uuid,
- self.right_record.uuid,
- self.relation_type.txt_idx,
- )
-
post_delete.connect(post_delete_record_relation, sender=RecordRelations)
@@ -3004,6 +3182,7 @@ class AdministrativeAct(DocumentItem, BaseHistorizedItem, OwnPerms, ValueGetter,
),
"signature_date": "signature_date",
"year": "signature_date__year",
+ "editors__person_id": "editors__person_id", # dynamic_table_documents
}
REVERSED_BOOL_FIELDS = [
"index__isnull",
diff --git a/archaeological_operations/templates/ishtar/sheet_operation.html b/archaeological_operations/templates/ishtar/sheet_operation.html
index e290ce319..fced5457a 100644
--- a/archaeological_operations/templates/ishtar/sheet_operation.html
+++ b/archaeological_operations/templates/ishtar/sheet_operation.html
@@ -410,28 +410,9 @@
{% if display_relations %}
<div class="tab-pane fade" id="{{window_id}}-relations"
role="tabpanel" aria-labelledby="{{window_id}}-relations-tab">
-
- {% if item.right_relations.count and not item.right_relations_not_available %}
- <h3>{% trans "Relations"%}</h3>
- {% for rel in item.right_relations.all %}
- {% ifchanged rel.relation_type %}
- {% if forloop.counter0 %}</div>{% endif %}
- <h4>{{rel.relation_type}}</h4>
- <div class="row">{% endifchanged %}
- <div class="col-12">
- <a href="#" onclick="load_window('/show-operation/{{rel.right_record.pk|unlocalize}}/');" class="display_details">
- <i class="fa fa-info-circle" aria-hidden="true"></i>
- </a> {{rel.right_record}}
- </div>
- {% if forloop.last %}
- </div>{% endif %}
- {% endfor %}
- {% else %}
- <div class="alert alert-info" role="alert">
- <i class="fa fa-info-circle" aria-hidden="true"></i> &nbsp;
- {% trans "No relations" %}
- </div>
- {% endif %}
+ {% with relation_url="/show-operation/" %}
+ {% include "ishtar/blocks/sheet_relations.html" %}
+ {% endwith %}
</div>
{% endif %}
diff --git a/archaeological_operations/templates/ishtar/sheet_site.html b/archaeological_operations/templates/ishtar/sheet_site.html
index 2deb54683..b153e5795 100644
--- a/archaeological_operations/templates/ishtar/sheet_site.html
+++ b/archaeological_operations/templates/ishtar/sheet_site.html
@@ -1,5 +1,5 @@
{% extends "ishtar/sheet.html" %}
-{% load i18n ishtar_helpers window_tables window_header window_ope_tables window_field from_dict %}
+{% load i18n l10n ishtar_helpers window_tables window_header window_ope_tables window_field from_dict %}
{% block head_title %}<strong>{{SITE_LABEL}}</strong> - {{item.cached_label}}{% endblock %}
@@ -8,6 +8,12 @@
{% endblock %}
{% block content %}
+{# trick to set to null non existing variable #}
+{% with permission_view_own_archaeologicalsite=permission_view_own_archaeologicalsite %}
+
+{% with display_relations=item|safe_or:"right_relations.count|left_relations.count"|safe_and_not:"right_relations_not_available"|safe_and_not:"left_relations_not_available" %}
+{% with can_change=permission_change_own_archaeologicalsite|or_:permission_change_archaeologicalsite %}
+{% with dating_list=item|m2m_listing:"datings" %}
{% with permission_change_own_geovectordata=permission_change_own_geovectordata %}
{% with permission_change_geovectordata=permission_change_geovectordata %}
@@ -40,16 +46,9 @@
<h3>{% trans "General"%}</h3>
<div class="row">
- {% field_flex "Other reference" item.other_reference %}
- {% field_flex_multiple_obj _("Types") item 'types' %}
- {% field_flex _("Discovery status") item.discovery_status %}
- {% field_flex _("Current status") item.current_status %}
- {% field_flex _("Nature of site") item.nature_of_site %}
- {% field_flex _("Interpretation level") item.interpretation_level %}
- {% field_flex_multiple_obj "Periods" item 'periods' %}
- {% field_flex_multiple_obj "Remains" item 'remains' %}
- {% field_flex_multiple_obj "Cultural attributions" item 'cultural_attributions' %}
- {% field_flex_detail _("Discoverer") item.discoverer %}
+ {% field_flex _("Other reference") item.other_reference %}
+ {% field_flex_multiple_obj _("Heritage interest") item 'heritage_interests' %}
+ {% field_flex_detail_multiple _("Actors") item.actors %}
{% field_flex_detail_multiple _("Collaborators") item.collaborators %}
{% if item.description == item.public_description %}
{% field_flex_full _("Description/Public description") item.description "<pre>" "</pre>" %}
@@ -57,8 +56,38 @@
{% field_flex_full _("Description") item.description "<pre>" "</pre>" %}
{% field_flex_full _("Public description") item.public_description "<pre>" "</pre>" %}
{% endif %}
- {% field_flex_full "Comment" item.comment "<pre>" "</pre>" %}
+ {% field_flex_full _("Comment") item.comment "<pre>" "</pre>" %}
+</div>
+{% if item.cached_periods or dating_list %}
+ <h3>{% trans "Periods / Datings" %}</h3>
+ {% field_flex_multiple_obj _("Periods") item 'periods' %}
+ {% with url_dating="site-dating" %}
+ {% include "ishtar/blocks/sheet_dating_list.html" %}
+ {% endwith %}
+{% endif %}
+{% if item.cached_types or item.nature_of_site or item.interpretation_level or item.discovery_status or item.cached_current_states or item.cached_periods or item.cached_remains or item.cultural_attributions.count or item.discoverer %}
+<h3>{% trans "Scientific" %}</h3>
+<div class="row">
+ {% field_flex_multiple_obj _("Types") item 'types' %}
+ {% field_flex _("Nature of site") item.nature_of_site %}
+ {% field_flex _("Interpretation level") item.interpretation_level %}
+ {% field_flex _("Discovery status") item.discovery_status %}
+ {% field_flex_multiple_obj _("Current states") item 'current_states' %}
+ {% field_flex_multiple_obj _("Periods") item 'periods' %}
+ {% field_flex_multiple_obj _("Remains") item 'remains' %}
+ {% field_flex_multiple_obj _("Cultural attributions") item 'cultural_attributions' %}
+ {% field_flex_detail _("Discoverer") item.discoverer %}
</div>
+{% endif %}
+{% if item.cached_heritage_environmental_protections or item.details_on_protection or item.protection_id or item.protection_date %}
+<h3>{% trans "Protection" %}</h3>
+<div class="row">
+ {% field_flex _("Protection ID") item.protection_id %}
+ {% field_flex _("Protection date") item.protection_date|date:"DATE_FORMAT" %}
+ {% field_flex_multiple_obj _("Heritage and environmental protections") item 'heritage_environmental_protections' %}
+ {% field_flex_full _("Details on protection") item.details_on_protection "<pre>" "</pre>" %}
+</div>
+{% endif %}
{% if item.affmar_number or item.drassm_number or item.oceanographic_service_localisation or item.shipwreck_code or item.sinking_date or item.discovery_area or item.shipwreck_name %}
<h3>{% trans "Underwater"%}</h3>
@@ -122,13 +151,19 @@
{% endif %}
{% endif %}
+{% if display_relations %}
+ {% with relation_url="/show-site/" %}
+ {% include "ishtar/blocks/sheet_relations.html" %}
+ {% endwith %}
+{% endif %}
+
{% if not is_external and SHOW_GEO %}
<h3>{% trans "Geographic data" %}</h3>
{% with geo_item=item %}{% include "ishtar/blocks/sheet_geographic.html" %}{% endwith %}
{% endif %}
{% if not is_external %}
-{% if item.history_creator or item.last_edition_date or item.created %}
+{% if item.history_creator or item.last_edition_date or item.created or item.editors.count %}
<h3>{% trans "Sheet"%}</h3>
<div class="row">
{% include "ishtar/blocks/sheet_creation_section.html" %}
@@ -136,5 +171,5 @@
{% endif %}
{% endif %}
-{% endwith %} {% endwith %} {% endwith %}
+{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}{% endwith %}
{% endblock %}
diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py
index b239b789d..136f710f1 100644
--- a/archaeological_operations/tests.py
+++ b/archaeological_operations/tests.py
@@ -127,7 +127,7 @@ class FileInit:
def create_file(self):
self.extra_models, self.model_list = {}, []
self.user, created = User.objects.get_or_create(
- username="username", is_superuser=True
+ username="username", is_superuser=True, is_staff=True
)
self.user.set_password("tralala")
self.user.save()
@@ -298,7 +298,7 @@ class ImportTest(BaseImportTest):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
def init_parcel_import(self):
self.init_ope()
@@ -327,7 +327,7 @@ class ImportTest(BaseImportTest):
importer, form = self.init_parcel_import()
impt = form.save(self.ishtar_user)
impt.initialize()
- impt.importation()
+ impt.start_import()
def init_context_record_import(self):
self.init_parcel()
@@ -364,7 +364,7 @@ class ImportTest(BaseImportTest):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_cr_targetkey(impt)
- impt.importation()
+ impt.start_import()
class ImportOperationTest(ImportTest, TestCase):
@@ -385,14 +385,14 @@ class ImportOperationTest(ImportTest, TestCase):
self.assertTrue(TargetKey.objects.count() > target_key_nb)
# first try to import
- impt.importation()
+ impt.start_import()
current_ope_nb = models.Operation.objects.count()
# no new operation imported because of a missing connection for
# operation_type value
self.assertEqual(current_ope_nb, first_ope_nb)
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
# new operations have now been imported
current_ope_nb = models.Operation.objects.count()
self.assertEqual(current_ope_nb, first_ope_nb + 2)
@@ -413,7 +413,7 @@ class ImportOperationTest(ImportTest, TestCase):
# a second importation will be not possible: no two same patriarche
# code
- impt.importation()
+ impt.start_import()
self.assertEqual(last_ope, models.Operation.objects.order_by("-pk").all()[0])
def test_import_bad_encoding(self):
@@ -428,7 +428,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
# new operations have now been imported
current_ope_nb = models.Operation.objects.count()
self.assertEqual(current_ope_nb, first_ope_nb + 2)
@@ -457,7 +457,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
current_ope_nb = models.Operation.objects.count()
self.assertEqual(current_ope_nb, first_ope_nb + 2)
@@ -481,7 +481,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
self.assertEqual(
models.Operation.objects.filter(
code_patriarche="4201",
@@ -523,7 +523,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
self.assertEqual(
models.Operation.objects.filter(
code_patriarche="4201",
@@ -542,7 +542,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
self.assertEqual(
models.Operation.objects.filter(code_patriarche="oa-4201").count(), 1
)
@@ -559,7 +559,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
self.assertEqual(
models.Operation.objects.filter(code_patriarche="oa-42014201").count(), 1
)
@@ -595,7 +595,7 @@ class ImportOperationTest(ImportTest, TestCase):
tg.associated_import = other_imp
tg.save()
- impt.importation()
+ impt.start_import()
current_ope_nb = models.Operation.objects.count()
# no new operation
self.assertEqual(current_ope_nb, init_ope_number)
@@ -615,7 +615,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
self.assertEqual(len(impt.errors), 2)
self.assertTrue("cody" in impt.errors[0]["error"])
self.assertTrue(
@@ -632,7 +632,7 @@ class ImportOperationTest(ImportTest, TestCase):
# no model defined in created_models: normal import
init_ope_number = models.Operation.objects.count()
- impt.importation()
+ impt.start_import()
current_ope_nb = models.Operation.objects.count()
self.assertEqual(current_ope_nb, init_ope_number + 2)
@@ -650,7 +650,7 @@ class ImportOperationTest(ImportTest, TestCase):
self.init_ope_targetkey(imp=impt)
# no imports
- impt.importation()
+ impt.start_import()
current_ope_nb = models.Operation.objects.count()
self.assertEqual(current_ope_nb, init_ope_number)
@@ -667,7 +667,7 @@ class ImportOperationTest(ImportTest, TestCase):
self.init_ope_targetkey(imp=impt)
# import of operations
- impt.importation()
+ impt.start_import()
current_ope_nb = models.Operation.objects.count()
self.assertEqual(current_ope_nb, init_ope_number + 2)
@@ -703,7 +703,7 @@ class ImportOperationTest(ImportTest, TestCase):
mcc_parcel, form = self.init_parcel_import()
impt = form.save(self.ishtar_user)
impt.initialize()
- impt.importation()
+ impt.start_import()
# new parcels has now been imported
current_nb = models.Parcel.objects.count()
self.assertEqual(current_nb, old_nb + 3)
@@ -752,7 +752,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
ope1 = models.Operation.objects.get(code_patriarche="4200")
self.assertEqual(ope1.data, {"autre_refs": {"arbitraire": 789}})
ope2 = models.Operation.objects.get(code_patriarche="4201")
@@ -767,7 +767,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
ope1 = models.Operation.objects.get(code_patriarche="4200")
self.assertEqual(ope1.data, {"autre_refs": {"arbitraire": 789}, "autre": 666})
ope2 = models.Operation.objects.get(code_patriarche="4201")
@@ -790,7 +790,7 @@ class ImportOperationTest(ImportTest, TestCase):
impt = form.save(self.ishtar_user)
impt.initialize()
self.init_ope_targetkey(imp=impt)
- impt.importation()
+ impt.start_import()
ope1 = models.Operation.objects.get(code_patriarche="4200")
self.assertEqual(ope1.data, {"code_insee": "45123"})
ope2 = models.Operation.objects.get(code_patriarche="4201")
@@ -858,7 +858,7 @@ class ImportDocumentTest(ImportTest, TestCase):
importer, form = self.init_doc_import()
self.assertTrue(form.is_valid())
impt = form.save(self.ishtar_user)
- impt.importation()
+ impt.start_import()
self.assertEqual(doc_nb + 2, Document.objects.count())
self.assertEqual(current_index + 2, Document.get_next_index() - 1)
diff --git a/archaeological_operations/urls.py b/archaeological_operations/urls.py
index 3082418d9..31930fbd9 100644
--- a/archaeological_operations/urls.py
+++ b/archaeological_operations/urls.py
@@ -352,6 +352,14 @@ urlpatterns = [
name="operation-site-relations-modify",
),
path(
+ "site-relations-modify/<int:pk>)/",
+ check_permissions(
+ ["archaeological_operations.change_archaeologicalsite",
+ "archaeological_operations.change_own_archaeologicalsite"]
+ )(views.site_modify_relations),
+ name="site-relations-modify",
+ ),
+ path(
"site-operation-relations-modify/<int:pk>/",
check_permissions(
["archaeological_operations.change_operation",
@@ -424,6 +432,33 @@ urlpatterns = [
name="site-qa-link",
kwargs={"model": models.ArchaeologicalSite, "url": "site-qa-link"},
),
+ path(
+ "site-dating/<int:pk>/",
+ check_permissions(
+ ["archaeological_sites.change_archaeologicalsite",
+ "archaeological_sites.change_own_archaeologicalsite"])(
+ views.site_dating_add
+ ),
+ name="site-dating-add",
+ ),
+ path(
+ "site-dating/<int:pk>/<int:dating_pk>/",
+ check_permissions(
+ ["archaeological_sites.change_archaeologicalsite",
+ "archaeological_sites.change_own_archaeologicalsite"])(
+ views.site_dating_modify
+ ),
+ name="site-dating-modify",
+ ),
+ path(
+ "site-dating-delete/<int:dating_pk>/",
+ check_permissions(
+ ["archaeological_sites.change_archaeologicalsite",
+ "archaeological_sites.change_own_archaeologicalsite"])(
+ views.site_dating_delete
+ ),
+ name="site-dating-delete",
+ ),
re_path(
r"^site-qa-bulk-update/(?P<pks>[0-9-]+)?/$",
check_permissions(
diff --git a/archaeological_operations/views.py b/archaeological_operations/views.py
index ecba76e05..f2117c747 100644
--- a/archaeological_operations/views.py
+++ b/archaeological_operations/views.py
@@ -43,7 +43,10 @@ from ishtar_common.models import (
)
from archaeological_context_records.models import ContextRecord
from ishtar_common.utils import check_permissions_condition
+
from ishtar_common.views import (
+ get_dating_delete,
+ get_dating_form,
gen_generate_doc,
QAItemEditForm,
QABaseLockView,
@@ -590,6 +593,12 @@ operation_modify_relations = get_relation_modify(
)
+site_modify_relations = get_relation_modify(
+ models.ArchaeologicalSite, models.SiteRecordRelations,
+ forms.SiteRecordRelationsFormSet, "site-relations-modify"
+)
+
+
RELATION_LIMIT = 50
@@ -662,8 +671,27 @@ def operation_site_modify(model, related_model, related_key, formset_class, url_
return view
-operation_site_modify_relations = operation_site_modify(models.Operation, models.ArchaeologicalSite, "archaeological_sites", forms.OpeSiteRelationsFormSet, "operation-site-relations-modify")
-site_operation_modify_relations = operation_site_modify(models.ArchaeologicalSite, models.Operation, "operations", forms.SiteOpeRelationsFormSet, "site-operation-relations-modify")
+operation_site_modify_relations = operation_site_modify(
+ models.Operation, models.ArchaeologicalSite, "archaeological_sites",
+ forms.OpeSiteRelationsFormSet, "operation-site-relations-modify")
+site_operation_modify_relations = operation_site_modify(
+ models.ArchaeologicalSite, models.Operation, "operations",
+ forms.SiteOpeRelationsFormSet, "site-operation-relations-modify")
+
+
+site_dating_add = get_dating_form(
+ models.ArchaeologicalSite, models.SiteDating, "site-dating-add"
+)
+
+
+site_dating_modify = get_dating_form(
+ models.ArchaeologicalSite, models.SiteDating, "site-dating-modify"
+)
+
+
+site_dating_delete = get_dating_delete(
+ models.ArchaeologicalSite, models.SiteDating, "site-dating-delete"
+)
# archaeological sites