Ajout modération par questions

Fix #68
This commit is contained in:
Jean-Marie Favreau 2024-04-13 11:30:58 +02:00
parent ed3c58c313
commit 6f3000c51b
10 changed files with 175 additions and 32 deletions

View File

@ -250,3 +250,23 @@ class ModerationAnswerForm(ModelForm):
'adds_tags': DynamicArrayWidgetTags(),
'removes_tags': DynamicArrayWidgetTags()
}
class ModerateForm(ModelForm):
class Meta:
model = Event
fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
mqs = ModerationQuestion.objects.all()
mas = ModerationAnswer.objects.all()
for q in mqs:
self.fields[q.complete_id()] = ChoiceField(widget=RadioSelect, label=q.question, choices=[(a.pk, a.html_description()) for a in mas if a.question == q], required=True)
for a in mas:
if a.question == q and a.valid_event(self.instance):
self.fields[q.complete_id()].initial = a.pk
break

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.7 on 2024-04-13 09:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agenda_culturel', '0045_auto_20240405_1941'),
]
operations = [
migrations.AddField(
model_name='event',
name='moderated_date',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@ -1,6 +1,7 @@
from django.db import models
from django_better_admin_arrayfield.models.fields import ArrayField
from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe
from django.template.defaultfilters import slugify
from django.urls import reverse
from colorfield.fields import ColorField
@ -161,6 +162,7 @@ class Event(models.Model):
created_date = models.DateTimeField(editable=False)
imported_date = models.DateTimeField(blank=True, null=True)
modified_date = models.DateTimeField(blank=True, null=True)
moderated_date = models.DateTimeField(blank=True, null=True)
recurrence_dtstart = models.DateTimeField(editable=False, blank=True, null=True)
recurrence_dtend = models.DateTimeField(editable=False, blank=True, null=True)
@ -880,6 +882,9 @@ class ModerationQuestion(models.Model):
def get_absolute_url(self):
return reverse("view_mquestion", kwargs={"pk": self.pk})
def complete_id(self):
return "question_" + str(self.pk)
class ModerationAnswer(models.Model):
@ -890,3 +895,41 @@ class ModerationAnswer(models.Model):
adds_tags = ArrayField(models.CharField(max_length=64), verbose_name=_('Adds tags'), help_text=_("A list of tags that will be added if you choose this answer."), blank=True, null=True)
removes_tags = ArrayField(models.CharField(max_length=64), verbose_name=_('Removes tags'), help_text=_("A list of tags that will be removed if you choose this answer."), blank=True, null=True)
def complete_id(self):
return "answer_" + str(self.question.pk) + '_' + str(self.pk)
def html_description(self):
result = self.answer + '<br /><span class="helptext">'
if self.adds_tags:
result += ' '.join(['<span role="button" class="small-cat">' + a + '</span>' for a in self.adds_tags])
if self.removes_tags:
result += ' '.join(['<span role="button" class="small-cat strike">' + a + '</span>' for a in self.removes_tags])
result += "</span>"
return mark_safe(result)
def valid_event(self, event):
if event.tags:
for t in self.adds_tags:
if t not in event.tags:
return False
for t in self.removes_tags:
if t in event.tags:
return False
return True
else:
return not self.adds_tags or len(self.adds_tags) == 0
def apply_answer(self, event):
if not self.adds_tags:
self.adds_tags = []
if not self.removes_tags:
self.removes_tags = []
logger.info('on applique la réponse ' + self.answer)
if event.tags:
event.tags = list((set(event.tags) | set(self.adds_tags)) - set(self.removes_tags))
else:
event.tags = self.adds_tags

View File

@ -1,6 +1,7 @@
{% load utils_extra %}
<a href="{% url 'edit_event' event.id %}" role="button">modifier {% picto_from_name "edit" %}</a>
<a href="{% url 'moderate_event' event.id %}" role="button">modérer {% picto_from_name "edit" %}</a>
<a href="{% url 'edit_event' event.id %}" role="button">modifier {% picto_from_name "edit-3" %}</a>
{% if event.status != "published" %}
<a href="{% url 'change_status_event' event.id 'published' %}" role="button">publier {% picto_from_name "eye" %}</a>

View File

@ -0,0 +1,30 @@
<footer class="remarque">
Informations complémentaires non éditables&nbsp;:
<ul>
{% if object.created_date %}<li>Création&nbsp;: {{ object.created_date }}</li>{% endif %}
{% if object.modified_date %}<li>Dernière modification&nbsp;: {{ object.modified_date }}</li>{% endif %}
{% if object.moderated_date %}<li>Dernière modération&nbsp;: {{ object.moderated_date }}</li>{% endif %}
{% if object.imported_date %}<li>Dernière importation&nbsp;: {{ object.imported_date }}</li>{% endif %}
{% if object.uuids %}
{% if object.uuids|length > 0 %}
<li>UUIDs (identifiants uniques d'événements dans les sources)&nbsp;:
<ul>
{% for u in object.uuids %}
<li>{{ u }}</li>
{% endfor %}
</ul></li>
{% endif %}
{% endif %}
{% if object.import_sources %}
{% if object.import_sources|length > 0 %}
<li>Sources d'import&nbsp;:
<ul>
{% for u in object.import_sources %}
<li><a href="{{ u }}">{{ u }}</a></li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endif %}
</ul>
</footer>

View File

@ -46,35 +46,7 @@
</div>
</form>
{% if object %}
<footer class="remarque">
Informations complémentaires non éditables&nbsp;:
<ul>
{% if object.created_date %}<li>Création&nbsp;: {{ object.created_date }}</li>{% endif %}
{% if object.modified_date %}<li>Dernière modification&nbsp;: {{ object.modified_date }}</li>{% endif %}
{% if object.imported_date %}<li>Dernière importation&nbsp;: {{ object.imported_date }}</li>{% endif %}
{% if object.uuids %}
{% if object.uuids|length > 0 %}
<li>UUIDs (identifiants uniques d'événements dans les sources)&nbsp;:
<ul>
{% for u in object.uuids %}
<li>{{ u }}</li>
{% endfor %}
</ul></li>
{% endif %}
{% endif %}
{% if object.import_sources %}
{% if object.import_sources|length > 0 %}
<li>Sources d'import&nbsp;:
<ul>
{% for u in object.import_sources %}
<li><a href="{{ u }}">{{ u }}</a></li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endif %}
</ul>
</footer>
{% include "agenda_culturel/event-info-inc.html" %}
{% endif %}
</article>

View File

@ -0,0 +1,34 @@
{% extends "agenda_culturel/event_form.html" %}
{% load static %}
{% block title %}Modérer {{ object.title }}{% endblock %}
{% block content %}
{% load static_content_extra %}
<article>
<header>
<h1>Modération de l'événement {{ object.title }} ({{ object.start_day }})</h1>
</header>
{% include "agenda_culturel/single-event/event-single-inc.html" with event=object noedit=1 %}
<div id="container"></div>
<form method="post">{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<div class="grid">
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
<input type="submit" value="Enregistrer">
</div>
</form>
{% include "agenda_culturel/event-info-inc.html" %}
</article>
{% endblock %}

View File

@ -65,9 +65,12 @@
{% if event.modified %}
— dernière modification&nbsp;: {{ event.modified_date }}
{% endif %}
{% if event.moderated_date %}
— dernière modération&nbsp;: {{ event.moderated_date }}
{% endif %}
</p>
</div>
{% if perms.agenda_culturel.change_event %}
{% if perms.agenda_culturel.change_event and not noedit %}
<div class="buttons">
{% include "agenda_culturel/edit-buttons-inc.html" with event=event %}
</div>

View File

@ -25,6 +25,7 @@ urlpatterns = [
path("event/<int:pk>/change-status/<status>", change_status_event, name="change_status_event"),
path("event/<int:pk>/delete", EventDeleteView.as_view(), name="delete_event"),
path("event/<int:year>/<int:month>/<int:day>/<int:pk>/set_duplicate", set_duplicate, name="set_duplicate"),
path("event/<int:pk>/moderate", EventModerateView.as_view(), name="moderate_event"),
path("ajouter", import_from_url, name="add_event"),
path("admin/", admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),

View File

@ -11,7 +11,7 @@ from django.http import HttpResponseRedirect
from django.urls import reverse
import urllib
from .forms import EventSubmissionForm, EventForm, BatchImportationForm, FixDuplicates, SelectEventInList, MergeDuplicates, RecurrentImportForm, CategorisationRuleImportForm, ModerationQuestionForm, ModerationAnswerForm
from .forms import EventSubmissionForm, EventForm, BatchImportationForm, FixDuplicates, SelectEventInList, MergeDuplicates, RecurrentImportForm, CategorisationRuleImportForm, ModerationQuestionForm, ModerationAnswerForm, ModerateForm
from .models import Event, Category, StaticContent, ContactMessage, BatchImportation, DuplicatedEvents, RecurrentImport, CategorisationRule, remove_accents, ModerationQuestion, ModerationAnswer
from django.utils import timezone
@ -259,6 +259,27 @@ class EventDetailView(UserPassesTestMixin, DetailView):
return obj
class EventModerateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
model = Event
permission_required = ["agenda_culturel.changes_event", "use_moderation_question"]
success_message = _('The event has been successfully modified.')
form_class = ModerateForm
template_name = 'agenda_culturel/event_moderation_form.html'
def form_valid(self, form):
mas = ModerationAnswer.objects.all()
logger.warning("ON valide la forme")
for f in form.cleaned_data:
ModerationAnswer.objects.get(pk=form.cleaned_data[f]).apply_answer(form.instance)
form.instance.moderated_date = timezone.now()
return super().form_valid(form)
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.change_event')
def change_status_event(request, pk, status):