From 8d400b4ffe9cf2d74a5139ccd7cbd49879ce4e96 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Fri, 21 Jan 2022 18:34:01 +0100 Subject: [PATCH] #31 API basic implementation Cleanup * cleans code * reworks many code fragments into smaller methods and split into super class --- api/views/v1/compensation.py | 37 +++----- api/views/v1/ecoaccount.py | 43 +++------ api/views/v1/ema.py | 43 +++------ api/views/v1/intervention.py | 34 ++----- api/views/v1/{general.py => views.py} | 127 +++++++++++++++++++++++++- api/views/views.py | 50 +++++++--- 6 files changed, 206 insertions(+), 128 deletions(-) rename api/views/v1/{general.py => views.py} (53%) diff --git a/api/views/v1/compensation.py b/api/views/v1/compensation.py index 19b6246b..1ef49b98 100644 --- a/api/views/v1/compensation.py +++ b/api/views/v1/compensation.py @@ -5,25 +5,19 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 21.01.22 """ -import json -from django.http import HttpRequest, JsonResponse - -from api.views.v1.general import AbstractModelAPIViewV1 +from api.views.v1.views import AbstractModelAPIViewV1 from compensation.models import Compensation class APICompensationViewV1(AbstractModelAPIViewV1): model = Compensation - def get(self, request: HttpRequest, id): + def prepare_lookup(self, id): self.lookup["id"] = id del self.lookup["users__in"] self.lookup["intervention__users__in"] = [self.user] - data = self.fetch_and_serialize() - return JsonResponse(data) - def intervention_to_json(self, entry): return { "id": entry.pk, @@ -31,21 +25,12 @@ class APICompensationViewV1(AbstractModelAPIViewV1): "title": entry.title, } - def model_to_json(self, entry): - entry_json = { - "identifier": entry.identifier, - "title": entry.title, - "is_cef": entry.is_cef, - "is_coherence_keeping": entry.is_coherence_keeping, - "intervention": self.intervention_to_json(entry.intervention), - "before_states": self.compensation_state_to_json(entry.before_states.all()), - "after_states": self.compensation_state_to_json(entry.after_states.all()), - "actions": self.compensation_actions_to_json(entry.actions.all()), - "deadlines": self.deadlines_to_json(entry.deadlines.all()), - "modified_on": self.modified_on_to_json(entry), - "created_on": self.created_on_to_json(entry), - } - geom = entry.geometry.geom.geojson - geo_json = json.loads(geom) - geo_json["properties"] = entry_json - return geo_json \ No newline at end of file + def extend_properties_data(self, entry): + self.properties_data["is_cef"] = entry.is_cef + self.properties_data["is_coherence_keeping"] = entry.is_coherence_keeping + self.properties_data["intervention"] = self.intervention_to_json(entry.intervention) + self.properties_data["before_states"] = self.compensation_state_to_json(entry.before_states.all()) + self.properties_data["after_states"] = self.compensation_state_to_json(entry.after_states.all()) + self.properties_data["actions"] = self.compensation_actions_to_json(entry.actions.all()) + self.properties_data["deadlines"] = self.deadlines_to_json(entry.deadlines.all()) + diff --git a/api/views/v1/ecoaccount.py b/api/views/v1/ecoaccount.py index caa1f245..7339a0a5 100644 --- a/api/views/v1/ecoaccount.py +++ b/api/views/v1/ecoaccount.py @@ -5,11 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 21.01.22 """ -import json - -from django.http import JsonResponse, HttpRequest - -from api.views.v1.general import AbstractModelAPIViewV1 +from api.views.v1.views import AbstractModelAPIViewV1 from compensation.models import EcoAccount from intervention.models import Legal, Responsibility @@ -17,33 +13,16 @@ from intervention.models import Legal, Responsibility class APIEcoAccountViewV1(AbstractModelAPIViewV1): model = EcoAccount - def get(self, request: HttpRequest, id): - self.lookup["id"] = id - self.lookup["users__in"] = [self.user] - - data = self.fetch_and_serialize() - return JsonResponse(data) - - def model_to_json(self, entry): - entry_json = { - "identifier": entry.identifier, - "title": entry.title, - "deductable_surface": entry.deductable_surface, - "deductable_surface_available": entry.deductable_surface - entry.get_deductions_surface(), - "responsible": self.responsible_to_json(entry.responsible), - "legal": self.legal_to_json(entry.legal), - "before_states": self.compensation_state_to_json(entry.before_states.all()), - "after_states": self.compensation_state_to_json(entry.after_states.all()), - "actions": self.compensation_actions_to_json(entry.actions.all()), - "deadlines": self.deadlines_to_json(entry.deadlines.all()), - "deductions": self.deductions_to_json(entry.deductions.all()), - "modified_on": self.modified_on_to_json(entry), - "created_on": self.created_on_to_json(entry), - } - geom = entry.geometry.geom.geojson - geo_json = json.loads(geom) - geo_json["properties"] = entry_json - return geo_json + def extend_properties_data(self, entry): + self.properties_data["deductable_surface"] = entry.deductable_surface + self.properties_data["deductable_surface_available"] = entry.deductable_surface - entry.get_deductions_surface() + self.properties_data["responsible"] = self.responsible_to_json(entry.responsible) + self.properties_data["legal"] = self.legal_to_json(entry.legal) + self.properties_data["before_states"] = self.compensation_state_to_json(entry.before_states.all()) + self.properties_data["after_states"] = self.compensation_state_to_json(entry.after_states.all()) + self.properties_data["actions"] = self.compensation_actions_to_json(entry.actions.all()) + self.properties_data["deadlines"] = self.deadlines_to_json(entry.deadlines.all()) + self.properties_data["deductions"] = self.deductions_to_json(entry.deductions.all()) def legal_to_json(self, legal: Legal): return { diff --git a/api/views/v1/ema.py b/api/views/v1/ema.py index ba07e507..991d3824 100644 --- a/api/views/v1/ema.py +++ b/api/views/v1/ema.py @@ -5,37 +5,24 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 21.01.22 """ -import json - -from django.http import JsonResponse, HttpRequest - -from api.views.v1.ecoaccount import APIEcoAccountViewV1 +from api.views.v1.views import AbstractModelAPIViewV1 from ema.models import Ema +from intervention.models import Responsibility -class APIEmaViewV1(APIEcoAccountViewV1): +class APIEmaViewV1(AbstractModelAPIViewV1): model = Ema - def get(self, request: HttpRequest, id): - self.lookup["id"] = id - self.lookup["users__in"] = [self.user] - - data = self.fetch_and_serialize() - return JsonResponse(data) - - def model_to_json(self, entry): - entry_json = { - "identifier": entry.identifier, - "title": entry.title, - "responsible": self.responsible_to_json(entry.responsible), - "before_states": self.compensation_state_to_json(entry.before_states.all()), - "after_states": self.compensation_state_to_json(entry.after_states.all()), - "actions": self.compensation_actions_to_json(entry.actions.all()), - "deadlines": self.deadlines_to_json(entry.deadlines.all()), - "modified_on": self.modified_on_to_json(entry), - "created_on": self.created_on_to_json(entry), + def responsible_to_json(self, responsible: Responsibility): + return { + "conservation_office": self.konova_code_to_json(responsible.conservation_office), + "conservation_file_number": responsible.conservation_file_number, + "handler": responsible.handler, } - geom = entry.geometry.geom.geojson - geo_json = json.loads(geom) - geo_json["properties"] = entry_json - return geo_json \ No newline at end of file + + def extend_properties_data(self, entry): + self.properties_data["responsible"] = self.responsible_to_json(entry.responsible) + self.properties_data["before_states"] = self.compensation_state_to_json(entry.before_states.all()) + self.properties_data["after_states"] = self.compensation_state_to_json(entry.after_states.all()) + self.properties_data["actions"] = self.compensation_actions_to_json(entry.actions.all()) + self.properties_data["deadlines"] = self.deadlines_to_json(entry.deadlines.all()) diff --git a/api/views/v1/intervention.py b/api/views/v1/intervention.py index e8c329d7..d0977939 100644 --- a/api/views/v1/intervention.py +++ b/api/views/v1/intervention.py @@ -5,25 +5,15 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 21.01.22 """ -import json - from django.db.models import QuerySet -from django.http import HttpRequest, JsonResponse -from api.views.v1.general import AbstractModelAPIViewV1 +from api.views.v1.views import AbstractModelAPIViewV1 from intervention.models import Intervention class APIInterventionViewV1(AbstractModelAPIViewV1): model = Intervention - def get(self, request: HttpRequest, id): - self.lookup["id"] = id - self.lookup["users__in"] = [self.user] - - data = self.fetch_and_serialize() - return JsonResponse(data) - def compensations_to_json(self, qs: QuerySet): return list( qs.values( @@ -31,19 +21,9 @@ class APIInterventionViewV1(AbstractModelAPIViewV1): ) ) - def model_to_json(self, entry: Intervention): - entry_json = { - "identifier": entry.identifier, - "title": entry.title, - "responsible": self.responsible_to_json(entry.responsible), - "legal": self.legal_to_json(entry.legal), - "compensations": self.compensations_to_json(entry.compensations.all()), - "payments": self.payments_to_json(entry.payments.all()), - "deductions": self.deductions_to_json(entry.deductions.all()), - "modified_on": self.modified_on_to_json(entry), - "created_on": self.created_on_to_json(entry), - } - geom = entry.geometry.geom.geojson - geo_json = json.loads(geom) - geo_json["properties"] = entry_json - return geo_json \ No newline at end of file + def extend_properties_data(self, entry): + self.properties_data["responsible"] = self.responsible_to_json(entry.responsible) + self.properties_data["legal"] = self.legal_to_json(entry.legal) + self.properties_data["compensations"] = self.compensations_to_json(entry.compensations.all()) + self.properties_data["payments"] = self.payments_to_json(entry.payments.all()) + self.properties_data["deductions"] = self.deductions_to_json(entry.deductions.all()) diff --git a/api/views/v1/general.py b/api/views/v1/views.py similarity index 53% rename from api/views/v1/general.py rename to api/views/v1/views.py index b1343fea..7aaeb187 100644 --- a/api/views/v1/general.py +++ b/api/views/v1/views.py @@ -5,7 +5,10 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 21.01.22 """ +import json + from django.db.models import QuerySet +from django.http import JsonResponse, HttpRequest from api.views.views import AbstractModelAPIView from codelist.models import KonovaCode @@ -17,7 +20,69 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): """ + def get(self, request: HttpRequest, id): + """ Handles the GET request + + Performs the fetching and serialization of the data + + Args: + request (HttpRequest): The incoming request + id (str): The entries id + + Returns: + + """ + self.prepare_lookup(id) + + try: + data = self.fetch_and_serialize() + except Exception as e: + return self.return_error_response(e, 500) + return JsonResponse(data) + + def model_to_geo_json(self, entry): + """ Adds the basic data, which all elements hold + + Args: + entry (): The data entry + + Returns: + + """ + geom = entry.geometry.geom.geojson + geo_json = json.loads(geom) + self.properties_data = { + "id": entry.id, + "identifier": entry.identifier, + "title": entry.title, + "created_on": self.created_on_to_json(entry), + "modified_on": self.modified_on_to_json(entry), + } + self.extend_properties_data(entry) + geo_json["properties"] = self.properties_data + return geo_json + + def prepare_lookup(self, id): + """ Customizes lookup values for db filtering + + Args: + id (str): The entries id + + Returns: + + """ + self.lookup["id"] = id + self.lookup["users__in"] = [self.user] + def konova_code_to_json(self, konova_code: KonovaCode): + """ Serializes KonovaCode model into json + + Args: + konova_code (KonovaCode): The KonovaCode entry + + Returns: + serialized_json (dict) + """ return { "atom_id": konova_code.atom_id, "long_name": konova_code.long_name, @@ -25,6 +90,14 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): } def responsible_to_json(self, responsible: Responsibility): + """ Serializes Responsibility model into json + + Args: + responsible (Responsibility): The Responsibility entry + + Returns: + serialized_json (dict) + """ return { "registration_office": self.konova_code_to_json(responsible.registration_office), "registration_file_number": responsible.registration_file_number, @@ -34,6 +107,14 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): } def legal_to_json(self, legal: Legal): + """ Serializes Legal model into json + + Args: + legal (Legal): The Legal entry + + Returns: + serialized_json (dict) + """ return { "registration_date": legal.registration_date, "binding_date": legal.binding_date, @@ -48,7 +129,7 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): qs (QuerySet): A queryset of Payment entries Returns: - + serialized_json (list) """ return list(qs.values("amount", "due_on", "comment")) @@ -59,7 +140,7 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): qs (QuerySet): A queryset of EcoAccountDeduction entries Returns: - + serialized_json (list) """ return [ { @@ -80,6 +161,14 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): ] def compensation_state_to_json(self, qs: QuerySet): + """ Serializes compensation states into json + + Args: + qs (QuerySet): A queryset of CompensationState entries + + Returns: + serialized_json (list) + """ return [ { "biotope": self.konova_code_to_json(entry.biotope_type), @@ -89,6 +178,14 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): ] def compensation_actions_to_json(self, qs: QuerySet): + """ Serializes CompensationActions into json + + Args: + qs (QuerySet): A queryset of CompensationAction entries + + Returns: + serialized_json (list) + """ return [ { "action": self.konova_code_to_json(entry.action_type), @@ -100,6 +197,14 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): ] def deadlines_to_json(self, qs: QuerySet): + """ Serializes deadlines into json + + Args: + qs (QuerySet): A queryset of Deadline entries + + Returns: + serialized_json (list) + """ return list(qs.values( "type", "date", @@ -107,9 +212,25 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): )) def created_on_to_json(self, entry): + """ Serializes the created_on into json + + Args: + entry (BaseObject): The entry + + Returns: + created_on (timestamp) + """ return entry.created.timestamp def modified_on_to_json(self, entry): + """ Serializes the modified_on into json + + Args: + entry (BaseObject): The entry + + Returns: + modified_on (timestamp) + """ modified_on = entry.modified or entry.created modified_on = modified_on.timestamp - return modified_on \ No newline at end of file + return modified_on diff --git a/api/views/views.py b/api/views/views.py index f68a68ab..21b60a8e 100644 --- a/api/views/views.py +++ b/api/views/views.py @@ -25,6 +25,7 @@ class AbstractModelAPIView(View): model = None user = None lookup = None + properties_data = None class Meta: abstract = True @@ -38,8 +39,20 @@ class AbstractModelAPIView(View): super().__init__(*args, **kwargs) @abstractmethod - def model_to_json(self, entry): - """ Defines the returned json values of the model + def model_to_geo_json(self, entry): + """ Defines the model as geo json + + Args: + entry (): The found entry from the database + + Returns: + + """ + raise NotImplementedError("Must be implemented in subclasses") + + @abstractmethod + def extend_properties_data(self, entry): + """ Defines the 'properties' part of geo json Args: entry (): The found entry from the database @@ -57,20 +70,33 @@ class AbstractModelAPIView(View): Returns: serialized_data (dict) """ - qs = self.model.objects.filter(**self.lookup) - serialized_data = {} - for entry in qs: - serialized_data[str(entry.pk)] = self.model_to_json(entry) + entry = self.model.objects.get(**self.lookup) + serialized_data = self.model_to_geo_json(entry) return serialized_data def dispatch(self, request, *args, **kwargs): try: self.user = APIUserToken.get_user_from_token(request.headers.get(KSP_TOKEN_HEADER_IDENTIFIER, None)) except PermissionError as e: - return JsonResponse( - { - "error": e.__str__() - }, - status=403 - ) + return self.return_error_response(e, 403) return super().dispatch(request, *args, **kwargs) + + def return_error_response(self, error, status_code=500): + """ Returns an error as JsonReponse + + Args: + error (): The error/exception + status_code (): The desired status code + + Returns: + + """ + content = [error.__str__()] + if hasattr(error, "messages"): + content = error.messages + return JsonResponse( + { + "errors": content + }, + status=status_code + )