summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2026-04-20 16:03:16 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2026-04-21 12:19:03 +0200
commite7257529560713e1b5bd36fd94279da91ec34979 (patch)
treeb097f71e0064500e8d370f8d9b45695e2b58d61a
parenta0f393c0369130620cc99d6222a57503c15fd5d9 (diff)
downloadIshtar-e7257529560713e1b5bd36fd94279da91ec34979.tar.bz2
Ishtar-e7257529560713e1b5bd36fd94279da91ec34979.zip
✨ generic search: add editors - find search: add many fields
-rw-r--r--archaeological_finds/forms.py43
-rw-r--r--archaeological_finds/models_finds.py132
-rw-r--r--archaeological_finds/templates/ishtar/sheet_find_treatments.html2
-rw-r--r--archaeological_finds/urls.py5
-rw-r--r--archaeological_finds/views.py8
-rw-r--r--archaeological_operations/forms.py8
-rw-r--r--ishtar_common/forms.py5
-rw-r--r--ishtar_common/views_item.py2
8 files changed, 174 insertions, 31 deletions
diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py
index 0e27b0303..6ea406b9e 100644
--- a/archaeological_finds/forms.py
+++ b/archaeological_finds/forms.py
@@ -401,7 +401,8 @@ 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"))
denomination = forms.CharField(label=_("Denomination"), required=False)
@@ -639,10 +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("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),
@@ -1830,6 +1834,7 @@ class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
)
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"))
@@ -1997,6 +2002,10 @@ class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
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)
@@ -2028,6 +2037,7 @@ class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
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=[])
@@ -2075,12 +2085,21 @@ class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
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_text = forms.CharField(label=_("Transcription of the marking"), required=False)
mark = forms.CharField(label=_("Marking details"))
museum_collections = forms.ChoiceField(
label=_("Museum - Collection"), choices=[]
@@ -2091,17 +2110,27 @@ class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
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 = DatingSelect.TYPES + [
FieldType("periods", Period),
@@ -2138,6 +2167,8 @@ class FindSelect(MuseumForm, GeoItemSelect, DatingSelect):
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/models_finds.py b/archaeological_finds/models_finds.py
index e68b3c5fd..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 (
@@ -1377,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")),
@@ -1424,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",
@@ -1489,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"
),
@@ -1560,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",
@@ -1672,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",
@@ -1727,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"
),
@@ -1959,6 +1986,22 @@ 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)
@@ -2026,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"),
@@ -2128,6 +2172,7 @@ class Find(
]
HISTORICAL_M2M = [
"material_types",
+ "iconographic_patterns",
"technical_processes",
"periods",
"datings",
@@ -2156,6 +2201,7 @@ class Find(
"cultural_attributions",
"functional_areas",
"material_types",
+ "iconographic_patterns",
"integrities",
"recommended_treatments",
"museum_former_collections",
@@ -2630,24 +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):
- return self.listed_building_protection_nature_id or \
- self.listed_building_id or \
- self.listed_building_notes or \
- self.listed_building_date
+ 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 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_") and not field.name.startswith("iconograph"):
+ 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
@@ -2656,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 (
@@ -2665,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
@@ -3116,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):
diff --git a/archaeological_finds/templates/ishtar/sheet_find_treatments.html b/archaeological_finds/templates/ishtar/sheet_find_treatments.html
index a36b361ce..670c1997b 100644
--- a/archaeological_finds/templates/ishtar/sheet_find_treatments.html
+++ b/archaeological_finds/templates/ishtar/sheet_find_treatments.html
@@ -9,7 +9,7 @@
</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' %}
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py
index b9cbb58f7..de96604c6 100644
--- a/archaeological_finds/urls.py
+++ b/archaeological_finds/urls.py
@@ -629,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 541d1fc4e..3ce2a466f 100644
--- a/archaeological_finds/views.py
+++ b/archaeological_finds/views.py
@@ -539,13 +539,15 @@ 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,
diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py
index bb62ad8d1..3e94e3963 100644
--- a/archaeological_operations/forms.py
+++ b/archaeological_operations/forms.py
@@ -450,7 +450,6 @@ class SiteRecordRelationsForm(RecordRelationsForm):
validators=[valid_id(models.ArchaeologicalSite)], required=False)
-
SiteRecordRelationsFormSet = formset_factory(
SiteRecordRelationsForm, can_delete=True, formset=RecordRelationsFormSetBase
)
@@ -473,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
@@ -655,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])
@@ -1294,11 +1295,6 @@ class SiteSelect(GeoItemSelect, DatingSelect):
label=_("Discovery area"), max_length=200,
required=False)
- editors = forms.IntegerField(
- label=_("Editors"), required=False,
- widget=widgets.JQueryAutoComplete(
- reverse_lazy('autocomplete-author')))
-
TYPES = [
FieldType('periods', models.Period),
FieldType('remains', models.RemainType),
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py
index 87d498596..138a15901 100644
--- a/ishtar_common/forms.py
+++ b/ishtar_common/forms.py
@@ -1091,6 +1091,10 @@ class TableSelect(IshtarForm):
class HistorySelect(CustomForm, TableSelect):
+ editors = forms.IntegerField(
+ label=_("Editor"), required=False,
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-author')))
history_creator = forms.IntegerField(
label=_("Created by"),
widget=widgets.JQueryAutoComplete(
@@ -1119,6 +1123,7 @@ class HistorySelect(CustomForm, TableSelect):
_explicit_ordering = True
CURRENT_FIELDS = [
+ "editors",
"history_creator",
"created",
"history_modifier",
diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py
index 7e9003661..df0d7f2ef 100644
--- a/ishtar_common/views_item.py
+++ b/ishtar_common/views_item.py
@@ -100,6 +100,7 @@ LIST_FIELDS = { # key: hierarchic depth
"conservatory_states": HIERARCHIC_LEVELS,
"identifications": HIERARCHIC_LEVELS,
"material_types": HIERARCHIC_LEVELS,
+ "iconographic_patterns": HIERARCHIC_LEVELS,
"material_type": HIERARCHIC_LEVELS,
"object_types": HIERARCHIC_LEVELS,
"period": HIERARCHIC_LEVELS,
@@ -118,6 +119,7 @@ LIST_FIELDS = { # key: hierarchic depth
"documentations": HIERARCHIC_LEVELS,
"excavation_technics": HIERARCHIC_LEVELS,
"treatment_types": HIERARCHIC_LEVELS,
+ "listed_building_protection_nature": HIERARCHIC_LEVELS,
"discovery_method": 0,
"discovery_status": 0,
"current_status": 0,