diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index 59d87c2..93bed5c 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -2,7 +2,7 @@ from django.forms import ModelForm, ValidationError, TextInput, Form, URLField, from datetime import date from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget -from .models import Event, BatchImportation, RecurrentImport, CategorisationRule +from .models import Event, BatchImportation, RecurrentImport, CategorisationRule, ModerationAnswer, ModerationQuestion from django.utils.translation import gettext_lazy as _ from string import ascii_uppercase as auc from .templatetags.utils_extra import int_to_abc @@ -235,3 +235,18 @@ class MergeDuplicates(Form): return [auc.rfind(v[-1]) for v in value] else: return auc.rfind(value[-1]) + + +class ModerationQuestionForm(ModelForm): + class Meta: + model = ModerationQuestion + fields = '__all__' + +class ModerationAnswerForm(ModelForm): + class Meta: + model = ModerationAnswer + exclude = ['question'] + widgets = { + 'adds_tags': DynamicArrayWidgetTags(), + 'removes_tags': DynamicArrayWidgetTags() + } diff --git a/src/agenda_culturel/migrations/0039_moderationquestion_moderationanswer.py b/src/agenda_culturel/migrations/0039_moderationquestion_moderationanswer.py new file mode 100644 index 0000000..d9ecdd2 --- /dev/null +++ b/src/agenda_culturel/migrations/0039_moderationquestion_moderationanswer.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.7 on 2024-04-01 14:28 + +from django.db import migrations, models +import django.db.models.deletion +import django_better_admin_arrayfield.models.fields + + + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0038_auto_20240331_1815'), + ] + + operations = [ + migrations.CreateModel( + name='ModerationQuestion', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question', models.CharField(help_text='Text that will be shown to moderators', max_length=512, unique=True, verbose_name='Question')), + ], + ), + migrations.CreateModel( + name='ModerationAnswer', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('answer', models.CharField(help_text='Text that will be shown to moderators', max_length=512, unique=True, verbose_name='Answer')), + ('adds_tags', django_better_admin_arrayfield.models.fields.ArrayField(base_field=models.CharField(max_length=64), blank=True, help_text='A list of tags that will be added if you choose this answer.', null=True, size=None, verbose_name='Adds tags')), + ('removes_tags', django_better_admin_arrayfield.models.fields.ArrayField(base_field=models.CharField(max_length=64), blank=True, help_text='A list of tags that will be removed if you choose this answer.', null=True, size=None, verbose_name='Removes tags')), + ('description', models.ForeignKey(help_text='Associated question from moderation', on_delete=django.db.models.deletion.CASCADE, to='agenda_culturel.moderationquestion', verbose_name='Question')), + ], + ), + ] diff --git a/src/agenda_culturel/migrations/0040_auto_20240403_1924.py b/src/agenda_culturel/migrations/0040_auto_20240403_1924.py new file mode 100644 index 0000000..18aaeb4 --- /dev/null +++ b/src/agenda_culturel/migrations/0040_auto_20240403_1924.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.7 on 2024-04-03 17:24 + +from django.db import migrations +from django.contrib.auth.management import create_permissions +from django.contrib.auth.models import Group, Permission + + +def update_groups_permissions(apps, schema_editor): + + all_perms = Permission.objects.all() + moderator_perms = [i for i in all_perms if i.content_type.app_label == 'agenda_culturel' and i.content_type.model in ['event', 'duplicatedevents']] + read_mod_perms = [i for i in moderator_perms if i.codename.startswith('view_')] + + # set permissions for receptionists + qanda_perms = [i for i in all_perms if i.content_type.app_label == 'agenda_culturel' and i.content_type.model in ['moderationquestion', 'moderationanswer']] + Group.objects.get(name="Q&A Manager").permissions.add(*qanda_perms) + Group.objects.get(name="Q&A Manager").permissions.add(*read_mod_perms) + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0039_moderationquestion_moderationanswer'), + ] + + operations = [ + migrations.RunPython(update_groups_permissions), + ] diff --git a/src/agenda_culturel/migrations/0041_rename_description_moderationanswer_question.py b/src/agenda_culturel/migrations/0041_rename_description_moderationanswer_question.py new file mode 100644 index 0000000..9e1e384 --- /dev/null +++ b/src/agenda_culturel/migrations/0041_rename_description_moderationanswer_question.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-04-03 20:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0040_auto_20240403_1924'), + ] + + operations = [ + migrations.RenameField( + model_name='moderationanswer', + old_name='description', + new_name='question', + ), + ] diff --git a/src/agenda_culturel/migrations/0042_alter_moderationanswer_answer.py b/src/agenda_culturel/migrations/0042_alter_moderationanswer_answer.py new file mode 100644 index 0000000..e0aee61 --- /dev/null +++ b/src/agenda_culturel/migrations/0042_alter_moderationanswer_answer.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-04-03 21:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0041_rename_description_moderationanswer_question'), + ] + + operations = [ + migrations.AlterField( + model_name='moderationanswer', + name='answer', + field=models.CharField(help_text='Text that will be shown to moderators', max_length=512, verbose_name='Answer'), + ), + ] diff --git a/src/agenda_culturel/migrations/0043_alter_moderationanswer_question.py b/src/agenda_culturel/migrations/0043_alter_moderationanswer_question.py new file mode 100644 index 0000000..7cfb659 --- /dev/null +++ b/src/agenda_culturel/migrations/0043_alter_moderationanswer_question.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.7 on 2024-04-03 21:14 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0042_alter_moderationanswer_answer'), + ] + + operations = [ + migrations.AlterField( + model_name='moderationanswer', + name='question', + field=models.ForeignKey(help_text='Associated question from moderation', on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='agenda_culturel.moderationquestion', verbose_name='Question'), + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 33632ed..ef0b5f5 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -851,3 +851,27 @@ class CategorisationRule(models.Model): return True return False + + +class ModerationQuestion(models.Model): + + question = models.CharField(verbose_name=_('Question'), help_text=_('Text that will be shown to moderators'), max_length=512, unique=True) + + + def __str__(self): + char_limit = 30 + return (self.question[:char_limit] + "...") if char_limit < len(self.question) else self.question + + def get_absolute_url(self): + return reverse("view_mquestion", kwargs={"pk": self.pk}) + + +class ModerationAnswer(models.Model): + + question = models.ForeignKey(ModerationQuestion, related_name="answers", verbose_name=_('Question'), help_text=_('Associated question from moderation'), on_delete=models.CASCADE) + + answer = models.CharField(verbose_name=_('Answer'), help_text=_('Text that will be shown to moderators'), max_length=512) + + 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) + diff --git a/src/agenda_culturel/templates/agenda_culturel/moderationanswer_confirm_delete.html b/src/agenda_culturel/templates/agenda_culturel/moderationanswer_confirm_delete.html new file mode 100644 index 0000000..09c4e9e --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/moderationanswer_confirm_delete.html @@ -0,0 +1,19 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}Supprimer la réponse #{{ object.pk }}{% endblock %} + + +{% block content %} + +

Suppression de la réponse de modération {{ object.pk }}

+
{% csrf_token %} +

Êtes-vous sûr·e de vouloir supprimer la réponse de modération #{{object.pk}} « {{ object.answer }} » associée à la question « {{ object.question.question }} » ? +

+ {{ form }} +
+ Annuler + +
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/moderationanswer_form.html b/src/agenda_culturel/templates/agenda_culturel/moderationanswer_form.html new file mode 100644 index 0000000..516fa24 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/moderationanswer_form.html @@ -0,0 +1,31 @@ +{% extends "agenda_culturel/page.html" %} +{% load static %} + +{% block title %}{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une réponse de modération{% endblock %} + +{% block entete_header %} + + + + + + +{% endblock %} + + +{% block content %} + +

{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une réponse de modération

+

{% if form.instance.pk %}Modifier{% else %}Ajouter{% endif %} une réponse à la question « {{ question }} »

+ +
+
{% csrf_token %} + {{ form.as_p }} +
+ Annuler + +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/moderationquestion_confirm_delete.html b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_confirm_delete.html new file mode 100644 index 0000000..907b9be --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_confirm_delete.html @@ -0,0 +1,19 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}Supprimer la question #{{ object.pk }}{% endblock %} + + +{% block content %} + +

Suppression de la question de modération {{ object.pk }}

+
{% csrf_token %} +

Êtes-vous sûr·e de vouloir supprimer la question de modération #{{object.pk}} « {{ object.question }} » ainsi que les réponses associées ? +

+ {{ form }} +
+ Annuler + +
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/moderationquestion_detail.html b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_detail.html new file mode 100644 index 0000000..cf8eee5 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_detail.html @@ -0,0 +1,67 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}Question de modération #{{ object.pk }}{% endblock %} + +{% load tag_extra %} +{% load utils_extra %} +{% load cat_extra %} + +{% block entete_header %} + {% css_categories %} +{% endblock %} + +{% block content %} + + +
+
+
+ < Retour + +

Question de modération #{{ object.pk }}

+

{{ object.question }}

+
+ + {% if object.answers %} + {% for answer in object.answers.all %} +
+ +
+

Réponse #{{ answer.pk }} : « {{ answer.answer }} »

+ {% if answer.adds_tags %} +

Cette réponse ajoute les étiquettes suivantes à l'événement : + {% for tag in answer.adds_tags %} + {{ tag | tag_button }} + {% endfor %} +

+ {% else %} +

Cette réponse n'ajoute pas d'étiquette à l'événement.

+ {% endif %} + {% if answer.removes_tags %} +

Cette réponse supprimer les étiquettes suivantes à l'événement : + {% for tag in answer.removes_tags %} + {{ tag | tag_button }} + {% endfor %} +

+ {% else %} +

Cette réponse ne supprime pas d'étiquette à l'événement.

+ {% endif %} +
+
+ {% endfor %} + {% else %} + Il n'y a pas encore de réponse associée à cette question. + {% endif %} +
+ +{% include "agenda_culturel/side-nav.html" with current="moderationquestions" %} +
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/moderationquestion_form.html b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_form.html new file mode 100644 index 0000000..b10a0a9 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_form.html @@ -0,0 +1,22 @@ +{% extends "agenda_culturel/page.html" %} +{% load static %} + +{% block title %} +{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une question de modération +{% endblock %} + + +{% block content %} + +

{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une question de modération

+
+
{% csrf_token %} + {{ form.as_p }} +
+ Annuler + +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/moderationquestion_list.html b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_list.html new file mode 100644 index 0000000..608a9a5 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/moderationquestion_list.html @@ -0,0 +1,65 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}Questions de modération{% endblock %} + +{% load utils_extra %} +{% load cat_extra %} +{% block entete_header %} + {% css_categories %} +{% endblock %} + +{% block content %} +
+
+
+ Ajouter {% picto_from_name "plus-circle" %} +

Questions de modération

+
+ {% if object_list %} + {% for question in object_list %} +
+
+ Détails... +

Question #{{ question.pk }} : {{ question.question }}

+

{% if question.answers %} +

réponses possibles :

+
    + {% for answer in question.answers.all %} +
  • {{ answer.answer }}
  • + {% endfor %} +
+ {% else %} +

aucune réponse définie

+ {% endif %} +

+
+ +
+ {% endfor %} + {% else %} +

Il n'y a aucune question définie.

+ {% endif %} + + +
+ +{% include "agenda_culturel/side-nav.html" with current="moderationquestions" %} +
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/side-nav.html b/src/agenda_culturel/templates/agenda_culturel/side-nav.html index 741b53c..ea93345 100644 --- a/src/agenda_culturel/templates/agenda_culturel/side-nav.html +++ b/src/agenda_culturel/templates/agenda_culturel/side-nav.html @@ -43,6 +43,14 @@ {% endif %} + {% if perms.agenda_culturel.view_moderationquestion %} +

Paramétrage de la modération

+ + {% endif %} {% if user.is_staff %}

Configuration interne