#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2012-2025 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the file COPYING for details. from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from ishtar_common.utils import ugettext_lazy as _, pgettext, get_current_profile from ishtar_common.forms import reverse_lazy from ishtar_common.wizards import ( Wizard, DeletionWizard, SearchWizard, MultipleDeletionWizard, ) from archaeological_operations.wizards import OperationAdministrativeActWizard from archaeological_operations.models import AdministrativeAct from archaeological_context_records.models import ContextRecord from . import models class FindSearch(SearchWizard): model = models.Find class FindWizard(Wizard): model = models.Find wizard_done_window = reverse_lazy("show-find") redirect_url = "find_modification" wizard_templates = { "selecrecord-find_creation": "ishtar/wizard/wizard_find_creation.html" } def get_current_contextrecord(self): step = self.steps.current if not step: return if step.endswith("_creation"): # a context record has been selected main_form_key = "selecrecord-" + self.url_name try: idx = int(self.session_get_value(main_form_key, "pk")) return ContextRecord.objects.get(pk=idx) except (TypeError, ValueError, ObjectDoesNotExist): pass current_item = self.get_current_object() if current_item: base_finds = current_item.base_finds.all() if base_finds: return base_finds[0].context_record def get_current_basefinds(self): find = self.get_current_object() if not find: return [] return find.base_finds.all() def get_form_kwargs(self, step=None): kwargs = super(FindWizard, self).get_form_kwargs(step) if step not in ( "find-find_creation", "find-find_modification", "simplefind-find_modification", ): return kwargs kwargs["context_record"] = self.get_current_contextrecord() if step == "simplefind-find_modification": kwargs["base_finds"] = self.get_current_basefinds() return kwargs def get_context_data(self, form, **kwargs): """ Get the operation and context record "reminder" on top of wizard forms """ context = super(FindWizard, self).get_context_data(form, **kwargs) current_cr = self.get_current_contextrecord() if self.steps.current.startswith("selec"): profile = get_current_profile() if profile.no_context_button_id: context["no_context_cr"] = profile.no_context_button_id if not current_cr or self.steps.current.startswith("select-"): return context context["reminders"] = ( (_("Operation"), str(current_cr.operation)), (_("Context record"), str(current_cr)), ) return context def get_extra_model(self, dct, m2m, form_list): dct = super(FindWizard, self).get_extra_model(dct, m2m, form_list) dct["order"] = 1 if "pk" in dct and type(dct["pk"]) == ContextRecord: dct["base_finds__context_record"] = dct.pop("pk") return dct class FindModificationWizard(FindWizard): modification = True main_item_select_keys = ("selec-", "selecw-") filter_owns = { "selec-find_modification": ["pk"], "selecw-find_modification": ["pk"], } wizard_templates = { "simplefind-find_modification": "ishtar/wizard/wizard_simplefind.html", } class FindDeletionWizard(MultipleDeletionWizard): model = models.Find main_item_select_keys = ("selec-", "selecw-") fields = [ "label", "material_types", "datings", "find_number", "object_types", "description", "conservatory_states", "mark", "preservation_to_considers", "integrities", "remarkabilities", "volume", "weight", "length", "width", "height", "diameter", "comment", ] redirect_url = "find_deletion" class TreatmentSearch(SearchWizard): model = models.Treatment class TreatmentBase(Wizard): model = models.Treatment wizard_done_window = reverse_lazy("show-treatment") base_url = "" saved_args = {"treatment_type_list": []} redirect_url = "treatment_search" def get_current_finds(self): step = self.steps.current if not step: return find_form_key = "selecfind-" + self.base_url find_ids = self.session_get_value(find_form_key, "resulting_pk") try: return [ models.Find.objects.get(pk=int(find_id.strip())) for find_id in find_ids.split(",") ] except (TypeError, ValueError, AttributeError, ObjectDoesNotExist): pass def get_form_initial(self, step, data=None): initial = super(TreatmentBase, self).get_form_initial(step) base_step = "basetreatment-" + self.base_url if step != base_step: return initial finds = self.get_current_finds() if not finds: return initial locations = [find.container.location for find in finds if find.container] # no location or multiple locations if not locations or len(set(locations)) != 1: return initial if not initial: initial = {} default_location = locations[0] initial["location"] = default_location.id if default_location.organization: initial["organization"] = default_location.organization_id return initial def get_extra_model(self, dct, m2m, form_list): dct = super(TreatmentBase, self).get_extra_model(dct, m2m, form_list) dct["treatment_type_list"] = [] for k, v in m2m: if k == "treatment_type": if type(v) not in (list, tuple): v = [v] dct["treatment_type_list"] += v return dct class TreatmentWizard(TreatmentBase): basket_step = "basetreatment-treatment_creation" saved_args = {"items": [], "treatment_type_list": []} base_url = "treatment_creation" def get_form_kwargs(self, step, **kwargs): kwargs = super(TreatmentWizard, self).get_form_kwargs(step, **kwargs) if self.basket_step not in step: return kwargs kwargs["user"] = self.request.user return kwargs def get_extra_model(self, dct, m2m, form_list): """ Get items concerned by the treatment """ dct = super(TreatmentWizard, self).get_extra_model(dct, m2m, form_list) if "resulting_pk" in dct: dct["items"] = [] pks = dct.pop("resulting_pk") if isinstance(pks, models.Find): pks = [pks] if not isinstance(pks, (list, tuple)): pks = str(pks).split(",") for pk in pks: if isinstance(pk, models.Find): find = pk else: try: find = models.Find.objects.get(pk=pk) except models.Find.DoesNotExist: raise PermissionDenied dct["items"].append(find) if "basket" in dct: basket = dct.pop("basket") if basket.user.pk != dct["history_modifier"].pk: raise PermissionDenied dct["items"] = list(basket.items.all()) if "items" in dct: for find in dct["items"]: if "own" in self.current_right and not find.is_own( dct["history_modifier"] ): raise PermissionDenied return dct class TreatmentModificationWizard(TreatmentWizard): modification = True class TreatmentN1Wizard(TreatmentBase): saved_args = { "upstream_items": [], "resulting_find": None, "treatment_type_list": [], } base_url = "treatment_creation_n1" def _update_simple_initial_from_finds(self, initial, find, k): r_k = "resulting_" + k if getattr(find, k) and r_k in initial: # pop k when value is inconsistent between finds if initial[r_k] and initial[r_k] != getattr(find, k).pk: initial.pop(r_k) else: initial[r_k] = getattr(find, k).pk return initial def _update_multi_initial_from_finds(self, initial, find, k): r_k = "resulting_" + k for value in getattr(find, k + "s").all(): if value.pk not in initial[r_k]: initial[r_k].append(value.pk) return initial def _update_num_initial_from_finds(self, initial, find, k): r_k = "resulting_" + k if not getattr(find, k): return initial if initial[r_k] is None: initial[r_k] = 0 initial[r_k] += getattr(find, k) return initial def _update_char_initial_from_finds(self, initial, find, k, sep=" ; "): r_k = "resulting_" + k value = getattr(find, k) if not value: return initial value = value.strip() if not initial[r_k]: initial[r_k] = value else: # new value is entirely inside the current value if ( value == initial[r_k] or (value + sep) in initial[r_k] or (sep + value) in initial[r_k] ): return initial initial[r_k] += sep + value return initial def get_form_initial(self, step, data=None): initial = super(TreatmentN1Wizard, self).get_form_initial(step) if step != "resultingfind-treatment_creation_n1": return initial finds = self.get_current_finds() if not finds: return initial simple_key = ["material_type_quality"] multi_key = ["material_type", "object_type", "communicabilitie"] numeric_key = [ "find_number", "min_number_of_individuals", "length", "width", "height", "diameter", "circumference", "thickness", "volume", "weight", "clutter_long_side", "clutter_short_side", "clutter_height" ] desc_key = ["decoration", "inscription", "comment", "dating_comment", "description", "dimensions_comment"] char_key = ["manufacturing_place"] for k in simple_key + numeric_key + desc_key + char_key: initial["resulting_" + k] = None for k in multi_key: initial["resulting_" + k] = [] for find in finds: for k in simple_key: initial = self._update_simple_initial_from_finds(initial, find, k) for k in multi_key: initial = self._update_multi_initial_from_finds(initial, find, k) for k in numeric_key: initial = self._update_num_initial_from_finds(initial, find, k) for k in char_key: initial = self._update_char_initial_from_finds( initial, find, k, sep=" ; " ) for k in desc_key: initial = self._update_char_initial_from_finds( initial, find, k, sep="\n" ) for k in list(initial.keys()): if initial[k] is None: initial.pop(k) return initial def get_extra_model(self, dct, m2m, form_list): """ Get items concerned by the treatment """ dct = super(TreatmentN1Wizard, self).get_extra_model(dct, m2m, form_list) if "resulting_pk" not in dct: return dct dct["upstream_items"] = [] # manage upstream items pks = dct.pop("resulting_pk") if hasattr(pks, "split"): pks = pks.split(",") # string for pk in pks: if isinstance(pk, models.Find): find = pk else: try: find = models.Find.objects.get(pk=pk) except models.Find.DoesNotExist: raise PermissionDenied dct["upstream_items"].append(find) for find in dct["upstream_items"]: if "own" in self.current_right and not find.is_own(dct["history_modifier"]): raise PermissionDenied # extract data of the new find dct["resulting_find"] = {} for k in list(dct.keys()): if k.startswith("resulting_") and k != "resulting_find": dct["resulting_find"][k[len("resulting_") :]] = dct.pop(k) return dct class Treatment1NWizard(TreatmentBase): saved_args = { "upstream_item": None, "resulting_finds": None, "treatment_type_list": [], } base_url = "treatment_creation_1n" redirect_url = "find_modification" open_created_in_redirect = False def get_form_kwargs(self, step, **kwargs): kwargs = super(Treatment1NWizard, self).get_form_kwargs(step, **kwargs) if step != "resultingfind-treatment_creation_1n": return kwargs kwargs["user"] = self.request.user return kwargs def get_form_initial(self, step, data=None): initial = super(Treatment1NWizard, self).get_form_initial(step) if step != "resultingfinds-treatment_creation_1n": return initial finds = self.get_current_finds() if not finds: return initial lbl = finds[0].label initial["resultings_basket_name"] = str(_("Basket")) + " - " + lbl initial["resultings_label"] = lbl + "-" return initial def get_extra_model(self, dct, m2m, form_list): """ Get items concerned by the treatment """ dct = super(Treatment1NWizard, self).get_extra_model(dct, m2m, form_list) if "resulting_pk" not in dct: return dct # manage upstream item pk = dct.pop("resulting_pk") if isinstance(pk, models.Find): find = pk else: try: find = models.Find.objects.get(pk=pk) except models.Find.DoesNotExist: raise PermissionDenied dct["upstream_item"] = find if "own" in self.current_right and not find.is_own(dct["history_modifier"]): raise PermissionDenied # extract attributes to generate the new find dct["resulting_finds"] = {} for k in list(dct.keys()): if k.startswith("resultings_"): dct["resulting_finds"][k[len("resultings_") :]] = dct.pop(k) messages.add_message( self.request, messages.INFO, str( _( 'The new basket: "{}" have been created with the ' "resulting items. This search have been pinned." ) ).format(dct["resulting_finds"]["basket_name"]), ) self.request.session["pin-search-find"] = '{}="{}"'.format( str(pgettext("key for text search", "basket")), dct["resulting_finds"]["basket_name"], ) self.request.session["find"] = "" return dct class TreatmentDeletionWizard(DeletionWizard): model = models.Treatment wizard_confirm = "ishtar/wizard/wizard_treatement_deletion.html" fields = [ "label", "reference", "year", "index", "treatment_types", "location", "person", "organization", "external_id", "comment", "description", "goal", "start_date", "end_date", "container", "administrative_act", ] redirect_url = "treatment_deletion" wizard_templates = { "final-treatment_deletion": "ishtar/wizard/wizard_delete_associated_to_admin_act.html" } class TreatmentAdministrativeActWizard(OperationAdministrativeActWizard): model = models.Treatment current_obj_slug = "administrativeacttreatment" ref_object_key = "treatment" redirect_url = "treatment_admacttreatment_modification" def get_reminder(self): return class TreatmentEditAdministrativeActWizard(TreatmentAdministrativeActWizard): model = AdministrativeAct modification = True def get_associated_item(self, dct): return self.get_current_object().treatment class TreatmentFileSearch(SearchWizard): model = models.TreatmentFile class TreatmentFileWizard(Wizard): model = models.TreatmentFile wizard_done_window = reverse_lazy("show-treatmentfile") redirect_url = "treatmentfile_modification" def get_formated_data_for_associated_basket_id(self, value): try: value = str(models.FindBasket.objects.get(pk=value)) except models.FindBasket.DoesNotExist: value = "" return value class TreatmentFileModificationWizard(TreatmentFileWizard): modification = True class TreatmentFileDeletionWizard(MultipleDeletionWizard): model = models.TreatmentFile fields = [ "name", "internal_reference", "external_id", "year", "index", "type", "in_charge", "reception_date", "creation_date", "end_date", "comment", "administrative_act", ] redirect_url = "treatmentfile_deletion" wizard_templates = { "final-treatmentfile_deletion": "ishtar/wizard/wizard_delete_associated_to_admin_act.html" } class TreatmentFileAdministrativeActWizard(OperationAdministrativeActWizard): model = models.TreatmentFile current_obj_slug = "administrativeacttreatmentfile" ref_object_key = "treatment_file" redirect_url = "treatmentfle_admacttreatmentfle_modification" def get_reminder(self): form_key = "selec-" + self.url_name if self.url_name.endswith("_administrativeactop"): # modification and deletion are suffixed with '_modification' # and '_deletion' so it is creation pk = self.session_get_value(form_key, "pk") try: return ( ( _("Treatment request"), str(models.TreatmentFile.objects.get(pk=pk)), ), ) except models.TreatmentFile.DoesNotExist: return else: admin_id = self.session_get_value(form_key, "pk") try: admin = AdministrativeAct.objects.get(pk=admin_id) if not admin.operation: return return ((_("Operation"), str(admin.operation)),) except AdministrativeAct.DoesNotExist: return class TreatmentFileEditAdministrativeActWizard(TreatmentFileAdministrativeActWizard): model = AdministrativeAct modification = True def get_associated_item(self, dct): return self.get_current_object().treatment_file class FindBasketSearch(SearchWizard): model = models.FindBasket class FindBasketWizard(Wizard): model = models.FindBasket wizard_done_window = reverse_lazy("show-findbasket") redirect_url = "find_basket_modification" class FindBasketEditWizard(FindBasketWizard): modification = True alt_is_own_method = "get_write_query_owns" def get_form_kwargs(self, step, **kwargs): kwargs = super(FindBasketEditWizard, self).get_form_kwargs(step, **kwargs) if step != "basket-find_basket_modification": return kwargs kwargs["basket_pk"] = self.get_current_object().pk kwargs["user"] = self.request.user return kwargs class FindBasketDeletionWizard(DeletionWizard): model = models.FindBasket redirect_url = "find_basket_deletion" wizard_confirm = "ishtar/wizard/wizard_findbasket_deletion.html" class ExhibitionSearch(SearchWizard): model = models.Exhibition template_name = "ishtar/wizard/exhibition.html" def get_context_data(self, form, **kwargs): data = super().get_context_data(form, **kwargs) data["permission_add_exhibition"] = self.request.user.has_perm("ishtar_common.add_exhibition") return data