mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 15:51:19 +00:00
Merge branch 'deletion_logs' into 'master'
Add generic operation logs and implements it for Sellings and Refilling deletions See merge request ae/Sith!259
This commit is contained in:
commit
50c2f8164d
@ -23,6 +23,7 @@
|
||||
#
|
||||
|
||||
import importlib
|
||||
import threading
|
||||
from django.conf import settings
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from django.contrib.auth import get_user
|
||||
@ -49,8 +50,31 @@ class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
|
||||
def process_request(self, request):
|
||||
assert hasattr(request, "session"), (
|
||||
"The Django authentication middleware requires session middleware "
|
||||
"to be installed. Edit your MIDDLEWARE_CLASSES setting to insert "
|
||||
"to be installed. Edit your MIDDLEWARE setting to insert "
|
||||
"'django.contrib.sessions.middleware.SessionMiddleware' before "
|
||||
"'account.middleware.AuthenticationMiddleware'."
|
||||
)
|
||||
request.user = SimpleLazyObject(lambda: get_cached_user(request))
|
||||
|
||||
|
||||
_threadlocal = threading.local()
|
||||
|
||||
|
||||
def get_signal_request():
|
||||
"""
|
||||
!!! Do not use if your operation is asynchronus !!!
|
||||
Allow to access current request in signals
|
||||
This is a hack that looks into the thread
|
||||
Mainly used for log purpose
|
||||
"""
|
||||
|
||||
return getattr(_threadlocal, "request", None)
|
||||
|
||||
|
||||
class SignalRequestMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
setattr(_threadlocal, "request", request)
|
||||
return self.get_response(request)
|
||||
|
51
core/migrations/0034_operationlog.py
Normal file
51
core/migrations/0034_operationlog.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Generated by Django 2.2.6 on 2019-11-14 15:10
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0033_auto_20191006_0049"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="OperationLog",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("date", models.DateTimeField(auto_now_add=True, verbose_name="date")),
|
||||
("label", models.CharField(max_length=255, verbose_name="label")),
|
||||
(
|
||||
"operation_type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("SELLING_DELETION", "Selling deletion"),
|
||||
("REFILLING_DELETION", "Refilling deletion"),
|
||||
],
|
||||
max_length=40,
|
||||
verbose_name="operation type",
|
||||
),
|
||||
),
|
||||
(
|
||||
"operator",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="logs",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
@ -1454,3 +1454,24 @@ class Gift(models.Model):
|
||||
|
||||
def is_owned_by(self, user):
|
||||
return user.is_board_member or user.is_root
|
||||
|
||||
|
||||
class OperationLog(models.Model):
|
||||
"""
|
||||
General purpose log object to register operations
|
||||
"""
|
||||
|
||||
date = models.DateTimeField(_("date"), auto_now_add=True)
|
||||
label = models.CharField(_("label"), max_length=255)
|
||||
operator = models.ForeignKey(
|
||||
User, related_name="logs", on_delete=models.SET_NULL, null=True
|
||||
)
|
||||
operation_type = models.CharField(
|
||||
_("operation type"), max_length=40, choices=settings.SITH_LOG_OPERATION_TYPE,
|
||||
)
|
||||
|
||||
def is_owned_by(self, user):
|
||||
return user.is_root
|
||||
|
||||
def __str__(self):
|
||||
return "%s - %s - %s" % (self.operation_type, self.label, self.operator)
|
||||
|
@ -63,7 +63,7 @@
|
||||
<td>{{ i.amount }} €</td>
|
||||
<td>{{ i.get_payment_method_display() }}</td>
|
||||
{% if i.is_owned_by(user) %}
|
||||
<td><a href="{{ url('counter:refilling_delete', refilling_id=i.id) }}">Delete</a></td>
|
||||
<td><a href="{{ url('counter:refilling_delete', refilling_id=i.id) }}">{% trans %}Delete{% endtrans %}</a></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -13,6 +13,7 @@
|
||||
{% if user.is_root %}
|
||||
<li><a href="{{ url('core:group_list') }}">{% trans %}Groups{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('rootplace:merge') }}">{% trans %}Merge users{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('rootplace:operation_logs') }}">{% trans %}Operation logs{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('rootplace:delete_forum_messages') }}">{% trans %}Delete user's forum messages{% endtrans %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.can_create_subscription or user.is_root %}
|
||||
|
@ -1,7 +1,8 @@
|
||||
# -*- coding:utf-8 -*
|
||||
#
|
||||
# Copyright 2016,2017
|
||||
# Copyright 2016,2017,2019
|
||||
# - Skia <skia@libskia.so>
|
||||
# - Sli <antoine@bartuccio.fr>
|
||||
#
|
||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
||||
# http://ae.utbm.fr.
|
||||
@ -21,3 +22,5 @@
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
|
||||
default_app_config = "counter.app.CounterConfig"
|
||||
|
34
counter/app.py
Normal file
34
counter/app.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding:utf-8 -*
|
||||
#
|
||||
# Copyright 2019
|
||||
# - Sli <antoine@bartuccio.fr>
|
||||
#
|
||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
||||
# http://ae.utbm.fr.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License a 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 General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class CounterConfig(AppConfig):
|
||||
name = "counter"
|
||||
verbose_name = _("counter")
|
||||
|
||||
def ready(self):
|
||||
import counter.signals
|
69
counter/signals.py
Normal file
69
counter/signals.py
Normal file
@ -0,0 +1,69 @@
|
||||
# -*- coding:utf-8 -*
|
||||
#
|
||||
# Copyright 2019
|
||||
# - Sli <antoine@bartuccio.fr>
|
||||
#
|
||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
||||
# http://ae.utbm.fr.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License a 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 General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from django.db.models.signals import pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
|
||||
from core.middleware import get_signal_request
|
||||
from core.models import OperationLog
|
||||
|
||||
from counter.models import Selling, Refilling, Counter
|
||||
|
||||
|
||||
def write_log(instance, operation_type):
|
||||
def get_user():
|
||||
request = get_signal_request()
|
||||
|
||||
if not request:
|
||||
return None
|
||||
|
||||
# Get a random barmen if deletion is from a counter
|
||||
session = getattr(request, "session", {})
|
||||
session_token = session.get("counter_token", None)
|
||||
if session_token:
|
||||
counter = Counter.objects.filter(token=session_token).first()
|
||||
if counter and len(counter.get_barmen_list()) > 0:
|
||||
return counter.get_random_barman()
|
||||
|
||||
# Get the current logged user if not from a counter
|
||||
if request.user and not request.user.is_anonymous:
|
||||
return request.user
|
||||
|
||||
# Return None by default
|
||||
return None
|
||||
|
||||
log = OperationLog(
|
||||
label=str(instance), operator=get_user(), operation_type=operation_type,
|
||||
).save()
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Refilling, dispatch_uid="write_log_refilling_deletion")
|
||||
def write_log_refilling_deletion(sender, instance, **kwargs):
|
||||
write_log(instance, "REFILLING_DELETION")
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Selling, dispatch_uid="write_log_refilling_deletion")
|
||||
def write_log_selling_deletion(sender, instance, **kwargs):
|
||||
write_log(instance, "SELLING_DELETION")
|
File diff suppressed because it is too large
Load Diff
32
rootplace/templates/rootplace/logs.jinja
Normal file
32
rootplace/templates/rootplace/logs.jinja
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
{% from 'core/macros.jinja' import paginate %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}Operation logs{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans %}Date{% endtrans %}</th>
|
||||
<th>{% trans %}Operation type{% endtrans %}</th>
|
||||
<th>{% trans %}Label{% endtrans %}</th>
|
||||
<th>{% trans %}Operator{% endtrans %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in object_list %}
|
||||
<tr>
|
||||
<td>{{ log.date }} </td>
|
||||
<td>{{ log.get_operation_type_display() }}</td>
|
||||
<td>{{ log.label }}</td>
|
||||
<td>{{ log.operator }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
{{ paginate(page_obj, paginator) }}
|
||||
{% endblock content %}
|
@ -2,6 +2,7 @@
|
||||
#
|
||||
# Copyright 2016,2017
|
||||
# - Skia <skia@libskia.so>
|
||||
# - Sli <antoine@bartuccio.fr>
|
||||
#
|
||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
||||
# http://ae.utbm.fr.
|
||||
@ -33,4 +34,5 @@ urlpatterns = [
|
||||
DeleteAllForumUserMessagesView.as_view(),
|
||||
name="delete_forum_messages",
|
||||
),
|
||||
re_path(r"^logs$", OperationLogListView.as_view(), name="operation_logs"),
|
||||
]
|
||||
|
@ -25,13 +25,15 @@
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic.edit import FormView
|
||||
from django.views.generic import ListView
|
||||
from django.urls import reverse
|
||||
from django import forms
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
from core.models import User
|
||||
from core.views import CanEditPropMixin
|
||||
from core.models import User, OperationLog
|
||||
from counter.models import Customer
|
||||
|
||||
from forum.models import ForumMessageMeta
|
||||
@ -165,3 +167,14 @@ class DeleteAllForumUserMessagesView(FormView):
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("core:user_profile", kwargs={"user_id": self.user.id})
|
||||
|
||||
|
||||
class OperationLogListView(ListView, CanEditPropMixin):
|
||||
"""
|
||||
List all logs
|
||||
"""
|
||||
|
||||
model = OperationLog
|
||||
template_name = "rootplace/logs.jinja"
|
||||
ordering = ["-date"]
|
||||
paginate_by = 100
|
||||
|
@ -106,6 +106,7 @@ MIDDLEWARE = (
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"core.middleware.AuthenticationMiddleware",
|
||||
"core.middleware.SignalRequestMiddleware",
|
||||
)
|
||||
|
||||
ROOT_URLCONF = "sith.urls"
|
||||
@ -443,6 +444,11 @@ SITH_PEDAGOGY_UV_RESULT_GRADE = [
|
||||
("ABS", _("Abs")),
|
||||
]
|
||||
|
||||
SITH_LOG_OPERATION_TYPE = [
|
||||
(("SELLING_DELETION"), _("Selling deletion")),
|
||||
(("REFILLING_DELETION"), _("Refilling deletion")),
|
||||
]
|
||||
|
||||
SITH_PEDAGOGY_UTBM_API = "https://extranet1.utbm.fr/gpedago/api/guide"
|
||||
|
||||
SITH_ECOCUP_CONS = 1152
|
||||
|
Loading…
Reference in New Issue
Block a user