Compare commits

...

3 Commits

Author SHA1 Message Date
1b3adc396f #31 API Deductions Tests
* adds tests for deduction API
2022-01-28 16:21:23 +01:00
c32911755a #31 API Deductions
* adds intervention check state reset to all deduction-changing API routes
2022-01-28 15:55:00 +01:00
4c98949ba3 #31 API Deductions
* adds GET/POST/PUT/DELETE support for EcoAccountDeductions
2022-01-28 15:44:09 +01:00
12 changed files with 325 additions and 25 deletions

View File

@ -0,0 +1,5 @@
{
"eco_account": "CHANGE_BEFORE_RUN!!!",
"surface": 500.0,
"intervention": "CHANGE_BEFORE_RUN!!!"
}

View File

@ -45,7 +45,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
self.assertIsNotNone(content.get("id", None), msg=response.content)
def test_create_intervention(self):
""" Tests api creation of bare minimum interventions
""" Tests api creation
Returns:
@ -57,7 +57,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
self._test_create_object(url, post_body)
def test_create_compensation(self):
""" Tests api creation of bare minimum interventions
""" Tests api creation
Returns:
@ -80,7 +80,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
self._test_create_object(url, post_body)
def test_create_eco_account(self):
""" Tests api creation of bare minimum interventions
""" Tests api creation
Returns:
@ -92,7 +92,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
self._test_create_object(url, post_body)
def test_create_ema(self):
""" Tests api creation of bare minimum interventions
""" Tests api creation
Returns:
@ -103,3 +103,20 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
post_body = json.load(fp=json_file)
self._test_create_object(url, post_body)
def test_create_deduction(self):
""" Tests api creation
Returns:
"""
self.intervention.share_with(self.superuser)
self.eco_account.share_with(self.superuser)
url = reverse("api:v1:deduction")
json_file_path = "api/tests/v1/create/deduction_create_post_body.json"
with open(json_file_path) as json_file:
post_body = json.load(fp=json_file)
post_body["intervention"] = str(self.intervention.id)
post_body["eco_account"] = str(self.eco_account.id)
self._test_create_object(url, post_body)

View File

@ -8,6 +8,7 @@ Created on: 28.01.22
import json
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
from api.tests.v1.share.test_api_sharing import BaseAPIV1TestCase
@ -93,3 +94,25 @@ class APIV1DeleteTestCase(BaseAPIV1TestCase):
url = reverse("api:v1:ema", args=(str(test_ema.id),))
self._test_delete_object(test_ema, url)
def test_delete_deduction(self):
""" Tests api creation of bare minimum interventions
Returns:
"""
test_deduction = self.create_dummy_deduction()
test_deduction.intervention.share_with(self.superuser)
url = reverse("api:v1:deduction", args=(str(test_deduction.id),))
response = self._run_delete_request(url)
content = json.loads(response.content)
self.assertEqual(response.status_code, 200, msg=response.content)
self.assertTrue(content.get("success", False), msg=response.content)
try:
test_deduction.refresh_from_db()
self.fail("Deduction is not deleted from db!")
except ObjectDoesNotExist:
pass

View File

@ -38,6 +38,9 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
content = json.loads(response.content)
geojson = content[str(obj.id)]
self.assertEqual(response.status_code, 200, msg=response.content)
return geojson
def _assert_geojson_format(self, geojson):
try:
geojson["type"]
geojson["coordinates"]
@ -49,7 +52,6 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
props["modified_on"]
except KeyError as e:
self.fail(e)
return geojson
def test_get_intervention(self):
""" Tests api GET
@ -60,6 +62,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
self.intervention.share_with(self.superuser)
url = reverse("api:v1:intervention", args=(str(self.intervention.id),))
geojson = self._test_get_object(self.intervention, url)
self._assert_geojson_format(geojson)
try:
props = geojson["properties"]
props["responsible"]
@ -89,6 +92,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
url = reverse("api:v1:compensation", args=(str(self.compensation.id),))
geojson = self._test_get_object(self.compensation, url)
self._assert_geojson_format(geojson)
try:
props = geojson["properties"]
props["is_cef"]
@ -114,6 +118,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
url = reverse("api:v1:ecoaccount", args=(str(self.eco_account.id),))
geojson = self._test_get_object(self.eco_account, url)
self._assert_geojson_format(geojson)
try:
props = geojson["properties"]
props["deductable_surface"]
@ -142,6 +147,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
url = reverse("api:v1:ema", args=(str(self.ema.id),))
geojson = self._test_get_object(self.ema, url)
self._assert_geojson_format(geojson)
try:
props = geojson["properties"]
props["responsible"]
@ -155,3 +161,27 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
except KeyError as e:
self.fail(e)
def test_get_deduction(self):
""" Tests api GET
Returns:
"""
self.deduction.intervention.share_with(self.superuser)
url = reverse("api:v1:deduction", args=(str(self.deduction.id),))
_json = self._test_get_object(self.deduction, url)
try:
_json["id"]
_json["eco_account"]
_json["eco_account"]["id"]
_json["eco_account"]["identifier"]
_json["eco_account"]["title"]
_json["surface"]
_json["intervention"]
_json["intervention"]["id"]
_json["intervention"]["identifier"]
_json["intervention"]["title"]
except KeyError as e:
self.fail(e)

View File

@ -0,0 +1,5 @@
{
"eco_account": "CHANGE_BEFORE_RUN!!!",
"surface": 523400.0,
"intervention": "CHANGE_BEFORE_RUN!!!"
}

View File

@ -161,3 +161,26 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
self.assertEqual(len(put_props["before_states"]), self.ema.before_states.count())
self.assertEqual(len(put_props["after_states"]), self.ema.after_states.count())
self.assertEqual(len(put_props["deadlines"]), self.ema.deadlines.count())
def test_update_deduction(self):
""" Tests api update
Returns:
"""
self.deduction.intervention.share_with(self.superuser)
self.deduction.account.share_with(self.superuser)
url = reverse("api:v1:deduction", args=(str(self.deduction.id),))
json_file_path = "api/tests/v1/update/deduction_update_put_body.json"
with open(json_file_path) as json_file:
put_body = json.load(fp=json_file)
put_body["intervention"] = str(self.deduction.intervention.id)
put_body["eco_account"] = str(self.deduction.account.id)
self._test_update_object(url, put_body)
self.deduction.refresh_from_db()
self.assertEqual(put_body["intervention"], str(self.deduction.intervention.id))
self.assertEqual(put_body["eco_account"], str(self.deduction.account.id))
self.assertEqual(put_body["surface"], self.deduction.surface)

View File

@ -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/<id>", EcoAccountAPIViewV1.as_view(), name="ecoaccount"),
path("ecoaccount/", EcoAccountAPIViewV1.as_view(), name="ecoaccount"),
path("deduction/<id>", DeductionAPIViewV1.as_view(), name="deduction"),
path("deduction/", DeductionAPIViewV1.as_view(), name="deduction"),
path("ema/<id>/share", EmaAPIShareView.as_view(), name="ema-share"),
path("ema/<id>", EmaAPIViewV1.as_view(), name="ema"),
path("ema/", EmaAPIViewV1.as_view(), name="ema"),

View File

@ -0,0 +1,166 @@
"""
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
)
deduction.intervention.mark_as_edited(user)
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()
deduction.intervention.mark_as_edited(user)
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

View File

@ -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
]

View File

@ -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

View File

@ -360,19 +360,21 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
self.eco_account.recorded = rec_action
self.eco_account.share_with_list([self.superuser])
self.eco_account.save()
num_all_deducs = EcoAccountDeduction.objects.count()
# Run the request
self.client_user.post(new_url, post_data)
# Expect the deduction to be created, since all constraints are fulfilled
self.assertEqual(1, self.eco_account.deductions.count())
self.assertEqual(1, EcoAccountDeduction.objects.count())
self.assertEqual(num_all_deducs + 1, EcoAccountDeduction.objects.count())
# Make sure the deduction contains the expected data
deduction = EcoAccountDeduction.objects.first()
deduction = EcoAccountDeduction.objects.get(
account=self.eco_account,
intervention=self.intervention
)
self.assertEqual(deduction.surface, test_surface)
self.assertEqual(deduction.intervention, self.intervention)
self.assertEqual(deduction.account, self.eco_account)
# Return deduction for further usage in tests
return deduction

View File

@ -16,7 +16,7 @@ from django.test import TestCase, Client
from django.urls import reverse
from codelist.models import KonovaCode
from compensation.models import Compensation, CompensationState, CompensationAction, EcoAccount
from compensation.models import Compensation, CompensationState, CompensationAction, EcoAccount, EcoAccountDeduction
from intervention.models import Legal, Responsibility, Intervention
from konova.management.commands.setup_data import GROUPS_DATA
from konova.models import Geometry
@ -54,6 +54,7 @@ class BaseTestCase(TestCase):
cls.compensation = cls.create_dummy_compensation()
cls.eco_account = cls.create_dummy_eco_account()
cls.ema = cls.create_dummy_ema()
cls.deduction = cls.create_dummy_deduction()
cls.create_dummy_states()
cls.create_dummy_action()
cls.codes = cls.create_dummy_codes()
@ -194,6 +195,14 @@ class BaseTestCase(TestCase):
)
return ema
@classmethod
def create_dummy_deduction(cls):
return EcoAccountDeduction.objects.create(
account=cls.create_dummy_eco_account(),
intervention=cls.create_dummy_intervention(),
surface=100,
)
@classmethod
def create_dummy_states(cls):
""" Creates an intervention which can be used for tests