Compare commits
170 Commits
1b6eea2c9e
...
Docker
| Author | SHA1 | Date | |
|---|---|---|---|
| fac658e52c | |||
| f146aa983a | |||
| 60e9430542 | |||
| d6a65dd59a | |||
| 970d0e79fa | |||
| 3f33de3626 | |||
| cbc8acf6f6 | |||
| 9e5bb84ab4 | |||
| 4c372c1a04 | |||
| 31de477f26 | |||
| ee2c859a9e | |||
| 328f672ec0 | |||
| 88058d7caf | |||
| 0e6f8d5b55 | |||
| 19b6e633df | |||
| 047c9489fe | |||
| 38b81996ed | |||
| 3966521cd4 | |||
| e70a8b51d1 | |||
| 02dc0d0a59 | |||
| 0b84d418db | |||
| 6aad76866f | |||
| 1af807deae | |||
| a2bda8d230 | |||
| e4c459f92e | |||
| 2da6f1dc6f | |||
| 72914bab9d | |||
| fdf3adf5ae | |||
| b98f821c98 | |||
| 4c4d64cc3d | |||
| fbde03caec | |||
| 43eb598d3f | |||
| 5421de4e80 | |||
| b7fac0ae03 | |||
| 447ba942b5 | |||
| a7a0044fc5 | |||
| 6df47f1615 | |||
| e25d549a97 | |||
| 5e65b8f4dc | |||
| 36552b3886 | |||
| 22cddb9902 | |||
| c986bd0b92 | |||
| a766c4dbe8 | |||
| 2c60d86177 | |||
| b7792ececc | |||
| 8126781b77 | |||
| 210f3fcafa | |||
| e7d67560f2 | |||
| b5e991fb95 | |||
| d3a555d406 | |||
| a6a66d7499 | |||
| 9a374f50de | |||
| ce63dd30bc | |||
| 1c0b67693d | |||
| ce6bb6b23b | |||
| 0b8176db2e | |||
| 3a299a040a | |||
| 3c5206139b | |||
| 6c53f39a28 | |||
| 64d8f47174 | |||
| f5f3246e89 | |||
| ad8961ab82 | |||
| c2c8630c82 | |||
| dce9e1fc71 | |||
| 2b84bab1d0 | |||
| 303583daa1 | |||
| d07b2ffbfb | |||
| 335800c44b | |||
| 5766cfde47 | |||
| 2ed3fcc0f9 | |||
| bf72295615 | |||
| 6b860f8ea5 | |||
| 2fa2fa547b | |||
| 3de956872c | |||
| 1c8e3992d6 | |||
| e6e9e141c8 | |||
| f8ece06ee8 | |||
| 149a351bfd | |||
| 0164717b8e | |||
| 104952bfc3 | |||
| f96241c8d1 | |||
| ac6b534f58 | |||
| 06910cd69a | |||
| a48ba520fc | |||
| 9f18aa5890 | |||
| ab3bd84f3b | |||
| f829cd5a4c | |||
| 0f2bf95b71 | |||
| 6a307016ec | |||
| 51017ef8fa | |||
| 05560534bc | |||
| c882173e78 | |||
| 1d94211428 | |||
| 37357080d8 | |||
| 5afa13ac92 | |||
| 416cad1c8f | |||
| b5f83b7163 | |||
| 20cfb5f345 | |||
| 88c96b95f2 | |||
| f6c500b02a | |||
| d702cd8716 | |||
| 329cdd4838 | |||
| 1b70024a29 | |||
| 58206853ee | |||
| 6356398c40 | |||
| 8519922d78 | |||
| 5ac0654fd4 | |||
| 6c07a81b4f | |||
| ba45b4f961 | |||
| 280de82a52 | |||
| 6022e2d879 | |||
| 1996efcc0d | |||
| 80569119cb | |||
| 98e71d4e8a | |||
| fec7191ac2 | |||
| 9b1085f206 | |||
| b35d175a5c | |||
| 7f5fb022ac | |||
| 2d3314ab18 | |||
| 8b489f013d | |||
| 16ce5506d8 | |||
| e440bf8372 | |||
| 607db267e6 | |||
| 352ca64e09 | |||
| f2b735da6e | |||
| 6f7cfb713e | |||
| 103b703ee9 | |||
| daf8b1dce6 | |||
| c088affd74 | |||
| ecc727c991 | |||
| 632569fa5d | |||
| 6c6cbb7396 | |||
| 5e6bfdf77e | |||
| 35e5e18b79 | |||
| c0e8c6bd84 | |||
| 64541b76c5 | |||
| f65b9262cb | |||
| 2765d0548e | |||
| 951f810ce5 | |||
| d2c177d448 | |||
| 299727a7b4 | |||
| b97976b2c5 | |||
| 20241661ff | |||
| ad5c0bea67 | |||
| 80a44277bc | |||
| 5c2b5affc9 | |||
| cd99743d1e | |||
| b39432be1a | |||
| 03f9a33e54 | |||
| 699a9c1e76 | |||
| 4dfd02291e | |||
| e7ca485a88 | |||
| 8319cbfe17 | |||
| 4a023e9f10 | |||
| 4100f96dc6 | |||
| ca24f098e4 | |||
| 80dcd62199 | |||
| 0cfd3da728 | |||
| e141851a87 | |||
| 89ec67999b | |||
| ec38daaedc | |||
| 45c0826a84 | |||
| 45a383cf85 | |||
| 90aff209f9 | |||
| 13528e91e9 | |||
| 04179d633c | |||
| 0a241305d3 | |||
| 31565a0bc4 | |||
| af747417d3 | |||
| c6606c4151 |
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Nutze ein schlankes Python-Image
|
||||||
|
FROM python:3.11-slim-bullseye
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
|
WORKDIR /konova
|
||||||
|
|
||||||
|
# Installiere System-Abhängigkeiten
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
gdal-bin redis-server nginx \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* # Platz sparen
|
||||||
|
|
||||||
|
# Erstelle benötigte Verzeichnisse & setze Berechtigungen
|
||||||
|
RUN mkdir -p /var/log/nginx /var/log/gunicorn /var/lib/nginx /tmp/nginx_client_body \
|
||||||
|
&& touch /var/log/nginx/access.log /var/log/nginx/error.log \
|
||||||
|
&& chown -R root:root /var/log/nginx /var/lib/nginx /tmp/nginx_client_body
|
||||||
|
|
||||||
|
# Kopiere und installiere Python-Abhängigkeiten
|
||||||
|
COPY ./requirements.txt /konova/
|
||||||
|
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Entferne Standard-Nginx-Site und ersetze sie durch eigene Config
|
||||||
|
RUN rm -rf /etc/nginx/sites-enabled/default
|
||||||
|
COPY ./nginx.conf /etc/nginx/conf.d
|
||||||
|
|
||||||
|
# Kopiere restliche Projektdateien
|
||||||
|
COPY . /konova/
|
||||||
|
|
||||||
|
# Sammle statische Dateien
|
||||||
|
RUN python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# Exponiere Ports
|
||||||
|
#EXPOSE 80 6379 8000
|
||||||
|
|
||||||
|
# Setze Entrypoint
|
||||||
|
ENTRYPOINT ["/konova/docker-entrypoint.sh"]
|
||||||
56
README.md
56
README.md
@@ -4,6 +4,7 @@ the database postgresql and the css library bootstrap as well as the icon packag
|
|||||||
fontawesome for a modern look, following best practices from the industry.
|
fontawesome for a modern look, following best practices from the industry.
|
||||||
|
|
||||||
## Background processes
|
## Background processes
|
||||||
|
### !!! For non-docker run
|
||||||
Konova uses celery for background processing. To start the worker you need to run
|
Konova uses celery for background processing. To start the worker you need to run
|
||||||
```shell
|
```shell
|
||||||
$ celery -A konova worker -l INFO
|
$ celery -A konova worker -l INFO
|
||||||
@@ -18,3 +19,58 @@ Technical documention is provided in the projects git wiki.
|
|||||||
A user documentation is not available (and not needed, yet).
|
A user documentation is not available (and not needed, yet).
|
||||||
|
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
To run the docker-compose as expected, you need to take the following steps:
|
||||||
|
|
||||||
|
1. Create a database containing docker, using an appropriate Dockerfile, e.g. the following
|
||||||
|
```
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
postgis:
|
||||||
|
image: postgis/postgis
|
||||||
|
restart: always
|
||||||
|
container_name: postgis-docker
|
||||||
|
ports:
|
||||||
|
- 5433:5432
|
||||||
|
volumes:
|
||||||
|
- db-volume:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
- POSTGRES_USER=postgres
|
||||||
|
networks:
|
||||||
|
- db-network-bridge
|
||||||
|
|
||||||
|
networks:
|
||||||
|
db-network-bridge:
|
||||||
|
driver: "bridge"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-volume:
|
||||||
|
```
|
||||||
|
This Dockerfile creates a Docker container running postgresql and postgis, creates the default superuser postgres,
|
||||||
|
creates a named volume for persisting the database and creates a new network bridge, which **must be used by any other
|
||||||
|
container, which wants to write/read on this database**.
|
||||||
|
|
||||||
|
2. Make sure the name of the network bridge above matches the network in the konova docker-compose.yml
|
||||||
|
3. Get into the running postgis container (`docker exec -it postgis-docker bash`) and create new databases, users and so on. Make sure the database `konova` exists now!
|
||||||
|
4. Replace all `CHANGE_ME_xy` values inside of konova/docker-compose.yml for your installation. Make sure the `SSO_HOST` holds the proper SSO host, e.g. for the arnova project `arnova.example.org` (Arnova must be installed and the webserver configured as well, of course)
|
||||||
|
5. Take a look on konova/settings.py and konova/sub_settings/django_settings.py. Again: Replace all occurences of `CHANGE_ME` with proper values for your installation.
|
||||||
|
1. Make sure you have the proper host strings added to `ALLOWED_HOSTS` inside of django_settings.py.
|
||||||
|
6. Build and run the docker setup using `docker-compose build` and `docker-compose start` from the main directory of this project (where the docker-compose.yml lives)
|
||||||
|
7. Run migrations! To do so, get into the konova service container (`docker exec -it konova-docker bash`) and run the needed commands (`python manage.py makemigrations LIST_OF_ALL_MIGRATABLE_APPS`, then `python manage.py migrate`)
|
||||||
|
8. Run the setup command `python manage.py setup` and follow the instructions on the CLI
|
||||||
|
9. To enable **SMTP** mail support, make sure your host machine (the one where the docker container run) has the postfix service configured properly. Make sure the `mynetworks` variable is xtended using the docker network bridge ip, created in the postgis container and used by the konova services.
|
||||||
|
1. **Hint**: You can find out this easily by trying to perform a test mail in the running konova web application (which will fail, of course). Then take a look to the latest entries in `/var/log/mail.log` on your host machine. The failed IP will be displayed there.
|
||||||
|
2. **Please note**: This installation guide is based on SMTP using postfix!
|
||||||
|
3. Restart the postfix service on your host machine to reload the new configuration (`service postfix restart`)
|
||||||
|
10. Finally, make sure your host machine webserver passes incoming requests properly to the docker nginx webserver of konova. A proper nginx config for the host machine may look like this:
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
server_name konova.domain.org;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:KONOVA_NGINX_DOCKER_PORT/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -10,6 +10,6 @@ from analysis.views import *
|
|||||||
|
|
||||||
app_name = "analysis"
|
app_name = "analysis"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("reports/", index_reports_view, name="reports"),
|
path("reports/", ReportIndexView.as_view(), name="reports"),
|
||||||
path("reports/<id>", detail_report_view, name="report-detail"),
|
path("reports/<id>", ReportDetailView.as_view(), name="report-detail"),
|
||||||
]
|
]
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
from django.views.generic import DetailView
|
||||||
|
|
||||||
from analysis.forms import TimespanReportForm
|
from analysis.forms import TimespanReportForm
|
||||||
from analysis.utils.excel.excel import TempExcelFile
|
from analysis.utils.excel.excel import TempExcelFile
|
||||||
@@ -42,57 +46,112 @@ def index_reports_view(request: HttpRequest):
|
|||||||
context = BaseContext(request, context).context
|
context = BaseContext(request, context).context
|
||||||
return render(request, template, context)
|
return render(request, template, context)
|
||||||
|
|
||||||
|
class ReportIndexView(LoginRequiredMixin, View):
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
def get(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
|
||||||
@login_required
|
"""
|
||||||
@conservation_office_group_required
|
|
||||||
def detail_report_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders the detailed report for a conservation office
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The incoming request
|
request (HttpRequest): The incoming request
|
||||||
id (str): The conservation_office KonovaCode id
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Try to resolve the requested office id
|
template = "analysis/reports/index.html"
|
||||||
cons_office = get_object_or_404(
|
form = TimespanReportForm(None)
|
||||||
KonovaCode,
|
context = {
|
||||||
id=id
|
"form": form
|
||||||
)
|
}
|
||||||
# Try to resolve the date parameters into Date objects -> redirect if this fails
|
context = BaseContext(request, context).context
|
||||||
try:
|
return render(request, template, context)
|
||||||
df = request.GET.get("df", None)
|
|
||||||
dt = request.GET.get("dt", None)
|
@method_decorator(conservation_office_group_required)
|
||||||
date_from = timezone.make_aware(timezone.datetime.fromisoformat(df))
|
def post(self, request: HttpRequest) -> HttpResponse:
|
||||||
date_to = timezone.make_aware(timezone.datetime.fromisoformat(dt))
|
|
||||||
except ValueError:
|
"""
|
||||||
messages.error(
|
|
||||||
request,
|
Args:
|
||||||
PARAMS_INVALID,
|
request (HttpRequest): The incoming request
|
||||||
extra_tags="danger",
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "analysis/reports/index.html"
|
||||||
|
form = TimespanReportForm(request.POST or None)
|
||||||
|
if form.is_valid():
|
||||||
|
redirect_url = form.save()
|
||||||
|
return redirect(redirect_url)
|
||||||
|
else:
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
FORM_INVALID,
|
||||||
|
extra_tags="danger",
|
||||||
|
)
|
||||||
|
context = {
|
||||||
|
"form": form
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportDetailView(LoginRequiredMixin, DetailView):
|
||||||
|
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
def get(self, request: HttpRequest, id: str):
|
||||||
|
""" Renders the detailed report for a conservation office
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The conservation_office KonovaCode id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Try to resolve the requested office id
|
||||||
|
cons_office = get_object_or_404(
|
||||||
|
KonovaCode,
|
||||||
|
id=id
|
||||||
)
|
)
|
||||||
return redirect("analysis:reports")
|
# Try to resolve the date parameters into Date objects -> redirect if this fails
|
||||||
|
try:
|
||||||
|
df = request.GET.get("df", None)
|
||||||
|
dt = request.GET.get("dt", None)
|
||||||
|
date_from = timezone.make_aware(timezone.datetime.fromisoformat(df))
|
||||||
|
date_to = timezone.make_aware(timezone.datetime.fromisoformat(dt))
|
||||||
|
except ValueError:
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
PARAMS_INVALID,
|
||||||
|
extra_tags="danger",
|
||||||
|
)
|
||||||
|
return redirect("analysis:reports")
|
||||||
|
|
||||||
# Check whether the html default rendering is requested or an alternative
|
# Check whether the html default rendering is requested or an alternative
|
||||||
format_param = request.GET.get("format", "html")
|
format_param = request.GET.get("format", "html")
|
||||||
report = TimespanReport(id, date_from, date_to)
|
report = TimespanReport(id, date_from, date_to)
|
||||||
|
|
||||||
if format_param == "html":
|
if format_param == "html":
|
||||||
|
return self.__handle_html_format(request, report, cons_office)
|
||||||
|
elif format_param == "excel":
|
||||||
|
return self.__handle_excel_format(report, cons_office, df, dt)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __handle_html_format(self, request, report: TimespanReport, office: KonovaCode):
|
||||||
template = "analysis/reports/detail.html"
|
template = "analysis/reports/detail.html"
|
||||||
context = {
|
context = {
|
||||||
"office": cons_office,
|
"office": office,
|
||||||
"report": report,
|
"report": report,
|
||||||
}
|
}
|
||||||
context = BaseContext(request, context).context
|
context = BaseContext(request, context).context
|
||||||
return render(request, template, context)
|
return render(request, template, context)
|
||||||
elif format_param == "excel":
|
|
||||||
|
def __handle_excel_format(self, report: TimespanReport, office: KonovaCode, df: str, dt: str):
|
||||||
file = TempExcelFile(report.excel_template_path, report.excel_map)
|
file = TempExcelFile(report.excel_template_path, report.excel_map)
|
||||||
response = HttpResponse(
|
response = HttpResponse(
|
||||||
content=file.stream,
|
content=file.stream,
|
||||||
content_type="application/ms-excel",
|
content_type="application/ms-excel",
|
||||||
)
|
)
|
||||||
response['Content-Disposition'] = f'attachment; filename={cons_office.long_name}_{df}_{dt}.xlsx'
|
response['Content-Disposition'] = f'attachment; filename={office.long_name}_{df}_{dt}.xlsx'
|
||||||
return response
|
return response
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
|
|||||||
# Expect this first request to fail, since user has no shared access on the intervention, we want to create
|
# Expect this first request to fail, since user has no shared access on the intervention, we want to create
|
||||||
# a compensation for
|
# a compensation for
|
||||||
response = self._run_create_request(url, post_body)
|
response = self._run_create_request(url, post_body)
|
||||||
self.assertEqual(response.status_code, 500, msg=response.content)
|
self.assertEqual(response.status_code, 400, msg=response.content)
|
||||||
content = json.loads(response.content)
|
content = json.loads(response.content)
|
||||||
self.assertGreater(len(content.get("errors", [])), 0, msg=response.content)
|
self.assertGreater(len(content.get("errors", [])), 0, msg=response.content)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ Created on: 21.01.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
from json import JSONDecodeError
|
||||||
|
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.http import JsonResponse, HttpRequest
|
from django.http import JsonResponse, HttpRequest
|
||||||
|
|
||||||
from api.utils.serializer.v1.compensation import CompensationAPISerializerV1
|
from api.utils.serializer.v1.compensation import CompensationAPISerializerV1
|
||||||
@@ -66,8 +68,12 @@ class AbstractAPIViewV1(AbstractAPIView):
|
|||||||
body = request.body.decode("utf-8")
|
body = request.body.decode("utf-8")
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
created_id = self.serializer.create_model_from_json(body, self.user)
|
created_id = self.serializer.create_model_from_json(body, self.user)
|
||||||
except Exception as e:
|
except (JSONDecodeError,
|
||||||
return self._return_error_response(e, 500)
|
AssertionError,
|
||||||
|
ValueError,
|
||||||
|
PermissionError,
|
||||||
|
ObjectDoesNotExist) as e:
|
||||||
|
return self._return_error_response(e, 400)
|
||||||
return JsonResponse({"id": created_id})
|
return JsonResponse({"id": created_id})
|
||||||
|
|
||||||
def put(self, request: HttpRequest, id=None):
|
def put(self, request: HttpRequest, id=None):
|
||||||
|
|||||||
@@ -81,9 +81,7 @@ class AbstractAPIView(View):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
content = [error.__str__()]
|
content = [f"{error.__class__.__name__}: {str(error)}"]
|
||||||
if hasattr(error, "messages"):
|
|
||||||
content = error.messages
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
"errors": content
|
"errors": content
|
||||||
|
|||||||
0
codelist/views.py
Normal file
0
codelist/views.py
Normal file
@@ -45,6 +45,14 @@ class AbstractCompensationAdmin(BaseObjectAdmin):
|
|||||||
states = "\n".join(states)
|
states = "\n".join(states)
|
||||||
return states
|
return states
|
||||||
|
|
||||||
|
def get_actions(self, request):
|
||||||
|
DELETE_ACTION_IDENTIFIER = "delete_selected"
|
||||||
|
actions = super().get_actions(request)
|
||||||
|
|
||||||
|
if DELETE_ACTION_IDENTIFIER in actions:
|
||||||
|
del actions[DELETE_ACTION_IDENTIFIER]
|
||||||
|
|
||||||
|
return actions
|
||||||
|
|
||||||
class CompensationAdmin(AbstractCompensationAdmin):
|
class CompensationAdmin(AbstractCompensationAdmin):
|
||||||
autocomplete_fields = [
|
autocomplete_fields = [
|
||||||
|
|||||||
@@ -168,17 +168,6 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
comp.log.add(action)
|
comp.log.add(action)
|
||||||
return comp, action
|
return comp, action
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
valid = super().is_valid()
|
|
||||||
intervention = self.cleaned_data.get("intervention", None)
|
|
||||||
if intervention.is_recorded:
|
|
||||||
valid &= False
|
|
||||||
self.add_error(
|
|
||||||
"intervention",
|
|
||||||
_("This intervention is currently recorded. You cannot add further compensations as long as it is recorded.")
|
|
||||||
)
|
|
||||||
return valid
|
|
||||||
|
|
||||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
comp, action = self.__create_comp(user)
|
comp, action = self.__create_comp(user)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from compensation.models import EcoAccount
|
|||||||
from intervention.models import Handler, Responsibility, Legal
|
from intervention.models import Handler, Responsibility, Legal
|
||||||
from konova.forms import SimpleGeomForm
|
from konova.forms import SimpleGeomForm
|
||||||
from konova.forms.modals import RemoveModalForm
|
from konova.forms.modals import RemoveModalForm
|
||||||
|
from konova.settings import ETS_GROUP
|
||||||
from konova.utils import validators
|
from konova.utils import validators
|
||||||
from user.models import User, UserActionLogEntry
|
from user.models import User, UserActionLogEntry
|
||||||
|
|
||||||
@@ -246,4 +247,13 @@ class RemoveEcoAccountModalForm(RemoveModalForm):
|
|||||||
"confirm",
|
"confirm",
|
||||||
_("The account can not be removed, since there are still deductions.")
|
_("The account can not be removed, since there are still deductions.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# If there are deductions but the performing user is not part of an ETS group, we assume this poor
|
||||||
|
# fella does not know what he/she does -> give a hint that they should contact someone in charge...
|
||||||
|
user_is_ets_user = self.user.in_group(ETS_GROUP)
|
||||||
|
if not user_is_ets_user:
|
||||||
|
self.add_error(
|
||||||
|
"confirm",
|
||||||
|
_("Please contact the responsible conservation office to find a solution!")
|
||||||
|
)
|
||||||
return super_valid and not has_deductions
|
return super_valid and not has_deductions
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ Created on: 18.08.22
|
|||||||
"""
|
"""
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
from compensation.models import CompensationAction
|
|
||||||
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple
|
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple
|
||||||
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
from konova.utils.message_templates import COMPENSATION_ACTION_EDITED, ADDED_COMPENSATION_ACTION
|
from konova.utils.message_templates import COMPENSATION_ACTION_EDITED, ADDED_COMPENSATION_ACTION
|
||||||
@@ -116,8 +114,7 @@ class EditCompensationActionModalForm(NewCompensationActionModalForm):
|
|||||||
action = None
|
action = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
action_id = kwargs.pop("action_id", None)
|
self.action = kwargs.pop("action", None)
|
||||||
self.action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit action")
|
self.form_title = _("Edit action")
|
||||||
form_data = {
|
form_data = {
|
||||||
@@ -150,8 +147,8 @@ class RemoveCompensationActionModalForm(RemoveModalForm):
|
|||||||
action = None
|
action = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
action_id = kwargs.pop("action_id", None)
|
action = kwargs.pop("action", None)
|
||||||
self.action = get_object_or_404(CompensationAction, id=action_id)
|
self.action = action
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ Created on: 18.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from konova.forms.modals import BaseModalForm
|
from konova.forms.modals import BaseModalForm
|
||||||
from konova.models import DeadlineType, Deadline
|
from konova.models import DeadlineType
|
||||||
from konova.utils import validators
|
from konova.utils import validators
|
||||||
from konova.utils.message_templates import DEADLINE_EDITED
|
from konova.utils.message_templates import DEADLINE_EDITED
|
||||||
|
|
||||||
@@ -91,8 +90,7 @@ class EditDeadlineModalForm(NewDeadlineModalForm):
|
|||||||
deadline = None
|
deadline = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
deadline_id = kwargs.pop("deadline_id", None)
|
self.deadline = kwargs.pop("deadline", None)
|
||||||
self.deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit deadline")
|
self.form_title = _("Edit deadline")
|
||||||
form_data = {
|
form_data = {
|
||||||
|
|||||||
@@ -6,27 +6,12 @@ Created on: 18.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.models import CompensationDocument, EcoAccountDocument
|
from compensation.models import CompensationDocument, EcoAccountDocument
|
||||||
from konova.forms.modals import NewDocumentModalForm, EditDocumentModalForm, RemoveDocumentModalForm
|
from konova.forms.modals import NewDocumentModalForm
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationDocumentModalForm(NewDocumentModalForm):
|
class NewCompensationDocumentModalForm(NewDocumentModalForm):
|
||||||
_DOCUMENT_CLS = CompensationDocument
|
document_model = CompensationDocument
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationDocumentModalForm(EditDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = CompensationDocument
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationDocumentModalForm(RemoveDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = CompensationDocument
|
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountDocumentModalForm(NewDocumentModalForm):
|
class NewEcoAccountDocumentModalForm(NewDocumentModalForm):
|
||||||
_DOCUMENT_CLS = EcoAccountDocument
|
document_model = EcoAccountDocument
|
||||||
|
|
||||||
class EditEcoAccountDocumentModalForm(EditDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = EcoAccountDocument
|
|
||||||
|
|
||||||
class RemoveEcoAccountDocumentModalForm(RemoveDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = EcoAccountDocument
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ Created on: 18.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import pgettext_lazy as _con, gettext_lazy as _
|
from django.utils.translation import pgettext_lazy as _con, gettext_lazy as _
|
||||||
|
|
||||||
from compensation.models import Payment
|
|
||||||
from konova.forms.modals import RemoveModalForm, BaseModalForm
|
from konova.forms.modals import RemoveModalForm, BaseModalForm
|
||||||
from konova.utils import validators
|
from konova.utils import validators
|
||||||
from konova.utils.message_templates import PAYMENT_EDITED
|
from konova.utils.message_templates import PAYMENT_EDITED
|
||||||
@@ -105,8 +103,7 @@ class EditPaymentModalForm(NewPaymentForm):
|
|||||||
payment = None
|
payment = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
payment_id = kwargs.pop("payment_id", None)
|
self.payment = kwargs.pop("payment", None)
|
||||||
self.payment = get_object_or_404(Payment, id=payment_id)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit payment")
|
self.form_title = _("Edit payment")
|
||||||
form_date = {
|
form_date = {
|
||||||
@@ -136,8 +133,8 @@ class RemovePaymentModalForm(RemoveModalForm):
|
|||||||
payment = None
|
payment = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
payment_id = kwargs.pop("payment_id", None)
|
payment = kwargs.pop("payment", None)
|
||||||
self.payment = get_object_or_404(Payment, id=payment_id)
|
self.payment = payment
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Created on: 21.10.25
|
|
||||||
|
|
||||||
"""
|
|
||||||
from compensation.models import Compensation, EcoAccount
|
|
||||||
from konova.forms.modals import ResubmissionModalForm
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationResubmissionModalForm(ResubmissionModalForm):
|
|
||||||
_MODEL_CLS = Compensation
|
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountResubmissionModalForm(ResubmissionModalForm):
|
|
||||||
_MODEL_CLS = EcoAccount
|
|
||||||
@@ -5,17 +5,21 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 18.08.22
|
Created on: 18.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from bootstrap_modal_forms.mixins import is_ajax
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import HttpResponseRedirect, HttpRequest
|
||||||
|
from django.shortcuts import render
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_BIOTOPES_ID, \
|
from codelist.settings import CODELIST_BIOTOPES_ID, \
|
||||||
CODELIST_BIOTOPES_EXTRA_CODES_FULL_ID
|
CODELIST_BIOTOPES_EXTRA_CODES_FULL_ID
|
||||||
from compensation.models import CompensationState
|
|
||||||
from intervention.inputs import CompensationStateTreeRadioSelect
|
from intervention.inputs import CompensationStateTreeRadioSelect
|
||||||
|
from konova.contexts import BaseContext
|
||||||
from konova.forms.modals import RemoveModalForm, BaseModalForm
|
from konova.forms.modals import RemoveModalForm, BaseModalForm
|
||||||
from konova.utils.message_templates import COMPENSATION_STATE_EDITED, ADDED_COMPENSATION_STATE
|
from konova.utils.message_templates import COMPENSATION_STATE_EDITED, FORM_INVALID, ADDED_COMPENSATION_STATE
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationStateModalForm(BaseModalForm):
|
class NewCompensationStateModalForm(BaseModalForm):
|
||||||
@@ -64,13 +68,10 @@ class NewCompensationStateModalForm(BaseModalForm):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_is_before_state: bool = False
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("New state")
|
self.form_title = _("New state")
|
||||||
self.form_caption = _("Insert data for the new state")
|
self.form_caption = _("Insert data for the new state")
|
||||||
self._is_before_state = bool(self.request.GET.get("before", False))
|
|
||||||
choices = KonovaCode.objects.filter(
|
choices = KonovaCode.objects.filter(
|
||||||
code_lists__in=[CODELIST_BIOTOPES_ID],
|
code_lists__in=[CODELIST_BIOTOPES_ID],
|
||||||
is_archived=False,
|
is_archived=False,
|
||||||
@@ -82,19 +83,65 @@ class NewCompensationStateModalForm(BaseModalForm):
|
|||||||
]
|
]
|
||||||
self.fields["biotope_type"].choices = choices
|
self.fields["biotope_type"].choices = choices
|
||||||
|
|
||||||
def save(self):
|
def save(self, is_before_state: bool = False):
|
||||||
state = self.instance.add_state(self, self._is_before_state)
|
state = self.instance.add_state(self, is_before_state)
|
||||||
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_STATE)
|
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_STATE)
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None):
|
||||||
|
""" Generic processing of request
|
||||||
|
|
||||||
|
Wraps the request processing logic, so we don't need the same code everywhere a RemoveModalForm is being used
|
||||||
|
|
||||||
|
+++
|
||||||
|
The generic method from super class can not be used, since we need to do some request parameter check in here.
|
||||||
|
+++
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
msg_success (str): The message in case of successful removing
|
||||||
|
msg_error (str): The message in case of an error
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home")
|
||||||
|
template = self.template
|
||||||
|
if request.method == "POST":
|
||||||
|
if self.is_valid():
|
||||||
|
# Modal forms send one POST for checking on data validity. This can be used to return possible errors
|
||||||
|
# on the form. A second POST (if no errors occured) is sent afterwards and needs to process the
|
||||||
|
# saving/commiting of the data to the database. is_ajax() performs this check. The first request is
|
||||||
|
# an ajax call, the second is a regular form POST.
|
||||||
|
if not is_ajax(request.META):
|
||||||
|
is_before_state = bool(request.GET.get("before", False))
|
||||||
|
self.save(is_before_state=is_before_state)
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
msg_success
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(redirect_url)
|
||||||
|
else:
|
||||||
|
context = {
|
||||||
|
"form": self,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
elif request.method == "GET":
|
||||||
|
context = {
|
||||||
|
"form": self,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationStateModalForm(NewCompensationStateModalForm):
|
class EditCompensationStateModalForm(NewCompensationStateModalForm):
|
||||||
state = None
|
state = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
state_id = kwargs.pop("state_id", None)
|
self.state = kwargs.pop("state", None)
|
||||||
self.state = CompensationState.objects.get(id=state_id)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit state")
|
self.form_title = _("Edit state")
|
||||||
biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
|
biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
|
||||||
@@ -125,8 +172,8 @@ class RemoveCompensationStateModalForm(RemoveModalForm):
|
|||||||
state = None
|
state = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
state_id = kwargs.pop("state_id", None)
|
state = kwargs.pop("state", None)
|
||||||
self.state = CompensationState.objects.get(id=state_id)
|
self.state = state
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% if deduction.intervention.recorded %}
|
{% if deduction.intervention.recorded %}
|
||||||
<em title="{% trans 'Recorded on' %} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
|
<em title="{% trans 'Recorded on' %} {{deduction.intervention.recorded.timestamp}} {% trans 'by' %} {{deduction.intervention.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
|
||||||
{% else %}
|
{% else %}
|
||||||
<em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
|
<em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -80,11 +80,7 @@ class EditCompensationActionModalFormTestCase(NewCompensationActionModalFormTest
|
|||||||
self.compensation.actions.add(self.comp_action)
|
self.compensation.actions.add(self.comp_action)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
form = EditCompensationActionModalForm(
|
form = EditCompensationActionModalForm(request=self.request, instance=self.compensation, action=self.comp_action)
|
||||||
request=self.request,
|
|
||||||
instance=self.compensation,
|
|
||||||
action_id=self.comp_action.id
|
|
||||||
)
|
|
||||||
self.assertEqual(form.form_title, str(_("Edit action")))
|
self.assertEqual(form.form_title, str(_("Edit action")))
|
||||||
self.assertEqual(len(form.fields["action_type"].initial), self.comp_action.action_type.count())
|
self.assertEqual(len(form.fields["action_type"].initial), self.comp_action.action_type.count())
|
||||||
self.assertEqual(len(form.fields["action_type_details"].initial), self.comp_action.action_type_details.count())
|
self.assertEqual(len(form.fields["action_type_details"].initial), self.comp_action.action_type_details.count())
|
||||||
@@ -105,7 +101,7 @@ class EditCompensationActionModalFormTestCase(NewCompensationActionModalFormTest
|
|||||||
"comment": comment,
|
"comment": comment,
|
||||||
}
|
}
|
||||||
|
|
||||||
form = EditCompensationActionModalForm(data, request=self.request, instance=self.compensation, action_id=self.comp_action.id)
|
form = EditCompensationActionModalForm(data, request=self.request, instance=self.compensation, action=self.comp_action)
|
||||||
|
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
action = form.save()
|
action = form.save()
|
||||||
@@ -130,7 +126,7 @@ class RemoveCompensationActionModalFormTestCase(EditCompensationActionModalFormT
|
|||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
self.assertIn(self.comp_action, self.compensation.actions.all())
|
self.assertIn(self.comp_action, self.compensation.actions.all())
|
||||||
form = RemoveCompensationActionModalForm(request=self.request, instance=self.compensation, action_id=self.comp_action.id)
|
form = RemoveCompensationActionModalForm(request=self.request, instance=self.compensation, action=self.comp_action)
|
||||||
self.assertEqual(form.action, self.comp_action)
|
self.assertEqual(form.action, self.comp_action)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
@@ -141,7 +137,7 @@ class RemoveCompensationActionModalFormTestCase(EditCompensationActionModalFormT
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
action_id=self.comp_action.id
|
action=self.comp_action
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
self.assertIn(self.comp_action, self.compensation.actions.all())
|
self.assertIn(self.comp_action, self.compensation.actions.all())
|
||||||
@@ -190,20 +186,12 @@ class NewCompensationStateModalFormTestCase(BaseTestCase):
|
|||||||
self.assertEqual(self.compensation.before_states.count(), 0)
|
self.assertEqual(self.compensation.before_states.count(), 0)
|
||||||
self.assertEqual(self.compensation.after_states.count(), 0)
|
self.assertEqual(self.compensation.after_states.count(), 0)
|
||||||
|
|
||||||
self.request.GET._mutable = True
|
form = NewCompensationStateModalForm(data, request=self.request, instance=self.compensation)
|
||||||
self.request.GET.update(
|
|
||||||
{
|
|
||||||
"before": True,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.request.GET._mutable = False
|
|
||||||
form = NewCompensationStateModalForm(
|
|
||||||
data,
|
|
||||||
request=self.request,
|
|
||||||
instance=self.compensation,
|
|
||||||
)
|
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
state = form.save()
|
|
||||||
|
is_before_state = True
|
||||||
|
state = form.save(is_before_state)
|
||||||
|
|
||||||
self.assertEqual(self.compensation.before_states.count(), 1)
|
self.assertEqual(self.compensation.before_states.count(), 1)
|
||||||
self.assertEqual(self.compensation.after_states.count(), 0)
|
self.assertEqual(self.compensation.after_states.count(), 0)
|
||||||
@@ -217,16 +205,8 @@ class NewCompensationStateModalFormTestCase(BaseTestCase):
|
|||||||
self.assertEqual(last_log.action, UserAction.EDITED)
|
self.assertEqual(last_log.action, UserAction.EDITED)
|
||||||
self.assertEqual(last_log.comment, ADDED_COMPENSATION_STATE)
|
self.assertEqual(last_log.comment, ADDED_COMPENSATION_STATE)
|
||||||
|
|
||||||
self.request.GET._mutable = True
|
is_before_state = False
|
||||||
del self.request.GET["before"]
|
state = form.save(is_before_state)
|
||||||
self.request.GET._mutable = False
|
|
||||||
form = NewCompensationStateModalForm(
|
|
||||||
data,
|
|
||||||
request=self.request,
|
|
||||||
instance=self.compensation,
|
|
||||||
)
|
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
|
||||||
state = form.save()
|
|
||||||
|
|
||||||
self.assertEqual(self.compensation.before_states.count(), 1)
|
self.assertEqual(self.compensation.before_states.count(), 1)
|
||||||
self.assertEqual(self.compensation.after_states.count(), 1)
|
self.assertEqual(self.compensation.after_states.count(), 1)
|
||||||
@@ -250,11 +230,7 @@ class EditCompensationStateModalFormTestCase(NewCompensationStateModalFormTestCa
|
|||||||
self.compensation.after_states.add(self.comp_state)
|
self.compensation.after_states.add(self.comp_state)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
form = EditCompensationStateModalForm(
|
form = EditCompensationStateModalForm(request=self.request, instance=self.compensation, state=self.comp_state)
|
||||||
request=self.request,
|
|
||||||
instance=self.compensation,
|
|
||||||
state_id=self.comp_state.id
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(form.state, self.comp_state)
|
self.assertEqual(form.state, self.comp_state)
|
||||||
self.assertEqual(form.form_title, str(_("Edit state")))
|
self.assertEqual(form.form_title, str(_("Edit state")))
|
||||||
@@ -285,7 +261,7 @@ class EditCompensationStateModalFormTestCase(NewCompensationStateModalFormTestCa
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
state_id=self.comp_state.id
|
state=self.comp_state
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
|
|
||||||
@@ -306,11 +282,7 @@ class RemoveCompensationStateModalFormTestCase(EditCompensationStateModalFormTes
|
|||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
form = RemoveCompensationStateModalForm(
|
form = RemoveCompensationStateModalForm(request=self.request, instance=self.compensation, state=self.comp_state)
|
||||||
request=self.request,
|
|
||||||
instance=self.compensation,
|
|
||||||
state_id=self.comp_state.id
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(form.state, self.comp_state)
|
self.assertEqual(form.state, self.comp_state)
|
||||||
|
|
||||||
@@ -322,7 +294,7 @@ class RemoveCompensationStateModalFormTestCase(EditCompensationStateModalFormTes
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
state_id=self.comp_state.id
|
state=self.comp_state
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class AbstractCompensationModelTestCase(BaseTestCase):
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
deadline_id=self.finished_deadline.id,
|
deadline=self.finished_deadline,
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
self.assertIn(self.finished_deadline, self.compensation.deadlines.all())
|
self.assertIn(self.finished_deadline, self.compensation.deadlines.all())
|
||||||
|
|||||||
@@ -7,30 +7,31 @@ Created on: 24.08.21
|
|||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
|
from compensation.views.compensation.detail import DetailCompensationView
|
||||||
from compensation.views.compensation.document import EditCompensationDocumentView, NewCompensationDocumentView, \
|
from compensation.views.compensation.document import EditCompensationDocumentView, NewCompensationDocumentView, \
|
||||||
GetCompensationDocumentView, RemoveCompensationDocumentView
|
GetCompensationDocumentView, RemoveCompensationDocumentView
|
||||||
|
from compensation.views.compensation.remove import RemoveCompensationView
|
||||||
from compensation.views.compensation.resubmission import CompensationResubmissionView
|
from compensation.views.compensation.resubmission import CompensationResubmissionView
|
||||||
from compensation.views.compensation.report import CompensationReportView
|
from compensation.views.compensation.report import CompensationPublicReportView
|
||||||
from compensation.views.compensation.deadline import NewCompensationDeadlineView, EditCompensationDeadlineView, \
|
from compensation.views.compensation.deadline import NewCompensationDeadlineView, EditCompensationDeadlineView, \
|
||||||
RemoveCompensationDeadlineView
|
RemoveCompensationDeadlineView
|
||||||
from compensation.views.compensation.action import NewCompensationActionView, EditCompensationActionView, \
|
from compensation.views.compensation.action import NewCompensationActionView, EditCompensationActionView, \
|
||||||
RemoveCompensationActionView
|
RemoveCompensationActionView
|
||||||
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
|
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
|
||||||
RemoveCompensationStateView
|
RemoveCompensationStateView
|
||||||
from compensation.views.compensation.compensation import \
|
from compensation.views.compensation.compensation import IndexCompensationView, CompensationIdentifierGeneratorView, \
|
||||||
CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView, \
|
EditCompensationView, NewCompensationView
|
||||||
NewCompensationFormView, EditCompensationFormView, RemoveCompensationView
|
|
||||||
from compensation.views.compensation.log import CompensationLogView
|
from compensation.views.compensation.log import CompensationLogView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Main compensation
|
# Main compensation
|
||||||
path("", CompensationIndexView.as_view(), name="index"),
|
path("", IndexCompensationView.as_view(), name="index"),
|
||||||
path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'),
|
path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'),
|
||||||
path('new/<intervention_id>', NewCompensationFormView.as_view(), name='new'),
|
path('new/<intervention_id>', NewCompensationView.as_view(), name='new'),
|
||||||
path('new', NewCompensationFormView.as_view(), name='new'),
|
path('new', NewCompensationView.as_view(), name='new'),
|
||||||
path('<id>', CompensationDetailView.as_view(), name='detail'),
|
path('<id>', DetailCompensationView.as_view(), name='detail'),
|
||||||
path('<id>/log', CompensationLogView.as_view(), name='log'),
|
path('<id>/log', CompensationLogView.as_view(), name='log'),
|
||||||
path('<id>/edit', EditCompensationFormView.as_view(), name='edit'),
|
path('<id>/edit', EditCompensationView.as_view(), name='edit'),
|
||||||
path('<id>/remove', RemoveCompensationView.as_view(), name='remove'),
|
path('<id>/remove', RemoveCompensationView.as_view(), name='remove'),
|
||||||
|
|
||||||
path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),
|
path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),
|
||||||
@@ -44,7 +45,7 @@ urlpatterns = [
|
|||||||
path('<id>/deadline/new', NewCompensationDeadlineView.as_view(), name="new-deadline"),
|
path('<id>/deadline/new', NewCompensationDeadlineView.as_view(), name="new-deadline"),
|
||||||
path('<id>/deadline/<deadline_id>/edit', EditCompensationDeadlineView.as_view(), name='deadline-edit'),
|
path('<id>/deadline/<deadline_id>/edit', EditCompensationDeadlineView.as_view(), name='deadline-edit'),
|
||||||
path('<id>/deadline/<deadline_id>/remove', RemoveCompensationDeadlineView.as_view(), name='deadline-remove'),
|
path('<id>/deadline/<deadline_id>/remove', RemoveCompensationDeadlineView.as_view(), name='deadline-remove'),
|
||||||
path('<id>/report', CompensationReportView.as_view(), name='report'),
|
path('<id>/report', CompensationPublicReportView.as_view(), name='report'),
|
||||||
path('<id>/resub', CompensationResubmissionView.as_view(), name='resubmission-create'),
|
path('<id>/resub', CompensationResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ Created on: 24.08.21
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from compensation.autocomplete.eco_account import EcoAccountAutocomplete
|
from compensation.autocomplete.eco_account import EcoAccountAutocomplete
|
||||||
from compensation.views.eco_account.eco_account import EcoAccountIndexView, EcoAccountIdentifierGeneratorView, \
|
from compensation.views.eco_account.detail import DetailEcoAccountView
|
||||||
EcoAccountDetailView, NewEcoAccountFormView, EditEcoAccountFormView, RemoveEcoAccountView
|
from compensation.views.eco_account.eco_account import IndexEcoAccountView, EcoAccountIdentifierGeneratorView, \
|
||||||
|
NewEcoAccountView, EditEcoAccountView
|
||||||
from compensation.views.eco_account.log import EcoAccountLogView
|
from compensation.views.eco_account.log import EcoAccountLogView
|
||||||
from compensation.views.eco_account.record import EcoAccountRecordView
|
from compensation.views.eco_account.record import EcoAccountRecordView
|
||||||
from compensation.views.eco_account.report import EcoAccountReportView
|
from compensation.views.eco_account.remove import RemoveEcoAccountView
|
||||||
|
from compensation.views.eco_account.report import EcoAccountPublicReportView
|
||||||
from compensation.views.eco_account.resubmission import EcoAccountResubmissionView
|
from compensation.views.eco_account.resubmission import EcoAccountResubmissionView
|
||||||
from compensation.views.eco_account.state import NewEcoAccountStateView, EditEcoAccountStateView, \
|
from compensation.views.eco_account.state import NewEcoAccountStateView, EditEcoAccountStateView, \
|
||||||
RemoveEcoAccountStateView
|
RemoveEcoAccountStateView
|
||||||
@@ -28,14 +30,14 @@ from compensation.views.eco_account.deduction import NewEcoAccountDeductionView,
|
|||||||
|
|
||||||
app_name = "acc"
|
app_name = "acc"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", EcoAccountIndexView.as_view(), name="index"),
|
path("", IndexEcoAccountView.as_view(), name="index"),
|
||||||
path('new/', NewEcoAccountFormView.as_view(), name='new'),
|
path('new/', NewEcoAccountView.as_view(), name='new'),
|
||||||
path('new/id', EcoAccountIdentifierGeneratorView.as_view(), name='new-id'),
|
path('new/id', EcoAccountIdentifierGeneratorView.as_view(), name='new-id'),
|
||||||
path('<id>', EcoAccountDetailView.as_view(), name='detail'),
|
path('<id>', DetailEcoAccountView.as_view(), name='detail'),
|
||||||
path('<id>/log', EcoAccountLogView.as_view(), name='log'),
|
path('<id>/log', EcoAccountLogView.as_view(), name='log'),
|
||||||
path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
|
path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
|
||||||
path('<id>/report', EcoAccountReportView.as_view(), name='report'),
|
path('<id>/report', EcoAccountPublicReportView.as_view(), name='report'),
|
||||||
path('<id>/edit', EditEcoAccountFormView.as_view(), name='edit'),
|
path('<id>/edit', EditEcoAccountView.as_view(), name='edit'),
|
||||||
path('<id>/remove', RemoveEcoAccountView.as_view(), name='remove'),
|
path('<id>/remove', RemoveEcoAccountView.as_view(), name='remove'),
|
||||||
path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'),
|
path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Created on: 24.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from compensation.views.payment import NewPaymentView, RemovePaymentView, EditPaymentView
|
from compensation.views.payment import *
|
||||||
|
|
||||||
app_name = "pay"
|
app_name = "pay"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|||||||
@@ -5,23 +5,53 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import Compensation
|
from compensation.forms.modals.compensation_action import RemoveCompensationActionModalForm, \
|
||||||
|
EditCompensationActionModalForm, NewCompensationActionModalForm
|
||||||
|
from compensation.models import Compensation, CompensationAction
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
|
from konova.utils.message_templates import COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_EDITED, \
|
||||||
|
COMPENSATION_ACTION_ADDED
|
||||||
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
||||||
AbstractRemoveCompensationActionView
|
AbstractRemoveCompensationActionView
|
||||||
|
|
||||||
_COMPENSATION_DETAIL_URL_NAME = "compensation:detail"
|
|
||||||
|
|
||||||
class NewCompensationActionView(AbstractNewCompensationActionView):
|
class NewCompensationActionView(AbstractNewCompensationActionView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = _COMPENSATION_DETAIL_URL_NAME
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationActionView(AbstractEditCompensationActionView):
|
class EditCompensationActionView(AbstractEditCompensationActionView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = _COMPENSATION_DETAIL_URL_NAME
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationActionView(AbstractRemoveCompensationActionView):
|
class RemoveCompensationActionView(AbstractRemoveCompensationActionView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = _COMPENSATION_DETAIL_URL_NAME
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -6,169 +6,260 @@ Created on: 19.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.http import HttpRequest, JsonResponse
|
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.urls import reverse
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from compensation.forms.compensation import EditCompensationForm, NewCompensationForm
|
from compensation.forms.compensation import EditCompensationForm, NewCompensationForm
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
from compensation.tables.compensation import CompensationTable
|
from compensation.tables.compensation import CompensationTable
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
from konova.contexts import BaseContext
|
||||||
from konova.forms.modals import RemoveModalForm
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
|
from konova.forms import SimpleGeomForm
|
||||||
RECORDED_BLOCKS_EDIT, PARAMS_INVALID
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, \
|
||||||
BaseEditSpatialLocatedObjectFormView
|
IDENTIFIER_REPLACED, COMPENSATION_ADDED_TEMPLATE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
||||||
from konova.views.detail import BaseDetailView
|
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||||
from konova.views.remove import BaseRemoveModalFormView
|
from konova.views.index import AbstractIndexView
|
||||||
|
|
||||||
|
|
||||||
class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
|
class IndexCompensationView(AbstractIndexView):
|
||||||
_TAB_TITLE = _("Compensations - Overview")
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
||||||
_INDEX_TABLE_CLS = CompensationTable
|
"""
|
||||||
|
Renders the index view for compensation
|
||||||
|
|
||||||
def _get_queryset(self):
|
Args:
|
||||||
qs = Compensation.objects.filter(
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A rendered view
|
||||||
|
"""
|
||||||
|
compensations = Compensation.objects.filter(
|
||||||
deleted=None, # only show those which are not deleted individually
|
deleted=None, # only show those which are not deleted individually
|
||||||
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
|
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
|
||||||
).order_by(
|
).order_by(
|
||||||
"-modified__timestamp"
|
"-modified__timestamp"
|
||||||
)
|
)
|
||||||
return qs
|
table = CompensationTable(
|
||||||
|
request=request,
|
||||||
|
queryset=compensations
|
||||||
class NewCompensationFormView(BaseNewSpatialLocatedObjectFormView):
|
|
||||||
_FORM_CLS = NewCompensationForm
|
|
||||||
_MODEL_CLS = Compensation
|
|
||||||
_TEMPLATE = "compensation/form/view.html"
|
|
||||||
_TAB_TITLE = _("New Compensation")
|
|
||||||
_REDIRECT_URL = "compensation:detail"
|
|
||||||
|
|
||||||
def _user_has_shared_access(self, user, **kwargs):
|
|
||||||
# On a new compensation make sure the intervention (if call came directly through an intervention's detail
|
|
||||||
# view) is shared with the user
|
|
||||||
intervention_id = kwargs.get("intervention_id", None)
|
|
||||||
if not intervention_id:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
intervention = get_object_or_404(Intervention, id=intervention_id)
|
|
||||||
return intervention.is_shared_with(user)
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
# User has to be an ets user
|
|
||||||
return user.is_default_user()
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
# Make sure there is an existing intervention based on the given id
|
|
||||||
# Compensations can not exist without an intervention
|
|
||||||
intervention_id = kwargs.get("intervention_id", None)
|
|
||||||
if intervention_id:
|
|
||||||
try:
|
|
||||||
intervention = Intervention.objects.get(id=intervention_id)
|
|
||||||
if intervention.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("intervention:detail", id=intervention_id)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
messages.error(request, PARAMS_INVALID, extra_tags="danger")
|
|
||||||
return redirect("home")
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationFormView(BaseEditSpatialLocatedObjectFormView):
|
|
||||||
_MODEL_CLS = Compensation
|
|
||||||
_FORM_CLS = EditCompensationForm
|
|
||||||
_TEMPLATE = "compensation/form/view.html"
|
|
||||||
_REDIRECT_URL = "compensation:detail"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
# User has to be a default user
|
|
||||||
return user.is_default_user()
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
|
||||||
_MODEL_CLS = Compensation
|
|
||||||
_REDIRECT_URL = "compensation:index"
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationDetailView(BaseDetailView):
|
|
||||||
_MODEL_CLS = Compensation
|
|
||||||
_TEMPLATE = "compensation/detail/compensation/view.html"
|
|
||||||
|
|
||||||
def _get_object(self, id: str):
|
|
||||||
""" Returns the compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
obj (Compensation): The compensation
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(
|
|
||||||
Compensation.objects.select_related(
|
|
||||||
"modified",
|
|
||||||
"created",
|
|
||||||
"geometry"
|
|
||||||
),
|
|
||||||
id=id,
|
|
||||||
deleted=None,
|
|
||||||
intervention__deleted=None,
|
|
||||||
)
|
)
|
||||||
return comp
|
context = {
|
||||||
|
"table": table,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Compensations - Overview"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
def _get_detail_context(self, obj: Compensation):
|
|
||||||
""" Generate object specific detail context for view
|
class NewCompensationView(LoginRequiredMixin, View):
|
||||||
|
_TEMPLATE = "compensation/form/view.html"
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "intervention_id"))
|
||||||
|
def get(self, request: HttpRequest, intervention_id: str = None, *args, **kwargs) -> HttpResponse:
|
||||||
|
"""
|
||||||
|
Renders a view for new compensation
|
||||||
|
|
||||||
|
A compensation creation may be called directly from the parent-intervention object. If so - we may take
|
||||||
|
the intervention's id and directly link the compensation to it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
obj (): The record
|
request (HttpRequest): The incoming request
|
||||||
|
intervention_id (str): The intervention identifier
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Order states according to surface
|
if intervention_id:
|
||||||
before_states = obj.before_states.all().prefetch_related("biotope_type").order_by("-surface")
|
# If the parent-intervention is recorded, we are not allowed to change anything on it's data.
|
||||||
after_states = obj.after_states.all().prefetch_related("biotope_type").order_by("-surface")
|
# Not even adding new child elements like compensations!
|
||||||
actions = obj.actions.all().prefetch_related("action_type")
|
intervention = get_object_or_404(Intervention, id=intervention_id)
|
||||||
|
recording_state_blocks_actions = intervention.is_recorded
|
||||||
|
if recording_state_blocks_actions:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=intervention_id)
|
||||||
|
|
||||||
# Precalculate logical errors between before- and after-states
|
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
|
||||||
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
sum_before_states = obj.get_surface_before_states()
|
|
||||||
sum_after_states = obj.get_surface_after_states()
|
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
|
||||||
|
|
||||||
last_checked = obj.intervention.get_last_checked_action()
|
|
||||||
last_checked_tooltip = ""
|
|
||||||
if last_checked:
|
|
||||||
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
|
|
||||||
last_checked.get_timestamp_str_formatted(),
|
|
||||||
last_checked.user
|
|
||||||
)
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"last_checked": last_checked,
|
"form": data_form,
|
||||||
"last_checked_tooltip": last_checked_tooltip,
|
"geom_form": geom_form,
|
||||||
"actions": actions,
|
TAB_TITLE_IDENTIFIER: _("New compensation"),
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"sum_before_states": sum_before_states,
|
|
||||||
"sum_after_states": sum_after_states,
|
|
||||||
"diff_states": diff_states,
|
|
||||||
"has_finished_deadlines": obj.get_finished_deadlines().exists(),
|
|
||||||
}
|
}
|
||||||
return context
|
context = BaseContext(request, context).context
|
||||||
|
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "intervention_id"))
|
||||||
|
def post(self, request: HttpRequest, intervention_id: str = None, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Renders a view for a new compensation creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
if intervention_id:
|
||||||
|
# If the parent-intervention is recorded, we are not allowed to change anything on it's data.
|
||||||
|
# Not even adding new child elements like compensations!
|
||||||
|
intervention = get_object_or_404(Intervention, id=intervention_id)
|
||||||
|
recording_state_blocks_actions = intervention.is_recorded
|
||||||
|
if recording_state_blocks_actions:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=intervention_id)
|
||||||
|
|
||||||
|
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
comp = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != comp.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
comp.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
||||||
|
if geom_form.has_geometry_simplified():
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRY_SIMPLIFIED
|
||||||
|
)
|
||||||
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
|
if num_ignored_geometries > 0:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
|
)
|
||||||
|
return redirect("compensation:detail", id=comp.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger", )
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New compensation"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationView(LoginRequiredMixin, BaseRemoveModalFormView):
|
class CompensationIdentifierGeneratorView(AbstractIdentifierGeneratorView):
|
||||||
_MODEL_CLS = Compensation
|
_MODEL = Compensation
|
||||||
_FORM_CLS = RemoveModalForm
|
|
||||||
_REDIRECT_URL = "compensation:index"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_default_user()
|
class EditCompensationView(LoginRequiredMixin, View):
|
||||||
|
_TEMPLATE = "compensation/form/view.html"
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Renders a view for editing compensations
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get object from db
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
if comp.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditCompensationForm(request.POST or None, instance=comp)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Renders a view for editing compensations
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get object from db
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
if comp.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditCompensationForm(request.POST or None, instance=comp)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
# Preserve state of intervention checked to determine whether the user must be informed or not
|
||||||
|
# about a change of the check state
|
||||||
|
intervention_is_checked = comp.intervention.checked is not None
|
||||||
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
|
comp = data_form.save(request.user, geom_form)
|
||||||
|
if intervention_is_checked:
|
||||||
|
messages.info(request, CHECK_STATE_RESET)
|
||||||
|
messages.success(request, _("Compensation {} edited").format(comp.identifier))
|
||||||
|
if geom_form.has_geometry_simplified():
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRY_SIMPLIFIED
|
||||||
|
)
|
||||||
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
|
if num_ignored_geometries > 0:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
|
)
|
||||||
|
return redirect("compensation:detail", id=comp.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger", )
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,21 +5,45 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.deadline import AbstractRemoveDeadlineView, AbstractEditDeadlineView, AbstractNewDeadlineView
|
from konova.views.deadline import AbstractRemoveDeadlineView, AbstractEditDeadlineView, AbstractNewDeadlineView
|
||||||
|
|
||||||
_COMPENSATION_DETAIL_URL_NAME = "compensation:detail"
|
|
||||||
|
|
||||||
class NewCompensationDeadlineView(AbstractNewDeadlineView):
|
class NewCompensationDeadlineView(AbstractNewDeadlineView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = _COMPENSATION_DETAIL_URL_NAME
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationDeadlineView(AbstractEditDeadlineView):
|
class EditCompensationDeadlineView(AbstractEditDeadlineView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = _COMPENSATION_DETAIL_URL_NAME
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationDeadlineView(AbstractRemoveDeadlineView):
|
class RemoveCompensationDeadlineView(AbstractRemoveDeadlineView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = _COMPENSATION_DETAIL_URL_NAME
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
97
compensation/views/compensation/detail.py
Normal file
97
compensation/views/compensation/detail.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.settings import ETS_GROUP, ZB_GROUP, DEFAULT_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
||||||
|
from konova.views.detail import AbstractDetailView
|
||||||
|
|
||||||
|
|
||||||
|
class DetailCompensationView(AbstractDetailView):
|
||||||
|
_TEMPLATE = "compensation/detail/compensation/view.html"
|
||||||
|
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
|
""" Renders a detail view for a compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
comp = get_object_or_404(
|
||||||
|
Compensation.objects.select_related(
|
||||||
|
"modified",
|
||||||
|
"created",
|
||||||
|
"geometry"
|
||||||
|
),
|
||||||
|
id=id,
|
||||||
|
deleted=None,
|
||||||
|
intervention__deleted=None,
|
||||||
|
)
|
||||||
|
geom_form = SimpleGeomForm(instance=comp)
|
||||||
|
parcels = comp.get_underlying_parcels()
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = comp.intervention.is_shared_with(_user)
|
||||||
|
|
||||||
|
# Order states according to surface
|
||||||
|
before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
|
||||||
|
after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
|
||||||
|
actions = comp.actions.all().prefetch_related("action_type")
|
||||||
|
|
||||||
|
# Precalculate logical errors between before- and after-states
|
||||||
|
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
||||||
|
sum_before_states = comp.get_surface_before_states()
|
||||||
|
sum_after_states = comp.get_surface_after_states()
|
||||||
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
|
|
||||||
|
request = comp.set_status_messages(request)
|
||||||
|
|
||||||
|
last_checked = comp.intervention.get_last_checked_action()
|
||||||
|
last_checked_tooltip = ""
|
||||||
|
if last_checked:
|
||||||
|
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
|
||||||
|
last_checked.get_timestamp_str_formatted(),
|
||||||
|
last_checked.user
|
||||||
|
)
|
||||||
|
|
||||||
|
requesting_user_is_only_shared_user = comp.is_only_shared_with(_user)
|
||||||
|
if requesting_user_is_only_shared_user:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
DO_NOT_FORGET_TO_SHARE
|
||||||
|
)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": comp,
|
||||||
|
"last_checked": last_checked,
|
||||||
|
"last_checked_tooltip": last_checked_tooltip,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"is_entry_shared": is_data_shared,
|
||||||
|
"actions": actions,
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"sum_before_states": sum_before_states,
|
||||||
|
"sum_after_states": sum_after_states,
|
||||||
|
"diff_states": diff_states,
|
||||||
|
"is_default_member": _user.in_group(DEFAULT_GROUP),
|
||||||
|
"is_zb_member": _user.in_group(ZB_GROUP),
|
||||||
|
"is_ets_member": _user.in_group(ETS_GROUP),
|
||||||
|
"LANIS_LINK": comp.get_LANIS_link(),
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
|
||||||
|
"has_finished_deadlines": comp.get_finished_deadlines().exists(),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,33 +5,62 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.forms.modals.document import NewCompensationDocumentModalForm, EditCompensationDocumentModalForm, \
|
from django.contrib.auth.decorators import login_required
|
||||||
RemoveCompensationDocumentModalForm
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.forms.modals.document import NewCompensationDocumentModalForm
|
||||||
from compensation.models import Compensation, CompensationDocument
|
from compensation.models import Compensation, CompensationDocument
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
||||||
AbstractEditDocumentView
|
AbstractEditDocumentView
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationDocumentView(AbstractNewDocumentView):
|
class NewCompensationDocumentView(AbstractNewDocumentView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_FORM_CLS = NewCompensationDocumentModalForm
|
form = NewCompensationDocumentModalForm
|
||||||
_REDIRECT_URL = "compensation:detail"
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GetCompensationDocumentView(AbstractGetDocumentView):
|
class GetCompensationDocumentView(AbstractGetDocumentView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_DOCUMENT_CLS = CompensationDocument
|
document_model = CompensationDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationDocumentView(AbstractRemoveDocumentView):
|
class RemoveCompensationDocumentView(AbstractRemoveDocumentView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_DOCUMENT_CLS = CompensationDocument
|
document_model = CompensationDocument
|
||||||
_FORM_CLS = RemoveCompensationDocumentModalForm
|
|
||||||
_REDIRECT_URL = "compensation:detail"
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationDocumentView(AbstractEditDocumentView):
|
class EditCompensationDocumentView(AbstractEditDocumentView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_DOCUMENT_CLS = CompensationDocument
|
document_model = CompensationDocument
|
||||||
_FORM_CLS = EditCompensationDocumentModalForm
|
form = EditDocumentModalForm
|
||||||
_REDIRECT_URL = "compensation:detail"
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,11 +5,20 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.log import AbstractLogView
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
class CompensationLogView(LoginRequiredMixin, AbstractLogView):
|
class CompensationLogView(AbstractLogView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
20
compensation/views/compensation/remove.py
Normal file
20
compensation/views/compensation/remove.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required
|
||||||
|
from konova.views.remove import AbstractRemoveView
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationView(AbstractRemoveView):
|
||||||
|
_MODEL = Compensation
|
||||||
|
_REDIRECT_URL = "compensation:index"
|
||||||
|
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
@@ -5,48 +5,81 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
from konova.sub_settings.django_settings import BASE_URL
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.qrcode import QrCode
|
from konova.utils.qrcode import QrCode
|
||||||
from konova.views.report import BaseReportView
|
from konova.views.report import AbstractPublicReportView
|
||||||
|
|
||||||
|
|
||||||
class BaseCompensationReportView(BaseReportView):
|
class CompensationPublicReportView(AbstractPublicReportView):
|
||||||
def _get_compensation_report_context(self, obj):
|
|
||||||
# Order states by surface
|
|
||||||
before_states = obj.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
|
||||||
after_states = obj.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
|
||||||
actions = obj.actions.all().prefetch_related("action_type")
|
|
||||||
|
|
||||||
return {
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"actions": actions,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationReportView(BaseCompensationReportView):
|
|
||||||
_MODEL = Compensation
|
|
||||||
_TEMPLATE = "compensation/report/compensation/report.html"
|
_TEMPLATE = "compensation/report/compensation/report.html"
|
||||||
|
|
||||||
def _get_report_context(self, obj):
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
report_url = BASE_URL + reverse("compensation:report", args=(obj.id,))
|
""" Renders the public report view
|
||||||
qrcode_report = QrCode(report_url, 10)
|
|
||||||
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
|
|
||||||
|
|
||||||
report_context = {
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
tab_title = _("Report {}").format(comp.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not comp.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=comp
|
||||||
|
)
|
||||||
|
parcels = comp.get_underlying_parcels()
|
||||||
|
|
||||||
|
qrcode = QrCode(
|
||||||
|
content=request.build_absolute_uri(reverse("compensation:report", args=(id,))),
|
||||||
|
size=10
|
||||||
|
)
|
||||||
|
qrcode_lanis = QrCode(
|
||||||
|
content=comp.get_LANIS_link(),
|
||||||
|
size=7
|
||||||
|
)
|
||||||
|
|
||||||
|
# Order states by surface
|
||||||
|
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
actions = comp.actions.all().prefetch_related("action_type")
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": comp,
|
||||||
"qrcode": {
|
"qrcode": {
|
||||||
"img": qrcode_report.get_img(),
|
"img": qrcode.get_img(),
|
||||||
"url": qrcode_report.get_content(),
|
"url": qrcode.get_content(),
|
||||||
},
|
},
|
||||||
"qrcode_lanis": {
|
"qrcode_lanis": {
|
||||||
"img": qrcode_lanis.get_img(),
|
"img": qrcode_lanis.get_img(),
|
||||||
"url": qrcode_lanis.get_content(),
|
"url": qrcode_lanis.get_content(),
|
||||||
},
|
},
|
||||||
"is_entry_shared": False, # disables action buttons during rendering
|
"is_entry_shared": False, # disables action buttons during rendering
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"actions": actions,
|
||||||
"tables_scrollable": False,
|
"tables_scrollable": False,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
}
|
}
|
||||||
report_context.update(self._get_compensation_report_context(obj))
|
context = BaseContext(request, context).context
|
||||||
return report_context
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|||||||
@@ -5,12 +5,22 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.forms.modals.resubmission import CompensationResubmissionModalForm
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.resubmission import AbstractResubmissionView
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
class CompensationResubmissionView(AbstractResubmissionView):
|
class CompensationResubmissionView(AbstractResubmissionView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_FORM_CLS = CompensationResubmissionModalForm
|
redirect_url_base = "compensation:detail"
|
||||||
_REDIRECT_URL = "compensation:detail"
|
form_action_url_base = "compensation:resubmission-create"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,21 +5,46 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
||||||
AbstractRemoveCompensationStateView
|
AbstractRemoveCompensationStateView
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationStateView(AbstractNewCompensationStateView):
|
class NewCompensationStateView(AbstractNewCompensationStateView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = "compensation:detail"
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationStateView(AbstractEditCompensationStateView):
|
class EditCompensationStateView(AbstractEditCompensationStateView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = "compensation:detail"
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationStateView(AbstractRemoveCompensationStateView):
|
class RemoveCompensationStateView(AbstractRemoveCompensationStateView):
|
||||||
_MODEL_CLS = Compensation
|
model = Compensation
|
||||||
_REDIRECT_URL = "compensation:detail"
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,22 +5,46 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
||||||
AbstractRemoveCompensationActionView
|
AbstractRemoveCompensationActionView
|
||||||
|
|
||||||
_ECO_ACCOUNT_DETAIL_URL_NAME = "compensation:acc:detail"
|
|
||||||
|
|
||||||
class NewEcoAccountActionView(AbstractNewCompensationActionView):
|
class NewEcoAccountActionView(AbstractNewCompensationActionView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountActionView(AbstractEditCompensationActionView):
|
class EditEcoAccountActionView(AbstractEditCompensationActionView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEcoAccountActionView(AbstractRemoveCompensationActionView):
|
class RemoveEcoAccountActionView(AbstractRemoveCompensationActionView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,22 +5,45 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.deadline import AbstractNewDeadlineView, AbstractEditDeadlineView, AbstractRemoveDeadlineView
|
from konova.views.deadline import AbstractNewDeadlineView, AbstractEditDeadlineView, AbstractRemoveDeadlineView
|
||||||
|
|
||||||
_ECO_ACCOUNT_DETAIL_URL_NAME = "compensation:acc:detail"
|
|
||||||
|
|
||||||
class NewEcoAccountDeadlineView(AbstractNewDeadlineView):
|
class NewEcoAccountDeadlineView(AbstractNewDeadlineView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountDeadlineView(AbstractEditDeadlineView):
|
class EditEcoAccountDeadlineView(AbstractEditDeadlineView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEcoAccountDeadlineView(AbstractRemoveDeadlineView):
|
class RemoveEcoAccountDeadlineView(AbstractRemoveDeadlineView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,33 +5,54 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import default_group_required, login_required_modal
|
||||||
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
|
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
|
||||||
|
|
||||||
_ECO_ACCOUNT_DETAIl_URL_NAME = "compensation:acc:detail"
|
|
||||||
|
|
||||||
class NewEcoAccountDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
|
class NewEcoAccountDeductionView(AbstractNewDeductionView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIl_URL_NAME
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def _custom_check(self, obj):
|
def _custom_check(self, obj):
|
||||||
# New deductions can only be created if the eco account has been recorded
|
|
||||||
if not obj.recorded:
|
if not obj.recorded:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
def _check_for_recorded_instance(self, obj):
|
|
||||||
# Deductions can be created on recorded as well as on non-recorded entries
|
class EditEcoAccountDeductionView(AbstractEditDeductionView):
|
||||||
return None
|
def _custom_check(self, obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountDeductionView(LoginRequiredMixin, AbstractEditDeductionView):
|
class RemoveEcoAccountDeductionView(AbstractRemoveDeductionView):
|
||||||
_MODEL_CLS = EcoAccount
|
def _custom_check(self, obj):
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIl_URL_NAME
|
pass
|
||||||
|
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
class RemoveEcoAccountDeductionView(LoginRequiredMixin, AbstractRemoveDeductionView):
|
|
||||||
_MODEL_CLS = EcoAccount
|
|
||||||
_REDIRECT_URL = _ECO_ACCOUNT_DETAIl_URL_NAME
|
|
||||||
|
|||||||
97
compensation/views/eco_account/detail.py
Normal file
97
compensation/views/eco_account/detail.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.settings import ETS_GROUP, ZB_GROUP, DEFAULT_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE
|
||||||
|
from konova.views.detail import AbstractDetailView
|
||||||
|
|
||||||
|
|
||||||
|
class DetailEcoAccountView(AbstractDetailView):
|
||||||
|
_TEMPLATE = "compensation/detail/eco_account/view.html"
|
||||||
|
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" Renders a detail view for a compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
acc = get_object_or_404(
|
||||||
|
EcoAccount.objects.prefetch_related(
|
||||||
|
"deadlines",
|
||||||
|
).select_related(
|
||||||
|
'geometry',
|
||||||
|
'responsible',
|
||||||
|
),
|
||||||
|
id=id,
|
||||||
|
deleted=None,
|
||||||
|
)
|
||||||
|
geom_form = SimpleGeomForm(instance=acc)
|
||||||
|
parcels = acc.get_underlying_parcels()
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = acc.is_shared_with(_user)
|
||||||
|
|
||||||
|
# Order states according to surface
|
||||||
|
before_states = acc.before_states.order_by("-surface")
|
||||||
|
after_states = acc.after_states.order_by("-surface")
|
||||||
|
|
||||||
|
# Precalculate logical errors between before- and after-states
|
||||||
|
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
||||||
|
sum_before_states = acc.get_surface_before_states()
|
||||||
|
sum_after_states = acc.get_surface_after_states()
|
||||||
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
|
# Calculate rest of available surface for deductions
|
||||||
|
available_total = acc.deductable_rest
|
||||||
|
available_relative = acc.get_deductable_rest_relative()
|
||||||
|
|
||||||
|
# Prefetch related data to decrease the amount of db connections
|
||||||
|
deductions = acc.deductions.filter(
|
||||||
|
intervention__deleted=None,
|
||||||
|
)
|
||||||
|
actions = acc.actions.all()
|
||||||
|
|
||||||
|
request = acc.set_status_messages(request)
|
||||||
|
|
||||||
|
requesting_user_is_only_shared_user = acc.is_only_shared_with(_user)
|
||||||
|
if requesting_user_is_only_shared_user:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
DO_NOT_FORGET_TO_SHARE
|
||||||
|
)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": acc,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"is_entry_shared": is_data_shared,
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"sum_before_states": sum_before_states,
|
||||||
|
"sum_after_states": sum_after_states,
|
||||||
|
"diff_states": diff_states,
|
||||||
|
"available": available_relative,
|
||||||
|
"available_total": available_total,
|
||||||
|
"is_default_member": _user.in_group(DEFAULT_GROUP),
|
||||||
|
"is_zb_member": _user.in_group(ZB_GROUP),
|
||||||
|
"is_ets_member": _user.in_group(ETS_GROUP),
|
||||||
|
"LANIS_LINK": acc.get_LANIS_link(),
|
||||||
|
"deductions": deductions,
|
||||||
|
"actions": actions,
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
|
||||||
|
"has_finished_deadlines": acc.get_finished_deadlines().exists(),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,33 +5,65 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.forms.modals.document import NewEcoAccountDocumentModalForm, RemoveEcoAccountDocumentModalForm, \
|
from django.contrib.auth.decorators import login_required
|
||||||
EditEcoAccountDocumentModalForm
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.forms.modals.document import NewEcoAccountDocumentModalForm
|
||||||
from compensation.models import EcoAccount, EcoAccountDocument
|
from compensation.models import EcoAccount, EcoAccountDocument
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
||||||
AbstractEditDocumentView
|
AbstractEditDocumentView
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountDocumentView(AbstractNewDocumentView):
|
class NewEcoAccountDocumentView(AbstractNewDocumentView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_FORM_CLS = NewEcoAccountDocumentModalForm
|
form = NewEcoAccountDocumentModalForm
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GetEcoAccountDocumentView(AbstractGetDocumentView):
|
class GetEcoAccountDocumentView(AbstractGetDocumentView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_DOCUMENT_CLS = EcoAccountDocument
|
document_model = EcoAccountDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEcoAccountDocumentView(AbstractRemoveDocumentView):
|
class RemoveEcoAccountDocumentView(AbstractRemoveDocumentView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_DOCUMENT_CLS = EcoAccountDocument
|
document_model = EcoAccountDocument
|
||||||
_FORM_CLS = RemoveEcoAccountDocumentModalForm
|
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountDocumentView(AbstractEditDocumentView):
|
class EditEcoAccountDocumentView(AbstractEditDocumentView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_DOCUMENT_CLS = EcoAccountDocument
|
document_model = EcoAccountDocument
|
||||||
_FORM_CLS = EditEcoAccountDocumentModalForm
|
form = EditDocumentModalForm
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -6,81 +6,94 @@ Created on: 19.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm, RemoveEcoAccountModalForm
|
from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
from compensation.tables.eco_account import EcoAccountTable
|
from compensation.tables.eco_account import EcoAccountTable
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
from konova.forms import SimpleGeomForm
|
from konova.forms import SimpleGeomForm
|
||||||
from konova.settings import ETS_GROUP
|
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
|
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, FORM_INVALID, \
|
||||||
IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
||||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||||
BaseEditSpatialLocatedObjectFormView
|
from konova.views.index import AbstractIndexView
|
||||||
from konova.views.detail import BaseDetailView
|
|
||||||
from konova.views.remove import BaseRemoveModalFormView
|
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
|
class IndexEcoAccountView(AbstractIndexView):
|
||||||
_INDEX_TABLE_CLS = EcoAccountTable
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
_TAB_TITLE = _("Eco-account - Overview")
|
"""
|
||||||
|
Renders the index view for eco accounts
|
||||||
|
|
||||||
def _get_queryset(self):
|
Args:
|
||||||
qs = EcoAccount.objects.filter(
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A rendered view
|
||||||
|
"""
|
||||||
|
eco_accounts = EcoAccount.objects.filter(
|
||||||
deleted=None,
|
deleted=None,
|
||||||
).order_by(
|
).order_by(
|
||||||
"-modified__timestamp"
|
"-modified__timestamp"
|
||||||
)
|
)
|
||||||
return qs
|
table = EcoAccountTable(
|
||||||
|
request=request,
|
||||||
|
queryset=eco_accounts
|
||||||
|
)
|
||||||
|
context = {
|
||||||
|
"table": table,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Eco-account - Overview"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountFormView(BaseNewSpatialLocatedObjectFormView):
|
class NewEcoAccountView(LoginRequiredMixin, View):
|
||||||
_FORM_CLS = NewEcoAccountForm
|
|
||||||
_MODEL_CLS = EcoAccount
|
|
||||||
_TEMPLATE = "compensation/form/view.html"
|
_TEMPLATE = "compensation/form/view.html"
|
||||||
_TAB_TITLE = _("New Eco-Account")
|
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(default_group_required)
|
||||||
# User has to be a default user
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
return user.is_default_user()
|
"""
|
||||||
|
Renders a view for a new eco account creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
class EditEcoAccountFormView(BaseEditSpatialLocatedObjectFormView):
|
Returns:
|
||||||
_FORM_CLS = EditEcoAccountForm
|
|
||||||
_MODEL_CLS = EcoAccount
|
|
||||||
_TEMPLATE = "compensation/form/view.html"
|
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
"""
|
||||||
# User has to be a default user
|
data_form = NewEcoAccountForm(request.POST or None)
|
||||||
return user.is_default_user()
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
|
||||||
@login_required
|
return render(request, self._TEMPLATE, context)
|
||||||
@default_group_required
|
|
||||||
def new_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders a view for a new eco account creation
|
|
||||||
|
|
||||||
Args:
|
@method_decorator(default_group_required)
|
||||||
request (HttpRequest): The incoming request
|
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
Returns:
|
"""
|
||||||
|
Renders a view for a new eco account creation
|
||||||
|
|
||||||
"""
|
Args:
|
||||||
template = "compensation/form/view.html"
|
request (HttpRequest): The incoming request
|
||||||
data_form = NewEcoAccountForm(request.POST or None)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
Returns:
|
||||||
if request.method == "POST":
|
|
||||||
|
"""
|
||||||
|
data_form = NewEcoAccountForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
acc = data_form.save(request.user, geom_form)
|
acc = data_form.save(request.user, geom_form)
|
||||||
@@ -98,61 +111,92 @@ def new_view(request: HttpRequest):
|
|||||||
request,
|
request,
|
||||||
GEOMETRY_SIMPLIFIED
|
GEOMETRY_SIMPLIFIED
|
||||||
)
|
)
|
||||||
|
|
||||||
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
if num_ignored_geometries > 0:
|
if num_ignored_geometries > 0:
|
||||||
messages.info(
|
messages.info(
|
||||||
request,
|
request,
|
||||||
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect("compensation:acc:detail", id=acc.id)
|
return redirect("compensation:acc:detail", id=acc.id)
|
||||||
else:
|
else:
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
messages.error(request, FORM_INVALID, extra_tags="danger", )
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
context = {
|
||||||
pass
|
"form": data_form,
|
||||||
context = {
|
"geom_form": geom_form,
|
||||||
"form": data_form,
|
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
|
||||||
"geom_form": geom_form,
|
}
|
||||||
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
|
context = BaseContext(request, context).context
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
return render(request, self._TEMPLATE, context)
|
||||||
return render(request, template, context)
|
|
||||||
|
class EcoAccountIdentifierGeneratorView(AbstractIdentifierGeneratorView):
|
||||||
|
_MODEL = EcoAccount
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
class EditEcoAccountView(LoginRequiredMixin, View):
|
||||||
_MODEL_CLS = EcoAccount
|
_TEMPLATE = "compensation/form/view.html"
|
||||||
_REDIRECT_URL = "compensation:acc:index"
|
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
@login_required
|
"""
|
||||||
@default_group_required
|
Renders a view for editing compensations
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def edit_view(request: HttpRequest, id: str):
|
|
||||||
"""
|
|
||||||
Renders a view for editing compensations
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The incoming request
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
template = "compensation/form/view.html"
|
# Get object from db
|
||||||
# Get object from db
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
if acc.is_recorded:
|
||||||
if acc.is_recorded:
|
messages.info(
|
||||||
messages.info(
|
request,
|
||||||
request,
|
RECORDED_BLOCKS_EDIT
|
||||||
RECORDED_BLOCKS_EDIT
|
)
|
||||||
)
|
return redirect("compensation:acc:detail", id=id)
|
||||||
return redirect("compensation:acc:detail", id=id)
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Renders a view for editing compensations
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get object from db
|
||||||
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
|
if acc.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:acc:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
|
||||||
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
|
||||||
if request.method == "POST":
|
|
||||||
data_form_valid = data_form.is_valid()
|
data_form_valid = data_form.is_valid()
|
||||||
geom_form_valid = geom_form.is_valid()
|
geom_form_valid = geom_form.is_valid()
|
||||||
if data_form_valid and geom_form_valid:
|
if data_form_valid and geom_form_valid:
|
||||||
@@ -164,101 +208,21 @@ def edit_view(request: HttpRequest, id: str):
|
|||||||
request,
|
request,
|
||||||
GEOMETRY_SIMPLIFIED
|
GEOMETRY_SIMPLIFIED
|
||||||
)
|
)
|
||||||
|
|
||||||
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
if num_ignored_geometries > 0:
|
if num_ignored_geometries > 0:
|
||||||
messages.info(
|
messages.info(
|
||||||
request,
|
request,
|
||||||
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect("compensation:acc:detail", id=acc.id)
|
return redirect("compensation:acc:detail", id=acc.id)
|
||||||
else:
|
else:
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
messages.error(request, FORM_INVALID, extra_tags="danger", )
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountDetailView(BaseDetailView):
|
|
||||||
_MODEL_CLS = EcoAccount
|
|
||||||
_TEMPLATE = "compensation/detail/eco_account/view.html"
|
|
||||||
|
|
||||||
def _get_object(self, id: str):
|
|
||||||
""" Fetch object for detail view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
id (str): The record's id'
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(
|
|
||||||
EcoAccount.objects.prefetch_related(
|
|
||||||
"deadlines",
|
|
||||||
).select_related(
|
|
||||||
'geometry',
|
|
||||||
'responsible',
|
|
||||||
),
|
|
||||||
id=id,
|
|
||||||
deleted=None,
|
|
||||||
)
|
|
||||||
return acc
|
|
||||||
|
|
||||||
def _get_detail_context(self, obj: EcoAccount):
|
|
||||||
""" Generate object specific detail context for view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
obj (): The record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Order states according to surface
|
|
||||||
before_states = obj.before_states.order_by("-surface")
|
|
||||||
after_states = obj.after_states.order_by("-surface")
|
|
||||||
|
|
||||||
# Precalculate logical errors between before- and after-states
|
|
||||||
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
|
||||||
sum_before_states = obj.get_surface_before_states()
|
|
||||||
sum_after_states = obj.get_surface_after_states()
|
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
|
||||||
# Calculate rest of available surface for deductions
|
|
||||||
available_total = obj.deductable_rest
|
|
||||||
available_relative = obj.get_deductable_rest_relative()
|
|
||||||
|
|
||||||
# Prefetch related data to decrease the amount of db connections
|
|
||||||
deductions = obj.deductions.filter(
|
|
||||||
intervention__deleted=None,
|
|
||||||
)
|
|
||||||
actions = obj.actions.all()
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"before_states": before_states,
|
"form": data_form,
|
||||||
"after_states": after_states,
|
"geom_form": geom_form,
|
||||||
"sum_before_states": sum_before_states,
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
|
||||||
"sum_after_states": sum_after_states,
|
|
||||||
"diff_states": diff_states,
|
|
||||||
"available": available_relative,
|
|
||||||
"available_total": available_total,
|
|
||||||
"deductions": deductions,
|
|
||||||
"actions": actions,
|
|
||||||
"has_finished_deadlines": obj.get_finished_deadlines().exists(),
|
|
||||||
}
|
}
|
||||||
return context
|
context = BaseContext(request, context).context
|
||||||
|
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
class RemoveEcoAccountView(LoginRequiredMixin, BaseRemoveModalFormView):
|
|
||||||
_MODEL_CLS = EcoAccount
|
|
||||||
_FORM_CLS = RemoveEcoAccountModalForm
|
|
||||||
_REDIRECT_URL = "compensation:acc:index"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_default_user()
|
|
||||||
|
|||||||
@@ -5,11 +5,20 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.log import AbstractLogView
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountLogView(LoginRequiredMixin, AbstractLogView):
|
class EcoAccountLogView(AbstractLogView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,12 +5,20 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
from konova.views.record import AbstractRecordView
|
from konova.views.record import AbstractRecordView
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountRecordView(LoginRequiredMixin, AbstractRecordView):
|
class EcoAccountRecordView(AbstractRecordView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
22
compensation/views/eco_account/remove.py
Normal file
22
compensation/views/eco_account/remove.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.forms.eco_account import RemoveEcoAccountModalForm
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required
|
||||||
|
from konova.views.remove import AbstractRemoveView
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountView(AbstractRemoveView):
|
||||||
|
_MODEL = EcoAccount
|
||||||
|
_REDIRECT_URL = "compensation:acc:index"
|
||||||
|
_FORM = RemoveEcoAccountModalForm
|
||||||
|
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
@@ -5,41 +5,88 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
from compensation.views.compensation.report import BaseCompensationReportView
|
from konova.contexts import BaseContext
|
||||||
from konova.sub_settings.django_settings import BASE_URL
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.qrcode import QrCode
|
from konova.utils.qrcode import QrCode
|
||||||
|
from konova.views.report import AbstractPublicReportView
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountReportView(BaseCompensationReportView):
|
class EcoAccountPublicReportView(AbstractPublicReportView):
|
||||||
_MODEL = EcoAccount
|
|
||||||
_TEMPLATE = "compensation/report/eco_account/report.html"
|
_TEMPLATE = "compensation/report/eco_account/report.html"
|
||||||
|
|
||||||
def _get_report_context(self, obj):
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
report_url = BASE_URL + reverse("compensation:acc:report", args=(obj.id,))
|
""" Renders the public report view
|
||||||
qrcode_report = QrCode(report_url, 10)
|
|
||||||
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
|
tab_title = _("Report {}").format(acc.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not acc.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=acc
|
||||||
|
)
|
||||||
|
parcels = acc.get_underlying_parcels()
|
||||||
|
|
||||||
|
qrcode = QrCode(
|
||||||
|
content=request.build_absolute_uri(reverse("compensation:acc:report", args=(id,))),
|
||||||
|
size=10
|
||||||
|
)
|
||||||
|
qrcode_lanis = QrCode(
|
||||||
|
content=acc.get_LANIS_link(),
|
||||||
|
size=7
|
||||||
|
)
|
||||||
|
|
||||||
|
# Order states by surface
|
||||||
|
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
|
||||||
|
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
|
||||||
|
actions = acc.actions.all().prefetch_related("action_type__parent")
|
||||||
|
|
||||||
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
|
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
|
||||||
deductions = obj.deductions.all() \
|
deductions = acc.deductions.all() \
|
||||||
.distinct("intervention") \
|
.distinct("intervention") \
|
||||||
.select_related("intervention") \
|
.select_related("intervention") \
|
||||||
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
|
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
|
||||||
|
|
||||||
report_context = {
|
context = {
|
||||||
|
"obj": acc,
|
||||||
"qrcode": {
|
"qrcode": {
|
||||||
"img": qrcode_report.get_img(),
|
"img": qrcode.get_img(),
|
||||||
"url": qrcode_report.get_content(),
|
"url": qrcode.get_content(),
|
||||||
},
|
},
|
||||||
"qrcode_lanis": {
|
"qrcode_lanis": {
|
||||||
"img": qrcode_lanis.get_img(),
|
"img": qrcode_lanis.get_img(),
|
||||||
"url": qrcode_lanis.get_content(),
|
"url": qrcode_lanis.get_content(),
|
||||||
},
|
},
|
||||||
"is_entry_shared": False, # disables action buttons during rendering
|
"is_entry_shared": False, # disables action buttons during rendering
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"actions": actions,
|
||||||
"deductions": deductions,
|
"deductions": deductions,
|
||||||
"tables_scrollable": False,
|
"tables_scrollable": False,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
}
|
}
|
||||||
report_context.update(self._get_compensation_report_context(obj))
|
context = BaseContext(request, context).context
|
||||||
return report_context
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|||||||
@@ -5,12 +5,22 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.forms.modals.resubmission import EcoAccountResubmissionModalForm
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.resubmission import AbstractResubmissionView
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountResubmissionView(AbstractResubmissionView):
|
class EcoAccountResubmissionView(AbstractResubmissionView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_FORM_CLS = EcoAccountResubmissionModalForm
|
redirect_url_base = "compensation:acc:detail"
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
form_action_url_base = "compensation:acc:resubmission-create"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,15 +5,29 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountShareByTokenView(AbstractShareByTokenView):
|
class EcoAccountShareByTokenView(AbstractShareByTokenView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountShareFormView(AbstractShareFormView):
|
class EcoAccountShareFormView(AbstractShareFormView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,21 +5,46 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||||
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
||||||
AbstractRemoveCompensationStateView
|
AbstractRemoveCompensationStateView
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountStateView(AbstractNewCompensationStateView):
|
class NewEcoAccountStateView(AbstractNewCompensationStateView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountStateView(AbstractEditCompensationStateView):
|
class EditEcoAccountStateView(AbstractEditCompensationStateView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEcoAccountStateView(AbstractRemoveCompensationStateView):
|
class RemoveEcoAccountStateView(AbstractRemoveCompensationStateView):
|
||||||
_MODEL_CLS = EcoAccount
|
model = EcoAccount
|
||||||
_REDIRECT_URL = "compensation:acc:detail"
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -6,37 +6,110 @@ Created on: 09.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from compensation.forms.modals.payment import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm
|
from compensation.forms.modals.payment import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm
|
||||||
|
from compensation.models import Payment
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED, PAYMENT_EDITED
|
from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED, PAYMENT_EDITED
|
||||||
from konova.views.base import BaseModalFormView
|
|
||||||
|
|
||||||
|
|
||||||
class BasePaymentView(LoginRequiredMixin, BaseModalFormView):
|
class NewPaymentView(LoginRequiredMixin, View):
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
|
||||||
class Meta:
|
def __process_request(self, request: HttpRequest, id: str):
|
||||||
abstract = True
|
""" Renders a modal view for adding new payments
|
||||||
|
|
||||||
def _get_redirect_url(self, *args, **kwargs):
|
Args:
|
||||||
url = super()._get_redirect_url(*args, **kwargs)
|
request (HttpRequest): The incoming request
|
||||||
return f"{url}#related_data"
|
id (str): The intervention's id for which a new payment shall be added
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
Returns:
|
||||||
return user.is_default_user()
|
|
||||||
|
"""
|
||||||
|
intervention = get_object_or_404(Intervention, id=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"
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str):
|
||||||
|
return self.__process_request(request, id=id)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str):
|
||||||
|
return self.__process_request(request, id=id)
|
||||||
|
|
||||||
|
|
||||||
class NewPaymentView(BasePaymentView):
|
class RemovePaymentView(LoginRequiredMixin, View):
|
||||||
_FORM_CLS = NewPaymentForm
|
|
||||||
_MSG_SUCCESS = PAYMENT_ADDED
|
def __process_request(self, request: HttpRequest, id: str, payment_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
|
||||||
|
|
||||||
|
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)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=PAYMENT_REMOVED,
|
||||||
|
redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, payment_id: str):
|
||||||
|
return self.__process_request(request, id=id, payment_id=payment_id)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, payment_id: str):
|
||||||
|
return self.__process_request(request, id=id, payment_id=payment_id)
|
||||||
|
|
||||||
|
|
||||||
class EditPaymentView(BasePaymentView):
|
class EditPaymentView(LoginRequiredMixin, View):
|
||||||
_MSG_SUCCESS = PAYMENT_EDITED
|
def __process_request(self, request: HttpRequest, id: str, payment_id: str):
|
||||||
_FORM_CLS = EditPaymentModalForm
|
""" Renders a modal view for editing payments
|
||||||
|
|
||||||
class RemovePaymentView(BasePaymentView):
|
Args:
|
||||||
_MSG_SUCCESS = PAYMENT_REMOVED
|
request (HttpRequest): The incoming request
|
||||||
_FORM_CLS = RemovePaymentModalForm
|
id (str): The intervention's id
|
||||||
|
payment_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 = EditPaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=PAYMENT_EDITED,
|
||||||
|
redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, payment_id: str):
|
||||||
|
return self.__process_request(request, id=id, payment_id=payment_id)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, payment_id: str):
|
||||||
|
return self.__process_request(request, id=id, payment_id=payment_id)
|
||||||
|
|||||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
konova:
|
||||||
|
external_links:
|
||||||
|
- postgis:db
|
||||||
|
- arnova-nginx-server:arnova
|
||||||
|
build: .
|
||||||
|
image: "ksp/konova:1.8"
|
||||||
|
container_name: "konova-docker"
|
||||||
|
command: ./docker-entrypoint.sh
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- /data/apps/konova/uploaded_files:/konova_uploaded_files
|
||||||
|
ports:
|
||||||
|
- "1337:80"
|
||||||
|
|
||||||
|
# Instead of an own, new network, we need to connect to the existing one, which is provided by the postgis container
|
||||||
|
# NOTE: THIS NETWORK MUST EXIST
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external:
|
||||||
|
name: postgis_nat_it_backend
|
||||||
27
docker-entrypoint.sh
Executable file
27
docker-entrypoint.sh
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e # Beende Skript bei Fehlern
|
||||||
|
set -o pipefail # Fehler in Pipelines nicht ignorieren
|
||||||
|
|
||||||
|
# Starte Redis
|
||||||
|
redis-server --daemonize yes
|
||||||
|
|
||||||
|
# Starte Celery Worker im Hintergrund
|
||||||
|
celery -A konova worker --loglevel=info &
|
||||||
|
|
||||||
|
# Starte Nginx als Hintergrundprozess
|
||||||
|
nginx -g "daemon off;" &
|
||||||
|
|
||||||
|
# Setze Gunicorn Worker-Anzahl (Standard: (2*CPUs)+1)
|
||||||
|
WORKERS=${GUNICORN_WORKERS:-$((2 * $(nproc) + 1))}
|
||||||
|
|
||||||
|
# Stelle sicher, dass Logs existieren
|
||||||
|
mkdir -p /var/log/gunicorn
|
||||||
|
touch /var/log/gunicorn/access.log /var/log/gunicorn/error.log
|
||||||
|
|
||||||
|
# Starte Gunicorn als Hauptprozess
|
||||||
|
exec gunicorn --workers="$WORKERS" konova.wsgi:application \
|
||||||
|
--bind=0.0.0.0:8000 \
|
||||||
|
--access-logfile /var/log/gunicorn/access.log \
|
||||||
|
--error-logfile /var/log/gunicorn/error.log \
|
||||||
|
--access-logformat '%({x-real-ip}i)s via %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
|
||||||
14
ema/forms.py
14
ema/forms.py
@@ -15,8 +15,7 @@ from compensation.forms.compensation import AbstractCompensationForm
|
|||||||
from ema.models import Ema, EmaDocument
|
from ema.models import Ema, EmaDocument
|
||||||
from intervention.models import Responsibility, Handler
|
from intervention.models import Responsibility, Handler
|
||||||
from konova.forms import SimpleGeomForm
|
from konova.forms import SimpleGeomForm
|
||||||
from konova.forms.modals import NewDocumentModalForm, EditDocumentModalForm, RemoveDocumentModalForm, \
|
from konova.forms.modals import NewDocumentModalForm
|
||||||
ResubmissionModalForm
|
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@@ -171,13 +170,4 @@ class EditEmaForm(NewEmaForm):
|
|||||||
|
|
||||||
|
|
||||||
class NewEmaDocumentModalForm(NewDocumentModalForm):
|
class NewEmaDocumentModalForm(NewDocumentModalForm):
|
||||||
_DOCUMENT_CLS = EmaDocument
|
document_model = EmaDocument
|
||||||
|
|
||||||
class EditEmaDocumentModalForm(EditDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = EmaDocument
|
|
||||||
|
|
||||||
class RemoveEmaDocumentModalForm(RemoveDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = EmaDocument
|
|
||||||
|
|
||||||
class EmaResubmissionModalForm(ResubmissionModalForm):
|
|
||||||
_MODEL_CLS = Ema
|
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'ema:resubmission-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'ema:resubmission-create' obj.id %}">
|
||||||
{% fa5_icon 'bell' %}
|
{% fa5_icon 'bell' %}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'ema:share-form' obj.id %}">
|
|
||||||
{% fa5_icon 'share-alt' %}
|
|
||||||
</button>
|
|
||||||
{% if is_ets_member %}
|
{% if is_ets_member %}
|
||||||
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'ema:share-form' obj.id %}">
|
||||||
|
{% fa5_icon 'share-alt' %}
|
||||||
|
</button>
|
||||||
{% if obj.recorded %}
|
{% if obj.recorded %}
|
||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Unrecord' %}" data-form-url="{% url 'ema:record' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Unrecord' %}" data-form-url="{% url 'ema:record' obj.id %}">
|
||||||
{% fa5_icon 'bookmark' 'far' %}
|
{% fa5_icon 'bookmark' 'far' %}
|
||||||
@@ -28,19 +28,21 @@
|
|||||||
{% fa5_icon 'bookmark' %}
|
{% fa5_icon 'bookmark' %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<a href="{% url 'ema:edit' obj.id %}" class="mr-2">
|
||||||
|
<button class="btn btn-default" title="{% trans 'Edit' %}">
|
||||||
|
{% fa5_icon 'edit' %}
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_default_member %}
|
{% if is_default_member %}
|
||||||
<a href="{% url 'ema:edit' obj.id %}" class="mr-2">
|
<button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'ema:log' obj.id %}" title="{% trans 'Show log' %}">
|
||||||
<button class="btn btn-default" title="{% trans 'Edit' %}">
|
{% fa5_icon 'history' %}
|
||||||
{% fa5_icon 'edit' %}
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_ets_member %}
|
||||||
|
<button class="btn btn-default btn-modal" data-form-url="{% url 'ema:remove' obj.id %}" title="{% trans 'Delete' %}">
|
||||||
|
{% fa5_icon 'trash' %}
|
||||||
</button>
|
</button>
|
||||||
</a>
|
|
||||||
<button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'ema:log' obj.id %}" title="{% trans 'Show log' %}">
|
|
||||||
{% fa5_icon 'history' %}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-default btn-modal" data-form-url="{% url 'ema:remove' obj.id %}" title="{% trans 'Delete' %}">
|
|
||||||
{% fa5_icon 'trash' %}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@@ -118,6 +118,7 @@ class EmaViewTestCase(CompensationViewTestCase):
|
|||||||
self.index_url,
|
self.index_url,
|
||||||
self.detail_url,
|
self.detail_url,
|
||||||
self.report_url,
|
self.report_url,
|
||||||
|
self.log_url,
|
||||||
]
|
]
|
||||||
fail_urls = [
|
fail_urls = [
|
||||||
self.new_url,
|
self.new_url,
|
||||||
@@ -133,7 +134,6 @@ class EmaViewTestCase(CompensationViewTestCase):
|
|||||||
self.action_remove_url,
|
self.action_remove_url,
|
||||||
self.action_new_url,
|
self.action_new_url,
|
||||||
self.new_doc_url,
|
self.new_doc_url,
|
||||||
self.log_url,
|
|
||||||
self.remove_url,
|
self.remove_url,
|
||||||
]
|
]
|
||||||
self.assert_url_fail(client, fail_urls)
|
self.assert_url_fail(client, fail_urls)
|
||||||
|
|||||||
17
ema/urls.py
17
ema/urls.py
@@ -9,27 +9,28 @@ from django.urls import path
|
|||||||
|
|
||||||
from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView
|
from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView
|
||||||
from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView
|
from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView
|
||||||
|
from ema.views.detail import DetailEmaView
|
||||||
from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView
|
from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView
|
||||||
from ema.views.ema import EmaIndexView, EmaIdentifierGeneratorView, EmaDetailView, EditEmaFormView, NewEmaFormView, \
|
from ema.views.ema import IndexEmaView, EmaIdentifierGeneratorView, EditEmaView, NewEmaView
|
||||||
RemoveEmaView
|
|
||||||
from ema.views.log import EmaLogView
|
from ema.views.log import EmaLogView
|
||||||
from ema.views.record import EmaRecordView
|
from ema.views.record import EmaRecordView
|
||||||
from ema.views.report import EmaReportView
|
from ema.views.remove import RemoveEmaView
|
||||||
|
from ema.views.report import EmaPublicReportView
|
||||||
from ema.views.resubmission import EmaResubmissionView
|
from ema.views.resubmission import EmaResubmissionView
|
||||||
from ema.views.share import EmaShareFormView, EmaShareByTokenView
|
from ema.views.share import EmaShareFormView, EmaShareByTokenView
|
||||||
from ema.views.state import NewEmaStateView, EditEmaStateView, RemoveEmaStateView
|
from ema.views.state import NewEmaStateView, EditEmaStateView, RemoveEmaStateView
|
||||||
|
|
||||||
app_name = "ema"
|
app_name = "ema"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", EmaIndexView.as_view(), name="index"),
|
path("", IndexEmaView.as_view(), name="index"),
|
||||||
path("new/", NewEmaFormView.as_view(), name="new"),
|
path("new/", NewEmaView.as_view(), name="new"),
|
||||||
path("new/id", EmaIdentifierGeneratorView.as_view(), name="new-id"),
|
path("new/id", EmaIdentifierGeneratorView.as_view(), name="new-id"),
|
||||||
path("<id>", EmaDetailView.as_view(), name="detail"),
|
path("<id>", DetailEmaView.as_view(), name="detail"),
|
||||||
path('<id>/log', EmaLogView.as_view(), name='log'),
|
path('<id>/log', EmaLogView.as_view(), name='log'),
|
||||||
path('<id>/edit', EditEmaFormView.as_view(), name='edit'),
|
path('<id>/edit', EditEmaView.as_view(), name='edit'),
|
||||||
path('<id>/remove', RemoveEmaView.as_view(), name='remove'),
|
path('<id>/remove', RemoveEmaView.as_view(), name='remove'),
|
||||||
path('<id>/record', EmaRecordView.as_view(), name='record'),
|
path('<id>/record', EmaRecordView.as_view(), name='record'),
|
||||||
path('<id>/report', EmaReportView.as_view(), name='report'),
|
path('<id>/report', EmaPublicReportView.as_view(), name='report'),
|
||||||
path('<id>/resub', EmaResubmissionView.as_view(), name='resubmission-create'),
|
path('<id>/resub', EmaResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
path('<id>/state/new', NewEmaStateView.as_view(), name='new-state'),
|
path('<id>/state/new', NewEmaStateView.as_view(), name='new-state'),
|
||||||
|
|||||||
@@ -5,31 +5,46 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
||||||
AbstractRemoveCompensationActionView
|
AbstractRemoveCompensationActionView
|
||||||
|
|
||||||
_EMA_ACCOUNT_DETAIL_URL_NAME = "ema:detail"
|
|
||||||
|
|
||||||
class NewEmaActionView(AbstractNewCompensationActionView):
|
class NewEmaActionView(AbstractNewCompensationActionView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = _EMA_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
class EditEmaActionView(AbstractEditCompensationActionView):
|
class EditEmaActionView(AbstractEditCompensationActionView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = _EMA_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEmaActionView(AbstractRemoveCompensationActionView):
|
class RemoveEmaActionView(AbstractRemoveCompensationActionView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = _EMA_ACCOUNT_DETAIL_URL_NAME
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,30 +5,46 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
from konova.views.deadline import AbstractNewDeadlineView, AbstractRemoveDeadlineView, AbstractEditDeadlineView
|
from konova.views.deadline import AbstractNewDeadlineView, AbstractRemoveDeadlineView, AbstractEditDeadlineView
|
||||||
|
|
||||||
_EMA_DETAIL_URL_NAME = "ema:detail"
|
|
||||||
|
|
||||||
class NewEmaDeadlineView(AbstractNewDeadlineView):
|
class NewEmaDeadlineView(AbstractNewDeadlineView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = _EMA_DETAIL_URL_NAME
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEmaDeadlineView(AbstractEditDeadlineView):
|
class EditEmaDeadlineView(AbstractEditDeadlineView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = _EMA_DETAIL_URL_NAME
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEmaDeadlineView(AbstractRemoveDeadlineView):
|
class RemoveEmaDeadlineView(AbstractRemoveDeadlineView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = _EMA_DETAIL_URL_NAME
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|||||||
76
ema/views/detail.py
Normal file
76
ema/views/detail.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import HttpResponse, HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE
|
||||||
|
from konova.views.detail import AbstractDetailView
|
||||||
|
|
||||||
|
|
||||||
|
class DetailEmaView(AbstractDetailView):
|
||||||
|
_TEMPLATE = "ema/detail/view.html"
|
||||||
|
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" Renders the detail view of an EMA
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The EMA id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
ema = get_object_or_404(Ema, id=id, deleted=None)
|
||||||
|
|
||||||
|
geom_form = SimpleGeomForm(instance=ema)
|
||||||
|
parcels = ema.get_underlying_parcels()
|
||||||
|
_user = request.user
|
||||||
|
is_entry_shared = ema.is_shared_with(_user)
|
||||||
|
|
||||||
|
# Order states according to surface
|
||||||
|
before_states = ema.before_states.all().order_by("-surface")
|
||||||
|
after_states = ema.after_states.all().order_by("-surface")
|
||||||
|
|
||||||
|
# Precalculate logical errors between before- and after-states
|
||||||
|
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
||||||
|
sum_before_states = ema.get_surface_before_states()
|
||||||
|
sum_after_states = ema.get_surface_after_states()
|
||||||
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
|
|
||||||
|
ema.set_status_messages(request)
|
||||||
|
|
||||||
|
requesting_user_is_only_shared_user = ema.is_only_shared_with(_user)
|
||||||
|
if requesting_user_is_only_shared_user:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
DO_NOT_FORGET_TO_SHARE
|
||||||
|
)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": ema,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"is_entry_shared": is_entry_shared,
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"sum_before_states": sum_before_states,
|
||||||
|
"sum_after_states": sum_after_states,
|
||||||
|
"diff_states": diff_states,
|
||||||
|
"is_default_member": _user.in_group(DEFAULT_GROUP),
|
||||||
|
"is_zb_member": _user.in_group(ZB_GROUP),
|
||||||
|
"is_ets_member": _user.in_group(ETS_GROUP),
|
||||||
|
"LANIS_LINK": ema.get_LANIS_link(),
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
|
||||||
|
"has_finished_deadlines": ema.get_finished_deadlines().exists(),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,41 +5,62 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ema.forms import NewEmaDocumentModalForm, RemoveEmaDocumentModalForm, EditEmaDocumentModalForm
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.forms import NewEmaDocumentModalForm
|
||||||
from ema.models import Ema, EmaDocument
|
from ema.models import Ema, EmaDocument
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
from konova.views.document import AbstractEditDocumentView, AbstractRemoveDocumentView, AbstractGetDocumentView, \
|
from konova.views.document import AbstractEditDocumentView, AbstractRemoveDocumentView, AbstractGetDocumentView, \
|
||||||
AbstractNewDocumentView
|
AbstractNewDocumentView
|
||||||
|
|
||||||
|
|
||||||
class NewEmaDocumentView(AbstractNewDocumentView):
|
class NewEmaDocumentView(AbstractNewDocumentView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_FORM_CLS = NewEmaDocumentModalForm
|
form = NewEmaDocumentModalForm
|
||||||
_REDIRECT_URL = "ema:detail"
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
class GetEmaDocumentView(AbstractGetDocumentView):
|
class GetEmaDocumentView(AbstractGetDocumentView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_DOCUMENT_CLS = EmaDocument
|
document_model = EmaDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
class RemoveEmaDocumentView(AbstractRemoveDocumentView):
|
class RemoveEmaDocumentView(AbstractRemoveDocumentView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_DOCUMENT_CLS = EmaDocument
|
document_model = EmaDocument
|
||||||
_FORM_CLS = RemoveEmaDocumentModalForm
|
|
||||||
_REDIRECT_URL = "ema:detail"
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
class EditEmaDocumentView(AbstractEditDocumentView):
|
class EditEmaDocumentView(AbstractEditDocumentView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_FORM_CLS = EditEmaDocumentModalForm
|
document_model = EmaDocument
|
||||||
_DOCUMENT_CLS = EmaDocument
|
form = EditDocumentModalForm
|
||||||
_REDIRECT_URL = "ema:detail"
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
298
ema/views/ema.py
298
ema/views/ema.py
@@ -5,112 +5,228 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.shortcuts import get_object_or_404
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic.base import View
|
||||||
|
|
||||||
from ema.forms import NewEmaForm, EditEmaForm
|
from ema.forms import NewEmaForm, EditEmaForm
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
from ema.tables import EmaTable
|
from ema.tables import EmaTable
|
||||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
from konova.contexts import BaseContext
|
||||||
BaseEditSpatialLocatedObjectFormView
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
from konova.views.detail import BaseDetailView
|
from konova.forms import SimpleGeomForm
|
||||||
from konova.views.remove import BaseRemoveModalFormView
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID, \
|
||||||
|
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
||||||
|
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||||
|
from konova.views.index import AbstractIndexView
|
||||||
|
|
||||||
|
|
||||||
class EmaIndexView(LoginRequiredMixin, BaseIndexView):
|
class IndexEmaView(AbstractIndexView):
|
||||||
_TAB_TITLE = _("EMAs - Overview")
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
_INDEX_TABLE_CLS = EmaTable
|
""" Renders the index view for EMAs
|
||||||
|
|
||||||
def _get_queryset(self):
|
Args:
|
||||||
qs = Ema.objects.filter(
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
emas = Ema.objects.filter(
|
||||||
deleted=None,
|
deleted=None,
|
||||||
).order_by(
|
).order_by(
|
||||||
"-modified__timestamp"
|
"-modified__timestamp"
|
||||||
)
|
)
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class NewEmaFormView(BaseNewSpatialLocatedObjectFormView):
|
|
||||||
_FORM_CLS = NewEmaForm
|
|
||||||
_MODEL_CLS = Ema
|
|
||||||
_TEMPLATE = "ema/form/view.html"
|
|
||||||
_TAB_TITLE = _("New EMA")
|
|
||||||
_REDIRECT_URL = "ema:detail"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
# User has to be an ets user
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
|
|
||||||
class EditEmaFormView(BaseEditSpatialLocatedObjectFormView):
|
|
||||||
_MODEL_CLS = Ema
|
|
||||||
_FORM_CLS = EditEmaForm
|
|
||||||
_TEMPLATE = "ema/form/view.html"
|
|
||||||
_REDIRECT_URL = "ema:detail"
|
|
||||||
_TAB_TITLE = _("Edit {}")
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
# User has to be an ets user
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
|
|
||||||
class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
|
||||||
_MODEL_CLS = Ema
|
|
||||||
_REDIRECT_URL = "ema:index"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_ets_user()
|
|
||||||
|
|
||||||
|
|
||||||
class EmaDetailView(BaseDetailView):
|
|
||||||
_MODEL_CLS = Ema
|
|
||||||
_TEMPLATE = "ema/detail/view.html"
|
|
||||||
|
|
||||||
def _get_object(self, id: str):
|
|
||||||
""" Fetch object for detail view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
id (str): The record's id'
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id, deleted=None)
|
|
||||||
return ema
|
|
||||||
|
|
||||||
def _get_detail_context(self, obj: Ema):
|
|
||||||
""" Generate object specific detail context for view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
obj (): The record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Order states according to surface
|
|
||||||
before_states = obj.before_states.all().order_by("-surface")
|
|
||||||
after_states = obj.after_states.all().order_by("-surface")
|
|
||||||
|
|
||||||
# Precalculate logical errors between before- and after-states
|
|
||||||
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
|
||||||
sum_before_states = obj.get_surface_before_states()
|
|
||||||
sum_after_states = obj.get_surface_after_states()
|
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
|
||||||
|
|
||||||
|
table = EmaTable(
|
||||||
|
request,
|
||||||
|
queryset=emas
|
||||||
|
)
|
||||||
context = {
|
context = {
|
||||||
"before_states": before_states,
|
"table": table,
|
||||||
"after_states": after_states,
|
TAB_TITLE_IDENTIFIER: _("EMAs - Overview"),
|
||||||
"sum_before_states": sum_before_states,
|
|
||||||
"sum_after_states": sum_after_states,
|
|
||||||
"diff_states": diff_states,
|
|
||||||
"has_finished_deadlines": obj.get_finished_deadlines().exists(),
|
|
||||||
}
|
}
|
||||||
return context
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
class RemoveEmaView(LoginRequiredMixin, BaseRemoveModalFormView):
|
class NewEmaView(LoginRequiredMixin, View):
|
||||||
_MODEL_CLS = Ema
|
_TEMPLATE = "ema/form/view.html"
|
||||||
_REDIRECT_URL = "ema:index"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(conservation_office_group_required)
|
||||||
return user.is_ets_user()
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" GET endpoint
|
||||||
|
|
||||||
|
Renders form for new EMA
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
*args ():
|
||||||
|
**kwargs ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
data_form = NewEmaForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New EMA"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" POST endpoint
|
||||||
|
|
||||||
|
Processes submitted form
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
*args ():
|
||||||
|
**kwargs ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
data_form = NewEmaForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
ema = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != ema.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
ema.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.success(request, _("EMA {} added").format(ema.identifier))
|
||||||
|
if geom_form.has_geometry_simplified():
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRY_SIMPLIFIED
|
||||||
|
)
|
||||||
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
|
if num_ignored_geometries > 0:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
|
)
|
||||||
|
return redirect("ema:detail", id=ema.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New EMA"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
class EmaIdentifierGeneratorView(AbstractIdentifierGeneratorView):
|
||||||
|
_MODEL = Ema
|
||||||
|
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
class EditEmaView(LoginRequiredMixin, View):
|
||||||
|
_TEMPLATE = "compensation/form/view.html"
|
||||||
|
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" GET endpoint
|
||||||
|
|
||||||
|
Renders form
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The ema identifier
|
||||||
|
*args ():
|
||||||
|
**kwargs ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get object from db
|
||||||
|
ema = get_object_or_404(Ema, id=id)
|
||||||
|
if ema.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("ema:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditEmaForm(instance=ema)
|
||||||
|
geom_form = SimpleGeomForm(read_only=False, instance=ema)
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def post(self, request: HttpRequest, id:str, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" POST endpoint
|
||||||
|
|
||||||
|
Process submitted forms
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the ema
|
||||||
|
*args ():
|
||||||
|
**kwargs ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get object from db
|
||||||
|
ema = get_object_or_404(Ema, id=id)
|
||||||
|
if ema.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("ema:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditEmaForm(request.POST or None, instance=ema)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
|
ema = data_form.save(request.user, geom_form)
|
||||||
|
messages.success(request, _("EMA {} edited").format(ema.identifier))
|
||||||
|
if geom_form.has_geometry_simplified():
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRY_SIMPLIFIED
|
||||||
|
)
|
||||||
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
|
if num_ignored_geometries > 0:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
|
)
|
||||||
|
return redirect("ema:detail", id=ema.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger", )
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|||||||
@@ -5,14 +5,19 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
from konova.views.log import AbstractLogView
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
class EmaLogView(LoginRequiredMixin, AbstractLogView):
|
class EmaLogView(AbstractLogView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,12 +5,20 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
from konova.views.record import AbstractRecordView
|
from konova.views.record import AbstractRecordView
|
||||||
|
|
||||||
|
|
||||||
class EmaRecordView(LoginRequiredMixin, AbstractRecordView):
|
class EmaRecordView(AbstractRecordView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = "ema:detail"
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
21
ema/views/remove.py
Normal file
21
ema/views/remove.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.remove import AbstractRemoveView
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEmaView(AbstractRemoveView):
|
||||||
|
_MODEL = Ema
|
||||||
|
_REDIRECT_URL = "ema:index"
|
||||||
|
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
@@ -5,36 +5,81 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.views.compensation.report import BaseCompensationReportView
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
from konova.sub_settings.django_settings import BASE_URL
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.qrcode import QrCode
|
from konova.utils.qrcode import QrCode
|
||||||
|
from konova.views.report import AbstractPublicReportView
|
||||||
|
|
||||||
|
|
||||||
class EmaReportView(BaseCompensationReportView):
|
class EmaPublicReportView(AbstractPublicReportView):
|
||||||
_TEMPLATE = "ema/report/report.html"
|
_TEMPLATE = "ema/report/report.html"
|
||||||
_MODEL = Ema
|
|
||||||
|
|
||||||
def _get_report_context(self, obj):
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
report_url = BASE_URL + reverse("ema:report", args=(obj.id,))
|
""" Renders the public report view
|
||||||
qrcode_report = QrCode(report_url, 10)
|
|
||||||
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
|
|
||||||
|
|
||||||
generic_compensation_report_context = self._get_compensation_report_context(obj)
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
report_context = {
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
ema = get_object_or_404(Ema, id=id)
|
||||||
|
tab_title = _("Report {}").format(ema.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not ema.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=ema,
|
||||||
|
)
|
||||||
|
parcels = ema.get_underlying_parcels()
|
||||||
|
|
||||||
|
qrcode = QrCode(
|
||||||
|
content=request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
||||||
|
size=10
|
||||||
|
)
|
||||||
|
qrcode_lanis = QrCode(
|
||||||
|
content=ema.get_LANIS_link(),
|
||||||
|
size=7
|
||||||
|
)
|
||||||
|
|
||||||
|
# Order states by surface
|
||||||
|
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
actions = ema.actions.all().prefetch_related("action_type")
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": ema,
|
||||||
"qrcode": {
|
"qrcode": {
|
||||||
"img": qrcode_report.get_img(),
|
"img": qrcode.get_img(),
|
||||||
"url": qrcode_report.get_content(),
|
"url": qrcode.get_content(),
|
||||||
},
|
},
|
||||||
"qrcode_lanis": {
|
"qrcode_lanis": {
|
||||||
"img": qrcode_lanis.get_img(),
|
"img": qrcode_lanis.get_img(),
|
||||||
"url": qrcode_lanis.get_content(),
|
"url": qrcode_lanis.get_content(),
|
||||||
},
|
},
|
||||||
"is_entry_shared": False, # disables action buttons during rendering
|
"is_entry_shared": False, # disables action buttons during rendering
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"actions": actions,
|
||||||
"tables_scrollable": False,
|
"tables_scrollable": False,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
}
|
}
|
||||||
report_context.update(generic_compensation_report_context)
|
context = BaseContext(request, context).context
|
||||||
return report_context
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,16 +5,22 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ema.forms import EmaResubmissionModalForm
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
|
||||||
from konova.views.resubmission import AbstractResubmissionView
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
class EmaResubmissionView(AbstractResubmissionView):
|
class EmaResubmissionView(AbstractResubmissionView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_FORM_CLS = EmaResubmissionModalForm
|
redirect_url_base = "ema:detail"
|
||||||
_REDIRECT_URL = "ema:detail"
|
form_action_url_base = "ema:resubmission-create"
|
||||||
action_url = "ema:resubmission-create"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,17 +5,29 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import conservation_office_group_required, shared_access_required, login_required_modal
|
||||||
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
||||||
|
|
||||||
|
|
||||||
class EmaShareByTokenView(AbstractShareByTokenView):
|
class EmaShareByTokenView(AbstractShareByTokenView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = "ema:detail"
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EmaShareFormView(AbstractShareFormView):
|
class EmaShareFormView(AbstractShareFormView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = "ema:detail"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,30 +5,46 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
|
from konova.decorators import conservation_office_group_required, shared_access_required, login_required_modal
|
||||||
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
||||||
AbstractRemoveCompensationStateView
|
AbstractRemoveCompensationStateView
|
||||||
|
|
||||||
|
|
||||||
class NewEmaStateView(AbstractNewCompensationStateView):
|
class NewEmaStateView(AbstractNewCompensationStateView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = "ema:detail"
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditEmaStateView(AbstractEditCompensationStateView):
|
class EditEmaStateView(AbstractEditCompensationStateView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = "ema:detail"
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveEmaStateView(AbstractRemoveCompensationStateView):
|
class RemoveEmaStateView(AbstractRemoveCompensationStateView):
|
||||||
_MODEL_CLS = Ema
|
model = Ema
|
||||||
_REDIRECT_URL = "ema:detail"
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
@method_decorator(login_required_modal)
|
||||||
return user.is_ets_user()
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -37,6 +37,14 @@ class InterventionAdmin(BaseObjectAdmin):
|
|||||||
"geometry",
|
"geometry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_actions(self, request):
|
||||||
|
DELETE_ACTION_IDENTIFIER = "delete_selected"
|
||||||
|
actions = super().get_actions(request)
|
||||||
|
|
||||||
|
if DELETE_ACTION_IDENTIFIER in actions:
|
||||||
|
del actions[DELETE_ACTION_IDENTIFIER]
|
||||||
|
|
||||||
|
return actions
|
||||||
|
|
||||||
class InterventionDocumentAdmin(AbstractDocumentAdmin):
|
class InterventionDocumentAdmin(AbstractDocumentAdmin):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -172,8 +172,7 @@ class EditEcoAccountDeductionModalForm(NewEcoAccountDeductionModalForm):
|
|||||||
deduction = None
|
deduction = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
deduction_id = kwargs.pop("deduction_id", None)
|
self.deduction = kwargs.pop("deduction", None)
|
||||||
self.deduction = EcoAccountDeduction.objects.get(id=deduction_id)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit Deduction")
|
self.form_title = _("Edit Deduction")
|
||||||
form_data = {
|
form_data = {
|
||||||
@@ -253,20 +252,19 @@ class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
|
|||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_DEDUCTION_OBJ = None
|
deduction = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
deduction_id = kwargs.pop("deduction_id", None)
|
deduction = kwargs.pop("deduction", None)
|
||||||
deduction = EcoAccountDeduction.objects.get(id=deduction_id)
|
self.deduction = deduction
|
||||||
self._DEDUCTION_OBJ = deduction
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self._DEDUCTION_OBJ.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
||||||
self._DEDUCTION_OBJ.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
||||||
self._DEDUCTION_OBJ.delete()
|
self.deduction.delete()
|
||||||
|
|
||||||
def check_for_recorded_instance(self):
|
def check_for_recorded_instance(self):
|
||||||
if self._DEDUCTION_OBJ.intervention.is_recorded:
|
if self.deduction.intervention.is_recorded:
|
||||||
self.block_form()
|
self.block_form()
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ Created on: 18.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from intervention.models import InterventionDocument
|
from intervention.models import InterventionDocument
|
||||||
from konova.forms.modals import NewDocumentModalForm, EditDocumentModalForm, RemoveDocumentModalForm
|
from konova.forms.modals import NewDocumentModalForm
|
||||||
|
|
||||||
|
|
||||||
class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
||||||
_DOCUMENT_CLS = InterventionDocument
|
document_model = InterventionDocument
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
""" Extension of regular NewDocumentModalForm
|
""" Extension of regular NewDocumentModalForm
|
||||||
@@ -28,31 +28,3 @@ class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
|||||||
self.instance.send_data_to_egon()
|
self.instance.send_data_to_egon()
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
class EditInterventionDocumentModalForm(EditDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = InterventionDocument
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
""" Extension of regular EditDocumentModalForm
|
|
||||||
|
|
||||||
Checks whether payments exist on the intervention and sends the data to EGON
|
|
||||||
|
|
||||||
Args:
|
|
||||||
*args ():
|
|
||||||
**kwargs ():
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
doc = super().save(*args, **kwargs)
|
|
||||||
self.instance.send_data_to_egon()
|
|
||||||
|
|
||||||
return doc
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveInterventionDocumentModalForm(RemoveDocumentModalForm):
|
|
||||||
_DOCUMENT_CLS = InterventionDocument
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
self.instance.send_data_to_egon()
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Created on: 08.11.25
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
|
|
||||||
from compensation.models import Compensation
|
|
||||||
from konova.forms.modals import RemoveModalForm
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationFromInterventionModalForm(RemoveModalForm):
|
|
||||||
""" Specific form for removing a compensation from an intervention
|
|
||||||
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
# The 'instance' that is pushed into the constructor by the generic base class points to the
|
|
||||||
# intervention instead of the compensation, which shall be deleted. Therefore we need to fetch the compensation
|
|
||||||
# and replace the instance parameter with that object
|
|
||||||
instance = get_object_or_404(Compensation, id=kwargs.pop("comp_id"))
|
|
||||||
kwargs["instance"] = instance
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Created on: 21.10.25
|
|
||||||
|
|
||||||
"""
|
|
||||||
from intervention.models import Intervention
|
|
||||||
from konova.forms.modals import ResubmissionModalForm
|
|
||||||
|
|
||||||
|
|
||||||
class InterventionResubmissionModalForm(ResubmissionModalForm):
|
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
@@ -7,10 +7,9 @@ Created on: 18.08.22
|
|||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from intervention.models import RevocationDocument, Revocation
|
from intervention.models import RevocationDocument
|
||||||
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
from konova.utils import validators
|
from konova.utils import validators
|
||||||
from konova.utils.message_templates import REVOCATION_ADDED, REVOCATION_EDITED
|
from konova.utils.message_templates import REVOCATION_ADDED, REVOCATION_EDITED
|
||||||
@@ -76,8 +75,7 @@ class EditRevocationModalForm(NewRevocationModalForm):
|
|||||||
revocation = None
|
revocation = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
revocation_id = kwargs.pop("revocation_id", None)
|
self.revocation = kwargs.pop("revocation", None)
|
||||||
self.revocation = get_object_or_404(Revocation, id=revocation_id)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit revocation")
|
self.form_title = _("Edit revocation")
|
||||||
try:
|
try:
|
||||||
@@ -106,8 +104,8 @@ class RemoveRevocationModalForm(RemoveModalForm):
|
|||||||
revocation = None
|
revocation = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
revocation_id = kwargs.pop("revocation_id", None)
|
revocation = kwargs.pop("revocation", None)
|
||||||
self.revocation = get_object_or_404(Revocation, id=revocation_id)
|
self.revocation = revocation
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ class EditRevocationModalFormTestCase(NewRevocationModalFormTestCase):
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.intervention,
|
instance=self.intervention,
|
||||||
revocation_id=self.revoc.id
|
revocation=self.revoc
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
obj = form.save()
|
obj = form.save()
|
||||||
@@ -302,7 +302,7 @@ class RemoveRevocationModalFormTestCase(EditRevocationModalFormTestCase):
|
|||||||
form = RemoveRevocationModalForm(
|
form = RemoveRevocationModalForm(
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.intervention,
|
instance=self.intervention,
|
||||||
revocation_id=self.revoc.id,
|
revocation=self.revoc,
|
||||||
)
|
)
|
||||||
self.assertEqual(form.instance, self.intervention)
|
self.assertEqual(form.instance, self.intervention)
|
||||||
self.assertEqual(form.revocation, self.revoc)
|
self.assertEqual(form.revocation, self.revoc)
|
||||||
@@ -317,7 +317,7 @@ class RemoveRevocationModalFormTestCase(EditRevocationModalFormTestCase):
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.intervention,
|
instance=self.intervention,
|
||||||
revocation_id=self.revoc.id
|
revocation=self.revoc
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
form.save()
|
form.save()
|
||||||
|
|||||||
@@ -14,30 +14,32 @@ from intervention.views.deduction import NewInterventionDeductionView, EditInter
|
|||||||
RemoveInterventionDeductionView
|
RemoveInterventionDeductionView
|
||||||
from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \
|
from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \
|
||||||
RemoveInterventionDocumentView, EditInterventionDocumentView
|
RemoveInterventionDocumentView, EditInterventionDocumentView
|
||||||
from intervention.views.intervention import InterventionIndexView, InterventionIdentifierGeneratorView, \
|
from intervention.views.intervention import IndexInterventionView, InterventionIdentifierGeneratorView, \
|
||||||
InterventionDetailView, NewInterventionFormView, EditInterventionFormView, RemoveInterventionView
|
NewInterventionView, EditInterventionView
|
||||||
|
from intervention.views.remove import RemoveInterventionView
|
||||||
|
from intervention.views.detail import DetailInterventionView
|
||||||
from intervention.views.log import InterventionLogView
|
from intervention.views.log import InterventionLogView
|
||||||
from intervention.views.record import InterventionRecordView
|
from intervention.views.record import InterventionRecordView
|
||||||
from intervention.views.report import InterventionReportView
|
from intervention.views.report import InterventionPublicReportView
|
||||||
from intervention.views.resubmission import InterventionResubmissionView
|
from intervention.views.resubmission import InterventionResubmissionView
|
||||||
from intervention.views.revocation import NewRevocationView, GetRevocationDocumentView, EditRevocationView, \
|
from intervention.views.revocation import NewInterventionRevocationView, GetInterventionRevocationView, \
|
||||||
RemoveRevocationView
|
EditInterventionRevocationView, RemoveInterventionRevocationView
|
||||||
from intervention.views.share import InterventionShareFormView, InterventionShareByTokenView
|
from intervention.views.share import InterventionShareFormView, InterventionShareByTokenView
|
||||||
|
|
||||||
app_name = "intervention"
|
app_name = "intervention"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", InterventionIndexView.as_view(), name="index"),
|
path("", IndexInterventionView.as_view(), name="index"),
|
||||||
path('new/', NewInterventionFormView.as_view(), name='new'),
|
path('new/', NewInterventionView.as_view(), name='new'),
|
||||||
path('new/id', InterventionIdentifierGeneratorView.as_view(), name='new-id'),
|
path('new/id', InterventionIdentifierGeneratorView.as_view(), name='new-id'),
|
||||||
path('<id>', InterventionDetailView.as_view(), name='detail'),
|
path('<id>', DetailInterventionView.as_view(), name='detail'),
|
||||||
path('<id>/log', InterventionLogView.as_view(), name='log'),
|
path('<id>/log', InterventionLogView.as_view(), name='log'),
|
||||||
path('<id>/edit', EditInterventionFormView.as_view(), name='edit'),
|
path('<id>/edit', EditInterventionView.as_view(), name='edit'),
|
||||||
path('<id>/remove', RemoveInterventionView.as_view(), name='remove'),
|
path('<id>/remove', RemoveInterventionView.as_view(), name='remove'),
|
||||||
path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
|
path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
|
||||||
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
|
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
|
||||||
path('<id>/check', InterventionCheckView.as_view(), name='check'),
|
path('<id>/check', InterventionCheckView.as_view(), name='check'),
|
||||||
path('<id>/record', InterventionRecordView.as_view(), name='record'),
|
path('<id>/record', InterventionRecordView.as_view(), name='record'),
|
||||||
path('<id>/report', InterventionReportView.as_view(), name='report'),
|
path('<id>/report', InterventionPublicReportView.as_view(), name='report'),
|
||||||
path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),
|
path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
# Compensations
|
# Compensations
|
||||||
@@ -55,10 +57,10 @@ urlpatterns = [
|
|||||||
path('<id>/deduction/<deduction_id>/remove', RemoveInterventionDeductionView.as_view(), name='remove-deduction'),
|
path('<id>/deduction/<deduction_id>/remove', RemoveInterventionDeductionView.as_view(), name='remove-deduction'),
|
||||||
|
|
||||||
# Revocation routes
|
# Revocation routes
|
||||||
path('<id>/revocation/new', NewRevocationView.as_view(), name='new-revocation'),
|
path('<id>/revocation/new', NewInterventionRevocationView.as_view(), name='new-revocation'),
|
||||||
path('<id>/revocation/<revocation_id>/edit', EditRevocationView.as_view(), name='edit-revocation'),
|
path('<id>/revocation/<revocation_id>/edit', EditInterventionRevocationView.as_view(), name='edit-revocation'),
|
||||||
path('<id>/revocation/<revocation_id>/remove', RemoveRevocationView.as_view(), name='remove-revocation'),
|
path('<id>/revocation/<revocation_id>/remove', RemoveInterventionRevocationView.as_view(), name='remove-revocation'),
|
||||||
path('revocation/<doc_id>', GetRevocationDocumentView.as_view(), name='get-doc-revocation'),
|
path('revocation/<doc_id>', GetInterventionRevocationView.as_view(), name='get-doc-revocation'),
|
||||||
|
|
||||||
# Autocomplete
|
# Autocomplete
|
||||||
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="autocomplete"),
|
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="autocomplete"),
|
||||||
|
|||||||
@@ -6,23 +6,43 @@ Created on: 19.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from intervention.forms.modals.check import CheckModalForm
|
from intervention.forms.modals.check import CheckModalForm
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.views.base import BaseModalFormView
|
from konova.decorators import registration_office_group_required, shared_access_required
|
||||||
|
from konova.utils.message_templates import INTERVENTION_INVALID
|
||||||
|
|
||||||
|
class InterventionCheckView(LoginRequiredMixin, View):
|
||||||
|
|
||||||
class InterventionCheckView(LoginRequiredMixin, BaseModalFormView):
|
def __process_request(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
_MODEL_CLS = Intervention
|
""" Renders check form for an intervention
|
||||||
_FORM_CLS = CheckModalForm
|
|
||||||
_MSG_SUCCESS = _("Check performed")
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
Args:
|
||||||
return user.is_zb_user()
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): Intervention's id
|
||||||
|
|
||||||
def _get_redirect_url(self, *args, **kwargs):
|
Returns:
|
||||||
redirect_url = super()._get_redirect_url(*args, **kwargs)
|
|
||||||
redirect_url += "#related_data"
|
"""
|
||||||
return redirect_url
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
msg_success=_("Check performed"),
|
||||||
|
msg_error=INTERVENTION_INVALID
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(registration_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, *args, **kwargs)
|
||||||
|
|
||||||
|
@method_decorator(registration_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, *args, **kwargs)
|
||||||
|
|||||||
@@ -6,35 +6,49 @@ Created on: 19.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.http import HttpRequest, Http404, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from compensation.models import Compensation
|
|
||||||
from intervention.forms.modals.remove import RemoveCompensationFromInterventionModalForm
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import shared_access_required
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
|
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
|
||||||
from konova.views.remove import BaseRemoveModalFormView
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationFromInterventionView(LoginRequiredMixin, BaseRemoveModalFormView):
|
class RemoveCompensationFromInterventionView(LoginRequiredMixin, View):
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_FORM_CLS = RemoveCompensationFromInterventionModalForm
|
|
||||||
_MSG_SUCCESS = COMPENSATION_REMOVED_TEMPLATE
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
|
||||||
def _user_has_shared_access(self, user, **kwargs):
|
def __process_request(self, request: HttpRequest, id: str, comp_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
compensation_id = kwargs.get("comp_id", None)
|
""" Renders a modal view for removing the compensation
|
||||||
compensation = get_object_or_404(Compensation, id=compensation_id)
|
|
||||||
return compensation.is_shared_with(user)
|
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
Args:
|
||||||
return user.is_default_user()
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
def _get_msg_success(self, *args, **kwargs):
|
Returns:
|
||||||
compensation_id = kwargs.get("comp_id", None)
|
|
||||||
compensation = get_object_or_404(Compensation, id=compensation_id)
|
|
||||||
return self._MSG_SUCCESS.format(compensation.identifier)
|
|
||||||
|
|
||||||
def _get_redirect_url(self, *args, **kwargs):
|
"""
|
||||||
obj = kwargs.get("obj")
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
return reverse(self._REDIRECT_URL, args=(obj.id,)) + "#related_data"
|
try:
|
||||||
|
comp = intervention.compensations.get(
|
||||||
|
id=comp_id
|
||||||
|
)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise Http404("Unknown compensation")
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
|
||||||
|
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data",
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request, id: str, comp_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, comp_id, *args, **kwargs)
|
||||||
|
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request, id: str, comp_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, comp_id, *args, **kwargs)
|
||||||
@@ -5,27 +5,51 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.utils.message_templates import DEDUCTION_ADDED, DEDUCTION_EDITED, DEDUCTION_REMOVED
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
|
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
|
||||||
|
|
||||||
_INTERVENTION_DETAIL_URL_NAME = "intervention:detail"
|
|
||||||
|
|
||||||
class NewInterventionDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
|
class NewInterventionDeductionView(AbstractNewDeductionView):
|
||||||
_MODEL_CLS = Intervention
|
def _custom_check(self, obj):
|
||||||
_MSG_SUCCESS = DEDUCTION_ADDED
|
pass
|
||||||
_REDIRECT_URL = _INTERVENTION_DETAIL_URL_NAME
|
|
||||||
|
model = Intervention
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditInterventionDeductionView(LoginRequiredMixin, AbstractEditDeductionView):
|
class EditInterventionDeductionView(AbstractEditDeductionView):
|
||||||
_MODEL_CLS = Intervention
|
def _custom_check(self, obj):
|
||||||
_MSG_SUCCESS = DEDUCTION_EDITED
|
pass
|
||||||
_REDIRECT_URL = _INTERVENTION_DETAIL_URL_NAME
|
|
||||||
|
model = Intervention
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveInterventionDeductionView(LoginRequiredMixin, AbstractRemoveDeductionView):
|
class RemoveInterventionDeductionView(AbstractRemoveDeductionView):
|
||||||
_MODEL_CLS = Intervention
|
def _custom_check(self, obj):
|
||||||
_MSG_SUCCESS = DEDUCTION_REMOVED
|
pass
|
||||||
_REDIRECT_URL = _INTERVENTION_DETAIL_URL_NAME
|
|
||||||
|
model = Intervention
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
79
intervention/views/detail.py
Normal file
79
intervention/views/detail.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, DO_NOT_FORGET_TO_SHARE
|
||||||
|
from konova.views.detail import AbstractDetailView
|
||||||
|
|
||||||
|
|
||||||
|
class DetailInterventionView(AbstractDetailView):
|
||||||
|
_TEMPLATE = "intervention/detail/view.html"
|
||||||
|
|
||||||
|
def get(self, request, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
# Fetch data, filter out deleted related data
|
||||||
|
intervention = get_object_or_404(
|
||||||
|
Intervention.objects.select_related(
|
||||||
|
"geometry",
|
||||||
|
"legal",
|
||||||
|
"responsible",
|
||||||
|
).prefetch_related(
|
||||||
|
"legal__revocations",
|
||||||
|
),
|
||||||
|
id=id,
|
||||||
|
deleted=None
|
||||||
|
)
|
||||||
|
compensations = intervention.compensations.filter(
|
||||||
|
deleted=None,
|
||||||
|
)
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = intervention.is_shared_with(user=_user)
|
||||||
|
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=intervention,
|
||||||
|
)
|
||||||
|
last_checked = intervention.get_last_checked_action()
|
||||||
|
last_checked_tooltip = ""
|
||||||
|
if last_checked:
|
||||||
|
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
|
||||||
|
last_checked.get_timestamp_str_formatted(),
|
||||||
|
last_checked.user
|
||||||
|
)
|
||||||
|
|
||||||
|
has_payment_without_document = intervention.payments.exists() and not intervention.get_documents()[1].exists()
|
||||||
|
|
||||||
|
requesting_user_is_only_shared_user = intervention.is_only_shared_with(_user)
|
||||||
|
if requesting_user_is_only_shared_user:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
DO_NOT_FORGET_TO_SHARE
|
||||||
|
)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": intervention,
|
||||||
|
"last_checked": last_checked,
|
||||||
|
"last_checked_tooltip": last_checked_tooltip,
|
||||||
|
"compensations": compensations,
|
||||||
|
"is_entry_shared": is_data_shared,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"is_default_member": _user.in_group(DEFAULT_GROUP),
|
||||||
|
"is_zb_member": _user.in_group(ZB_GROUP),
|
||||||
|
"is_ets_member": _user.in_group(ETS_GROUP),
|
||||||
|
"LANIS_LINK": intervention.get_LANIS_link(),
|
||||||
|
"has_payment_without_document": has_payment_without_document,
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
|
||||||
|
}
|
||||||
|
|
||||||
|
request = intervention.set_status_messages(request)
|
||||||
|
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,33 +5,59 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from intervention.forms.modals.document import NewInterventionDocumentModalForm, EditInterventionDocumentModalForm, \
|
from django.contrib.auth.decorators import login_required
|
||||||
RemoveInterventionDocumentModalForm
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from intervention.forms.modals.document import NewInterventionDocumentModalForm
|
||||||
from intervention.models import Intervention, InterventionDocument
|
from intervention.models import Intervention, InterventionDocument
|
||||||
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
||||||
AbstractEditDocumentView
|
AbstractEditDocumentView
|
||||||
|
|
||||||
|
|
||||||
class NewInterventionDocumentView(AbstractNewDocumentView):
|
class NewInterventionDocumentView(AbstractNewDocumentView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_DOCUMENT_MODEL = InterventionDocument
|
form = NewInterventionDocumentModalForm
|
||||||
_FORM_CLS = NewInterventionDocumentModalForm
|
redirect_url = "intervention:detail"
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GetInterventionDocumentView(AbstractGetDocumentView):
|
class GetInterventionDocumentView(AbstractGetDocumentView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_DOCUMENT_CLS = InterventionDocument
|
document_model = InterventionDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RemoveInterventionDocumentView(AbstractRemoveDocumentView):
|
class RemoveInterventionDocumentView(AbstractRemoveDocumentView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_DOCUMENT_CLS = InterventionDocument
|
document_model = InterventionDocument
|
||||||
_FORM_CLS = RemoveInterventionDocumentModalForm
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EditInterventionDocumentView(AbstractEditDocumentView):
|
class EditInterventionDocumentView(AbstractEditDocumentView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_DOCUMENT_CLS = InterventionDocument
|
document_model = InterventionDocument
|
||||||
_FORM_CLS = EditInterventionDocumentModalForm
|
form = EditDocumentModalForm
|
||||||
_REDIRECT_URL = "intervention:detail"
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ Created on: 19.08.22
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from intervention.forms.intervention import EditInterventionForm, NewInterventionForm
|
from intervention.forms.intervention import EditInterventionForm, NewInterventionForm
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
@@ -19,132 +21,189 @@ from konova.contexts import BaseContext
|
|||||||
from konova.decorators import default_group_required, shared_access_required
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
from konova.forms import SimpleGeomForm
|
from konova.forms import SimpleGeomForm
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
|
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, \
|
||||||
CHECK_STATE_RESET, FORM_INVALID, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, \
|
||||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
GEOMETRIES_IGNORED_TEMPLATE
|
||||||
BaseEditSpatialLocatedObjectFormView
|
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||||
from konova.views.detail import BaseDetailView
|
from konova.views.index import AbstractIndexView
|
||||||
from konova.views.remove import BaseRemoveModalFormView
|
|
||||||
|
|
||||||
|
|
||||||
class InterventionIndexView(LoginRequiredMixin, BaseIndexView):
|
class IndexInterventionView(AbstractIndexView):
|
||||||
_INDEX_TABLE_CLS = InterventionTable
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
_TAB_TITLE = _("Interventions - Overview")
|
"""
|
||||||
|
Renders the index view for Interventions
|
||||||
|
|
||||||
def _get_queryset(self):
|
Args:
|
||||||
qs = Intervention.objects.filter(
|
request (HttpRequest): The incoming request
|
||||||
deleted=None,
|
|
||||||
|
Returns:
|
||||||
|
A rendered view
|
||||||
|
"""
|
||||||
|
# Filtering by user access is performed in table filter inside InterventionTableFilter class
|
||||||
|
interventions = Intervention.objects.filter(
|
||||||
|
deleted=None, # not deleted
|
||||||
).select_related(
|
).select_related(
|
||||||
"legal"
|
"legal"
|
||||||
).order_by(
|
).order_by(
|
||||||
"-modified__timestamp"
|
"-modified__timestamp"
|
||||||
)
|
)
|
||||||
return qs
|
table = InterventionTable(
|
||||||
|
request=request,
|
||||||
|
queryset=interventions
|
||||||
class NewInterventionFormView(BaseNewSpatialLocatedObjectFormView):
|
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_FORM_CLS = NewInterventionForm
|
|
||||||
_TEMPLATE = "intervention/form/view.html"
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
_TAB_TITLE = _("New intervention")
|
|
||||||
|
|
||||||
|
|
||||||
class EditInterventionFormView(BaseEditSpatialLocatedObjectFormView):
|
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_FORM_CLS = EditInterventionForm
|
|
||||||
_TEMPLATE = "intervention/form/view.html"
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
_TAB_TITLE = _("Edit {}")
|
|
||||||
|
|
||||||
|
|
||||||
class InterventionIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_REDIRECT_URL = "intervention:index"
|
|
||||||
|
|
||||||
|
|
||||||
class InterventionDetailView(BaseDetailView):
|
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_TEMPLATE = "intervention/detail/view.html"
|
|
||||||
|
|
||||||
def _get_object(self, id: str):
|
|
||||||
""" Returns the intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
id (str): The intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
obj (Intervention): The intervention
|
|
||||||
"""
|
|
||||||
# Fetch data, filter out deleted related data
|
|
||||||
obj = get_object_or_404(
|
|
||||||
self._MODEL_CLS.objects.select_related(
|
|
||||||
"geometry",
|
|
||||||
"legal",
|
|
||||||
"responsible",
|
|
||||||
).prefetch_related(
|
|
||||||
"legal__revocations",
|
|
||||||
),
|
|
||||||
id=id,
|
|
||||||
deleted=None
|
|
||||||
)
|
)
|
||||||
return obj
|
|
||||||
|
|
||||||
def _get_detail_context(self, obj: Intervention):
|
|
||||||
""" Generate object specific detail context for view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
obj (): The record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
compensations = obj.compensations.filter(deleted=None)
|
|
||||||
last_checked = obj.get_last_checked_action()
|
|
||||||
last_checked_tooltip = ""
|
|
||||||
if last_checked:
|
|
||||||
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
|
|
||||||
last_checked.get_timestamp_str_formatted(),
|
|
||||||
last_checked.user
|
|
||||||
)
|
|
||||||
|
|
||||||
has_payment_without_document = obj.payments.exists() and not obj.get_documents()[1].exists()
|
|
||||||
context = {
|
context = {
|
||||||
"last_checked": last_checked,
|
"table": table,
|
||||||
"last_checked_tooltip": last_checked_tooltip,
|
TAB_TITLE_IDENTIFIER: _("Interventions - Overview"),
|
||||||
"compensations": compensations,
|
|
||||||
"has_payment_without_document": has_payment_without_document,
|
|
||||||
}
|
}
|
||||||
return context
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
class NewInterventionView(LoginRequiredMixin, View):
|
||||||
@default_group_required
|
_TEMPLATE = "intervention/form/view.html"
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def edit_view(request: HttpRequest, id: str):
|
|
||||||
"""
|
|
||||||
Renders a view for editing interventions
|
|
||||||
|
|
||||||
Args:
|
@method_decorator(default_group_required)
|
||||||
request (HttpRequest): The incoming request
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
Returns:
|
"""
|
||||||
|
Renders a view for a new intervention creation
|
||||||
|
|
||||||
"""
|
Args:
|
||||||
template = "intervention/form/view.html"
|
request (HttpRequest): The incoming request
|
||||||
# Get object from db
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
if intervention.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("intervention:detail", id=id)
|
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
Returns:
|
||||||
data_form = EditInterventionForm(request.POST or None, instance=intervention)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
|
"""
|
||||||
if request.method == "POST":
|
data_form = NewInterventionForm()
|
||||||
|
geom_form = SimpleGeomForm(read_only=False)
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New intervention"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Renders a view for a new intervention creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
data_form = NewInterventionForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
intervention = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != intervention.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
intervention.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
messages.success(request, _("Intervention {} added").format(intervention.identifier))
|
||||||
|
if geom_form.has_geometry_simplified():
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRY_SIMPLIFIED
|
||||||
|
)
|
||||||
|
|
||||||
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
|
if num_ignored_geometries > 0:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=intervention.id)
|
||||||
|
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger", )
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New intervention"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
|
||||||
|
class InterventionIdentifierGeneratorView(AbstractIdentifierGeneratorView):
|
||||||
|
_MODEL = Intervention
|
||||||
|
|
||||||
|
|
||||||
|
class EditInterventionView(LoginRequiredMixin, View):
|
||||||
|
_TEMPLATE = "intervention/form/view.html"
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
"""
|
||||||
|
Renders a view for editing interventions
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The intervention identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HttpResponse: The rendered view
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get object from db
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
if intervention.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditInterventionForm(request.POST or None, instance=intervention)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
"""
|
||||||
|
Process saved form content
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The intervention id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HttpResponse:
|
||||||
|
"""
|
||||||
|
# Get object from db
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
if intervention.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditInterventionForm(request.POST or None, instance=intervention)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
# The data form takes the geom form for processing, as well as the performing user
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
# Save the current state of recorded|checked to inform the user in case of a status reset due to editing
|
# Save the current state of recorded|checked to inform the user in case of a status reset due to editing
|
||||||
@@ -158,28 +217,17 @@ def edit_view(request: HttpRequest, id: str):
|
|||||||
request,
|
request,
|
||||||
GEOMETRY_SIMPLIFIED
|
GEOMETRY_SIMPLIFIED
|
||||||
)
|
)
|
||||||
|
|
||||||
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||||
if num_ignored_geometries > 0:
|
if num_ignored_geometries > 0:
|
||||||
messages.info(
|
messages.info(
|
||||||
request,
|
request,
|
||||||
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect("intervention:detail", id=intervention.id)
|
return redirect("intervention:detail", id=intervention.id)
|
||||||
else:
|
context = {
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
"form": data_form,
|
||||||
else:
|
"geom_form": geom_form,
|
||||||
# For clarification: nothing in this case
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
|
||||||
pass
|
}
|
||||||
context = {
|
context = BaseContext(request, context).context
|
||||||
"form": data_form,
|
return render(request, self._TEMPLATE, context)
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
class RemoveInterventionView(LoginRequiredMixin, BaseRemoveModalFormView):
|
|
||||||
_MODEL_CLS = Intervention
|
|
||||||
_REDIRECT_URL = "intervention:index"
|
|
||||||
|
|||||||
@@ -5,11 +5,19 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
from konova.views.log import AbstractLogView
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
class InterventionLogView(LoginRequiredMixin, AbstractLogView):
|
class InterventionLogView(AbstractLogView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -5,12 +5,19 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import conservation_office_group_required, shared_access_required
|
||||||
from konova.views.record import AbstractRecordView
|
from konova.views.record import AbstractRecordView
|
||||||
|
|
||||||
|
|
||||||
class InterventionRecordView(LoginRequiredMixin, AbstractRecordView):
|
class InterventionRecordView(AbstractRecordView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
20
intervention/views/remove.py
Normal file
20
intervention/views/remove.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Created on: 14.12.25
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import shared_access_required
|
||||||
|
from konova.views.remove import AbstractRemoveView
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveInterventionView(AbstractRemoveView):
|
||||||
|
_MODEL = Intervention
|
||||||
|
_REDIRECT_URL = "intervention:index"
|
||||||
|
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
@@ -5,41 +5,78 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.sub_settings.django_settings import BASE_URL
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.qrcode import QrCode
|
from konova.utils.qrcode import QrCode
|
||||||
from konova.views.report import BaseReportView
|
from konova.views.report import AbstractPublicReportView
|
||||||
|
|
||||||
|
|
||||||
class InterventionReportView(BaseReportView):
|
class InterventionPublicReportView(AbstractPublicReportView):
|
||||||
_TEMPLATE = 'intervention/report/report.html'
|
_TEMPLATE = "intervention/report/report.html"
|
||||||
_MODEL = Intervention
|
|
||||||
|
|
||||||
def _get_report_context(self, obj: Intervention):
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
""" Returns the specific context needed for an intervention report
|
""" Renders the public report view
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
obj (Intervention): The object for the report
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: The object specific context for rendering the report
|
|
||||||
"""
|
|
||||||
distinct_deductions = obj.deductions.all().distinct("account")
|
|
||||||
report_url = BASE_URL + reverse("intervention:report", args=(obj.id,))
|
|
||||||
qrcode_report = QrCode(report_url, 10)
|
|
||||||
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
|
|
||||||
|
|
||||||
return {
|
"""
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
|
||||||
|
tab_title = _("Report {}").format(intervention.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not intervention.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=intervention
|
||||||
|
)
|
||||||
|
parcels = intervention.get_underlying_parcels()
|
||||||
|
|
||||||
|
distinct_deductions = intervention.deductions.all().distinct(
|
||||||
|
"account"
|
||||||
|
)
|
||||||
|
|
||||||
|
qrcode = QrCode(
|
||||||
|
content=request.build_absolute_uri(reverse("intervention:report", args=(id,))),
|
||||||
|
size=10
|
||||||
|
)
|
||||||
|
qrcode_lanis = QrCode(
|
||||||
|
content=intervention.get_LANIS_link(),
|
||||||
|
size=7
|
||||||
|
)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": intervention,
|
||||||
"deductions": distinct_deductions,
|
"deductions": distinct_deductions,
|
||||||
"qrcode": {
|
"qrcode": {
|
||||||
"img": qrcode_report.get_img(),
|
"img": qrcode.get_img(),
|
||||||
"url": qrcode_report.get_content(),
|
"url": qrcode.get_content(),
|
||||||
},
|
},
|
||||||
"qrcode_lanis": {
|
"qrcode_lanis": {
|
||||||
"img": qrcode_lanis.get_img(),
|
"img": qrcode_lanis.get_img(),
|
||||||
"url": qrcode_lanis.get_content(),
|
"url": qrcode_lanis.get_content(),
|
||||||
},
|
},
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"tables_scrollable": False,
|
"tables_scrollable": False,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
}
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, self._TEMPLATE, context)
|
||||||
@@ -5,12 +5,22 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from intervention.forms.modals.resubmission import InterventionResubmissionModalForm
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import default_group_required, shared_access_required, login_required_modal
|
||||||
from konova.views.resubmission import AbstractResubmissionView
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
class InterventionResubmissionView(AbstractResubmissionView):
|
class InterventionResubmissionView(AbstractResubmissionView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_FORM_CLS = InterventionResubmissionModalForm
|
redirect_url_base = "intervention:detail"
|
||||||
_REDIRECT_URL = "intervention:detail"
|
form_action_url_base = "intervention:resubmission-create"
|
||||||
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -7,52 +7,65 @@ Created on: 19.08.22
|
|||||||
"""
|
"""
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
from intervention.forms.modals.revocation import NewRevocationModalForm, EditRevocationModalForm, \
|
from intervention.forms.modals.revocation import NewRevocationModalForm, EditRevocationModalForm, \
|
||||||
RemoveRevocationModalForm
|
RemoveRevocationModalForm
|
||||||
from intervention.models import Intervention, RevocationDocument
|
from intervention.models import Intervention, RevocationDocument, Revocation
|
||||||
|
from konova.decorators import default_group_required, shared_access_required, login_required_modal
|
||||||
from konova.utils.documents import get_document
|
from konova.utils.documents import get_document
|
||||||
from konova.utils.message_templates import DATA_UNSHARED, REVOCATION_EDITED, REVOCATION_REMOVED, REVOCATION_ADDED
|
from konova.utils.message_templates import REVOCATION_ADDED, DATA_UNSHARED, REVOCATION_EDITED, REVOCATION_REMOVED
|
||||||
from konova.views.base import BaseModalFormView, BaseView
|
|
||||||
|
|
||||||
|
|
||||||
class BaseRevocationView(LoginRequiredMixin, BaseModalFormView):
|
class NewInterventionRevocationView(LoginRequiredMixin, View):
|
||||||
_MODEL_CLS = Intervention
|
def __process_request(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
_REDIRECT_URL = "intervention:detail"
|
""" Renders sharing form for an intervention
|
||||||
|
|
||||||
class Meta:
|
Args:
|
||||||
abstract = True
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): Intervention's id
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
Returns:
|
||||||
return user.is_default_user()
|
|
||||||
|
|
||||||
def _get_redirect_url(self, *args, **kwargs):
|
"""
|
||||||
url = super()._get_redirect_url(*args, **kwargs)
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
return f"{url}#related_data"
|
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention,
|
||||||
|
request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
msg_success=REVOCATION_ADDED,
|
||||||
|
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, *args, **kwargs)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class NewRevocationView(BaseRevocationView):
|
class GetInterventionRevocationView(LoginRequiredMixin, View):
|
||||||
_FORM_CLS = NewRevocationModalForm
|
@method_decorator(default_group_required)
|
||||||
_MSG_SUCCESS = REVOCATION_ADDED
|
def get(self, request: HttpRequest, doc_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" Returns the revocation document as downloadable file
|
||||||
|
|
||||||
|
Wraps the generic document fetcher function from konova.utils.
|
||||||
|
|
||||||
class EditRevocationView(BaseRevocationView):
|
Args:
|
||||||
_FORM_CLS = EditRevocationModalForm
|
request (HttpRequest): The incoming request
|
||||||
_MSG_SUCCESS = REVOCATION_EDITED
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
class RemoveRevocationView(BaseRevocationView):
|
"""
|
||||||
_FORM_CLS = RemoveRevocationModalForm
|
|
||||||
_MSG_SUCCESS = REVOCATION_REMOVED
|
|
||||||
|
|
||||||
|
|
||||||
class GetRevocationDocumentView(LoginRequiredMixin, BaseView):
|
|
||||||
_MODEL_CLS = RevocationDocument
|
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
|
||||||
def get(self, request: HttpRequest, doc_id: str):
|
|
||||||
doc = get_object_or_404(RevocationDocument, id=doc_id)
|
doc = get_object_or_404(RevocationDocument, id=doc_id)
|
||||||
# File download only possible if related instance is shared with user
|
# File download only possible if related instance is shared with user
|
||||||
if not doc.instance.legal.intervention.users.filter(id=request.user.id):
|
if not doc.instance.legal.intervention.users.filter(id=request.user.id):
|
||||||
@@ -63,14 +76,70 @@ class GetRevocationDocumentView(LoginRequiredMixin, BaseView):
|
|||||||
return redirect("intervention:detail", id=doc.instance.id)
|
return redirect("intervention:detail", id=doc.instance.id)
|
||||||
return get_document(doc)
|
return get_document(doc)
|
||||||
|
|
||||||
def _user_has_permission(self, user, **kwargs):
|
|
||||||
return user.is_default_user()
|
|
||||||
|
|
||||||
def _user_has_shared_access(self, user, **kwargs):
|
class EditInterventionRevocationView(LoginRequiredMixin, View):
|
||||||
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get("doc_id"))
|
def __process_request(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
assert obj is not None
|
""" Renders a edit view for a revocation
|
||||||
return obj.instance.intervention.is_shared_with(user)
|
|
||||||
|
|
||||||
def _get_redirect_url(self, *args, **kwargs):
|
Args:
|
||||||
url = super()._get_redirect_url(*args, **kwargs)
|
request (HttpRequest): The incoming request
|
||||||
return f"{url}#related_data"
|
id (str): The intervention's id as string
|
||||||
|
revocation_id (str): The revocation's id as string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
revocation = get_object_or_404(Revocation, id=revocation_id)
|
||||||
|
|
||||||
|
form = EditRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention,
|
||||||
|
revocation=revocation, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
REVOCATION_EDITED,
|
||||||
|
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, revocation_id, *args, **kwargs)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, revocation_id, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveInterventionRevocationView(LoginRequiredMixin, View):
|
||||||
|
def __process_request(self, request, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
""" Renders a remove view for a revocation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The intervention's id as string
|
||||||
|
revocation_id (str): The revocation's id as string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
revocation = get_object_or_404(Revocation, id=revocation_id)
|
||||||
|
|
||||||
|
form = RemoveRevocationModalForm(request.POST or None, instance=intervention, revocation=revocation,
|
||||||
|
request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
REVOCATION_REMOVED,
|
||||||
|
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def get(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, revocation_id, *args, **kwargs)
|
||||||
|
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def post(self, request, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
|
||||||
|
return self.__process_request(request, id, revocation_id, *args, **kwargs)
|
||||||
@@ -5,15 +5,29 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
|||||||
Created on: 19.08.22
|
Created on: 19.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import default_group_required, shared_access_required, login_required_modal
|
||||||
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
||||||
|
|
||||||
|
|
||||||
class InterventionShareByTokenView(AbstractShareByTokenView):
|
class InterventionShareByTokenView(AbstractShareByTokenView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_REDIRECT_URL = "intervention:detail"
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class InterventionShareFormView(AbstractShareFormView):
|
class InterventionShareFormView(AbstractShareFormView):
|
||||||
_MODEL_CLS = Intervention
|
model = Intervention
|
||||||
_REDIRECT_URL = "intervention:detail"
|
|
||||||
|
@method_decorator(login_required_modal)
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -10,18 +10,21 @@ from abc import abstractmethod
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.models import BaseObject
|
||||||
|
|
||||||
|
|
||||||
class BaseForm(forms.Form):
|
class BaseForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
Basic form for that holds attributes needed in all other forms
|
Basic form for that holds attributes needed in all other forms
|
||||||
"""
|
"""
|
||||||
|
template = None
|
||||||
action_url = None
|
action_url = None
|
||||||
action_btn_label = _("Save")
|
action_btn_label = _("Save")
|
||||||
form_title = None
|
form_title = None
|
||||||
cancel_redirect = None
|
cancel_redirect = None
|
||||||
form_caption = None
|
form_caption = None
|
||||||
instance = None # The data holding model object
|
instance = None # The data holding model object
|
||||||
user = None # The performing user
|
|
||||||
request = None
|
request = None
|
||||||
form_attrs = {} # Holds additional attributes, that can be used in the template
|
form_attrs = {} # Holds additional attributes, that can be used in the template
|
||||||
has_required_fields = False # Automatically set. Triggers hint rendering in templates
|
has_required_fields = False # Automatically set. Triggers hint rendering in templates
|
||||||
@@ -30,7 +33,6 @@ class BaseForm(forms.Form):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.instance = kwargs.pop("instance", None)
|
self.instance = kwargs.pop("instance", None)
|
||||||
self.user = kwargs.pop("user", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
if self.request is not None:
|
if self.request is not None:
|
||||||
self.user = self.request.user
|
self.user = self.request.user
|
||||||
@@ -40,10 +42,11 @@ class BaseForm(forms.Form):
|
|||||||
self.has_required_fields = True
|
self.has_required_fields = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
self.check_for_recorded_instance()
|
||||||
self.__check_valid_label_input_ratio()
|
self.__check_valid_label_input_ratio()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def save(self, *arg, **kwargs):
|
def save(self):
|
||||||
# To be implemented in subclasses!
|
# To be implemented in subclasses!
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -133,3 +136,34 @@ class BaseForm(forms.Form):
|
|||||||
set_class = self.fields[field].widget.attrs.get("class", "")
|
set_class = self.fields[field].widget.attrs.get("class", "")
|
||||||
set_class = set_class.replace(cls, "")
|
set_class = set_class.replace(cls, "")
|
||||||
self.fields[field].widget.attrs["class"] = set_class
|
self.fields[field].widget.attrs["class"] = set_class
|
||||||
|
|
||||||
|
def check_for_recorded_instance(self):
|
||||||
|
""" Checks if the instance is recorded and runs some special logic if yes
|
||||||
|
|
||||||
|
If the instance is recorded, the form shall not display any possibility to
|
||||||
|
edit any data. Instead, the users should get some information about why they can not edit anything.
|
||||||
|
|
||||||
|
There are situations where the form should be rendered regularly,
|
||||||
|
e.g deduction forms for (recorded) eco accounts.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
is_none = self.instance is None
|
||||||
|
is_other_data_type = not isinstance(self.instance, BaseObject)
|
||||||
|
|
||||||
|
if is_none or is_other_data_type:
|
||||||
|
# Do nothing
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.instance.is_recorded:
|
||||||
|
self.block_form()
|
||||||
|
|
||||||
|
def block_form(self):
|
||||||
|
"""
|
||||||
|
Overwrites template, providing no actions
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.template = "form/recorded_no_edit.html"
|
||||||
@@ -35,6 +35,7 @@ class SimpleGeomForm(BaseForm):
|
|||||||
disabled=False,
|
disabled=False,
|
||||||
)
|
)
|
||||||
_num_geometries_ignored: int = 0
|
_num_geometries_ignored: int = 0
|
||||||
|
empty = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.read_only = kwargs.pop("read_only", True)
|
self.read_only = kwargs.pop("read_only", True)
|
||||||
@@ -49,11 +50,11 @@ class SimpleGeomForm(BaseForm):
|
|||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
|
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
|
||||||
self._set_geojson_properties(geojson, title=self.instance.identifier or None)
|
geojson = self._set_geojson_properties(geojson, title=self.instance.identifier or None)
|
||||||
geom = json.dumps(geojson)
|
geom = json.dumps(geojson)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
||||||
geom = ""
|
geom = json.dumps({})
|
||||||
self.empty = True
|
self.empty = True
|
||||||
|
|
||||||
self.initialize_form_field("output", geom)
|
self.initialize_form_field("output", geom)
|
||||||
@@ -62,17 +63,17 @@ class SimpleGeomForm(BaseForm):
|
|||||||
super().is_valid()
|
super().is_valid()
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
|
||||||
# Get geojson from form
|
# Make sure invalid geometry is properly rendered again to the user
|
||||||
geom = self.data.get("output", None)
|
# Therefore: write submitted data back into form field
|
||||||
if geom is None or len(geom) == 0:
|
# (does not matter whether we know if it is valid or invalid)
|
||||||
# empty geometry is a valid geometry
|
submitted_data = self.data["output"]
|
||||||
self.cleaned_data["output"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
|
submitted_data = json.loads(submitted_data)
|
||||||
return is_valid
|
submitted_data = self._set_geojson_properties(submitted_data)
|
||||||
geom = json.loads(geom)
|
self.initialize_form_field("output", json.dumps(submitted_data))
|
||||||
|
|
||||||
# Write submitted data back into form field to make sure invalid geometry
|
# Get geojson from form for validity checking
|
||||||
# will be rendered again on failed submit
|
geom = self.data.get("output", json.dumps({}))
|
||||||
self.initialize_form_field("output", self.data["output"])
|
geom = json.loads(geom)
|
||||||
|
|
||||||
# Initialize features list with empty MultiPolygon, so that an empty input will result in a
|
# Initialize features list with empty MultiPolygon, so that an empty input will result in a
|
||||||
# proper empty MultiPolygon object
|
# proper empty MultiPolygon object
|
||||||
@@ -84,20 +85,23 @@ class SimpleGeomForm(BaseForm):
|
|||||||
"MultiPolygon",
|
"MultiPolygon",
|
||||||
"MultiPolygon25D",
|
"MultiPolygon25D",
|
||||||
]
|
]
|
||||||
|
# Check validity for each feature of the geometry
|
||||||
for feature in features_json:
|
for feature in features_json:
|
||||||
feature_geom = feature.get("geometry", feature)
|
feature_geom = feature.get("geometry", feature)
|
||||||
if feature_geom is None:
|
if feature_geom is None:
|
||||||
# Fallback for rare cases where a feature does not contain any geometry
|
# Fallback for rare cases where a feature does not contain any geometry
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Try to create a geometry object from the single feature
|
||||||
feature_geom = json.dumps(feature_geom)
|
feature_geom = json.dumps(feature_geom)
|
||||||
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
|
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
|
||||||
|
|
||||||
flatten_geometry = g.coord_dim > 2
|
geometry_has_unwanted_dimensions = g.coord_dim > 2
|
||||||
if flatten_geometry:
|
if geometry_has_unwanted_dimensions:
|
||||||
g = self.__flatten_geom_to_2D(g)
|
g = self.__flatten_geom_to_2D(g)
|
||||||
|
|
||||||
if g.geom_type not in accepted_ogr_types:
|
geometry_type_is_accepted = g.geom_type not in accepted_ogr_types
|
||||||
|
if geometry_type_is_accepted:
|
||||||
self.add_error("output", _("Only surfaces allowed. Points or lines must be buffered."))
|
self.add_error("output", _("Only surfaces allowed. Points or lines must be buffered."))
|
||||||
is_valid &= False
|
is_valid &= False
|
||||||
return is_valid
|
return is_valid
|
||||||
@@ -109,27 +113,33 @@ class SimpleGeomForm(BaseForm):
|
|||||||
self._num_geometries_ignored += 1
|
self._num_geometries_ignored += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Whatever this geometry object is -> try to create a Polygon from it
|
||||||
|
# The resulting polygon object automatically detects whether a valid polygon has been created or not
|
||||||
g = Polygon.from_ewkt(g.ewkt)
|
g = Polygon.from_ewkt(g.ewkt)
|
||||||
is_valid &= g.valid
|
is_valid &= g.valid
|
||||||
if not g.valid:
|
if not g.valid:
|
||||||
self.add_error("output", g.valid_reason)
|
self.add_error("output", g.valid_reason)
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
|
# If the resulting polygon is just a single polygon, we add it to the list of properly casted features
|
||||||
if isinstance(g, Polygon):
|
if isinstance(g, Polygon):
|
||||||
features.append(g)
|
features.append(g)
|
||||||
elif isinstance(g, MultiPolygon):
|
elif isinstance(g, MultiPolygon):
|
||||||
|
# The resulting polygon could be of type MultiPolygon (due to multiple surfaces)
|
||||||
|
# If so, we extract all polygons from the MultiPolygon and extend the casted features list
|
||||||
features.extend(list(g))
|
features.extend(list(g))
|
||||||
|
|
||||||
# Unionize all geometry features into one new MultiPolygon
|
# Unionize all polygon features into one new MultiPolygon
|
||||||
if features:
|
if features:
|
||||||
form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union
|
form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union
|
||||||
else:
|
else:
|
||||||
|
# If no features have been processed, this indicates an empty geometry - so we store an empty geometry
|
||||||
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
||||||
|
|
||||||
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
|
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
|
||||||
form_geom = Geometry.cast_to_multipolygon(form_geom)
|
form_geom = Geometry.cast_to_multipolygon(form_geom)
|
||||||
|
|
||||||
# Write unioned Multipolygon into cleaned data
|
# Write unionized Multipolygon back into cleaned data
|
||||||
if self.cleaned_data is None:
|
if self.cleaned_data is None:
|
||||||
self.cleaned_data = {}
|
self.cleaned_data = {}
|
||||||
self.cleaned_data["output"] = form_geom.ewkt
|
self.cleaned_data["output"] = form_geom.ewkt
|
||||||
@@ -252,6 +262,8 @@ class SimpleGeomForm(BaseForm):
|
|||||||
"""
|
"""
|
||||||
features = geojson.get("features", [])
|
features = geojson.get("features", [])
|
||||||
for feature in features:
|
for feature in features:
|
||||||
|
if not feature.get("properties", None):
|
||||||
|
feature["properties"] = {}
|
||||||
feature["properties"]["editable"] = not self.read_only
|
feature["properties"]["editable"] = not self.read_only
|
||||||
if title:
|
if title:
|
||||||
feature["properties"]["title"] = title
|
feature["properties"]["title"] = title
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class BaseModalForm(BaseForm, BSModalForm):
|
|||||||
"""
|
"""
|
||||||
is_modal_form = True
|
is_modal_form = True
|
||||||
render_submit = True
|
render_submit = True
|
||||||
_TEMPLATE = "modal/modal_form.html"
|
template = "modal/modal_form.html"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -43,7 +43,7 @@ class BaseModalForm(BaseForm, BSModalForm):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home")
|
redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home")
|
||||||
template = self._TEMPLATE
|
template = self.template
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if self.is_valid():
|
if self.is_valid():
|
||||||
if not is_ajax(request.META):
|
if not is_ajax(request.META):
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ Created on: 15.08.22
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.fields.files import FieldFile
|
from django.db.models.fields.files import FieldFile
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from konova.forms.modals.base_form import BaseModalForm
|
from konova.forms.modals.base_form import BaseModalForm
|
||||||
|
from konova.models import AbstractDocument
|
||||||
from konova.utils import validators
|
from konova.utils import validators
|
||||||
from konova.utils.message_templates import DOCUMENT_EDITED, FILE_SIZE_TOO_LARGE, FILE_TYPE_UNSUPPORTED
|
from konova.utils.message_templates import DOCUMENT_EDITED, FILE_SIZE_TOO_LARGE, FILE_TYPE_UNSUPPORTED
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
@@ -69,7 +69,7 @@ class NewDocumentModalForm(BaseModalForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_DOCUMENT_CLS = None
|
document_model = None
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
@@ -81,7 +81,7 @@ class NewDocumentModalForm(BaseModalForm):
|
|||||||
self.form_attrs = {
|
self.form_attrs = {
|
||||||
"enctype": "multipart/form-data", # important for file upload
|
"enctype": "multipart/form-data", # important for file upload
|
||||||
}
|
}
|
||||||
if not self._DOCUMENT_CLS:
|
if not self.document_model:
|
||||||
raise NotImplementedError("Unsupported document type for {}".format(self.instance.__class__))
|
raise NotImplementedError("Unsupported document type for {}".format(self.instance.__class__))
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
@@ -93,14 +93,14 @@ class NewDocumentModalForm(BaseModalForm):
|
|||||||
# FieldFile declares that no new file has been uploaded and we do not need to check on the file again
|
# FieldFile declares that no new file has been uploaded and we do not need to check on the file again
|
||||||
return super_valid
|
return super_valid
|
||||||
|
|
||||||
mime_type_valid = self._DOCUMENT_CLS.is_mime_type_valid(_file)
|
mime_type_valid = self.document_model.is_mime_type_valid(_file)
|
||||||
if not mime_type_valid:
|
if not mime_type_valid:
|
||||||
self.add_error(
|
self.add_error(
|
||||||
"file",
|
"file",
|
||||||
FILE_TYPE_UNSUPPORTED
|
FILE_TYPE_UNSUPPORTED
|
||||||
)
|
)
|
||||||
|
|
||||||
file_size_valid = self._DOCUMENT_CLS.is_file_size_valid(_file)
|
file_size_valid = self.document_model.is_file_size_valid(_file)
|
||||||
if not file_size_valid:
|
if not file_size_valid:
|
||||||
self.add_error(
|
self.add_error(
|
||||||
"file",
|
"file",
|
||||||
@@ -115,7 +115,7 @@ class NewDocumentModalForm(BaseModalForm):
|
|||||||
action = UserActionLogEntry.get_created_action(self.user)
|
action = UserActionLogEntry.get_created_action(self.user)
|
||||||
edited_action = UserActionLogEntry.get_edited_action(self.user, _("Added document"))
|
edited_action = UserActionLogEntry.get_edited_action(self.user, _("Added document"))
|
||||||
|
|
||||||
doc = self._DOCUMENT_CLS.objects.create(
|
doc = self.document_model.objects.create(
|
||||||
created=action,
|
created=action,
|
||||||
title=self.cleaned_data["title"],
|
title=self.cleaned_data["title"],
|
||||||
comment=self.cleaned_data["comment"],
|
comment=self.cleaned_data["comment"],
|
||||||
@@ -133,12 +133,10 @@ class NewDocumentModalForm(BaseModalForm):
|
|||||||
|
|
||||||
class EditDocumentModalForm(NewDocumentModalForm):
|
class EditDocumentModalForm(NewDocumentModalForm):
|
||||||
document = None
|
document = None
|
||||||
_DOCUMENT_CLS = None
|
document_model = AbstractDocument
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
doc_id = kwargs.pop("doc_id", None)
|
self.document = kwargs.pop("document", None)
|
||||||
self.document = get_object_or_404(self._DOCUMENT_CLS, id=doc_id)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit document")
|
self.form_title = _("Edit document")
|
||||||
form_data = {
|
form_data = {
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ Created on: 15.08.22
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from konova.forms.modals.base_form import BaseModalForm
|
from konova.forms.modals.base_form import BaseModalForm
|
||||||
from konova.models import BaseObject, Deadline
|
from konova.models import BaseObject
|
||||||
|
|
||||||
|
|
||||||
class RemoveModalForm(BaseModalForm):
|
class RemoveModalForm(BaseModalForm):
|
||||||
@@ -52,19 +51,9 @@ class RemoveDeadlineModalForm(RemoveModalForm):
|
|||||||
deadline = None
|
deadline = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
deadline_id = kwargs.pop("deadline_id", None)
|
deadline = kwargs.pop("deadline", None)
|
||||||
self.deadline = get_object_or_404(Deadline, id=deadline_id)
|
self.deadline = deadline
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
self.instance.remove_deadline(self)
|
self.instance.remove_deadline(self)
|
||||||
|
|
||||||
|
|
||||||
class RemoveDocumentModalForm(RemoveModalForm):
|
|
||||||
instance = None
|
|
||||||
_DOCUMENT_CLS = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
document_id = kwargs.pop("doc_id", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.instance = get_object_or_404(self._DOCUMENT_CLS, id=document_id)
|
|
||||||
@@ -10,6 +10,7 @@ import json
|
|||||||
from django.contrib.gis.db.models import MultiPolygonField
|
from django.contrib.gis.db.models import MultiPolygonField
|
||||||
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib.gis.geos import MultiPolygon
|
from django.contrib.gis.geos import MultiPolygon
|
||||||
|
|
||||||
@@ -102,24 +103,41 @@ class Geometry(BaseResource):
|
|||||||
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
||||||
resolved_conflicts.delete()
|
resolved_conflicts.delete()
|
||||||
|
|
||||||
def get_data_objects(self):
|
def get_data_objects(self, limit_to_attrs: list = None):
|
||||||
""" Getter for all objects which are related to this geometry
|
""" Getter for all objects which are related to this geometry
|
||||||
|
|
||||||
|
Using the limit_to_attrs we can limit the amount of returned data directly onto the data object attributes
|
||||||
|
we want to have. Reduces memory consumption and runtime.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
objs (list): The list of objects
|
objs (list): The list of objects
|
||||||
"""
|
"""
|
||||||
objs = []
|
objs = []
|
||||||
sets = [
|
|
||||||
|
# Some related data sets can be processed rather easily
|
||||||
|
regular_sets = [
|
||||||
self.intervention_set,
|
self.intervention_set,
|
||||||
self.compensation_set,
|
|
||||||
self.ema_set,
|
self.ema_set,
|
||||||
self.ecoaccount_set,
|
self.ecoaccount_set,
|
||||||
]
|
]
|
||||||
for _set in sets:
|
for _set in regular_sets:
|
||||||
set_objs = _set.filter(
|
set_objs = _set.filter(
|
||||||
deleted=None
|
deleted=None
|
||||||
)
|
)
|
||||||
objs += set_objs
|
if limit_to_attrs:
|
||||||
|
objs += set_objs.values_list(*limit_to_attrs, flat=True)
|
||||||
|
else:
|
||||||
|
objs += set_objs
|
||||||
|
|
||||||
|
# ... but we need a special treatment for compensations, since they can be deleted directly OR inherit their
|
||||||
|
# de-facto-deleted status from their deleted parent intervention
|
||||||
|
comp_objs = self.compensation_set.filter(
|
||||||
|
Q(deleted=None) & Q(intervention__deleted=None)
|
||||||
|
)
|
||||||
|
if limit_to_attrs:
|
||||||
|
objs += comp_objs.values_list(*limit_to_attrs, flat=True)
|
||||||
|
else:
|
||||||
|
objs += comp_objs
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
def get_data_object(self):
|
def get_data_object(self):
|
||||||
@@ -397,7 +415,10 @@ class Geometry(BaseResource):
|
|||||||
"""
|
"""
|
||||||
output_geom = input_geom
|
output_geom = input_geom
|
||||||
if not isinstance(input_geom, MultiPolygon):
|
if not isinstance(input_geom, MultiPolygon):
|
||||||
output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP)
|
try:
|
||||||
|
output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP)
|
||||||
|
except TypeError as e:
|
||||||
|
raise AssertionError(f"Only (Multi)Polygon allowed! Could not convert {input_geom.geom_type} to MultiPolygon")
|
||||||
return output_geom
|
return output_geom
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -677,19 +677,23 @@ class GeoReferencedMixin(models.Model):
|
|||||||
return request
|
return request
|
||||||
|
|
||||||
instance_objs = []
|
instance_objs = []
|
||||||
conflicts = self.geometry.conflicts_geometries.all()
|
needed_data_object_attrs = [
|
||||||
|
"identifier"
|
||||||
|
]
|
||||||
|
conflicts = self.geometry.conflicts_geometries.iterator()
|
||||||
|
|
||||||
for conflict in conflicts:
|
for conflict in conflicts:
|
||||||
instance_objs += conflict.affected_geometry.get_data_objects()
|
# Only check the affected geometry of this conflict, since we know the conflicting geometry is self.geometry
|
||||||
|
instance_objs += conflict.affected_geometry.get_data_objects(needed_data_object_attrs)
|
||||||
|
|
||||||
conflicts = self.geometry.conflicted_by_geometries.all()
|
conflicts = self.geometry.conflicted_by_geometries.iterator()
|
||||||
for conflict in conflicts:
|
for conflict in conflicts:
|
||||||
instance_objs += conflict.conflicting_geometry.get_data_objects()
|
# Only check the conflicting geometry of this conflict, since we know the affected geometry is self.geometry
|
||||||
|
instance_objs += conflict.conflicting_geometry.get_data_objects(needed_data_object_attrs)
|
||||||
|
|
||||||
add_message = len(instance_objs) > 0
|
add_message = len(instance_objs) > 0
|
||||||
if add_message:
|
if add_message:
|
||||||
instance_identifiers = [x.identifier for x in instance_objs]
|
instance_identifiers = ", ".join(instance_objs)
|
||||||
instance_identifiers = ", ".join(instance_identifiers)
|
|
||||||
message_str = GEOMETRY_CONFLICT_WITH_TEMPLATE.format(instance_identifiers)
|
message_str = GEOMETRY_CONFLICT_WITH_TEMPLATE.format(instance_identifiers)
|
||||||
messages.info(request, message_str)
|
messages.info(request, message_str)
|
||||||
return request
|
return request
|
||||||
|
|||||||
@@ -291,5 +291,5 @@ Overwrites netgis.css attributes
|
|||||||
}
|
}
|
||||||
|
|
||||||
.netgis-menu{
|
.netgis-menu{
|
||||||
z-index: 100 !important;
|
z-index: 1 !important;
|
||||||
}
|
}
|
||||||
@@ -11,4 +11,4 @@ BASE_TITLE = "KSP - Kompensationsverzeichnis Service Portal"
|
|||||||
BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
|
BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
|
||||||
TAB_TITLE_IDENTIFIER = "tab_title"
|
TAB_TITLE_IDENTIFIER = "tab_title"
|
||||||
HELP_LINK = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp2:start"
|
HELP_LINK = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp2:start"
|
||||||
IMPRESSUM_LINK = "https://naturschutz.rlp.de/index.php?q=impressum"
|
IMPRESSUM_LINK = "https://naturschutz.rlp.de/ueber-uns/impressum"
|
||||||
|
|||||||
@@ -191,11 +191,10 @@ STATICFILES_DIRS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# EMAIL (see https://docs.djangoproject.com/en/dev/topics/email/)
|
# EMAIL (see https://docs.djangoproject.com/en/dev/topics/email/)
|
||||||
|
|
||||||
# CHANGE_ME !!! ONLY FOR DEVELOPMENT !!!
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
# ONLY FOR DEVELOPMENT NEEDED
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
|
||||||
EMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location
|
EMAIL_FILE_PATH = '/tmp/app-messages'
|
||||||
|
|
||||||
DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL") # The default email address for the 'from' element
|
DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL") # The default email address for the 'from' element
|
||||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL # The default email sender address, which is used by Django to send errors via mail
|
SERVER_EMAIL = DEFAULT_FROM_EMAIL # The default email sender address, which is used by Django to send errors via mail
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ class BaseViewTestCase(BaseTestCase):
|
|||||||
for url, redirect_to in urls.items():
|
for url, redirect_to in urls.items():
|
||||||
response = client.get(url, follow=True)
|
response = client.get(url, follow=True)
|
||||||
# Expect redirects to the landing page
|
# Expect redirects to the landing page
|
||||||
self.assertEqual(response.redirect_chain[0], (redirect_to, 302), msg=f"Failed for {url}. Expected {redirect_to}")
|
self.assertEqual(response.redirect_chain[0], (redirect_to, 302), msg=f"Failed for {url}")
|
||||||
|
|
||||||
def assert_url_fail(self, client: Client, urls: list):
|
def assert_url_fail(self, client: Client, urls: list):
|
||||||
""" Assert for all given urls a direct 302 response
|
""" Assert for all given urls a direct 302 response
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class EditDeadlineModalFormTestCase(NewDeadlineModalFormTestCase):
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
deadline_id=self.finished_deadline.id,
|
deadline=self.finished_deadline,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ from django.utils.timezone import now
|
|||||||
from compensation.forms.modals.document import NewEcoAccountDocumentModalForm, NewCompensationDocumentModalForm
|
from compensation.forms.modals.document import NewEcoAccountDocumentModalForm, NewCompensationDocumentModalForm
|
||||||
from compensation.models import Payment
|
from compensation.models import Payment
|
||||||
from ema.forms import NewEmaDocumentModalForm
|
from ema.forms import NewEmaDocumentModalForm
|
||||||
from intervention.forms.modals.document import NewInterventionDocumentModalForm, EditInterventionDocumentModalForm
|
from intervention.forms.modals.document import NewInterventionDocumentModalForm
|
||||||
from intervention.models import InterventionDocument
|
from intervention.models import InterventionDocument
|
||||||
from konova.forms.modals import NewDocumentModalForm, RecordModalForm, RemoveModalForm, \
|
from konova.forms.modals import EditDocumentModalForm, NewDocumentModalForm, RecordModalForm, RemoveModalForm, \
|
||||||
RemoveDeadlineModalForm, ResubmissionModalForm
|
RemoveDeadlineModalForm, ResubmissionModalForm
|
||||||
from konova.models import Resubmission
|
from konova.models import Resubmission
|
||||||
from konova.tests.test_views import BaseTestCase
|
from konova.tests.test_views import BaseTestCase
|
||||||
@@ -106,12 +106,12 @@ class EditDocumentModalFormTestCase(NewDocumentModalFormTestCase):
|
|||||||
InterventionDocument,
|
InterventionDocument,
|
||||||
instance=self.intervention
|
instance=self.intervention
|
||||||
)
|
)
|
||||||
self.form = EditInterventionDocumentModalForm(
|
self.form = EditDocumentModalForm(
|
||||||
self.data,
|
self.data,
|
||||||
dummy_file_dict,
|
dummy_file_dict,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.intervention,
|
instance=self.intervention,
|
||||||
doc_id=self.doc.id
|
document=self.doc
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
@@ -122,6 +122,7 @@ class EditDocumentModalFormTestCase(NewDocumentModalFormTestCase):
|
|||||||
self.assertEqual(self.form.fields["title"].initial, self.doc.title)
|
self.assertEqual(self.form.fields["title"].initial, self.doc.title)
|
||||||
self.assertEqual(self.form.fields["comment"].initial, self.doc.comment)
|
self.assertEqual(self.form.fields["comment"].initial, self.doc.comment)
|
||||||
self.assertEqual(self.form.fields["creation_date"].initial, self.doc.date_of_creation)
|
self.assertEqual(self.form.fields["creation_date"].initial, self.doc.date_of_creation)
|
||||||
|
self.assertEqual(self.form.fields["file"].initial, self.doc.file)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
self.assertTrue(self.form.is_valid(), msg=self.form.errors)
|
self.assertTrue(self.form.is_valid(), msg=self.form.errors)
|
||||||
@@ -255,7 +256,7 @@ class RemoveDeadlineTestCase(BaseTestCase):
|
|||||||
form = RemoveDeadlineModalForm(
|
form = RemoveDeadlineModalForm(
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
deadline_id=self.finished_deadline.id
|
deadline=self.finished_deadline
|
||||||
)
|
)
|
||||||
self.assertEqual(form.form_title, str(_("Remove")))
|
self.assertEqual(form.form_title, str(_("Remove")))
|
||||||
self.assertEqual(form.form_caption, str(_("Are you sure?")))
|
self.assertEqual(form.form_caption, str(_("Are you sure?")))
|
||||||
@@ -272,7 +273,7 @@ class RemoveDeadlineTestCase(BaseTestCase):
|
|||||||
data,
|
data,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
instance=self.compensation,
|
instance=self.compensation,
|
||||||
deadline_id=self.finished_deadline.id
|
deadline=self.finished_deadline
|
||||||
)
|
)
|
||||||
self.assertTrue(form.is_valid(), msg=form.errors)
|
self.assertTrue(form.is_valid(), msg=form.errors)
|
||||||
form.save()
|
form.save()
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ Created on: 01.09.21
|
|||||||
"""
|
"""
|
||||||
from django.http import FileResponse, HttpRequest, Http404
|
from django.http import FileResponse, HttpRequest, Http404
|
||||||
|
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
from konova.models import AbstractDocument
|
from konova.models import AbstractDocument
|
||||||
|
from konova.utils.message_templates import DOCUMENT_REMOVED_TEMPLATE
|
||||||
|
|
||||||
|
|
||||||
def get_document(doc: AbstractDocument):
|
def get_document(doc: AbstractDocument):
|
||||||
@@ -24,3 +26,28 @@ def get_document(doc: AbstractDocument):
|
|||||||
return FileResponse(doc.file, as_attachment=True)
|
return FileResponse(doc.file, as_attachment=True)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
|
|
||||||
|
def remove_document(request: HttpRequest, doc: AbstractDocument):
|
||||||
|
""" Renders a form for uploading new documents
|
||||||
|
|
||||||
|
This function works using a modal. We are not using the regular way, the django bootstrap modal forms are
|
||||||
|
intended to be used. Instead of View classes we work using the classic way of dealing with forms (see below).
|
||||||
|
It is important to mention, that modal forms, which should reload the page afterwards, must provide a
|
||||||
|
'reload_page' bool in the context. This way, the modal may reload the page or not.
|
||||||
|
|
||||||
|
For further details see the comments in templates/modal or
|
||||||
|
https://github.com/trco/django-bootstrap-modal-forms
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
title = doc.title
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=doc, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=DOCUMENT_REMOVED_TEMPLATE.format(title),
|
||||||
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user