summaryrefslogtreecommitdiff
path: root/archaeological_finds/models_treatments.py
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_finds/models_treatments.py')
-rw-r--r--archaeological_finds/models_treatments.py329
1 files changed, 327 insertions, 2 deletions
diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py
index 845da66ac..cd82ab57e 100644
--- a/archaeological_finds/models_treatments.py
+++ b/archaeological_finds/models_treatments.py
@@ -51,6 +51,7 @@ from ishtar_common.models import (
HistoryModel,
ImageModel,
MainItem,
+ OrderedHierarchicalType,
OrderedType,
Organization,
OwnPerms,
@@ -59,7 +60,11 @@ from ishtar_common.models import (
SearchVectorConfig,
ValueGetter,
)
-from ishtar_common.models_common import CompleteIdentifierItem, HistoricalRecords
+from ishtar_common.models_common import (
+ CompleteIdentifierItem,
+ IdentifierItem,
+ HistoricalRecords
+)
from ishtar_common.utils import (
cached_label_changed,
get_current_year,
@@ -385,6 +390,13 @@ class Treatment(
null=True,
on_delete=models.SET_NULL,
)
+ statement_condition = models.ForeignKey(
+ "StatementCondition",
+ verbose_name=_("Statement condition"),
+ blank=True,
+ null=True,
+ on_delete=models.SET_NULL,
+ )
treatment_status = models.ForeignKey(
TreatmentStatus,
verbose_name=_("Treatment status"),
@@ -1642,10 +1654,323 @@ class TreatmentFile(
m2m_changed.connect(document_attached_changed, sender=TreatmentFile.documents.through)
-
post_save.connect(cached_label_changed, sender=TreatmentFile)
+class StatementConditionType(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Statement condition type")
+ verbose_name_plural = _("Statement condition types")
+ ordering = (
+ "order",
+ "parent__label",
+ "label",
+ )
+ ADMIN_SECTION = _("Treatments")
+
+
+post_save.connect(post_save_cache, sender=StatementConditionType)
+post_delete.connect(post_save_cache, sender=StatementConditionType)
+
+
+class FollowUpActionType(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Follow-up action type")
+ verbose_name_plural = _("Follow-up action types")
+ ordering = (
+ "order",
+ "parent__label",
+ "label",
+ )
+ ADMIN_SECTION = _("Treatments")
+
+
+post_save.connect(post_save_cache, sender=FollowUpActionType)
+post_delete.connect(post_save_cache, sender=FollowUpActionType)
+
+
+class StatementCondition(
+ DocumentItem,
+ BaseHistorizedItem,
+ IdentifierItem,
+ ValueGetter,
+):
+ SLUG = "statementcondition"
+ APP = "archaeological-finds"
+ MODEL = SLUG
+ SHOW_URL = "show-statementcondition"
+
+ # changing theses fields on the statement condition change them in
+ # associated find
+ OVERLOADED_FIELDS = (
+ "description",
+ "conservatory_comment",
+ "length",
+ "width",
+ "height",
+ "volume",
+ "weight",
+ "diameter",
+ "circumference",
+ "thickness",
+ "clutter_long_side",
+ "clutter_short_side",
+ "clutter_height",
+ "dimensions_comment",
+ "treatment_emergency_id",
+ "find_number",
+ "museum_observed_quantity",
+ )
+ OVERLOADED_M2M_FIELDS = (
+ "integrities",
+ "conservatory_states",
+ "recommended_treatments",
+ "alterations",
+ "alteration_causes",
+ "museum_marking_type",
+ "museum_inventory_marking_presence"
+ )
+ APPLIED_CHOICES = (
+ ("D", _("Draft")),
+ ("V", _("Validated")),
+ ("T", _("Validated with treatment")),
+ )
+
+ date = models.DateField(_("Date"))
+ applied = models.CharField(_("Input status"), default="D", choices=APPLIED_CHOICES)
+ initial = models.BooleanField(_("Initial"), default=False)
+ last = models.BooleanField(_("Last"), default=True)
+ find = models.ForeignKey(Find, verbose_name=_("Find"), on_delete=models.CASCADE,
+ related_name="statement_conditions")
+ campaign_number = models.TextField(_("Campaign/observation number"), default="",
+ blank=True)
+ report_number = models.TextField(_("Report number"), default="", blank=True)
+ verification_officer = models.ForeignKey(Person, verbose_name=_("Verification officer"), null=True,
+ blank=True, on_delete=models.SET_NULL)
+ statement_condition_type = models.ForeignKey(
+ StatementConditionType, verbose_name=_("Type"), on_delete=models.PROTECT)
+ follow_up_actions = models.ManyToManyField(
+ FollowUpActionType, verbose_name=_("Follow-up actions"), blank=True
+ )
+ observations = models.TextField(_("Observations"), blank=True, default="")
+
+ # find field
+ integrities = models.ManyToManyField(
+ "IntegrityType",
+ verbose_name=_("Integrity"),
+ blank=True,
+ )
+ conservatory_states = models.ManyToManyField(
+ "ConservatoryState",
+ verbose_name=_("Conservatory states"),
+ blank=True,
+ )
+ recommended_treatments = models.ManyToManyField(
+ "RecommendedTreatmentType",
+ verbose_name=_("Recommended treatments"),
+ blank=True,
+ )
+ treatment_emergency = models.ForeignKey(
+ "TreatmentEmergencyType",
+ verbose_name=_("Treatment emergency"),
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ )
+ conservatory_comment = models.TextField(
+ _("Conservatory comment"), blank=True, default=""
+ )
+ alterations = models.ManyToManyField(
+ "AlterationType", verbose_name=_("Alteration"), blank=True
+ )
+ alteration_causes = models.ManyToManyField(
+ "AlterationCauseType", verbose_name=_("Alteration cause"), blank=True,
+ )
+ description = models.TextField(_("Description"), blank=True, default="")
+ # find fields - museum
+ museum_inventory_marking_presence = models.ManyToManyField(
+ "InventoryMarkingPresence", blank=True,
+ related_name="statement_conditions",
+ verbose_name=_("Presence of inventory marking"),
+ )
+ museum_marking_type = models.ManyToManyField(
+ "MarkingType",
+ verbose_name=_("Type of marking"),
+ blank=True,
+ related_name="statement_conditions",
+ )
+ # find field - dimensions
+ find_number = models.IntegerField(_("Number of remains"), blank=True, null=True)
+ museum_observed_quantity = models.PositiveSmallIntegerField(
+ _("Observed quantity"), blank=True, null=True
+ )
+ length = models.FloatField(_("Length (cm)"), blank=True, null=True)
+ width = models.FloatField(_("Width (cm)"), blank=True, null=True)
+ height = models.FloatField(_("Height (cm)"), blank=True, null=True)
+ volume = models.FloatField(_("Volume (l)"), blank=True, null=True)
+ weight = models.FloatField(_("Weight"), blank=True, null=True)
+ diameter = models.FloatField(_("Diameter (cm)"), blank=True, null=True)
+ circumference = models.FloatField(_("Circumference (cm)"), blank=True, null=True)
+ thickness = models.FloatField(_("Thickness (cm)"), blank=True, null=True)
+ clutter_long_side = models.FloatField(
+ _("Clutter - long side (cm)"), blank=True, null=True
+ )
+ clutter_short_side = models.FloatField(
+ _("Clutter - short side (cm)"), blank=True, null=True
+ )
+ clutter_height = models.FloatField(
+ _("Clutter - height (cm)"), blank=True, null=True
+ )
+ dimensions_comment = models.TextField(
+ _("Dimensions comment"), blank=True, default=""
+ )
+
+ class Meta:
+ verbose_name = _("Statement of condition")
+ verbose_name_plural = _("Statements of condition")
+ ordering = ("find", "-date", "cached_label")
+ indexes = [
+ GinIndex(fields=["data"]),
+ ]
+ ADMIN_SECTION = _("Treatments")
+
+ @property
+ def is_last(self):
+ return not self.__class__.objects.filter(date__gt=self.date).exists()
+
+ @property
+ def applied_label(self):
+ dct = dict(self.APPLIED_CHOICES)
+ if self.applied not in dct:
+ return "-"
+ return dct[self.applied]
+
+ @classmethod
+ def get_initial_from_find(cls, find, prefix="qa_"):
+ initial = {}
+ base_attrs = list(cls.OVERLOADED_FIELDS)
+ for attr in base_attrs:
+ initial[f"{prefix}{attr}"] = getattr(find, attr)
+ m2m_attrs = cls.OVERLOADED_M2M_FIELDS
+ for attr in m2m_attrs:
+ initial[f"{prefix}{attr}"] = list(
+ getattr(find, attr).all().values_list("id", flat=True)
+ )
+ return initial
+
+ def get_initial(self, prefix="qa_"):
+ initial = {}
+ base_attrs = ["pk", "date", "find_id", "statement_condition_type_id", "verification_officer_id",
+ "campaign_number", "report_number", "observations"]
+ for attr in base_attrs:
+ initial[attr] = getattr(self, attr)
+ for attr in self.OVERLOADED_FIELDS:
+ initial[f"{prefix}{attr}"] = getattr(self, attr)
+ m2m_attrs = list(self.OVERLOADED_M2M_FIELDS) + ["follow_up_actions"]
+ for attr in m2m_attrs:
+ initial[f"{prefix}{attr}"] = list(
+ getattr(self, attr).all().values_list("id", flat=True)
+ )
+ return initial
+
+ def _check_apply_validation(self):
+ if not self.pk:
+ # no previous state so apply immediatly
+ return True
+ # check previous applied is draft
+ return list(
+ self.__class__.objects.filter(pk=self.pk).values_list(
+ "applied", flat=True)
+ )[0] == "D"
+
+ def _create_associated_treatment(self, treatment_fields):
+ pass
+
+ def _create_initial_statementcondition(self):
+ """
+ Create a reference statement condition in order to get the diff
+ """
+ obj = self.__class__.objects.get(pk=self.pk)
+ obj.pk = None # duplicate
+ obj.applied = "V"
+ obj.initial = True
+ obj.last = False
+ obj.verification_officer = None
+ for k in ("campaign_number", "report_number", "observations"):
+ setattr(obj, k, "")
+ # reinit with find fields
+ for attr in self.OVERLOADED_FIELDS:
+ setattr(obj, attr, getattr(self.find, attr))
+ obj.save()
+ obj.follow_up_actions.clear()
+
+ for attr in self.OVERLOADED_M2M_FIELDS:
+ new_m2m = list(
+ sorted(
+ list(getattr(self.find, attr).values_list("id", flat=True))
+ )
+ )
+ m2m = list(
+ sorted(
+ list(getattr(obj, attr).values_list("id", flat=True))
+ )
+ )
+ if m2m == new_m2m:
+ continue
+ getattr(obj, attr).clear()
+ getattr(obj, attr).add(*new_m2m)
+
+ def apply_validation(self, treatment_fields=None):
+ """
+ Copy statement condition fields to the associated find
+ """
+ # create a reference condition if not available
+ if not self.__class__.objects.filter(initial=True, find=self.find).exists():
+ self._create_initial_statementcondition()
+
+ # treatment creation
+ if self.applied == "T":
+ find = self._create_associated_treatment(treatment_fields)
+ if find.pk != self.find_id: # new find is created after treatment
+ self.find, self.find_id = find, find.pk
+ self.__class__.objects.filter(pk=self.pk).update(find_id=find.pk)
+
+ # update find fields
+ for attr in self.OVERLOADED_FIELDS:
+ setattr(self.find, attr, getattr(self, attr))
+ for attr in self.OVERLOADED_M2M_FIELDS:
+ m2m = list(
+ sorted(
+ list(getattr(self.find, attr).values_list("id", flat=True))
+ )
+ )
+ new_m2m = list(
+ sorted(
+ list(getattr(self, attr).values_list("id", flat=True))
+ )
+ )
+ if m2m == new_m2m:
+ continue
+ getattr(self.find, attr).clear()
+ getattr(self.find, attr).add(*new_m2m)
+ # TODO: verify find history
+ self.find.save()
+
+ # update last field
+ self.__class__.objects.filter(find=self.find).exclude(pk=self.pk).update(
+ last=False)
+ # set last field and applied state
+ self.__class__.objects.filter(pk=self.pk).update(last=True, applied=self.applied)
+
+ def save(self, *args, **kwargs):
+ apply_validation = False
+ if self.applied in ("V", "T"):
+ apply_validation = self._check_apply_validation()
+ super().save(*args, **kwargs)
+ if apply_validation:
+ self.apply_validation()
+
+
class ExhibitionType(GeneralType):
treatment_file_type = models.ForeignKey(
TreatmentFileType,