#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2012-2017 É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. import logging from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.http import Http404 from django.shortcuts import render from django.urls import reverse from ishtar_common.utils import ugettext_lazy as _ from archaeological_files.models import File from archaeological_operations import models from .forms import GenerateDocForm from ishtar_common.forms import reverse_lazy from ishtar_common.models import get_current_profile from ishtar_common.wizards import ( Wizard, ClosingWizard, DeletionWizard, SearchWizard, MultipleDeletionWizard, ) logger = logging.getLogger(__name__) class OperationSearch(SearchWizard): model = models.Operation class OperationWizard(Wizard): model = models.Operation relations_step_key = "relations" # step including the current(s) town(s) town_step_keys = ["towns-", "townsgeneral-"] town_input_id = "town" # input id of the current(s) town(s) multi_towns = False # true if current town are multi valued towns_formset = True # true if towns are managed with formset wizard_done_window = reverse_lazy("show-operation") redirect_url = "operation_modification" def get_template_names(self): templates = super(OperationWizard, self).get_template_names() current_step = self.steps.current if current_step.startswith(self.relations_step_key): templates = ["ishtar/wizard/relations_wizard.html"] + templates return templates def get_current_file(self): step = self.steps.current if not step: return file_form_key = "general-" + self.url_name if self.url_name == "operation_creation": file_form_key = "filechoice-" + self.url_name file_id = self.session_get_value(file_form_key, "associated_file") try: idx = int(file_id) current_file = File.objects.get(pk=idx) return current_file except (TypeError, ValueError, ObjectDoesNotExist): pass def get_reminder(self): archaeological_file = self.get_current_file() if archaeological_file: return ((_("Archaeological file"), str(archaeological_file)),) def get_context_data(self, form, **kwargs): """ Return extra context for templates """ context = super(OperationWizard, self).get_context_data(form, **kwargs) step = self.steps.current # reminder of the current file reminder = self.get_reminder() if reminder: context["reminders"] = reminder return context def get_towns(self): """ Get available towns """ towns = [] file = self.get_current_file() if not file: return -1 try: towns = [(town.pk, str(town)) for town in file.towns.all()] except (ValueError, ObjectDoesNotExist): pass return sorted(towns, key=lambda x: x[1]) def get_form(self, step=None, data=None, files=None): """ Manage specifics fields """ if data: data = data.copy() else: data = None if not step: step = self.steps.current form = super(OperationWizard, self).get_form(step, data, files) return form def get_formated_datas(self, forms): """ Show a specific warning if no archaeological file is provided """ datas = super(OperationWizard, self).get_formated_datas(forms) # if the general town form is used the advertisement is relevant has_no_af = [ form.prefix for form in forms if form.prefix == "townsgeneral-operation" ] and True if has_no_af: datas = [ [ _( "Warning: No Archaeological File is provided. " "If you have forget it return to the first step." ), [], ] ] + datas return datas def get_form_initial(self, step, data=None): initial = super(OperationWizard, self).get_form_initial(step) if step == "general-operation_creation": initial.update(self._copy_from_associated_field()) return initial def __copy_fields(self, item, keys): initial = {} for orig_keys, dest_key in keys: value, c_value = None, item for orig_key in orig_keys: c_value = getattr(c_value, orig_key) if not c_value: break else: value = c_value if not value: continue initial[dest_key] = value return initial def _copy_from_associated_field(self): initial = {} file = self.get_current_file() if not file: return initial keys = ( (("in_charge", "pk"), "in_charge"), (("name",), "common_name"), (("total_surface",), "surface"), ) initial.update(self.__copy_fields(file, keys)) if "town" not in initial: initial["town"] = [idx for idx, __ in self.get_towns()] if file.is_preventive(): return initial keys = ( (("scientist", "pk"), "scientist"), (("requested_operation_type", "pk"), "operation_type"), (("organization", "pk"), "operator"), ) initial.update(self.__copy_fields(file, keys)) return initial class OperationModificationWizard(OperationWizard): modification = True filter_owns = {"selec-operation_modification": ["pk"]} def get_form_kwargs(self, step, **kwargs): kwargs = super(OperationModificationWizard, self).get_form_kwargs( step, **kwargs ) if step != "relations-operation_modification": return kwargs kwargs["left_record"] = self.get_current_object() return kwargs class OperationClosingWizard(ClosingWizard): model = models.Operation fields = [ "year", "operation_code", "operation_type", "associated_file", "in_charge", "scientist", "start_date", "excavation_end_date", "comment", "towns", "remains", "administrative_act", ] class OperationDeletionWizard(MultipleDeletionWizard): model = models.Operation fields = OperationClosingWizard.fields filter_owns = {"selec-operation_deletion": ["pks"]} redirect_url = "operation_deletion" wizard_templates = { "final-operation_deletion": "ishtar/wizard/wizard_delete_associated_to_admin_act.html" } class OperationAdministrativeActWizard(OperationWizard): edit = False wizard_done_window = reverse_lazy("show-administrativeact") current_obj_slug = "administrativeactop" ref_object_key = "operation" redirect_url = "operation_administrativeactop_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 operation_id = self.session_get_value(form_key, "pk") try: return ( ( _("Operation"), str(models.Operation.objects.get(pk=operation_id)), ), ) except models.Operation.DoesNotExist: return else: admin_id = self.session_get_value(form_key, "pk") try: admin = models.AdministrativeAct.objects.get(pk=admin_id) if not admin.operation: return return ((_("Operation"), str(admin.operation)),) except models.AdministrativeAct.DoesNotExist: return def get_extra_model(self, dct, m2m, form_list): dct["history_modifier"] = self.request.user return dct def get_context_data(self, form, **kwargs): # manage document generation context = super(OperationAdministrativeActWizard, self).get_context_data( form, **kwargs ) step = self.steps.current if step.startswith("final-"): general_form_key = "administrativeact-" + self.url_name act_type = None try: act_type = models.ActType.objects.get( pk=self.session_get_value(general_form_key, "act_type") ) except models.ActType.DoesNotExist: pass if act_type and act_type.associated_template.count(): context["extra_form"] = GenerateDocForm( choices=act_type.associated_template.all() ) return context def get_associated_item(self, dct): return self.get_current_object() def save_model(self, dct, m2m, whole_associated_models, form_list, return_object): dct["history_modifier"] = self.request.user if "pk" in dct: dct.pop("pk") if self.edit: admact = self.get_current_object() for k in dct: if hasattr(admact, k): setattr(admact, k, dct[k]) else: associated_item = self.get_associated_item(dct) if not associated_item: logger.warning("Admin act save: no associated model") return self.render(form_list[-1]) dct[self.ref_object_key] = associated_item admact = models.AdministrativeAct(**dct) admact.save() dct["item"] = admact # check if a doc generation is required keys = [ self.storage.prefix, "step_data", "final-" + self.url_name, "doc_generation", ] r = self.request.session for k in keys: if k in r and r[k]: r = r[k] else: break if k == keys[-1]: # the whole list as been traversed wizard_done_window = str(self.wizard_done_window) if wizard_done_window: dct["wizard_done_window"] = wizard_done_window # redirect to the generated doc if r and type(r) in (tuple, list) and r[0]: dct["redirect"] = reverse( "generatedoc-administrativeactop", args=[admact.pk, r[0]] ) # make the new object a default ishtaruser = ( self.request.user.ishtaruser if hasattr(self.request.user, "ishtaruser") else None ) if ( ishtaruser and ishtaruser.current_profile and ishtaruser.current_profile.auto_pin ): self.request.session[self.current_obj_slug] = str(admact.pk) self.request.session[self.get_object_name(admact)] = str(admact.pk) res = render(self.request, "ishtar/wizard/wizard_done.html", dct) return res def get_form_initial(self, step, data=None): if not self.edit: return {} initial = super().get_form_initial(step) return initial class OperationEditAdministrativeActWizard(OperationAdministrativeActWizard): model = models.AdministrativeAct edit = True def get_associated_item(self, dct): return self.get_current_object().operation ######## # Site # ######## class SiteLabel(object): SITE_KEY = "" def get_label(self): return get_current_profile().get_site_label(self.SITE_KEY) class SiteSearch(SiteLabel, SearchWizard): SITE_KEY = "search" model = models.ArchaeologicalSite class SiteWizard(SiteLabel, Wizard): SITE_KEY = "new" model = models.ArchaeologicalSite wizard_done_window = reverse_lazy("show-site") redirect_url = "site_modification" class SiteModificationWizard(SiteWizard): SITE_KEY = "modification" modification = True class SiteDeletionWizard(SiteLabel, MultipleDeletionWizard): SITE_KEY = "deletion" model = models.ArchaeologicalSite fields = models.ArchaeologicalSite.TABLE_COLS + ["operations"] redirect_url = "site_deletion" class AdministrativeActDeletionWizard(ClosingWizard): model = models.AdministrativeAct wizard_templates = { "final-operation_administrativeactop_deletion": "ishtar/wizard/wizard_adminact_deletion.html", "final-file_administrativeactfile_deletion": "ishtar/wizard/wizard_adminact_deletion.html", } fields = [ "act_type", "in_charge", "operator", "scientist", "signatory", "operation", "associated_file", "signature_date", "act_object", ] if settings.COUNTRY == "fr": fields += ["ref_sra"] def done(self, form_list, **kwargs): obj = self.get_current_object() obj.delete() return render(self.request, "ishtar/wizard/wizard_delete_done.html", {}) def is_preventive(form_name, model, type_key="operation_type", key=""): def func(self): request = self.request storage = self.storage if ( storage.prefix not in request.session or "step_data" not in request.session[storage.prefix] or form_name not in request.session[storage.prefix]["step_data"] or form_name + "-" + type_key not in request.session[storage.prefix]["step_data"][form_name] ): return False try: typ = request.session[storage.prefix]["step_data"][form_name][ form_name + "-" + type_key ] if type(typ) in (list, tuple): typ = typ[0] typ = int(typ) return model.is_preventive(typ, key) except ValueError: return False return func def is_not_preventive(form_name, model, type_key="operation_type", key=""): def func(self): return not is_preventive(form_name, model, type_key, key)(self) return func def is_judiciary(form_name, model, type_key="operation_type"): def func(self): request = self.request storage = self.storage if ( storage.prefix not in request.session or "step_data" not in request.session[storage.prefix] or form_name not in request.session[storage.prefix]["step_data"] or form_name + "-" + type_key not in request.session[storage.prefix]["step_data"][form_name] ): return False try: typ = request.session[storage.prefix]["step_data"][form_name][ form_name + "-" + type_key ] if type(typ) in (list, tuple): typ = typ[0] typ = int(typ) return model.is_judiciary(typ) except ValueError: return False return func def has_associated_file(form_name, file_key="associated_file", negate=False): def func(self): request = self.request storage = self.storage if ( storage.prefix not in request.session or "step_data" not in request.session[storage.prefix] or form_name not in request.session[storage.prefix]["step_data"] or form_name + "-" + file_key not in request.session[storage.prefix]["step_data"][form_name] ): return negate try: file_id = request.session[storage.prefix]["step_data"][form_name][ form_name + "-" + file_key ] if type(file_id) in (list, tuple): file_id = file_id[0] int(file_id) return not negate except ValueError: return negate return func