mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 14:13:21 +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 importlib
|
||||||
|
import threading
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
from django.contrib.auth import get_user
|
from django.contrib.auth import get_user
|
||||||
@ -49,8 +50,31 @@ class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
|
|||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
assert hasattr(request, "session"), (
|
assert hasattr(request, "session"), (
|
||||||
"The Django authentication middleware requires session middleware "
|
"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 "
|
"'django.contrib.sessions.middleware.SessionMiddleware' before "
|
||||||
"'account.middleware.AuthenticationMiddleware'."
|
"'account.middleware.AuthenticationMiddleware'."
|
||||||
)
|
)
|
||||||
request.user = SimpleLazyObject(lambda: get_cached_user(request))
|
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):
|
def is_owned_by(self, user):
|
||||||
return user.is_board_member or user.is_root
|
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.amount }} €</td>
|
||||||
<td>{{ i.get_payment_method_display() }}</td>
|
<td>{{ i.get_payment_method_display() }}</td>
|
||||||
{% if i.is_owned_by(user) %}
|
{% 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 %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
{% if user.is_root %}
|
{% if user.is_root %}
|
||||||
<li><a href="{{ url('core:group_list') }}">{% trans %}Groups{% endtrans %}</a></li>
|
<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: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>
|
<li><a href="{{ url('rootplace:delete_forum_messages') }}">{% trans %}Delete user's forum messages{% endtrans %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.can_create_subscription or user.is_root %}
|
{% if user.can_create_subscription or user.is_root %}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
# -*- coding:utf-8 -*
|
# -*- coding:utf-8 -*
|
||||||
#
|
#
|
||||||
# Copyright 2016,2017
|
# Copyright 2016,2017,2019
|
||||||
# - Skia <skia@libskia.so>
|
# - Skia <skia@libskia.so>
|
||||||
|
# - Sli <antoine@bartuccio.fr>
|
||||||
#
|
#
|
||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
||||||
# http://ae.utbm.fr.
|
# http://ae.utbm.fr.
|
||||||
@ -21,3 +22,5 @@
|
|||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
# 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
|
# Copyright 2016,2017
|
||||||
# - Skia <skia@libskia.so>
|
# - Skia <skia@libskia.so>
|
||||||
|
# - Sli <antoine@bartuccio.fr>
|
||||||
#
|
#
|
||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
||||||
# http://ae.utbm.fr.
|
# http://ae.utbm.fr.
|
||||||
@ -33,4 +34,5 @@ urlpatterns = [
|
|||||||
DeleteAllForumUserMessagesView.as_view(),
|
DeleteAllForumUserMessagesView.as_view(),
|
||||||
name="delete_forum_messages",
|
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.utils.translation import ugettext as _
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
|
from django.views.generic import ListView
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
from ajax_select.fields import AutoCompleteSelectField
|
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 counter.models import Customer
|
||||||
|
|
||||||
from forum.models import ForumMessageMeta
|
from forum.models import ForumMessageMeta
|
||||||
@ -165,3 +167,14 @@ class DeleteAllForumUserMessagesView(FormView):
|
|||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse("core:user_profile", kwargs={"user_id": self.user.id})
|
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.clickjacking.XFrameOptionsMiddleware",
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
"core.middleware.AuthenticationMiddleware",
|
"core.middleware.AuthenticationMiddleware",
|
||||||
|
"core.middleware.SignalRequestMiddleware",
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = "sith.urls"
|
ROOT_URLCONF = "sith.urls"
|
||||||
@ -443,6 +444,11 @@ SITH_PEDAGOGY_UV_RESULT_GRADE = [
|
|||||||
("ABS", _("Abs")),
|
("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_PEDAGOGY_UTBM_API = "https://extranet1.utbm.fr/gpedago/api/guide"
|
||||||
|
|
||||||
SITH_ECOCUP_CONS = 1152
|
SITH_ECOCUP_CONS = 1152
|
||||||
|
Loading…
Reference in New Issue
Block a user