diff options
Diffstat (limited to 'archaeological_finds/models_finds.py')
| -rw-r--r-- | archaeological_finds/models_finds.py | 279 |
1 files changed, 255 insertions, 24 deletions
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 |
