summaryrefslogtreecommitdiff
path: root/archaeological_finds
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_finds')
-rw-r--r--archaeological_finds/admin.py20
-rw-r--r--archaeological_finds/forms.py159
-rw-r--r--archaeological_finds/migrations/0149_add_editors.py64
-rw-r--r--archaeological_finds/migrations/0150_findrelationtype_findrecordrelations.py58
-rw-r--r--archaeological_finds/migrations/0151_data_migration.json30
-rw-r--r--archaeological_finds/migrations/0151_data_migration_find_relation_type.py24
-rw-r--r--archaeological_finds/migrations/0152_find_actors_heritage_museum_fields.py154
-rw-r--r--archaeological_finds/migrations/0153_data_migration.json46
-rw-r--r--archaeological_finds/migrations/0153_data_migration_find_listed_building_icono_patterns.py24
-rw-r--r--archaeological_finds/models.py8
-rw-r--r--archaeological_finds/models_finds.py279
-rw-r--r--archaeological_finds/templates/ishtar/sheet_find.html38
-rw-r--r--archaeological_finds/templates/ishtar/sheet_find_treatments.html3
-rw-r--r--archaeological_finds/templates/ishtar/sheet_museum_find.html38
-rw-r--r--archaeological_finds/tests.py20
-rw-r--r--archaeological_finds/urls.py13
-rw-r--r--archaeological_finds/views.py14
17 files changed, 922 insertions, 70 deletions
diff --git a/archaeological_finds/admin.py b/archaeological_finds/admin.py
index 555ef8821..c764cf66d 100644
--- a/archaeological_finds/admin.py
+++ b/archaeological_finds/admin.py
@@ -20,7 +20,9 @@
from django.contrib import admin
from ishtar_common.apps import admin_site
-from ishtar_common.admin import HistorizedObjectAdmin, GeneralTypeAdmin, MainGeoDataItem
+from ishtar_common.admin import GeneralTypeAdmin, HistorizedObjectAdmin, MainGeoDataItem
+from archaeological_operations.admin import RecordRelationsAdmin, RelationTypeAdmin
+
from . import models
@@ -242,6 +244,10 @@ class TreatmentStatusAdmin(GeneralTypeAdmin):
extra_list_display = ["order"]
+admin_site.register(models.FindRelationType, RelationTypeAdmin)
+admin_site.register(models.FindRecordRelations, RecordRelationsAdmin)
+
+
@admin.register(models.Exhibition, site=admin_site)
class ExhibitionAdmin(HistorizedObjectAdmin):
list_display = ('name', 'year', 'reference', 'exhibition_type')
@@ -269,12 +275,12 @@ class RecommendedTreatmentTypeAdmin(GeneralTypeAdmin):
general_models = [
models.AlterationCauseType, models.AlterationType, models.BatchType,
- models.CollectionEntryModeType, models.IntegrityType, models.InventoryConformity,
- models.InventoryMarkingPresence, models.MarkingType, models.MaterialTypeQualityType,
- models.MuseumCollection, models.ObjectTypeQualityType, models.OriginalReproduction,
- models.RemarkabilityType, models.TreatmentEmergencyType, models.DiscoveryMethod,
- models.ExhibitionType, models.OwnershipStatus, models.FollowUpActionType,
- models.StatementConditionType
+ models.CollectionEntryModeType, models.IconographicPatternType, models.IntegrityType,
+ models.InventoryConformity, models.InventoryMarkingPresence, models.ListedBuildingProtectionNature,
+ models.MarkingType, models.MaterialTypeQualityType, models.MuseumCollection,
+ models.ObjectTypeQualityType, models.OriginalReproduction, models.RemarkabilityType,
+ models.TreatmentEmergencyType, models.DiscoveryMethod, models.ExhibitionType,
+ models.OwnershipStatus, models.FollowUpActionType, models.StatementConditionType
]
for model in general_models:
diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py
index d4f954d59..6ea406b9e 100644
--- a/archaeological_finds/forms.py
+++ b/archaeological_finds/forms.py
@@ -35,7 +35,6 @@ from ishtar_common.utils import gettext_lazy as _
from . import models
from archaeological_operations.models import CulturalAttributionType, RemainType
from archaeological_context_records.models import (
- Dating,
DatingType,
DatingQuality,
ContextRecord,
@@ -92,10 +91,15 @@ from ishtar_common.forms import (
GeoItemSelect,
)
from ishtar_common.forms_common import get_town_field
-from archaeological_context_records.forms import PeriodSelect
+from archaeological_operations.forms import (
+ RecordRelationsForm,
+ RecordRelationsFormSetBase,
+)
+from archaeological_context_records.forms import DatingSelect
from ishtar_common.models import (
Area,
+ Author,
BiographicalNote,
get_current_profile,
IshtarUser,
@@ -103,6 +107,7 @@ from ishtar_common.models import (
Organization,
Person,
person_type_pks_lazy,
+ QualifiedBiographicalNote,
valid_id,
valid_ids,
)
@@ -202,8 +207,6 @@ class RecordFormSelection(CustomForm, forms.Form):
class MuseumForm:
- MARK_FIELD = "mark"
-
def update_museum_fields(self):
"""
Overload fields labels for museum
@@ -222,10 +225,6 @@ class MuseumForm:
fields["museum_id"] = self.fields.pop("museum_id")
fields.update(self.fields)
self.fields = fields
- # update label of mark field
- if self.MARK_FIELD not in self.fields:
- return
- self.fields[self.MARK_FIELD].label = _("Marking details")
class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
@@ -238,6 +237,8 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
form_admin_name = _("Simple find - 020 - General")
form_slug = "find-020-simplegeneral"
base_models = [
+ "actor",
+ "editor",
"object_type",
"period",
"material_type",
@@ -246,6 +247,7 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"functional_area",
"technical_area",
"technical_processe",
+ "iconographic_pattern",
"museum_former_collection",
"museum_donor",
"museum_inventory_marking_presence",
@@ -253,6 +255,8 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"museum_collection",
]
associated_models = {
+ "actor": QualifiedBiographicalNote,
+ "editor": Author,
"material_type": models.MaterialType,
"cultural_attribution": CulturalAttributionType,
"object_type": models.ObjectType,
@@ -264,6 +268,8 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"material_type_quality": models.MaterialTypeQualityType,
"object_type_quality": models.ObjectTypeQualityType,
"checked_type": models.CheckedType,
+ "iconographic_pattern": models.IconographicPatternType,
+ "listed_building_protection_nature": models.ListedBuildingProtectionNature,
"museum_collection_entry_mode": models.CollectionEntryModeType,
"museum_inventory_marking_presence": models.InventoryMarkingPresence,
"museum_marking_type": models.MarkingType,
@@ -283,11 +289,13 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"denomination",
"previous_id",
"laboratory_id",
+ "title",
"museum_id",
"museum_id_comment",
"seal_number",
"museum_inventory_marking_presence",
"museum_marking_type",
+ "mark_text",
"mark",
"museum_owner_institution",
"museum_assigned_institution",
@@ -308,6 +316,8 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"museum_original_repro",
"museum_allocation_date",
"museum_purchase_price",
+ "iconographic_pattern",
+ "iconography_notes",
"description",
"public_description",
"get_first_base_find__discovery_method",
@@ -333,6 +343,7 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"cultural_attribution",
"period",
"dating_comment",
+ "actor",
"length",
"width",
"height",
@@ -349,6 +360,8 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
"check_date",
]
extra_form_modals = [
+ "author",
+ "qualifiedbiographicalnote",
"biographicalnote",
"person",
"organization",
@@ -388,14 +401,14 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
museum_id_prefix = forms.CharField(label=_("Museum ID prefix"), required=False)
museum_id = forms.CharField(label=_("Museum inventory number"), required=False)
museum_id_suffix = forms.CharField(label=_("Museum ID suffix"), required=False)
- museum_id_comment = forms.CharField(label=_("Comment on museum ID"), widget=forms.Textarea, required=False)
+ museum_id_comment = forms.CharField(label=_("Comment on museum ID"),
+ widget=forms.Textarea, required=False)
HEADERS["label"] = FormHeader(_("Identification"))
- label = forms.CharField(
- label=_("Free ID"), validators=[validators.MaxLengthValidator(60)]
- )
+ label = forms.CharField(label=_("Free ID"))
denomination = forms.CharField(label=_("Denomination"), required=False)
previous_id = forms.CharField(label=_("Previous ID"), required=False)
laboratory_id = forms.CharField(label=_("Laboratory ID"), required=False)
+ title = forms.CharField(label=_("Title"), required=False)
seal_number = forms.CharField(label=_("Seal number"), required=False)
museum_inventory_marking_presence = widgets.Select2MultipleField(
label=_("Presence of inventory marking"), required=False
@@ -403,7 +416,8 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
museum_marking_type = widgets.Select2MultipleField(
label=_("Type of marking"), required=False
)
- mark = forms.CharField(label=_("Mark"), required=False)
+ mark_text = forms.CharField(label=_("Transcription of the marking"), required=False)
+ mark = forms.CharField(label=_("Marking details"), required=False)
HEADERS["ownership_status"] = FormHeader(_("Ownership"))
ownership_status = forms.ChoiceField(
label=_("Ownership status"), required=False, choices=[]
@@ -451,6 +465,12 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
)
museum_allocation_date = DateField(label=_("Date of museum allocation"), required=False)
museum_purchase_price = forms.CharField(label=_("Purchase price"), required=False)
+ iconographic_pattern = widgets.Select2MultipleField(
+ label=_("Iconographic patterns"), required=False,
+ )
+ iconography_notes = forms.CharField(
+ label=_("Iconography notes"), widget=forms.Textarea, required=False
+ )
HEADERS["museum_inventory_transcript"] = FormHeader(_("Description"))
museum_inventory_transcript = forms.CharField(
@@ -528,6 +548,9 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
dating_comment = forms.CharField(
label=_("Comment on datings"), required=False, widget=forms.Textarea
)
+ actor = widgets.Select2MultipleField(
+ model=QualifiedBiographicalNote, label=_("Actors"), required=False,
+ remote=True, new=True, remote_filter='qualification_type__F-A')
HEADERS["length"] = FormHeader(_("Dimensions"))
length = FloatField(
@@ -573,9 +596,26 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
label=_("Dimensions comment"), required=False, widget=forms.Textarea
)
+ HEADERS["listed_building_id"] = FormHeader(_("Listed buildings"))
+ listed_building_id = forms.CharField(label=_("Listed building ID"), required=False)
+ listed_building_protection_nature = forms.ChoiceField(
+ label=_("Nature of listed buildings protection"), choices=[],
+ required=False
+ )
+ listed_building_date = DateField(
+ label=_("Date of listing as a listed building"),
+ required=False)
+ listed_building_notes = forms.CharField(
+ label=_("Notes on listed building"), required=False,
+ widget=forms.Textarea)
+
HEADERS["checked_type"] = FormHeader(_("Sheet"))
checked_type = forms.ChoiceField(label=_("Check"), required=False)
check_date = DateField(initial=get_now, label=_("Check date"))
+ editor = widgets.Select2MultipleField(
+ label=_("Editors"), required=False,
+ model=Author, remote=True, new=True
+ )
TYPES = [
FieldType(
@@ -600,8 +640,13 @@ class BasicFindForm(MuseumForm, CustomForm, ManageOldType):
FieldType("technical_processe", models.TechnicalProcessType, is_multiple=True),
FieldType("communicabilitie", models.CommunicabilityType, is_multiple=True),
FieldType("checked_type", models.CheckedType, is_multiple=True),
+ FieldType("iconographic_pattern", models.IconographicPatternType,
+ is_multiple=True),
+ FieldType("listed_building_protection_nature",
+ models.ListedBuildingProtectionNature),
FieldType("museum_collection_entry_mode", models.CollectionEntryModeType),
- FieldType("museum_inventory_marking_presence", models.InventoryMarkingPresence, is_multiple=True),
+ FieldType("museum_inventory_marking_presence",
+ models.InventoryMarkingPresence, is_multiple=True),
FieldType("museum_marking_type", models.MarkingType, is_multiple=True),
FieldType("museum_collection", models.MuseumCollection),
FieldType("museum_inventory_conformity", models.InventoryConformity),
@@ -663,11 +708,13 @@ class FindForm(BasicFindForm):
"denomination",
"previous_id",
"get_first_base_find__excavation_id",
+ "title",
"laboratory_id",
"museum_id",
"seal_number",
"museum_inventory_marking_presence",
"museum_marking_type",
+ "mark_text",
"mark",
"ownership_status",
"owner",
@@ -687,8 +734,10 @@ class FindForm(BasicFindForm):
"museum_inventory_conformity",
"museum_conformity_comment",
"museum_original_repro",
- "museum_allocation_date",
"museum_purchase_price",
+ "iconographic_pattern",
+ "iconography_notes",
+ "museum_allocation_date",
"museum_inventory_transcript",
"description",
"public_description",
@@ -719,6 +768,7 @@ class FindForm(BasicFindForm):
"comment",
"period",
"dating_comment",
+ "actor",
"length",
"width",
"height",
@@ -731,8 +781,13 @@ class FindForm(BasicFindForm):
"clutter_short_side",
"clutter_height",
"dimensions_comment",
+ "listed_building_id",
+ "listed_building_protection_nature",
+ "listed_building_date",
+ "listed_building_notes",
"checked_type",
"check_date",
+ "editor",
]
HEADERS = BasicFindForm.HEADERS.copy()
@@ -925,6 +980,32 @@ class ResultingFindsForm(CustomForm, ManageOldType):
return self.cleaned_data
+class FindRecordRelationsForm(RecordRelationsForm):
+ current_model = models.FindRelationType
+ current_related_model = models.Find
+ associated_models = {
+ "right_record": models.Find,
+ "relation_type": models.FindRelationType,
+ }
+ ERROR_MISSING = _("You should select a find and a relation type.")
+ ERROR_SAME = _("A find cannot be related to himself.")
+
+ right_record = forms.IntegerField(
+ label=_("Find"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-find'),
+ associated_model=models.Find),
+ validators=[valid_id(models.Find)], required=False)
+
+
+FindRecordRelationsFormSet = formset_factory(
+ FindRecordRelationsForm, can_delete=True, formset=RecordRelationsFormSetBase
+)
+FindRecordRelationsFormSet.form_label = _("Find - Relations")
+FindRecordRelationsFormSet.form_admin_name = _("Find - Relations")
+FindRecordRelationsFormSet.form_slug = "find-recordrelations"
+
+
class QAFindFormMulti(MuseumForm, QAForm):
form_admin_name = _("Find - Quick action - Modify")
form_slug = "find-quickaction-modify"
@@ -1069,7 +1150,7 @@ class QAFindFormMulti(MuseumForm, QAForm):
qa_museum_id_suffix = forms.CharField(label=_("Museum ID suffix"), required=False)
qa_laboratory_id = forms.CharField(label=_("Laboratory ID"), required=False)
qa_seal_number = forms.CharField(label=_("Seal number"), required=False)
- qa_mark = forms.CharField(label=_("Mark"), required=False)
+ qa_mark = forms.CharField(label=_("Marking details"), required=False)
## Ownership
qa_ownership_status = forms.ChoiceField(label=_("Ownership status"),
@@ -1543,6 +1624,10 @@ class PreservationForm(CustomForm, ManageOldType):
widget=widgets.Select2Multiple,
required=False,
)
+ conservatory_states_details = forms.CharField(
+ label=_("Conservatory state details"), required=False,
+ widget=forms.Textarea
+ )
alteration = forms.MultipleChoiceField(
label=_("Alteration"),
choices=[],
@@ -1616,7 +1701,7 @@ class DateForm(ManageOldType, forms.Form):
]
-class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
+class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
_model = models.Find
form_admin_name = _("Find - 001 - Search")
form_slug = "find-001-search"
@@ -1714,6 +1799,9 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
"museum": [
"cache_complete_museum_id",
"museum_id_comment",
+ "museum_id",
+ "museum_id_prefix",
+ "museum_id_suffix",
"museum_owner_institution",
"museum_assigned_institution",
"museum_custodian_institution",
@@ -1746,7 +1834,10 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
)
label = forms.CharField(label=_("Free ID"))
denomination = forms.CharField(label=_("Denomination"))
+ title = forms.CharField(label=_("Title"))
+ museum_id_prefix = forms.CharField(label=_("Museum ID prefix"))
museum_id = forms.CharField(label=_("Museum inventory number"))
+ museum_id_suffix = forms.CharField(label=_("Museum ID suffix"))
cache_complete_museum_id = forms.CharField(label=_("Complete museum ID"))
previous_id = forms.CharField(label=_("Previous ID"))
base_finds__excavation_id = forms.CharField(label=_("Excavation ID"))
@@ -1911,6 +2002,10 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
label=_("Periods"), choices=[], required=False
)
dating_comment = forms.CharField(label=_("Comment on datings"))
+ actors = forms.IntegerField(
+ label=_("Actors"), required=False,
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-qualifiedbiographicalnote')))
length = FloatField(label=_("Length (cm)"), widget=widgets.CentimeterMeterWidget)
width = FloatField(label=_("Width (cm)"), widget=widgets.CentimeterMeterWidget)
@@ -1942,6 +2037,7 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
integrities = forms.ChoiceField(label=_("Integrity"), choices=[])
remarkabilities = forms.ChoiceField(label=_("Remarkability"), choices=[])
conservatory_states = forms.ChoiceField(label=_("Conservatory states"), choices=[])
+ conservatory_states_details = forms.CharField(label=_("Conservatory state details"))
conservatory_comment = forms.CharField(label=_("Conservatory comment"))
alterations = forms.ChoiceField(label=_("Alteration"), choices=[])
alteration_causes = forms.ChoiceField(label=_("Alteration cause"), choices=[])
@@ -1989,13 +2085,22 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
reverse_lazy('autocomplete-biographicalnote'),
associated_model=BiographicalNote),
validators=[valid_id(BiographicalNote)])
+ iconographic_patterns = forms.IntegerField(
+ label=_("Iconographic patterns"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy("autocomplete-iconographicpattern"),
+ associated_model=models.IconographicPatternType,
+ ),
+ )
+ iconography_notes = forms.CharField(label=_("Iconography notes"))
museum_inventory_marking_presence = forms.ChoiceField(
label=_("Museum - Presence of inventory marking"), choices=[]
)
museum_marking_type = forms.ChoiceField(
label=_("Museum - Type of marking"), choices=[]
)
- mark = forms.CharField(label=_("Mark"))
+ mark_text = forms.CharField(label=_("Transcription of the marking"), required=False)
+ mark = forms.CharField(label=_("Marking details"))
museum_collections = forms.ChoiceField(
label=_("Museum - Collection"), choices=[]
)
@@ -2005,19 +2110,29 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
reverse_lazy('autocomplete-biographicalnote'),
associated_model=BiographicalNote),
validators=[valid_id(BiographicalNote)])
- museum_inventory_entry_year= forms.IntegerField(label=_("Museum - Inventory entry year"))
+ museum_inventory_entry_year= forms.IntegerField(
+ label=_("Museum - Inventory entry year")
+ )
museum_inventory_conformity = forms.ChoiceField(
label=_("Museum - Conformity with inventory"), choices=[]
)
museum_conformity_comment = forms.CharField(label=_("Museum - Comment on conformity"))
- museum_inventory_transcript = forms.CharField(label=_("Museum - Inventory transcript"))
+ museum_inventory_transcript = forms.CharField(
+ label=_("Museum - Inventory transcript")
+ )
museum_original_repro = forms.ChoiceField(
label=_("Museum - Original/reproduction"), choices=[]
)
museum_allocation_date = DateField(label=_("Museum - Date of allocation"))
museum_purchase_price = forms.CharField(label=_("Museum - Purchase price"))
+ listed_building_id = forms.CharField(label=_("Listed building ID"))
+ listed_building_protection_nature = forms.ChoiceField(
+ label=_("Nature of listed buildings protection"), choices=[],
+ )
+ listed_building_date = DateField(label=_("Date of listing as a listed building"))
+ listed_building_notes = forms.CharField(label=_("Notes on listed building"))
- TYPES = PeriodSelect.TYPES + [
+ TYPES = DatingSelect.TYPES + [
FieldType("periods", Period),
FieldType("conservatory_states", models.ConservatoryState),
FieldType("base_finds__batch", models.BatchType),
@@ -2052,6 +2167,8 @@ class FindSelect(MuseumForm, GeoItemSelect, PeriodSelect):
FieldType("museum_collections", models.MuseumCollection),
FieldType("museum_inventory_conformity", models.InventoryConformity),
FieldType("museum_original_repro", models.OriginalReproduction),
+ FieldType("listed_building_protection_nature",
+ models.ListedBuildingProtectionNature),
] + GeoItemSelect.TYPES
SITE_KEYS = {
"archaeological_sites": "attached-to-operation",
diff --git a/archaeological_finds/migrations/0149_add_editors.py b/archaeological_finds/migrations/0149_add_editors.py
new file mode 100644
index 000000000..a74739a1d
--- /dev/null
+++ b/archaeological_finds/migrations/0149_add_editors.py
@@ -0,0 +1,64 @@
+# 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_finds', '0148_statementcondition_documents'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='basefind',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='exhibition',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='property',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='statementcondition',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='treatment',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AddField(
+ model_name='treatmentfile',
+ name='editors',
+ field=models.ManyToManyField(blank=True, related_name='%(class)s_edited', to='ishtar_common.author', verbose_name='Editors'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='weight',
+ field=models.FloatField(blank=True, null=True, verbose_name='Weight (g)'),
+ ),
+ migrations.AlterField(
+ model_name='historicalfind',
+ name='weight',
+ field=models.FloatField(blank=True, null=True, verbose_name='Weight (g)'),
+ ),
+ migrations.AlterField(
+ model_name='statementcondition',
+ name='weight',
+ field=models.FloatField(blank=True, null=True, verbose_name='Weight (g)'),
+ ),
+ ]
diff --git a/archaeological_finds/migrations/0150_findrelationtype_findrecordrelations.py b/archaeological_finds/migrations/0150_findrelationtype_findrecordrelations.py
new file mode 100644
index 000000000..a7be6cc8b
--- /dev/null
+++ b/archaeological_finds/migrations/0150_findrelationtype_findrecordrelations.py
@@ -0,0 +1,58 @@
+# Generated by Django 4.2.21 on 2026-04-06 18:14
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models_common
+import re
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0277_data_migration_qualifiedbionotetype'),
+ ('archaeological_finds', '0149_add_editors'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='FindRelationType',
+ 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_finds.findrelationtype', verbose_name='Inverse relation')),
+ ],
+ options={
+ 'verbose_name': 'Find relation type',
+ 'verbose_name_plural': 'Find relation types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='FindRecordRelations',
+ 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_finds.find')),
+ ('relation_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='archaeological_finds.findrelationtype')),
+ ('right_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='left_relations', to='archaeological_finds.find')),
+ ],
+ options={
+ 'verbose_name': 'Find record relation',
+ 'verbose_name_plural': 'Find record relations',
+ 'ordering': ('left_record__cached_label', 'relation_type', 'right_record__cached_label'),
+ 'permissions': [('view_findrelation', 'Can view all Find relations')],
+ },
+ ),
+ ]
diff --git a/archaeological_finds/migrations/0151_data_migration.json b/archaeological_finds/migrations/0151_data_migration.json
new file mode 100644
index 000000000..58dd88c85
--- /dev/null
+++ b/archaeological_finds/migrations/0151_data_migration.json
@@ -0,0 +1,30 @@
+[
+ {
+ "model": "archaeological_finds.findrelationtype",
+ "fields": {
+ "label": "Recolle avec",
+ "txt_idx": "recolle-avec",
+ "comment": "",
+ "available": true,
+ "order": 10,
+ "symmetrical": true,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ },
+ {
+ "model": "archaeological_finds.findrelationtype",
+ "fields": {
+ "label": "Voisin de",
+ "txt_idx": "voisin-de",
+ "comment": "",
+ "available": true,
+ "order": 10,
+ "symmetrical": true,
+ "tiny_label": null,
+ "inverse_relation": null,
+ "logical_relation": null
+ }
+ }
+]
diff --git a/archaeological_finds/migrations/0151_data_migration_find_relation_type.py b/archaeological_finds/migrations/0151_data_migration_find_relation_type.py
new file mode 100644
index 000000000..f3a1c1b4e
--- /dev/null
+++ b/archaeological_finds/migrations/0151_data_migration_find_relation_type.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, __):
+ FindRelationtypeType = apps.get_model("archaeological_finds", "findrelationtype")
+ if not FindRelationtypeType.objects.count():
+ json_path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1] + ["0151_data_migration.json"])
+ call_command("loaddata", json_path)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_finds', '0150_findrelationtype_findrecordrelations'),
+ ]
+
+ operations = [
+ migrations.RunPython(load_data)
+ ]
diff --git a/archaeological_finds/migrations/0152_find_actors_heritage_museum_fields.py b/archaeological_finds/migrations/0152_find_actors_heritage_museum_fields.py
new file mode 100644
index 000000000..1d386c3ee
--- /dev/null
+++ b/archaeological_finds/migrations/0152_find_actors_heritage_museum_fields.py
@@ -0,0 +1,154 @@
+# Generated by Django 4.2.21 on 2026-04-16 10:44
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models_common
+import re
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0277_data_migration_qualifiedbionotetype'),
+ ('archaeological_finds', '0151_data_migration_find_relation_type'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='find',
+ name='actors',
+ field=models.ManyToManyField(blank=True, related_name='finds', to='ishtar_common.qualifiedbiographicalnote', verbose_name='Actors'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='conservatory_states_details',
+ field=models.TextField(blank=True, default='', verbose_name='Conservatory state details'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='iconography_notes',
+ field=models.TextField(blank=True, default='', verbose_name='Notes on iconography'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='listed_building_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Date of listing as a listed building'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='listed_building_id',
+ field=models.TextField(blank=True, default='', verbose_name='Listed building ID'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='listed_building_notes',
+ field=models.TextField(blank=True, default='', verbose_name='Notes on listed building'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='mark_text',
+ field=models.TextField(blank=True, default='', verbose_name='Mark text'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='title',
+ field=models.TextField(blank=True, default='', verbose_name='Title'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='conservatory_states_details',
+ field=models.TextField(blank=True, default='', verbose_name='Conservatory state details'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='iconography_notes',
+ field=models.TextField(blank=True, default='', verbose_name='Notes on iconography'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='listed_building_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Date of listing as a listed building'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='listed_building_id',
+ field=models.TextField(blank=True, default='', verbose_name='Listed building ID'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='listed_building_notes',
+ field=models.TextField(blank=True, default='', verbose_name='Notes on listed building'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='mark_text',
+ field=models.TextField(blank=True, default='', verbose_name='Mark text'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='title',
+ field=models.TextField(blank=True, default='', verbose_name='Title'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='mark',
+ field=models.TextField(blank=True, default='', verbose_name='Marking details'),
+ ),
+ migrations.AlterField(
+ model_name='historicalfind',
+ name='mark',
+ field=models.TextField(blank=True, default='', verbose_name='Marking details'),
+ ),
+ migrations.CreateModel(
+ name='ListedBuildingProtectionNature',
+ 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_finds.listedbuildingprotectionnature', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Listed building protection nature',
+ 'verbose_name_plural': 'Listed building protection nature',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='IconographicPatternType',
+ 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_finds.iconographicpatterntype', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Iconographic pattern type',
+ 'verbose_name_plural': 'Iconographic pattern types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='iconographic_patterns',
+ field=models.ManyToManyField(blank=True, related_name='finds', to='archaeological_finds.iconographicpatterntype', verbose_name='Iconographic patterns'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='listed_building_protection_nature',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='finds', to='archaeological_finds.listedbuildingprotectionnature', verbose_name='Nature of listed buildings protection'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='listed_building_protection_nature',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.listedbuildingprotectionnature', verbose_name='Nature of listed buildings protection'),
+ ),
+ ]
diff --git a/archaeological_finds/migrations/0153_data_migration.json b/archaeological_finds/migrations/0153_data_migration.json
new file mode 100644
index 000000000..2391c6097
--- /dev/null
+++ b/archaeological_finds/migrations/0153_data_migration.json
@@ -0,0 +1,46 @@
+[
+ {
+ "model": "archaeological_finds.listedbuildingprotectionnature",
+ "fields": {
+ "label": "Inscription au titre des monuments historiques",
+ "txt_idx": "inscription-au-titre-des-monuments-historiques",
+ "comment": "",
+ "available": true,
+ "order": 10,
+ "parent": null
+ }
+ },
+ {
+ "model": "archaeological_finds.listedbuildingprotectionnature",
+ "fields": {
+ "label": "Classement au titre des monuments historiques",
+ "txt_idx": "classement-au-titre-des-monuments-historiques",
+ "comment": "",
+ "available": true,
+ "order": 20,
+ "parent": null
+ }
+ },
+ {
+ "model": "archaeological_finds.iconographicpatterntype",
+ "fields": {
+ "label": "Motif iconographique 1",
+ "txt_idx": "motif-iconographique-1",
+ "comment": "",
+ "available": true,
+ "order": 10,
+ "parent": null
+ }
+ },
+ {
+ "model": "archaeological_finds.iconographicpatterntype",
+ "fields": {
+ "label": "Motif iconographique 2",
+ "txt_idx": "motif-iconographique-2",
+ "comment": "",
+ "available": true,
+ "order": 20,
+ "parent": null
+ }
+ }
+]
diff --git a/archaeological_finds/migrations/0153_data_migration_find_listed_building_icono_patterns.py b/archaeological_finds/migrations/0153_data_migration_find_listed_building_icono_patterns.py
new file mode 100644
index 000000000..571197971
--- /dev/null
+++ b/archaeological_finds/migrations/0153_data_migration_find_listed_building_icono_patterns.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, __):
+ IconographicPatternType = apps.get_model("archaeological_finds", "iconographicpatterntype")
+ if not IconographicPatternType.objects.count():
+ json_path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1] + ["0153_data_migration.json"])
+ call_command("loaddata", json_path)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_finds', '0152_find_actors_heritage_museum_fields'),
+ ]
+
+ operations = [
+ migrations.RunPython(load_data)
+ ]
diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py
index d79369711..3b02d751e 100644
--- a/archaeological_finds/models.py
+++ b/archaeological_finds/models.py
@@ -14,12 +14,16 @@ from archaeological_finds.models_finds import (
FindBasket,
FindDating,
FindInsideContainer,
+ FindRecordRelations,
+ FindRelationType,
FindTreatment,
FirstBaseFindView,
FunctionalArea,
+ IconographicPatternType,
IntegrityType,
InventoryConformity,
InventoryMarkingPresence,
+ ListedBuildingProtectionNature,
MarkingType,
MaterialType,
MaterialTypeQualityType,
@@ -78,14 +82,18 @@ __all__ = [
"FindDownstreamTreatments",
"FindInsideContainer",
"FindNonModifTreatments",
+ "FindRecordRelations",
+ "FindRelationType",
"FindTreatment",
"FindTreatments",
"FindUpstreamTreatments",
"FollowUpActionType",
"FunctionalArea",
+ "IconographicPatternType",
"IntegrityType",
"InventoryConformity",
"InventoryMarkingPresence",
+ "ListedBuildingProtectionNature",
"MarkingType",
"MaterialType",
"MaterialTypeQualityType",
diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py
index 9b4156a9d..e475318af 100644
--- a/archaeological_finds/models_finds.py
+++ b/archaeological_finds/models_finds.py
@@ -29,6 +29,7 @@ from django.db.models import Max, Q, F
from django.db.models.signals import m2m_changed, post_save, post_delete, pre_delete
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse, reverse_lazy
+from django.utils.formats import date_format
from ishtar_common.data_importer import post_importer_action, ImporterError
from ishtar_common.utils import (
@@ -54,26 +55,31 @@ from ishtar_common.models import (
Document,
DocumentItem,
document_attached_changed,
+ GeneralRecordRelations,
+ GeneralRelationType,
GeneralType,
GeoItem,
+ geo_item_pre_delete,
get_current_profile,
HierarchicalType,
HistoryModel,
Imported,
IshtarSiteProfile,
LightHistorizedItem,
+ QualifiedBiographicalNote,
+ main_item_pre_delete,
MainItem,
OrderedHierarchicalType,
OrderedType,
Organization,
OwnPerms,
Person,
+ post_delete_record_relation,
post_save_cache,
QuickAction,
+ RecordRelationManager,
SearchVectorConfig,
ValueGetter,
- main_item_pre_delete,
- geo_item_pre_delete
)
from ishtar_common.models_common import HistoricalRecords, SerializeItem, \
GeoVectorData, geodata_attached_changed
@@ -120,9 +126,37 @@ post_save.connect(post_save_cache, sender=MaterialTypeQualityType)
post_delete.connect(post_save_cache, sender=MaterialTypeQualityType)
-class ConservatoryState(HierarchicalType):
- order = models.IntegerField(_("Order"), default=10)
+class IconographicPatternType(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Iconographic pattern type")
+ verbose_name_plural = _("Iconographic pattern types")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Finds")
+
+
+post_save.connect(post_save_cache, sender=IconographicPatternType)
+post_delete.connect(post_save_cache, sender=IconographicPatternType)
+
+
+class ListedBuildingProtectionNature(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Listed building protection nature")
+ verbose_name_plural = _("Listed building protection nature")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Finds")
+
+
+post_save.connect(post_save_cache, sender=ListedBuildingProtectionNature)
+post_delete.connect(post_save_cache, sender=ListedBuildingProtectionNature)
+
+class ConservatoryState(OrderedHierarchicalType):
class Meta:
verbose_name = _("Conservatory state type")
verbose_name_plural = _("Conservatory state types")
@@ -207,6 +241,7 @@ class TreatmentType(HierarchicalType):
initial=None,
force=False,
full_hierarchy=False,
+ limit=None
):
types = super(TreatmentType, cls).get_types(
dct=dct,
@@ -217,6 +252,7 @@ class TreatmentType(HierarchicalType):
initial=initial,
force=force,
full_hierarchy=full_hierarchy,
+ limit=limit
)
if dct and not exclude:
rank = 0
@@ -1342,6 +1378,7 @@ class Find(
("datings__period__label", _("Chronological period")),
("material_types__label", _("Material type")),
("object_types__label", _("Object type")),
+ ("iconographic_patterns__label", _("Iconographic patterns")),
("recommended_treatments__label", _("Recommended treatments")),
("conservatory_states__label", _("Conservatory states")),
("integrities__label", _("Integrity")),
@@ -1389,6 +1426,7 @@ class Find(
"museum_entry_date",
"museum_entry_date_end",
"museum_allocation_date",
+ "listed_building_date"
]
NUMBER_FIELDS = [
"base_finds__context_record__operation__year",
@@ -1405,15 +1443,13 @@ class Find(
"weight",
"find_number",
"min_number_of_individuals",
- "datings__start_date",
- "datings__end_date",
"clutter_long_side",
"clutter_short_side",
"clutter_height",
"museum_inventory_entry_year",
"museum_inventory_quantity",
"museum_observed_quantity",
- ] + GeographicSubTownItem.NUMBER_FIELDS
+ ] + GeographicSubTownItem.NUMBER_FIELDS + BaseDating.NUMBER_FIELDS
BASE_REQUEST = {"downstream_treatment__isnull": True}
EXTRA_REQUEST_KEYS = {
"all_base_finds__context_record": "base_finds__context_record__context_record_tree_parent__cr_parent_id",
@@ -1436,6 +1472,7 @@ class Find(
"documents__image__isnull": "documents__image__isnull",
"container__location": "container__location__pk",
"container_ref__location": "container_ref__location__pk",
+ "base_finds__excavation_id": "base_finds__excavation_id"
}
for table in (TABLE_COLS, TABLE_COLS_FOR_OPE):
for key in table:
@@ -1455,6 +1492,9 @@ class Find(
"label": SearchAltName(
pgettext_lazy("key for text search", "free-id"), "label__iexact"
),
+ "title": SearchAltName(
+ pgettext_lazy("key for text search", "title"), "title__iexact"
+ ),
"denomination": SearchAltName(
pgettext_lazy("key for text search", "denomination"), "denomination__iexact"
),
@@ -1526,6 +1566,19 @@ class Find(
"material_types__label__iexact",
related_name="material_types",
),
+ "iconographic_patterns": SearchAltName(
+ pgettext_lazy("key for text search", "iconographic-patterns"),
+ "iconographic_patterns__label__iexact",
+ related_name="iconographic_patterns",
+ ),
+ "iconography_notes": SearchAltName(
+ pgettext_lazy("key for text search", "iconography-notes"),
+ "iconography_notes__iexact",
+ ),
+ "actors": SearchAltName(
+ pgettext_lazy("key for text search", "actors"),
+ "actors__cached_label__iexact"
+ ),
"object_types": SearchAltName(
pgettext_lazy("key for text search", "object-type"),
"object_types__label__iexact",
@@ -1605,7 +1658,7 @@ class Find(
"previous_id": SearchAltName(
pgettext_lazy("key for text search", "previous-id"), "previous_id__iexact"
),
- #'collection':
+ # 'collection':
# SearchAltName(
# pgettext_lazy("key for text search", "collection"),
# 'collection__name__iexact'),
@@ -1619,8 +1672,17 @@ class Find(
"museum_id": SearchAltName(
pgettext_lazy("key for text search", "museum-id"), "museum_id__iexact"
),
+ "museum_id_prefix": SearchAltName(
+ pgettext_lazy("key for text search", "museum-id-prefix"),
+ "museum_id_prefix__iexact"
+ ),
+ "museum_id_suffix": SearchAltName(
+ pgettext_lazy("key for text search", "museum-id-suffix"),
+ "museum_id_suffix__iexact"
+ ),
"cache_complete_museum_id": SearchAltName(
- pgettext_lazy("key for text search", "complete-museum-id"), "cache_complete_museum_id__iexact"
+ pgettext_lazy("key for text search", "complete-museum-id"),
+ "cache_complete_museum_id__iexact"
),
"laboratory_id": SearchAltName(
pgettext_lazy("key for text search", "laboratory-id"),
@@ -1629,6 +1691,10 @@ class Find(
"mark": SearchAltName(
pgettext_lazy("key for text search", "mark"), "mark__iexact"
),
+ "mark_text": SearchAltName(
+ pgettext_lazy("key for text search", "marking-transcription"),
+ "mark_text__iexact"
+ ),
"base_finds__discovery_date": SearchAltName(
pgettext_lazy("key for text search", "discovery-date"),
"base_finds__discovery_date",
@@ -1684,6 +1750,10 @@ class Find(
pgettext_lazy("key for text search", "conservatory-comment"),
"conservatory_comment__iexact",
),
+ "conservatory_states_details": SearchAltName(
+ pgettext_lazy("key for text search", "conservatory-states-details"),
+ "conservatory_states_details__iexact",
+ ),
"length": SearchAltName(
pgettext_lazy("key for text search", "length"), "length"
),
@@ -1916,12 +1986,28 @@ class Find(
pgettext_lazy("key for text search", "museum-observed-quantity"),
"museum_observed_quantity"
),
+ "listed_building_id": SearchAltName(
+ pgettext_lazy("key for text search", "listed-building-id"),
+ "listed_building_id__iexact"
+ ),
+ "listed_building_protection_nature": SearchAltName(
+ pgettext_lazy("key for text search", "listed-building-protection-nature"),
+ "listed_building_protection_nature__label__iexact"
+ ),
+ "listed_building_date": SearchAltName(
+ pgettext_lazy("key for text search", "listed-building-date"),
+ "listed_building_date"
+ ),
+ "listed_building_notes": SearchAltName(
+ pgettext_lazy("key for text search", "listed-building-notes"),
+ "listed_building_notes__iexact"
+ ),
}
ALT_NAMES.update(BaseHistorizedItem.ALT_NAMES)
ALT_NAMES.update(DocumentItem.ALT_NAMES)
- ALT_NAMES.update(Dating.ASSOCIATED_ALT_NAMES)
ALT_NAMES.update(GeoItem.ALT_NAMES_FOR_FIND())
ALT_NAMES.update(Imported.ALT_NAMES)
+ ALT_NAMES.update(BaseDating.ALT_NAMES)
DEFAULT_SEARCH_FORM = ("archaeological_finds.forms", "FindSelect")
@@ -1983,6 +2069,7 @@ class Find(
SearchVectorConfig("periods__label", "local"),
SearchVectorConfig("integrities__label", "raw"),
SearchVectorConfig("material_types__label", "local"),
+ SearchVectorConfig("iconographic_patterns__label", "local"),
SearchVectorConfig("object_types__label", "raw"),
SearchVectorConfig("remarkabilities__label", "raw"),
SearchVectorConfig("technical_processes__label", "raw"),
@@ -2085,6 +2172,7 @@ class Find(
]
HISTORICAL_M2M = [
"material_types",
+ "iconographic_patterns",
"technical_processes",
"periods",
"datings",
@@ -2113,6 +2201,7 @@ class Find(
"cultural_attributions",
"functional_areas",
"material_types",
+ "iconographic_patterns",
"integrities",
"recommended_treatments",
"museum_former_collections",
@@ -2171,6 +2260,7 @@ class Find(
order = models.IntegerField(_("Order"), default=1)
label = models.TextField(_("Free ID"))
denomination = models.TextField(_("Denomination"), blank=True, default="")
+ title = models.TextField(_("Title"), blank=True, default="")
# museum module IDs
museum_id_prefix = models.TextField(_("Museum ID prefix"), blank=True, default="")
museum_id = models.TextField(_("Museum inventory number"), blank=True, default="")
@@ -2180,6 +2270,14 @@ class Find(
description = models.TextField(_("Description"), blank=True, default="")
decoration = models.TextField(_("Decoration"), blank=True, default="")
inscription = models.TextField(_("Inscription"), blank=True, default="")
+ iconographic_patterns = models.ManyToManyField(
+ IconographicPatternType,
+ verbose_name=_("Iconographic patterns"),
+ related_name="finds",
+ blank=True,
+ )
+ iconography_notes = models.TextField(_("Notes on iconography"), blank=True,
+ default="")
manufacturing_place = models.TextField(
_("Manufacturing place"), blank=True, default=""
)
@@ -2238,6 +2336,28 @@ class Find(
cultural_attributions = models.ManyToManyField(
CulturalAttributionType, verbose_name=_("Cultural attribution"), blank=True
)
+ actors = models.ManyToManyField(
+ QualifiedBiographicalNote, related_name="finds", verbose_name=_("Actors"),
+ blank=True
+ )
+ ## listed building
+ listed_building_id = models.TextField(
+ _("Listed building ID"), default="", blank=True,
+ )
+ listed_building_protection_nature = models.ForeignKey(
+ ListedBuildingProtectionNature,
+ verbose_name=_("Nature of listed buildings protection"),
+ blank=True,
+ null=True,
+ related_name="finds",
+ on_delete=models.SET_NULL,
+ )
+ listed_building_date = models.DateField(
+ _("Date of listing as a listed building"), blank=True, null=True)
+ listed_building_notes = models.TextField(
+ _("Notes on listed building"), default="", blank=True,
+ )
+ ## containers
container = models.ForeignKey(
"archaeological_warehouse.Container",
verbose_name=_("Container"),
@@ -2322,7 +2442,8 @@ class Find(
dimensions_comment = models.TextField(
_("Dimensions comment"), blank=True, default=""
)
- mark = models.TextField(_("Mark"), blank=True, default="")
+ mark_text = models.TextField(_("Transcription of the marking"), blank=True, default="")
+ mark = models.TextField(_("Marking details"), blank=True, default="")
comment = models.TextField(_("General comment"), blank=True, default="")
dating_comment = models.TextField(_("Comment on dating"), blank=True, default="")
previous_id = models.TextField(_("Previous ID"), blank=True, default="")
@@ -2419,6 +2540,9 @@ class Find(
verbose_name=_("Conservatory states"),
blank=True,
)
+ conservatory_states_details = models.TextField(
+ _("Conservatory state details"), blank=True, default=""
+ )
conservatory_comment = models.TextField(
_("Conservatory comment"), blank=True, default=""
)
@@ -2552,16 +2676,55 @@ class Find(
]
)
+ def _has_section(self, name, attrs):
+ """
+ For sheets: evaluate availability of a section.
+ Cache is set.
+ """
+ if getattr(self, "_cache_section", None) is None:
+ self._cache_section = {}
+ if name in self._cache_section:
+ return self._cache_section[name]
+ has_value = False
+ for attr in attrs:
+ if getattr(self, attr):
+ has_value = True
+ break
+ self._cache_section[name] = has_value
+ return self._cache_section[name]
+
+ @property
+ def has_listed_building_section(self):
+ attrs = ["listed_building_protection_nature_id", "listed_building_id",
+ "listed_building_notes", "listed_building_date"]
+ return self._has_section("has_listed_building_section", attrs)
+
+ @property
+ def has_preservation_fields(self):
+ attrs = [
+ "integrities_count", "remarkabilities_count", "conservatory_states_count",
+ "conservatory_comment", "alterations_count", "alteration_causes_count",
+ "recommended_treatments_count", "appraisal_date", "treatment_emergency",
+ "insurance_value", "estimated_value", "conservatory_states_details"
+ ]
+ return self._has_section("has_preservation_fields", attrs)
+
@property
def has_museum_section(self):
- if get_current_profile().museum and self.mark:
+ if getattr(self, "_has_museum_section", None) is not None:
+ return self._has_museum_section
+ if self.mark or self.mark_text:
+ self._has_museum_section = True
return True
for field in self._meta.get_fields():
- if not field.name.startswith("museum_"):
+ if not field.name.startswith("museum_") and \
+ not field.name.startswith("iconograph"):
continue
instanced_field = getattr(self, field.name)
if instanced_field and (not field.many_to_many or instanced_field.count()):
+ self._has_museum_section = True
return True
+ self._has_museum_section = False
return False
@property
@@ -2570,7 +2733,6 @@ class Find(
@property
def museum_entry_date_label(self):
- from django.utils.formats import date_format
if not self.museum_entry_date:
return
if self.museum_entry_date and self.museum_entry_date_end and (
@@ -2579,9 +2741,12 @@ class Find(
self.museum_entry_date.day == 1 and self.museum_entry_date_end.day == 31
):
return self.museum_entry_date.year
- dates = [date_format(self.museum_entry_date, format='SHORT_DATE_FORMAT', use_l10n=True)]
+ dates = [date_format(self.museum_entry_date, format='SHORT_DATE_FORMAT',
+ use_l10n=True)]
if self.museum_entry_date_end:
- dates.append(date_format(self.museum_entry_date_end, format='SHORT_DATE_FORMAT', use_l10n=True))
+ dates.append(
+ date_format(self.museum_entry_date_end, format='SHORT_DATE_FORMAT',
+ use_l10n=True))
return " / ".join(dates)
@classmethod
@@ -2920,7 +3085,7 @@ class Find(
# no particular rights: if you can view an item you can add it to your
# own basket
- actions = super(Find, self).get_extra_actions(request)
+ actions = super().get_extra_actions(request)
is_locked = hasattr(self, "is_locked") and self.is_locked(request.user)
profile = get_current_profile()
@@ -2928,6 +3093,14 @@ class Find(
if can_edit_find and not is_locked:
actions += [
(
+ reverse("find-relations-modify", args=[self.pk]),
+ _("Modify finds relations"),
+ "fa fa-retweet",
+ _("finds"),
+ "",
+ True,
+ ),
+ (
reverse("find-dating-add", args=[self.pk]),
_("Add dating"),
"fa fa-plus",
@@ -3022,29 +3195,52 @@ class Find(
return ""
return "{}-{}".format(bf.context_record.operation.get_reference(), self.index)
+ def _get_count(self, attr):
+ """
+ For sheets: evaluate count of m2m.
+ Cache is set.
+ """
+ if getattr(self, "_cache_count", None) is None:
+ self._cache_count = {}
+ if attr not in self._cache_count:
+ self._cache_count[attr] = getattr(self, attr).count()
+ return self._cache_count[attr]
+
@property
def integrities_count(self):
- return self.integrities.count()
+ return self._get_count("integrities")
@property
def conservatory_states_count(self):
- return self.conservatory_states.count()
+ return self._get_count("conservatory_states")
@property
def remarkabilities_count(self):
- return self.remarkabilities.count()
+ return self._get_count("remarkabilities")
@property
def cultural_attributions_count(self):
- return self.cultural_attributions.count()
+ return self._get_count("cultural_attributions")
@property
def documents_count(self):
- return self.documents.count()
+ return self._get_count("documents")
@property
def periods_count(self):
- return self.periods.count()
+ return self._get_count("periods")
+
+ @property
+ def alterations_count(self):
+ return self._get_count("alterations")
+
+ @property
+ def alteration_causes_count(self):
+ return self._get_count("alteration_causes")
+
+ @property
+ def recommended_treatments_count(self):
+ return self._get_count("recommended_treatments")
@property
def operation(self):
@@ -3943,10 +4139,45 @@ def base_find_find_changed(sender, **kwargs):
m2m_changed.connect(base_find_find_changed, sender=Find.base_finds.through)
-
m2m_changed.connect(document_attached_changed, sender=Find.documents.through)
+class FindRelationType(GeneralRelationType):
+ ADMIN_SECTION = _("Finds")
+ class Meta:
+ verbose_name = _("Find relation type")
+ verbose_name_plural = _("Find relation types")
+ ordering = ("order", "label")
+
+
+class FindRecordRelations(GeneralRecordRelations):
+ ADMIN_SECTION = _("Finds")
+ MAIN_ATTR = "left_record"
+ left_record = models.ForeignKey(
+ Find, related_name="right_relations", on_delete=models.CASCADE
+ )
+ right_record = models.ForeignKey(
+ Find, related_name="left_relations", on_delete=models.CASCADE
+ )
+ relation_type = models.ForeignKey(FindRelationType, on_delete=models.PROTECT)
+ objects = RecordRelationManager()
+
+ class Meta:
+ verbose_name = _("Find record relation")
+ verbose_name_plural = _("Find record relations")
+ ordering = (
+ "left_record__cached_label",
+ "relation_type",
+ "right_record__cached_label",
+ )
+ permissions = [
+ ("view_findrelation", "Can view all Find relations"),
+ ]
+
+
+post_delete.connect(post_delete_record_relation, sender=FindRecordRelations)
+
+
class FindDating(BaseDating):
SERIALIZE_EXCLUDE = ["find"]
CURRENT_MODEL = Find
diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html
index 0ae238d66..b3663bbbc 100644
--- a/archaeological_finds/templates/ishtar/sheet_find.html
+++ b/archaeological_finds/templates/ishtar/sheet_find.html
@@ -26,6 +26,7 @@
{% with non_modif_treatments_count=item.non_modif_treatments_count %}
{% with associated_treatment_files_count=item.associated_treatment_files_count %}
+{% 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_view_container=permission_view_own_container|or_:permission_view_container %}
{% with display_warehouse_treatments=item.container|or_:item.container_ref|or_:item.upstream_treatment|or_:item.downstream_treatment|or_:non_modif_treatments_count|or_:associated_treatment_files_count %}
{% with dating_list=item|m2m_listing:"datings" %}
@@ -51,6 +52,15 @@
{% trans "Archaeological context" %}
</a>
</li>
+ {% if display_relations %}
+ <li class="nav-item">
+ <a class="nav-link" id="{{window_id}}-relations-tab"
+ data-toggle="tab" href="#{{window_id}}-relations" role="tab"
+ aria-controls="{{window_id}}-relations" aria-selected="false">
+ {% trans "Relations" %}
+ </a>
+ </li>
+ {% endif %}
<li class="nav-item">
<a class="nav-link" id="{{window_id}}-datations-tab"
data-toggle="tab" href="#{{window_id}}-datations" role="tab"
@@ -114,6 +124,7 @@
<div class='row'>
{% field_flex "Denomination" item.denomination %}
+ {% field_flex _("Title") item.title %}
{% field_flex "Complete museum ID" item.cache_complete_museum_id %}
{% field_flex "Free ID" item.label %}
{% field_flex "Previous ID" item.previous_id %}
@@ -129,6 +140,7 @@
{% field_flex_full "Public description" item.public_description "<pre>" "</pre>" %}
{% field_flex "Is complete?" item.is_complete %}
{% with material=item.get_hierarchical_material_types %}{% if material %}
+ {% field_flex_detail_multiple _("Actors") item.actors %}
{% field_flex "Material types" material %}{% else %}
{% field_flex_multiple_obj "Material types" item 'material_types' %}
{% endif %}{% endwith %}
@@ -152,6 +164,16 @@
{% field_flex_full _("General comment") item.comment "<pre>" "</pre>" %}
</div>
+ {% if item.has_listed_building_section %}
+ <h3>{% trans "Listed building" %}</h3>
+ <div class='row'>
+ {% field_flex _("Nature of listed buildings protection") item.listed_building_protection_nature %}
+ {% field_flex _("Listed building ID") item.listed_building_id %}
+ {% field_flex _("Date of listing as a listed building") item.listed_building_date|date:"SHORT_DATE_FORMAT" %}
+ {% field_flex _("Notes on listed building") item.listed_building_notes %}
+ </div>
+ {% endif %}
+
{% if item.has_ownership_section %}
<h3>{% trans "Ownership" %}</h3>
<div class='row'>
@@ -177,8 +199,11 @@
{% field_flex museum_entry_date_label item.museum_entry_date_label %}
{% field_flex "Comment on museum entry date" item.museum_entry_date_comment %}
{% field_flex_detail_multiple _("Donors, testators or vendors") item.museum_donors %}
+ {% field_flex_multiple_obj _("Iconographic patterns") item 'iconographic_patterns' %}
+ {% field_flex_full _("Notes on iconography") item.iconography_notes "<pre>" "</pre>" %}
{% field_flex_multiple_obj "Presence of inventory marking" item "museum_inventory_marking_presence" %}
{% field_flex_multiple_obj "Type of marking" item "museum_marking_type" %}
+ {% field_flex _("Transcription of the marking") item.mark_text %}
{% field_flex "Marking details" item.mark "<pre>" "</pre>" %}
{% field_flex "Conformity with inventory" item.museum_inventory_conformity %}
{% field_flex "Comment on conformity" item.museum_conformity_comment %}
@@ -213,7 +238,7 @@
{% 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'>
{% trans "Checked" as checked_label %}
@@ -270,6 +295,15 @@
</div>
</div>
+ {% if display_relations %}
+ <div class="tab-pane fade" id="{{window_id}}-relations"
+ role="tabpanel" aria-labelledby="{{window_id}}-relations-tab">
+ {% with relation_url="/show-find/" %}
+ {% include "ishtar/blocks/sheet_relations.html" %}
+ {% endwith %}
+ </div>
+ {% endif %}
+
<div class="tab-pane fade" id="{{window_id}}-datations"
role="tabpanel" aria-labelledby="{{window_id}}-datations-tab">
<h3>{% trans "Periods / Datings" %}</h3>
@@ -326,7 +360,7 @@
{% 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 %}
<script type='text/javascript'>
$( "#{{window_id}}-tabs" ).on( "tabsactivate", function( event, ui ) {
diff --git a/archaeological_finds/templates/ishtar/sheet_find_treatments.html b/archaeological_finds/templates/ishtar/sheet_find_treatments.html
index 49e70d5f8..670c1997b 100644
--- a/archaeological_finds/templates/ishtar/sheet_find_treatments.html
+++ b/archaeological_finds/templates/ishtar/sheet_find_treatments.html
@@ -9,12 +9,13 @@
</div>
{% endif %}
{% endcomment %}
- {% if item.integrities_count or item.remarkabilities_count or item.conservatory_states_count or item.conservatory_comment or item.alterations.count or item.alteration_causes.count or item.recommended_treatments.count or item.appraisal_date or item.treatment_emergency or item.insurance_value or item.estimated_value %}
+ {% if item.has_preservation_fields %}
<h3>{% trans "Preservation" %}</h3>
<div class='row'>
{% field_flex_multiple_obj _("Integrity") item 'integrities' %}
{% field_flex_multiple_obj _("Remarkability") item 'remarkabilities' %}
{% field_flex_multiple_obj _("Conservatory states") item 'conservatory_states' %}
+ {% field_flex_full _("Conservatory state details") item.conservatory_states_details "<pre>" "</pre>" %}
{% field_flex_multiple_obj _("Alteration") item 'alterations' %}
{% field_flex_multiple_obj _("Alteration cause") item 'alteration_causes' %}
{% field_flex_multiple_obj _("Recommended treatments") item 'recommended_treatments' %}
diff --git a/archaeological_finds/templates/ishtar/sheet_museum_find.html b/archaeological_finds/templates/ishtar/sheet_museum_find.html
index 3acec990e..b97143e15 100644
--- a/archaeological_finds/templates/ishtar/sheet_museum_find.html
+++ b/archaeological_finds/templates/ishtar/sheet_museum_find.html
@@ -26,6 +26,7 @@
{% with non_modif_treatments_count=item.non_modif_treatments_count %}
{% with associated_treatment_files_count=item.associated_treatment_files_count %}
+{% 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_view_container=permission_view_own_container|or_:permission_view_container %}
{% with display_warehouse_treatments=item.container|or_:item.container_ref|or_:item.upstream_treatment|or_:item.downstream_treatment|or_:non_modif_treatments_count|or_:associated_treatment_files_count %}
{% with dating_list=item|m2m_listing:"datings" %}
@@ -51,6 +52,15 @@
{% trans "Archaeological context" %}
</a>
</li>
+ {% if display_relations %}
+ <li class="nav-item">
+ <a class="nav-link" id="{{window_id}}-relations-tab"
+ data-toggle="tab" href="#{{window_id}}-relations" role="tab"
+ aria-controls="{{window_id}}-relations" aria-selected="false">
+ {% trans "Relations" %}
+ </a>
+ </li>
+ {% endif %}
<li class="nav-item">
<a class="nav-link" id="{{window_id}}-datations-tab"
data-toggle="tab" href="#{{window_id}}-datations" role="tab"
@@ -114,6 +124,7 @@
<div class='row'>
{% field_flex "Denomination" item.denomination %}
+ {% field_flex _("Title") item.title %}
{% field_flex "Complete museum ID" item.cache_complete_museum_id %}
{% field_flex "Free ID" item.label %}
{% field_flex "Previous ID" item.previous_id %}
@@ -129,6 +140,7 @@
{% field_flex_full "Public description" item.public_description "<pre>" "</pre>" %}
{% field_flex "Is complete?" item.is_complete %}
{% with material=item.get_hierarchical_material_types %}{% if material %}
+ {% field_flex_detail_multiple _("Actors") item.actors %}
{% field_flex "Material types" material %}{% else %}
{% field_flex_multiple_obj "Material types" item 'material_types' %}
{% endif %}{% endwith %}
@@ -152,6 +164,16 @@
{% field_flex_full _("General comment") item.comment "<pre>" "</pre>" %}
</div>
+ {% if item.has_listed_building_section %}
+ <h3>{% trans "Listed building" %}</h3>
+ <div class='row'>
+ {% field_flex _("Nature of listed buildings protection") item.listed_building_protection_nature %}
+ {% field_flex _("Listed building ID") item.listed_building_id %}
+ {% field_flex _("Date of listing as a listed building") item.listed_building_date|date:"SHORT_DATE_FORMAT" %}
+ {% field_flex _("Notes on listed building") item.listed_building_notes %}
+ </div>
+ {% endif %}
+
{% if item.has_ownership_section %}
<h3>{% trans "Ownership" %}</h3>
<div class='row'>
@@ -177,8 +199,11 @@
{% field_flex museum_entry_date_label item.museum_entry_date_label %}
{% field_flex "Comment on museum entry date" item.museum_entry_date_comment %}
{% field_flex_detail_multiple _("Donors, testators or vendors") item.museum_donors %}
+ {% field_flex_multiple_obj _("Iconographic patterns") item 'iconographic_patterns' %}
+ {% field_flex_full _("Notes on iconography") item.iconography_notes "<pre>" "</pre>" %}
{% field_flex_multiple_obj "Presence of inventory marking" item "museum_inventory_marking_presence" %}
{% field_flex_multiple_obj "Type of marking" item "museum_marking_type" %}
+ {% field_flex _("Transcription of the marking") item.mark_text %}
{% field_flex "Marking details" item.mark "<pre>" "</pre>" %}
{% field_flex "Conformity with inventory" item.museum_inventory_conformity %}
{% field_flex "Comment on conformity" item.museum_conformity_comment %}
@@ -213,7 +238,7 @@
{% 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'>
{% trans "Checked" as checked_label %}
@@ -270,6 +295,15 @@
</div>
</div>
+ {% if display_relations %}
+ <div class="tab-pane fade" id="{{window_id}}-relations"
+ role="tabpanel" aria-labelledby="{{window_id}}-relations-tab">
+ {% with relation_url="/show-find/" %}
+ {% include "ishtar/blocks/sheet_relations.html" %}
+ {% endwith %}
+ </div>
+ {% endif %}
+
<div class="tab-pane fade" id="{{window_id}}-datations"
role="tabpanel" aria-labelledby="{{window_id}}-datations-tab">
<h3>{% trans "Periods / Datings" %}</h3>
@@ -326,7 +360,7 @@
{% 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 %}
<script type='text/javascript'>
$( "#{{window_id}}-tabs" ).on( "tabsactivate", function( event, ui ) {
diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py
index 87d0a62f2..aaca76d51 100644
--- a/archaeological_finds/tests.py
+++ b/archaeological_finds/tests.py
@@ -676,7 +676,7 @@ class ImportFindTest(BaseImportFindTest):
#base_find.save()
base_find = models.BaseFind.objects.get(pk=base_find.pk)
- impt.importation()
+ impt.start_import()
if impt.error_file:
self.assertIsNone(
impt.error_file,
@@ -722,7 +722,7 @@ class ImportFindTest(BaseImportFindTest):
nb_find = models.Find.objects.count()
nb_container = Container.objects.count()
nb_docs = Document.objects.count()
- impt.importation()
+ impt.start_import()
self.assertEqual(models.BaseFind.objects.count(), nb_base_find + 1)
self.assertEqual(models.Find.objects.count(), nb_find + 1)
@@ -775,7 +775,7 @@ class ImportFindTest(BaseImportFindTest):
glass = models.MaterialType.objects.get(txt_idx="glass").pk
self.set_target_key("material_types", "terre-cuite", ceram)
self.set_target_key("material_types", "verre", glass)
- impt.importation()
+ impt.start_import()
# new finds has now been imported
current_nb = models.BaseFind.objects.count()
self.assertEqual(current_nb, (old_nb + 4))
@@ -910,7 +910,7 @@ class ImportFindTest(BaseImportFindTest):
impt = form.save(self.ishtar_user)
impt.initialize()
- impt.importation()
+ impt.start_import()
# no new finds has now been imported
current_nb = models.BaseFind.objects.count()
self.assertEqual(current_nb, old_nb)
@@ -976,7 +976,7 @@ class ImportFindTest(BaseImportFindTest):
# reimport
impt.initialize()
- impt.importation()
+ impt.start_import()
# check errors
self.assertEqual(len(impt.errors), 2)
error_msg = str(
@@ -987,7 +987,7 @@ class ImportFindTest(BaseImportFindTest):
# test ignore errors
impt.importer_type.ignore_errors = error_msg[:-5]
impt.importer_type.save()
- impt.importation()
+ impt.start_import()
# check errors
self.assertEqual(len(impt.errors), 0)
@@ -1023,8 +1023,8 @@ class ImportFindTest(BaseImportFindTest):
impt.save()
impt.initialize()
# import 2 times the description field
- impt.importation()
- impt.importation()
+ impt.start_import()
+ impt.start_import()
for find in models.Find.objects.filter(imports__pk=impt.pk).all():
sp = find.description.split(" - ")
self.assertEqual(len(sp), 2)
@@ -1112,7 +1112,7 @@ class ImportFindLiveServerTest(LiveServerTestCase, BaseImportFindTest):
self.assertTrue(form.is_valid())
impt = form.save(self.ishtar_user)
impt.initialize()
- impt.importation()
+ impt.start_import()
# new finds has now been imported
current_nb = models.BaseFind.objects.count()
self.assertEqual(current_nb, (old_nb + 4))
@@ -1820,7 +1820,7 @@ class FindSearchTest(FindInit, TestCase, SearchText, StatisticsTest):
find2.save()
material_key = str(pgettext_lazy("key for text search", "material"))
- period_key = str(pgettext_lazy("key for text search", "datings-period"))
+ period_key = str(pgettext_lazy("key for text search", "dating-period"))
ope_key = str(pgettext_lazy("key for text search", "code-patriarche"))
result = [
(f'{ope_key}="{ope_values[0]}" {period_key}="{neo}" '
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py
index d1520d4ba..de96604c6 100644
--- a/archaeological_finds/urls.py
+++ b/archaeological_finds/urls.py
@@ -99,6 +99,14 @@ urlpatterns = [
),
name="find-dating-delete",
),
+ path(
+ "find-relations-modify/<int:pk>)/",
+ check_permissions(
+ ["archaeological_finds.change_find",
+ "archaeological_finds.change_own_find"]
+ )(views.find_modify_relations),
+ name="find-relations-modify",
+ ),
re_path(r"get-findbasket/$", views.get_find_basket, name="get-findbasket"),
re_path(
r"get-findbasket-write/$",
@@ -621,6 +629,11 @@ urlpatterns = [
name="autocomplete-materialtype",
),
re_path(
+ r"autocomplete-iconographicpattern/$",
+ views.autocomplete_iconographicpattern,
+ name="autocomplete-iconographicpattern",
+ ),
+ re_path(
r"autocomplete-treatmenttype/$",
views.autocomplete_treatmenttype,
name="autocomplete-treatmenttype",
diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py
index d11e820fa..3ce2a466f 100644
--- a/archaeological_finds/views.py
+++ b/archaeological_finds/views.py
@@ -63,6 +63,7 @@ from ishtar_common.views_item import (
get_autocomplete_queries,
get_autocomplete_query
)
+from archaeological_operations.views import get_relation_modify
from archaeological_operations.wizards import AdministrativeActDeletionWizard
from archaeological_finds import wizards
@@ -538,13 +539,20 @@ def find_delete(request, pk):
return redirect(reverse("find_deletion", kwargs={"step": step}))
+autocomplete_functionalarea = get_autocomplete_generic(models.FunctionalArea)
+autocomplete_integritytype = get_autocomplete_generic(models.IntegrityType)
+autocomplete_iconographicpattern = get_autocomplete_generic(
+ models.IconographicPatternType)
autocomplete_objecttype = get_autocomplete_generic(models.ObjectType)
autocomplete_materialtype = get_autocomplete_generic(models.MaterialType)
-autocomplete_treatmenttype = get_autocomplete_generic(models.TreatmentType)
-autocomplete_integritytype = get_autocomplete_generic(models.IntegrityType)
-autocomplete_functionalarea = get_autocomplete_generic(models.FunctionalArea)
autocomplete_technicalarea = get_autocomplete_generic(models.TechnicalAreaType)
autocomplete_technicalprocess = get_autocomplete_generic(models.TechnicalProcessType)
+autocomplete_treatmenttype = get_autocomplete_generic(models.TreatmentType)
+
+find_modify_relations = get_relation_modify(
+ models.Find, models.FindRecordRelations,
+ forms.FindRecordRelationsFormSet, "find-relations-modify"
+)
class NewFindBasketView(IshtarMixin, LoginRequiredMixin, CreateView):