* fixes created timestamp in detail views where modified needs to be displayed
* adds fallback timestamp if data has not been edited, yet --> show created timestamp
* fixes bug where deleting of certain data didn't redirect to the index view
* adds quality_check() method for EMA, needed for recording
* adds all functions which are provided for compensations to EMA
* adds/updates translations
This commit is contained in:
mipel 2021-08-19 13:44:06 +02:00
parent d1f43f8c64
commit 615b7bf5ea
18 changed files with 717 additions and 18 deletions

View File

@ -257,9 +257,17 @@ class EcoAccount(AbstractCompensation):
y, y,
) )
def quality_check(self) -> (bool, dict): def quality_check(self) -> list:
# ToDo """ Quality check
pass
Returns:
ret_msgs (list): Holds error messages
"""
ret_msgs = []
# ToDo: Add check methods!
return ret_msgs
class EcoAccountWithdraw(BaseResource): class EcoAccountWithdraw(BaseResource):

View File

@ -63,9 +63,9 @@
<tr> <tr>
<th scope="row">{% trans 'Last modified' %}</th> <th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle"> <td class="align-middle">
{{obj.created.timestamp|default_if_none:""|naturalday}} {{obj.modified.timestamp|default_if_none:""|naturalday}}
<br> <br>
{{obj.created.user.username}} {{obj.modified.user.username}}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -55,9 +55,9 @@
<tr> <tr>
<th scope="row">{% trans 'Last modified' %}</th> <th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle"> <td class="align-middle">
{{obj.created.timestamp|default_if_none:""|naturalday}} {{obj.modified.timestamp|default_if_none:""|naturalday}}
<br> <br>
{{obj.created.user.username}} {{obj.modified.user.username}}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -141,7 +141,7 @@ def remove_view(request: HttpRequest, id: str):
return form.process_request( return form.process_request(
request=request, request=request,
msg_success=_("Compensation removed"), msg_success=_("Compensation removed"),
redirect_url="", redirect_url=reverse("compensation:index"),
) )

View File

@ -72,3 +72,15 @@ class Ema(AbstractCompensation):
x, x,
y, y,
) )
def quality_check(self) -> list:
""" Quality check
Returns:
ret_msgs (list): Holds error messages
"""
ret_msgs = []
# ToDo: Add check methods!
return ret_msgs

View File

@ -0,0 +1,61 @@
{% load i18n l10n fontawesome_5 humanize %}
<div id="actions" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{{obj.actions.count}}</span>
{% trans 'Actions' context 'Compensation' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'ema:new-action' obj.id %}" title="{% trans 'Add new action' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'seedling' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'Action type' %}
</th>
<th scope="col">
{% trans 'Amount' context 'Compensation' %}
</th>
<th scope="col">
{% trans 'Comment' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% for action in obj.actions.all %}
<tr>
<td class="align-middle">
{{ action.action_type }}
</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>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:action-remove' action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,40 @@
{% load i18n l10n fontawesome_5 %}
<div class="d-flex justify-content-end">
<a href="{{LANIS_LINK}}" class="mr-2" target="_blank">
<button class="btn btn-default" title="{% trans 'Open in LANIS' %}">
LANIS
</button>
</a>
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %}
</button>
</a>
{% if has_access %}
{% if is_ets_member %}
{% if obj.recorded %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Unrecord' %}" data-form-url="{% url 'ema:record' obj.id %}">
{% fa5_icon 'bookmark' 'far' %}
</button>
{% else %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Record' %}" data-form-url="{% url 'ema:record' obj.id %}">
{% fa5_icon 'bookmark' %}
</button>
{% endif %}
{% endif %}
{% if is_default_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %}
</button>
</a>
<button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'ema:log' obj.id %}" title="{% trans 'Show log' %}">
{% fa5_icon 'history' %}
</button>
<button class="btn btn-default btn-modal" data-form-url="{% url 'ema:remove' obj.id %}" title="{% trans 'Delete' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
{% endif %}
</div>

View File

@ -0,0 +1,61 @@
{% load i18n l10n fontawesome_5 %}
<div id="deadlines" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{{obj.deadlines.count}}</span>
{% trans 'Deadlines' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'ema:new-deadline' obj.id %}" title="{% trans 'Add new deadline' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'calendar-check' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'Type' %}
</th>
<th scope="col">
{% trans 'Date' %}
</th>
<th scope="col">
{% trans 'Comment' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% for deadline in obj.deadlines.all %}
<tr>
<td class="align-middle">
{% trans deadline.type_humanized %}
</td>
<td class="align-middle">{{ deadline.date }}</td>
<td class="align-middle">{{ deadline.comment }}</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'deadline-remove' deadline.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove deadline' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,59 @@
{% load i18n l10n fontawesome_5 %}
<div id="documents" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{{obj.documents.count}}</span>
{% trans 'Documents' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'ema:new-doc' obj.id %}" title="{% trans 'Add new document' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'file' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'Title' %}
</th>
<th scope="col">
{% trans 'Comment' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% for doc in obj.documents.all %}
<tr>
<td class="align-middle">
<a href="{% url 'doc-open' doc.id %}">
{{ doc.title }}
</a>
</td>
<td class="align-middle">{{ doc.comment }}</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,62 @@
{% load i18n l10n fontawesome_5 %}
<div id="states-after" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{{obj.after_states.count}}</span>
{% trans 'States after' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'ema:new-state' obj.id %}" title="{% trans 'Add new state after' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'layer-group' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
{% if sum_before_states > sum_after_states %}
<div class="row alert alert-danger">
{% trans 'Missing surfaces according to states before: ' %}{{ diff_states|floatformat:2 }} m²
</div>
{% endif %}
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'Biotope type' %}
</th>
<th scope="col">
{% trans 'Surface' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% for state in after_states %}
<tr>
<td class="align-middle">
{{ state.biotope_type }}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,62 @@
{% load i18n l10n fontawesome_5 %}
<div id="states-before" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{{obj.before_states.count}}</span>
{% trans 'States before' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'ema:new-state' obj.id %}?before=true" title="{% trans 'Add new state before' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'layer-group' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
{% if sum_before_states < sum_after_states %}
<div class="row alert alert-danger">
{% trans 'Missing surfaces according to states after: ' %}{{ diff_states|floatformat:2 }} m²
</div>
{% endif %}
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'Biotope type' %}
</th>
<th scope="col">
{% trans 'Surface' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% for state in before_states %}
<tr>
<td class="align-middle">
{{ state.biotope_type }}
</td>
<td class="align-middle">{{ state.surface|floatformat:2 }} m²</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'ema:state-remove' state.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove state' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,100 @@
{% extends 'base.html' %}
{% load i18n l10n static fontawesome_5 humanize %}
{% block head %}
{% endblock %}
{% block body %}
<div id="detail-header" class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<h3>{{obj.identifier}}</h3>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'ema/detail/includes/controls.html' %}
</div>
</div>
<hr>
<div id="data" class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="table-container">
<table class="table table-hover">
<tr>
<th class="w-25" scope="row">{% trans 'Title' %}</th>
<td class="align-middle">{{obj.title}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Recorded' %}</th>
<td class="align-middle">
{% if obj.recorded is None %}
<span title="{% trans 'Not recorded yet' %}">
{% fa5_icon 'bookmark' 'far' %}
</span>
{% else %}
<span class="registered-bookmark" title="{% trans 'Recorded on '%} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}">
{% fa5_icon 'bookmark' %}
</span>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
{% if obj.modified %}
{{obj.modified.timestamp|default_if_none:""|naturalday}}
<br>
{{obj.modified.user.username}}
{% else %}
{{obj.created.timestamp|default_if_none:""|naturalday}}
<br>
{{obj.created.user.username}}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Shared with' %}</th>
<td class="align-middle">
{% for user in obj.users.all %}
{% include 'user/includes/contact_modal_button.html' %}
{% endfor %}
</td>
</tr>
</table>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'map/geom_form.html' %}
</div>
</div>
<hr>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'ema/detail/includes/states-before.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'ema/detail/includes/states-after.html' %}
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'ema/detail/includes/actions.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'ema/detail/includes/deadlines.html' %}
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'ema/detail/includes/documents.html' %}
</div>
</div>
{% with 'btn-modal' as btn_class %}
{% include 'modal/modal_form_script.html' %}
{% endwith %}
{% endblock %}

View File

@ -6,11 +6,28 @@ Created on: 19.08.21
""" """
from django.urls import path from django.urls import path
from ema.views import index_view, new_view, open_view from ema.views import *
app_name = "ema" app_name = "ema"
urlpatterns = [ urlpatterns = [
path("", index_view, name="index"), path("", index_view, name="index"),
path("new/", new_view, name="new"), path("new/", new_view, name="new"),
path("<id>", open_view, name="open"), path("<id>", open_view, name="open"),
path('<id>/log', log_view, name='log'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/record', record_view, name='record'),
path('<id>/state/new', state_new_view, name='new-state'),
path('<id>/action/new', action_new_view, name='new-action'),
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
# Documents
# Document remove route can be found in konova/urls.py
path('<id>/document/new/', document_new_view, name='new-doc'),
# Generic state routes
path('state/<id>/remove', state_remove_view, name='state-remove'),
# Generic action routes
path('action/<id>/remove', action_remove_view, name='action-remove'),
] ]

View File

@ -1,16 +1,24 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Sum
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
import compensation
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from ema.tables import EmaTable from ema.tables import EmaTable
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.decorators import conservation_office_group_required from konova.decorators import conservation_office_group_required
from ema.models import Ema from ema.models import Ema
from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.user_checks import in_group
@login_required @login_required
def index_view(request: HttpRequest): def index_view(request: HttpRequest):
""" Renders the index view for MAEs """ Renders the index view for EMAs
Args: Args:
request (HttpRequest): The incoming request request (HttpRequest): The incoming request
@ -38,7 +46,7 @@ def index_view(request: HttpRequest):
@login_required @login_required
@conservation_office_group_required @conservation_office_group_required
def new_view(request: HttpRequest): def new_view(request: HttpRequest):
""" Renders the form for a new MAE """ Renders the form for a new EMA
Args: Args:
request (HttpRequest): The incoming request request (HttpRequest): The incoming request
@ -54,17 +62,224 @@ def new_view(request: HttpRequest):
@login_required @login_required
def open_view(request: HttpRequest, id: str): def open_view(request: HttpRequest, id: str):
""" Renders the form for a new MAE """ Renders the detail view of an EMA
Args: Args:
request (HttpRequest): The incoming request request (HttpRequest): The incoming request
id (str): The MAE id id (str): The EMA id
Returns:
"""
template = "ema/detail/view.html"
ema = get_object_or_404(Ema, id=id, deleted=None)
geom_form = SimpleGeomForm(instance=ema)
_user = request.user
is_data_shared = ema.is_shared_with(_user)
# Order states according to surface
before_states = ema.before_states.all().order_by("-surface")
after_states = ema.after_states.all().order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
diff_states = abs(sum_before_states - sum_after_states)
context = {
"obj": ema,
"geom_form": geom_form,
"has_access": is_data_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": in_group(_user, DEFAULT_GROUP),
"is_zb_member": in_group(_user, ZB_GROUP),
"is_ets_member": in_group(_user, ETS_GROUP),
"LANIS_LINK": ema.get_LANIS_link(),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def log_view(request: HttpRequest, id: str):
""" Renders a log view using modal
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id
Returns: Returns:
""" """
ema = get_object_or_404(Ema, id=id) ema = get_object_or_404(Ema, id=id)
template = "generic_index.html" template = "modal/modal_generic.html"
context = {} body_template = "log.html"
context = {
"modal_body_template": body_template,
"log": ema.log.all(),
"modal_title": _("Log"),
}
context = BaseContext(request, context).context context = BaseContext(request, context).context
return render(request, template, context) return render(request, template, context)
@login_required
def edit_view(request: HttpRequest, id: str):
get_object_or_404(Ema, id=id)
@login_required
def remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing the EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = RemoveModalForm(request.POST or None, instance=ema, user=request.user)
return form.process_request(
request=request,
msg_success=_("EMA removed"),
redirect_url=reverse("ema:index"),
)
@login_required
def record_view(request: HttpRequest, id: str):
""" Renders a modal view for recording the EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = RecordForm(request.POST or None, instance=ema, user=request.user)
return form.process_request(
request=request,
msg_success=_("EMA recorded"),
)
@login_required
def state_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new states for an EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id to which the new state will be related
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = NewStateModalForm(request.POST or None, instance=ema, user=request.user)
return form.process_request(
request,
msg_success=_("State added")
)
@login_required
def action_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new actions for an EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id to which the new state will be related
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = NewActionModalForm(request.POST or None, instance=ema, user=request.user)
return form.process_request(
request,
msg_success=_("Action added")
)
@login_required
def deadline_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new states for an EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id to which the new state will be related
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = NewDeadlineModalForm(request.POST or None, instance=ema, user=request.user)
return form.process_request(
request,
msg_success=_("Deadline added")
)
@login_required
def document_new_view(request: HttpRequest, id: str):
""" Renders a form for uploading new documents
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id to which the new document will be related
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = NewDocumentForm(request.POST or None, request.FILES or None, instance=ema, user=request.user)
return form.process_request(
request,
msg_success=_("Document added")
)
@login_required
def state_remove_view(request: HttpRequest, id: str):
""" Renders a form for removing an EMA state
Args:
request (HttpRequest): The incoming request
id (str): The state's id
Returns:
"""
return compensation.views.compensation_views.state_remove_view(
request,
id
)
@login_required
def action_remove_view(request: HttpRequest, id: str):
""" Renders a form for removing an EMA state
Args:
request (HttpRequest): The incoming request
id (str): The state's id
Returns:
"""
# Reuses the route logic from compensation view
return compensation.views.compensation_views.action_remove_view(
request,
id
)

View File

@ -174,7 +174,7 @@ class Intervention(BaseObject):
""" Quality check """ Quality check
Returns: Returns:
ret_msgs (list): True if quality acceptable, False otherwise ret_msgs (list): Holds error messages
""" """
ret_msgs = [] ret_msgs = []

View File

@ -22,6 +22,7 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from compensation.models import EcoAccount from compensation.models import EcoAccount
from ema.models import Ema
from intervention.models import Intervention from intervention.models import Intervention
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.models import Document, BaseObject from konova.models import Document, BaseObject
@ -370,7 +371,8 @@ class RecordForm(BaseModalForm):
implemented_cls_logic = { implemented_cls_logic = {
Intervention, Intervention,
EcoAccount EcoAccount,
Ema,
} }
instance_name = self.instance.__class__ instance_name = self.instance.__class__
if instance_name not in implemented_cls_logic: if instance_name not in implemented_cls_logic:

Binary file not shown.

View File

@ -1276,7 +1276,7 @@ msgstr "Filter anwenden"
#: templates/log.html:7 #: templates/log.html:7
msgid "Timestamp" msgid "Timestamp"
msgstr "" msgstr "Zeitpunkt"
#: templates/log.html:13 #: templates/log.html:13
msgid "User" msgid "User"