summaryrefslogtreecommitdiff
path: root/archaeological_finds/models_finds.py
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_finds/models_finds.py')
-rw-r--r--archaeological_finds/models_finds.py279
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