Merge branch 'master' into Docker

# Conflicts:
#	konova/settings.py
#	konova/sub_settings/django_settings.py
This commit is contained in:
mpeltriaux 2022-02-04 13:59:30 +01:00
commit fac55f7acb
82 changed files with 1967 additions and 411 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
# Project exclude paths # Project exclude paths
/venv/ /venv/
/.idea/ /.idea/
*/migrations/

View File

@ -12,5 +12,9 @@ class APITokenAdmin(admin.ModelAdmin):
readonly_fields = [ readonly_fields = [
"token" "token"
] ]
search_fields = [
"token"
]
admin.site.register(APIUserToken, APITokenAdmin) admin.site.register(APIUserToken, APITokenAdmin)

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1.3 on 2022-01-28 15:48
from django.db import migrations, models
import konova.utils.generators
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='APIUserToken',
fields=[
('token', models.CharField(default=konova.utils.generators.generate_token, max_length=1000, primary_key=True, serialize=False)),
('valid_until', models.DateField(blank=True, help_text='Token is only valid until this date', null=True)),
('is_active', models.BooleanField(default=False, help_text='Must be activated by an admin')),
],
),
]

View File

@ -14,7 +14,8 @@ from django.db.models import QuerySet
from api.utils.serializer.serializer import AbstractModelAPISerializer from api.utils.serializer.serializer import AbstractModelAPISerializer
from codelist.models import KonovaCode from codelist.models import KonovaCode
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_PROCESS_TYPE_ID, \ from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_PROCESS_TYPE_ID, \
CODELIST_LAW_ID, CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID CODELIST_LAW_ID, CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, \
CODELIST_COMPENSATION_ACTION_DETAIL_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID
from compensation.models import CompensationAction, UnitChoices, CompensationState from compensation.models import CompensationAction, UnitChoices, CompensationState
from intervention.models import Responsibility, Legal from intervention.models import Responsibility, Legal
from konova.models import Deadline, DeadlineType from konova.models import Deadline, DeadlineType
@ -323,6 +324,9 @@ class AbstractCompensationAPISerializerV1Mixin:
states = [] states = []
for entry in states_data: for entry in states_data:
biotope_type = entry["biotope"] biotope_type = entry["biotope"]
biotope_details = [
self._konova_code_from_json(e, CODELIST_BIOTOPES_EXTRA_CODES_ID) for e in entry["biotope_details"]
]
surface = float(entry["surface"]) surface = float(entry["surface"])
# Check on validity # Check on validity
@ -331,22 +335,22 @@ class AbstractCompensationAPISerializerV1Mixin:
# If this exact data is already existing, we do not create it new. Instead put it's id in the list of # If this exact data is already existing, we do not create it new. Instead put it's id in the list of
# entries, we will use to set the new actions # entries, we will use to set the new actions
pre_existing_state = states_manager.filter( state = states_manager.filter(
biotope_type__atom_id=biotope_type, biotope_type__atom_id=biotope_type,
surface=surface, surface=surface,
).exclude( ).exclude(
id__in=states id__in=states
).first() ).first()
if pre_existing_state is not None: if state is not None:
states.append(pre_existing_state.id) states.append(state.id)
else: else:
# Create and add id to list # Create and add id to list
new_state = CompensationState.objects.create( state = CompensationState.objects.create(
biotope_type=self._konova_code_from_json(biotope_type, CODELIST_BIOTOPES_ID), biotope_type=self._konova_code_from_json(biotope_type, CODELIST_BIOTOPES_ID),
surface=surface surface=surface
) )
states.append(new_state.id) states.append(state.id)
state.biotope_type_details.set(biotope_details)
states_manager.set(states) states_manager.set(states)
return obj return obj
@ -364,6 +368,9 @@ class AbstractCompensationAPISerializerV1Mixin:
actions = [] actions = []
for entry in actions_data: for entry in actions_data:
action = entry["action"] action = entry["action"]
action_details = [
self._konova_code_from_json(e, CODELIST_COMPENSATION_ACTION_DETAIL_ID) for e in entry["action_details"]
]
amount = float(entry["amount"]) amount = float(entry["amount"])
unit = entry["unit"] unit = entry["unit"]
comment = entry["comment"] comment = entry["comment"]
@ -376,7 +383,7 @@ class AbstractCompensationAPISerializerV1Mixin:
# If this exact data is already existing, we do not create it new. Instead put it's id in the list of # If this exact data is already existing, we do not create it new. Instead put it's id in the list of
# entries, we will use to set the new actions # entries, we will use to set the new actions
pre_existing_action = obj.actions.filter( action_entry = obj.actions.filter(
action_type__atom_id=action, action_type__atom_id=action,
amount=amount, amount=amount,
unit=unit, unit=unit,
@ -384,17 +391,19 @@ class AbstractCompensationAPISerializerV1Mixin:
).exclude( ).exclude(
id__in=actions id__in=actions
).first() ).first()
if pre_existing_action is not None: if action_entry is not None:
actions.append(pre_existing_action.id) actions.append(action_entry.id)
else: else:
# Create and add id to list # Create and add id to list
new_action = CompensationAction.objects.create( action_entry = CompensationAction.objects.create(
action_type=self._konova_code_from_json(action, CODELIST_COMPENSATION_ACTION_ID), action_type=self._konova_code_from_json(action, CODELIST_COMPENSATION_ACTION_ID),
amount=amount, amount=amount,
unit=unit, unit=unit,
comment=comment, comment=comment,
) )
actions.append(new_action.id) actions.append(action_entry.id)
action_entry.action_type_details.set(action_details)
obj.actions.set(actions) obj.actions.set(actions)
return obj return obj
@ -410,6 +419,9 @@ class AbstractCompensationAPISerializerV1Mixin:
return [ return [
{ {
"biotope": self._konova_code_to_json(entry.biotope_type), "biotope": self._konova_code_to_json(entry.biotope_type),
"biotope_details": [
self._konova_code_to_json(detail) for detail in entry.biotope_type_details.all()
],
"surface": entry.surface, "surface": entry.surface,
} }
for entry in qs for entry in qs
@ -427,6 +439,9 @@ class AbstractCompensationAPISerializerV1Mixin:
return [ return [
{ {
"action": self._konova_code_to_json(entry.action_type), "action": self._konova_code_to_json(entry.action_type),
"action_details": [
self._konova_code_to_json(detail) for detail in entry.action_type_details.all()
],
"amount": entry.amount, "amount": entry.amount,
"unit": entry.unit, "unit": entry.unit,
"comment": entry.comment, "comment": entry.comment,

View File

@ -35,6 +35,13 @@ class KonovaCodeAdmin(admin.ModelAdmin):
"parent", "parent",
] ]
search_fields = [
"id",
"atom_id",
"long_name",
"short_name",
]
#admin.site.register(KonovaCodeList, KonovaCodeListAdmin) #admin.site.register(KonovaCodeList, KonovaCodeListAdmin)
admin.site.register(KonovaCode, KonovaCodeAdmin) admin.site.register(KonovaCode, KonovaCodeAdmin)

View File

@ -13,7 +13,8 @@ from codelist.models import KonovaCode, KonovaCodeList
from codelist.settings import CODELIST_INTERVENTION_HANDLER_ID, CODELIST_CONSERVATION_OFFICE_ID, \ from codelist.settings import CODELIST_INTERVENTION_HANDLER_ID, CODELIST_CONSERVATION_OFFICE_ID, \
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, CODELIST_COMPENSATION_HANDLER_ID, \ CODELIST_REGISTRATION_OFFICE_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, CODELIST_COMPENSATION_HANDLER_ID, \
CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_CLASS_ID, CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID, \ CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_CLASS_ID, CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID, \
CODELIST_BASE_URL, CODELIST_PROCESS_TYPE_ID CODELIST_BASE_URL, CODELIST_PROCESS_TYPE_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
CODELIST_COMPENSATION_ACTION_DETAIL_ID
from konova.management.commands.setup import BaseKonovaCommand from konova.management.commands.setup import BaseKonovaCommand
from konova.settings import PROXIES from konova.settings import PROXIES
@ -33,10 +34,12 @@ class Command(BaseKonovaCommand):
CODELIST_CONSERVATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID,
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID,
CODELIST_BIOTOPES_ID, CODELIST_BIOTOPES_ID,
CODELIST_BIOTOPES_EXTRA_CODES_ID,
CODELIST_LAW_ID, CODELIST_LAW_ID,
CODELIST_COMPENSATION_HANDLER_ID, CODELIST_COMPENSATION_HANDLER_ID,
CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_ID,
CODELIST_COMPENSATION_ACTION_CLASS_ID, CODELIST_COMPENSATION_ACTION_CLASS_ID,
CODELIST_COMPENSATION_ACTION_DETAIL_ID,
CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID, CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID,
CODELIST_PROCESS_TYPE_ID, CODELIST_PROCESS_TYPE_ID,
] ]

View File

@ -0,0 +1,35 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='KonovaCode',
fields=[
('id', models.IntegerField(help_text='Regular Id', primary_key=True, serialize=False)),
('atom_id', models.IntegerField(blank=True, help_text='AtomId; Identifies this code uniquely over all NatIT projects; Duplicates possible', null=True)),
('short_name', models.CharField(blank=True, help_text='Short version of long name', max_length=500, null=True)),
('long_name', models.CharField(blank=True, max_length=1000, null=True)),
('is_selectable', models.BooleanField(default=False, help_text='Whether this code shall be used for any select actions or not')),
('is_leaf', models.BooleanField(default=False, help_text='Whether this code has children or not')),
('is_archived', models.BooleanField(default=False, help_text='Whether this code is archived or not')),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='codelist.konovacode')),
],
),
migrations.CreateModel(
name='KonovaCodeList',
fields=[
('id', models.IntegerField(help_text='List identifier', primary_key=True, serialize=False)),
('codes', models.ManyToManyField(blank=True, help_text='Codes for this list', related_name='code_lists', to='codelist.KonovaCode')),
],
),
]

View File

View File

@ -14,11 +14,13 @@ CODELIST_INTERVENTION_HANDLER_ID = 903 # CLMassnahmeträger
CODELIST_CONSERVATION_OFFICE_ID = 907 # CLNaturschutzbehörden CODELIST_CONSERVATION_OFFICE_ID = 907 # CLNaturschutzbehörden
CODELIST_REGISTRATION_OFFICE_ID = 1053 # CLZulassungsbehörden CODELIST_REGISTRATION_OFFICE_ID = 1053 # CLZulassungsbehörden
CODELIST_BIOTOPES_ID = 974 # CL_EIV_Biotoptypen CODELIST_BIOTOPES_ID = 974 # CL_EIV_Biotoptypen
CODELIST_BIOTOPES_EXTRA_CODES_ID = 975 # CLZusatzbezeichnung
CODELIST_LAW_ID = 1048 # CLVerfahrensrecht CODELIST_LAW_ID = 1048 # CLVerfahrensrecht
CODELIST_PROCESS_TYPE_ID = 44382 # CLVerfahrenstyp CODELIST_PROCESS_TYPE_ID = 44382 # CLVerfahrenstyp
CODELIST_COMPENSATION_HANDLER_ID = 1052 # CLEingreifer CODELIST_COMPENSATION_HANDLER_ID = 1052 # CLEingreifer
CODELIST_COMPENSATION_ACTION_ID = 1026 # CLMassnahmedetail CODELIST_COMPENSATION_ACTION_ID = 1026 # CLMassnahmedetail
CODELIST_COMPENSATION_ACTION_DETAIL_ID = 1035 # CLZusatzmerkmal
CODELIST_COMPENSATION_ACTION_CLASS_ID = 1034 # CLMassnahmeklasse CODELIST_COMPENSATION_ACTION_CLASS_ID = 1034 # CLMassnahmeklasse
CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID = 1028 # CLMassnahmetyp, CEF and stuff CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID = 1028 # CLMassnahmetyp, CEF and stuff
CODELIST_COMPENSATION_FUNDING_ID = 1049 # CLKombimassnahme CODELIST_COMPENSATION_FUNDING_ID = 1049 # CLKombimassnahme

View File

@ -2,7 +2,100 @@ from django.contrib import admin
from compensation.models import Compensation, CompensationAction, CompensationState, Payment, \ from compensation.models import Compensation, CompensationAction, CompensationState, Payment, \
EcoAccountDeduction, EcoAccount EcoAccountDeduction, EcoAccount
from konova.admin import BaseObjectAdmin from konova.admin import BaseObjectAdmin, BaseResourceAdmin
class AbstractCompensationAdmin(BaseObjectAdmin):
list_display = [
"id",
"identifier",
"title",
"created",
"deleted",
]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + [
"identifier",
"title",
"comment",
"after_states",
"before_states",
]
def get_readonly_fields(self, request, obj=None):
return super().get_readonly_fields(request, obj) + [
"after_states",
"before_states",
]
class CompensationAdmin(AbstractCompensationAdmin):
autocomplete_fields = [
"intervention",
]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + [
"is_cef",
"is_coherence_keeping",
"intervention",
]
class EcoAccountAdmin(AbstractCompensationAdmin):
list_display = [
"id",
"identifier",
"title",
"created",
"deleted",
]
filter_horizontal = [
"users"
]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + [
"deductable_surface",
"users"
]
class PaymentAdmin(admin.ModelAdmin):
list_display = [
"id",
"amount",
"due_on",
]
class EcoAccountDeductionAdmin(BaseResourceAdmin):
list_display = [
"id",
"account",
"intervention",
"surface",
]
search_fields = [
"account__identifier",
"account__title",
"intervention__identifier",
"intervention__title",
"surface",
]
autocomplete_fields = [
"account",
"intervention",
]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + [
"account",
"intervention",
"surface",
]
class CompensationStateAdmin(admin.ModelAdmin): class CompensationStateAdmin(admin.ModelAdmin):
@ -23,44 +116,11 @@ class CompensationActionAdmin(admin.ModelAdmin):
] ]
class CompensationAdmin(BaseObjectAdmin):
list_display = [
"id",
"identifier",
"title",
"created",
"deleted",
]
class EcoAccountAdmin(admin.ModelAdmin):
list_display = [
"id",
"identifier",
"title",
]
class PaymentAdmin(admin.ModelAdmin):
list_display = [
"id",
"amount",
"due_on",
]
class EcoAccountDeductionAdmin(admin.ModelAdmin):
list_display = [
"id",
"account",
"intervention",
"surface",
]
admin.site.register(Compensation, CompensationAdmin) admin.site.register(Compensation, CompensationAdmin)
admin.site.register(Payment, PaymentAdmin)
admin.site.register(CompensationAction, CompensationActionAdmin)
admin.site.register(CompensationState, CompensationStateAdmin)
admin.site.register(EcoAccount, EcoAccountAdmin) admin.site.register(EcoAccount, EcoAccountAdmin)
admin.site.register(EcoAccountDeduction, EcoAccountDeductionAdmin) admin.site.register(EcoAccountDeduction, EcoAccountDeductionAdmin)
# For a more cleaner admin interface these rarely used admin views are not important for deployment
#admin.site.register(Payment, PaymentAdmin)
#admin.site.register(CompensationAction, CompensationActionAdmin)
#admin.site.register(CompensationState, CompensationStateAdmin)

View File

@ -14,7 +14,8 @@ from django.shortcuts import render
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 codelist.models import KonovaCode from codelist.models import KonovaCode
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
CODELIST_COMPENSATION_ACTION_DETAIL_ID
from compensation.models import CompensationDocument, EcoAccountDocument from compensation.models import CompensationDocument, EcoAccountDocument
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.forms import BaseModalForm, NewDocumentForm from konova.forms import BaseModalForm, NewDocumentForm
@ -127,6 +128,23 @@ class NewStateModalForm(BaseModalForm):
} }
), ),
) )
biotope_extra = forms.ModelMultipleChoiceField(
label=_("Biotope additional type"),
label_suffix="",
required=False,
help_text=_("Select an additional biotope type"),
queryset=KonovaCode.objects.filter(
is_archived=False,
is_leaf=True,
code_lists__in=[CODELIST_BIOTOPES_EXTRA_CODES_ID],
),
widget=autocomplete.ModelSelect2Multiple(
url="codes-biotope-extra-type-autocomplete",
attrs={
"data-placeholder": _("Biotope additional type"),
}
),
)
surface = forms.DecimalField( surface = forms.DecimalField(
min_value=0.00, min_value=0.00,
decimal_places=2, decimal_places=2,
@ -283,6 +301,23 @@ class NewActionModalForm(BaseModalForm):
} }
), ),
) )
action_type_details = forms.ModelMultipleChoiceField(
label=_("Action Type detail"),
label_suffix="",
required=False,
help_text=_("Select the action type detail"),
queryset=KonovaCode.objects.filter(
is_archived=False,
is_leaf=True,
code_lists__in=[CODELIST_COMPENSATION_ACTION_DETAIL_ID],
),
widget=autocomplete.ModelSelect2Multiple(
url="codes-compensation-action-detail-autocomplete",
attrs={
"data-placeholder": _("Action Type detail"),
}
),
)
unit = forms.ChoiceField( unit = forms.ChoiceField(
label=_("Unit"), label=_("Unit"),
label_suffix="", label_suffix="",

View File

@ -0,0 +1,115 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
import django.core.validators
from django.db import migrations, models
import konova.models.document
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Compensation',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('identifier', models.CharField(blank=True, max_length=1000, null=True)),
('title', models.CharField(blank=True, max_length=1000, null=True)),
('comment', models.TextField(blank=True, null=True)),
('is_cef', models.BooleanField(blank=True, default=False, help_text="Flag if compensation is a 'CEF-Maßnahme'", null=True)),
('is_coherence_keeping', models.BooleanField(blank=True, default=False, help_text="Flag if compensation is a 'Kohärenzsicherung'", null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='CompensationAction',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('amount', models.FloatField()),
('unit', models.CharField(blank=True, choices=[('cm', 'cm'), ('m', 'm'), ('km', 'km'), ('qm', ''), ('ha', 'ha'), ('pcs', 'Pieces')], max_length=100, null=True)),
('comment', models.TextField(blank=True, help_text='Additional comment', null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='CompensationDocument',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('title', models.CharField(blank=True, max_length=500, null=True)),
('date_of_creation', models.DateField()),
('comment', models.TextField()),
('file', models.FileField(max_length=1000, upload_to=konova.models.document.generate_document_file_upload_path)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='CompensationState',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('surface', models.FloatField()),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='EcoAccount',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('identifier', models.CharField(blank=True, max_length=1000, null=True)),
('title', models.CharField(blank=True, max_length=1000, null=True)),
('comment', models.TextField(blank=True, null=True)),
('access_token', models.CharField(blank=True, help_text='Used for sharing access', max_length=255, null=True)),
('deductable_surface', models.FloatField(blank=True, default=0, help_text='Amount of deductable surface - can be lower than the total surface due to deduction limitations', null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='EcoAccountDeduction',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('surface', models.FloatField(blank=True, help_text='Amount deducted (m²)', null=True, validators=[django.core.validators.MinValueValidator(limit_value=0.0)])),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='EcoAccountDocument',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('title', models.CharField(blank=True, max_length=500, null=True)),
('date_of_creation', models.DateField()),
('comment', models.TextField()),
('file', models.FileField(max_length=1000, upload_to=konova.models.document.generate_document_file_upload_path)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Payment',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('amount', models.FloatField(validators=[django.core.validators.MinValueValidator(limit_value=0.0)])),
('due_on', models.DateField(blank=True, null=True)),
('comment', models.TextField(blank=True, help_text="Refers to german money transfer 'Verwendungszweck'", null=True)),
],
options={
'ordering': ['-amount'],
},
),
]

View File

@ -0,0 +1,227 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('intervention', '0001_initial'),
('konova', '0001_initial'),
('compensation', '0001_initial'),
('user', '0001_initial'),
('codelist', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='payment',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='payment',
name='intervention',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='intervention.intervention'),
),
migrations.AddField(
model_name='payment',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccountdocument',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccountdocument',
name='instance',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='compensation.ecoaccount'),
),
migrations.AddField(
model_name='ecoaccountdocument',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccountdeduction',
name='account',
field=models.ForeignKey(blank=True, help_text='Deducted from', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='deductions', to='compensation.ecoaccount'),
),
migrations.AddField(
model_name='ecoaccountdeduction',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccountdeduction',
name='intervention',
field=models.ForeignKey(blank=True, help_text='Deducted for', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='deductions', to='intervention.intervention'),
),
migrations.AddField(
model_name='ecoaccountdeduction',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccount',
name='actions',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Maßnahmen'", to='compensation.CompensationAction'),
),
migrations.AddField(
model_name='ecoaccount',
name='after_states',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Zielzustand Biotop'", related_name='_ecoaccount_after_states_+', to='compensation.CompensationState'),
),
migrations.AddField(
model_name='ecoaccount',
name='before_states',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Ausgangszustand Biotop'", related_name='_ecoaccount_before_states_+', to='compensation.CompensationState'),
),
migrations.AddField(
model_name='ecoaccount',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccount',
name='deadlines',
field=models.ManyToManyField(blank=True, related_name='_ecoaccount_deadlines_+', to='konova.Deadline'),
),
migrations.AddField(
model_name='ecoaccount',
name='deleted',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccount',
name='geometry',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.geometry'),
),
migrations.AddField(
model_name='ecoaccount',
name='legal',
field=models.OneToOneField(blank=True, help_text='Holds data on legal dates or law', null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.legal'),
),
migrations.AddField(
model_name='ecoaccount',
name='log',
field=models.ManyToManyField(blank=True, editable=False, help_text='Keeps all user actions of an object', to='user.UserActionLogEntry'),
),
migrations.AddField(
model_name='ecoaccount',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccount',
name='recorded',
field=models.OneToOneField(blank=True, help_text='Holds data on user and timestamp of this action', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ecoaccount',
name='responsible',
field=models.OneToOneField(blank=True, help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle') and handler", null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.responsibility'),
),
migrations.AddField(
model_name='ecoaccount',
name='users',
field=models.ManyToManyField(help_text='Users having access (data shared with)', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='compensationstate',
name='biotope_type',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [974], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, to='codelist.konovacode'),
),
migrations.AddField(
model_name='compensationdocument',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensationdocument',
name='instance',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='compensation.compensation'),
),
migrations.AddField(
model_name='compensationdocument',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensationaction',
name='action_type',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [1026], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, to='codelist.konovacode'),
),
migrations.AddField(
model_name='compensationaction',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensationaction',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensation',
name='actions',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Maßnahmen'", to='compensation.CompensationAction'),
),
migrations.AddField(
model_name='compensation',
name='after_states',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Zielzustand Biotop'", related_name='_compensation_after_states_+', to='compensation.CompensationState'),
),
migrations.AddField(
model_name='compensation',
name='before_states',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Ausgangszustand Biotop'", related_name='_compensation_before_states_+', to='compensation.CompensationState'),
),
migrations.AddField(
model_name='compensation',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensation',
name='deadlines',
field=models.ManyToManyField(blank=True, related_name='_compensation_deadlines_+', to='konova.Deadline'),
),
migrations.AddField(
model_name='compensation',
name='deleted',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensation',
name='geometry',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.geometry'),
),
migrations.AddField(
model_name='compensation',
name='intervention',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='compensations', to='intervention.intervention'),
),
migrations.AddField(
model_name='compensation',
name='log',
field=models.ManyToManyField(blank=True, editable=False, help_text='Keeps all user actions of an object', to='user.UserActionLogEntry'),
),
migrations.AddField(
model_name='compensation',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='compensation',
name='responsible',
field=models.OneToOneField(blank=True, help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle') and handler", null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.responsibility'),
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 3.1.3 on 2022-02-02 07:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('codelist', '0001_initial'),
('compensation', '0002_auto_20220114_0936'),
]
operations = [
migrations.AddField(
model_name='compensationaction',
name='action_type_details',
field=models.ManyToManyField(blank=True, limit_choices_to={'code_lists__in': [1035], 'is_archived': False, 'is_selectable': True}, related_name='_compensationaction_action_type_details_+', to='codelist.KonovaCode'),
),
migrations.AddField(
model_name='compensationstate',
name='biotope_type_details',
field=models.ManyToManyField(blank=True, limit_choices_to={'code_lists__in': [975], 'is_archived': False, 'is_selectable': True}, related_name='_compensationstate_biotope_type_details_+', to='codelist.KonovaCode'),
),
migrations.AlterField(
model_name='compensationaction',
name='action_type',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [1026], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='codelist.konovacode'),
),
migrations.AlterField(
model_name='compensationstate',
name='biotope_type',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [974], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='codelist.konovacode'),
),
]

View File

View File

@ -9,7 +9,7 @@ from django.db import models
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 from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
from compensation.managers import CompensationActionManager from compensation.managers import CompensationActionManager
from konova.models import BaseResource from konova.models import BaseResource
@ -39,7 +39,18 @@ class CompensationAction(BaseResource):
"code_lists__in": [CODELIST_COMPENSATION_ACTION_ID], "code_lists__in": [CODELIST_COMPENSATION_ACTION_ID],
"is_selectable": True, "is_selectable": True,
"is_archived": False, "is_archived": False,
} },
related_name='+',
)
action_type_details = models.ManyToManyField(
KonovaCode,
blank=True,
limit_choices_to={
"code_lists__in": [CODELIST_COMPENSATION_ACTION_DETAIL_ID],
"is_selectable": True,
"is_archived": False,
},
related_name='+',
) )
amount = models.FloatField() amount = models.FloatField()
unit = models.CharField(max_length=100, null=True, blank=True, choices=UnitChoices.choices) unit = models.CharField(max_length=100, null=True, blank=True, choices=UnitChoices.choices)
@ -48,7 +59,7 @@ class CompensationAction(BaseResource):
objects = CompensationActionManager() objects = CompensationActionManager()
def __str__(self): def __str__(self):
return "{} | {} {}".format(self.action_type, self.amount, self.unit) return f"{self.action_type} | {self.amount} {self.unit}"
@property @property
def unit_humanize(self): def unit_humanize(self):

View File

@ -95,6 +95,8 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
comment=form_data["comment"], comment=form_data["comment"],
created=user_action, created=user_action,
) )
comp_action_details = form_data["action_type_details"]
comp_action.action_type_details.set(comp_action_details)
self.actions.add(comp_action) self.actions.add(comp_action)
return comp_action return comp_action
@ -114,6 +116,8 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
biotope_type=form_data["biotope_type"], biotope_type=form_data["biotope_type"],
surface=form_data["surface"], surface=form_data["surface"],
) )
state_additional_types = form_data["biotope_extra"]
state.biotope_type_details.set(state_additional_types)
if is_before_state: if is_before_state:
self.before_states.add(state) self.before_states.add(state)
else: else:
@ -310,17 +314,19 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
) )
return docs return docs
def mark_as_edited(self, user: User, request: HttpRequest = None, edit_comment: str = None): def mark_as_edited(self, user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True):
""" Performs internal logic for setting the recordedd/checked state of the related intervention """ Performs internal logic for setting the recordedd/checked state of the related intervention
Args: Args:
user (User): The performing user user (User): The performing user
request (HttpRequest): The performing request request (HttpRequest): The performing request
edit_comment (str): Additional comment for the log entry
reset_recorded (bool): Whether the record-state of the object should be reset
Returns: Returns:
""" """
self.intervention.mark_as_edited(user, request, edit_comment) self.intervention.mark_as_edited(user, request, edit_comment, reset_recorded)
def is_ready_for_publish(self) -> bool: def is_ready_for_publish(self) -> bool:
""" Not inherited by RecordableObjectMixin """ Not inherited by RecordableObjectMixin

View File

@ -46,7 +46,7 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
objects = EcoAccountManager() objects = EcoAccountManager()
def __str__(self): def __str__(self):
return "{}".format(self.identifier) return f"{self.identifier} ({self.title})"
def clean(self): def clean(self):
# Deductable surface can not be larger than added states after surface # Deductable surface can not be larger than added states after surface

View File

@ -8,7 +8,7 @@ Created on: 16.11.21
from django.db import models from django.db import models
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_ID
from compensation.managers import CompensationStateManager from compensation.managers import CompensationStateManager
from konova.models import UuidModel from konova.models import UuidModel
@ -26,11 +26,22 @@ class CompensationState(UuidModel):
"code_lists__in": [CODELIST_BIOTOPES_ID], "code_lists__in": [CODELIST_BIOTOPES_ID],
"is_selectable": True, "is_selectable": True,
"is_archived": False, "is_archived": False,
} },
related_name='+',
)
biotope_type_details = models.ManyToManyField(
KonovaCode,
blank=True,
limit_choices_to={
"code_lists__in": [CODELIST_BIOTOPES_EXTRA_CODES_ID],
"is_selectable": True,
"is_archived": False,
},
related_name='+',
) )
surface = models.FloatField() surface = models.FloatField()
objects = CompensationStateManager() objects = CompensationStateManager()
def __str__(self): def __str__(self):
return "{} | {}".format(self.biotope_type, self.surface) return f"{self.biotope_type} | {self.surface}"

View File

@ -24,9 +24,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Action type' %} {% trans 'Action type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Action type details' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Amount' context 'Compensation' %} {% trans 'Amount' context 'Compensation' %}
</th> </th>
@ -46,9 +49,14 @@
<td class="align-middle"> <td class="align-middle">
{{ action.action_type }} {{ action.action_type }}
</td> </td>
<td class="align-middle">
{% for detail in action.action_type_details.all %}
<div class="mb-2" title="{{detail}}">{{detail.long_name}}</div>
{% endfor %}
</td>
<td class="align-middle">{{ action.amount|floatformat:2|intcomma }} {{ action.unit_humanize }}</td> <td class="align-middle">{{ action.amount|floatformat:2|intcomma }} {{ action.unit_humanize }}</td>
<td class="align-middle">{{ action.comment|default_if_none:"" }}</td> <td class="align-middle">{{ action.comment|default_if_none:"" }}</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}"> <button data-form-url="{% url 'compensation:action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -29,9 +29,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Biotope type' %} {% trans 'Biotope type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Biotope additional type' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
@ -48,8 +51,15 @@
<td class="align-middle"> <td class="align-middle">
{{ state.biotope_type }} {{ state.biotope_type }}
</td> </td>
<td class="align-middle">
{% for biotope_extra in state.biotope_type_details.all %}
<div class="mb-2" title="{{ biotope_extra }}">
{{ biotope_extra.long_name }}
</div>
{% endfor %}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td> <td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}"> <button data-form-url="{% url 'compensation:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -29,9 +29,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Biotope type' %} {% trans 'Biotope type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Biotope additional type' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
@ -48,8 +51,15 @@
<td class="align-middle"> <td class="align-middle">
{{ state.biotope_type }} {{ state.biotope_type }}
</td> </td>
<td class="align-middle">
{% for biotope_extra in state.biotope_type_details.all %}
<div class="mb-2" title="{{ biotope_extra }}">
{{ biotope_extra.long_name }}
</div>
{% endfor %}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td> <td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}"> <button data-form-url="{% url 'compensation:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -24,9 +24,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Action type' %} {% trans 'Action type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Action type details' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Amount' context 'Compensation' %} {% trans 'Amount' context 'Compensation' %}
</th> </th>
@ -46,9 +49,14 @@
<td class="align-middle"> <td class="align-middle">
{{ action.action_type }} {{ action.action_type }}
</td> </td>
<td class="align-middle">
{% for detail in action.action_type_details.all %}
<div class="mb-2" title="{{detail}}">{{detail.long_name}}</div>
{% endfor %}
</td>
<td class="align-middle">{{ action.amount|floatformat:2|intcomma }} {{ action.unit_humanize }}</td> <td class="align-middle">{{ action.amount|floatformat:2|intcomma }} {{ action.unit_humanize }}</td>
<td class="align-middle">{{ action.comment|default_if_none:"" }}</td> <td class="align-middle">{{ action.comment|default_if_none:"" }}</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:acc-action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}"> <button data-form-url="{% url 'compensation:acc-action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -29,9 +29,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Biotope type' %} {% trans 'Biotope type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Biotope additional type' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
@ -48,8 +51,15 @@
<td class="align-middle"> <td class="align-middle">
{{ state.biotope_type }} {{ state.biotope_type }}
</td> </td>
<td class="align-middle">
{% for biotope_extra in state.biotope_type_details.all %}
<div class="mb-2" title="{{ biotope_extra }}">
{{ biotope_extra.long_name }}
</div>
{% endfor %}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td> <td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:acc-state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}"> <button data-form-url="{% url 'compensation:acc-state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -29,9 +29,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Biotope type' %} {% trans 'Biotope type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Biotope additional type' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
@ -48,8 +51,15 @@
<td class="align-middle"> <td class="align-middle">
{{ state.biotope_type }} {{ state.biotope_type }}
</td> </td>
<td class="align-middle">
{% for biotope_extra in state.biotope_type_details.all %}
<div class="mb-2" title="{{ biotope_extra }}">
{{ biotope_extra.long_name }}
</div>
{% endfor %}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td> <td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:acc-state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}"> <button data-form-url="{% url 'compensation:acc-state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -1,10 +1,17 @@
from django.contrib import admin from django.contrib import admin
from compensation.admin import CompensationAdmin from compensation.admin import AbstractCompensationAdmin
from ema.models import Ema from ema.models import Ema
class EmaAdmin(CompensationAdmin): class EmaAdmin(AbstractCompensationAdmin):
pass filter_horizontal = [
"users"
]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + [
"users"
]
admin.site.register(Ema, EmaAdmin) admin.site.register(Ema, EmaAdmin)

View File

@ -0,0 +1,42 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.db import migrations, models
import konova.models.document
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Ema',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('identifier', models.CharField(blank=True, max_length=1000, null=True)),
('title', models.CharField(blank=True, max_length=1000, null=True)),
('comment', models.TextField(blank=True, null=True)),
('access_token', models.CharField(blank=True, help_text='Used for sharing access', max_length=255, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='EmaDocument',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('title', models.CharField(blank=True, max_length=500, null=True)),
('date_of_creation', models.DateField()),
('comment', models.TextField()),
('file', models.FileField(max_length=1000, upload_to=konova.models.document.generate_document_file_upload_path)),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,97 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('intervention', '0001_initial'),
('konova', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('compensation', '0002_auto_20220114_0936'),
('user', '0001_initial'),
('ema', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='emadocument',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='emadocument',
name='instance',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='ema.ema'),
),
migrations.AddField(
model_name='emadocument',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ema',
name='actions',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Maßnahmen'", to='compensation.CompensationAction'),
),
migrations.AddField(
model_name='ema',
name='after_states',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Zielzustand Biotop'", related_name='_ema_after_states_+', to='compensation.CompensationState'),
),
migrations.AddField(
model_name='ema',
name='before_states',
field=models.ManyToManyField(blank=True, help_text="Refers to 'Ausgangszustand Biotop'", related_name='_ema_before_states_+', to='compensation.CompensationState'),
),
migrations.AddField(
model_name='ema',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ema',
name='deadlines',
field=models.ManyToManyField(blank=True, related_name='_ema_deadlines_+', to='konova.Deadline'),
),
migrations.AddField(
model_name='ema',
name='deleted',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ema',
name='geometry',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.geometry'),
),
migrations.AddField(
model_name='ema',
name='log',
field=models.ManyToManyField(blank=True, editable=False, help_text='Keeps all user actions of an object', to='user.UserActionLogEntry'),
),
migrations.AddField(
model_name='ema',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ema',
name='recorded',
field=models.OneToOneField(blank=True, help_text='Holds data on user and timestamp of this action', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='ema',
name='responsible',
field=models.OneToOneField(blank=True, help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle') and handler", null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.responsibility'),
),
migrations.AddField(
model_name='ema',
name='users',
field=models.ManyToManyField(help_text='Users having access (data shared with)', to=settings.AUTH_USER_MODEL),
),
]

View File

View File

@ -24,9 +24,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Action type' %} {% trans 'Action type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Action type details' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Amount' context 'Compensation' %} {% trans 'Amount' context 'Compensation' %}
</th> </th>
@ -44,9 +47,14 @@
<td class="align-middle"> <td class="align-middle">
{{ action.action_type }} {{ action.action_type }}
</td> </td>
<td class="align-middle">
{% for detail in action.action_type_details.all %}
<div class="mb-2" title="{{detail}}">{{detail.long_name}}</div>
{% endfor %}
</td>
<td class="align-middle">{{ action.amount|floatformat:2|intcomma }} {{ action.unit_humanize }}</td> <td class="align-middle">{{ action.amount|floatformat:2|intcomma }} {{ action.unit_humanize }}</td>
<td class="align-middle">{{ action.comment|default_if_none:"" }}</td> <td class="align-middle">{{ action.comment|default_if_none:"" }}</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}"> <button data-form-url="{% url 'ema:action-remove' obj.id action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -29,9 +29,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Biotope type' %} {% trans 'Biotope type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Biotope additional type' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
@ -46,8 +49,15 @@
<td class="align-middle"> <td class="align-middle">
{{ state.biotope_type }} {{ state.biotope_type }}
</td> </td>
<td class="align-middle">
{% for biotope_extra in state.biotope_type_details.all %}
<div class="mb-2" title="{{ biotope_extra }}">
{{ biotope_extra.long_name }}
</div>
{% endfor %}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td> <td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}"> <button data-form-url="{% url 'ema:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -29,9 +29,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"> <th class="w-25" scope="col">
{% trans 'Biotope type' %} {% trans 'Biotope type' %}
</th> </th>
<th class="w-25" scope="col">
{% trans 'Biotope additional type' %}
</th>
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
@ -46,8 +49,15 @@
<td class="align-middle"> <td class="align-middle">
{{ state.biotope_type }} {{ state.biotope_type }}
</td> </td>
<td class="align-middle">
{% for biotope_extra in state.biotope_type_details.all %}
<div class="mb-2" title="{{ biotope_extra }}">
{{ biotope_extra.long_name }}
</div>
{% endfor %}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td> <td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td> <td class="align-middle">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}"> <button data-form-url="{% url 'ema:state-remove' obj.id state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}

View File

@ -13,6 +13,26 @@ class InterventionAdmin(BaseObjectAdmin):
"deleted", "deleted",
] ]
filter_horizontal = [
"users"
]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + [
"identifier",
"title",
"comment",
"checked",
"recorded",
"users",
]
def get_readonly_fields(self, request, obj=None):
return super().get_readonly_fields(request, obj) + [
"checked",
"recorded",
]
class InterventionDocumentAdmin(AbstractDocumentAdmin): class InterventionDocumentAdmin(AbstractDocumentAdmin):
pass pass
@ -48,7 +68,9 @@ class RevocationAdmin(admin.ModelAdmin):
admin.site.register(Intervention, InterventionAdmin) admin.site.register(Intervention, InterventionAdmin)
admin.site.register(Responsibility, ResponsibilityAdmin)
admin.site.register(Legal, LegalAdmin) # Outcommented for a cleaner admin backend on production
admin.site.register(Revocation, RevocationAdmin) #admin.site.register(Responsibility, ResponsibilityAdmin)
admin.site.register(InterventionDocument, InterventionDocumentAdmin) #admin.site.register(Legal, LegalAdmin)
#admin.site.register(Revocation, RevocationAdmin)
#admin.site.register(InterventionDocument, InterventionDocumentAdmin)

View File

@ -370,7 +370,7 @@ class NewDeductionModalForm(BaseModalForm):
def save(self): def save(self):
deduction = self.instance.add_deduction(self) deduction = self.instance.add_deduction(self)
self.instance.mark_as_edited(self.user, self.request) self.instance.mark_as_edited(self.user, self.request, reset_recorded=False)
return deduction return deduction

View File

@ -0,0 +1,89 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.db import migrations, models
import konova.models.document
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Intervention',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('identifier', models.CharField(blank=True, max_length=1000, null=True)),
('title', models.CharField(blank=True, max_length=1000, null=True)),
('comment', models.TextField(blank=True, null=True)),
('access_token', models.CharField(blank=True, help_text='Used for sharing access', max_length=255, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='InterventionDocument',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('title', models.CharField(blank=True, max_length=500, null=True)),
('date_of_creation', models.DateField()),
('comment', models.TextField()),
('file', models.FileField(max_length=1000, upload_to=konova.models.document.generate_document_file_upload_path)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Legal',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('registration_date', models.DateField(blank=True, help_text="Refers to 'Zugelassen am'", null=True)),
('binding_date', models.DateField(blank=True, help_text="Refers to 'Bestandskraft am'", null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Responsibility',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('registration_file_number', models.CharField(blank=True, max_length=1000, null=True)),
('conservation_file_number', models.CharField(blank=True, max_length=1000, null=True)),
('handler', models.CharField(blank=True, help_text="Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'", max_length=500, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Revocation',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('date', models.DateField(blank=True, help_text='Revocation from', null=True)),
('comment', models.TextField(blank=True, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='RevocationDocument',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('title', models.CharField(blank=True, max_length=500, null=True)),
('date_of_creation', models.DateField()),
('comment', models.TextField()),
('file', models.FileField(max_length=1000, upload_to=konova.models.document.generate_document_file_upload_path)),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,136 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('intervention', '0001_initial'),
('konova', '0001_initial'),
('user', '0001_initial'),
('codelist', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='revocationdocument',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='revocationdocument',
name='instance',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='document', to='intervention.revocation'),
),
migrations.AddField(
model_name='revocationdocument',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='revocation',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='revocation',
name='legal',
field=models.ForeignKey(help_text="Refers to 'Widerspruch am'", on_delete=django.db.models.deletion.CASCADE, related_name='revocations', to='intervention.legal'),
),
migrations.AddField(
model_name='revocation',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='responsibility',
name='conservation_office',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [907], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='codelist.konovacode'),
),
migrations.AddField(
model_name='responsibility',
name='registration_office',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [1053], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='codelist.konovacode'),
),
migrations.AddField(
model_name='legal',
name='laws',
field=models.ManyToManyField(blank=True, limit_choices_to={'code_lists__in': [1048], 'is_archived': False, 'is_selectable': True}, to='codelist.KonovaCode'),
),
migrations.AddField(
model_name='legal',
name='process_type',
field=models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [44382], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='codelist.konovacode'),
),
migrations.AddField(
model_name='interventiondocument',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='interventiondocument',
name='instance',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='intervention.intervention'),
),
migrations.AddField(
model_name='interventiondocument',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='intervention',
name='checked',
field=models.OneToOneField(blank=True, help_text='Holds data on user and timestamp of this action', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='intervention',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='intervention',
name='deleted',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='intervention',
name='geometry',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.geometry'),
),
migrations.AddField(
model_name='intervention',
name='legal',
field=models.OneToOneField(blank=True, help_text='Holds data on legal dates or law', null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.legal'),
),
migrations.AddField(
model_name='intervention',
name='log',
field=models.ManyToManyField(blank=True, editable=False, help_text='Keeps all user actions of an object', to='user.UserActionLogEntry'),
),
migrations.AddField(
model_name='intervention',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='intervention',
name='recorded',
field=models.OneToOneField(blank=True, help_text='Holds data on user and timestamp of this action', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='intervention',
name='responsible',
field=models.OneToOneField(blank=True, help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle')", null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.responsibility'),
),
migrations.AddField(
model_name='intervention',
name='users',
field=models.ManyToManyField(help_text='Users having access (data shared with)', to=settings.AUTH_USER_MODEL),
),
]

View File

View File

@ -51,7 +51,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
objects = InterventionManager() objects = InterventionManager()
def __str__(self): def __str__(self):
return "{} ({})".format(self.identifier, self.title) return f"{self.identifier} ({self.title})"
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
""" Custom save functionality """ Custom save functionality
@ -250,17 +250,20 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
) )
return deduction return deduction
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None): def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True):
""" In case the object or a related object changed, internal processes need to be started, such as """ In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck unrecord and uncheck
Args: Args:
performing_user (User): The user which performed the editing action performing_user (User): The user which performed the editing action
request (HttpRequest): The used request for this action
edit_comment (str): Additional comment for the log entry
reset_recorded (bool): Whether the record-state of the object should be reset
Returns: Returns:
""" """
super().mark_as_edited(performing_user, request) super().mark_as_edited(performing_user, request, edit_comment, reset_recorded)
if self.checked: if self.checked:
self.set_unchecked() self.set_unchecked()

View File

@ -62,16 +62,35 @@ class DeadlineAdmin(admin.ModelAdmin):
] ]
class BaseObjectAdmin(admin.ModelAdmin): class BaseResourceAdmin(admin.ModelAdmin):
fields = [
"created",
"modified",
]
readonly_fields = [ readonly_fields = [
"modified", "modified",
"deleted",
"created", "created",
] ]
admin.site.register(Geometry, GeometryAdmin) class BaseObjectAdmin(BaseResourceAdmin):
admin.site.register(Parcel, ParcelAdmin) search_fields = [
admin.site.register(District, DistrictAdmin) "identifier",
admin.site.register(GeometryConflict, GeometryConflictAdmin) "title",
admin.site.register(Deadline, DeadlineAdmin) ]
def get_fields(self, request, obj=None):
return super().get_fields(request, obj) + ["deleted"]
def get_readonly_fields(self, request, obj=None):
return super().get_readonly_fields(request, obj) + [
"deleted",
]
# Outcommented for a cleaner admin backend on production
#admin.site.register(Geometry, GeometryAdmin)
#admin.site.register(Parcel, ParcelAdmin)
#admin.site.register(District, DistrictAdmin)
#admin.site.register(GeometryConflict, GeometryConflictAdmin)
#admin.site.register(Deadline, DeadlineAdmin)

View File

@ -11,7 +11,8 @@ from django.db.models import Q
from codelist.models import KonovaCode from codelist.models import KonovaCode
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, \ from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, \
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_PROCESS_TYPE_ID CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_PROCESS_TYPE_ID, \
CODELIST_BIOTOPES_EXTRA_CODES_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
from compensation.models import EcoAccount from compensation.models import EcoAccount
from intervention.models import Intervention from intervention.models import Intervention
@ -94,6 +95,7 @@ class KonovaCodeAutocomplete(Select2GroupQuerySetView):
* c: Search inside a special codelist * c: Search inside a special codelist
""" """
paginate_by = 50
def order_by(self, qs): def order_by(self, qs):
""" Orders by a predefined value """ Orders by a predefined value
@ -162,6 +164,24 @@ class CompensationActionCodeAutocomplete(KonovaCodeAutocomplete):
) )
class CompensationActionDetailCodeAutocomplete(KonovaCodeAutocomplete):
"""
Due to limitations of the django dal package, we need to subclass for each code list
"""
group_by_related = "parent"
related_field_name = "long_name"
paginate_by = 200
def __init__(self, *args, **kwargs):
self.c = CODELIST_COMPENSATION_ACTION_DETAIL_ID
super().__init__(*args, **kwargs)
def order_by(self, qs):
return qs.order_by(
"parent__long_name"
)
class BiotopeCodeAutocomplete(KonovaCodeAutocomplete): class BiotopeCodeAutocomplete(KonovaCodeAutocomplete):
""" """
Due to limitations of the django dal package, we need to subclass for each code list Due to limitations of the django dal package, we need to subclass for each code list
@ -192,6 +212,37 @@ class BiotopeCodeAutocomplete(KonovaCodeAutocomplete):
return f"{result.long_name} ({result.short_name})" return f"{result.long_name} ({result.short_name})"
class BiotopeExtraCodeAutocomplete(KonovaCodeAutocomplete):
"""
Due to limitations of the django dal package, we need to subclass for each code list
"""
group_by_related = "parent"
related_field_name = "long_name"
paginate_by = 200
def __init__(self, *args, **kwargs):
self.c = CODELIST_BIOTOPES_EXTRA_CODES_ID
super().__init__(*args, **kwargs)
def order_by(self, qs):
""" Orders by a predefined value
Wrapped in a function to provide inheritance-based different orders
Args:
qs (QuerySet): The queryset to be ordered
Returns:
qs (QuerySet): The ordered queryset
"""
return qs.order_by(
"parent__long_name",
)
def get_result_label(self, result):
return f"{result.long_name} ({result.short_name})"
class LawCodeAutocomplete(KonovaCodeAutocomplete): class LawCodeAutocomplete(KonovaCodeAutocomplete):
""" """
Due to limitations of the django dal package, we need to subclass for each code list Due to limitations of the django dal package, we need to subclass for each code list

View File

@ -0,0 +1,78 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
import django.contrib.gis.db.models.fields
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Deadline',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('type', models.CharField(blank=True, choices=[('finished', 'Finished'), ('maintain', 'Maintain'), ('control', 'Control'), ('other', 'Other')], max_length=255, null=True)),
('date', models.DateField(blank=True, null=True)),
('comment', models.TextField(blank=True, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='District',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('gmnd', models.CharField(blank=True, help_text='Gemeinde', max_length=1000, null=True)),
('krs', models.CharField(blank=True, help_text='Kreis', max_length=1000, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Geometry',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('geom', django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Parcel',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('gmrkng', models.CharField(blank=True, help_text='Gemarkung', max_length=1000, null=True)),
('flrstck_nnr', models.CharField(blank=True, help_text='Flurstücksnenner', max_length=1000, null=True)),
('flrstck_zhlr', models.CharField(blank=True, help_text='Flurstückszähler', max_length=1000, null=True)),
('flr', models.CharField(blank=True, help_text='Flur', max_length=1000, null=True)),
('updated_on', models.DateTimeField(auto_now_add=True)),
('district', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parcels', to='konova.district')),
('geometries', models.ManyToManyField(blank=True, related_name='parcels', to='konova.Geometry')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='GeometryConflict',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('detected_on', models.DateTimeField(auto_now_add=True, null=True)),
('affected_geometry', models.ForeignKey(help_text='The geometry which came first', on_delete=django.db.models.deletion.CASCADE, related_name='conflicted_by_geometries', to='konova.geometry')),
('conflicting_geometry', models.ForeignKey(help_text='The geometry which came second', on_delete=django.db.models.deletion.CASCADE, related_name='conflicts_geometries', to='konova.geometry')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('user', '0001_initial'),
('konova', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='geometry',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='geometry',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='deadline',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='deadline',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
]

View File

View File

@ -262,12 +262,15 @@ class RecordableObjectMixin(models.Model):
return action return action
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None): def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True):
""" In case the object or a related object changed, internal processes need to be started, such as """ In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck unrecord and uncheck
Args: Args:
performing_user (User): The user which performed the editing action performing_user (User): The user which performed the editing action
request (HttpRequest): The used request for this action
edit_comment (str): Additional comment for the log entry
reset_recorded (bool): Whether the record-state of the object should be reset
Returns: Returns:
@ -277,7 +280,7 @@ class RecordableObjectMixin(models.Model):
self.log.add(action) self.log.add(action)
self.save() self.save()
if self.recorded: if self.recorded and reset_recorded:
action = self.set_unrecorded(performing_user) action = self.set_unrecorded(performing_user)
self.log.add(action) self.log.add(action)
if request: if request:

View File

@ -11,14 +11,15 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
""" """
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Load other settings # Load sub settings
# If new settings need to be added as general settings to the whole project, outsource them into new xy_settings files!
from konova.sub_settings.django_settings import * from konova.sub_settings.django_settings import *
from konova.sub_settings.proxy_settings import *
from konova.sub_settings.sso_settings import *
from konova.sub_settings.table_settings import *
from konova.sub_settings.lanis_settings import *
from konova.sub_settings.wfs_parcel_settings import *
proxy = "CHANGE_ME"
PROXIES = {
"http": proxy,
"https": proxy,
}
# ALLOWED FILE UPLOAD DEFINITIONS # ALLOWED FILE UPLOAD DEFINITIONS
# Default: Upload into upper folder of code # Default: Upload into upper folder of code
@ -37,57 +38,7 @@ EMA_DOC_PATH = BASE_DOC_PATH + "ema/{}/"
# German DateTime string format # German DateTime string format
STRF_DATE_TIME = "%d.%m.%Y %H:%M:%S" STRF_DATE_TIME = "%d.%m.%Y %H:%M:%S"
# Tables
RESULTS_PER_PAGE_PARAM = "rpp"
PAGE_PARAM = "page"
PAGE_SIZE_OPTIONS = [5, 10, 15, 20, 25, 50, 100]
PAGE_SIZE_OPTIONS_TUPLES = [
(5, 5),
(10, 10),
(15, 15),
(20, 20),
(25, 25),
(50, 50),
(100, 100),
]
PAGE_SIZE_DEFAULT = 5
PAGE_SIZE_MAX = 100
PAGE_DEFAULT = 1
# SSO settings
SSO_SERVER_BASE = f"http://{os.environ.get('SSO_HOST')}/"
SSO_SERVER = f"{SSO_SERVER_BASE}sso/"
SSO_PRIVATE_KEY = "CHANGE_ME"
SSO_PUBLIC_KEY = "CHANGE_ME"
# MAPS
DEFAULT_LAT = 50.00
DEFAULT_LON = 7.00
DEFAULT_ZOOM = 8.0
DEFAULT_SRID = 4326
DEFAULT_SRID_RLP = 25832
# GROUPS # GROUPS
DEFAULT_GROUP = "Default" DEFAULT_GROUP = "Default"
ZB_GROUP = "Registration office" ZB_GROUP = "Registration office"
ETS_GROUP = "Conservation office" ETS_GROUP = "Conservation office"
# Needed to redirect to LANIS
## 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"
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
LANIS_ZOOM_LUT = {
1000000000: 6,
100000000: 10,
10000000: 17,
1000000: 20,
100000: 25,
10000: 28,
1000: 30,
500: 31,
}
# Parcel WFS settings
PARCEL_WFS_BASE_URL = "https://www.geoportal.rlp.de/registry/wfs/519"
PARCEL_WFS_USER = "ksp"
PARCEL_WFS_PW = "CHANGE_ME"

View File

@ -121,18 +121,12 @@ a {
.card{ .card{
margin: 0 0.5rem 0.5rem 0; margin: 0 0.5rem 0.5rem 0;
font-size: 12px; font-size: 0.9rem;
} }
.card:hover{ .card:hover{
box-shadow: 1px 1px 3px var(--rlp-gray-light); box-shadow: 1px 1px 3px var(--rlp-gray-light);
} }
.card .card-text{
font-size: 12px;
max-height: 150px;
overflow: auto;
}
.qs-box{ .qs-box{
background-color: var(--rlp-red); background-color: var(--rlp-red);
color: white; color: white;
@ -215,6 +209,11 @@ Overwrites bootstrap .btn:focus box shadow color
color: var(--rlp-red); color: var(--rlp-red);
} }
.scroll-150{
max-height: 150px;
overflow: auto;
}
.scroll-300{ .scroll-300{
max-height: 300px; max-height: 300px;
overflow: auto; overflow: auto;
@ -230,3 +229,6 @@ No other approach worked to get the autocomplete fields to full width of parent
.select2-results__option--highlighted{ .select2-results__option--highlighted{
background-color: var(--rlp-red) !important; background-color: var(--rlp-red) !important;
} }
.select2-container--default .select2-results > .select2-results__options{
max-height: 500px !important;
}

View File

@ -32,6 +32,10 @@ SECRET_KEY = '5=9-)2)h$u9=!zrhia9=lj-2#cpcb8=#$7y+)l$5tto$3q(n_+'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ADMINS = [
('KSP-Servicestelle', 'ksp-servicestelle@sgdnord.rlp.de'),
]
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
"127.0.0.1", "127.0.0.1",
"localhost", "localhost",
@ -212,41 +216,8 @@ if DEBUG:
EMAIL_FILE_PATH = '/tmp/app-messages' EMAIL_FILE_PATH = '/tmp/app-messages'
DEFAULT_FROM_EMAIL = "no-reply@ksp.de" # The default email address for the 'from' element DEFAULT_FROM_EMAIL = "no-reply@ksp.de" # 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
EMAIL_HOST = os.environ.get('SMTP_HOST') EMAIL_HOST = os.environ.get('SMTP_HOST')
EMAIL_REPLY_TO = os.environ.get('SMTP_REAL_REPLY_MAIL') EMAIL_REPLY_TO = os.environ.get('SMTP_REAL_REPLY_MAIL')
SUPPORT_MAIL_RECIPIENT = EMAIL_REPLY_TO SUPPORT_MAIL_RECIPIENT = EMAIL_REPLY_TO
EMAIL_PORT = os.environ.get('SMTP_PORT') EMAIL_PORT = os.environ.get('SMTP_PORT')
# LOGGING
BASIC_LOGGER = "logger"
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module}: {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'handlers': {
'log_to_file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '{}/logs/error.log'.format(BASE_DIR),
'maxBytes': 1024*1024*5, # 5 MB
'backupCount': 5,
'formatter': 'verbose',
},
},
'loggers': {
BASIC_LOGGER: {
'handlers': ['log_to_file'],
'level': 'INFO',
'propagate': True,
},
},
}

View File

@ -0,0 +1,29 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 31.01.22
"""
# MAPS
DEFAULT_LAT = 50.00
DEFAULT_LON = 7.00
DEFAULT_ZOOM = 8.0
DEFAULT_SRID = 4326
DEFAULT_SRID_RLP = 25832
# Needed to redirect to LANIS
## 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"
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
LANIS_ZOOM_LUT = {
1000000000: 6,
100000000: 10,
10000000: 17,
1000000: 20,
100000: 25,
10000: 28,
1000: 30,
500: 31,
}

View File

@ -0,0 +1,13 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 31.01.22
"""
proxy = ""
PROXIES = {
"http": proxy,
"https": proxy,
}

View File

@ -0,0 +1,13 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 31.01.22
"""
# SSO settings
SSO_SERVER_BASE = "http://127.0.0.1:8000/"
SSO_SERVER = f"{SSO_SERVER_BASE}sso/"
SSO_PRIVATE_KEY = "CHANGE_ME"
SSO_PUBLIC_KEY = "CHANGE_ME"

View File

@ -0,0 +1,24 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 31.01.22
"""
# Tables
RESULTS_PER_PAGE_PARAM = "rpp"
PAGE_PARAM = "page"
PAGE_SIZE_OPTIONS = [5, 10, 15, 20, 25, 50, 100]
PAGE_SIZE_OPTIONS_TUPLES = [
(5, 5),
(10, 10),
(15, 15),
(20, 20),
(25, 25),
(50, 50),
(100, 100),
]
PAGE_SIZE_DEFAULT = 5
PAGE_SIZE_MAX = 100
PAGE_DEFAULT = 1

View File

@ -0,0 +1,12 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 31.01.22
"""
# Parcel WFS settings
PARCEL_WFS_BASE_URL = "https://www.geoportal.rlp.de/registry/wfs/519"
PARCEL_WFS_USER = "ksp"
PARCEL_WFS_PW = "CHANGE_ME"

View File

@ -7,123 +7,13 @@
<div id="quickstart" class="col-md px-3"> <div id="quickstart" class="col-md px-3">
<div class="row px-3"> <div class="row px-3">
<div class="col-md"> <div class="col-md">
<h4> {% include 'konova/includes/quickstart/interventions.html' %}
{% trans 'Intervention' %}
</h4>
<div class="row">
<a href="{% url 'intervention:index' %}">
<div class="col-sm-5">
<div class="qs-box d-flex justify-content-center align-items-center">
{% fa5_icon 'pencil-ruler' %}
</div>
</div>
</a>
<div class="col-sm">
<div class="col-md mb-2">
<div>{% trans 'Total' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{total_intervention_count|intcomma}}</div>
</div> </div>
<div class="col-md"> <div class="col-md">
<div>{% trans 'Shared with you' %}</div> {% include 'konova/includes/quickstart/compensations.html' %}
<div class="class badge badge-pill rlp-gd-outline w-100">{{user_intervention_count|intcomma}}</div>
</div>
</div>
</div> </div>
<div class="col-md"> <div class="col-md">
<div class="row my-1"> {% include 'konova/includes/quickstart/ecoaccounts.html' %}
<a href="{% url 'intervention:new' %}">
<button class="btn btn-default">{% fa5_icon 'plus' %} {% trans 'Create' %}</button>
</a>
</div>
<div class="row my-1">
<a href="{% url 'intervention:index' %}">
<button class="btn btn-default">{% fa5_icon 'eye' %} {% trans 'Show' %}</button>
</a>
</div>
</div>
<hr>
</div>
<div class="col-md">
<h4>
{% trans 'Compensation' %}
</h4>
<div class="row">
<a href="{% url 'compensation:index' %}">
<div class="col-sm-5">
<div class="qs-box d-flex justify-content-center align-items-center">
{% fa5_icon 'leaf' %}
</div>
</div>
</a>
<div class="col-sm">
<div class="col-md mb-2">
<div>{% trans 'Total' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{total_compensation_count|intcomma}}</div>
</div>
<div class="col-md">
<div>{% trans 'Shared with you' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{user_compensation_count|intcomma}}</div>
</div>
</div>
</div>
<div class="col-md">
<div class="row my-1">
<a href="{% url 'compensation:new' %}">
<button class="btn btn-default">{% fa5_icon 'plus' %} {% trans 'Create' %}</button>
</a>
</div>
<div class="row my-1">
<a href="{% url 'compensation:index' %}">
<button class="btn btn-default">{% fa5_icon 'eye' %} {% trans 'Show' %}</button>
</a>
</div>
</div>
<hr>
</div>
<div class="col-md">
<h4>
{% trans 'Eco-account' %}
</h4>
<div class="row">
<a href="{% url 'compensation:acc-index' %}">
<div class="col-sm-5">
<div class="qs-box d-flex justify-content-center align-items-center">
{% fa5_icon 'tree' %}
</div>
</div>
</a>
<div class="col-sm">
<div class="col-md mb-2">
<div>{% trans 'Total' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{total_eco_count|intcomma}}</div>
</div>
<div class="col-md">
<div>{% trans 'Shared with you' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{user_eco_count|intcomma}}</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-lg">
<div class="col-sm">
<div class="row my-1">
<a href="{% url 'compensation:acc-new' %}">
<button class="btn btn-default">{% fa5_icon 'plus' %} {% trans 'Create' %}</button>
</a>
</div>
<div class="row my-1">
<a href="{% url 'compensation:acc-index' %}">
<button class="btn btn-default">{% fa5_icon 'eye' %} {% trans 'Show' %}</button>
</a>
</div>
</div>
</div>
</div>
</div>
<hr>
</div> </div>
</div> </div>
</div> </div>

View File

@ -19,7 +19,7 @@
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="card-text font-italic"> <div class="scroll-150 font-italic">
{{obj.comment}} {{obj.comment}}
</div> </div>
</div> </div>

View File

@ -0,0 +1,37 @@
{% load i18n fontawesome_5 humanize %}
<h4>
{% trans 'Compensation' %}
</h4>
<div class="row">
<a class="text-decoration-none" href="{% url 'compensation:index' %}">
<div class="col-sm-5">
<div class="qs-box d-flex justify-content-center align-items-center">
{% fa5_icon 'leaf' %}
</div>
</div>
</a>
<div class="col-sm">
<div class="col-md mb-2">
<div>{% trans 'Total' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{total_compensation_count|intcomma}}</div>
</div>
<div class="col-md">
<div>{% trans 'Shared with you' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{user_compensation_count|intcomma}}</div>
</div>
</div>
</div>
<div class="col-md">
<div class="row my-1">
<a href="{% url 'compensation:new' %}">
<button class="btn btn-default">{% fa5_icon 'plus' %} {% trans 'Create' %}</button>
</a>
</div>
<div class="row my-1">
<a href="{% url 'compensation:index' %}">
<button class="btn btn-default">{% fa5_icon 'eye' %} {% trans 'Show' %}</button>
</a>
</div>
</div>
<hr>

View File

@ -0,0 +1,42 @@
{% load i18n fontawesome_5 humanize %}
<h4>
{% trans 'Eco-account' %}
</h4>
<div class="row">
<a class="text-decoration-none" href="{% url 'compensation:acc-index' %}">
<div class="col-sm-5">
<div class="qs-box d-flex justify-content-center align-items-center">
{% fa5_icon 'tree' %}
</div>
</div>
</a>
<div class="col-sm">
<div class="col-md mb-2">
<div>{% trans 'Total' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{total_eco_count|intcomma}}</div>
</div>
<div class="col-md">
<div>{% trans 'Shared with you' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{user_eco_count|intcomma}}</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-lg">
<div class="col-sm">
<div class="row my-1">
<a href="{% url 'compensation:acc-new' %}">
<button class="btn btn-default">{% fa5_icon 'plus' %} {% trans 'Create' %}</button>
</a>
</div>
<div class="row my-1">
<a href="{% url 'compensation:acc-index' %}">
<button class="btn btn-default">{% fa5_icon 'eye' %} {% trans 'Show' %}</button>
</a>
</div>
</div>
</div>
</div>
</div>
<hr>

View File

@ -0,0 +1,37 @@
{% load i18n fontawesome_5 humanize %}
<h4>
{% trans 'Intervention' %}
</h4>
<div class="row">
<a class="text-decoration-none" href="{% url 'intervention:index' %}">
<div class="col-sm-5">
<div class="qs-box d-flex justify-content-center align-items-center">
{% fa5_icon 'pencil-ruler' %}
</div>
</div>
</a>
<div class="col-sm">
<div class="col-md mb-2">
<div>{% trans 'Total' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{total_intervention_count|intcomma}}</div>
</div>
<div class="col-md">
<div>{% trans 'Shared with you' %}</div>
<div class="class badge badge-pill rlp-gd-outline w-100">{{user_intervention_count|intcomma}}</div>
</div>
</div>
</div>
<div class="col-md">
<div class="row my-1">
<a href="{% url 'intervention:new' %}">
<button class="btn btn-default">{% fa5_icon 'plus' %} {% trans 'Create' %}</button>
</a>
</div>
<div class="row my-1">
<a href="{% url 'intervention:index' %}">
<button class="btn btn-default">{% fa5_icon 'eye' %} {% trans 'Show' %}</button>
</a>
</div>
</div>
<hr>

View File

@ -57,3 +57,33 @@ class AutocompleteTestCase(BaseTestCase):
) )
content = json.loads(response.content) content = json.loads(response.content)
self.assertEqual([], content["results"]) self.assertEqual([], content["results"])
def test_all_autocompletes(self):
tests = [
"accounts-autocomplete",
"interventions-autocomplete",
"codes-compensation-action-autocomplete",
"codes-compensation-action-detail-autocomplete",
"codes-biotope-autocomplete",
"codes-biotope-extra-type-autocomplete",
"codes-law-autocomplete",
"codes-process-type-autocomplete",
"codes-registration-office-autocomplete",
"codes-conservation-office-autocomplete",
"share-user-autocomplete",
]
for test in tests:
self.client.login(username=self.superuser.username, password=self.superuser_pw)
user_autocomplete_url = reverse(test)
data = {
"q": ""
}
response = self.client.get(
user_autocomplete_url,
data,
)
content = json.loads(response.content)
try:
content["results"]
except KeyError:
self.fail(f"No results returned for autocomplete {test}")

View File

@ -20,7 +20,7 @@ from django.urls import path, include
from konova.autocompletes import EcoAccountAutocomplete, \ from konova.autocompletes import EcoAccountAutocomplete, \
InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \ InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \ RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \
ShareUserAutocomplete ShareUserAutocomplete, BiotopeExtraCodeAutocomplete, CompensationActionDetailCodeAutocomplete
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, remove_deadline_view from konova.views import logout_view, home_view, remove_deadline_view
@ -47,7 +47,9 @@ urlpatterns = [
path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="accounts-autocomplete"), path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="accounts-autocomplete"),
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="interventions-autocomplete"), path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="interventions-autocomplete"),
path("atcmplt/codes/comp/action", CompensationActionCodeAutocomplete.as_view(), name="codes-compensation-action-autocomplete"), path("atcmplt/codes/comp/action", CompensationActionCodeAutocomplete.as_view(), name="codes-compensation-action-autocomplete"),
path("atcmplt/codes/comp/action/detail", CompensationActionDetailCodeAutocomplete.as_view(), name="codes-compensation-action-detail-autocomplete"),
path("atcmplt/codes/biotope", BiotopeCodeAutocomplete.as_view(), name="codes-biotope-autocomplete"), path("atcmplt/codes/biotope", BiotopeCodeAutocomplete.as_view(), name="codes-biotope-autocomplete"),
path("atcmplt/codes/biotope/extra", BiotopeExtraCodeAutocomplete.as_view(), name="codes-biotope-extra-type-autocomplete"),
path("atcmplt/codes/law", LawCodeAutocomplete.as_view(), name="codes-law-autocomplete"), path("atcmplt/codes/law", LawCodeAutocomplete.as_view(), name="codes-law-autocomplete"),
path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="codes-process-type-autocomplete"), path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="codes-process-type-autocomplete"),
path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="codes-registration-office-autocomplete"), path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="codes-registration-office-autocomplete"),

View File

@ -5,16 +5,12 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 09.11.20 Created on: 09.11.20
""" """
import logging
from django.core.mail import send_mail from django.core.mail import send_mail
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, EMAIL_REPLY_TO, SUPPORT_MAIL_RECIPIENT from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, EMAIL_REPLY_TO, SUPPORT_MAIL_RECIPIENT
logger = logging.getLogger(__name__)
class Mailer: class Mailer:
""" """

View File

@ -60,7 +60,7 @@ def home_view(request: HttpRequest):
unpublish_on__gte=now, unpublish_on__gte=now,
).order_by( ).order_by(
"-publish_on" "-publish_on"
)[:4] )[:3]
# First fetch all valid objects (undeleted, only newest versions) # First fetch all valid objects (undeleted, only newest versions)
interventions = Intervention.objects.filter( interventions = Intervention.objects.filter(

Binary file not shown.

View File

@ -3,9 +3,9 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#: compensation/filters.py:122 compensation/forms/modalForms.py:34 #: compensation/filters.py:122 compensation/forms/modalForms.py:35
#: compensation/forms/modalForms.py:45 compensation/forms/modalForms.py:61 #: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62
#: compensation/forms/modalForms.py:238 compensation/forms/modalForms.py:316 #: compensation/forms/modalForms.py:256 compensation/forms/modalForms.py:351
#: intervention/forms/forms.py:52 intervention/forms/forms.py:154 #: intervention/forms/forms.py:52 intervention/forms/forms.py:154
#: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125 #: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125
#: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151 #: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151
@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-28 16:27+0100\n" "POT-Creation-Date: 2022-01-31 12:41+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -95,7 +95,7 @@ msgstr ""
#: analysis/templates/analysis/reports/includes/eco_account/amount.html:3 #: analysis/templates/analysis/reports/includes/eco_account/amount.html:3
#: analysis/templates/analysis/reports/includes/intervention/amount.html:3 #: analysis/templates/analysis/reports/includes/intervention/amount.html:3
#: analysis/templates/analysis/reports/includes/old_data/amount.html:3 #: analysis/templates/analysis/reports/includes/old_data/amount.html:3
#: compensation/forms/modalForms.py:299 #: compensation/forms/modalForms.py:334
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34
#: intervention/templates/intervention/detail/includes/deductions.html:31 #: intervention/templates/intervention/detail/includes/deductions.html:31
msgid "Amount" msgid "Amount"
@ -177,8 +177,9 @@ msgstr "Einzelflächen"
#: analysis/templates/analysis/reports/includes/intervention/laws.html:23 #: analysis/templates/analysis/reports/includes/intervention/laws.html:23
#: analysis/templates/analysis/reports/includes/intervention/laws.html:43 #: analysis/templates/analysis/reports/includes/intervention/laws.html:43
#: analysis/templates/analysis/reports/includes/old_data/amount.html:19 #: analysis/templates/analysis/reports/includes/old_data/amount.html:19
#: konova/templates/konova/home.html:23 konova/templates/konova/home.html:61 #: konova/templates/konova/includes/quickstart/compensations.html:16
#: konova/templates/konova/home.html:100 #: konova/templates/konova/includes/quickstart/ecoaccounts.html:16
#: konova/templates/konova/includes/quickstart/interventions.html:16
msgid "Total" msgid "Total"
msgstr "Insgesamt" msgstr "Insgesamt"
@ -212,13 +213,13 @@ msgstr "Abbuchungen"
#: analysis/templates/analysis/reports/includes/eco_account/deductions.html:9 #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:9
#: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11 #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11
#: compensation/forms/modalForms.py:133 #: compensation/forms/modalForms.py:151
#: compensation/templates/compensation/detail/compensation/includes/states-after.html:36 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:39
#: compensation/templates/compensation/detail/compensation/includes/states-before.html:36 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:39
#: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:39
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-before.html:39
#: ema/templates/ema/detail/includes/states-after.html:36 #: ema/templates/ema/detail/includes/states-after.html:39
#: ema/templates/ema/detail/includes/states-before.html:36 #: ema/templates/ema/detail/includes/states-before.html:39
#: intervention/forms/modalForms.py:295 #: intervention/forms/modalForms.py:295
msgid "Surface" msgid "Surface"
msgstr "Fläche" msgstr "Fläche"
@ -240,12 +241,13 @@ msgstr "Kompensationsart"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:29 #: analysis/templates/analysis/reports/includes/old_data/amount.html:29
#: compensation/tables.py:85 #: compensation/tables.py:85
#: compensation/templates/compensation/detail/compensation/view.html:19 #: compensation/templates/compensation/detail/compensation/view.html:19
#: konova/templates/konova/home.html:49 templates/navbars/navbar.html:28 #: konova/templates/konova/includes/quickstart/compensations.html:4
#: templates/navbars/navbar.html:28
msgid "Compensation" msgid "Compensation"
msgstr "Kompensation" msgstr "Kompensation"
#: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:21 #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:21
#: compensation/forms/modalForms.py:74 #: compensation/forms/modalForms.py:75
msgid "Payment" msgid "Payment"
msgstr "Zahlung" msgstr "Zahlung"
@ -285,7 +287,8 @@ msgstr "Typ"
#: intervention/forms/modalForms.py:306 intervention/forms/modalForms.py:313 #: intervention/forms/modalForms.py:306 intervention/forms/modalForms.py:313
#: intervention/tables.py:89 #: intervention/tables.py:89
#: intervention/templates/intervention/detail/view.html:19 #: intervention/templates/intervention/detail/view.html:19
#: konova/templates/konova/home.html:11 templates/navbars/navbar.html:22 #: konova/templates/konova/includes/quickstart/interventions.html:4
#: templates/navbars/navbar.html:22
msgid "Intervention" msgid "Intervention"
msgstr "Eingriff" msgstr "Eingriff"
@ -293,7 +296,8 @@ msgstr "Eingriff"
#: compensation/tables.py:226 #: compensation/tables.py:226
#: compensation/templates/compensation/detail/eco_account/view.html:19 #: compensation/templates/compensation/detail/eco_account/view.html:19
#: intervention/forms/modalForms.py:279 intervention/forms/modalForms.py:286 #: intervention/forms/modalForms.py:279 intervention/forms/modalForms.py:286
#: konova/templates/konova/home.html:88 templates/navbars/navbar.html:34 #: konova/templates/konova/includes/quickstart/ecoaccounts.html:4
#: templates/navbars/navbar.html:34
msgid "Eco-account" msgid "Eco-account"
msgstr "Ökokonto" msgstr "Ökokonto"
@ -349,9 +353,9 @@ msgstr "Aussagekräftiger Titel"
msgid "Compensation XY; Location ABC" msgid "Compensation XY; Location ABC"
msgstr "Kompensation XY; Flur ABC" msgstr "Kompensation XY; Flur ABC"
#: compensation/forms/forms.py:57 compensation/forms/modalForms.py:60 #: compensation/forms/forms.py:57 compensation/forms/modalForms.py:61
#: compensation/forms/modalForms.py:237 compensation/forms/modalForms.py:315 #: compensation/forms/modalForms.py:255 compensation/forms/modalForms.py:350
#: compensation/templates/compensation/detail/compensation/includes/actions.html:34 #: compensation/templates/compensation/detail/compensation/includes/actions.html:37
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34
#: compensation/templates/compensation/detail/compensation/includes/documents.html:31 #: compensation/templates/compensation/detail/compensation/includes/documents.html:31
#: compensation/templates/compensation/detail/eco_account/includes/actions.html:34 #: compensation/templates/compensation/detail/eco_account/includes/actions.html:34
@ -465,66 +469,80 @@ msgstr "Ökokonto XY; Flur ABC"
msgid "Edit Eco-Account" msgid "Edit Eco-Account"
msgstr "Ökokonto bearbeiten" msgstr "Ökokonto bearbeiten"
#: compensation/forms/modalForms.py:35 #: compensation/forms/modalForms.py:36
msgid "in Euro" msgid "in Euro"
msgstr "in Euro" msgstr "in Euro"
#: compensation/forms/modalForms.py:44 #: compensation/forms/modalForms.py:45
#: intervention/templates/intervention/detail/includes/payments.html:31 #: intervention/templates/intervention/detail/includes/payments.html:31
msgid "Due on" msgid "Due on"
msgstr "Fällig am" msgstr "Fällig am"
#: compensation/forms/modalForms.py:47 #: compensation/forms/modalForms.py:48
msgid "Due on which date" msgid "Due on which date"
msgstr "Zahlung wird an diesem Datum erwartet" msgstr "Zahlung wird an diesem Datum erwartet"
#: compensation/forms/modalForms.py:62 compensation/forms/modalForms.py:239 #: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:257
#: compensation/forms/modalForms.py:317 intervention/forms/modalForms.py:152 #: compensation/forms/modalForms.py:352 intervention/forms/modalForms.py:152
#: konova/forms.py:375 #: konova/forms.py:375
msgid "Additional comment, maximum {} letters" msgid "Additional comment, maximum {} letters"
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
#: compensation/forms/modalForms.py:75 #: compensation/forms/modalForms.py:76
msgid "Add a payment for intervention '{}'" msgid "Add a payment for intervention '{}'"
msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen" msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen"
#: compensation/forms/modalForms.py:95 #: compensation/forms/modalForms.py:96
msgid "If there is no date you can enter, please explain why." msgid "If there is no date you can enter, please explain why."
msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb." msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb."
#: compensation/forms/modalForms.py:114 compensation/forms/modalForms.py:126 #: compensation/forms/modalForms.py:115 compensation/forms/modalForms.py:127
msgid "Biotope Type" msgid "Biotope Type"
msgstr "Biotoptyp" msgstr "Biotoptyp"
#: compensation/forms/modalForms.py:117 #: compensation/forms/modalForms.py:118
msgid "Select the biotope type" msgid "Select the biotope type"
msgstr "Biotoptyp wählen" msgstr "Biotoptyp wählen"
#: compensation/forms/modalForms.py:136 intervention/forms/modalForms.py:297 #: compensation/forms/modalForms.py:132 compensation/forms/modalForms.py:144
#: compensation/templates/compensation/detail/compensation/includes/states-after.html:36
#: compensation/templates/compensation/detail/compensation/includes/states-before.html:36
#: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:36
#: ema/templates/ema/detail/includes/states-after.html:36
#: ema/templates/ema/detail/includes/states-before.html:36
msgid "Biotope additional type"
msgstr "Zusatzbezeichnung"
#: compensation/forms/modalForms.py:135
msgid "Select an additional biotope type"
msgstr "Zusatzbezeichnung wählen"
#: compensation/forms/modalForms.py:154 intervention/forms/modalForms.py:297
msgid "in m²" msgid "in m²"
msgstr "" msgstr ""
#: compensation/forms/modalForms.py:147 #: compensation/forms/modalForms.py:165
msgid "New state" msgid "New state"
msgstr "Neuer Zustand" msgstr "Neuer Zustand"
#: compensation/forms/modalForms.py:148 #: compensation/forms/modalForms.py:166
msgid "Insert data for the new state" msgid "Insert data for the new state"
msgstr "Geben Sie die Daten des neuen Zustandes ein" msgstr "Geben Sie die Daten des neuen Zustandes ein"
#: compensation/forms/modalForms.py:155 konova/forms.py:190 #: compensation/forms/modalForms.py:173 konova/forms.py:190
msgid "Object removed" msgid "Object removed"
msgstr "Objekt entfernt" msgstr "Objekt entfernt"
#: compensation/forms/modalForms.py:209 #: compensation/forms/modalForms.py:227
msgid "Deadline Type" msgid "Deadline Type"
msgstr "Fristart" msgstr "Fristart"
#: compensation/forms/modalForms.py:212 #: compensation/forms/modalForms.py:230
msgid "Select the deadline type" msgid "Select the deadline type"
msgstr "Fristart wählen" msgstr "Fristart wählen"
#: compensation/forms/modalForms.py:221 #: compensation/forms/modalForms.py:239
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31
#: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31
#: ema/templates/ema/detail/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31
@ -532,43 +550,43 @@ msgstr "Fristart wählen"
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: compensation/forms/modalForms.py:224 #: compensation/forms/modalForms.py:242
msgid "Select date" msgid "Select date"
msgstr "Datum wählen" msgstr "Datum wählen"
#: compensation/forms/modalForms.py:251 #: compensation/forms/modalForms.py:269
msgid "New deadline" msgid "New deadline"
msgstr "Neue Frist" msgstr "Neue Frist"
#: compensation/forms/modalForms.py:252 #: compensation/forms/modalForms.py:270
msgid "Insert data for the new deadline" msgid "Insert data for the new deadline"
msgstr "Geben Sie die Daten der neuen Frist ein" msgstr "Geben Sie die Daten der neuen Frist ein"
#: compensation/forms/modalForms.py:270 #: compensation/forms/modalForms.py:288
msgid "Action Type" msgid "Action Type"
msgstr "Maßnahmentyp" msgstr "Maßnahmentyp"
#: compensation/forms/modalForms.py:273 #: compensation/forms/modalForms.py:291
msgid "Select the action type" msgid "Select the action type"
msgstr "Maßnahmentyp wählen" msgstr "Maßnahmentyp wählen"
#: compensation/forms/modalForms.py:282 #: compensation/forms/modalForms.py:300
#: compensation/templates/compensation/detail/compensation/includes/actions.html:38 #: compensation/templates/compensation/detail/compensation/includes/actions.html:41
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:38 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:38
#: compensation/templates/compensation/detail/compensation/includes/documents.html:35 #: compensation/templates/compensation/detail/compensation/includes/documents.html:35
#: compensation/templates/compensation/detail/compensation/includes/states-after.html:40 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:43
#: compensation/templates/compensation/detail/compensation/includes/states-before.html:40 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:43
#: compensation/templates/compensation/detail/eco_account/includes/actions.html:38 #: compensation/templates/compensation/detail/eco_account/includes/actions.html:38
#: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:37 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:37
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:40 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:40
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:34 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:34
#: compensation/templates/compensation/detail/eco_account/includes/states-after.html:40 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:43
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:40 #: compensation/templates/compensation/detail/eco_account/includes/states-before.html:43
#: ema/templates/ema/detail/includes/actions.html:37 #: ema/templates/ema/detail/includes/actions.html:37
#: ema/templates/ema/detail/includes/deadlines.html:37 #: ema/templates/ema/detail/includes/deadlines.html:37
#: ema/templates/ema/detail/includes/documents.html:34 #: ema/templates/ema/detail/includes/documents.html:34
#: ema/templates/ema/detail/includes/states-after.html:39 #: ema/templates/ema/detail/includes/states-after.html:42
#: ema/templates/ema/detail/includes/states-before.html:39 #: ema/templates/ema/detail/includes/states-before.html:42
#: intervention/templates/intervention/detail/includes/compensations.html:37 #: intervention/templates/intervention/detail/includes/compensations.html:37
#: intervention/templates/intervention/detail/includes/deductions.html:38 #: intervention/templates/intervention/detail/includes/deductions.html:38
#: intervention/templates/intervention/detail/includes/documents.html:35 #: intervention/templates/intervention/detail/includes/documents.html:35
@ -578,23 +596,31 @@ msgstr "Maßnahmentyp wählen"
msgid "Action" msgid "Action"
msgstr "Aktionen" msgstr "Aktionen"
#: compensation/forms/modalForms.py:287 #: compensation/forms/modalForms.py:305 compensation/forms/modalForms.py:317
msgid "Action Type detail"
msgstr "Zusatzmerkmal"
#: compensation/forms/modalForms.py:308
msgid "Select the action type detail"
msgstr "Zusatzmerkmal wählen"
#: compensation/forms/modalForms.py:322
msgid "Unit" msgid "Unit"
msgstr "Einheit" msgstr "Einheit"
#: compensation/forms/modalForms.py:290 #: compensation/forms/modalForms.py:325
msgid "Select the unit" msgid "Select the unit"
msgstr "Einheit wählen" msgstr "Einheit wählen"
#: compensation/forms/modalForms.py:302 #: compensation/forms/modalForms.py:337
msgid "Insert the amount" msgid "Insert the amount"
msgstr "Menge eingeben" msgstr "Menge eingeben"
#: compensation/forms/modalForms.py:328 #: compensation/forms/modalForms.py:363
msgid "New action" msgid "New action"
msgstr "Neue Maßnahme" msgstr "Neue Maßnahme"
#: compensation/forms/modalForms.py:329 #: compensation/forms/modalForms.py:364
msgid "Insert data for the new action" msgid "Insert data for the new action"
msgstr "Geben Sie die Daten der neuen Maßnahme ein" msgstr "Geben Sie die Daten der neuen Maßnahme ein"
@ -724,13 +750,17 @@ msgid "Action type"
msgstr "Maßnahmentyp" msgstr "Maßnahmentyp"
#: compensation/templates/compensation/detail/compensation/includes/actions.html:31 #: compensation/templates/compensation/detail/compensation/includes/actions.html:31
msgid "Action type details"
msgstr "Zusatzmerkmale"
#: compensation/templates/compensation/detail/compensation/includes/actions.html:34
#: compensation/templates/compensation/detail/eco_account/includes/actions.html:31 #: compensation/templates/compensation/detail/eco_account/includes/actions.html:31
#: ema/templates/ema/detail/includes/actions.html:31 #: ema/templates/ema/detail/includes/actions.html:31
msgctxt "Compensation" msgctxt "Compensation"
msgid "Amount" msgid "Amount"
msgstr "Menge" msgstr "Menge"
#: compensation/templates/compensation/detail/compensation/includes/actions.html:53 #: compensation/templates/compensation/detail/compensation/includes/actions.html:61
#: compensation/templates/compensation/detail/eco_account/includes/actions.html:53 #: compensation/templates/compensation/detail/eco_account/includes/actions.html:53
#: ema/templates/ema/detail/includes/actions.html:51 #: ema/templates/ema/detail/includes/actions.html:51
msgid "Remove action" msgid "Remove action"
@ -840,12 +870,12 @@ msgstr "Fehlende Flächenmengen laut Ausgangszustand: "
msgid "Biotope type" msgid "Biotope type"
msgstr "Biotoptyp" msgstr "Biotoptyp"
#: compensation/templates/compensation/detail/compensation/includes/states-after.html:54 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:64
#: compensation/templates/compensation/detail/compensation/includes/states-before.html:54 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:64
#: compensation/templates/compensation/detail/eco_account/includes/states-after.html:54 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:64
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:54 #: compensation/templates/compensation/detail/eco_account/includes/states-before.html:64
#: ema/templates/ema/detail/includes/states-after.html:52 #: ema/templates/ema/detail/includes/states-after.html:62
#: ema/templates/ema/detail/includes/states-before.html:52 #: ema/templates/ema/detail/includes/states-before.html:62
msgid "Remove state" msgid "Remove state"
msgstr "Zustand entfernen" msgstr "Zustand entfernen"
@ -876,13 +906,13 @@ msgstr "Ist CEF Maßnahme"
#: compensation/templates/compensation/detail/compensation/view.html:56 #: compensation/templates/compensation/detail/compensation/view.html:56
#: venv/lib/python3.7/site-packages/django/forms/widgets.py:710 #: venv/lib/python3.7/site-packages/django/forms/widgets.py:710
msgid "Yes" msgid "Yes"
msgstr "" msgstr "Ja"
#: compensation/templates/compensation/detail/compensation/view.html:48 #: compensation/templates/compensation/detail/compensation/view.html:48
#: compensation/templates/compensation/detail/compensation/view.html:58 #: compensation/templates/compensation/detail/compensation/view.html:58
#: venv/lib/python3.7/site-packages/django/forms/widgets.py:711 #: venv/lib/python3.7/site-packages/django/forms/widgets.py:711
msgid "No" msgid "No"
msgstr "" msgstr "Nein"
#: compensation/templates/compensation/detail/compensation/view.html:53 #: compensation/templates/compensation/detail/compensation/view.html:53
msgid "Is Coherence keeping compensation" msgid "Is Coherence keeping compensation"
@ -1725,21 +1755,6 @@ msgstr ""
msgid "English" msgid "English"
msgstr "" msgstr ""
#: konova/templates/konova/home.html:27 konova/templates/konova/home.html:65
#: konova/templates/konova/home.html:104
msgid "Shared with you"
msgstr "Für Sie freigegeben"
#: konova/templates/konova/home.html:35 konova/templates/konova/home.html:73
#: konova/templates/konova/home.html:114
msgid "Create"
msgstr "Neu"
#: konova/templates/konova/home.html:40 konova/templates/konova/home.html:78
#: konova/templates/konova/home.html:119
msgid "Show"
msgstr "Anzeigen"
#: konova/templates/konova/includes/parcels.html:3 #: konova/templates/konova/includes/parcels.html:3
msgid "Spatial reference" msgid "Spatial reference"
msgstr "Raumreferenz" msgstr "Raumreferenz"
@ -1764,6 +1779,24 @@ msgstr "Kreis"
msgid "Gemarkung" msgid "Gemarkung"
msgstr "Gemarkung" msgstr "Gemarkung"
#: konova/templates/konova/includes/quickstart/compensations.html:20
#: konova/templates/konova/includes/quickstart/ecoaccounts.html:20
#: konova/templates/konova/includes/quickstart/interventions.html:20
msgid "Shared with you"
msgstr "Für Sie freigegeben"
#: konova/templates/konova/includes/quickstart/compensations.html:28
#: konova/templates/konova/includes/quickstart/ecoaccounts.html:30
#: konova/templates/konova/includes/quickstart/interventions.html:28
msgid "Create"
msgstr "Neu"
#: konova/templates/konova/includes/quickstart/compensations.html:33
#: konova/templates/konova/includes/quickstart/ecoaccounts.html:35
#: konova/templates/konova/includes/quickstart/interventions.html:33
msgid "Show"
msgstr "Anzeigen"
#: konova/templates/konova/widgets/generate-content-input.html:6 #: konova/templates/konova/widgets/generate-content-input.html:6
msgid "Generate new" msgid "Generate new"
msgstr "Neu generieren" msgstr "Neu generieren"

View File

@ -4,11 +4,23 @@ from news.models import ServerMessage
class ServerMessageAdmin(admin.ModelAdmin): class ServerMessageAdmin(admin.ModelAdmin):
readonly_fields = [
"modified",
"created",
]
list_display = [ list_display = [
"id", "id",
"subject", "subject",
"publish_on", "publish_on",
"is_active", "is_active",
] ]
search_fields = [
"subject"
]
def save_model(self, request, obj, form, change):
obj.save(user=request.user)
admin.site.register(ServerMessage, ServerMessageAdmin) admin.site.register(ServerMessage, ServerMessageAdmin)

View File

@ -0,0 +1,30 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='ServerMessage',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('subject', models.CharField(max_length=500)),
('body', models.TextField()),
('is_active', models.BooleanField(default=True)),
('publish_on', models.DateTimeField()),
('unpublish_on', models.DateTimeField()),
('importance', models.CharField(choices=[('default', 'Default'), ('info', 'Info'), ('warning', 'Warning')], max_length=100)),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('user', '0001_initial'),
('news', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='servermessage',
name='created',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
migrations.AddField(
model_name='servermessage',
name='modified',
field=models.ForeignKey(blank=True, help_text='Last modified', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='user.useractionlogentry'),
),
]

View File

View File

@ -2,6 +2,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from konova.models import BaseResource from konova.models import BaseResource
from user.models import UserActionLogEntry
class ServerMessageImportance(models.TextChoices): class ServerMessageImportance(models.TextChoices):
@ -23,3 +24,13 @@ class ServerMessage(BaseResource):
publish_on = models.DateTimeField() publish_on = models.DateTimeField()
unpublish_on = models.DateTimeField() unpublish_on = models.DateTimeField()
importance = models.CharField(max_length=100, choices=ServerMessageImportance.choices) importance = models.CharField(max_length=100, choices=ServerMessageImportance.choices)
def save(self, user=None, *args, **kwargs):
user = kwargs.pop("user", None)
if user is not None:
if self.created is None:
self.created = UserActionLogEntry.get_created_action(user)
else:
self.modified = UserActionLogEntry.get_edited_action(user)
super().save(*args, **kwargs)

View File

@ -10,14 +10,14 @@
{{msg.subject}} {{msg.subject}}
</h6> </h6>
<small>{% trans 'Published on' %} {{msg.publish_on}}</small> <small>{% trans 'Published on' %} {{msg.publish_on}}</small>
<article class="card-text">{{msg.body|safe}}</article> <article class="scroll-150">{{msg.body|safe}}</article>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
<div class="col-sm-12 col-md-12 col-lg"> <div class="col-sm-12 col-md-12 col-lg">
<div class="card {{msg.importance|bootstrap_cls}} h-100"> <div class="card {{msg.importance|bootstrap_cls}} h-100">
<a class="w-100 h-100" href="{% url 'news:index' %}"> <a class="w-100 h-100 text-decoration-none" href="{% url 'news:index' %}">
<div class="card-body d-flex align-items-center justify-content-center h-100"> <div class="card-body d-flex align-items-center justify-content-center h-100">
<h5 class="card-title">{% trans 'Older ...' %}</h5> <h5 class="card-title">{% trans 'Older ...' %}</h5>
</div> </div>

View File

@ -19,7 +19,7 @@
<small> {% trans 'Published on' %} {{msg.publish_on}}</small> <small> {% trans 'Published on' %} {{msg.publish_on}}</small>
</h5> </h5>
<small></small> <small></small>
<article class="card-text">{{msg.body|safe}}</article> <article class="scroll-150">{{msg.body|safe}}</article>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}

View File

@ -9,7 +9,7 @@
<br> <br>
{% trans 'the following dataset has just been checked' %} {% trans 'the following dataset has just been checked' %}
<br> <br>
<strong>'{{obj_identifier}}'</strong> <strong>{{obj_identifier}}</strong>
<br> <br>
{% trans 'This means, the responsible registration office just confirmed the correctness of this dataset.' %} {% trans 'This means, the responsible registration office just confirmed the correctness of this dataset.' %}
<br> <br>

View File

@ -9,7 +9,7 @@
<br> <br>
{% trans 'the following dataset has just been deleted' %} {% trans 'the following dataset has just been deleted' %}
<br> <br>
<strong>'{{obj_identifier}}'</strong> <strong>{{obj_identifier}}</strong>
<br> <br>
{% trans 'If this should not have been happened, please contact us. See the signature for details.' %} {% trans 'If this should not have been happened, please contact us. See the signature for details.' %}
<br> <br>

View File

@ -9,7 +9,7 @@
<br> <br>
{% trans 'the following dataset has just been recorded' %} {% trans 'the following dataset has just been recorded' %}
<br> <br>
<strong>'{{obj_identifier}}'</strong> <strong>{{obj_identifier}}</strong>
<br> <br>
{% trans 'This means the data is now publicly available, e.g. in LANIS' %} {% trans 'This means the data is now publicly available, e.g. in LANIS' %}
<br> <br>

View File

@ -9,7 +9,7 @@
<br> <br>
{% trans 'the following dataset has just been unrecorded' %} {% trans 'the following dataset has just been unrecorded' %}
<br> <br>
<strong>'{{obj_identifier}}'</strong> <strong>{{obj_identifier}}</strong>
<br> <br>
{% trans 'This means the data is no longer publicly available.' %} {% trans 'This means the data is no longer publicly available.' %}
<br> <br>

View File

@ -9,7 +9,7 @@
<br> <br>
{% trans 'the following dataset has just been shared with you' %} {% trans 'the following dataset has just been shared with you' %}
<br> <br>
<strong>'{{obj_identifier}}'</strong> <strong>{{obj_identifier}}</strong>
<br> <br>
{% trans 'This means you can now edit this dataset.' %} {% trans 'This means you can now edit this dataset.' %}
{% trans 'The shared dataset appears now by default on your overview for this dataset type.' %} {% trans 'The shared dataset appears now by default on your overview for this dataset type.' %}

View File

@ -9,7 +9,7 @@
<br> <br>
{% trans 'your shared access, including editing, has been revoked for the dataset ' %} {% trans 'your shared access, including editing, has been revoked for the dataset ' %}
<br> <br>
<strong>'{{obj_identifier}}'</strong> <strong>{{obj_identifier}}</strong>
<br> <br>
{% trans 'However, you are still able to view the dataset content.' %} {% trans 'However, you are still able to view the dataset content.' %}
{% trans 'Please use the provided search filter on the dataset`s overview pages to find them.' %} {% trans 'Please use the provided search filter on the dataset`s overview pages to find them.' %}

View File

@ -19,6 +19,40 @@ class UserAdmin(admin.ModelAdmin):
"last_name", "last_name",
"email", "email",
] ]
fields = [
"username",
"first_name",
"last_name",
"email",
"is_active",
"is_staff",
"is_superuser",
"api_token",
"groups",
"notifications",
"date_joined",
"last_login",
]
search_fields = [
"username",
"first_name",
"last_name",
"email",
]
filter_horizontal = [
"groups",
"notifications",
]
readonly_fields = [
"date_joined",
"last_login",
]
autocomplete_fields = [
"api_token",
]
exclude = [
"user_permissions",
]
class UserActionLogEntryAdmin(admin.ModelAdmin): class UserActionLogEntryAdmin(admin.ModelAdmin):
@ -30,6 +64,8 @@ class UserActionLogEntryAdmin(admin.ModelAdmin):
] ]
admin.site.register(UserNotification, UserNotificationAdmin)
admin.site.register(UserActionLogEntry, UserActionLogEntryAdmin)
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
# Outcommented for a cleaner admin backend on production
#admin.site.register(UserNotification, UserNotificationAdmin)
#admin.site.register(UserActionLogEntry, UserActionLogEntryAdmin)

View File

@ -0,0 +1,77 @@
# Generated by Django 3.1.3 on 2022-01-14 08:36
from django.conf import settings
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='UserNotification',
fields=[
('id', models.CharField(choices=[('NOTIFY_ON_SHARED_ACCESS_REMOVED', 'NOTIFY_ON_SHARED_ACCESS_REMOVED'), ('NOTIFY_ON_SHARED_DATA_RECORDED', 'NOTIFY_ON_SHARED_DATA_RECORDED'), ('NOTIFY_ON_SHARED_DATA_DELETED', 'NOTIFY_ON_SHARED_DATA_DELETED'), ('NOTIFY_ON_SHARED_DATA_CHECKED', 'NOTIFY_ON_SHARED_DATA_CHECKED'), ('NOTIFY_ON_SHARED_ACCESS_GAINED', 'NOTIFY_ON_SHARED_ACCESS_GAINED')], max_length=500, primary_key=True, serialize=False)),
('name', models.CharField(help_text='Human readable name', max_length=500, unique=True)),
('is_active', models.BooleanField(default=True, help_text='Can be toggle to enable/disable this notification for all users')),
],
),
migrations.CreateModel(
name='UserActionLogEntry',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('timestamp', models.DateTimeField(auto_now_add=True, help_text='Timestamp of performed action')),
('action', models.CharField(blank=True, choices=[('checked', 'Checked'), ('recorded', 'Recorded'), ('unrecorded', 'Unrecorded'), ('created', 'Created'), ('edited', 'Edited'), ('deleted', 'Deleted')], help_text='Short name for performed action - optional', max_length=255, null=True)),
('comment', models.CharField(blank=True, help_text='Additional comment on this entry', max_length=255, null=True)),
('user', models.ForeignKey(help_text='Performing user', on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-timestamp',),
},
),
migrations.AddField(
model_name='user',
name='notifications',
field=models.ManyToManyField(blank=True, related_name='_user_notifications_+', to='user.UserNotification'),
),
migrations.AddField(
model_name='user',
name='user_permissions',
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.1.3 on 2022-01-28 15:50
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0001_initial'),
('user', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='api_token',
field=models.OneToOneField(blank=True, help_text="The user's API token", null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.apiusertoken'),
),
]

View File

View File

@ -10,6 +10,8 @@ import uuid
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT
class UserAction(models.TextChoices): class UserAction(models.TextChoices):
""" """
@ -49,6 +51,9 @@ class UserActionLogEntry(models.Model):
"-timestamp", "-timestamp",
) )
def __str__(self):
return f"{self.timestamp.strftime(DEFAULT_DATE_TIME_FORMAT)} | {self.action} | {self.user}"
@property @property
def action_humanize(self): def action_humanize(self):
""" Returns humanized version of enum """ Returns humanized version of enum