diff options
| -rw-r--r-- | archaeological_finds/forms_treatments.py | 259 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 13 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/forms/qa_statement_condition_form.html | 14 | ||||
| -rw-r--r-- | archaeological_finds/urls.py | 24 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 61 |
5 files changed, 370 insertions, 1 deletions
diff --git a/archaeological_finds/forms_treatments.py b/archaeological_finds/forms_treatments.py index 2c6e49c46..1713bff1a 100644 --- a/archaeological_finds/forms_treatments.py +++ b/archaeological_finds/forms_treatments.py @@ -17,6 +17,7 @@ # See the file COPYING for details. +from copy import copy import datetime import logging from collections import OrderedDict @@ -26,7 +27,7 @@ from django import forms from django.core import validators from django.utils import timezone from ishtar_common.utils import gettext_lazy as _ -from ishtar_common.forms import FormHeader +from ishtar_common.forms import FloatField, FormHeader from archaeological_finds import models from archaeological_operations.forms import AdministrativeActForm, \ @@ -1174,3 +1175,259 @@ class QANewExhibitionLoanForm(IshtarForm): obj = models.TreatmentFile.objects.create(**values) self.exhibition.treatment_files.add(obj) return obj + + +class QAStatementCondition(ManageOldType, forms.Form): + form_label = _("Statement condition") + base_models = [ + "qa_alterations", + "qa_alteration_causes", + "qa_recommended_treatments", + "qa_integrities", + "qa_conservatory_states", + "qa_museum_marking_type", + "qa_museum_inventory_marking_presence" + ] + associated_models = { + "qa_alterations": models.AlterationType, + "qa_alteration_causes": models.AlterationCauseType, + "qa_treatment_emergency_id": models.TreatmentEmergencyType, + "qa_conservatory_states": models.ConservatoryState, + "qa_recommended_treatments": models.RecommendedTreatmentType, + "qa_integrities": models.IntegrityType, + "follow_up_action": models.FollowUpActionType, + "statement_condition_type_id": models.StatementConditionType, + "treatment_person": Person, + "qa_museum_marking_type": models.MarkingType, + "qa_museum_inventory_marking_presence": models.InventoryMarkingPresence, + } + TREATMENT_FIELDS = ("treatment_type", "treatment_person", "treatment_organization") + HEADERS = {} + pk = forms.IntegerField(required=False, widget=forms.HiddenInput) + HEADERS["date"] = FormHeader(_("Statement of condition")) + date = DateField(label=_("Date"), initial=datetime.date.today) + applied = forms.ChoiceField(label=_("Input status"), + choices=models.StatementCondition.APPLIED_CHOICES) + statement_condition_type_id = forms.ChoiceField(label=_("Type"), choices=[], + required=True) + verification_officer_id = forms.IntegerField( + label=_("Verification officer"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-person'), associated_model=Person, + new=True), + validators=[valid_id(Person)], required=False) + campaign_number = forms.CharField(label=_("Campaign/observation number"), + required=False) + report_number = forms.CharField(label=_("Report number"), required=False) + follow_up_action = forms.MultipleChoiceField( + label=_("Follow-up actions"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + observations = forms.CharField(label=_("Observations"), widget=forms.Textarea, + required=False) + + HEADERS["treatment_type"] = FormHeader(_("Treatment"), collapse=True) + treatment_type = forms.ChoiceField(label=_("Treatment type"), choices=[], + required=False) + treatment_person = forms.IntegerField( + label=_("Responsible"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-person'), associated_model=Person, + new=True), + validators=[valid_id(Person)], required=False) + treatment_organization = forms.IntegerField( + label=_("Organization"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-organization'), + associated_model=Organization, new=True), + validators=[valid_id(Organization)], required=False) + + HEADERS["qa_description"] = FormHeader(_("Find")) + qa_description = forms.CharField( + label=_("Description"), widget=forms.Textarea, required=False + ) + + HEADERS["qa_museum_inventory_marking_presence"] = FormHeader(_("Museum")) + qa_museum_inventory_marking_presence = forms.MultipleChoiceField( + label=_("Presence of inventory marking"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + qa_museum_marking_type = forms.MultipleChoiceField( + label=_("Type of marking"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + + HEADERS["find_number"] = FormHeader(_("Dimensions / Quantities")) + qa_find_number = forms.IntegerField(label=_("Number of remains"), required=False) + qa_museum_observed_quantity = forms.IntegerField( + label=_("Observed quantity"), required=False) + qa_length = FloatField( + label=_("Length (cm)"), widget=widgets.CentimeterMeterWidget, required=False + ) + qa_width = FloatField( + label=_("Width (cm)"), required=False, widget=widgets.CentimeterMeterWidget + ) + qa_height = FloatField( + label=_("Height (cm)"), widget=widgets.CentimeterMeterWidget, required=False + ) + qa_thickness = FloatField( + label=_("Thickness (cm)"), widget=widgets.CentimeterMeterWidget, required=False + ) + qa_diameter = FloatField( + label=_("Diameter (cm)"), widget=widgets.CentimeterMeterWidget, required=False + ) + qa_circumference = FloatField( + label=_("Circumference (cm)"), + widget=widgets.CentimeterMeterWidget, + required=False, + ) + qa_volume = FloatField(label=_("Volume (l)"), required=False) + qa_weight = FloatField( + label=_("Weight (g)"), widget=widgets.GramKilogramWidget, required=False + ) + qa_clutter_long_side = FloatField( + label=_("Clutter long side (cm)"), + widget=widgets.CentimeterMeterWidget, + required=False, + ) + qa_clutter_short_side = FloatField( + label=_("Clutter short side (cm)"), + widget=widgets.CentimeterMeterWidget, + required=False, + ) + qa_clutter_height = FloatField( + label=_("Clutter height (cm)"), + widget=widgets.CentimeterMeterWidget, + required=False, + ) + qa_dimensions_comment = forms.CharField( + label=_("Dimensions comment"), required=False, widget=forms.Textarea + ) + + HEADERS["qa_integrities"] = FormHeader(_("Preservation")) + qa_integrities = forms.MultipleChoiceField( + label=_("Integrity"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + qa_conservatory_states = forms.MultipleChoiceField( + label=_("Conservatory states"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + qa_alterations = forms.MultipleChoiceField( + label=_("Alteration"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + qa_alteration_causes = forms.MultipleChoiceField( + label=_("Alteration cause"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + qa_recommended_treatments = forms.MultipleChoiceField( + label=_("Recommended treatments"), + choices=[], + widget=widgets.Select2Multiple, + required=False, + ) + qa_treatment_emergency_id = forms.ChoiceField( + label=_("Treatment emergency"), choices=[], required=False + ) + qa_conservatory_comment = forms.CharField( + label=_("Conservatory comment"), required=False, widget=forms.Textarea + ) + + TYPES = [ + FieldType("qa_alterations", models.AlterationType, True), + FieldType("qa_alteration_causes", models.AlterationCauseType, True), + FieldType("qa_conservatory_states", models.ConservatoryState, is_multiple=True), + FieldType("follow_up_action", models.FollowUpActionType, is_multiple=True), + FieldType("qa_integrities", models.IntegrityType, is_multiple=True), + FieldType("qa_museum_marking_type", models.MarkingType, is_multiple=True), + FieldType("qa_museum_inventory_marking_presence", models.InventoryMarkingPresence, + is_multiple=True), + FieldType("qa_recommended_treatments", models.RecommendedTreatmentType, True), + FieldType("statement_condition_type_id", models.StatementConditionType, + empty_first=False), + FieldType("qa_treatment_emergency_id", models.TreatmentEmergencyType), + FieldType("treatment_type", models.TreatmentType, empty_first=False, + extra_args={'dct': {"is_statement_condition": True}}), + ] + PROFILE_FILTER = { + "museum": ["qa_museum_observed_quantity", "qa_museum_marking_type", + "qa_museum_inventory_marking_presence"] + } + + def __init__(self, *args, **kwargs): + self.current_item = kwargs.pop("current_item") + self.user = None + if 'user' in kwargs: + self.user = kwargs.pop('user') + super().__init__(*args, **kwargs) + if not self.user: + return + q = Person.objects.filter(ishtaruser__pk=self.user.pk) + if q.count(): + person = q.all()[0] + self.fields['treatment_person'].initial = person.pk + self.fields['verification_officer_id'].initial = person.pk + + def save(self): + data = copy(self.cleaned_data) + data["find_id"] = self.current_item.pk + applied = data.pop("applied") + follow_up_actions = data.pop("follow_up_action") + # remove m2m fields + m2m = {} + for m2m_attr in models.StatementCondition.OVERLOADED_M2M_FIELDS: + key = f"qa_{m2m_attr}" + if key in data: + m2m[m2m_attr] = data.pop(key) + # remove treatment fields + treatment = {} + for k in self.TREATMENT_FIELDS: + if k in data.keys(): + treatment[k] = data.pop(k) + # remove "qa_" prefix for remaining fields + for k in list(data.keys()): + if k.startswith("qa_"): + data[k[3:]] = data.pop(k) + created = None + if data.get("pk", None): + # update condition + created = True + pk = data.pop("pk") + models.StatementCondition.objects.filter(pk=pk).update(**data) + st = models.StatementCondition.objects.get(pk=pk) + else: + # new statement condition + st = models.StatementCondition.objects.create(**data) + st.follow_up_actions.clear() + for action in follow_up_actions: + st.follow_up_actions.add(action) + for m2m_attr, value in m2m.items(): + # update m2m + value = list(sorted([int(v) for v in value])) + if not created: + current_value = getattr(st, m2m_attr).values_list( + "pk", flat=True).order_by("pk") + if current_value == value: + continue + getattr(st, m2m_attr).clear() + getattr(st, m2m_attr).add(*value) + if applied == "D": + # draft exit + return + st.applied = applied + st.apply_validation(treatment) diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 9e9886635..f6b0c42b8 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -226,6 +226,8 @@ class TreatmentType(HierarchicalType): else: type_list = [idx for idx, __ in types] dct["available"] = True + if empty_first and type_list: + type_list = type_list[1:] q = cls.objects.filter(**dct).exclude(pk__in=type_list) for t in q.all(): if instances: @@ -2911,6 +2913,17 @@ class Find( True, ), ] + if can_edit_find and not is_locked: + actions += [ + ( + reverse("find-statement-condition-add", args=[self.pk]), + _("Add statement condition"), + "fa fa-plus", + _("statement condition"), + "", + True, + ), + ] can_add_geo = profile.mapping and self.can_do(request, "ishtar_common.add_geovectordata") if can_add_geo: diff --git a/archaeological_finds/templates/ishtar/forms/qa_statement_condition_form.html b/archaeological_finds/templates/ishtar/forms/qa_statement_condition_form.html new file mode 100644 index 000000000..5307d1db8 --- /dev/null +++ b/archaeological_finds/templates/ishtar/forms/qa_statement_condition_form.html @@ -0,0 +1,14 @@ +{% extends "ishtar/forms/qa_form.html" %} +{% load i18n inline_formset table_form %} + +{% block js_ready %} +$("#collapse-parent-traitement").hide(); +$("#id_applied").on("change", function(){ + if ($(this).val() == 'T'){ + $("#collapse-parent-traitement").show(); + $("#collapse-traitement").collapse('show'); + } else { + $("#collapse-parent-traitement").hide(); + } +}); +{% endblock %} diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 380d16cec..9a573198c 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -834,6 +834,30 @@ urlpatterns = [ )(views.autocomplete_basefind), name="autocomplete-basefind", ), + path( + "find-statement-condition/<int:find_pk>/", + check_permissions(["archaeological_finds.change_find", + "archaeological_finds.change_own_find"])( + views.statement_condition_form + ), + name="find-statement-condition-add", + ), + path( + "find-statement-condition/<int:find_pk>/<int:statement_condition_pk>/", + check_permissions(["archaeological_finds.change_find", + "archaeological_finds.change_own_find"])( + views.statement_condition_form + ), + name="find-statement-condition-modify", + ), + path( + "find-statement-condition-delete/<int:statement_condition_pk>/", + check_permissions(["archaeological_finds.change_find", + "archaeological_finds.change_own_find"])( + views.statement_condition_delete_form + ), + name="find-statement-condition-delete", + ), ] urlpatterns += get_urls_for_model(models.Find, views, own=True, autocomplete=True) diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index b289ac075..96b60587a 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -1520,3 +1520,64 @@ def qa_gam_export_views(request, pk, *args, **kwargs): ] dct["unavailable"] = True return render(request, "ishtar/forms/qa_form.html", dct) + + +def statement_condition_form(request, find_pk, statement_condition_pk=None, + current_right=None): + try: + item = models.Find.objects.get(pk=find_pk) + except models.Find.DoesNotExist: + raise Http404() + # permission not provided + if not current_right: + raise PermissionDenied() + # specificaly check permission for own item, otherwise already checked + if "_own_" in current_right: + if not request.user.has_perm(current_right, item): + raise PermissionDenied() + initial = {} + if statement_condition_pk: + try: + statement_condition = models.StatementCondition.objects.get( + pk=statement_condition_pk) + initial = statement_condition.get_initial() + except models.StatementCondition.DoesNotExist: + raise Http404() + else: + # redirect to edit if there is already a draft + q = models.StatementCondition.objects.filter(find_id=find_pk, applied="D") + if q.exists(): + sc_pk = q.values_list("pk", flat=True)[0] + return HttpResponseRedirect( + reverse("find-statement-condition-modify", args=[find_pk, sc_pk])) + initial = models.StatementCondition.get_initial_from_find(item) + if request.method == 'POST': + form = forms_treatments.QAStatementCondition( + request.POST, current_item=item, user=request.user.ishtaruser) + if form.is_valid(): + form.save() + return HttpResponseRedirect(reverse("success")) + else: + form = forms_treatments.QAStatementCondition( + current_item=item, user=request.user.ishtaruser, initial=initial) + button_name = _("Add") if not statement_condition_pk else _("Modify") + icon = "fa fa-plus" if not statement_condition_pk else "fa fa-pencil" + url = "find-statement-condition-" + url_args = [find_pk] + if statement_condition_pk: + url_args.append(statement_condition_pk) + url += "modify" + else: + url += "add" + return render( + request, + "ishtar/forms/qa_statement_condition_form.html", { + "page_name": _("Statement condition"), + "icon": icon, + "action_name": button_name, + "form": form, + "modal_size": "large", + "url": reverse(url, args=url_args) + }) + +statement_condition_delete_form = statement_condition_form |
