diff options
| -rw-r--r-- | archaeological_finds/forms_treatments.py | 15 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 9 | ||||
| -rw-r--r-- | archaeological_finds/models_treatments.py | 129 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/blocks/sheet_statement_condition.html | 4 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 23 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/forms/error.html | 9 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/forms/success.html | 2 | ||||
| -rw-r--r-- | ishtar_common/urls.py | 5 | ||||
| -rw-r--r-- | ishtar_common/views.py | 4 |
9 files changed, 158 insertions, 42 deletions
diff --git a/archaeological_finds/forms_treatments.py b/archaeological_finds/forms_treatments.py index 53a928fd8..fa9a03118 100644 --- a/archaeological_finds/forms_treatments.py +++ b/archaeological_finds/forms_treatments.py @@ -1258,7 +1258,7 @@ class QAStatementCondition(ManageOldType, forms.Form): "qa_integrities", "qa_conservatory_states", "qa_museum_marking_type", - "qa_museum_inventory_marking_presence" + "qa_museum_inventory_marking_presence", ] associated_models = { "qa_alterations": models.AlterationType, @@ -1271,6 +1271,7 @@ class QAStatementCondition(ManageOldType, forms.Form): "statement_condition_type_id": models.StatementConditionType, "qa_museum_marking_type": models.MarkingType, "qa_museum_inventory_marking_presence": models.InventoryMarkingPresence, + "qa_museum_inventory_conformity_id": models.InventoryConformity, } HEADERS = {} pk = forms.IntegerField(required=False, widget=forms.HiddenInput) @@ -1323,9 +1324,18 @@ class QAStatementCondition(ManageOldType, forms.Form): widget=widgets.Select2Multiple, required=False, ) + qa_mark_text = forms.CharField(label=_("Transcription of the marking"), required=False) + qa_mark = forms.CharField(label=_("Marking details"), widget=forms.Textarea, required=False) + qa_museum_inventory_conformity_id = forms.ChoiceField( + label=_("Conformity with inventory"), required=False, choices=[] + ) + qa_museum_conformity_comment = forms.CharField( + label=_("Comment on conformity"), widget=forms.Textarea, required=False + ) - HEADERS["find_number"] = FormHeader(_("Dimensions / Quantities")) + HEADERS["qa_find_number"] = FormHeader(_("Dimensions / Quantities")) qa_find_number = forms.IntegerField(label=_("Number of remains"), required=False) + qa_museum_inventory_quantity = forms.IntegerField(label=_("Inventory quantity"), required=False, max_value=32000) qa_museum_observed_quantity = forms.IntegerField( label=_("Observed quantity"), required=False) qa_length = FloatField( @@ -1422,6 +1432,7 @@ class QAStatementCondition(ManageOldType, forms.Form): FieldType("statement_condition_type_id", models.StatementConditionType, empty_first=False), FieldType("qa_treatment_emergency_id", models.TreatmentEmergencyType), + FieldType("qa_museum_inventory_conformity_id", models.InventoryConformity), ] PROFILE_FILTER = { "museum": ["qa_museum_observed_quantity", "qa_museum_marking_type", diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index dc62789d5..7e789a578 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -2855,12 +2855,17 @@ class Find( first_statement_condition = previous = q.all()[0] else: # if no initial is set get diff from the find previous = self + # remove "_id" on fields + overloaded_fields = [ + attr[:-len("_id")] if attr.endswith("_id") else attr + for attr in StatementCondition.OVERLOADED_FIELDS + ] for idx, state in enumerate(statement_conditions_list): diff = {} initial = {} if not idx else None - for field in StatementCondition._meta.get_fields(): + for field in StatementCondition.get_model_fields(): attr = field.name - if attr in StatementCondition.OVERLOADED_FIELDS: + if attr in overloaded_fields: previous_value = getattr(previous, attr) or None value = getattr(state, attr) or None if previous_value == value: diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py index 5a0fdde32..4a687e56e 100644 --- a/archaeological_finds/models_treatments.py +++ b/archaeological_finds/models_treatments.py @@ -18,6 +18,7 @@ # See the file COPYING for details. import datetime +import itertools # nosec: used to build a controlled XML import lxml.etree # nosec import lxml.builder # nosec @@ -1714,6 +1715,11 @@ class StatementCondition( "dimensions_comment", "treatment_emergency_id", "find_number", + "mark_text", + "mark", + "museum_inventory_conformity_id", + "museum_conformity_comment", + "museum_inventory_quantity", "museum_observed_quantity", ) OVERLOADED_M2M_FIELDS = ( @@ -1753,38 +1759,8 @@ class StatementCondition( 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", @@ -1803,6 +1779,7 @@ class StatementCondition( ) museum_conformity_comment = models.TextField(_("Comment on conformity"), blank=True, default="") + # find field - dimensions find_number = models.IntegerField(_("Number of remains"), blank=True, null=True) museum_inventory_quantity = models.PositiveSmallIntegerField( @@ -1814,11 +1791,11 @@ class StatementCondition( 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 (g)"), blank=True, null=True) + thickness = models.FloatField(_("Thickness (cm)"), 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) + volume = models.FloatField(_("Volume (l)"), blank=True, null=True) + weight = models.FloatField(_("Weight (g)"), blank=True, null=True) clutter_long_side = models.FloatField( _("Clutter - long side (cm)"), blank=True, null=True ) @@ -1831,6 +1808,38 @@ class StatementCondition( dimensions_comment = models.TextField( _("Dimensions comment"), blank=True, default="" ) + # conservation + integrities = models.ManyToManyField( + "IntegrityType", + verbose_name=_("Integrity"), + blank=True, + ) + alterations = models.ManyToManyField( + "AlterationType", verbose_name=_("Alteration"), blank=True + ) + alteration_causes = models.ManyToManyField( + "AlterationCauseType", verbose_name=_("Alteration cause"), 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_states = models.ManyToManyField( + "ConservatoryState", + verbose_name=_("Conservatory states"), + blank=True, + ) + conservatory_comment = models.TextField( + _("Conservatory comment"), blank=True, default="" + ) # documents documents = models.ManyToManyField( Document, @@ -1863,6 +1872,25 @@ class StatementCondition( lbl += f" - {self.find}" return lbl + @classmethod + def get_model_fields(cls): + """ + Get fields sorted by declaration + """ + # django's Field class defines custom __eq__ and __lt__ methods that + # reference an internal creation_counter. This allows to construct a + # list of all the fields on the model, and simply use sorted to get + # them in the order. + sortable_private_fields = [ + f for f in cls._meta.private_fields + if isinstance(f, Field) + ] + return sorted( + itertools.chain( + cls._meta.concrete_fields, sortable_private_fields, + cls._meta.many_to_many) + ) + def pk_str(self): return str(self.pk) @@ -1935,6 +1963,7 @@ class StatementCondition( obj.initial = True obj.last = False obj.verification_officer = None + obj.treatment = None for k in ("campaign_number", "report_number", "observations"): setattr(obj, k, "") # reinit with find fields @@ -1994,6 +2023,38 @@ class StatementCondition( # set last field and applied state self.__class__.objects.filter(pk=self.pk).update(last=True, applied=self.applied) + def delete(self, using=None, keep_parents=False) -> bool: + self._error = None + find_id, initial = self.find_id, self.initial + q_has_statements = self.__class__.objects.filter( + find_id=find_id, initial=False) + if initial and q_has_statements.count(): + # don't delete initial if there is statement condition + self._error = _("Initial state cannot be deleted if there is other statement condition.") + return + nb, __ = super().delete(using=using, keep_parents=keep_parents) + if not nb: + self._error = _("An unknow error occured.") + return + if initial: # no more statements conditions associated + return True + if not q_has_statements.count(): + # no current statement condition left for this find + # delete initial + q_initial = self.__class__.objects.filter( + find_id=find_id, initial=True) + if q_initial.count(): + q_initial.all()[0].delete() + return True + + last = q_has_statements.order_by("-date").all()[0] + if last.last: + return True # last is the last + last.last = True + last.save() + q_has_statements.exclude(pk=last.pk).update(last=False) + return True + def save(self, *args, **kwargs): apply_validation = False if self.applied in ("V", "T"): diff --git a/archaeological_finds/templates/ishtar/blocks/sheet_statement_condition.html b/archaeological_finds/templates/ishtar/blocks/sheet_statement_condition.html index 23667852c..c994f3888 100644 --- a/archaeological_finds/templates/ishtar/blocks/sheet_statement_condition.html +++ b/archaeological_finds/templates/ishtar/blocks/sheet_statement_condition.html @@ -19,11 +19,9 @@ <a class="wait-button btn btn-success" href="/document/create/?statementcondition={{statement_condition.pk}}" title="Ajouter un document / une image"> <i class="fa fa-plus"></i> doc./image </a> - {% if statement_condition.applied == "D" %} - <a class="btn btn-danger wait-button" href="#" data-target="{% url 'find-statement-condition-delete' statement_condition.pk %}" title="{% trans 'Delete' %}"> + <a class="btn-qa btn btn-danger" href="#" data-target="{% url 'find-statement-condition-delete' statement_condition.pk %}" title="{% trans 'Delete' %}"> <i class="fa fa-trash" aria-hidden="true"></i> </a> - {% endif %} </div> </div> {% endif %} diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index cde528a05..a805fb64b 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -19,6 +19,7 @@ from collections import OrderedDict import json +import urllib.parse from django.conf import settings from django.core.exceptions import PermissionDenied @@ -1631,4 +1632,24 @@ def statement_condition_form(request, find_pk, statement_condition_pk=None, "url": reverse(url, args=url_args) }) -statement_condition_delete_form = statement_condition_form + +def statement_condition_delete_form( + request, statement_condition_pk=None, current_right=None): + try: + item = models.StatementCondition.objects.get(pk=statement_condition_pk) + except models.StatementCondition.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.find): + raise PermissionDenied() + if item.delete(): + return HttpResponseRedirect(reverse("success")) + error = "" + if hasattr(item, "_error") and item._error: + error = urllib.parse.quote_plus(str(item._error)) + return HttpResponseRedirect( + reverse("error") + f"?message={error}") diff --git a/ishtar_common/templates/ishtar/forms/error.html b/ishtar_common/templates/ishtar/forms/error.html new file mode 100644 index 000000000..7bedbabf2 --- /dev/null +++ b/ishtar_common/templates/ishtar/forms/error.html @@ -0,0 +1,9 @@ +{% extends "ishtar/forms/success.html" %} +{% load i18n inline_formset table_form %} +{% block main_content %} +{% if message %} +{{ message }} +{% else %} +{% trans "An error has been encountered. It may be necessary to refresh the table/sheet." %} +{% endif %} +{% endblock %} diff --git a/ishtar_common/templates/ishtar/forms/success.html b/ishtar_common/templates/ishtar/forms/success.html index a3885a813..21bd27dcb 100644 --- a/ishtar_common/templates/ishtar/forms/success.html +++ b/ishtar_common/templates/ishtar/forms/success.html @@ -29,11 +29,13 @@ $(document).ready(function(){ </button> </div> <div class="modal-body form-row"> + {% block main_content %} {% if message %} {{ message }} {% else %} {% trans "Changes made successfully. It may be necessary to refresh the table/sheet." %} {% endif %} + {% endblock %} </div> </div> </div> diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 827e85a7a..201e4fdbc 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -414,6 +414,11 @@ urlpatterns = [ views.SuccessView.as_view(), name="success", ), + re_path( + r"^error(?:/(?P<context>[a-z-]+)(?:/(?P<arg>[0-9a-z-|]+))?)?/$", + views.ErrorView.as_view(), + name="error", + ), ] menu = Menu(None) diff --git a/ishtar_common/views.py b/ishtar_common/views.py index dbaf8626b..03ab3f3f4 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -3258,6 +3258,10 @@ class SuccessView(TemplateView): return data +class ErrorView(SuccessView): + template_name = "ishtar/forms/error.html" + + class BookmarkList( SearchQueryMixin, JSONResponseMixin, LoginRequiredMixin, TemplateView ): |
