From e5be88b055fab375af5a09858679f5e705e820d9 Mon Sep 17 00:00:00 2001 From: Dorian Lesbre <dorian.lesbre@gmail.com> Date: Mon, 13 Sep 2021 09:50:28 +0200 Subject: [PATCH] WIP on activity form --- admin_pages/views.py | 5 +- home/forms.py | 37 +++++++ home/models.py | 123 +++++++++++++++++++----- home/templates/activity_submission.html | 89 +++++++++++++++++ home/urls.py | 1 + home/views.py | 18 +++- interludes/settings.py | 2 + site_settings/models.py | 9 +- 8 files changed, 249 insertions(+), 35 deletions(-) create mode 100644 home/templates/activity_submission.html diff --git a/admin_pages/views.py b/admin_pages/views.py index dee6081..ee62229 100644 --- a/admin_pages/views.py +++ b/admin_pages/views.py @@ -408,7 +408,7 @@ class NewEmail(SuperuserRequiredMixin, FormView): """Créer un nouveau mail""" template_name = "send_email.html" form_class = SendEmailForm - success_url = "admin_pages:index" + success_url = reverse_lazy("admin_pages:index") from_address = None def get_emails(self, selection): @@ -477,6 +477,3 @@ class NewEmail(SuperuserRequiredMixin, FormView): return super().get(request, *args, **kwargs) messages.error(request, "L'envoi de mail de masse est désactivé dans les réglages") return HttpResponseRedirect(self.get_success_url()) - - def get_success_url(self): - return reverse(self.success_url) diff --git a/home/forms.py b/home/forms.py index bc8cb63..463dda0 100644 --- a/home/forms.py +++ b/home/forms.py @@ -57,3 +57,40 @@ class BaseActivityFormSet(forms.BaseFormSet): if activity in activities: raise ValidationError("Vous ne pouvez pas sélectionner une même activtté plusieurs fois") activities.append(activity) + + +class ActivitySubmissionForm(FormRenderMixin, forms.ModelForm): + + class Meta: + model = models.ActivityModel + fields = ( + "title", "act_type", "game_type", "description", + + "host_info", + + "must_subscribe", "communicate_participants", + "max_participants", "min_participants", + + "duration", "desired_slot_nb", + "available_friday_evening", + "available_friday_night", + "available_saturday_morning", + "available_saturday_afternoon", + "available_saturday_evening", + "available_saturday_night", + "available_sunday_morning", + "available_sunday_afternoon", + "constraints", + + "status", "needs", + + "comments", + ) + + + def save(self, *args, commit=True, **kwargs): + participant = super().save(*args, commit=False, **kwargs) + participant.is_registered = True + if commit: + participant.save() + return participant diff --git a/home/models.py b/home/models.py index 7177f23..301a431 100644 --- a/home/models.py +++ b/home/models.py @@ -7,6 +7,13 @@ from django.utils import timezone from accounts.models import EmailUser from site_settings.models import Colors, SiteSettings +def validate_nonzero(value): + """Make a positive integer field non-zero""" + if value == 0: + raise ValidationError( + _('Cette valeur doit-être non-nulle'), + ) + class ActivityModel(models.Model): """une activité des interludes (i.e. JDR, murder)...""" @@ -16,12 +23,16 @@ class ActivityModel(models.Model): DISTANT = "D", _("En distanciel uniquement") BOTH = "2", _("Les deux") - class Types(models.TextChoices): - """types d'activités""" + class ActivityTypes(models.TextChoices): + """quantité d'activité""" + GAME = "1 partie", _("Une partie") + GAMES = "2+ parties", _("Quelques parties") TOURNAMENT = "Tournoi", _("Tournoi") - GAME = "partie", _("Une partie") - GAMES = "parties", _("Quelques parties") FREEPLAY = "freeplay", _("Freeplay") + OTHER = "other", _("Autre") + + class GameTypes(models.TextChoices): + """types de jeu""" CARD_GAME = "jeu cartes", _("Jeu de cartes") BOARD_GAME = "jeu plateau", _("Jeu de société") TABLETOP_RPG = "table RPG", _("Jeu de rôle sur table") @@ -33,25 +44,31 @@ class ActivityModel(models.Model): COOP = "coop", _("Jeu coopératif") OTHER = "other", _("Autre") - title = models.CharField("Titre", max_length=200) + class Availability(models.TextChoices): + """Diponibilité à un moment donné""" + IDEAL = "0", _("Idéal") + POSSIBLE = "1", _("Acceptable") + UNAVAILABLE = "2", _("Indisponible") - status = models.CharField("Présentiel/distanciel", choices=Status.choices, max_length=1) - act_type = models.CharField("Type", choices=Types.choices, max_length=12) + display = models.BooleanField("afficher dans la liste", default=False, + help_text="Si vrai, s'affiche sur la page activités" + ) - duration = models.DurationField("Durée", help_text="format hh:mm:ss") - max_participants = models.PositiveIntegerField( - "Nombre maximum de participants", help_text="0 pour illimité" + title = models.CharField("Titre", max_length=200) + + act_type = models.CharField("Type d'activité", choices=ActivityTypes.choices, max_length=12) + game_type = models.CharField("Type de jeu", choices=GameTypes.choices, max_length=12) + description = models.TextField( + "description", max_length=10000, + help_text='Texte ou html selon la valeur de "Description HTML".\n' ) - min_participants = models.PositiveIntegerField( - "Nombre minimum de participants" + desc_as_html = models.BooleanField("Description au format HTML", default=False, + help_text="Assurer vous que le texte est bien formaté, cette option peut casser la page activités." ) - communicate_participants = models.BooleanField("communiquer la liste des participants à l'orga avant l'événement") - display = models.BooleanField("afficher dans la liste", default=False, - help_text="Si vrai, s'affiche sur la page activités" - ) - must_subscribe = models.BooleanField("sur inscription", default=False, - help_text="Informatif, il faut utiliser les créneaux pour ajouter dans la liste d'inscription" + host = models.ForeignKey( + EmailUser, on_delete=models.SET_NULL, verbose_name="Organisateur", + blank=True, null=True ) host_name = models.CharField( "nom de l'organisateur", max_length=50, null=True, blank=True, @@ -61,15 +78,73 @@ class ActivityModel(models.Model): "email de l'organisateur", help_text="Utilisé pour communiquer la liste des participants si demandé" ) - description = models.TextField( - "description", max_length=10000, - help_text='Texte ou html selon la valeur de "Description HTML".\n' + host_info = models.TextField( + "Autre orgas/contacts", max_length=1000, blank=True, null=True ) - desc_as_html = models.BooleanField("Description au format HTML", default=False, - help_text="Assurer vous que le texte est bien formaté, cette option peut casser la page activités." + + must_subscribe = models.BooleanField("sur inscription", default=False, + help_text="Informatif, il faut utiliser les créneaux pour ajouter dans la liste d'inscription" + ) + communicate_participants = models.BooleanField("communiquer la liste des participants à l'orga avant l'événement") + max_participants = models.PositiveIntegerField( + "Nombre maximum de participants", help_text="0 pour illimité", default=0 + ) + min_participants = models.PositiveIntegerField( + "Nombre minimum de participants", default=0 + ) + + ## Information fournies par le respo + duration = models.DurationField("Durée", help_text="format hh:mm:ss") + desired_slot_nb = models.PositiveIntegerField( + "Nombre de créneaux souhaités", default=1, + validators=[validate_nonzero] + ) + + available_friday_evening = models.CharField( + "Crénau vendredi soir", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_friday_night = models.CharField( + "Crénau vendredi nuit", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_saturday_morning = models.CharField( + "Crénau samedi matin", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_saturday_afternoon = models.CharField( + "Crénau samedi après-midi", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_saturday_evening = models.CharField( + "Crénau samedi soir", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_saturday_night = models.CharField( + "Crénau samedi nuit", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_sunday_morning = models.CharField( + "Crénau dimanche matin", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + available_sunday_afternoon = models.CharField( + "Crénau dimanche après-midi", choices=Availability.choices, max_length=1, + default=Availability.POSSIBLE, + ) + + constraints = models.TextField( + "Contraintes particulières", max_length=2000, blank=True, null=True ) - notes = models.TextField("Notes privées", max_length=2000, blank=True) + status = models.CharField("Présentiel/distanciel", choices=Status.choices, max_length=1) + needs = models.TextField( + "Besoin particuliers", max_length=2000, blank=True, null=True + ) + + comments = models.TextField( + "Commentaires", max_length=2000, blank=True, null=True + ) @property def nb_participants(self) -> str: diff --git a/home/templates/activity_submission.html b/home/templates/activity_submission.html new file mode 100644 index 0000000..381cbd6 --- /dev/null +++ b/home/templates/activity_submission.html @@ -0,0 +1,89 @@ +{% extends "base.html" %} +{% load static %} + +{% block nav_activite %}current{% endblock %} + +{% block "content" %} +<h2>Proposer une activité</h2> + +<form method="post" action="{% url 'activity_submission' %}"> + {% csrf_token %} + + <table> + <tr><td><strong>Titre :</strong></td><td> {{ form.title }}</td></tr> + <tr><td><strong>Type d'activité :</strong></td><td> {{ form.act_type }}</td></tr> + <tr><td><strong>Type de jeu :</strong></td><td> {{ form.game_type }}</td></tr> + </table> + + <p><strong>Description :</strong> ce texte sera affiché sur la page + <a href="{% url 'activites' %}">activités</a> pour présenter votre activité. + </p> + {{ form.description }} + + <h3>Organisateurs</h3> + + <p> + L'email de votre compte sera le principal moyen de contact. + Vous pouvez spécifiez d'autre moyen de contact ci-dessous, ainsi que d'autres + organisateurs si vous êtes plusieurs + </p> + + {{ form.host_info }} + + <h3>Participants et inscription</h3> + + <table> + <tr><td><strong>Nécessite une inscription :</strong></td><td> {{ form.must_subscribe }}</td></tr> + <tr><td><strong>Me communiquer la liste des participants à l'avance :</strong></td><td> {{ form.communicate_participants }}</td></tr> + + <tr><td><strong>Nombre max de participants :</strong></td><td> {{ form.max_participants }} (mettez 0 pour illimité)</td></tr> + <tr><td><strong>Nombre min de participants :</strong></td><td> {{ form.min_participants }}</td></tr> + </table> + + <h3>Durée et crénaux</h3> + + <table> + <tr><td><strong>Durée approximative :</strong></td><td> {{ form.duration }} (format hh:mm:ss)</td></tr> + <tr><td><strong>Nombre de crénaux souhaités :</strong></td><td> {{ form.desired_slot_nb }}</td></tr> + <tr><td><strong>Disponibilités :</strong></td></tr> + <tr><td> - Vendredi soir :</td><td>{{ form.available_friday_evening }}</td></tr> + <tr><td> - Vendredi nuit :</td><td>{{ form.available_friday_night }}</td></tr> + <tr><td> - Samedi matin :</td><td>{{ form.available_saturday_morning }}</td></tr> + <tr><td> - Samedi après-midi :</td><td>{{ form.available_saturday_afternoon }}</td></tr> + <tr><td> - Samedi soir :</td><td>{{ form.available_saturday_evening }}</td></tr> + <tr><td> - Samedi nuit :</td><td>{{ form.available_saturday_night }}</td></tr> + <tr><td> - Dimanche matin :</td><td>{{ form.available_sunday_morning }}</td></tr> + <tr><td> - Dimanche après-midi :</td><td>{{ form.available_sunday_afternoon }}</td></tr> + </table> + + <p>Si vous avez des contraintes particulières, vous pouvez les préciser ici :</p> + + {{ form.constraints }} + + <h3>Modalités pratiques</h3> + + <p><strong>Présentiel/distanciel :</strong> {{ form.status }}</p> + + <p><strong>Besoins spécifiques :</strong></p> + <ul> + <li>Vous faut-il une ou plusieurs salles ?</li> + <li>Vous faut-il du matériel spécial ?</li> + <li>Si possible en distanciel, quel outils faut-il pour participer ?</li> + <li>Et tout autre besoin de ce type...</li> + </ul> + + {{ form.needs }} + + <h3>Commentaires</h3> + + <p>D'autre choses à nous communiquer qui ne rentre pas dans les champs précédents ?</p> + + {{ form.comments }} + + <div class="flex"> + <input type="submit" value="Valider"> + <a class="button" href="{% url 'profile' %}">Annuler</a> + </div> + +</form> +{% endblock %} diff --git a/home/urls.py b/home/urls.py index f71aefa..8fd2ddd 100644 --- a/home/urls.py +++ b/home/urls.py @@ -13,6 +13,7 @@ urlpatterns = [ path('inscription/', views.RegisterView.as_view(), name = 'inscription'), path('desinscription/', views.UnregisterView.as_view(), name="desinscription"), path('activites/', views.ActivityView.as_view(), name = 'activites'), + path('activites/nouvelle/', views.ActivitySubmissionView.as_view(), name = 'activity_submission'), path('faq/', views.FAQView.as_view(), name = 'FAQ'), path("profile/", views.ProfileView.as_view(), name="profile"), path('favicon.ico', RedirectView.as_view(url='/static/imgs/favicon.ico')), diff --git a/home/views.py b/home/views.py index 99644bc..c1decb9 100644 --- a/home/views.py +++ b/home/views.py @@ -5,11 +5,11 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.sitemaps import Sitemap from django.forms import formset_factory from django.shortcuts import redirect, render -from django.urls import reverse -from django.views.generic import RedirectView, TemplateView, View +from django.urls import reverse, reverse_lazy +from django.views.generic import FormView, RedirectView, TemplateView, View from home import models -from home.forms import ActivityForm, BaseActivityFormSet, InscriptionForm +from home.forms import ActivityForm, ActivitySubmissionForm, BaseActivityFormSet, InscriptionForm from site_settings.models import SiteSettings @@ -160,6 +160,18 @@ class UnregisterView(LoginRequiredMixin, RedirectView): return reverse(self.pattern_name) +# ============================== +# Activity Submission Form +# ============================== + + +class ActivitySubmissionView(LoginRequiredMixin, FormView): + """Vue pour proposer une activité""" + template_name = "activity_submission.html" + form_class = ActivitySubmissionForm + success_url = reverse_lazy("profile") + + # ============================== # Sitemap # ============================== diff --git a/interludes/settings.py b/interludes/settings.py index 9a44862..7a6ab3e 100644 --- a/interludes/settings.py +++ b/interludes/settings.py @@ -123,6 +123,8 @@ TEMPLATES = [ WSGI_APPLICATION = 'interludes.wsgi.application' +# Auto primary key type +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases diff --git a/site_settings/models.py b/site_settings/models.py index 3398541..8bc73df 100644 --- a/site_settings/models.py +++ b/site_settings/models.py @@ -71,7 +71,7 @@ class SiteSettings(SingletonModel): date_start = models.DateField("Date de début", blank=True, null=True) date_end = models.DateField("Date de fin", blank=True, null=True) - registrations_open = models.BooleanField("Ouvrir la création de compte", default=False) + registrations_open = models.BooleanField("Ouvrir la création de compte", default=True) inscriptions_open = models.BooleanField("Ouvrir les inscriptions", default=False) inscriptions_start = models.DateTimeField("Ouverture des inscriptions", @@ -94,10 +94,11 @@ class SiteSettings(SingletonModel): help_text="Suppose que l'allocation des activités a été effectuée." ) - activity_submission_form = models.CharField( - "Lien pour soumettre une activité", max_length=200, default="", - blank=True, null=True + activity_submission_open = models.BooleanField( + "Ouvrir l'ajout d'activité", default=False, + help_text="Permet de proposer une activité via le formulaire dédié" ) + discord_link = models.CharField( "Lien du serveur discord", max_length=200, blank=True, null=True ) -- GitLab