Compare commits
1 Commits
c230014176
...
6b3f37321c
Author | SHA1 | Date |
---|---|---|
Frédéric Péters | 6b3f37321c |
|
@ -0,0 +1,56 @@
|
|||
# chrono - agendas system
|
||||
# Copyright (C) 2016-2024 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
|
||||
import django_filters
|
||||
from django.forms.widgets import DateInput
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from chrono.agendas.models import Agenda
|
||||
|
||||
from .models import AuditEntry
|
||||
|
||||
|
||||
class DateWidget(DateInput):
|
||||
input_type = 'date'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['format'] = '%Y-%m-%d'
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class DayFilter(django_filters.DateFilter):
|
||||
def filter(self, qs, value):
|
||||
if value:
|
||||
qs = qs.filter(timestamp__gte=value, timestamp__lt=value + datetime.timedelta(days=1))
|
||||
return qs
|
||||
|
||||
|
||||
class JournalFilterSet(django_filters.FilterSet):
|
||||
timestamp = DayFilter(widget=DateWidget())
|
||||
agenda = django_filters.ModelChoiceFilter(queryset=Agenda.objects.all())
|
||||
action_type = django_filters.ChoiceFilter(
|
||||
choices=(
|
||||
('booking', _('Booking')),
|
||||
('check', _('Checking')),
|
||||
('invoice', _('Invoicing')),
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = AuditEntry
|
||||
fields = []
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.2.16 on 2024-03-26 08:40
|
||||
# Generated by Django 3.2.16 on 2024-04-15 13:59
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
@ -22,8 +22,8 @@ class Migration(migrations.Migration):
|
|||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('action_type', models.CharField(max_length=100)),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
|
||||
('action_type', models.CharField(max_length=100, verbose_name='Action type')),
|
||||
('action_text', models.TextField()),
|
||||
(
|
||||
'agenda',
|
||||
|
@ -37,7 +37,10 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name='User',
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class AuditEntry(models.Model):
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
|
||||
action_type = models.CharField(max_length=100)
|
||||
timestamp = models.DateTimeField(verbose_name=_('Date'), auto_now_add=True)
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, verbose_name=_('User'), on_delete=models.SET_NULL, null=True
|
||||
)
|
||||
action_type = models.CharField(verbose_name=_('Action type'), max_length=100)
|
||||
action_text = models.TextField()
|
||||
agenda = models.ForeignKey(
|
||||
'agendas.Agenda', on_delete=models.SET_NULL, null=True, related_name='audit_entries'
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{% extends "chrono/manager_base.html" %}
|
||||
{% load gadjo i18n %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'chrono-manager-audit-journal' %}">{% trans "Audit journal" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans "Audit journal" %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<table class="main">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "User" %}</th>
|
||||
<th>{% trans "Agenda" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for line in object_list %}
|
||||
<tr>
|
||||
<td>{{ line.timestamp }}</td>
|
||||
<td>{{ line.user.get_full_name }}</td>
|
||||
<td>{{ line.agenda }}</td>
|
||||
<td>{{ line.action_text }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include "gadjo/pagination.html" %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
<h3>{% trans "Search" %}</h3>
|
||||
<form>
|
||||
{{ filter.form|with_template }}
|
||||
<div class="buttons">
|
||||
<button>{% trans "Search" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</aside>
|
||||
{% endblock %}
|
|
@ -0,0 +1,24 @@
|
|||
# chrono - agendas system
|
||||
# Copyright (C) 2016-2024 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.journal_home, name='chrono-manager-audit-journal'),
|
||||
]
|
|
@ -0,0 +1,42 @@
|
|||
# chrono - agendas system
|
||||
# Copyright (C) 2016-2024 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.views.generic import ListView
|
||||
|
||||
from .forms import JournalFilterSet
|
||||
|
||||
|
||||
class JournalHomeView(ListView):
|
||||
template_name = 'chrono/journal/home.html'
|
||||
paginate_by = 10
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_staff:
|
||||
raise PermissionDenied()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
self.filterset = JournalFilterSet(self.request.GET)
|
||||
return self.filterset.qs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['filter'] = self.filterset
|
||||
return context
|
||||
|
||||
|
||||
journal_home = JournalHomeView.as_view()
|
|
@ -71,6 +71,9 @@
|
|||
<a class="button button-paragraph" href="{% url 'chrono-manager-ants-hub' %}">{% trans 'ANTS Hub' %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user.is_staff and audit_journal_enabled %}
|
||||
<a class="button button-paragraph" href="{% url 'chrono-manager-audit-journal' %}">{% trans 'Audit journal' %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'chrono/includes/application_list_fragment.html' with title_no_application=_('Agendas outside applications') %}
|
||||
|
|
|
@ -568,4 +568,5 @@ urlpatterns = [
|
|||
),
|
||||
re_path(r'^menu.json$', views.menu_json),
|
||||
path('ants/', include('chrono.apps.ants_hub.urls')),
|
||||
path('journal/', include('chrono.apps.journal.urls')),
|
||||
]
|
||||
|
|
|
@ -222,6 +222,7 @@ class HomepageView(WithApplicationsMixin, ListView):
|
|||
context['has_access_to_unavailability_calendars'] = self.has_access_to_unavailability_calendars()
|
||||
context['shared_custody_enabled'] = settings.SHARED_CUSTODY_ENABLED
|
||||
context['ants_hub_enabled'] = bool(settings.CHRONO_ANTS_HUB_URL)
|
||||
context['audit_journal_enabled'] = settings.AUDIT_JOURNAL_ENABLED
|
||||
context['with_sidebar'] = True
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
|
|
@ -208,6 +208,7 @@ REST_FRAMEWORK = {'EXCEPTION_HANDLER': 'chrono.api.utils.exception_handler'}
|
|||
SHARED_CUSTODY_ENABLED = False
|
||||
PARTIAL_BOOKINGS_ENABLED = False
|
||||
SNAPSHOTS_ENABLED = False
|
||||
AUDIT_JOURNAL_ENABLED = False
|
||||
|
||||
CHRONO_ANTS_HUB_URL = None
|
||||
|
||||
|
|
Loading…
Reference in New Issue