diff --git a/api/urls/v1/urls.py b/api/urls/v1/urls.py index 4c92510..37cd52f 100644 --- a/api/urls/v1/urls.py +++ b/api/urls/v1/urls.py @@ -12,7 +12,11 @@ from api.views.v1.views import EmaAPIViewV1, EcoAccountAPIViewV1, CompensationAP app_name = "v1" urlpatterns = [ path("intervention/", InterventionAPIViewV1.as_view(), name="intervention"), + path("intervention/", InterventionAPIViewV1.as_view(), name="intervention"), path("compensation/", CompensationAPIViewV1.as_view(), name="compensation"), + path("compensation/", CompensationAPIViewV1.as_view(), name="compensation"), path("ecoaccount/", EcoAccountAPIViewV1.as_view(), name="ecoaccount"), + path("ecoaccount/", EcoAccountAPIViewV1.as_view(), name="ecoaccount"), path("ema/", EmaAPIViewV1.as_view(), name="ema"), + path("ema/", EmaAPIViewV1.as_view(), name="ema"), ] diff --git a/api/utils/v1/__init__.py b/api/utils/serializer/__init__.py similarity index 100% rename from api/utils/v1/__init__.py rename to api/utils/serializer/__init__.py diff --git a/api/utils/serializer.py b/api/utils/serializer/serializer.py similarity index 67% rename from api/utils/serializer.py rename to api/utils/serializer/serializer.py index eb7dc60..a2b2b4b 100644 --- a/api/utils/serializer.py +++ b/api/utils/serializer/serializer.py @@ -5,12 +5,15 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 24.01.22 """ +import json from abc import abstractmethod +from django.contrib.gis import geos +from django.contrib.gis.geos import GEOSGeometry + class AbstractModelAPISerializer: model = None - user = None lookup = None properties_data = None @@ -74,3 +77,30 @@ class AbstractModelAPISerializer: entry = self.model.objects.get(**self.lookup) serialized_data = self.model_to_geo_json(entry) return serialized_data + + @abstractmethod + def create_model_from_json(self, json_model, user): + """ Creates a new instance from given json data + + Args: + json_model (dict): JSON data + user (User): The performing user + + Returns: + + """ + raise NotImplementedError("Must be implemented in subclasses") + + def create_geometry_from_json(self, geojson) -> GEOSGeometry: + """ Creates a GEOSGeometry object based on the given geojson + + Args: + geojson (str|dict): The geojson as str or dict + + Returns: + geometry (GEOSGeometry) + """ + if isinstance(geojson, dict): + geojson = json.dumps(geojson) + geometry = geos.fromstr(geojson) + return geometry \ No newline at end of file diff --git a/api/utils/serializer/v1/__init__.py b/api/utils/serializer/v1/__init__.py new file mode 100644 index 0000000..71a67bb --- /dev/null +++ b/api/utils/serializer/v1/__init__.py @@ -0,0 +1,7 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 24.01.22 + +""" diff --git a/api/utils/v1/compensation.py b/api/utils/serializer/v1/compensation.py similarity index 92% rename from api/utils/v1/compensation.py rename to api/utils/serializer/v1/compensation.py index 557ae13..44d61c7 100644 --- a/api/utils/v1/compensation.py +++ b/api/utils/serializer/v1/compensation.py @@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 24.01.22 """ -from api.utils.v1.serializer import AbstractModelAPISerializerV1 +from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1 from compensation.models import Compensation @@ -31,4 +31,4 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1): 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["deadlines"] = self.deadlines_to_json(entry.deadlines.all()) \ No newline at end of file diff --git a/api/utils/v1/ecoaccount.py b/api/utils/serializer/v1/ecoaccount.py similarity index 95% rename from api/utils/v1/ecoaccount.py rename to api/utils/serializer/v1/ecoaccount.py index 0aab2bb..2491eb1 100644 --- a/api/utils/v1/ecoaccount.py +++ b/api/utils/serializer/v1/ecoaccount.py @@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 24.01.22 """ -from api.utils.v1.serializer import AbstractModelAPISerializerV1 +from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1 from compensation.models import EcoAccount from intervention.models import Legal, Responsibility diff --git a/api/utils/v1/ema.py b/api/utils/serializer/v1/ema.py similarity index 93% rename from api/utils/v1/ema.py rename to api/utils/serializer/v1/ema.py index 66e6b40..e8f2228 100644 --- a/api/utils/v1/ema.py +++ b/api/utils/serializer/v1/ema.py @@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 24.01.22 """ -from api.utils.v1.serializer import AbstractModelAPISerializerV1 +from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1 from ema.models import Ema from intervention.models import Responsibility diff --git a/api/utils/serializer/v1/intervention.py b/api/utils/serializer/v1/intervention.py new file mode 100644 index 0000000..b3ab207 --- /dev/null +++ b/api/utils/serializer/v1/intervention.py @@ -0,0 +1,88 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 24.01.22 + +""" +from django.db import transaction +from django.db.models import QuerySet + +from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1 +from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID, \ + CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID +from intervention.models import Intervention, Responsibility, Legal +from konova.models import Geometry +from konova.tasks import celery_update_parcels +from user.models import UserActionLogEntry + + +class InterventionAPISerializerV1(AbstractModelAPISerializerV1): + model = Intervention + + def compensations_to_json(self, qs: QuerySet): + return list( + qs.values( + "id", "identifier", "title" + ) + ) + + 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()) + + def create_model_from_json(self, json_model, user): + with transaction.atomic(): + # Create geometry + json_geom = self.create_geometry_from_json(json_model) + geometry = Geometry() + geometry.geom = json_geom + + # Create linked objects + obj = Intervention() + resp = Responsibility() + legal = Legal() + created = UserActionLogEntry.get_created_action(user, comment="API Import") + obj.legal = legal + obj.created = created + obj.geometry = geometry + obj.responsible = resp + + # Fill in data to objects + properties = json_model["properties"] + obj.identifier = obj.generate_new_identifier() + obj.title = properties["title"] + obj.responsible.registration_office = self.konova_code_from_json( + properties["responsible"]["registration_office"], + CODELIST_REGISTRATION_OFFICE_ID + ) + obj.responsible.registration_file_number = properties["responsible"]["registration_file_number"] + obj.responsible.conservation_office = self.konova_code_from_json( + properties["responsible"]["conservation_office"], + CODELIST_CONSERVATION_OFFICE_ID, + ) + obj.responsible.conservation_file_number = properties["responsible"]["conservation_file_number"] + obj.responsible.handler = properties["responsible"]["handler"] + + obj.legal.registration_date = properties["legal"]["registration_date"] + obj.legal.binding_date = properties["legal"]["binding_date"] + obj.legal.process_type = self.konova_code_from_json( + properties["legal"]["process_type"], + CODELIST_PROCESS_TYPE_ID, + ) + laws = [self.konova_code_from_json(law, CODELIST_LAW_ID) for law in properties["legal"]["laws"]] + obj.legal.laws.set(laws) + + obj.responsible.save() + obj.geometry.save() + obj.legal.save() + obj.save() + + obj.users.add(user) + + celery_update_parcels.delay(geometry.id) + + return obj.id diff --git a/api/utils/v1/serializer.py b/api/utils/serializer/v1/serializer.py similarity index 91% rename from api/utils/v1/serializer.py rename to api/utils/serializer/v1/serializer.py index 0bdef78..984038d 100644 --- a/api/utils/v1/serializer.py +++ b/api/utils/serializer/v1/serializer.py @@ -10,7 +10,7 @@ import json from django.db.models import QuerySet -from api.utils.serializer import AbstractModelAPISerializer +from api.utils.serializer.serializer import AbstractModelAPISerializer from codelist.models import KonovaCode from intervention.models import Responsibility, Legal @@ -53,6 +53,22 @@ class AbstractModelAPISerializerV1(AbstractModelAPISerializer): "short_name": konova_code.short_name, } + def konova_code_from_json(self, json_str, code_list_identifier): + """ Returns a konova code instance + + Args: + json_str (str): The value for the code (atom id) + code_list_identifier (str): From which konova code list this code is supposed to be from + + Returns: + + """ + code = KonovaCode.objects.get( + atom_id=json_str, + code_lists__in=[code_list_identifier] + ) + return code + def responsible_to_json(self, responsible: Responsibility): """ Serializes Responsibility model into json diff --git a/api/utils/v1/intervention.py b/api/utils/v1/intervention.py deleted file mode 100644 index 76f08b1..0000000 --- a/api/utils/v1/intervention.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Author: Michel Peltriaux -Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany -Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 24.01.22 - -""" - -from django.db.models import QuerySet - -from api.utils.v1.serializer import AbstractModelAPISerializerV1 -from intervention.models import Intervention - - -class InterventionAPISerializerV1(AbstractModelAPISerializerV1): - model = Intervention - - def compensations_to_json(self, qs: QuerySet): - return list( - qs.values( - "id", "identifier", "title" - ) - ) - - 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()) \ No newline at end of file diff --git a/api/views/v1/views.py b/api/views/v1/views.py index 688d4e0..f62aa27 100644 --- a/api/views/v1/views.py +++ b/api/views/v1/views.py @@ -5,12 +5,14 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 21.01.22 """ +import json + from django.http import JsonResponse, HttpRequest -from api.utils.v1.compensation import CompensationAPISerializerV1 -from api.utils.v1.ecoaccount import EcoAccountAPISerializerV1 -from api.utils.v1.ema import EmaAPISerializerV1 -from api.utils.v1.intervention import InterventionAPISerializerV1 +from api.utils.serializer.v1.compensation import CompensationAPISerializerV1 +from api.utils.serializer.v1.ecoaccount import EcoAccountAPISerializerV1 +from api.utils.serializer.v1.ema import EmaAPISerializerV1 +from api.utils.serializer.v1.intervention import InterventionAPISerializerV1 from api.views.views import AbstractModelAPIView @@ -19,7 +21,7 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): """ - def get(self, request: HttpRequest, id): + def get(self, request: HttpRequest, id=None): """ Handles the GET request Performs the fetching and serialization of the data @@ -32,12 +34,23 @@ class AbstractModelAPIViewV1(AbstractModelAPIView): """ try: + if id is None: + raise AttributeError("No id provided") self.serializer.prepare_lookup(id, self.user) data = self.serializer.fetch_and_serialize() except Exception as e: return self.return_error_response(e, 500) return JsonResponse(data) + def post(self, request: HttpRequest, id=None): + try: + body = request.body.decode("utf-8") + body = json.loads(body) + created_id = self.serializer.create_model_from_json(body, self.user) + except Exception as e: + return self.return_error_response(e, 500) + return JsonResponse({"id": created_id}) + class InterventionAPIViewV1(AbstractModelAPIViewV1): serializer = InterventionAPISerializerV1 diff --git a/api/views/views.py b/api/views/views.py index 4316fb9..9608aec 100644 --- a/api/views/views.py +++ b/api/views/views.py @@ -8,6 +8,7 @@ Created on: 21.01.22 from django.http import JsonResponse from django.views import View +from django.views.decorators.csrf import csrf_exempt from api.models import APIUserToken from api.settings import KSP_TOKEN_HEADER_IDENTIFIER @@ -36,6 +37,7 @@ class AbstractModelAPIView(View): super().__init__(*args, **kwargs) self.serializer = self.serializer() + @csrf_exempt def dispatch(self, request, *args, **kwargs): try: # Fetch the proper user from the given request header token