This commit is contained in:
mipel
2021-07-01 13:36:07 +02:00
commit a5e8bcfa8c
91 changed files with 22395 additions and 0 deletions

0
process/__init__.py Normal file
View File

38
process/admin.py Normal file
View File

@@ -0,0 +1,38 @@
from django.contrib import admin
from process.models import Process
def activate_process(modeladmin, request, queryset):
for process in queryset:
process.activate()
def deactivate_process(modeladmin, request, queryset):
for process in queryset:
process.deactivate()
activate_process.short_description = "Activate selected process"
deactivate_process.short_description = "Deactivate selected process"
class ProcessAdmin(admin.ModelAdmin):
list_display = [
"id",
"licensing_authority",
"licensing_authority_document_identifier",
"registration_office",
"registration_office_document_identifier",
"is_active",
"is_deleted",
]
actions = [
activate_process,
deactivate_process,
]
admin.site.register(Process, ProcessAdmin)

5
process/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class ProcessConfig(AppConfig):
name = 'process'

202
process/enums.py Normal file
View File

@@ -0,0 +1,202 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 25.11.20
"""
from django.contrib.auth.models import User
from konova.enums import BaseEnum
from organisation.enums import RoleTypeEnum
class ProcessStateEnum(BaseEnum):
"""
ProcessStates define in which state a process can be:
private
Only the user can see and edit this process. Not viewable by any others
accessible
The user and all participated users can see this process. Only the responsible next user can edit
the process
licensed
The user and all participated users can see this process. Only the responsible next user can edit
the process
official
The user and all participated users can see this process. Only the registration office user can now
change details. If a process is changed in this state, the state will be set back to accessible, so all
participated users need to take a look on the changed data again.
recorded [Will be set automatically after certain time]
The user and all participated users can see this process. No one can edit data. To change any details,
the process has to be set to accessible manually again.
"""
PRIVATE = 0
ACCESSIBLE = 1
LICENSED = 2
OFFICIAL = 3
RECORDED = 4
@classmethod
def as_choices(cls, drop_empty_choice: bool = False) -> list:
""" Extends as_choices, so choices will be translated
Args:
drop_empty_choice (bool): Whether the empty choice shall be dropped or not
Returns:
trans_choices (list): Translated choices
"""
choices = super().as_choices(drop_empty_choice)
return ProcessStateEnum.__translate_choices(choices)
@staticmethod
def __translate_choices(choices: list) -> list:
""" Translates a list of prepared but untranslated choices
Args:
choices (list): A list of tuple chocies
Returns:
choices (list): The same list but translated
"""
from process.settings import PROCESS_STATE_STRINGS
trans_choices = []
# Translate
for choice in choices:
if choice[0] is not None:
choice = list(choice)
trans = PROCESS_STATE_STRINGS.get(choice[0])
choice[1] = trans
choice = tuple(choice)
trans_choices.append(choice)
return trans_choices
@classmethod
def is_state(cls, state: int) -> bool:
""" Checks whether the given state is a valid Enum
Args:
state (int): The state to be checked
Returns:
is_valid (bool)
"""
valid_vals = {enum.value for enum in cls}
return state in valid_vals
@classmethod
def as_role_choices(cls, user: User) -> list:
""" Checks whether the given state is a valid Enum
Args:
user (User): The performing user
Returns:
is_valid (bool)
"""
role = user.current_role
if role is None:
return []
role_type = role.role.type
role_type_enum = RoleTypeEnum[role_type]
choices = PROCESS_ROLE_STATES.get(role_type_enum)
choices = [(enum.value, enum.name) for enum in choices]
choices = ProcessStateEnum.__translate_choices(choices)
return choices
@classmethod
def as_next_role_choices(cls, user: User, current_state: int, is_owner: bool = False) -> list:
""" Returns a list of valid choices depending on the current role of the user and the current state
Args:
user (User): The performing user
current_state (int): The current state of the process
Returns:
choices (list): A list of valid choices
"""
role = user.current_role
if role is None:
return []
role_type = role.role.type
role_type_enum = RoleTypeEnum[role_type]
# Merge the possible choices depending on the current user role
# with the possible choices depending on the process state
role_choices = PROCESS_ROLE_STATES.get(role_type_enum)
status_choices = PROCESS_STATE_NEXT_CHOICES.get(current_state)
current_choice = {ProcessStateEnum(current_state)}
choices = (status_choices & role_choices) | current_choice
# If user is owner of this process, we shall add the private choice if not existing, yet
if is_owner:
choices = {ProcessStateEnum.PRIVATE} | choices
# Make sure enums are ordered by numerical value
choices = sorted(choices, key=lambda _enum: _enum.value)
# Create selectable and translated choices from enum list
choices = [(enum.value, enum.name) for enum in choices]
choices = ProcessStateEnum.__translate_choices(choices)
return choices
# DEFINES THE AVAILABLE STATES FOR EACH ROLE
PROCESS_ROLE_STATES = {
RoleTypeEnum.DATAPROVIDER: {
ProcessStateEnum.PRIVATE,
ProcessStateEnum.ACCESSIBLE,
},
RoleTypeEnum.LICENSINGAUTHORITY: {
#ProcessStateEnum.PRIVATE,
ProcessStateEnum.ACCESSIBLE,
ProcessStateEnum.LICENSED,
},
RoleTypeEnum.REGISTRATIONOFFICE: {
#ProcessStateEnum.PRIVATE,
ProcessStateEnum.ACCESSIBLE,
ProcessStateEnum.OFFICIAL,
},
}
# DEFINES POSSIBLE NEXT STATES FOR EACH PROCESS STATE
PROCESS_STATE_NEXT_CHOICES = {
ProcessStateEnum.PRIVATE.value: {
ProcessStateEnum.PRIVATE,
ProcessStateEnum.ACCESSIBLE,
},
ProcessStateEnum.ACCESSIBLE.value: {
ProcessStateEnum.PRIVATE,
ProcessStateEnum.ACCESSIBLE,
ProcessStateEnum.LICENSED,
},
ProcessStateEnum.LICENSED.value: {
ProcessStateEnum.PRIVATE,
ProcessStateEnum.ACCESSIBLE,
ProcessStateEnum.LICENSED,
ProcessStateEnum.OFFICIAL,
},
ProcessStateEnum.OFFICIAL.value: {
ProcessStateEnum.ACCESSIBLE,
ProcessStateEnum.OFFICIAL,
ProcessStateEnum.RECORDED,
},
}
# DEFINES FOR EACH STATE WHICH ROLE CAN EDIT THE PROCESS
PROCESS_EDITABLE_STATE = {
ProcessStateEnum.PRIVATE.value: {
RoleTypeEnum.DATAPROVIDER,
RoleTypeEnum.LICENSINGAUTHORITY,
},
ProcessStateEnum.ACCESSIBLE.value: {
RoleTypeEnum.LICENSINGAUTHORITY,
},
ProcessStateEnum.LICENSED.value: {
RoleTypeEnum.REGISTRATIONOFFICE,
},
ProcessStateEnum.OFFICIAL.value: {
RoleTypeEnum.REGISTRATIONOFFICE,
}
}

218
process/forms.py Normal file
View File

@@ -0,0 +1,218 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 30.11.20
"""
from dal import autocomplete
from django import forms
from django.contrib.auth.models import User
from django.db import transaction
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from intervention.models import Intervention
from konova.forms import BaseForm
from organisation.models import Organisation
from process.enums import ProcessStateEnum
from process.models import Process
class NewProcessForm(BaseForm):
"""
A form for new process
"""
identifier = forms.CharField(
max_length=255,
label=_("Identifier"),
label_suffix=_(""),
help_text=_("Generated automatically if none was given"),
required=False,
)
title = forms.CharField(
max_length=255,
label=_("Title"),
label_suffix=_(""),
help_text=_("Proper title of the process"),
required=True,
)
type = forms.CharField(
max_length=255,
label=_("Type"),
label_suffix=_(""),
help_text=_("Which process type is this"),
required=True,
)
licensing_authority = forms.ModelChoiceField(
label=_("Licencing Authority"),
label_suffix=_(""),
required=True,
queryset=Organisation.objects.all(),
widget=autocomplete.ModelSelect2(
url="orgs-autocomplete",
attrs={
"data-placeholder": _("Organization"),
"data-minimum-input-length": 3,
}
),
)
licensing_authority_document_identifier = forms.CharField(
max_length=255,
label=_("Licencing document identifier"),
label_suffix=_(""),
required=True,
)
comment_licensing_authority = forms.CharField(
widget=forms.Textarea,
label=_("Comment licensing authority"),
label_suffix=_(""),
required=False,
)
registration_office = forms.ModelChoiceField(
label=_("Registration office"),
label_suffix=_(""),
required=True,
queryset=Organisation.objects.all(),
widget=autocomplete.ModelSelect2(
url="orgs-autocomplete",
attrs={
"data-placeholder": _("Organization"),
"data-minimum-input-length": 3,
}
),
)
registration_office_document_identifier = forms.CharField(
max_length=255,
label=_("Registration document identifier"),
label_suffix=_(""),
required=True,
)
comment_registration_office = forms.CharField(
widget=forms.Textarea,
label=_("Comment registration office"),
label_suffix=_(""),
required=False,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.action_url = reverse("process:new")
self.cancel_redirect = reverse("process:index")
self.form_title = _("Add new process")
self.form_caption = _("Enter these basic information for the new process.")
def save(self, user: User):
"""
Persists process objects into database
"""
with transaction.atomic():
identifier = self.cleaned_data.get("identifier", None)
title = self.cleaned_data.get("title", None)
_type = self.cleaned_data.get("type", None)
licencing_auth = self.cleaned_data.get("licensing_authority", None)
licencing_auth_doc_id = self.cleaned_data.get("licensing_authority_document_identifier", None)
reg_off = self.cleaned_data.get("registration_office", None)
reg_off_doc_id = self.cleaned_data.get("registration_office_document_identifier", None)
comment_license = self.cleaned_data.get("comment_licensing_authority", None)
comment_registration = self.cleaned_data.get("comment_registration_office", None)
process = Process()
process.licensing_authority = licencing_auth
process.licensing_authority_document_identifier = licencing_auth_doc_id
process.registration_office = reg_off
process.registration_office_document_identifier = reg_off_doc_id
process.state = ProcessStateEnum.PRIVATE.value
process.created_by = user
process.licensing_authority_comment = comment_license
process.registration_office_comment = comment_registration
process.save()
intervention = Intervention()
intervention.title = title
intervention.type = _type
intervention.created_by = user
intervention.process = process
intervention.identifier = identifier
intervention.save()
return process
class EditProcessForm(NewProcessForm):
status = forms.ChoiceField(
label=_("Status"),
label_suffix="",
choices=[],
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
if self.instance is not None:
self.action_url = reverse("process:edit", args=(self.instance.id,))
self.cancel_redirect = reverse("process:index")
self.form_title = _("Edit process")
self.form_caption = ""
self.fields["status"].choices = ProcessStateEnum.as_next_role_choices(self.user, self.instance.state)
# Initialize form data
form_data = {
"identifier": self.instance.intervention.identifier,
"title": self.instance.intervention.title,
"type": self.instance.intervention.type,
"licensing_authority": self.instance.licensing_authority,
"licensing_authority_document_identifier": self.instance.licensing_authority_document_identifier,
"registration_office": self.instance.registration_office,
"registration_office_document_identifier": self.instance.registration_office_document_identifier,
"comment_licensing_authority": self.instance.licensing_authority_comment,
"comment_registration_office": self.instance.registration_office_comment,
"status": self.instance.state,
}
disabled_fields = [
"identifier",
]
self.load_initial_data(
form_data,
disabled_fields,
)
def save(self, user: User):
""" Persists changes from form to instance
Args:
user (User): The performing user
Returns:
process (Process): The edited process instance
"""
with transaction.atomic():
title = self.cleaned_data.get("title", None)
_type = self.cleaned_data.get("type", None)
licencing_auth = self.cleaned_data.get("licensing_authority", None)
licencing_auth_doc_id = self.cleaned_data.get("licensing_authority_document_identifier", None)
reg_off = self.cleaned_data.get("registration_office", None)
reg_off_doc_id = self.cleaned_data.get("registration_office_document_identifier", None)
comment_license = self.cleaned_data.get("comment_licensing_authority", None)
comment_registration = self.cleaned_data.get("comment_registration_office", None)
new_state = self.cleaned_data.get("status", None)
self.instance.licensing_authority = licencing_auth
self.instance.licensing_authority_document_identifier = licencing_auth_doc_id
self.instance.registration_office = reg_off
self.instance.registration_office_document_identifier = reg_off_doc_id
self.instance.state = ProcessStateEnum.PRIVATE.value
self.instance.created_by = user
self.instance.licensing_authority_comment = comment_license
self.instance.registration_office_comment = comment_registration
self.instance.state = new_state
self.instance.save()
self.instance.intervention.title = title
self.instance.intervention.type = _type
self.instance.intervention.created_by = user
self.instance.intervention.save()
return self.instance

158
process/models.py Normal file
View File

@@ -0,0 +1,158 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 17.11.20
"""
from django.contrib.auth.models import User
from django.db import models, transaction
from konova.models import BaseResource
from organisation.enums import RoleTypeEnum
from organisation.models import Organisation
from process.enums import ProcessStateEnum
from process.settings import PROCESS_STATE_STRINGS
class Process(BaseResource):
"""
Process links compensation, intervention and eco accounts. These links are realized as ForeignKeys in their models.
This process model therefore just holds further information on participated legal organizations.
Attribute 'state' holds information on the current state of the process, which are defined in ProcessStateEnum
"""
licensing_authority = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, blank=True, related_name="+")
licensing_authority_document_identifier = models.CharField(max_length=500, null=True, blank=True)
licensing_authority_comment = models.CharField(max_length=500, null=True, blank=True)
registration_office_document_identifier = models.CharField(max_length=500, null=True, blank=True)
registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, blank=True, related_name="+")
registration_office_comment = models.CharField(max_length=500, null=True, blank=True)
state = models.PositiveIntegerField(choices=ProcessStateEnum.as_choices(drop_empty_choice=True), default=0)
def __str__(self) -> str:
try:
intervention = self.intervention
title = intervention.title
except AttributeError:
title = "NO TITLE"
return title
def get_state_str(self):
"""
Translates the numeric state into a string
Returns:
"""
return PROCESS_STATE_STRINGS.get(self.state, None)
def deactivate(self):
""" Deactivates a process and it's related elements
Returns:
"""
if self.is_active and not self.is_deleted:
self.toggle_deletion()
def activate(self):
""" Activates a process and it's related elements
Returns:
"""
if not self.is_active and self.is_deleted:
self.toggle_deletion()
def toggle_deletion(self):
""" Enables or disables a process
Processes are not truly removed from the database, just toggled in their flags 'is_active' and 'is_deleted'
Returns:
"""
with transaction.atomic():
self.is_active = not self.is_active
self.is_deleted = not self.is_deleted
self.save()
# toggle related elements
comps = self.compensation.all()
elements = list(comps)
if self.intervention is not None:
elements.append(self.intervention)
for elem in elements:
elem.is_active = self.is_active
elem.is_deleted = self.is_deleted
elem.save()
@staticmethod
def get_role_objects(user: User, order_by: str = "-created_on"):
""" Returns processes depending on the currently selected role of the user
* REGISTRATIONOFFICE
* User can see the processes where registration_office is set to the organisation of the currently selected role
* User can see self-created processes
* LICENSINGOFFICE
* same
* DATAPROVIDER
* User can see only self-created processes
Args:
user (User): The performing user
order_by (str): Order by which Process attribute
Returns:
"""
role = user.current_role
if role is None:
return Process.objects.none()
_filter = {
"is_deleted": False,
}
if role.role.type == RoleTypeEnum.REGISTRATIONOFFICE.value:
_filter["registration_office"] = role.organisation
elif role.role.type == RoleTypeEnum.LICENSINGAUTHORITY.value:
_filter["licensing_authority"] = role.organisation
elif role.role.type == RoleTypeEnum.DATAPROVIDER.value:
# Nothing special
_filter["created_by"] = user
else:
# None of the above
pass
other_processes = Process.objects.filter(
**_filter
)
user_created_processes = Process.objects.filter(
created_by=user,
is_deleted=False,
)
qs = (other_processes | user_created_processes).distinct()
qs = qs.order_by(order_by)
return qs
@staticmethod
def create_from_intervention(intervention):
""" Creates a process for an intervention, in case an intervention has been created without a process
Args:
intervention (Intervention): The intervention
Returns:
process (Process)
"""
process = Process()
process.identifier = intervention.identifier
process.title = intervention.title
process.created_by = intervention.created_by
process.save()
intervention.process = process
intervention.save()
return process

19
process/settings.py Normal file
View File

@@ -0,0 +1,19 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 25.11.20
"""
from django.utils.translation import gettext_lazy as _
from process.enums import ProcessStateEnum
# DEFINES TRANSLATIONS FOR EACH PROCESSSTATEENUM
PROCESS_STATE_STRINGS = {
ProcessStateEnum.PRIVATE.value: _("private"),
ProcessStateEnum.ACCESSIBLE.value: _("accessible"),
ProcessStateEnum.LICENSED.value: _("licensed"),
ProcessStateEnum.OFFICIAL.value: _("official"),
ProcessStateEnum.RECORDED.value: _("recorded"),
}

130
process/tables.py Normal file
View File

@@ -0,0 +1,130 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 25.11.20
"""
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.html import format_html
from konova.utils.tables import BaseTable, ChoicesColumnForm
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from process.enums import ProcessStateEnum
from process.models import Process
from process.settings import PROCESS_STATE_STRINGS
class ProcessTable(BaseTable):
id = tables.Column(
verbose_name=_("Intervention identifier"),
orderable=True,
accessor="intervention.identifier",
)
t = tables.Column(
verbose_name=_("Title"),
orderable=True,
accessor="intervention.title",
)
"""
THESE COLUMNS MIGHT NOT BE OF INTEREST. TO REDUCE TABLE WIDTH THEY CAN BE REMOVED
dila = tables.Column(
verbose_name=_("Licensing authority document identifier"),
orderable=True,
accessor="licensing_authority_document_identifier",
)
diro = tables.Column(
verbose_name=_("Registration office document identifier"),
orderable=True,
accessor="registration_office_document_identifier",
)
"""
s = tables.Column(
verbose_name=_("Status"),
orderable=True,
accessor="state",
)
d = tables.Column(
verbose_name=_("Created on"),
orderable=True,
accessor="created_on",
)
ac = tables.Column(
verbose_name=_("Actions"),
orderable=False,
empty_values=[],
attrs={"td": {"class": "action-col"}}
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = _("Processes")
self.add_new_url = reverse("process:new")
def render_id(self, value, record: Process):
""" Renders the id column for an intervention
Args:
value (str): The identifier value
record (Process): The process record
Returns:
"""
html = ""
html += self.render_link(
tooltip=_("Open {}").format(_("Process")),
href=reverse("process:open", args=(record.id,)),
txt=value,
new_tab=False,
)
return format_html(html)
def render_s(self, value, record, column) -> str:
""" Translates the record state to a desired language
Args:
value (str): The value of state inside record
record (Process): The whole record itself
Returns:
str
"""
state = record.state
is_owner = record.created_by == self.request.user
choices = ProcessStateEnum.as_next_role_choices(self.request.user, state, is_owner)
valid_choices = [choice[0] for choice in choices]
if state in valid_choices:
form = ChoicesColumnForm(
action_url=reverse("process:edit-status", args=(record.id,)),
choices=choices,
initial={"select": state},
)
rendered = render_to_string("konova/choiceColumnForm.html", context={"form": form}, request=self.request)
else:
rendered = PROCESS_STATE_STRINGS.get(state)
return rendered
def render_ac(self, value, record):
"""
Renders possible actions for this record, such as delete.
"""
process = _("Process")
html = ""
html += self.render_open_btn(
_("Open {}").format(process),
reverse("process:open", args=(record.id,)),
)
html += self.render_edit_btn(
_("Edit {}").format(process),
reverse("process:edit", args=(record.id,)),
)
html += self.render_delete_btn(
_("Delete {}").format(process),
reverse("process:remove", args=(record.id,)),
)
return format_html(html)

View File

@@ -0,0 +1,95 @@
{% extends 'base.html' %}
{% load i18n static custom_tags %}
{% block body %}
<div class="rows">
<div class="columns">
<div class="large-10 column">
<h3>{% trans 'Process' %}: {{process.intervention.identifier}}</h3>
<h4>{{process.intervention.title}}</h4>
</div>
<div class="large-2 column">
<a href="{% url 'process:edit' process.id %}">
<button class="button small" role="button" value="{% trans 'Edit' %}"><em class='fas fa-edit'></em> {% trans 'Edit' %}</button>
</a>
</div>
</div>
</div>
<div class="rows">
<table>
<tbody>
<tr>
<th scope="row">{% trans 'Licencing Authority' %}</th>
<td>{{ process.licensing_authority }}</td>
</tr>
<tr>
<th scope="row">{% trans 'Licencing document identifier' %}</th>
<td>{{ process.licensing_authority_document_identifier }}</td>
</tr>
<tr>
<th scope="row">{% trans 'Registration office' %}</th>
<td>{{ process.registration_office }}</td>
</tr>
<tr>
<th scope="row">{% trans 'Registration document identifier' %}</th>
<td>{{ process.registration_office_document_identifier }}</td>
</tr>
<tr>
<th scope="row">{% trans 'Status' %}</th>
<td>
<span class="small info" title="{% trans 'Status' %}">
<em class="fas fa-exclamation-triangle"></em>
{{process.state|resolve_process_state}}
</span>
</td>
</tr>
<tr>
<th scope="row">{% trans 'Intervention' %}</th>
<td>
<a href="{% url 'intervention:open' process.intervention.id %}" target="_blank">
<button class="button small" role="button" title="{{process.intervention.title}} - {{process.intervention.type}}">{{process.intervention.identifier}}</button>
</a>
</td>
</tr>
<tr>
<th scope="row" style="vertical-align: baseline">
{% trans 'Compensations' %}
<br>
<a href="{% url 'process:add-compensation' process.id %}" target="_blank">
<button class="button small" role="button" title="{% trans 'Add a new compensation' %}">
<em class="fas fa-plus-circle"></em>
{% trans 'New' %}
</button>
</a>
</th>
<td>
<table>
<tbody>
{% for compensation in process.compensations.all %}
<tr>
<td>
<a href="{% url 'compensation:open' compensation.id %}" target="_blank">
<button class="button small" role="button" title="{{compensation.title}} - {{compensation.type}}">{{compensation.identifier}}</button>
</a>
</td>
<td class="action-col">
<a href="{% url 'compensation:edit' compensation.id %}" title="{% trans 'Edit' %}"><button class="button small" title="{% trans 'Edit' %}"><em class='fas fa-edit'></em></button></a>
<a href="{% url 'compensation:remove' compensation.id %}" title="{% trans 'Remove' %}"><button class="button small" title="{% trans 'Remove' %}"><em class='fas fa-trash-alt'></em></button></a>
</td>
</tr>
{% empty %}
<tr>
<td>
{% trans 'No compensation' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
{% endblock %}

3
process/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

22
process/urls.py Normal file
View File

@@ -0,0 +1,22 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 25.11.20
"""
from django.urls import path
from process.views import index_view, new_view, open_view, edit_view, remove_view, edit_status_view, \
add_compensation_view
app_name = "process"
urlpatterns = [
path('', index_view, name='index'),
path('new/', new_view, name='new'),
path('open/<id>', open_view, name='open'),
path('edit/<id>', edit_view, name='edit'),
path('edit/<id>/status', edit_status_view, name='edit-status'),
path('remove/<id>', remove_view, name='remove'),
path('ac/<id>', add_compensation_view, name='add-compensation'),
]

217
process/views.py Normal file
View File

@@ -0,0 +1,217 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.models import Compensation
from konova.contexts import BaseContext
from konova.decorators import resolve_user_role, valid_process_role_required
from konova.forms import RemoveForm
from konova.utils.tables import ChoicesColumnForm
from process.enums import ProcessStateEnum
from process.forms import NewProcessForm, EditProcessForm
from process.models import Process
from process.settings import PROCESS_STATE_STRINGS
from process.tables import ProcessTable
# URL definitions
PROCESS_INDEX_URL = "process:index"
@login_required
@resolve_user_role
def index_view(request: HttpRequest):
"""
Renders the index view for process
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
template = "generic_index.html"
user = request.user
processes = Process.get_role_objects(user)
table = ProcessTable(
request=request,
queryset=processes
)
context = {
"table": table,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def new_view(request: HttpRequest):
"""
Renders a view for a new process creation
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "konova/form.html"
form = NewProcessForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
process = form.save(request.user)
intervention = process.intervention
messages.info(request, _("A process is based on an intervention. Please fill in the missing data for this intervention"))
return redirect("intervention:edit", id=intervention.id)
else:
messages.error(request, _("Invalid input"))
else:
# For clarification: Nothing to do here
pass
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def open_view(request: HttpRequest, id: str):
""" Renders a detail view for this process
Args:
request (HttpRequest): The incoming request
id (str): The uuid id as string
Returns:
"""
template = "process/open.html"
process = get_object_or_404(Process, id=id)
context = {
"process": process,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@resolve_user_role
def edit_view(request: HttpRequest, id: str):
""" Renders an edit view for this process
Args:
request (HttpRequest): The incoming request
id (str): The uuid id as string
Returns:
"""
template = "konova/form.html"
process = get_object_or_404(Process, id=id)
if request.method == "POST":
form = EditProcessForm(request.POST or None, instance=process, user=request.user)
if form.is_valid():
process = form.save(request.user)
messages.success(request, _("{} edited").format(process))
return redirect("process:index")
else:
messages.error(request, _("Invalid input"))
form = EditProcessForm(instance=process, user=request.user)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def remove_view(request: HttpRequest, id: str):
""" Renders a remove view for this process
Args:
request (HttpRequest): The incoming request
id (str): The uuid id as string
Returns:
"""
template = "konova/form.html"
process = get_object_or_404(Process, id=id)
if request.method == "POST":
form = RemoveForm(
request.POST or None,
object_to_remove=process,
remove_post_url=reverse("process:remove", args=(id,)),
cancel_url=reverse("process:index"),
)
if form.is_valid():
confirmed = form.is_checked()
if confirmed:
process.deactivate()
messages.success(request, _("Process {} removed").format(process))
return redirect("process:index")
else:
messages.error(request, _("Invalid input"))
form = RemoveForm(
object_to_remove=process,
remove_post_url=reverse("process:remove", args=(id,)),
cancel_url=reverse("process:index"),
)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def edit_status_view(request: HttpRequest, id: str):
""" Changes only the status of a process
Args:
request (The incoming request):
id ():
Returns:
"""
process = get_object_or_404(Process, id=id)
old_state = process.state
form = ChoicesColumnForm(request.POST or None, choices=ProcessStateEnum.as_choices(drop_empty_choice=True))
if request.method == "POST":
if form.is_valid():
process.state = int(form.cleaned_data.get("select", -1))
process.save()
_from = PROCESS_STATE_STRINGS.get(old_state)
_to = PROCESS_STATE_STRINGS.get(process.state)
messages.info(request, _("{} status changed from {} to {}").format(process.intervention.title, _from, _to))
else:
messages.error(request, form.errors)
return redirect("process:index")
@login_required
def add_compensation_view(request: HttpRequest, id: str):
""" Adds a new compensation to a process
Args:
request (HttpRequest): The incoming request
id (str): The process' id
Returns:
"""
process = get_object_or_404(Process, id=id)
comp = Compensation()
comp.process = process
comp.save()
messages.info(request, _("Please fill in the data for this compensation"))
return redirect("compensation:edit", id=comp.id)
#template = ""
#context = {}
#context = BaseContext(request, context).context
#return render(request, template, context)