Compare commits

..

No commits in common. "a096b2a413ddfc8537105dd90e7d487edfe1679c" and "7535f008b79ca7a2f9e618c293d0fbeb4e360a35" have entirely different histories.

51 changed files with 771 additions and 1567 deletions

View File

@ -18,10 +18,10 @@ from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION
CODELIST_COMPENSATION_ACTION_DETAIL_ID
from compensation.models import CompensationDocument, EcoAccountDocument
from konova.contexts import BaseContext
from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm
from konova.forms import BaseModalForm, NewDocumentForm
from konova.models import DeadlineType
from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \
ADDED_COMPENSATION_ACTION
ADDED_COMPENSATION_ACTION, PAYMENT_ADDED
class NewPaymentForm(BaseModalForm):
@ -100,26 +100,10 @@ class NewPaymentForm(BaseModalForm):
def save(self):
pay = self.instance.add_payment(self)
self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_ADDED)
return pay
class RemovePaymentModalForm(RemoveModalForm):
""" Removing modal form for Payment
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
payment = None
def __init__(self, *args, **kwargs):
payment = kwargs.pop("payment", None)
self.payment = payment
super().__init__(*args, **kwargs)
def save(self):
self.instance.remove_payment(self)
class NewStateModalForm(BaseModalForm):
""" Form handling state related input
@ -235,40 +219,6 @@ class NewStateModalForm(BaseModalForm):
raise NotImplementedError
class RemoveCompensationStateModalForm(RemoveModalForm):
""" Removing modal form for CompensationState
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
state = None
def __init__(self, *args, **kwargs):
state = kwargs.pop("state", None)
self.state = state
super().__init__(*args, **kwargs)
def save(self):
self.instance.remove_state(self)
class RemoveCompensationActionModalForm(RemoveModalForm):
""" Removing modal form for CompensationAction
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
action = None
def __init__(self, *args, **kwargs):
action = kwargs.pop("action", None)
self.action = action
super().__init__(*args, **kwargs)
def save(self):
self.instance.remove_action(self)
class NewDeadlineModalForm(BaseModalForm):
""" Form handling deadline related input
@ -321,6 +271,7 @@ class NewDeadlineModalForm(BaseModalForm):
def save(self):
deadline = self.instance.add_deadline(self)
self.instance.mark_as_edited(self.user, self.request, ADDED_DEADLINE)
return deadline

View File

@ -76,3 +76,13 @@ class CompensationAction(BaseResource):
if choice[0] == self.unit:
return choice[1]
return None
def delete(self, user=None, *args, **kwargs):
from compensation.models import Compensation
if user:
comps = Compensation.objects.filter(
actions__id__in=[self.id]
).distinct()
for comp in comps:
comp.mark_as_edited(user, edit_comment=COMPENSATION_ACTION_REMOVED)
super().delete(*args, **kwargs)

View File

@ -21,8 +21,7 @@ from konova.models import BaseObject, AbstractDocument, Deadline, generate_docum
GeoReferencedMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, COMPENSATION_REMOVED_TEMPLATE, \
DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE, DEADLINE_REMOVED, ADDED_DEADLINE, \
COMPENSATION_ACTION_REMOVED, COMPENSATION_STATE_REMOVED, INTERVENTION_HAS_REVOCATIONS_TEMPLATE
DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE
from user.models import UserActionLogEntry
@ -72,24 +71,8 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
self.save()
self.deadlines.add(deadline)
self.mark_as_edited(user, edit_comment=ADDED_DEADLINE)
return deadline
def remove_deadline(self, form):
""" Removes a deadline from the abstract compensation
Args:
form (RemoveDeadlineModalForm): The form holding all relevant data
Returns:
"""
deadline = form.deadline
user = form.user
with transaction.atomic():
deadline.delete()
self.mark_as_edited(user, edit_comment=DEADLINE_REMOVED)
def add_action(self, form) -> CompensationAction:
""" Adds a new action to the compensation
@ -115,21 +98,6 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
self.actions.add(comp_action)
return comp_action
def remove_action(self, form):
""" Removes a CompensationAction from the abstract compensation
Args:
form (RemoveCompensationActionModalForm): The form holding all relevant data
Returns:
"""
action = form.action
user = form.user
with transaction.atomic():
action.delete()
self.mark_as_edited(user, edit_comment=COMPENSATION_ACTION_REMOVED)
def add_state(self, form, is_before_state: bool) -> CompensationState:
""" Adds a new compensation state to the compensation
@ -154,21 +122,6 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
self.after_states.add(state)
return state
def remove_state(self, form):
""" Removes a CompensationState from the abstract compensation
Args:
form (RemoveCompensationStateModalForm): The form holding all relevant data
Returns:
"""
state = form.state
user = form.user
with transaction.atomic():
state.delete()
self.mark_as_edited(user, edit_comment=COMPENSATION_STATE_REMOVED)
def get_surface_after_states(self) -> float:
""" Calculates the compensation's/account's surface
@ -331,6 +284,28 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
"""
return self.intervention.users.all()
def get_LANIS_link(self) -> str:
""" Generates a link for LANIS depending on the geometry
Returns:
"""
try:
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
x = geom.centroid.x
y = geom.centroid.y
zoom_lvl = 16
except AttributeError:
# If no geometry has been added, yet.
x = 1
y = 1
zoom_lvl = 6
return LANIS_LINK_TEMPLATE.format(
zoom_lvl,
x,
y,
)
def get_documents(self) -> QuerySet:
""" Getter for all documents of a compensation
@ -355,7 +330,7 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
"""
self.intervention.unrecord(user, request)
action = super().mark_as_edited(user, edit_comment=edit_comment)
action = super().mark_as_edited(user, edit_comment)
return action
def is_ready_for_publish(self) -> bool:
@ -368,26 +343,6 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
"""
return self.intervention.is_ready_for_publish()
def set_status_messages(self, request: HttpRequest):
""" Setter for different information that need to be rendered
Adds messages to the given HttpRequest
Args:
request (HttpRequest): The incoming request
Returns:
request (HttpRequest): The modified request
"""
if self.intervention.legal.revocations.exists():
messages.error(
request,
INTERVENTION_HAS_REVOCATIONS_TEMPLATE.format(self.intervention.legal.revocations.count()),
extra_tags="danger",
)
super().set_status_messages(request)
return request
class CompensationDocument(AbstractDocument):
"""

View File

@ -123,6 +123,28 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
return ret_val_total, ret_val_relative
def get_LANIS_link(self) -> str:
""" Generates a link for LANIS depending on the geometry
Returns:
"""
try:
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
x = geom.centroid.x
y = geom.centroid.y
zoom_lvl = 16
except AttributeError:
# If no geometry has been added, yet.
x = 1
y = 1
zoom_lvl = 6
return LANIS_LINK_TEMPLATE.format(
zoom_lvl,
x,
y,
)
def quality_check(self) -> EcoAccountQualityChecker:
""" Quality check

View File

@ -37,3 +37,8 @@ class Payment(BaseResource):
ordering = [
"-amount",
]
def delete(self, user=None, *args, **kwargs):
if user is not None:
self.intervention.mark_as_edited(user, edit_comment=PAYMENT_REMOVED)
super().delete(*args, **kwargs)

View File

@ -47,3 +47,14 @@ class CompensationState(UuidModel):
def __str__(self):
return f"{self.biotope_type} | {self.surface}"
def delete(self, user=None, *args, **kwargs):
from compensation.models import Compensation
if user:
comps = Compensation.objects.filter(
Q(before_states__id__in=[self.id]) |
Q(after_states__id__in=[self.id])
).distinct()
for comp in comps:
comp.mark_as_edited(user, edit_comment=COMPENSATION_STATE_REMOVED)
super().delete(*args, **kwargs)

View File

@ -31,11 +31,6 @@ class CompensationTable(BaseTable, TableRenderMixin):
orderable=True,
accessor="title",
)
d = tables.Column(
verbose_name=_("Parcel gmrkng"),
orderable=True,
accessor="geometry",
)
c = tables.Column(
verbose_name=_("Checked"),
orderable=True,
@ -85,17 +80,14 @@ class CompensationTable(BaseTable, TableRenderMixin):
Returns:
"""
context = {
"tooltip": _("Open {}").format(_("Intervention")),
"content": value,
"url": reverse("compensation:detail", args=(record.id,)),
"has_revocations": record.intervention.legal.revocations.exists()
}
html = render_to_string(
"table/revocation_warning_col.html",
context
html = ""
html += self.render_link(
tooltip=_("Open {}").format(_("Compensation")),
href=reverse("compensation:detail", args=(record.id,)),
txt=value,
new_tab=False,
)
return html
return format_html(html)
def render_c(self, value, record: Compensation):
""" Renders the checked column for a compensation
@ -123,28 +115,6 @@ class CompensationTable(BaseTable, TableRenderMixin):
)
return format_html(html)
def render_d(self, value, record: Compensation):
""" Renders the parcel district column for a compensation
Args:
value (str): The geometry
record (Compensation): The compensation record
Returns:
"""
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()
html = render_to_string(
"table/gmrkng_col.html",
{
"entries": parcels
}
)
return html
def render_r(self, value, record: Compensation):
""" Renders the registered column for a compensation
@ -203,20 +173,10 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
orderable=True,
accessor="title",
)
d = tables.Column(
verbose_name=_("Parcel gmrkng"),
orderable=True,
accessor="geometry",
)
av = tables.Column(
verbose_name=_("Available"),
orderable=True,
empty_values=[],
attrs={
"th": {
"class": "w-20",
}
}
)
r = tables.Column(
verbose_name=_("Recorded"),
@ -284,28 +244,6 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
return format_html(html)
def render_d(self, value, record: Compensation):
""" Renders the parcel district column for a compensation
Args:
value (str): The geometry
record (Compensation): The compensation record
Returns:
"""
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()
html = render_to_string(
"table/gmrkng_col.html",
{
"entries": parcels
}
)
return html
def render_r(self, value, record: EcoAccount):
""" Renders the recorded column for an eco account

View File

@ -1,7 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 07.02.22
"""

View File

@ -1,7 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 07.02.22
"""

View File

@ -1,194 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 27.10.21
"""
from django.urls import reverse
from django.test import Client
from compensation.tests.compensation.test_views import CompensationViewTestCase
from konova.settings import DEFAULT_GROUP
class EcoAccountViewTestCase(CompensationViewTestCase):
"""
These tests focus on proper returned views depending on the user's groups privileges and login status
EcoAccounts can inherit the same tests used for compensations.
"""
comp_state = None
comp_action = None
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
state = cls.create_dummy_states()
cls.eco_account.before_states.set([state])
cls.eco_account.after_states.set([state])
action = cls.create_dummy_action()
cls.eco_account.actions.set([action])
# Prepare urls
cls.index_url = reverse("compensation:acc:index", args=())
cls.new_url = reverse("compensation:acc:new", args=())
cls.new_id_url = reverse("compensation:acc:new-id", args=())
cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,))
cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,))
cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,))
cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,))
cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,))
cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,))
cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,))
cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,))
cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,))
cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,))
cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.id,))
def test_logged_in_no_groups_shared(self):
""" Check correct status code for all requests
Assumption: User logged in and has no groups and data is shared
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.eco_account.share_with_list([self.superuser])
# Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference
# to a user without access, since the important permissions are missing
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
]
fail_urls = [
self.new_url,
self.new_id_url,
self.log_url,
self.edit_url,
self.remove_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_logged_in_no_groups_unshared(self):
""" Check correct status code for all requests
Assumption: User logged in and has no groups and data is shared
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.eco_account.share_with_list([])
# Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference
# to a user having shared access, since all important permissions are missing
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
]
fail_urls = [
self.new_url,
self.new_id_url,
self.log_url,
self.edit_url,
self.remove_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_logged_in_default_group_shared(self):
""" Check correct status code for all requests
Assumption: User logged in, is default group member and data is shared
--> Default group necessary since all base functionalities depend on this group membership
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.eco_account.share_with_list([self.superuser])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_url,
self.new_id_url,
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_success(client, success_urls)
def test_logged_in_default_group_unshared(self):
""" Check correct status code for all requests
Assumption: User logged in, is default group member and data is NOT shared
--> Default group necessary since all base functionalities depend on this group membership
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
self.eco_account.share_with_list([])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_id_url,
self.new_url,
]
fail_urls = [
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_fail(client, fail_urls)
self.assert_url_success(client, success_urls)

View File

@ -1,231 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 11.11.21
"""
import datetime
from django.contrib.gis.geos import MultiPolygon
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
from compensation.models import EcoAccount
from konova.settings import ETS_GROUP, DEFAULT_GROUP
from konova.tests.test_views import BaseWorkflowTestCase
from user.models import UserAction
class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Add user to conservation office group and give shared access to the account
self.superuser.groups.add(self.groups.get(name=DEFAULT_GROUP))
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
self.eco_account.share_with_list([self.superuser])
def test_new(self):
""" Test the creation of an EcoAccount
Returns:
"""
# Prepare url and form data to be posted
new_url = reverse("compensation:acc:new")
test_id = self.create_dummy_string()
test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry()
test_deductable_surface = 1000
test_conservation_office = self.get_conservation_office_code()
post_data = {
"identifier": test_id,
"title": test_title,
"geom": test_geom.geojson,
"deductable_surface": test_deductable_surface,
"conservation_office": test_conservation_office.id
}
self.client_user.post(new_url, post_data)
try:
acc = EcoAccount.objects.get(
identifier=test_id
)
except ObjectDoesNotExist:
self.fail(msg="EcoAccount not created")
self.assertEqual(acc.identifier, test_id)
self.assertEqual(acc.title, test_title)
self.assert_equal_geometries(acc.geometry.geom, test_geom)
self.assertEqual(acc.log.count(), 1)
# Expect logs to be set
self.assertEqual(acc.log.count(), 1)
self.assertEqual(acc.log.first().action, UserAction.CREATED)
def test_edit(self):
""" Checks that the editing of an EcoAccount works
Returns:
"""
self.eco_account.share_with(self.superuser)
url = reverse("compensation:acc:edit", args=(self.eco_account.id,))
pre_edit_log_count = self.eco_account.log.count()
new_title = self.create_dummy_string()
new_identifier = self.create_dummy_string()
new_comment = self.create_dummy_string()
new_geometry = MultiPolygon(srid=4326) # Create an empty geometry
test_conservation_office = self.get_conservation_office_code()
test_deductable_surface = 10005
check_on_elements = {
self.eco_account.title: new_title,
self.eco_account.identifier: new_identifier,
self.eco_account.comment: new_comment,
self.eco_account.deductable_surface: test_deductable_surface,
}
for k, v in check_on_elements.items():
self.assertNotEqual(k, v)
post_data = {
"identifier": new_identifier,
"title": new_title,
"comment": new_comment,
"geom": new_geometry.geojson,
"surface": test_deductable_surface,
"conservation_office": test_conservation_office.id
}
self.client_user.post(url, post_data)
self.eco_account.refresh_from_db()
check_on_elements = {
self.eco_account.title: new_title,
self.eco_account.identifier: new_identifier,
self.eco_account.deductable_surface: test_deductable_surface,
self.eco_account.comment: new_comment,
}
for k, v in check_on_elements.items():
self.assertEqual(k, v)
self.assert_equal_geometries(self.eco_account.geometry.geom, new_geometry)
# Expect logs to be set
self.assertEqual(pre_edit_log_count + 1, self.eco_account.log.count())
self.assertEqual(self.eco_account.log.first().action, UserAction.EDITED)
def test_recordability(self):
"""
This tests if the recordability of the EcoAccount is triggered by the quality of it's data (e.g. not all fields filled)
Returns:
"""
# Add proper privilege for the user
self.eco_account.share_with(self.superuser)
pre_record_log_count = self.eco_account.log.count()
# Prepare url and form data
record_url = reverse("compensation:acc:record", args=(self.eco_account.id,))
post_data = {
"confirm": True,
}
self.eco_account.refresh_from_db()
# Make sure the account is not recorded
self.assertIsNone(self.eco_account.recorded)
# Run the request --> expect fail, since the account is not valid, yet
self.client_user.post(record_url, post_data)
# Check that the account is still not recorded
self.assertIsNone(self.eco_account.recorded)
# Now fill out the data for an ecoaccount
self.eco_account = self.fill_out_eco_account(self.eco_account)
# Rerun the request
self.client_user.post(record_url, post_data)
# Expect the EcoAccount now to be recorded
# Attention: We can only test the date part of the timestamp,
# since the delay in microseconds would lead to fail
self.eco_account.refresh_from_db()
recorded = self.eco_account.recorded
self.assertIsNotNone(recorded)
self.assertEqual(self.superuser, recorded.user)
self.assertEqual(UserAction.RECORDED, recorded.action)
self.assertEqual(datetime.date.today(), recorded.timestamp.date())
# Expect the user action to be in the log
self.assertIn(recorded, self.eco_account.log.all())
self.assertEqual(pre_record_log_count + 1, self.eco_account.log.count())
def test_deductability(self):
"""
This tests the deductability of an eco account.
An eco account should only be deductible if it is recorded.
Returns:
"""
# Give user shared access to the dummy intervention, which will be needed here
self.intervention.share_with(self.superuser)
pre_deduction_acc_log_count = self.eco_account.log.count()
pre_deduction_int_log_count = self.intervention.log.count()
# Prepare data for deduction creation
deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,))
test_surface = 10.00
post_data = {
"surface": test_surface,
"account": self.id,
"intervention": self.intervention.id,
}
# Perform request --> expect to fail
self.client_user.post(deduct_url, post_data)
# Expect that no deduction has been created
self.assertEqual(0, self.eco_account.deductions.count())
self.assertEqual(0, self.intervention.deductions.count())
self.assertEqual(pre_deduction_acc_log_count, 0)
self.assertEqual(pre_deduction_int_log_count, 0)
# Now mock the eco account as it would be recorded (with invalid data)
# Make sure the deductible surface is high enough for the request
self.eco_account.set_recorded(self.superuser)
self.eco_account.refresh_from_db()
self.eco_account.deductable_surface = test_surface + 1.00
self.eco_account.save()
self.assertIsNotNone(self.eco_account.recorded)
self.assertGreater(self.eco_account.deductable_surface, test_surface)
# Expect the recorded entry in the log
self.assertEqual(pre_deduction_acc_log_count + 1, self.eco_account.log.count())
self.assertTrue(self.eco_account.log.first().action == UserAction.RECORDED)
# Rerun the request
self.client_user.post(deduct_url, post_data)
# Expect that the deduction has been created
self.assertEqual(1, self.eco_account.deductions.count())
self.assertEqual(1, self.intervention.deductions.count())
deduction = self.eco_account.deductions.first()
self.assertEqual(deduction.surface, test_surface)
self.assertEqual(deduction.account, self.eco_account)
self.assertEqual(deduction.intervention, self.intervention)
# Expect entries in the log
self.assertEqual(pre_deduction_acc_log_count + 2, self.eco_account.log.count())
self.assertTrue(self.eco_account.log.first().action == UserAction.EDITED)
self.assertEqual(pre_deduction_int_log_count + 1, self.intervention.log.count())
self.assertTrue(self.intervention.log.first().action == UserAction.EDITED)

View File

@ -2,11 +2,11 @@
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 07.02.22
Created on: 27.10.21
"""
from django.test.client import Client
from django.urls import reverse
from django.test import Client
from konova.settings import DEFAULT_GROUP
from konova.tests.test_views import BaseViewTestCase
@ -223,3 +223,184 @@ class CompensationViewTestCase(BaseViewTestCase):
self.assert_url_fail(client, fail_urls)
self.assert_url_success(client, success_urls)
class EcoAccountViewTestCase(CompensationViewTestCase):
"""
These tests focus on proper returned views depending on the user's groups privileges and login status
EcoAccounts can inherit the same tests used for compensations.
"""
comp_state = None
comp_action = None
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
state = cls.create_dummy_states()
cls.eco_account.before_states.set([state])
cls.eco_account.after_states.set([state])
action = cls.create_dummy_action()
cls.eco_account.actions.set([action])
# Prepare urls
cls.index_url = reverse("compensation:acc:index", args=())
cls.new_url = reverse("compensation:acc:new", args=())
cls.new_id_url = reverse("compensation:acc:new-id", args=())
cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,))
cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,))
cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,))
cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,))
cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,))
cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,))
cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,))
cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,))
cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,))
cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,))
cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.id,))
def test_logged_in_no_groups_shared(self):
""" Check correct status code for all requests
Assumption: User logged in and has no groups and data is shared
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.eco_account.share_with_list([self.superuser])
# Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference
# to a user without access, since the important permissions are missing
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
]
fail_urls = [
self.new_url,
self.new_id_url,
self.log_url,
self.edit_url,
self.remove_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_logged_in_no_groups_unshared(self):
""" Check correct status code for all requests
Assumption: User logged in and has no groups and data is shared
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.eco_account.share_with_list([])
# Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference
# to a user having shared access, since all important permissions are missing
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
]
fail_urls = [
self.new_url,
self.new_id_url,
self.log_url,
self.edit_url,
self.remove_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
]
self.assert_url_success(client, success_urls)
self.assert_url_fail(client, fail_urls)
def test_logged_in_default_group_shared(self):
""" Check correct status code for all requests
Assumption: User logged in, is default group member and data is shared
--> Default group necessary since all base functionalities depend on this group membership
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.eco_account.share_with_list([self.superuser])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_url,
self.new_id_url,
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_success(client, success_urls)
def test_logged_in_default_group_unshared(self):
""" Check correct status code for all requests
Assumption: User logged in, is default group member and data is NOT shared
--> Default group necessary since all base functionalities depend on this group membership
Returns:
"""
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
self.eco_account.share_with_list([])
success_urls = [
self.index_url,
self.detail_url,
self.report_url,
self.new_id_url,
self.new_url,
]
fail_urls = [
self.edit_url,
self.state_new_url,
self.action_new_url,
self.deadline_new_url,
self.state_remove_url,
self.action_remove_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_fail(client, fail_urls)
self.assert_url_success(client, success_urls)

View File

@ -2,7 +2,7 @@
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 07.02.22
Created on: 11.11.21
"""
import datetime
@ -11,7 +11,7 @@ from django.contrib.gis.geos import MultiPolygon
from django.urls import reverse
from compensation.models import Compensation
from konova.settings import ZB_GROUP, ETS_GROUP
from konova.settings import ETS_GROUP, ZB_GROUP
from konova.tests.test_views import BaseWorkflowTestCase
from user.models import UserAction
@ -55,7 +55,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
"geom": test_geom.geojson,
"intervention": self.intervention.id,
}
pre_creation_intervention_log_count = self.intervention.log.count()
# Preserve the current number of intervention's compensations
num_compensations = self.intervention.compensations.count()
@ -67,13 +66,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
self.assertEqual(new_compensation.identifier, test_id)
self.assertEqual(new_compensation.title, test_title)
self.assert_equal_geometries(new_compensation.geometry.geom, test_geom)
self.assertEqual(new_compensation.log.count(), 1)
# Expect logs to be set
self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count())
self.assertEqual(new_compensation.log.count(), 1)
self.assertEqual(self.intervention.log.first().action, UserAction.EDITED)
self.assertEqual(new_compensation.log.first().action, UserAction.CREATED)
def test_new_from_intervention(self):
""" Test the creation of a compensation from a given intervention
@ -91,7 +83,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
"title": test_title,
"geom": test_geom.geojson,
}
pre_creation_intervention_log_count = self.intervention.log.count()
# Preserve the current number of intervention's compensations
num_compensations = self.intervention.compensations.count()
@ -104,12 +95,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
self.assertEqual(new_compensation.title, test_title)
self.assert_equal_geometries(new_compensation.geometry.geom, test_geom)
# Expect logs to be set
self.assertEqual(new_compensation.log.count(), 1)
self.assertEqual(new_compensation.log.first().action, UserAction.CREATED)
self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count())
self.assertEqual(self.intervention.log.first().action, UserAction.EDITED)
def test_edit(self):
""" Checks that the editing of a compensation works
@ -118,7 +103,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
"""
url = reverse("compensation:edit", args=(self.compensation.id,))
self.compensation = self.fill_out_compensation(self.compensation)
pre_edit_log_count = self.compensation.log.count()
new_title = self.create_dummy_string()
new_identifier = self.create_dummy_string()
@ -154,10 +138,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
self.assert_equal_geometries(self.compensation.geometry.geom, new_geometry)
# Expect logs to be set
self.assertEqual(pre_edit_log_count + 1, self.compensation.log.count())
self.assertEqual(self.compensation.log.first().action, UserAction.EDITED)
def test_checkability(self):
"""
This tests if the checkability of the compensation (which is defined by the linked intervention's checked
@ -172,8 +152,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
# Add proper privilege for the user
self.superuser.groups.add(self.groups.get(name=ZB_GROUP))
pre_check_log_count = self.compensation.log.count()
# Prepare url and form data
url = reverse("intervention:check", args=(self.intervention.id,))
post_data = {
@ -208,7 +186,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
# Expect the user action to be in the log
self.assertIn(checked, self.compensation.log.all())
self.assertEqual(pre_check_log_count + 1, self.compensation.log.count())
def test_recordability(self):
"""
@ -223,7 +200,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
"""
# Add proper privilege for the user
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
pre_record_log_count = self.compensation.log.count()
# Prepare url and form data
record_url = reverse("intervention:record", args=(self.intervention.id,))
@ -258,5 +234,62 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
# Expect the user action to be in the log
self.assertIn(recorded, self.compensation.log.all())
self.assertEqual(pre_record_log_count + 1, self.compensation.log.count())
class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
# Add user to conservation office group and give shared access to the account
cls.superuser.groups.add(cls.groups.get(name=ETS_GROUP))
cls.eco_account.share_with_list([cls.superuser])
def test_deductability(self):
"""
This tests the deductability of an eco account.
An eco account should only be deductible if it is recorded.
Returns:
"""
# Give user shared access to the dummy intervention, which will be needed here
self.intervention.share_with(self.superuser)
# Prepare data for deduction creation
deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,))
test_surface = 10.00
post_data = {
"surface": test_surface,
"account": self.id,
"intervention": self.intervention.id,
}
# Perform request --> expect to fail
self.client_user.post(deduct_url, post_data)
# Expect that no deduction has been created
self.assertEqual(0, self.eco_account.deductions.count())
self.assertEqual(0, self.intervention.deductions.count())
# Now mock the eco account as it would be recorded (with invalid data)
# Make sure the deductible surface is high enough for the request
self.eco_account.set_recorded(self.superuser)
self.eco_account.refresh_from_db()
self.eco_account.deductable_surface = test_surface + 1.00
self.eco_account.save()
self.assertIsNotNone(self.eco_account.recorded)
self.assertGreater(self.eco_account.deductable_surface, test_surface)
# Rerun the request
self.client_user.post(deduct_url, post_data)
# Expect that the deduction has been created
self.assertEqual(1, self.eco_account.deductions.count())
self.assertEqual(1, self.intervention.deductions.count())
deduction = self.eco_account.deductions.first()
self.assertEqual(deduction.surface, test_surface)
self.assertEqual(deduction.account, self.eco_account)
self.assertEqual(deduction.intervention, self.intervention)

View File

@ -10,6 +10,6 @@ from compensation.views.payment import *
app_name = "pay"
urlpatterns = [
path('<id>/new', new_payment_view, name='new'),
path('<id>/remove/<payment_id>', payment_remove_view, name='remove'),
path('<intervention_id>/new', new_payment_view, name='new'),
path('<id>/remove', payment_remove_view, name='remove'),
]

View File

@ -6,21 +6,20 @@ from django.utils.translation import gettext_lazy as _
from compensation.forms.forms import NewCompensationForm, EditCompensationForm
from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \
NewCompensationDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm
NewCompensationDocumentForm
from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument
from compensation.tables import CompensationTable
from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import *
from konova.forms import RemoveModalForm, SimpleGeomForm, RemoveDeadlineModalForm
from konova.forms import RemoveModalForm, SimpleGeomForm
from konova.models import Deadline
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \
CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \
COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \
DEADLINE_ADDED, DEADLINE_REMOVED
COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED
from konova.utils.user_checks import in_group
@ -393,7 +392,7 @@ def deadline_new_view(request: HttpRequest, id: str):
form = NewDeadlineModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request,
msg_success=DEADLINE_ADDED,
msg_success=_("Deadline added"),
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
)
@ -412,12 +411,11 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
Returns:
"""
comp = get_object_or_404(Compensation, id=id)
deadline = get_object_or_404(Deadline, id=deadline_id)
form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request)
form = RemoveModalForm(request.POST or None, instance=deadline, request=request)
return form.process_request(
request,
msg_success=DEADLINE_REMOVED,
msg_success=_("Deadline removed"),
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
)
@ -436,9 +434,8 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str):
Returns:
"""
comp = get_object_or_404(Compensation, id=id)
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request)
form = RemoveModalForm(request.POST or None, instance=state, request=request)
return form.process_request(
request,
msg_success=COMPENSATION_STATE_REMOVED,
@ -460,9 +457,8 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str):
Returns:
"""
comp = get_object_or_404(Compensation, id=id)
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request)
form = RemoveModalForm(request.POST or None, instance=action, request=request)
return form.process_request(
request,
msg_success=COMPENSATION_ACTION_REMOVED,

View File

@ -16,14 +16,14 @@ from django.shortcuts import render, get_object_or_404, redirect
from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \
NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm
NewEcoAccountDocumentForm
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
from compensation.tables import EcoAccountTable
from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm
from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm
from konova.contexts import BaseContext
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \
shared_access_required
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm, RemoveDeadlineModalForm
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
from konova.models import Deadline
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
@ -31,7 +31,7 @@ from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \
COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED
COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED
from konova.utils.user_checks import in_group
@ -286,7 +286,7 @@ def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str):
except ObjectDoesNotExist:
raise Http404("Unknown deduction")
form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request)
form = RemoveModalForm(request.POST or None, instance=eco_deduction, request=request)
return form.process_request(
request=request,
msg_success=DEDUCTION_REMOVED,
@ -401,9 +401,8 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str):
Returns:
"""
acc = get_object_or_404(EcoAccount, id=id)
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveCompensationStateModalForm(request.POST or None, instance=acc, state=state, request=request)
form = RemoveModalForm(request.POST or None, instance=state, request=request)
return form.process_request(
request,
msg_success=COMPENSATION_STATE_REMOVED,
@ -425,9 +424,8 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str):
Returns:
"""
acc = get_object_or_404(EcoAccount, id=id)
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveCompensationActionModalForm(request.POST or None, instance=acc, action=action, request=request)
form = RemoveModalForm(request.POST or None, instance=action, request=request)
return form.process_request(
request,
msg_success=COMPENSATION_ACTION_REMOVED,
@ -449,12 +447,11 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
Returns:
"""
comp = get_object_or_404(EcoAccount, id=id)
deadline = get_object_or_404(Deadline, id=deadline_id)
form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request)
form = RemoveModalForm(request.POST or None, instance=deadline, request=request)
return form.process_request(
request,
msg_success=DEADLINE_REMOVED,
msg_success=_("Deadline removed"),
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
)
@ -476,7 +473,7 @@ def deadline_new_view(request: HttpRequest, id: str):
form = NewDeadlineModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request,
msg_success=DEADLINE_ADDED,
msg_success=_("Deadline added"),
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
)

View File

@ -11,7 +11,7 @@ from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
from compensation.forms.modalForms import NewPaymentForm, RemovePaymentModalForm
from compensation.forms.modalForms import NewPaymentForm
from compensation.models import Payment
from intervention.models import Intervention
from konova.decorators import default_group_required
@ -21,41 +21,39 @@ from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED
@login_required
@default_group_required
def new_payment_view(request: HttpRequest, id: str):
def new_payment_view(request: HttpRequest, intervention_id: str):
""" Renders a modal view for adding new payments
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id for which a new payment shall be added
intervention_id (str): The intervention's id for which a new payment shall be added
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
intervention = get_object_or_404(Intervention, id=intervention_id)
form = NewPaymentForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=PAYMENT_ADDED,
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
redirect_url=reverse("intervention:detail", args=(intervention_id,)) + "#related_data"
)
@login_required
@default_group_required
def payment_remove_view(request: HttpRequest, id: str, payment_id: str):
def payment_remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing payments
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
payment_id (str): The payment's id
id (str): The payment's id
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
payment = get_object_or_404(Payment, id=payment_id)
form = RemovePaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request)
payment = get_object_or_404(Payment, id=id)
form = RemoveModalForm(request.POST or None, instance=payment, request=request)
return form.process_request(
request=request,
msg_success=PAYMENT_REMOVED,

View File

@ -51,6 +51,28 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
self.identifier = new_id
super().save(*args, **kwargs)
def get_LANIS_link(self) -> str:
""" Generates a link for LANIS depending on the geometry
Returns:
"""
try:
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
x = geom.centroid.x
y = geom.centroid.y
zoom_lvl = 16
except AttributeError:
# If no geometry has been added, yet.
x = 1
y = 1
zoom_lvl = 6
return LANIS_LINK_TEMPLATE.format(
zoom_lvl,
x,
y,
)
def quality_check(self) -> EmaQualityChecker:
""" Quality check

View File

@ -6,7 +6,6 @@ Created on: 19.08.21
"""
from django.http import HttpRequest
from django.template.loader import render_to_string
from django.utils.html import format_html
from django.utils.timezone import localtime
from django.utils.translation import gettext_lazy as _
@ -35,11 +34,6 @@ class EmaTable(BaseTable, TableRenderMixin):
orderable=True,
accessor="title",
)
d = tables.Column(
verbose_name=_("Parcel gmrkng"),
orderable=True,
accessor="geometry",
)
r = tables.Column(
verbose_name=_("Recorded"),
orderable=True,
@ -93,29 +87,6 @@ class EmaTable(BaseTable, TableRenderMixin):
)
return format_html(html)
def render_d(self, value, record: Ema):
""" Renders the parcel district column for a ema
Args:
value (str): The geometry
record (Ema): The ema record
Returns:
"""
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()
html = render_to_string(
"table/gmrkng_col.html",
{
"entries": parcels
}
)
return html
def render_r(self, value, record: Ema):
""" Renders the registered column for a EMA

View File

@ -5,12 +5,11 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 26.10.21
"""
from django.db.models import Q
from django.urls import reverse
from django.test.client import Client
from compensation.tests.compensation.test_views import CompensationViewTestCase
from compensation.tests.test_views import CompensationViewTestCase
from ema.models import Ema
from intervention.models import Responsibility
from konova.models import Geometry

View File

@ -1,165 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 08.02.22
"""
import datetime
from django.contrib.gis.geos import MultiPolygon
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
from ema.models import Ema
from konova.settings import ETS_GROUP
from konova.tests.test_views import BaseWorkflowTestCase
from user.models import UserAction
class EmaWorkflowTestCase(BaseWorkflowTestCase):
ema = None
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Create a fresh dummy (non-valid) compensation before each test
self.ema = self.create_dummy_ema()
def test_new(self):
""" Test the creation of an Ema
Returns:
"""
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
# Prepare url and form data to be posted
new_url = reverse("ema:new")
test_id = self.create_dummy_string()
test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry()
test_conservation_office = self.get_conservation_office_code()
post_data = {
"identifier": test_id,
"title": test_title,
"geom": test_geom.geojson,
"conservation_office": test_conservation_office.id
}
self.client_user.post(new_url, post_data)
try:
ema = Ema.objects.get(
identifier=test_id
)
except ObjectDoesNotExist:
self.fail(msg="Ema not created")
self.assertEqual(ema.identifier, test_id)
self.assertEqual(ema.title, test_title)
self.assert_equal_geometries(ema.geometry.geom, test_geom)
self.assertEqual(ema.log.count(), 1)
# Expect logs to be set
self.assertEqual(ema.log.count(), 1)
self.assertEqual(ema.log.first().action, UserAction.CREATED)
def test_edit(self):
""" Checks that the editing of an Ema works
Returns:
"""
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
self.ema.users.add(self.superuser)
url = reverse("ema:edit", args=(self.ema.id,))
self.ema = self.fill_out_ema(self.ema)
pre_edit_log_count = self.ema.log.count()
new_title = self.create_dummy_string()
new_identifier = self.create_dummy_string()
new_comment = self.create_dummy_string()
new_geometry = MultiPolygon(srid=4326) # Create an empty geometry
test_conservation_office = self.get_conservation_office_code()
check_on_elements = {
self.ema.title: new_title,
self.ema.identifier: new_identifier,
self.ema.comment: new_comment,
}
for k, v in check_on_elements.items():
self.assertNotEqual(k, v)
post_data = {
"identifier": new_identifier,
"title": new_title,
"comment": new_comment,
"geom": new_geometry.geojson,
"conservation_office": test_conservation_office.id
}
self.client_user.post(url, post_data)
self.ema.refresh_from_db()
check_on_elements = {
self.ema.title: new_title,
self.ema.identifier: new_identifier,
self.ema.comment: new_comment,
}
for k, v in check_on_elements.items():
self.assertEqual(k, v)
self.assert_equal_geometries(self.ema.geometry.geom, new_geometry)
# Expect logs to be set
self.assertEqual(pre_edit_log_count + 1, self.ema.log.count())
self.assertEqual(self.ema.log.first().action, UserAction.EDITED)
def test_recordability(self):
"""
This tests if the recordability of the Ema is triggered by the quality of it's data (e.g. not all fields filled)
Returns:
"""
# Add proper privilege for the user
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
self.ema.users.add(self.superuser)
pre_record_log_count = self.ema.log.count()
# Prepare url and form data
record_url = reverse("ema:record", args=(self.ema.id,))
post_data = {
"confirm": True,
}
# Make sure the ema is not recorded
self.assertIsNone(self.ema.recorded)
# Run the request --> expect fail, since the Ema is not valid, yet
self.client_user.post(record_url, post_data)
# Check that the Ema is still not recorded
self.assertIsNone(self.ema.recorded)
# Now fill out the data for a compensation
self.ema = self.fill_out_ema(self.ema)
# Rerun the request
self.client_user.post(record_url, post_data)
# Expect the Ema now to be recorded
# Attention: We can only test the date part of the timestamp,
# since the delay in microseconds would lead to fail
self.ema.refresh_from_db()
recorded = self.ema.recorded
self.assertIsNotNone(recorded)
self.assertEqual(self.superuser, recorded.user)
self.assertEqual(UserAction.RECORDED, recorded.action)
self.assertEqual(datetime.date.today(), recorded.timestamp.date())
# Expect the user action to be in the log
self.assertIn(recorded, self.ema.log.all())
self.assertEqual(pre_record_log_count + 1, self.ema.log.count())

View File

@ -6,8 +6,7 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \
RemoveCompensationActionModalForm, RemoveCompensationStateModalForm
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from compensation.models import CompensationAction, CompensationState
from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentForm
from ema.tables import EmaTable
@ -15,7 +14,7 @@ from intervention.forms.modalForms import ShareModalForm
from konova.contexts import BaseContext
from konova.decorators import conservation_office_group_required, shared_access_required
from ema.models import Ema, EmaDocument
from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, RemoveDeadlineModalForm
from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm
from konova.models import Deadline
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
@ -23,7 +22,7 @@ from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \
COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED
COMPENSATION_ACTION_ADDED
from konova.utils.user_checks import in_group
@ -338,7 +337,7 @@ def deadline_new_view(request: HttpRequest, id: str):
form = NewDeadlineModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request,
msg_success=DEADLINE_ADDED,
msg_success=_("Deadline added"),
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
)
@ -426,9 +425,8 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str):
Returns:
"""
ema = get_object_or_404(Ema, id=id)
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveCompensationStateModalForm(request.POST or None, instance=ema, state=state, request=request)
form = RemoveModalForm(request.POST or None, instance=state, request=request)
return form.process_request(
request,
msg_success=COMPENSATION_STATE_REMOVED,
@ -450,9 +448,8 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str):
Returns:
"""
ema = get_object_or_404(Ema, id=id)
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveCompensationActionModalForm(request.POST or None, instance=ema, action=action, request=request)
form = RemoveModalForm(request.POST or None, instance=action, request=request)
return form.process_request(
request,
msg_success=COMPENSATION_ACTION_REMOVED,
@ -593,11 +590,10 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
Returns:
"""
ema = get_object_or_404(Ema, id=id)
deadline = get_object_or_404(Deadline, id=deadline_id)
form = RemoveDeadlineModalForm(request.POST or None, instance=ema, deadline=deadline, request=request)
form = RemoveModalForm(request.POST or None, instance=deadline, request=request)
return form.process_request(
request,
msg_success=DEADLINE_REMOVED,
msg_success=_("Deadline removed"),
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
)

View File

@ -7,7 +7,7 @@ Created on: 27.09.21
"""
from dal import autocomplete
from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED
from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED
from user.models import User, UserActionLogEntry
from django.db import transaction
from django import forms
@ -16,7 +16,7 @@ from django.utils.translation import gettext_lazy as _
from compensation.models import EcoAccount, EcoAccountDeduction
from intervention.inputs import TextToClipboardInput
from intervention.models import Intervention, InterventionDocument
from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm
from konova.forms import BaseModalForm, NewDocumentForm
from konova.utils.general import format_german_float
from konova.utils.user_checks import is_default_group_only
@ -172,23 +172,6 @@ class NewRevocationModalForm(BaseModalForm):
return revocation
class RemoveRevocationModalForm(RemoveModalForm):
""" Removing modal form for Revocation
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
revocation = None
def __init__(self, *args, **kwargs):
revocation = kwargs.pop("revocation", None)
self.revocation = revocation
super().__init__(*args, **kwargs)
def save(self):
self.instance.remove_revocation(self)
class CheckModalForm(BaseModalForm):
""" The modal form for running a check on interventions and their compensations
@ -407,25 +390,5 @@ class NewDeductionModalForm(BaseModalForm):
return deduction
class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
""" Removing modal form for EcoAccountDeduction
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
deduction = None
def __init__(self, *args, **kwargs):
deduction = kwargs.pop("deduction", None)
self.deduction = deduction
super().__init__(*args, **kwargs)
def save(self):
with transaction.atomic():
self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
self.deduction.delete()
class NewInterventionDocumentForm(NewDocumentForm):
document_model = InterventionDocument

View File

@ -16,6 +16,7 @@ from django.db import models, transaction
from django.db.models import QuerySet
from django.http import HttpRequest
from compensation.models import EcoAccountDeduction
from intervention.managers import InterventionManager
from intervention.models.legal import Legal
from intervention.models.responsibility import Responsibility
@ -25,8 +26,7 @@ from konova.models import generate_document_file_upload_path, AbstractDocument,
ShareableObjectMixin, \
RecordableObjectMixin, CheckableObjectMixin, GeoReferencedMixin
from konova.settings import LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT, DEFAULT_SRID_RLP
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE, \
PAYMENT_REMOVED, PAYMENT_ADDED, REVOCATION_REMOVED, INTERVENTION_HAS_REVOCATIONS_TEMPLATE
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DEDUCTION_ADDED, DOCUMENT_REMOVED_TEMPLATE
from user.models import UserActionLogEntry
@ -100,6 +100,34 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
checker.run_check()
return checker
def get_LANIS_link(self) -> str:
""" Generates a link for LANIS depending on the geometry
Returns:
"""
try:
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
x = geom.centroid.x
y = geom.centroid.y
area = int(geom.envelope.area)
z_l = 16
for k_area, v_zoom in LANIS_ZOOM_LUT.items():
if k_area < area:
z_l = v_zoom
break
zoom_lvl = z_l
except (AttributeError, IndexError) as e:
# If no geometry has been added, yet.
x = 1
y = 1
zoom_lvl = 6
return LANIS_LINK_TEMPLATE.format(
zoom_lvl,
x,
y,
)
def get_documents(self) -> (QuerySet, QuerySet):
""" Getter for all documents of an intervention
@ -168,7 +196,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
comment=form_data.get("comment", None),
intervention=self,
)
self.mark_as_edited(user, form.request, edit_comment=PAYMENT_ADDED)
return pay
def add_revocation(self, form):
@ -202,21 +229,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
)
return revocation
def remove_revocation(self, form):
""" Removes a revocation from the intervention
Args:
form (RemoveRevocationModalForm): The form holding all relevant data
Returns:
"""
revocation = form.revocation
user = form.user
with transaction.atomic():
revocation.delete()
self.mark_as_edited(user, request=form.request, edit_comment=REVOCATION_REMOVED)
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True):
""" In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck
@ -230,7 +242,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
Returns:
"""
action = super().mark_as_edited(performing_user, edit_comment=edit_comment)
action = super().mark_as_edited(performing_user, edit_comment)
if reset_recorded:
self.unrecord(performing_user, request)
if self.checked:
@ -248,13 +260,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
Returns:
request (HttpRequest): The modified request
"""
# Inform user about revocation
if self.legal.revocations.exists():
messages.error(
request,
INTERVENTION_HAS_REVOCATIONS_TEMPLATE.format(self.legal.revocations.count()),
extra_tags="danger",
)
if not self.is_shared_with(request.user):
messages.info(request, DATA_UNSHARED_EXPLANATION)
request = self.set_geometry_conflict_message(request)
@ -284,21 +289,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
"""
return reverse("intervention:share", args=(self.id, self.access_token))
def remove_payment(self, form):
""" Removes a Payment from the intervention
Args:
form (RemovePaymentModalForm): The form holding all relevant data
Returns:
"""
payment = form.payment
user = form.user
with transaction.atomic():
payment.delete()
self.mark_as_edited(user, request=form.request, edit_comment=PAYMENT_REMOVED)
class InterventionDocument(AbstractDocument):
"""

View File

@ -23,7 +23,7 @@ class Revocation(BaseResource):
legal = models.ForeignKey("Legal", null=False, blank=False, on_delete=models.CASCADE, help_text="Refers to 'Widerspruch am'", related_name="revocations")
comment = models.TextField(null=True, blank=True)
def delete(self, *args, **kwargs):
def delete(self, user=None, *args, **kwargs):
# Make sure related objects are being removed as well
try:
self.document.delete(*args, **kwargs)
@ -31,6 +31,9 @@ class Revocation(BaseResource):
# No file to delete
pass
if user is not None:
self.legal.intervention.mark_as_edited(user, edit_comment=REVOCATION_REMOVED)
super().delete()
@property

View File

@ -6,7 +6,6 @@ Created on: 01.12.20
"""
from django.http import HttpRequest
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.html import format_html
from django.utils.timezone import localtime
@ -30,11 +29,6 @@ class InterventionTable(BaseTable, TableRenderMixin):
orderable=True,
accessor="title",
)
d = tables.Column(
verbose_name=_("Parcel gmrkng"),
orderable=True,
accessor="geometry",
)
c = tables.Column(
verbose_name=_("Checked"),
orderable=True,
@ -47,6 +41,12 @@ class InterventionTable(BaseTable, TableRenderMixin):
empty_values=[],
accessor="recorded",
)
rev = tables.Column(
verbose_name=_("Revocation"),
orderable=True,
empty_values=[],
accessor="legal__revocation",
)
e = tables.Column(
verbose_name=_("Editable"),
orderable=True,
@ -84,17 +84,14 @@ class InterventionTable(BaseTable, TableRenderMixin):
Returns:
"""
context = {
"tooltip": _("Open {}").format(_("Intervention")),
"content": value,
"url": reverse("intervention:detail", args=(record.id,)),
"has_revocations": record.legal.revocations.exists()
}
html = render_to_string(
"table/revocation_warning_col.html",
context
html = ""
html += self.render_link(
tooltip=_("Open {}").format(_("Intervention")),
href=reverse("intervention:detail", args=(record.id,)),
txt=value,
new_tab=False,
)
return html
return format_html(html)
def render_c(self, value, record: Intervention):
""" Renders the checked column for an intervention
@ -120,28 +117,6 @@ class InterventionTable(BaseTable, TableRenderMixin):
)
return format_html(html)
def render_d(self, value, record: Intervention):
""" Renders the parcel district column for an intervention
Args:
value (str): The intervention geometry
record (Intervention): The intervention record
Returns:
"""
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()
html = render_to_string(
"table/gmrkng_col.html",
{
"entries": parcels
}
)
return html
def render_r(self, value, record: Intervention):
""" Renders the recorded column for an intervention
@ -187,3 +162,28 @@ class InterventionTable(BaseTable, TableRenderMixin):
)
return format_html(html)
def render_rev(self, value, record: Intervention):
""" Renders the revocation column for an intervention
Args:
value (str): The revocation value
record (Intervention): The intervention record
Returns:
"""
html = ""
exists = value is not None
tooltip = _("No revocation")
if exists:
_date = value.date
added_ts = localtime(value.created.timestamp)
added_ts = added_ts.strftime(DEFAULT_DATE_TIME_FORMAT)
on = _date.strftime(DEFAULT_DATE_FORMAT)
tooltip = _("Revocation from {}, added on {} by {}").format(on, added_ts, value.created.user)
html += self.render_stop(
tooltip=tooltip,
icn_filled=exists,
)
return format_html(html)

View File

@ -56,7 +56,7 @@
</td>
<td class="align-middle">
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:pay:remove' obj.id pay.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove payment' %}">
<button data-form-url="{% url 'compensation:pay:remove' pay.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove payment' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

View File

@ -65,7 +65,7 @@
</td>
<td class="align-middle">
{% if is_default_member and has_access %}
<button data-form-url="{% url 'intervention:remove-revocation' obj.id rev.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove revocation' %}">
<button data-form-url="{% url 'intervention:remove-revocation' rev.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove revocation' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

View File

@ -10,7 +10,6 @@ from django.test import Client
from django.contrib.auth.models import Group
from django.urls import reverse
from intervention.models import Revocation
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.tests.test_views import BaseViewTestCase
@ -35,17 +34,6 @@ class InterventionViewTestCase(BaseViewTestCase):
cls.record_url = reverse("intervention:record", args=(cls.intervention.id,))
cls.report_url = reverse("intervention:report", args=(cls.intervention.id,))
cls.deduction.intervention = cls.intervention
cls.deduction.save()
cls.deduction_new_url = reverse("intervention:new-deduction", args=(cls.intervention.id,))
cls.deduction_remove_url = reverse("intervention:remove-deduction", args=(cls.intervention.id, cls.deduction.id))
cls.revocation = Revocation.objects.create(
legal=cls.intervention.legal
)
cls.revocation_new_url = reverse("intervention:new-revocation", args=(cls.intervention.id,))
cls.revocation_remove_url = reverse("intervention:remove-revocation", args=(cls.intervention.id, cls.revocation.id))
def test_views_anonymous_user(self):
""" Check correct status code for all requests
@ -73,10 +61,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.share_create_url: f"{login_redirect_base}{self.share_create_url}",
self.run_check_url: f"{login_redirect_base}{self.run_check_url}",
self.record_url: f"{login_redirect_base}{self.record_url}",
self.deduction_new_url: f"{login_redirect_base}{self.deduction_new_url}",
self.deduction_remove_url: f"{login_redirect_base}{self.deduction_remove_url}",
self.revocation_new_url: f"{login_redirect_base}{self.revocation_new_url}",
self.revocation_remove_url: f"{login_redirect_base}{self.revocation_remove_url}",
}
self.assert_url_success(client, success_urls)
@ -112,10 +96,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.share_create_url,
self.run_check_url,
self.record_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
self.assert_url_success(client, success_urls)
@ -148,10 +128,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.edit_url,
self.remove_url,
self.share_create_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
fail_urls = [
self.run_check_url,
@ -196,10 +172,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.remove_url,
self.share_create_url,
self.log_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
success_urls_redirect = {
self.share_url: self.detail_url
@ -240,10 +212,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.remove_url,
self.share_create_url,
self.record_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
success_urls_redirect = {
self.share_url: self.detail_url
@ -284,10 +252,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.share_create_url,
self.record_url,
self.run_check_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
success_urls_redirect = {
self.share_url: self.detail_url
@ -328,10 +292,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.remove_url,
self.share_create_url,
self.run_check_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
success_urls_redirect = {
self.share_url: self.detail_url
@ -372,10 +332,6 @@ class InterventionViewTestCase(BaseViewTestCase):
self.remove_url,
self.share_create_url,
self.run_check_url,
self.revocation_new_url,
self.revocation_remove_url,
self.deduction_new_url,
self.deduction_remove_url,
]
# Define urls where a redirect to a specific location is the proper response
success_urls_redirect = {

View File

@ -74,9 +74,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
self.assertEqual(obj.identifier, test_id)
self.assertEqual(obj.title, test_title)
self.assert_equal_geometries(obj.geometry.geom, test_geom)
self.assertEqual(1, obj.log.count())
self.assertEqual(obj.log.first().action, UserAction.CREATED)
self.assertEqual(obj.log.first().user, self.superuser)
except ObjectDoesNotExist:
# Fail if there is no such object
self.fail()
@ -218,8 +215,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
# Make sure there are no payments on the intervention, yet
self.assertEqual(0, self.intervention.payments.count())
pre_payment_logs_count = self.intervention.log.count()
# Create form data to be sent to the url
test_amount = 10.00
test_due = "2021-01-01"
@ -244,10 +239,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
self.assertEqual(payment.amount, test_amount)
self.assertEqual(payment.due_on, datetime.date.fromisoformat(test_due))
self.assertEqual(payment.comment, test_comment)
# Make sure a log entry has been created
self.assertEqual(self.intervention.log.first().action, UserAction.EDITED)
self.assertEqual(pre_payment_logs_count + 1, self.intervention.log.count())
return payment
def subtest_delete_payment(self, payment: Payment):
@ -259,10 +250,8 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
Returns:
"""
pre_payment_logs_count = self.intervention.log.count()
# Create removing url for the payment
remove_url = reverse("compensation:pay:remove", args=(self.intervention.id, payment.id,))
remove_url = reverse("compensation:pay:remove", args=(payment.id,))
post_data = {
"confirm": True,
}
@ -277,11 +266,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
# Now make sure the intervention has no payments anymore
self.assertEqual(0, self.intervention.payments.count())
# Make sure a log entry has been created
self.assertEqual(self.intervention.log.first().action, UserAction.EDITED)
self.assertEqual(self.intervention.log.first().user, self.superuser)
self.assertEqual(pre_payment_logs_count + 1, self.intervention.log.count())
def test_payments(self):
"""
Checks a 'normal' case of adding a payment.
@ -369,8 +353,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
Returns:
"""
pre_deduction_logs_count = self.intervention.log.count()
# Prepare the account for a working situation (enough deductable surface, recorded and shared)
self.eco_account.deductable_surface = 10000.00
if self.eco_account.recorded is None:
@ -394,11 +376,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
)
self.assertEqual(deduction.surface, test_surface)
# Make sure a log entry has been created
self.assertEqual(self.intervention.log.first().action, UserAction.EDITED)
self.assertEqual(self.intervention.log.first().user, self.superuser)
self.assertEqual(pre_deduction_logs_count + 1, self.intervention.log.count())
# Return deduction for further usage in tests
return deduction
@ -437,8 +414,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
Returns:
"""
pre_delete_logs_count = self.intervention.log.count()
# Prepare url for deleting of this deduction
delete_url = reverse("compensation:acc:remove-deduction", args=(self.eco_account.id, deduction.id,))
post_data = {
@ -458,11 +433,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
# Expect the deduction to be totally gone
self.assert_object_is_deleted(deduction)
# Make sure a log entry has been created
self.assertEqual(self.intervention.log.first().action, UserAction.EDITED)
self.assertEqual(self.intervention.log.first().user, self.superuser)
self.assertEqual(pre_delete_logs_count + 1, self.intervention.log.count())
def test_deduction(self):
"""
Checks a 'normal case of adding a deduction.

View File

@ -28,7 +28,7 @@ urlpatterns = [
path('<id>/report', report_view, name='report'),
# Compensations
path('<id>/compensation/<comp_id>/remove', remove_compensation_view, name='remove-compensation'),
path('<id>/remove/<comp_id>', remove_compensation_view, name='remove-compensation'),
# Documents
path('<id>/document/new/', new_document_view, name='new-doc'),
@ -37,10 +37,10 @@ urlpatterns = [
# Deductions
path('<id>/deduction/new', new_deduction_view, name='new-deduction'),
path('<id>/deduction/<deduction_id>/remove', remove_deduction_view, name='remove-deduction'),
path('<id>/remove/<deduction_id>', remove_deduction_view, name='remove-deduction'),
# Revocation routes
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
path('revocation/<id>/remove', remove_revocation_view, name='remove-revocation'),
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
]

View File

@ -6,8 +6,7 @@ from django.shortcuts import render
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \
CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \
RemoveRevocationModalForm
CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
from intervention.tables import InterventionTable
from konova.contexts import BaseContext
@ -245,6 +244,14 @@ def detail_view(request: HttpRequest, id: str):
parcels = intervention.get_underlying_parcels()
# Inform user about revocation
if intervention.legal.revocations.exists():
messages.error(
request,
_("This intervention has {} revocations").format(intervention.legal.revocations.count()),
extra_tags="danger",
)
context = {
"obj": intervention,
"compensations": compensations,
@ -333,8 +340,7 @@ def remove_view(request: HttpRequest, id: str):
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str):
def remove_revocation_view(request: HttpRequest, id: str):
""" Renders a remove view for a revocation
Args:
@ -344,14 +350,13 @@ def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str):
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
revocation = get_object_or_404(Revocation, id=revocation_id)
obj = Revocation.objects.get(id=id)
form = RemoveRevocationModalForm(request.POST or None, instance=intervention, revocation=revocation, request=request)
form = RemoveModalForm(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
REVOCATION_REMOVED,
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
redirect_url=reverse("intervention:detail", args=(obj.intervention.id,)) + "#related_data"
)
@ -528,7 +533,7 @@ def remove_deduction_view(request: HttpRequest, id: str, deduction_id: str):
except ObjectDoesNotExist:
raise Http404("Unknown deduction")
form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request)
form = RemoveModalForm(request.POST or None, instance=eco_deduction, request=request)
return form.process_request(
request=request,
msg_success=DEDUCTION_REMOVED,

View File

@ -35,9 +35,8 @@ class EcoAccountAutocomplete(Select2QuerySetView):
)
if self.q:
qs = qs.filter(
Q(identifier__icontains=self.q) |
Q(title__icontains=self.q)
).distinct()
identifier__icontains=self.q
)
return qs
@ -58,9 +57,8 @@ class InterventionAutocomplete(Select2QuerySetView):
)
if self.q:
qs = qs.filter(
Q(identifier__icontains=self.q) |
Q(title__icontains=self.q)
).distinct()
identifier__icontains=self.q
)
return qs
@ -83,9 +81,8 @@ class ShareUserAutocomplete(Select2QuerySetView):
if self.q:
# Due to privacy concerns only a full username match will return the proper user entry
qs = qs.filter(
Q(username=self.q) |
Q(email=self.q)
).distinct()
username=self.q
)
return qs

View File

@ -327,24 +327,7 @@ class RemoveModalForm(BaseModalForm):
self.instance.mark_as_deleted(self.user)
else:
# If the class does not provide restorable delete functionality, we must delete the entry finally
self.instance.delete()
class RemoveDeadlineModalForm(RemoveModalForm):
""" Removing modal form for deadlines
Can be used for anything, where removing shall be confirmed by the user a second time.
"""
deadline = None
def __init__(self, *args, **kwargs):
deadline = kwargs.pop("deadline", None)
self.deadline = deadline
super().__init__(*args, **kwargs)
def save(self):
self.instance.remove_deadline(self)
self.instance.delete(self.user)
class NewDocumentForm(BaseModalForm):

View File

@ -12,7 +12,6 @@ from abc import abstractmethod
from django.contrib import messages
from django.db.models import QuerySet
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP, LANIS_ZOOM_LUT, LANIS_LINK_TEMPLATE
from konova.tasks import celery_send_mail_shared_access_removed, celery_send_mail_shared_access_given, \
celery_send_mail_shared_data_recorded, celery_send_mail_shared_data_unrecorded, \
celery_send_mail_shared_data_deleted, celery_send_mail_shared_data_checked
@ -129,11 +128,11 @@ class BaseObject(BaseResource):
# Send mail
shared_users = self.shared_users.values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_deleted.delay(self.identifier, self.title, user_id)
celery_send_mail_shared_data_deleted.delay(self.identifier, user_id)
self.save()
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None):
def mark_as_edited(self, performing_user: User, edit_comment: str = None):
""" In case the object or a related object changed the log history needs to be updated
Args:
@ -218,10 +217,6 @@ class BaseObject(BaseResource):
_str = "{}{}-{}".format(curr_month, curr_year, rand_str)
return definitions[self.__class__]["template"].format(_str)
@abstractmethod
def get_detail_url(self):
raise NotImplementedError()
class RecordableObjectMixin(models.Model):
""" Wraps record related fields and functionality
@ -258,7 +253,7 @@ class RecordableObjectMixin(models.Model):
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_unrecorded.delay(self.identifier, self.title, user_id)
celery_send_mail_shared_data_unrecorded.delay(self.identifier, user_id)
return action
@ -280,7 +275,7 @@ class RecordableObjectMixin(models.Model):
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_recorded.delay(self.identifier, self.title, user_id)
celery_send_mail_shared_data_recorded.delay(self.identifier, user_id)
return action
@ -365,7 +360,7 @@ class CheckableObjectMixin(models.Model):
# Send mail
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_checked.delay(self.identifier, self.title, user_id)
celery_send_mail_shared_data_checked.delay(self.identifier, user_id)
self.log.add(action)
return action
@ -479,9 +474,9 @@ class ShareableObjectMixin(models.Model):
# Send mails
for user in removed_users:
celery_send_mail_shared_access_removed.delay(self.identifier, self.title, user["id"])
celery_send_mail_shared_access_removed.delay(self.identifier, user["id"])
for user in new_accessing_users:
celery_send_mail_shared_access_given.delay(self.identifier, self.title, user)
celery_send_mail_shared_access_given.delay(self.identifier, user)
# Set new shared users
self.share_with_list(users)
@ -545,31 +540,3 @@ class GeoReferencedMixin(models.Model):
message_str = GEOMETRY_CONFLICT_WITH_TEMPLATE.format(instance_identifiers)
messages.info(request, message_str)
return request
def get_LANIS_link(self) -> str:
""" Generates a link for LANIS depending on the geometry
Returns:
"""
try:
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
x = geom.centroid.x
y = geom.centroid.y
area = int(geom.envelope.area)
z_l = 16
for k_area, v_zoom in LANIS_ZOOM_LUT.items():
if k_area < area:
z_l = v_zoom
break
zoom_lvl = z_l
except (AttributeError, IndexError) as e:
# If no geometry has been added, yet.
x = 1
y = 1
zoom_lvl = 6
return LANIS_LINK_TEMPLATE.format(
zoom_lvl,
x,
y,
)

View File

@ -219,13 +219,6 @@ Overwrites bootstrap .btn:focus box shadow color
overflow: auto;
}
.w-20{
width: 20%;
}
.w-10{
width: 20%;
}
/*
Extends css for django autocomplete light (dal)
No other approach worked to get the autocomplete fields to full width of parent containers

View File

@ -19,42 +19,42 @@ def celery_update_parcels(geometry_id: str, recheck: bool = True):
@shared_task
def celery_send_mail_shared_access_removed(obj_identifier, obj_title=None, user_id=None):
def celery_send_mail_shared_access_removed(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_access_removed(obj_identifier, obj_title)
user.send_mail_shared_access_removed(obj_identifier)
@shared_task
def celery_send_mail_shared_access_given(obj_identifier, obj_title=None, user_id=None):
def celery_send_mail_shared_access_given(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_access_given(obj_identifier, obj_title)
user.send_mail_shared_access_given(obj_identifier)
@shared_task
def celery_send_mail_shared_data_recorded(obj_identifier, obj_title=None, user_id=None):
def celery_send_mail_shared_data_recorded(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_recorded(obj_identifier, obj_title)
user.send_mail_shared_data_recorded(obj_identifier)
@shared_task
def celery_send_mail_shared_data_unrecorded(obj_identifier, obj_title=None, user_id=None):
def celery_send_mail_shared_data_unrecorded(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_unrecorded(obj_identifier, obj_title)
user.send_mail_shared_data_unrecorded(obj_identifier)
@shared_task
def celery_send_mail_shared_data_deleted(obj_identifier, obj_title=None, user_id=None):
def celery_send_mail_shared_data_deleted(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_deleted(obj_identifier, obj_title)
user.send_mail_shared_data_deleted(obj_identifier)
@shared_task
def celery_send_mail_shared_data_checked(obj_identifier, obj_title=None, user_id=None):
def celery_send_mail_shared_data_checked(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_checked(obj_identifier, obj_title)
user.send_mail_shared_data_checked(obj_identifier)

View File

@ -7,7 +7,6 @@ Created on: 26.10.21
"""
import datetime
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
from ema.models import Ema
from user.models import User
from django.contrib.auth.models import Group
@ -16,7 +15,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase, Client
from django.urls import reverse
from codelist.models import KonovaCode, KonovaCodeList
from codelist.models import KonovaCode
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
@ -237,10 +236,10 @@ class BaseTestCase(TestCase):
"""
codes = KonovaCode.objects.bulk_create([
KonovaCode(id=1, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test1"),
KonovaCode(id=2, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test2"),
KonovaCode(id=3, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test3"),
KonovaCode(id=4, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test4"),
KonovaCode(id=1, is_selectable=True, long_name="Test1"),
KonovaCode(id=2, is_selectable=True, long_name="Test2"),
KonovaCode(id=3, is_selectable=True, long_name="Test3"),
KonovaCode(id=4, is_selectable=True, long_name="Test4"),
])
return codes
@ -299,58 +298,6 @@ class BaseTestCase(TestCase):
compensation.geometry.save()
return compensation
@classmethod
def get_conservation_office_code(cls):
""" Returns a dummy KonovaCode as conservation office code
Returns:
"""
codelist = KonovaCodeList.objects.get_or_create(
id=CODELIST_CONSERVATION_OFFICE_ID
)[0]
code = KonovaCode.objects.get(id=2)
codelist.codes.add(code)
return code
@classmethod
def fill_out_ema(cls, ema):
""" Adds all required (dummy) data to an Ema
Returns:
"""
ema.responsible.conservation_office = cls.get_conservation_office_code()
ema.responsible.conservation_file_number = "test"
ema.responsible.handler = "handler"
ema.responsible.save()
ema.after_states.add(cls.comp_state)
ema.before_states.add(cls.comp_state)
ema.actions.add(cls.comp_action)
ema.geometry.geom = cls.create_dummy_geometry()
ema.geometry.save()
return ema
@classmethod
def fill_out_eco_account(cls, eco_account):
""" Adds all required (dummy) data to an EcoAccount
Returns:
"""
eco_account.legal.registration_date = "2022-01-01"
eco_account.legal.save()
eco_account.responsible.conservation_office = cls.get_conservation_office_code()
eco_account.responsible.conservation_file_number = "test"
eco_account.responsible.handler = "handler"
eco_account.responsible.save()
eco_account.after_states.add(cls.comp_state)
eco_account.before_states.add(cls.comp_state)
eco_account.actions.add(cls.comp_action)
eco_account.geometry.geom = cls.create_dummy_geometry()
eco_account.geometry.save()
eco_account.deductable_surface = eco_account.get_state_after_surface_sum()
eco_account.save()
return eco_account
def assert_equal_geometries(self, geom1: MultiPolygon, geom2: MultiPolygon):
""" Assert for geometries to be equal
@ -555,7 +502,6 @@ class BaseWorkflowTestCase(BaseTestCase):
Returns:
"""
super().setUp()
# Set the default group as only group for the user
default_group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([default_group])

View File

@ -45,12 +45,11 @@ class Mailer:
auth_password=self.auth_password
)
def send_mail_shared_access_removed(self, obj_identifier, obj_title, user):
def send_mail_shared_access_removed(self, obj_identifier, user):
""" Send a mail if user has no access to the object anymore
Args:
obj_identifier (str): The object identifier
obj_title (str): The object title
Returns:
@ -58,7 +57,6 @@ class Mailer:
context = {
"user": user,
"obj_identifier": obj_identifier,
"obj_title": obj_title,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/sharing/shared_access_removed.html", context)
@ -69,7 +67,7 @@ class Mailer:
msg
)
def send_mail_shared_access_given(self, obj_identifier, obj_title, user):
def send_mail_shared_access_given(self, obj_identifier, user):
""" Send a mail if user just got access to the object
Args:
@ -81,7 +79,6 @@ class Mailer:
context = {
"user": user,
"obj_identifier": obj_identifier,
"obj_title": obj_title,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/sharing/shared_access_given.html", context)
@ -92,7 +89,7 @@ class Mailer:
msg
)
def send_mail_shared_data_recorded(self, obj_identifier, obj_title, user):
def send_mail_shared_data_recorded(self, obj_identifier, user):
""" Send a mail if the user's shared data has just been unrecorded
Args:
@ -104,7 +101,6 @@ class Mailer:
context = {
"user": user,
"obj_identifier": obj_identifier,
"obj_title": obj_title,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/recording/shared_data_recorded.html", context)
@ -115,7 +111,7 @@ class Mailer:
msg
)
def send_mail_shared_data_unrecorded(self, obj_identifier, obj_title, user):
def send_mail_shared_data_unrecorded(self, obj_identifier, user):
""" Send a mail if the user's shared data has just been unrecorded
Args:
@ -127,7 +123,6 @@ class Mailer:
context = {
"user": user,
"obj_identifier": obj_identifier,
"obj_title": obj_title,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/recording/shared_data_unrecorded.html", context)
@ -138,7 +133,7 @@ class Mailer:
msg
)
def send_mail_shared_data_deleted(self, obj_identifier, obj_title, user):
def send_mail_shared_data_deleted(self, obj_identifier, user):
""" Send a mail if shared data has just been deleted
Args:
@ -150,7 +145,6 @@ class Mailer:
context = {
"user": user,
"obj_identifier": obj_identifier,
"obj_title": obj_title,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/deleting/shared_data_deleted.html", context)
@ -161,7 +155,7 @@ class Mailer:
msg
)
def send_mail_shared_data_checked(self, obj_identifier, obj_title, user):
def send_mail_shared_data_checked(self, obj_identifier, user):
""" Send a mail if shared data just has been checked
Args:
@ -173,7 +167,6 @@ class Mailer:
context = {
"user": user,
"obj_identifier": obj_identifier,
"obj_title": obj_title,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/checking/shared_data_checked.html", context)

View File

@ -40,10 +40,6 @@ COMPENSATION_ACTION_REMOVED = _("Action removed")
DEDUCTION_ADDED = _("Deduction added")
DEDUCTION_REMOVED = _("Deduction removed")
# DEADLINE
DEADLINE_ADDED = _("Deadline added")
DEADLINE_REMOVED = _("Deadline removed")
# PAYMENTS
PAYMENT_ADDED = _("Payment added")
PAYMENT_REMOVED = _("Payment removed")
@ -62,6 +58,3 @@ ADDED_DEADLINE = _("Added deadline")
# Geometry conflicts
GEOMETRY_CONFLICT_WITH_TEMPLATE = _("Geometry conflict detected with {}")
# INTERVENTION
INTERVENTION_HAS_REVOCATIONS_TEMPLATE = _("This intervention has {} revocations")

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,6 @@
<br>
<strong>{{obj_identifier}}</strong>
<br>
<strong>{{obj_title}}</strong>
<br>
{% trans 'This means, the responsible registration office just confirmed the correctness of this dataset.' %}
<br>
<br>

View File

@ -11,8 +11,6 @@
<br>
<strong>{{obj_identifier}}</strong>
<br>
<strong>"{{obj_title}}"</strong>
<br>
{% trans 'If this should not have been happened, please contact us. See the signature for details.' %}
<br>
<br>

View File

@ -11,8 +11,6 @@
<br>
<strong>{{obj_identifier}}</strong>
<br>
<strong>"{{obj_title}}"</strong>
<br>
{% trans 'This means the data is now publicly available, e.g. in LANIS' %}
<br>
<br>

View File

@ -11,8 +11,6 @@
<br>
<strong>{{obj_identifier}}</strong>
<br>
<strong>"{{obj_title}}"</strong>
<br>
{% trans 'This means the data is no longer publicly available.' %}
<br>
<br>

View File

@ -11,8 +11,6 @@
<br>
<strong>{{obj_identifier}}</strong>
<br>
<strong>"{{obj_title}}"</strong>
<br>
{% trans 'This means you can now edit this dataset.' %}
{% trans 'The shared dataset appears now by default on your overview for this dataset type.' %}
<br>

View File

@ -11,8 +11,6 @@
<br>
<strong>{{obj_identifier}}</strong>
<br>
<strong>"{{obj_title}}"</strong>
<br>
{% trans 'However, you are still able to view the dataset content.' %}
{% trans 'Please use the provided search filter on the dataset`s overview pages to find them.' %}
<br>

View File

@ -1,9 +0,0 @@
{% load i18n fontawesome_5 %}
{% for entry in entries %}
<span class="badge pill-badge rlp-r">{{entry}}</span>
{% empty %}
<span class="text-info" title="{% trans 'If the geometry is not empty, the parcels are currently recalculated. Please refresh this page in a few moments.' %}">
{% fa5_icon 'hourglass-half' %}
</span>
{% endfor %}

View File

@ -1,14 +0,0 @@
{% load i18n fontawesome_5 %}
{% if has_revocations %}
<strong>
<a href="{{url}}" title="{% trans 'Revocations exists' %}">
{% fa5_icon 'ban' %}
{{content}}
</a>
</strong>
{% else %}
<a href="{{url}}" title="{{tooltip}}">
{{content}}
</a>
{% endif %}

View File

@ -60,12 +60,11 @@ class User(AbstractUser):
name=ETS_GROUP
).exists()
def send_mail_shared_access_removed(self, obj_identifier, obj_title):
def send_mail_shared_access_removed(self, obj_identifier):
""" Sends a mail to the user in case of removed shared access
Args:
obj_identifier ():
obj_title ():
Returns:
@ -73,9 +72,9 @@ class User(AbstractUser):
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_ACCESS_REMOVED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_access_removed(obj_identifier, obj_title, self)
mailer.send_mail_shared_access_removed(obj_identifier, self)
def send_mail_shared_access_given(self, obj_identifier, obj_title):
def send_mail_shared_access_given(self, obj_identifier):
""" Sends a mail to the user in case of given shared access
Args:
@ -87,9 +86,9 @@ class User(AbstractUser):
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_ACCESS_GAINED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_access_given(obj_identifier, obj_title, self)
mailer.send_mail_shared_access_given(obj_identifier, self)
def send_mail_shared_data_recorded(self, obj_identifier, obj_title):
def send_mail_shared_data_recorded(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been recorded
Args:
@ -101,9 +100,9 @@ class User(AbstractUser):
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_RECORDED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_recorded(obj_identifier, obj_title, self)
mailer.send_mail_shared_data_recorded(obj_identifier, self)
def send_mail_shared_data_unrecorded(self, obj_identifier, obj_title):
def send_mail_shared_data_unrecorded(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been unrecorded
Args:
@ -115,9 +114,9 @@ class User(AbstractUser):
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_RECORDED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_unrecorded(obj_identifier, obj_title, self)
mailer.send_mail_shared_data_unrecorded(obj_identifier, self)
def send_mail_shared_data_deleted(self, obj_identifier, obj_title):
def send_mail_shared_data_deleted(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been deleted
Args:
@ -129,9 +128,9 @@ class User(AbstractUser):
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_DELETED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_deleted(obj_identifier, obj_title, self)
mailer.send_mail_shared_data_deleted(obj_identifier, self)
def send_mail_shared_data_checked(self, obj_identifier, obj_title):
def send_mail_shared_data_checked(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been deleted
Args:
@ -143,7 +142,7 @@ class User(AbstractUser):
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_CHECKED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_checked(obj_identifier, obj_title, self)
mailer.send_mail_shared_data_checked(obj_identifier, self)
def get_API_token(self):
""" Getter for an API token