From 69ced85e1eb9673be80095e52435487f35c1ba3d Mon Sep 17 00:00:00 2001 From: SebF Date: Tue, 25 Mar 2025 17:08:41 +0100 Subject: [PATCH] =?UTF-8?q?S=C3=A9paration=20des=20vues=20mod=C3=A9ration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/urls.py | 72 ++++---- src/agenda_culturel/views/__init__.py | 1 + src/agenda_culturel/views/moderation_views.py | 168 +++++++++++++++++ src/agenda_culturel/views/oldviews.py | 174 ++---------------- 4 files changed, 221 insertions(+), 194 deletions(-) create mode 100644 src/agenda_culturel/views/moderation_views.py diff --git a/src/agenda_culturel/urls.py b/src/agenda_culturel/urls.py index 27174b2..5c17241 100644 --- a/src/agenda_culturel/urls.py +++ b/src/agenda_culturel/urls.py @@ -26,7 +26,11 @@ from .views import ( mentions_legales, moderation_rules, thank_you, - # tags + # Moderation + EventModerateView, + moderate_event_next, + moderate_from_date, + # Tags view_tag, view_tag_past, TagUpdateView, @@ -72,8 +76,6 @@ from .views import ( run_all_rimports, EventDetailView, EventUpdateView, - EventModerateView, - moderate_event_next, RecurrentImportCreateView, RecurrentImportDeleteView, RecurrentImportUpdateView, @@ -92,7 +94,6 @@ from .views import ( delete_cm_spam, PlaceCreateView, PlaceFromEventCreateView, - moderate_from_date, update_from_source, change_status_event, EventDeleteView, @@ -149,6 +150,38 @@ urlpatterns = [ path("mentions-legales", mentions_legales, name="mentions_legales"), path("regles-de-moderation", moderation_rules, name="moderation_rules"), path("merci", thank_you, name="thank_you"), + # Moderation + path("moderate", EventModerateView.as_view(), name="moderate"), + path( + "event//moderate", + EventModerateView.as_view(), + name="moderate_event", + ), + path( + "event//moderate-force", + EventModerateView.as_view(), + name="moderate_event_force", + ), + path( + "event//moderate/after/", + EventModerateView.as_view(), + name="moderate_event_step", + ), + path( + "event//moderate/back/", + EventModerateView.as_view(), + name="moderate_event_backstep", + ), + path( + "event//moderate-next", + moderate_event_next, + name="moderate_event_next", + ), + path( + "moderate///", + moderate_from_date, + name="moderate_from_date", + ), # TODO pas encore triƩ path("", home, name="home"), path("cat:/", home, name="home_category"), @@ -218,37 +251,6 @@ urlpatterns = [ path( "event//edit-force", EventUpdateView.as_view(), name="edit_event_force" ), - path( - "event//moderate", - EventModerateView.as_view(), - name="moderate_event", - ), - path( - "event//moderate-force", - EventModerateView.as_view(), - name="moderate_event_force", - ), - path( - "event//moderate/after/", - EventModerateView.as_view(), - name="moderate_event_step", - ), - path( - "event//moderate/back/", - EventModerateView.as_view(), - name="moderate_event_backstep", - ), - path( - "event//moderate-next", - moderate_event_next, - name="moderate_event_next", - ), - path("moderate", EventModerateView.as_view(), name="moderate"), - path( - "moderate///", - moderate_from_date, - name="moderate_from_date", - ), path( "event//simple-clone/edit", EventUpdateView.as_view(), diff --git a/src/agenda_culturel/views/__init__.py b/src/agenda_culturel/views/__init__.py index 3d380be..f35e9ab 100644 --- a/src/agenda_culturel/views/__init__.py +++ b/src/agenda_culturel/views/__init__.py @@ -1,4 +1,5 @@ from .oldviews import * from .errors import * from .general_pages_views import * +from .moderation_views import * from .tag_views import * diff --git a/src/agenda_culturel/views/moderation_views.py b/src/agenda_culturel/views/moderation_views.py new file mode 100644 index 0000000..c1184ca --- /dev/null +++ b/src/agenda_culturel/views/moderation_views.py @@ -0,0 +1,168 @@ +from datetime import date + +from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin +from django.http import Http404, HttpResponseRedirect +from django.shortcuts import render +from django.urls import reverse_lazy +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ +from django.views.generic import UpdateView +from django.db.models import Q, F +from django.utils.timezone import datetime +from ..forms import EventModerateForm +from ..models import Event + + +class EventModerateView( + SuccessMessageMixin, + PermissionRequiredMixin, + LoginRequiredMixin, + UpdateView, +): + model = Event + permission_required = "agenda_culturel.change_event" + template_name = "agenda_culturel/event_form_moderate.html" + form_class = EventModerateForm + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs["is_moderation_expert"] = ( + self.request.user.userprofile.is_moderation_expert + ) + return kwargs + + def get_success_message(self, cleaned_data): + txt = ( + _(" A message has been sent to the person who proposed the event.") + if hasattr(self, "with_msg") and self.with_msg + else "" + ) + return mark_safe( + _('The event {} has been moderated with success.').format( + self.object.get_absolute_url(), self.object.title + ) + + txt + ) + + def is_moderate_next(self): + return "after" in self.request.path.split("/") + + def is_moderate_back(self): + return "back" in self.request.path.split("/") + + def is_moderate_force(self): + return "moderate-force" in self.request.path.split("/") + + def is_starting_moderation(self): + return "pk" not in self.kwargs + + def is_moderation_from_date(self): + return "m" in self.kwargs and "y" in self.kwargs and "d" in self.kwargs + + def get_next_event(start_day, start_time, opk): + # select non moderated events + qs = Event.objects.filter(moderated_date__isnull=True) + + # select events after the current one + if start_time: + qs = qs.filter( + Q(start_day__gt=start_day) + | ( + Q(start_day=start_day) + & (Q(start_time__isnull=True) | Q(start_time__gt=start_time)) + ) + ) + else: + qs = qs.filter(Q(start_day__gte=start_day) & ~Q(pk=opk)) + + # get only possibly representative events + qs = qs.filter( + Q(other_versions__isnull=True) + | Q(other_versions__representative=F("pk")) + | Q(other_versions__representative__isnull=True) + ) + + # remove trash events + qs = qs.filter(~Q(status=Event.STATUS.TRASH)) + + # sort by datetime + qs = qs.order_by("start_day", "start_time") + + return qs.first() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + if self.is_moderate_next(): + context["pred"] = self.kwargs["pred"] + return context + + def get_object(self, queryset=None): + if self.is_starting_moderation(): + now = datetime.now() + event = EventModerateView.get_next_event(now.date(), now.time(), None) + else: + event = super().get_object(queryset) + if event.status == Event.STATUS.DRAFT: + event.status = Event.STATUS.PUBLISHED + if self.is_moderate_back(): + pred = Event.objects.filter(pk=self.kwargs["pred"]).first() + pred.free_modification_lock(self.request.user) + if self.is_moderate_force(): + event.free_modification_lock(self.request.user, False) + return event + + def post(self, request, *args, **kwargs): + try: + return super().post(request, args, kwargs) + except Http404: + return HttpResponseRedirect( + reverse_lazy("error_next_event", args=[self.object.pk]) + ) + + def form_valid(self, form): + form.instance.set_no_modification_date_changed() + form.instance.set_in_moderation_process() + form.instance.set_processing_user(self.request.user) + self.with_msg = form.instance.notify_if_required(self.request) + return super().form_valid(form) + + def get_success_url(self): + if "save_and_next" in self.request.POST: + return reverse_lazy("moderate_event_next", args=[self.object.pk]) + elif "save_and_edit_local" in self.request.POST: + return reverse_lazy("edit_event", args=[self.object.get_local_version().pk]) + else: + return self.object.get_absolute_url() + + +@login_required(login_url="/accounts/login/") +@permission_required("agenda_culturel.change_event") +def moderate_event_next(request, pk): + # current event + obj = Event.objects.filter(pk=pk).first() + # free lock + obj.free_modification_lock(request.user) + start_day = obj.start_day + start_time = obj.start_time + + next_obj = EventModerateView.get_next_event(start_day, start_time, pk) + if next_obj is None: + return render( + request, + "agenda_culturel/event_next_error_message.html", + {"pk": pk, "object": obj}, + ) + else: + return HttpResponseRedirect( + reverse_lazy("moderate_event_step", args=[next_obj.pk, obj.pk]) + ) + + +@login_required(login_url="/accounts/login/") +@permission_required("agenda_culturel.change_event") +def moderate_from_date(request, y, m, d): + d = date(y, m, d) + obj = EventModerateView.get_next_event(d, None, None) + return HttpResponseRedirect(reverse_lazy("moderate_event", args=[obj.pk])) diff --git a/src/agenda_culturel/views/oldviews.py b/src/agenda_culturel/views/oldviews.py index 1ebaa5b..f0fdb6c 100644 --- a/src/agenda_culturel/views/oldviews.py +++ b/src/agenda_culturel/views/oldviews.py @@ -14,10 +14,13 @@ from django.contrib.auth.mixins import ( from django.contrib.messages.views import SuccessMessageMixin from django.core.cache import cache from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from django.db.models import Aggregate, FloatField +from django.db.models import Avg, Max, Min from django.db.models import Count, F, Func, OuterRef, Q, Subquery +from django.db.models.aggregates import StdDev +from django.db.models.functions import ExtractDay from django.db.models.functions import TruncMonth from django.http import ( - Http404, HttpResponse, HttpResponseForbidden, HttpResponseRedirect, @@ -37,10 +40,6 @@ from django.views.generic.edit import ( UpdateView, ) from honeypot.decorators import check_honeypot -from django.db.models.aggregates import StdDev -from django.db.models import Avg, Max, Min -from django.db.models import Aggregate, FloatField -from django.db.models.functions import ExtractDay from ..calendar import CalendarDay, CalendarList, CalendarMonth, CalendarWeek from ..celery import app as celery_app @@ -70,7 +69,6 @@ from ..forms import ( EventAddPlaceForm, EventForm, EventFormWithContact, - EventModerateForm, FixDuplicates, MergeDuplicates, MessageEventForm, @@ -159,6 +157,17 @@ class PaginatorFilter(Paginator): return page +# +# +# Useful for translation +to_be_translated = [ + _("Recurrent import name"), + _("Add another"), + _("Browse..."), + _("No file selected."), +] + + def home(request, cat=None): return week_view(request, home=True, cat=cat) @@ -475,159 +484,6 @@ class EventUpdateView( return self.object.get_absolute_url() -class EventModerateView( - SuccessMessageMixin, - PermissionRequiredMixin, - LoginRequiredMixin, - UpdateView, -): - model = Event - permission_required = "agenda_culturel.change_event" - template_name = "agenda_culturel/event_form_moderate.html" - form_class = EventModerateForm - - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - kwargs["is_moderation_expert"] = ( - self.request.user.userprofile.is_moderation_expert - ) - return kwargs - - def get_success_message(self, cleaned_data): - txt = ( - _(" A message has been sent to the person who proposed the event.") - if hasattr(self, "with_msg") and self.with_msg - else "" - ) - return mark_safe( - _('The event {} has been moderated with success.').format( - self.object.get_absolute_url(), self.object.title - ) - + txt - ) - - def is_moderate_next(self): - return "after" in self.request.path.split("/") - - def is_moderate_back(self): - return "back" in self.request.path.split("/") - - def is_moderate_force(self): - return "moderate-force" in self.request.path.split("/") - - def is_starting_moderation(self): - return "pk" not in self.kwargs - - def is_moderation_from_date(self): - return "m" in self.kwargs and "y" in self.kwargs and "d" in self.kwargs - - def get_next_event(start_day, start_time, opk): - # select non moderated events - qs = Event.objects.filter(moderated_date__isnull=True) - - # select events after the current one - if start_time: - qs = qs.filter( - Q(start_day__gt=start_day) - | ( - Q(start_day=start_day) - & (Q(start_time__isnull=True) | Q(start_time__gt=start_time)) - ) - ) - else: - qs = qs.filter(Q(start_day__gte=start_day) & ~Q(pk=opk)) - - # get only possibly representative events - qs = qs.filter( - Q(other_versions__isnull=True) - | Q(other_versions__representative=F("pk")) - | Q(other_versions__representative__isnull=True) - ) - - # remove trash events - qs = qs.filter(~Q(status=Event.STATUS.TRASH)) - - # sort by datetime - qs = qs.order_by("start_day", "start_time") - - return qs.first() - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - if self.is_moderate_next(): - context["pred"] = self.kwargs["pred"] - return context - - def get_object(self, queryset=None): - if self.is_starting_moderation(): - now = datetime.now() - event = EventModerateView.get_next_event(now.date(), now.time(), None) - else: - event = super().get_object(queryset) - if event.status == Event.STATUS.DRAFT: - event.status = Event.STATUS.PUBLISHED - if self.is_moderate_back(): - pred = Event.objects.filter(pk=self.kwargs["pred"]).first() - pred.free_modification_lock(self.request.user) - if self.is_moderate_force(): - event.free_modification_lock(self.request.user, False) - return event - - def post(self, request, *args, **kwargs): - try: - return super().post(request, args, kwargs) - except Http404: - return HttpResponseRedirect( - reverse_lazy("error_next_event", args=[self.object.pk]) - ) - - def form_valid(self, form): - form.instance.set_no_modification_date_changed() - form.instance.set_in_moderation_process() - form.instance.set_processing_user(self.request.user) - self.with_msg = form.instance.notify_if_required(self.request) - return super().form_valid(form) - - def get_success_url(self): - if "save_and_next" in self.request.POST: - return reverse_lazy("moderate_event_next", args=[self.object.pk]) - elif "save_and_edit_local" in self.request.POST: - return reverse_lazy("edit_event", args=[self.object.get_local_version().pk]) - else: - return self.object.get_absolute_url() - - -@login_required(login_url="/accounts/login/") -@permission_required("agenda_culturel.change_event") -def moderate_event_next(request, pk): - # current event - obj = Event.objects.filter(pk=pk).first() - # free lock - obj.free_modification_lock(request.user) - start_day = obj.start_day - start_time = obj.start_time - - next_obj = EventModerateView.get_next_event(start_day, start_time, pk) - if next_obj is None: - return render( - request, - "agenda_culturel/event_next_error_message.html", - {"pk": pk, "object": obj}, - ) - else: - return HttpResponseRedirect( - reverse_lazy("moderate_event_step", args=[next_obj.pk, obj.pk]) - ) - - -@login_required(login_url="/accounts/login/") -@permission_required("agenda_culturel.change_event") -def moderate_from_date(request, y, m, d): - d = date(y, m, d) - obj = EventModerateView.get_next_event(d, None, None) - return HttpResponseRedirect(reverse_lazy("moderate_event", args=[obj.pk])) - - class EventDeleteView( SuccessMessageMixin, PermissionRequiredMixin,