1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-04-01 19:31:11 +00:00

Add survey notification in the menu

This commit is contained in:
Emmy D'Anello 2025-03-19 23:56:53 +01:00
parent 702c8d8c9e
commit 97eea3b11a
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
11 changed files with 450 additions and 339 deletions

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.db.models.signals import post_save, pre_save from django.db.models.signals import post_save, pre_save
from django.utils.translation import gettext_lazy as _
class ParticipationConfig(AppConfig): class ParticipationConfig(AppConfig):
@ -10,6 +11,7 @@ class ParticipationConfig(AppConfig):
The participation app contains the data about the teams, solutions, ... The participation app contains the data about the teams, solutions, ...
""" """
name = 'participation' name = 'participation'
verbose_name = _("participations")
def ready(self): def ready(self):
from participation import signals from participation import signals

View File

@ -3,13 +3,12 @@
from datetime import date, timedelta from datetime import date, timedelta
import math import math
import os
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
from django.db import models from django.db import models
from django.db.models import Index from django.db.models import Index, Q
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils import timezone, translation from django.utils import timezone, translation
@ -847,6 +846,8 @@ class Participation(models.Model):
return _("Participation of the team {name} ({trigram})").format(name=self.team.name, trigram=self.team.trigram) return _("Participation of the team {name} ({trigram})").format(name=self.team.name, trigram=self.team.trigram)
def important_informations(self): def important_informations(self):
from survey.models import Survey
informations = [] informations = []
missing_payments = Payment.objects.filter(registrations__in=self.team.participants.all(), valid=False) missing_payments = Payment.objects.filter(registrations__in=self.team.participants.all(), valid=False)
@ -865,6 +866,19 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
if self.valid:
for survey in Survey.objects.filter(Q(tournament__isnull=True) | Q(tournament=self.tournament), Q(invite_team=True),
~Q(completed_teams=self.team)).all():
text = _("Please answer to the survey \"{name}\". You can go to the survey on <a href=\"{survey_link}\">that link</a>, "
"using the token code you received by mail.")
content = format_lazy(text, name=survey.name, survey_link=f"{settings.LIMESURVEY_URL}/index.php/{survey.survey_id}")
informations.append({
'title': _("Required answer to survey"),
'type': "warning",
'priority': 12,
'content': content
})
if self.tournament: if self.tournament:
informations.extend(self.informations_for_tournament(self.tournament)) informations.extend(self.informations_for_tournament(self.tournament))
if self.final: if self.final:

View File

@ -234,7 +234,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
mail_plain = render_to_string("participation/mails/request_validation.txt", mail_context) mail_plain = render_to_string("participation/mails/request_validation.txt", mail_context)
mail_html = render_to_string("participation/mails/request_validation.html", mail_context) mail_html = render_to_string("participation/mails/request_validation.html", mail_context)
send_mail(f"[{settings.APP_NAME}] {_('Team validation')}", mail_plain, settings.DEFAULT_FROM_EMAIL, send_mail(f"[{settings.APP_NAME}] {_('Team validation')}", mail_plain, settings.DEFAULT_FROM_EMAIL,
[self.object.participation.tournament.organizers_email], html_message=mail_html) [self.object.participation.tournament.organizers_email], html_message=mail_html)
return super().form_valid(form) return super().form_valid(form)
@ -270,7 +270,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context_plain) mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context_plain)
mail_html = render_to_string("participation/mails/team_validated.html", mail_context_html) mail_html = render_to_string("participation/mails/team_validated.html", mail_context_html)
registration.user.email_user(f"[{settings.APP_NAME}] {_('Team validated')}", mail_plain, registration.user.email_user(f"[{settings.APP_NAME}] {_('Team validated')}", mail_plain,
html_message=mail_html) html_message=mail_html)
elif "invalidate" in self.request.POST: elif "invalidate" in self.request.POST:
self.object.participation.valid = None self.object.participation.valid = None
self.object.participation.save() self.object.participation.save()
@ -280,7 +280,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
mail_plain = render_to_string("participation/mails/team_not_validated.txt", mail_context_plain) mail_plain = render_to_string("participation/mails/team_not_validated.txt", mail_context_plain)
mail_html = render_to_string("participation/mails/team_not_validated.html", mail_context_html) mail_html = render_to_string("participation/mails/team_not_validated.html", mail_context_html)
send_mail(f"[{settings.APP_NAME}] {_('Team not validated')}", mail_plain, send_mail(f"[{settings.APP_NAME}] {_('Team not validated')}", mail_plain,
None, [self.object.email], html_message=mail_html) None, [self.object.email], html_message=mail_html)
else: else:
form.add_error(None, _("You must specify if you validate the registration or not.")) form.add_error(None, _("You must specify if you validate the registration or not."))
return self.form_invalid(form) return self.form_invalid(form)

View File

@ -3,6 +3,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.db.models.signals import post_save, pre_save from django.db.models.signals import post_save, pre_save
from django.utils.translation import gettext_lazy as _
class RegistrationConfig(AppConfig): class RegistrationConfig(AppConfig):
@ -10,6 +11,7 @@ class RegistrationConfig(AppConfig):
Registration app contains the detail about users only. Registration app contains the detail about users only.
""" """
name = 'registration' name = 'registration'
verbose_name = _("registrations")
def ready(self): def ready(self):
from registration import signals from registration import signals

View File

@ -8,6 +8,7 @@ from django.contrib.sites.models import Site
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Q
from django.template import loader from django.template import loader
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils import timezone, translation from django.utils import timezone, translation
@ -260,6 +261,8 @@ class ParticipantRegistration(Registration):
raise NotImplementedError raise NotImplementedError
def registration_informations(self): def registration_informations(self):
from survey.models import Survey
informations = [] informations = []
if not self.team: if not self.team:
text = _("You are not in a team. You can <a href=\"{create_url}\">create one</a> " text = _("You are not in a team. You can <a href=\"{create_url}\">create one</a> "
@ -300,6 +303,20 @@ class ParticipantRegistration(Registration):
'content': content, 'content': content,
}) })
if self.team.participation.valid:
for survey in Survey.objects.filter(Q(tournament__isnull=True) | Q(tournament=self.team.participation.tournament),
Q(invite_team=False), Q(invite_coaches=True) | Q(invite_coaches=self.is_coach),
~Q(completed_registrations=self)):
text = _("Please answer to the survey \"{name}\". You can go to the survey on <a href=\"{survey_link}\">that link</a>, "
"using the token code you received by mail.")
content = format_lazy(text, name=survey.name, survey_link=f"{settings.LIMESURVEY_URL}/index.php/{survey.survey_id}")
informations.append({
'title': _("Required answer to survey"),
'type': "warning",
'priority': 12,
'content': content
})
informations.extend(self.team.important_informations()) informations.extend(self.team.important_informations())
return informations return informations
@ -315,19 +332,19 @@ class ParticipantRegistration(Registration):
tournament = Tournament.final_tournament() tournament = Tournament.final_tournament()
payment = self.payments.filter(final=True).first() if self.is_student else None payment = self.payments.filter(final=True).first() if self.is_student else None
message = loader.render_to_string('registration/mails/final_selection.txt', message = loader.render_to_string('registration/mails/final_selection.txt',
{ {
'user': self.user, 'user': self.user,
'domain': site.domain, 'domain': site.domain,
'tournament': tournament, 'tournament': tournament,
'payment': payment, 'payment': payment,
}) })
html = loader.render_to_string('registration/mails/final_selection.html', html = loader.render_to_string('registration/mails/final_selection.html',
{ {
'user': self.user, 'user': self.user,
'domain': site.domain, 'domain': site.domain,
'tournament': tournament, 'tournament': tournament,
'payment': payment, 'payment': payment,
}) })
self.user.email_user(subject, message, html_message=html) self.user.email_user(subject, message, html_message=html)
class Meta: class Meta:
@ -807,9 +824,9 @@ class Payment(models.Model):
site = Site.objects.first() site = Site.objects.first()
for registration in self.registrations.all(): for registration in self.registrations.all():
message = loader.render_to_string('registration/mails/payment_reminder.txt', message = loader.render_to_string('registration/mails/payment_reminder.txt',
dict(registration=registration, payment=self, domain=site.domain)) dict(registration=registration, payment=self, domain=site.domain))
html = loader.render_to_string('registration/mails/payment_reminder.html', html = loader.render_to_string('registration/mails/payment_reminder.html',
dict(registration=registration, payment=self, domain=site.domain)) dict(registration=registration, payment=self, domain=site.domain))
registration.user.email_user(subject, message, html_message=html) registration.user.email_user(subject, message, html_message=html)
def send_helloasso_payment_confirmation_mail(self): def send_helloasso_payment_confirmation_mail(self):
@ -818,18 +835,18 @@ class Payment(models.Model):
site = Site.objects.first() site = Site.objects.first()
for registration in self.registrations.all(): for registration in self.registrations.all():
message = loader.render_to_string('registration/mails/payment_confirmation.txt', message = loader.render_to_string('registration/mails/payment_confirmation.txt',
dict(registration=registration, payment=self, domain=site.domain)) dict(registration=registration, payment=self, domain=site.domain))
html = loader.render_to_string('registration/mails/payment_confirmation.html', html = loader.render_to_string('registration/mails/payment_confirmation.html',
dict(registration=registration, payment=self, domain=site.domain)) dict(registration=registration, payment=self, domain=site.domain))
registration.user.email_user(subject, message, html_message=html) registration.user.email_user(subject, message, html_message=html)
payer = self.get_checkout_intent()['order']['payer'] payer = self.get_checkout_intent()['order']['payer']
payer_name = f"{payer['firstName']} {payer['lastName']}" payer_name = f"{payer['firstName']} {payer['lastName']}"
if not self.registrations.filter(user__email=payer['email']).exists(): if not self.registrations.filter(user__email=payer['email']).exists():
message = loader.render_to_string('registration/mails/payment_confirmation.txt', message = loader.render_to_string('registration/mails/payment_confirmation.txt',
dict(registration=payer_name, payment=self, domain=site.domain)) dict(registration=payer_name, payment=self, domain=site.domain))
html = loader.render_to_string('registration/mails/payment_confirmation.html', html = loader.render_to_string('registration/mails/payment_confirmation.html',
dict(registration=payer_name, payment=self, domain=site.domain)) dict(registration=payer_name, payment=self, domain=site.domain))
send_mail(subject, message, None, [payer['email']], html_message=html) send_mail(subject, message, None, [payer['email']], html_message=html)
def get_absolute_url(self): def get_absolute_url(self):

View File

@ -145,9 +145,9 @@ class AddOrganizerView(VolunteerMixin, CreateView):
password=password, password=password,
domain=site.domain)) domain=site.domain))
html = render_to_string('registration/mails/add_organizer.html', dict(user=registration.user, html = render_to_string('registration/mails/add_organizer.html', dict(user=registration.user,
inviter=self.request.user, inviter=self.request.user,
password=password, password=password,
domain=site.domain)) domain=site.domain))
registration.user.email_user(subject, message, html_message=html) registration.user.email_user(subject, message, html_message=html)
if registration.is_admin: if registration.is_admin:

View File

@ -1,6 +1,11 @@
# Copyright (C) 2025 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class SurveyConfig(AppConfig): class SurveyConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField" default_auto_field = "django.db.models.BigAutoField"
name = "survey" name = "survey"
verbose_name = _("surveys")

View File

@ -0,0 +1,53 @@
# Generated by Django 5.1.5 on 2025-03-19 22:51
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"participation",
"0023_tournament_unified_registration",
),
("registration", "0014_participantregistration_country"),
("survey", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="survey",
name="completed_registrations",
field=models.ManyToManyField(
blank=True,
related_name="completed_surveys",
to="registration.participantregistration",
verbose_name="participants that completed the survey",
),
),
migrations.AlterField(
model_name="survey",
name="completed_teams",
field=models.ManyToManyField(
blank=True,
related_name="completed_surveys",
to="participation.team",
verbose_name="teams that completed the survey",
),
),
migrations.AlterField(
model_name="survey",
name="tournament",
field=models.ForeignKey(
blank=True,
default=None,
help_text="When this field is filled, the survey participants will be restricted to this tournament members.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="surveys",
to="participation.tournament",
verbose_name="tournament restriction",
),
),
]

View File

@ -45,18 +45,21 @@ class Survey(models.Model):
blank=True, blank=True,
default=None, default=None,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name="surveys",
verbose_name=_("tournament restriction"), verbose_name=_("tournament restriction"),
help_text=_("When this field is filled, the survey participants will be restricted to this tournament members."), help_text=_("When this field is filled, the survey participants will be restricted to this tournament members."),
) )
completed_registrations = models.ManyToManyField( completed_registrations = models.ManyToManyField(
ParticipantRegistration, ParticipantRegistration,
blank=True,
related_name="completed_surveys", related_name="completed_surveys",
verbose_name=_("participants that completed the survey"), verbose_name=_("participants that completed the survey"),
) )
completed_teams = models.ManyToManyField( completed_teams = models.ManyToManyField(
Team, Team,
blank=True,
related_name="completed_surveys", related_name="completed_surveys",
verbose_name=_("teams that completed the survey"), verbose_name=_("teams that completed the survey"),
) )

View File

@ -1,8 +1,6 @@
# Copyright (C) 2020 by Animath # Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os
from django.conf import settings from django.conf import settings
_client = None _client = None