diff options
| 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 |
| commit | e7257529560713e1b5bd36fd94279da91ec34979 (patch) | |
| tree | b097f71e0064500e8d370f8d9b45695e2b58d61a | |
| parent | a0f393c0369130620cc99d6222a57503c15fd5d9 (diff) | |
| download | Ishtar-e7257529560713e1b5bd36fd94279da91ec34979.tar.bz2 Ishtar-e7257529560713e1b5bd36fd94279da91ec34979.zip | |
✨ generic search: add editors - find search: add many fields
| -rw-r--r-- | archaeological_finds/forms.py | 43 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 132 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/sheet_find_treatments.html | 2 | ||||
| -rw-r--r-- | archaeological_finds/urls.py | 5 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 8 | ||||
| -rw-r--r-- | archaeological_operations/forms.py | 8 | ||||
| -rw-r--r-- | ishtar_common/forms.py | 5 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 2 |
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, |
