From 4c98949ba3e04db0b7fdb8bd4a7f096e35e43103 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Fri, 28 Jan 2022 15:44:09 +0100 Subject: [PATCH] #31 API Deductions * adds GET/POST/PUT/DELETE support for EcoAccountDeductions --- api/urls/v1/urls.py | 6 +- api/utils/serializer/v1/deduction.py | 163 ++++++++++++++++++++++++++ api/utils/serializer/v1/serializer.py | 39 +++--- api/views/v1/views.py | 5 + 4 files changed, 198 insertions(+), 15 deletions(-) create mode 100644 api/utils/serializer/v1/deduction.py diff --git a/api/urls/v1/urls.py b/api/urls/v1/urls.py index 313a43b9..24439a4e 100644 --- a/api/urls/v1/urls.py +++ b/api/urls/v1/urls.py @@ -7,7 +7,8 @@ Created on: 21.01.22 """ from django.urls import path -from api.views.v1.views import EmaAPIViewV1, EcoAccountAPIViewV1, CompensationAPIViewV1, InterventionAPIViewV1 +from api.views.v1.views import EmaAPIViewV1, EcoAccountAPIViewV1, CompensationAPIViewV1, InterventionAPIViewV1, \ + DeductionAPIViewV1 from api.views.views import InterventionCheckAPIView, InterventionAPIShareView, EcoAccountAPIShareView, EmaAPIShareView app_name = "v1" @@ -24,6 +25,9 @@ urlpatterns = [ path("ecoaccount/", EcoAccountAPIViewV1.as_view(), name="ecoaccount"), path("ecoaccount/", EcoAccountAPIViewV1.as_view(), name="ecoaccount"), + path("deduction/", DeductionAPIViewV1.as_view(), name="deduction"), + path("deduction/", DeductionAPIViewV1.as_view(), name="deduction"), + path("ema//share", EmaAPIShareView.as_view(), name="ema-share"), path("ema/", EmaAPIViewV1.as_view(), name="ema"), path("ema/", EmaAPIViewV1.as_view(), name="ema"), diff --git a/api/utils/serializer/v1/deduction.py b/api/utils/serializer/v1/deduction.py new file mode 100644 index 00000000..bfa2be65 --- /dev/null +++ b/api/utils/serializer/v1/deduction.py @@ -0,0 +1,163 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 28.01.22 + +""" +from django.core.exceptions import ObjectDoesNotExist + +from api.utils.serializer.v1.serializer import DeductableAPISerializerV1Mixin, AbstractModelAPISerializerV1 +from compensation.models import EcoAccountDeduction, EcoAccount +from intervention.models import Intervention +from konova.utils.message_templates import DATA_UNSHARED + + +class DeductionAPISerializerV1(AbstractModelAPISerializerV1, + DeductableAPISerializerV1Mixin): + model = EcoAccountDeduction + + def prepare_lookup(self, _id, user): + """ Updates lookup dict for db fetching + + Args: + _id (str): The object's id + user (User): The user requesting for + + Returns: + + """ + super().prepare_lookup(_id, user) + del self.lookup["users__in"] + del self.lookup["deleted__isnull"] + self.lookup["intervention__users__in"] = [user] + + def _model_to_geo_json(self, entry): + """ Adds the basic data + + Args: + entry (): The data entry + + Returns: + + """ + return self._single_deduction_to_json(entry) + + def create_model_from_json(self, json_model, user): + """ Creates a new entry for the model based on the contents of json_model + + Args: + json_model (dict): The json containing data + user (User): The API user + + Returns: + created_id (str): The id of the newly created Intervention entry + """ + acc_id = json_model["eco_account"] + intervention_id = json_model["intervention"] + surface = float(json_model["surface"]) + if surface <= 0: + raise ValueError("Surface must be > 0 m²") + + acc = EcoAccount.objects.get( + id=acc_id, + deleted__isnull=True, + ) + intervention = Intervention.objects.get( + id=intervention_id, + deleted__isnull=True, + ) + acc_shared = acc.is_shared_with(user) + intervention_shared = intervention.is_shared_with(user) + if not acc_shared: + raise PermissionError(f"Account: {DATA_UNSHARED}") + if not intervention_shared: + raise PermissionError(f"Intervention: {DATA_UNSHARED}") + + deduction = self.model.objects.create( + intervention=intervention, + account=acc, + surface=surface + ) + return str(deduction.id) + + def _get_obj_from_db(self, id, user): + """ Returns the object from database + + Fails if id not found or user does not have shared access + + Args: + id (str): The object's id + user (User): The API user + + Returns: + + """ + obj = self.model.objects.get( + id=id, + ) + shared_with = obj.intervention.is_shared_with(user) + if not shared_with: + raise PermissionError(f"Intervention: {DATA_UNSHARED}") + return obj + + def update_model_from_json(self, id, json_model, user): + """ Updates an entry for the model based on the contents of json_model + + Args: + id (str): The object's id + json_model (dict): The json containing data + user (User): The API user + + Returns: + created_id (str): The id of the newly created Intervention entry + """ + deduction = self._get_obj_from_db(id, user) + + acc_id = json_model["eco_account"] + intervention_id = json_model["intervention"] + surface = float(json_model["surface"]) + if surface <= 0: + raise ValueError("Surface must be > 0 m²") + + acc = EcoAccount.objects.get( + id=acc_id, + deleted__isnull=True, + ) + intervention = Intervention.objects.get( + id=intervention_id, + deleted__isnull=True, + ) + acc_shared = acc.is_shared_with(user) + intervention_shared = intervention.is_shared_with(user) + if not acc_shared: + raise PermissionError(f"Account: {DATA_UNSHARED}") + if not intervention_shared: + raise PermissionError(f"Intervention: {DATA_UNSHARED}") + + deduction.intervention = intervention + deduction.account = acc + deduction.surface = surface + deduction.save() + + return str(deduction.id) + + def delete_entry(self, id, user): + """ Deletes the entry + + Args: + id (str): The entry's id + user (User): The API user + + Returns: + + """ + entry = self._get_obj_from_db(id, user) + entry.intervention.mark_as_edited(user) + entry.delete() + try: + entry.refresh_from_db() + success = False + except ObjectDoesNotExist: + success = True + return success diff --git a/api/utils/serializer/v1/serializer.py b/api/utils/serializer/v1/serializer.py index 2b94d07f..66421708 100644 --- a/api/utils/serializer/v1/serializer.py +++ b/api/utils/serializer/v1/serializer.py @@ -132,6 +132,30 @@ class DeductableAPISerializerV1Mixin: class Meta: abstract = True + def _single_deduction_to_json(self, entry): + """ Serializes a single eco account deduction into json + + Args: + entry (EcoAccountDeduction): An EcoAccountDeduction + + Returns: + serialized_json (dict) + """ + return { + "id": entry.pk, + "eco_account": { + "id": entry.account.pk, + "identifier": entry.account.identifier, + "title": entry.account.title, + }, + "surface": entry.surface, + "intervention": { + "id": entry.intervention.pk, + "identifier": entry.intervention.identifier, + "title": entry.intervention.title, + } + } + def _deductions_to_json(self, qs: QuerySet): """ Serializes eco account deductions into json @@ -142,20 +166,7 @@ class DeductableAPISerializerV1Mixin: serialized_json (list) """ return [ - { - "id": entry.pk, - "eco_account": { - "id": entry.account.pk, - "identifier": entry.account.identifier, - "title": entry.account.title, - }, - "surface": entry.surface, - "intervention": { - "id": entry.intervention.pk, - "identifier": entry.intervention.identifier, - "title": entry.intervention.title, - } - } + self._single_deduction_to_json(entry) for entry in qs ] diff --git a/api/views/v1/views.py b/api/views/v1/views.py index 42447bc7..7789680f 100644 --- a/api/views/v1/views.py +++ b/api/views/v1/views.py @@ -10,6 +10,7 @@ import json from django.http import JsonResponse, HttpRequest from api.utils.serializer.v1.compensation import CompensationAPISerializerV1 +from api.utils.serializer.v1.deduction import DeductionAPISerializerV1 from api.utils.serializer.v1.ecoaccount import EcoAccountAPISerializerV1 from api.utils.serializer.v1.ema import EmaAPISerializerV1 from api.utils.serializer.v1.intervention import InterventionAPISerializerV1 @@ -125,3 +126,7 @@ class EcoAccountAPIViewV1(AbstractAPIViewV1): class EmaAPIViewV1(AbstractAPIViewV1): serializer = EmaAPISerializerV1 + + +class DeductionAPIViewV1(AbstractAPIViewV1): + serializer = DeductionAPISerializerV1