#18 File upload in certain folders
* refactors documents and file upload to be distributed into different subfolders, depending on the type of document (InterventionDocument, RevocationDocument, ...) * refactors Document model into AbstractDocument * subclasses RevocationDocument, InterventionDocument, COmpensationDocument, EmaDocument and EcoAccountDocument from AbstractDocument to provide proper functionality for each * adds new specialized routes for each new document type (opening, removing) * drops generic get and remove routes for documents
This commit is contained in:
parent
17d574e2a5
commit
f64a11cb50
@ -21,8 +21,9 @@ urlpatterns = [
|
|||||||
path('<id>/deadline/new', deadline_new_view, name="acc-new-deadline"),
|
path('<id>/deadline/new', deadline_new_view, name="acc-new-deadline"),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
# Document remove route can be found in konova/urls.py
|
|
||||||
path('<id>/document/new/', new_document_view, name='acc-new-doc'),
|
path('<id>/document/new/', new_document_view, name='acc-new-doc'),
|
||||||
|
path('document/<doc_id>', get_document_view, name='acc-get-doc'),
|
||||||
|
path('document/<doc_id>/remove/', remove_document_view, name='acc-remove-doc'),
|
||||||
|
|
||||||
# Eco-account deductions
|
# Eco-account deductions
|
||||||
path('<id>/remove/<deduction_id>', deduction_remove_view, name='deduction-remove'),
|
path('<id>/remove/<deduction_id>', deduction_remove_view, name='deduction-remove'),
|
||||||
|
@ -21,8 +21,9 @@ urlpatterns = [
|
|||||||
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
|
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
# Document remove route can be found in konova/urls.py
|
|
||||||
path('<id>/document/new/', new_document_view, name='new-doc'),
|
path('<id>/document/new/', new_document_view, name='new-doc'),
|
||||||
|
path('document/<doc_id>', get_document_view, name='get-doc'),
|
||||||
|
path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
||||||
|
|
||||||
# Generic state routes
|
# Generic state routes
|
||||||
path('state/<id>/remove', state_remove_view, name='state-remove'),
|
path('state/<id>/remove', state_remove_view, name='state-remove'),
|
||||||
|
@ -14,7 +14,8 @@ 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_BIOTOPES_ID
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID
|
||||||
from intervention.models import Intervention, ResponsibilityData
|
from intervention.models import Intervention, ResponsibilityData
|
||||||
from konova.models import BaseObject, BaseResource, Geometry, UuidModel
|
from konova.models import BaseObject, BaseResource, Geometry, UuidModel, AbstractDocument, \
|
||||||
|
generate_document_file_upload_path
|
||||||
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
@ -137,7 +138,6 @@ class AbstractCompensation(BaseObject):
|
|||||||
deadlines = models.ManyToManyField("konova.Deadline", blank=True, related_name="+")
|
deadlines = models.ManyToManyField("konova.Deadline", blank=True, related_name="+")
|
||||||
|
|
||||||
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
|
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
documents = models.ManyToManyField("konova.Document", blank=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
@ -198,6 +198,20 @@ class Compensation(AbstractCompensation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationDocument(AbstractDocument):
|
||||||
|
"""
|
||||||
|
Specializes document upload for revocations with certain path
|
||||||
|
"""
|
||||||
|
instance = models.ForeignKey(
|
||||||
|
Compensation,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="documents",
|
||||||
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
upload_to=generate_document_file_upload_path
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EcoAccount(AbstractCompensation):
|
class EcoAccount(AbstractCompensation):
|
||||||
"""
|
"""
|
||||||
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
|
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
|
||||||
@ -299,6 +313,20 @@ class EcoAccount(AbstractCompensation):
|
|||||||
return ret_msgs
|
return ret_msgs
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountDocument(AbstractDocument):
|
||||||
|
"""
|
||||||
|
Specializes document upload for revocations with certain path
|
||||||
|
"""
|
||||||
|
instance = models.ForeignKey(
|
||||||
|
EcoAccount,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="documents",
|
||||||
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
upload_to=generate_document_file_upload_path
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountDeduction(BaseResource):
|
class EcoAccountDeduction(BaseResource):
|
||||||
"""
|
"""
|
||||||
A deduction object for eco accounts
|
A deduction object for eco accounts
|
||||||
|
@ -39,14 +39,14 @@
|
|||||||
{% for doc in obj.documents.all %}
|
{% for doc in obj.documents.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<a href="{% url 'doc-open' doc.id %}">
|
<a href="{% url 'compensation:get-doc' doc.id %}">
|
||||||
{{ doc.title }}
|
{{ doc.title }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">{{ doc.comment }}</td>
|
<td class="align-middle">{{ doc.comment }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if is_default_member and has_access %}
|
{% if is_default_member and has_access %}
|
||||||
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
<button data-form-url="{% url 'compensation:remove-doc' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
||||||
{% fa5_icon 'trash' %}
|
{% fa5_icon 'trash' %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -39,14 +39,14 @@
|
|||||||
{% for doc in obj.documents.all %}
|
{% for doc in obj.documents.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<a href="{% url 'doc-open' doc.id %}">
|
<a href="{% url 'compensation:acc-get-doc' doc.id %}">
|
||||||
{{ doc.title }}
|
{{ doc.title }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">{{ doc.comment }}</td>
|
<td class="align-middle">{{ doc.comment }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if is_default_member and has_access %}
|
{% if is_default_member and has_access %}
|
||||||
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
<button data-form-url="{% url 'compensation:acc-remove-doc' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
||||||
{% fa5_icon 'trash' %}
|
{% fa5_icon 'trash' %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -5,11 +5,12 @@ from django.shortcuts import render, get_object_or_404
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.forms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm
|
from compensation.forms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm
|
||||||
from compensation.models import Compensation, CompensationState, CompensationAction
|
from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument
|
||||||
from compensation.tables import CompensationTable
|
from compensation.tables import CompensationTable
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import *
|
from konova.decorators import *
|
||||||
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm
|
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm
|
||||||
|
from konova.utils.documents import get_document, remove_document
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@ -163,6 +164,43 @@ def new_document_view(request: HttpRequest, id: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Returns the document as downloadable file
|
||||||
|
|
||||||
|
Wraps the generic document fetcher function from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(CompensationDocument, id=doc_id)
|
||||||
|
return get_document(doc)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def remove_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Removes the document from the database and file system
|
||||||
|
|
||||||
|
Wraps the generic functionality from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(CompensationDocument, id=doc_id)
|
||||||
|
return remove_document(
|
||||||
|
request,
|
||||||
|
doc
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def state_new_view(request: HttpRequest, id: str):
|
def state_new_view(request: HttpRequest, id: str):
|
||||||
""" Renders a form for adding new states for a compensation
|
""" Renders a form for adding new states for a compensation
|
||||||
|
@ -14,13 +14,14 @@ from django.http import HttpRequest, Http404
|
|||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
||||||
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
|
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount, EcoAccountDocument
|
||||||
from compensation.tables import EcoAccountTable
|
from compensation.tables import EcoAccountTable
|
||||||
from intervention.forms import NewDeductionForm
|
from intervention.forms import NewDeductionForm
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required
|
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required
|
||||||
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
|
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
|
||||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.utils.documents import get_document, remove_document
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@ -289,6 +290,43 @@ def new_document_view(request: HttpRequest, id: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Returns the document as downloadable file
|
||||||
|
|
||||||
|
Wraps the generic document fetcher function from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(EcoAccountDocument, id=doc_id)
|
||||||
|
return get_document(doc)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def remove_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Removes the document from the database and file system
|
||||||
|
|
||||||
|
Wraps the generic functionality from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(EcoAccountDocument, id=doc_id)
|
||||||
|
return remove_document(
|
||||||
|
request,
|
||||||
|
doc
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@default_group_required
|
@default_group_required
|
||||||
def new_deduction_view(request: HttpRequest, id: str):
|
def new_deduction_view(request: HttpRequest, id: str):
|
||||||
|
@ -2,7 +2,8 @@ from django.contrib.auth.models import User
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from compensation.models import AbstractCompensation
|
from compensation.models import AbstractCompensation
|
||||||
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
from konova.models import AbstractDocument, generate_document_file_upload_path
|
||||||
|
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE, EMA_DOC_PATH
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@ -84,3 +85,17 @@ class Ema(AbstractCompensation):
|
|||||||
# ToDo: Add check methods!
|
# ToDo: Add check methods!
|
||||||
|
|
||||||
return ret_msgs
|
return ret_msgs
|
||||||
|
|
||||||
|
|
||||||
|
class EmaDocument(AbstractDocument):
|
||||||
|
"""
|
||||||
|
Specializes document upload for ema with certain path
|
||||||
|
"""
|
||||||
|
instance = models.ForeignKey(
|
||||||
|
Ema,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="documents",
|
||||||
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
upload_to=generate_document_file_upload_path
|
||||||
|
)
|
@ -39,14 +39,14 @@
|
|||||||
{% for doc in obj.documents.all %}
|
{% for doc in obj.documents.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<a href="{% url 'doc-open' doc.id %}">
|
<a href="{% url 'ema:get-doc' doc.id %}">
|
||||||
{{ doc.title }}
|
{{ doc.title }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">{{ doc.comment }}</td>
|
<td class="align-middle">{{ doc.comment }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if is_default_member and has_access %}
|
{% if is_default_member and has_access %}
|
||||||
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
<button data-form-url="{% url 'ema:remove-doc' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
||||||
{% fa5_icon 'trash' %}
|
{% fa5_icon 'trash' %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -24,6 +24,8 @@ urlpatterns = [
|
|||||||
# Documents
|
# Documents
|
||||||
# Document remove route can be found in konova/urls.py
|
# Document remove route can be found in konova/urls.py
|
||||||
path('<id>/document/new/', document_new_view, name='new-doc'),
|
path('<id>/document/new/', document_new_view, name='new-doc'),
|
||||||
|
path('document/<doc_id>', get_document_view, name='get-doc'),
|
||||||
|
path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
||||||
|
|
||||||
# Generic state routes
|
# Generic state routes
|
||||||
path('state/<id>/remove', state_remove_view, name='state-remove'),
|
path('state/<id>/remove', state_remove_view, name='state-remove'),
|
||||||
|
40
ema/views.py
40
ema/views.py
@ -10,9 +10,10 @@ from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlin
|
|||||||
from ema.tables import EmaTable
|
from ema.tables import EmaTable
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import conservation_office_group_required
|
from konova.decorators import conservation_office_group_required
|
||||||
from ema.models import Ema
|
from ema.models import Ema, EmaDocument
|
||||||
from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm
|
from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm
|
||||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.utils.documents import get_document, remove_document
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@ -250,6 +251,43 @@ def document_new_view(request: HttpRequest, id: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Returns the document as downloadable file
|
||||||
|
|
||||||
|
Wraps the generic document fetcher function from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(EmaDocument, id=doc_id)
|
||||||
|
return get_document(doc)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def remove_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Removes the document from the database and file system
|
||||||
|
|
||||||
|
Wraps the generic functionality from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(EmaDocument, id=doc_id)
|
||||||
|
return remove_document(
|
||||||
|
request,
|
||||||
|
doc
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def state_remove_view(request: HttpRequest, id: str):
|
def state_remove_view(request: HttpRequest, id: str):
|
||||||
""" Renders a form for removing an EMA state
|
""" Renders a form for removing an EMA state
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from intervention.models import Intervention, ResponsibilityData, LegalData, Revocation
|
from intervention.models import Intervention, ResponsibilityData, LegalData, Revocation, InterventionDocument
|
||||||
|
from konova.admin import AbstractDocumentAdmin
|
||||||
|
|
||||||
|
|
||||||
class InterventionAdmin(admin.ModelAdmin):
|
class InterventionAdmin(admin.ModelAdmin):
|
||||||
@ -12,6 +13,8 @@ class InterventionAdmin(admin.ModelAdmin):
|
|||||||
"deleted",
|
"deleted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
class InterventionDocumentAdmin(AbstractDocumentAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
class ResponsibilityAdmin(admin.ModelAdmin):
|
class ResponsibilityAdmin(admin.ModelAdmin):
|
||||||
list_display = [
|
list_display = [
|
||||||
@ -47,3 +50,4 @@ admin.site.register(Intervention, InterventionAdmin)
|
|||||||
admin.site.register(ResponsibilityData, ResponsibilityAdmin)
|
admin.site.register(ResponsibilityData, ResponsibilityAdmin)
|
||||||
admin.site.register(LegalData, LegalAdmin)
|
admin.site.register(LegalData, LegalAdmin)
|
||||||
admin.site.register(Revocation, RevocationAdmin)
|
admin.site.register(Revocation, RevocationAdmin)
|
||||||
|
admin.site.register(InterventionDocument, InterventionDocumentAdmin)
|
||||||
|
@ -15,9 +15,8 @@ from django.urls import reverse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.models import EcoAccountDeduction, EcoAccount
|
from compensation.models import EcoAccountDeduction, EcoAccount
|
||||||
from intervention.models import Intervention, Revocation
|
from intervention.models import Intervention, Revocation, RevocationDocument
|
||||||
from konova.forms import BaseForm, BaseModalForm
|
from konova.forms import BaseForm, BaseModalForm
|
||||||
from konova.models import Document
|
|
||||||
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM, ZB_GROUP, ETS_GROUP
|
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM, ZB_GROUP, ETS_GROUP
|
||||||
from konova.utils.messenger import Messenger
|
from konova.utils.messenger import Messenger
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
@ -372,19 +371,9 @@ class NewRevocationForm(BaseModalForm):
|
|||||||
user=self.user,
|
user=self.user,
|
||||||
action=UserAction.EDITED
|
action=UserAction.EDITED
|
||||||
)
|
)
|
||||||
if self.cleaned_data["file"]:
|
|
||||||
document = Document.objects.create(
|
|
||||||
title="revocation_of_{}".format(self.instance.identifier),
|
|
||||||
date_of_creation=self.cleaned_data["date"],
|
|
||||||
comment=self.cleaned_data["comment"],
|
|
||||||
file=self.cleaned_data["file"],
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
document = None
|
|
||||||
revocation = Revocation.objects.create(
|
revocation = Revocation.objects.create(
|
||||||
date=self.cleaned_data["date"],
|
date=self.cleaned_data["date"],
|
||||||
comment=self.cleaned_data["comment"],
|
comment=self.cleaned_data["comment"],
|
||||||
document=document,
|
|
||||||
created=created_action,
|
created=created_action,
|
||||||
)
|
)
|
||||||
self.instance.modified = edited_action
|
self.instance.modified = edited_action
|
||||||
@ -392,6 +381,15 @@ class NewRevocationForm(BaseModalForm):
|
|||||||
self.instance.log.add(edited_action)
|
self.instance.log.add(edited_action)
|
||||||
self.instance.legal.revocation = revocation
|
self.instance.legal.revocation = revocation
|
||||||
self.instance.legal.save()
|
self.instance.legal.save()
|
||||||
|
|
||||||
|
if self.cleaned_data["file"]:
|
||||||
|
RevocationDocument.objects.create(
|
||||||
|
title="revocation_of_{}".format(self.instance.identifier),
|
||||||
|
date_of_creation=self.cleaned_data["date"],
|
||||||
|
comment=self.cleaned_data["comment"],
|
||||||
|
file=self.cleaned_data["file"],
|
||||||
|
instance=revocation
|
||||||
|
)
|
||||||
return revocation
|
return revocation
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_LAW_ID, \
|
from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_LAW_ID, \
|
||||||
CODELIST_PROCESS_TYPE_ID
|
CODELIST_PROCESS_TYPE_ID
|
||||||
from konova.models import BaseObject, Geometry, UuidModel, BaseResource
|
from konova.models import BaseObject, Geometry, UuidModel, BaseResource, AbstractDocument, \
|
||||||
|
generate_document_file_upload_path
|
||||||
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
||||||
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
|
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
|
||||||
from konova.utils import generators
|
from konova.utils import generators
|
||||||
from organisation.models import Organisation
|
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@ -68,15 +68,32 @@ class Revocation(BaseResource):
|
|||||||
"""
|
"""
|
||||||
date = models.DateField(null=True, blank=True, help_text="Revocation from")
|
date = models.DateField(null=True, blank=True, help_text="Revocation from")
|
||||||
comment = models.TextField(null=True, blank=True)
|
comment = models.TextField(null=True, blank=True)
|
||||||
document = models.ForeignKey("konova.Document", blank=True, null=True, on_delete=models.SET_NULL)
|
|
||||||
|
|
||||||
def delete(self):
|
def delete(self, *args, **kwargs):
|
||||||
# Make sure related objects are being removed as well
|
# Make sure related objects are being removed as well
|
||||||
if self.document:
|
if self.document:
|
||||||
self.document.delete()
|
self.document.delete(*args, **kwargs)
|
||||||
super().delete()
|
super().delete()
|
||||||
|
|
||||||
|
|
||||||
|
class RevocationDocument(AbstractDocument):
|
||||||
|
"""
|
||||||
|
Specializes document upload for revocations with certain path
|
||||||
|
"""
|
||||||
|
instance = models.OneToOneField(
|
||||||
|
Revocation,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="document",
|
||||||
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
upload_to=generate_document_file_upload_path
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def intervention(self):
|
||||||
|
return self.instance.legaldata.intervention
|
||||||
|
|
||||||
|
|
||||||
class LegalData(UuidModel):
|
class LegalData(UuidModel):
|
||||||
"""
|
"""
|
||||||
Holds intervention legal data such as important dates, laws or responsible handler
|
Holds intervention legal data such as important dates, laws or responsible handler
|
||||||
@ -112,7 +129,7 @@ class LegalData(UuidModel):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
revocation = models.ForeignKey(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL)
|
revocation = models.OneToOneField(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{} | {} | {}".format(
|
return "{} | {} | {}".format(
|
||||||
@ -131,7 +148,6 @@ class Intervention(BaseObject):
|
|||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
related_name='+',
|
|
||||||
help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle')"
|
help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle')"
|
||||||
)
|
)
|
||||||
legal = models.OneToOneField(
|
legal = models.OneToOneField(
|
||||||
@ -139,11 +155,9 @@ class Intervention(BaseObject):
|
|||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
related_name='+',
|
|
||||||
help_text="Holds data on legal dates or law"
|
help_text="Holds data on legal dates or law"
|
||||||
)
|
)
|
||||||
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
|
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
documents = models.ManyToManyField("konova.Document", blank=True)
|
|
||||||
|
|
||||||
# Checks - Refers to "Genehmigen" but optional
|
# Checks - Refers to "Genehmigen" but optional
|
||||||
checked = models.OneToOneField(
|
checked = models.OneToOneField(
|
||||||
@ -308,3 +322,17 @@ class Intervention(BaseObject):
|
|||||||
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
tooltip = _("Recorded on {} by {}").format(on, self.recorded.user)
|
tooltip = _("Recorded on {} by {}").format(on, self.recorded.user)
|
||||||
return tooltip
|
return tooltip
|
||||||
|
|
||||||
|
|
||||||
|
class InterventionDocument(AbstractDocument):
|
||||||
|
"""
|
||||||
|
Specializes document upload for an intervention with certain path
|
||||||
|
"""
|
||||||
|
instance = models.ForeignKey(
|
||||||
|
Intervention,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="documents",
|
||||||
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
upload_to=generate_document_file_upload_path
|
||||||
|
)
|
@ -39,14 +39,14 @@
|
|||||||
{% for doc in intervention.documents.all %}
|
{% for doc in intervention.documents.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<a href="{% url 'doc-open' doc.id %}">
|
<a href="{% url 'intervention:get-doc' doc.id %}">
|
||||||
{{ doc.title }}
|
{{ doc.title }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">{{ doc.comment }}</td>
|
<td class="align-middle">{{ doc.comment }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if is_default_member and has_access %}
|
{% if is_default_member and has_access %}
|
||||||
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
<button data-form-url="{% url 'intervention:remove-doc' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
|
||||||
{% fa5_icon 'trash' %}
|
{% fa5_icon 'trash' %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -31,10 +31,10 @@
|
|||||||
{% trans 'From' context 'Revocation' %}
|
{% trans 'From' context 'Revocation' %}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
{% trans 'Comment' %}
|
{% trans 'Document' %}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
{% trans 'Document' %}
|
{% trans 'Comment' %}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
{% trans 'Action' %}
|
{% trans 'Action' %}
|
||||||
@ -48,14 +48,14 @@
|
|||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{{ rev.date }}
|
{{ rev.date }}
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">{{ rev.comment }}</td>
|
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% if rev.document %}
|
{% if rev.document %}
|
||||||
<a href="{% url 'doc-open' rev.document.id %}">
|
<a href="{% url 'intervention:get-doc-revocation' rev.document.id %}">
|
||||||
{{ rev.document.file }}
|
{% trans 'Revocation' %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="align-middle">{{ rev.comment }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if is_default_member and has_access %}
|
{% if is_default_member and has_access %}
|
||||||
<button data-form-url="{% url 'intervention:remove-revocation' rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove revocation' %}">
|
<button data-form-url="{% url 'intervention:remove-revocation' rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove revocation' %}">
|
||||||
|
@ -9,13 +9,12 @@ from django.urls import path
|
|||||||
|
|
||||||
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
|
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
|
||||||
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_deduction_view, \
|
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_deduction_view, \
|
||||||
record_view
|
record_view, remove_document_view, get_document_view, get_revocation_view
|
||||||
|
|
||||||
app_name = "intervention"
|
app_name = "intervention"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", index_view, name="index"),
|
path("", index_view, name="index"),
|
||||||
path('new/', new_view, name='new'),
|
path('new/', new_view, name='new'),
|
||||||
path('<id>/document/new/', new_document_view, name='new-doc'),
|
|
||||||
path('<id>', open_view, name='open'),
|
path('<id>', open_view, name='open'),
|
||||||
path('<id>/log', log_view, name='log'),
|
path('<id>/log', log_view, name='log'),
|
||||||
path('<id>/edit', edit_view, name='edit'),
|
path('<id>/edit', edit_view, name='edit'),
|
||||||
@ -25,10 +24,16 @@ urlpatterns = [
|
|||||||
path('<id>/check', run_check_view, name='run-check'),
|
path('<id>/check', run_check_view, name='run-check'),
|
||||||
path('<id>/record', record_view, name='record'),
|
path('<id>/record', record_view, name='record'),
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
path('<id>/document/new/', new_document_view, name='new-doc'),
|
||||||
|
path('document/<doc_id>', get_document_view, name='get-doc'),
|
||||||
|
path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
||||||
|
|
||||||
# Deductions
|
# Deductions
|
||||||
path('<id>/deduction/new', new_deduction_view, name='acc-new-deduction'),
|
path('<id>/deduction/new', new_deduction_view, name='acc-new-deduction'),
|
||||||
|
|
||||||
# Revocation routes
|
# Revocation routes
|
||||||
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
|
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
|
||||||
path('revocation/<id>/remove', remove_revocation_view, name='remove-revocation'),
|
path('revocation/<id>/remove', remove_revocation_view, name='remove-revocation'),
|
||||||
|
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
|
||||||
]
|
]
|
@ -6,12 +6,13 @@ from django.shortcuts import render, get_object_or_404
|
|||||||
|
|
||||||
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm, \
|
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm, \
|
||||||
RunCheckForm, NewDeductionForm
|
RunCheckForm, NewDeductionForm
|
||||||
from intervention.models import Intervention, Revocation
|
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
|
||||||
from intervention.tables import InterventionTable
|
from intervention.tables import InterventionTable
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import *
|
from konova.decorators import *
|
||||||
from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm, RecordModalForm
|
from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm, RecordModalForm
|
||||||
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
|
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
|
||||||
|
from konova.utils.documents import remove_document, get_document
|
||||||
from konova.utils.message_templates import FORM_INVALID, INTERVENTION_INVALID
|
from konova.utils.message_templates import FORM_INVALID, INTERVENTION_INVALID
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
@ -94,6 +95,60 @@ def new_document_view(request: HttpRequest, id: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_revocation_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Returns the revocation document as downloadable file
|
||||||
|
|
||||||
|
Wraps the generic document fetcher function from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(RevocationDocument, id=doc_id)
|
||||||
|
return get_document(doc)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Returns the document as downloadable file
|
||||||
|
|
||||||
|
Wraps the generic document fetcher function from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(InterventionDocument, id=doc_id)
|
||||||
|
return get_document(doc)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def remove_document_view(request: HttpRequest, doc_id: str):
|
||||||
|
""" Removes the document from the database and file system
|
||||||
|
|
||||||
|
Wraps the generic functionality from konova.utils.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
doc_id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
doc = get_object_or_404(InterventionDocument, id=doc_id)
|
||||||
|
return remove_document(
|
||||||
|
request,
|
||||||
|
doc
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@any_group_check
|
@any_group_check
|
||||||
def open_view(request: HttpRequest, id: str):
|
def open_view(request: HttpRequest, id: str):
|
||||||
|
@ -7,7 +7,7 @@ Created on: 22.07.21
|
|||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from konova.models import Geometry, Document, Deadline
|
from konova.models import Geometry, Deadline
|
||||||
|
|
||||||
|
|
||||||
class GeometryAdmin(admin.ModelAdmin):
|
class GeometryAdmin(admin.ModelAdmin):
|
||||||
@ -17,7 +17,7 @@ class GeometryAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DocumentAdmin(admin.ModelAdmin):
|
class AbstractDocumentAdmin(admin.ModelAdmin):
|
||||||
list_display = [
|
list_display = [
|
||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
@ -36,5 +36,4 @@ class DeadlineAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
admin.site.register(Geometry, GeometryAdmin)
|
admin.site.register(Geometry, GeometryAdmin)
|
||||||
admin.site.register(Document, DocumentAdmin)
|
|
||||||
admin.site.register(Deadline, DeadlineAdmin)
|
admin.site.register(Deadline, DeadlineAdmin)
|
||||||
|
@ -21,11 +21,11 @@ from django.shortcuts import render
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount, Compensation, EcoAccountDocument, CompensationDocument
|
||||||
from ema.models import Ema
|
from ema.models import Ema, EmaDocument
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention, Revocation, RevocationDocument, InterventionDocument
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.models import Document, BaseObject
|
from konova.models import BaseObject
|
||||||
from konova.utils.message_templates import FORM_INVALID
|
from konova.utils.message_templates import FORM_INVALID
|
||||||
from user.models import UserActionLogEntry, UserAction
|
from user.models import UserActionLogEntry, UserAction
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ class NewDocumentForm(BaseModalForm):
|
|||||||
attrs={
|
attrs={
|
||||||
"class": "w-75"
|
"class": "w-75"
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
comment = forms.CharField(
|
comment = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -322,6 +322,13 @@ class NewDocumentForm(BaseModalForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
document_instance_map = {
|
||||||
|
Intervention: InterventionDocument,
|
||||||
|
Compensation: CompensationDocument,
|
||||||
|
EcoAccount: EcoAccountDocument,
|
||||||
|
Revocation: RevocationDocument,
|
||||||
|
Ema: EmaDocument,
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -331,6 +338,12 @@ class NewDocumentForm(BaseModalForm):
|
|||||||
self.form_attrs = {
|
self.form_attrs = {
|
||||||
"enctype": "multipart/form-data", # important for file upload
|
"enctype": "multipart/form-data", # important for file upload
|
||||||
}
|
}
|
||||||
|
self.document_type = self.document_instance_map.get(
|
||||||
|
self.instance.__class__,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
if not self.document_type:
|
||||||
|
raise NotImplementedError("Unsupported document type for {}".format(self.instance.__class__))
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
@ -338,14 +351,14 @@ class NewDocumentForm(BaseModalForm):
|
|||||||
user=self.user,
|
user=self.user,
|
||||||
action=UserAction.CREATED,
|
action=UserAction.CREATED,
|
||||||
)
|
)
|
||||||
doc = Document.objects.create(
|
doc = self.document_type.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"],
|
||||||
file=self.cleaned_data["file"],
|
file=self.cleaned_data["file"],
|
||||||
date_of_creation=self.cleaned_data["creation_date"],
|
date_of_creation=self.cleaned_data["creation_date"],
|
||||||
|
instance=self.instance,
|
||||||
)
|
)
|
||||||
self.instance.documents.add(doc)
|
|
||||||
|
|
||||||
edited_action = UserActionLogEntry.objects.create(
|
edited_action = UserActionLogEntry.objects.create(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
|
@ -18,6 +18,7 @@ from compensation.settings import COMPENSATION_IDENTIFIER_TEMPLATE, COMPENSATION
|
|||||||
ECO_ACCOUNT_IDENTIFIER_TEMPLATE, ECO_ACCOUNT_IDENTIFIER_LENGTH
|
ECO_ACCOUNT_IDENTIFIER_TEMPLATE, ECO_ACCOUNT_IDENTIFIER_LENGTH
|
||||||
from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_TEMPLATE
|
from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_TEMPLATE
|
||||||
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
|
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
|
||||||
|
from konova.settings import INTERVENTION_REVOCATION_DOC_PATH
|
||||||
from konova.utils.generators import generate_random_string
|
from konova.utils.generators import generate_random_string
|
||||||
from user.models import UserActionLogEntry, UserAction
|
from user.models import UserActionLogEntry, UserAction
|
||||||
|
|
||||||
@ -220,7 +221,47 @@ class Deadline(BaseResource):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Document(BaseResource):
|
def generate_document_file_upload_path(instance, filename):
|
||||||
|
""" Generates the file upload path for certain document instances
|
||||||
|
|
||||||
|
Documents derived from AbstractDocument need specific upload paths for their related models.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
instance (): The document instance
|
||||||
|
filename (): The filename
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
from compensation.models import CompensationDocument, EcoAccountDocument
|
||||||
|
from ema.models import EmaDocument
|
||||||
|
from intervention.models import InterventionDocument, RevocationDocument
|
||||||
|
from konova.settings import ECO_ACCOUNT_DOC_PATH, EMA_DOC_PATH, \
|
||||||
|
COMPENSATION_DOC_PATH, \
|
||||||
|
INTERVENTION_DOC_PATH
|
||||||
|
|
||||||
|
# Map document types to paths on the hard drive
|
||||||
|
path_map = {
|
||||||
|
InterventionDocument: INTERVENTION_DOC_PATH,
|
||||||
|
CompensationDocument: COMPENSATION_DOC_PATH,
|
||||||
|
EmaDocument: EMA_DOC_PATH,
|
||||||
|
RevocationDocument: INTERVENTION_REVOCATION_DOC_PATH,
|
||||||
|
EcoAccountDocument: ECO_ACCOUNT_DOC_PATH,
|
||||||
|
}
|
||||||
|
path = path_map.get(instance.__class__, None)
|
||||||
|
if path is None:
|
||||||
|
raise NotImplementedError("Unidentified document type: {}".format(instance.__class__))
|
||||||
|
|
||||||
|
# RevocationDocument needs special treatment, since these files need to be stored in a subfolder of the related
|
||||||
|
# instance's (Revocation) legaldata interventions folder
|
||||||
|
if instance.__class__ is RevocationDocument:
|
||||||
|
path = path.format(instance.intervention.id)
|
||||||
|
else:
|
||||||
|
path = path.format(instance.instance.id)
|
||||||
|
return path + filename
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractDocument(BaseResource):
|
||||||
"""
|
"""
|
||||||
Documents can be attached to compensation or intervention for uploading legal documents or pictures.
|
Documents can be attached to compensation or intervention for uploading legal documents or pictures.
|
||||||
"""
|
"""
|
||||||
@ -229,6 +270,9 @@ class Document(BaseResource):
|
|||||||
file = models.FileField()
|
file = models.FileField()
|
||||||
comment = models.TextField()
|
comment = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def delete(self, using=None, keep_parents=False):
|
def delete(self, using=None, keep_parents=False):
|
||||||
""" Custom delete function to remove the real file from the hard drive
|
""" Custom delete function to remove the real file from the hard drive
|
||||||
|
|
||||||
@ -239,7 +283,11 @@ class Document(BaseResource):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
os.remove(self.file.file.name)
|
os.remove(self.file.file.name)
|
||||||
|
except FileNotFoundError:
|
||||||
|
# File seems to missing anyway - continue!
|
||||||
|
pass
|
||||||
super().delete(using=using, keep_parents=keep_parents)
|
super().delete(using=using, keep_parents=keep_parents)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +55,20 @@ DEFAULT_GROUP = "Default"
|
|||||||
ZB_GROUP = "Registration office"
|
ZB_GROUP = "Registration office"
|
||||||
ETS_GROUP = "Conservation office"
|
ETS_GROUP = "Conservation office"
|
||||||
|
|
||||||
|
|
||||||
# Needed to redirect to LANIS
|
# Needed to redirect to LANIS
|
||||||
## Values to be inserted are [zoom_level, x_coord, y_coord]
|
## Values to be inserted are [zoom_level, x_coord, y_coord]
|
||||||
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_f,eiv_l,eiv_p,kom_f,kom_l,kom_p,oek_f,ema_f,mae&service=kartendienste_naturschutz"
|
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_f,eiv_l,eiv_p,kom_f,kom_l,kom_p,oek_f,ema_f,mae&service=kartendienste_naturschutz"
|
||||||
|
|
||||||
|
# ALLOWED FILE UPLOAD DEFINITIONS
|
||||||
|
# Default: Upload into upper project folder
|
||||||
|
MEDIA_ROOT = BASE_DIR + "/.."
|
||||||
|
|
||||||
|
# DOCUMENT UPLOAD PATHS
|
||||||
|
# Extends MEDIA_ROOT
|
||||||
|
## {} is a placeholder for the object's uuid --> each object will have it's own folder
|
||||||
|
BASE_DOC_PATH = "konova_uploaded_files/"
|
||||||
|
INTERVENTION_DOC_PATH = BASE_DOC_PATH + "interventions/{}/"
|
||||||
|
INTERVENTION_REVOCATION_DOC_PATH = BASE_DOC_PATH + "interventions/{}/revocation/"
|
||||||
|
COMPENSATION_DOC_PATH = BASE_DOC_PATH + "compensations/{}/"
|
||||||
|
ECO_ACCOUNT_DOC_PATH = BASE_DOC_PATH + "eco_account/{}/"
|
||||||
|
EMA_DOC_PATH = BASE_DOC_PATH + "ema/{}/"
|
@ -22,7 +22,7 @@ from konova.autocompletes import OrganisationAutocomplete, NonOfficialOrganisati
|
|||||||
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete
|
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete
|
||||||
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
||||||
from konova.sso.sso import KonovaSSOClient
|
from konova.sso.sso import KonovaSSOClient
|
||||||
from konova.views import logout_view, home_view, get_document_view, remove_document_view, remove_deadline_view
|
from konova.views import logout_view, home_view, remove_deadline_view
|
||||||
|
|
||||||
sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -38,10 +38,6 @@ urlpatterns = [
|
|||||||
path('news/', include("news.urls")),
|
path('news/', include("news.urls")),
|
||||||
path('news/', include("codelist.urls")),
|
path('news/', include("codelist.urls")),
|
||||||
|
|
||||||
# Generic documents routes
|
|
||||||
path('document/<id>', get_document_view, name="doc-open"),
|
|
||||||
path('document/<id>/remove', remove_document_view, name="doc-remove"),
|
|
||||||
|
|
||||||
# Generic deadline routes
|
# Generic deadline routes
|
||||||
path('deadline/<id>/remove', remove_deadline_view, name="deadline-remove"),
|
path('deadline/<id>/remove', remove_deadline_view, name="deadline-remove"),
|
||||||
|
|
||||||
|
50
konova/utils/documents.py
Normal file
50
konova/utils/documents.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 01.09.21
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import FileResponse, HttpRequest
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from konova.forms import RemoveModalForm
|
||||||
|
from konova.models import AbstractDocument
|
||||||
|
|
||||||
|
|
||||||
|
def get_document(doc: AbstractDocument):
|
||||||
|
""" Returns a document as downloadable attachment
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The document id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
return FileResponse(doc.file, as_attachment=True)
|
||||||
|
|
||||||
|
|
||||||
|
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, user=request.user)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=_("Document '{}' deleted").format(title)
|
||||||
|
)
|
@ -17,7 +17,7 @@ from intervention.models import Intervention
|
|||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import any_group_check
|
from konova.decorators import any_group_check
|
||||||
from konova.forms import RemoveModalForm
|
from konova.forms import RemoveModalForm
|
||||||
from konova.models import Document, Deadline
|
from konova.models import Deadline
|
||||||
from news.models import ServerMessage
|
from news.models import ServerMessage
|
||||||
from konova.settings import SSO_SERVER_BASE
|
from konova.settings import SSO_SERVER_BASE
|
||||||
|
|
||||||
@ -97,48 +97,6 @@ def home_view(request: HttpRequest):
|
|||||||
return render(request, template, context)
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def get_document_view(request: HttpRequest, id: str):
|
|
||||||
""" Returns a document as downloadable attachment
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
doc = get_object_or_404(Document, id=id)
|
|
||||||
return FileResponse(doc.file, as_attachment=True)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def remove_document_view(request: HttpRequest, id: str):
|
|
||||||
""" 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:
|
|
||||||
|
|
||||||
"""
|
|
||||||
doc = get_object_or_404(Document, id=id)
|
|
||||||
title = doc.title
|
|
||||||
form = RemoveModalForm(request.POST or None, instance=doc, user=request.user)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=_("Document '{}' deleted").format(title)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def remove_deadline_view(request: HttpRequest, id:str):
|
def remove_deadline_view(request: HttpRequest, id:str):
|
||||||
""" Renders a modal form for removing a deadline object
|
""" Renders a modal form for removing a deadline object
|
||||||
|
Loading…
Reference in New Issue
Block a user