mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-22 10:38:25 +02:00
Compare commits
11 Commits
main
...
ca0601fb24
Author | SHA1 | Date | |
---|---|---|---|
ca0601fb24
|
|||
d315c8371a
|
|||
7488d3eae1
|
|||
cfaf7c4287
|
|||
e3c216e44e
|
|||
73012bd61e
|
|||
bdf181e7e4
|
|||
c57ad854fe
|
|||
a2e5ab5f6a
|
|||
758a2c9a00
|
|||
fb10df77e5
|
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management import BaseCommand
|
from django.core.management import BaseCommand
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
from participation.models import Team, Tournament
|
from participation.models import Team, Tournament
|
||||||
from registration.models import ParticipantRegistration, VolunteerRegistration
|
from registration.models import ParticipantRegistration, VolunteerRegistration
|
||||||
from tfjm.lists import get_sympa_client
|
from tfjm.lists import get_sympa_client
|
||||||
@ -36,7 +37,7 @@ class Command(BaseCommand):
|
|||||||
"education", raise_error=False)
|
"education", raise_error=False)
|
||||||
|
|
||||||
for tournament in Tournament.objects.all():
|
for tournament in Tournament.objects.all():
|
||||||
slug = tournament.name.lower().replace(" ", "-")
|
slug = slugify(tournament.name)
|
||||||
sympa.create_list(f"equipes-{slug}", f"Equipes du tournoi {tournament.name}", "hotline",
|
sympa.create_list(f"equipes-{slug}", f"Equipes du tournoi {tournament.name}", "hotline",
|
||||||
f"Liste de diffusion pour contacter toutes les equipes du tournoi {tournament.name}"
|
f"Liste de diffusion pour contacter toutes les equipes du tournoi {tournament.name}"
|
||||||
" du TFJM2.", "education", raise_error=False)
|
" du TFJM2.", "education", raise_error=False)
|
||||||
@ -54,7 +55,7 @@ class Command(BaseCommand):
|
|||||||
for team in Team.objects.filter(participation__valid=True).all():
|
for team in Team.objects.filter(participation__valid=True).all():
|
||||||
team.create_mailing_list()
|
team.create_mailing_list()
|
||||||
sympa.unsubscribe(team.email, "equipes-non-valides", True)
|
sympa.unsubscribe(team.email, "equipes-non-valides", True)
|
||||||
sympa.subscribe(team.email, f"equipes-{team.participation.tournament.name.lower().replace(' ', '-')}",
|
sympa.subscribe(team.email, f"equipes-{slugify(team.participation.tournament.name)}",
|
||||||
True, f"Equipe {team.name}")
|
True, f"Equipe {team.name}")
|
||||||
|
|
||||||
for team in Team.objects.filter(Q(participation__valid=False) | Q(participation__valid__isnull=True)).all():
|
for team in Team.objects.filter(Q(participation__valid=False) | Q(participation__valid__isnull=True)).all():
|
||||||
@ -62,16 +63,16 @@ class Command(BaseCommand):
|
|||||||
sympa.subscribe(team.email, "equipes-non-valides", True, f"Equipe {team.name}")
|
sympa.subscribe(team.email, "equipes-non-valides", True, f"Equipe {team.name}")
|
||||||
|
|
||||||
for participant in ParticipantRegistration.objects.filter(team__isnull=False).all():
|
for participant in ParticipantRegistration.objects.filter(team__isnull=False).all():
|
||||||
sympa.subscribe(participant.user.email, f"equipe-{participant.team.trigram.lower()}",
|
sympa.subscribe(participant.user.email, f"equipe-{slugify(participant.team.trigram)}",
|
||||||
True, f"{participant}")
|
True, f"{participant}")
|
||||||
|
|
||||||
for volunteer in VolunteerRegistration.objects.all():
|
for volunteer in VolunteerRegistration.objects.all():
|
||||||
for organized_tournament in volunteer.organized_tournaments.all():
|
for organized_tournament in volunteer.organized_tournaments.all():
|
||||||
slug = organized_tournament.name.lower().replace(" ", "-")
|
slug = slugify(organized_tournament.name)
|
||||||
sympa.subscribe(volunteer.user.email, f"organisateurs-{slug}", True)
|
sympa.subscribe(volunteer.user.email, f"organisateurs-{slug}", True)
|
||||||
|
|
||||||
for jury_in in volunteer.jury_in.all():
|
for jury_in in volunteer.jury_in.all():
|
||||||
slug = jury_in.tournament.name.lower().replace(" ", "-")
|
slug = slugify(jury_in.tournament.name)
|
||||||
sympa.subscribe(volunteer.user.email, f"jurys-{slug}", True)
|
sympa.subscribe(volunteer.user.email, f"jurys-{slug}", True)
|
||||||
|
|
||||||
for admin in VolunteerRegistration.objects.filter(admin=True).all():
|
for admin in VolunteerRegistration.objects.filter(admin=True).all():
|
||||||
|
@ -15,6 +15,12 @@ from ...models import Tournament
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
"""
|
||||||
|
Création de notifications Google Drive pour récupérer les modifications sur les tableurs de notes.
|
||||||
|
|
||||||
|
Documentation de l'API : https://developers.google.com/calendar/api/guides/push?hl=fr
|
||||||
|
"""
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--tournament', '-t', help="Tournament name to update (if not set, all tournaments will be updated)",
|
'--tournament', '-t', help="Tournament name to update (if not set, all tournaments will be updated)",
|
||||||
|
@ -10,6 +10,7 @@ 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
|
||||||
|
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
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
@ -210,14 +211,14 @@ class Team(models.Model):
|
|||||||
"""
|
"""
|
||||||
:return: The mailing list to contact the team members.
|
:return: The mailing list to contact the team members.
|
||||||
"""
|
"""
|
||||||
return f"equipe-{self.trigram.lower()}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
return f"equipe-{slugify(self.trigram)}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
||||||
|
|
||||||
def create_mailing_list(self):
|
def create_mailing_list(self):
|
||||||
"""
|
"""
|
||||||
Create a new Sympa mailing list to contact the team.
|
Create a new Sympa mailing list to contact the team.
|
||||||
"""
|
"""
|
||||||
get_sympa_client().create_list(
|
get_sympa_client().create_list(
|
||||||
f"equipe-{self.trigram.lower()}",
|
f"equipe-{slugify(self.trigram)}",
|
||||||
f"Equipe {self.name} ({self.trigram})",
|
f"Equipe {self.name} ({self.trigram})",
|
||||||
"hotline", # TODO Use a custom sympa template
|
"hotline", # TODO Use a custom sympa template
|
||||||
f"Liste de diffusion pour contacter l'equipe {self.name} du TFJM2",
|
f"Liste de diffusion pour contacter l'equipe {self.name} du TFJM2",
|
||||||
@ -231,7 +232,7 @@ class Team(models.Model):
|
|||||||
"""
|
"""
|
||||||
if self.participation.valid: # pragma: no cover
|
if self.participation.valid: # pragma: no cover
|
||||||
get_sympa_client().unsubscribe(
|
get_sympa_client().unsubscribe(
|
||||||
self.email, f"equipes-{self.participation.tournament.name.lower().replace(' ', '-')}", False)
|
self.email, f"equipes-{slugify(self.participation.tournament.name)}", False)
|
||||||
else:
|
else:
|
||||||
get_sympa_client().unsubscribe(self.email, "equipes-non-valides", False)
|
get_sympa_client().unsubscribe(self.email, "equipes-non-valides", False)
|
||||||
get_sympa_client().delete_list(f"equipe-{self.trigram}")
|
get_sympa_client().delete_list(f"equipe-{self.trigram}")
|
||||||
@ -391,28 +392,28 @@ class Tournament(models.Model):
|
|||||||
"""
|
"""
|
||||||
:return: The mailing list to contact the team members.
|
:return: The mailing list to contact the team members.
|
||||||
"""
|
"""
|
||||||
return f"equipes-{self.name.lower().replace(' ', '-')}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
return f"equipes-{slugify(self.name)}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def organizers_email(self):
|
def organizers_email(self):
|
||||||
"""
|
"""
|
||||||
:return: The mailing list to contact the team members.
|
:return: The mailing list to contact the team members.
|
||||||
"""
|
"""
|
||||||
return f"organisateurs-{self.name.lower().replace(' ', '-')}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
return f"organisateurs-{slugify(self.name)}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def jurys_email(self):
|
def jurys_email(self):
|
||||||
"""
|
"""
|
||||||
:return: The mailing list to contact the team members.
|
:return: The mailing list to contact the team members.
|
||||||
"""
|
"""
|
||||||
return f"jurys-{self.name.lower().replace(' ', '-')}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
return f"jurys-{slugify(self.name)}@{os.getenv('SYMPA_HOST', 'localhost')}"
|
||||||
|
|
||||||
def create_mailing_lists(self):
|
def create_mailing_lists(self):
|
||||||
"""
|
"""
|
||||||
Create a new Sympa mailing list to contact the team.
|
Create a new Sympa mailing list to contact the team.
|
||||||
"""
|
"""
|
||||||
get_sympa_client().create_list(
|
get_sympa_client().create_list(
|
||||||
f"equipes-{self.name.lower().replace(' ', '-')}",
|
f"equipes-{slugify(self.name)}",
|
||||||
f"Equipes du tournoi de {self.name}",
|
f"Equipes du tournoi de {self.name}",
|
||||||
"hotline", # TODO Use a custom sympa template
|
"hotline", # TODO Use a custom sympa template
|
||||||
f"Liste de diffusion pour contacter les equipes du tournoi {self.name} du TFJM²",
|
f"Liste de diffusion pour contacter les equipes du tournoi {self.name} du TFJM²",
|
||||||
@ -420,7 +421,7 @@ class Tournament(models.Model):
|
|||||||
raise_error=False,
|
raise_error=False,
|
||||||
)
|
)
|
||||||
get_sympa_client().create_list(
|
get_sympa_client().create_list(
|
||||||
f"organisateurs-{self.name.lower().replace(' ', '-')}",
|
f"organisateurs-{slugify(self.name)}",
|
||||||
f"Organisateurs du tournoi de {self.name}",
|
f"Organisateurs du tournoi de {self.name}",
|
||||||
"hotline", # TODO Use a custom sympa template
|
"hotline", # TODO Use a custom sympa template
|
||||||
f"Liste de diffusion pour contacter les equipes du tournoi {self.name} du TFJM²",
|
f"Liste de diffusion pour contacter les equipes du tournoi {self.name} du TFJM²",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
from participation.models import Note, Participation, Passage, Pool, Team, Tournament
|
from participation.models import Note, Participation, Passage, Pool, Team, Tournament
|
||||||
from registration.models import Payment
|
from registration.models import Payment
|
||||||
from tfjm.lists import get_sympa_client
|
from tfjm.lists import get_sympa_client
|
||||||
@ -34,10 +35,10 @@ def update_mailing_list(instance: Team, raw, **_):
|
|||||||
instance.create_mailing_list()
|
instance.create_mailing_list()
|
||||||
# Subscribe all team members in the mailing list
|
# Subscribe all team members in the mailing list
|
||||||
for student in instance.students.all():
|
for student in instance.students.all():
|
||||||
get_sympa_client().subscribe(student.user.email, f"equipe-{instance.trigram.lower()}", False,
|
get_sympa_client().subscribe(student.user.email, f"equipe-{slugify(instance.trigram)}", False,
|
||||||
f"{student.user.first_name} {student.user.last_name}")
|
f"{student.user.first_name} {student.user.last_name}")
|
||||||
for coach in instance.coaches.all():
|
for coach in instance.coaches.all():
|
||||||
get_sympa_client().subscribe(coach.user.email, f"equipe-{instance.trigram.lower()}", False,
|
get_sympa_client().subscribe(coach.user.email, f"equipe-{slugify(instance.trigram)}", False,
|
||||||
f"{coach.user.first_name} {coach.user.last_name}")
|
f"{coach.user.first_name} {coach.user.last_name}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ from django.db import transaction
|
|||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.http import FileResponse, Http404, HttpResponse
|
from django.http import FileResponse, Http404, HttpResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
@ -88,7 +89,7 @@ class CreateTeamView(LoginRequiredMixin, CreateView):
|
|||||||
registration.save()
|
registration.save()
|
||||||
|
|
||||||
# Subscribe the user mail address to the team mailing list
|
# Subscribe the user mail address to the team mailing list
|
||||||
get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False,
|
get_sympa_client().subscribe(user.email, f"equipe-{slugify(form.instance.trigram)}", False,
|
||||||
f"{user.first_name} {user.last_name}")
|
f"{user.first_name} {user.last_name}")
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
@ -130,7 +131,7 @@ class JoinTeamView(LoginRequiredMixin, FormView):
|
|||||||
registration.save()
|
registration.save()
|
||||||
|
|
||||||
# Subscribe to the team mailing list
|
# Subscribe to the team mailing list
|
||||||
get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False,
|
get_sympa_client().subscribe(user.email, f"equipe-{slugify(form.instance.trigram)}", False,
|
||||||
f"{user.first_name} {user.last_name}")
|
f"{user.first_name} {user.last_name}")
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
@ -229,10 +230,11 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
|
|||||||
self.object.participation.save()
|
self.object.participation.save()
|
||||||
|
|
||||||
mail_context = dict(team=self.object, domain=Site.objects.first().domain)
|
mail_context = dict(team=self.object, domain=Site.objects.first().domain)
|
||||||
mail_plain = render_to_string("participation/mails/request_validation.txt", mail_context)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
mail_html = render_to_string("participation/mails/request_validation.html", mail_context)
|
mail_plain = render_to_string("participation/mails/request_validation.txt", mail_context)
|
||||||
send_mail(f"[{settings.APP_NAME}] {_('Team validation')}", mail_plain, settings.DEFAULT_FROM_EMAIL,
|
mail_html = render_to_string("participation/mails/request_validation.html", mail_context)
|
||||||
[self.object.participation.tournament.organizers_email], html_message=mail_html)
|
send_mail(f"[{settings.APP_NAME}] {_('Team validation')}", mail_plain, settings.DEFAULT_FROM_EMAIL,
|
||||||
|
[self.object.participation.tournament.organizers_email], html_message=mail_html)
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
@ -264,19 +266,21 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
|
|||||||
message=form.cleaned_data["message"])
|
message=form.cleaned_data["message"])
|
||||||
mail_context_html = dict(domain=domain, registration=registration, team=self.object, payment=payment,
|
mail_context_html = dict(domain=domain, registration=registration, team=self.object, payment=payment,
|
||||||
message=form.cleaned_data["message"].replace('\n', '<br>'))
|
message=form.cleaned_data["message"].replace('\n', '<br>'))
|
||||||
mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context_plain)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
mail_html = render_to_string("participation/mails/team_validated.html", mail_context_html)
|
mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context_plain)
|
||||||
registration.user.email_user(f"[{settings.APP_NAME}] {_('Team validated')}", mail_plain,
|
mail_html = render_to_string("participation/mails/team_validated.html", mail_context_html)
|
||||||
html_message=mail_html)
|
registration.user.email_user(f"[{settings.APP_NAME}] {_('Team validated')}", mail_plain,
|
||||||
|
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()
|
||||||
mail_context_plain = dict(team=self.object, message=form.cleaned_data["message"])
|
mail_context_plain = dict(team=self.object, message=form.cleaned_data["message"])
|
||||||
mail_context_html = dict(team=self.object, message=form.cleaned_data["message"].replace('\n', '<br>'))
|
mail_context_html = dict(team=self.object, message=form.cleaned_data["message"].replace('\n', '<br>'))
|
||||||
mail_plain = render_to_string("participation/mails/team_not_validated.txt", mail_context_plain)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
mail_html = render_to_string("participation/mails/team_not_validated.html", mail_context_html)
|
mail_plain = render_to_string("participation/mails/team_not_validated.txt", mail_context_plain)
|
||||||
send_mail(f"[{settings.APP_NAME}] {_('Team not validated')}", mail_plain,
|
mail_html = render_to_string("participation/mails/team_not_validated.html", mail_context_html)
|
||||||
None, [self.object.email], html_message=mail_html)
|
send_mail(f"[{settings.APP_NAME}] {_('Team not validated')}", mail_plain,
|
||||||
|
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)
|
||||||
@ -313,6 +317,7 @@ class TeamUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
instance=self.object.participation)
|
instance=self.object.participation)
|
||||||
if not self.request.user.registration.is_volunteer:
|
if not self.request.user.registration.is_volunteer:
|
||||||
del context["participation_form"].fields['final']
|
del context["participation_form"].fields['final']
|
||||||
|
context["participation_form"].helper.layout.remove('final')
|
||||||
context["title"] = _("Update team {trigram}").format(trigram=self.object.trigram)
|
context["title"] = _("Update team {trigram}").format(trigram=self.object.trigram)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@ -321,6 +326,7 @@ class TeamUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
participation_form = ParticipationForm(data=self.request.POST or None, instance=self.object.participation)
|
participation_form = ParticipationForm(data=self.request.POST or None, instance=self.object.participation)
|
||||||
if not self.request.user.registration.is_volunteer:
|
if not self.request.user.registration.is_volunteer:
|
||||||
del participation_form.fields['final']
|
del participation_form.fields['final']
|
||||||
|
participation_form.helper.layout.remove('final')
|
||||||
if not participation_form.is_valid():
|
if not participation_form.is_valid():
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
@ -517,7 +523,7 @@ class TeamLeaveView(LoginRequiredMixin, TemplateView):
|
|||||||
team = request.user.registration.team
|
team = request.user.registration.team
|
||||||
request.user.registration.team = None
|
request.user.registration.team = None
|
||||||
request.user.registration.save()
|
request.user.registration.save()
|
||||||
get_sympa_client().unsubscribe(request.user.email, f"equipe-{team.trigram.lower()}", False)
|
get_sympa_client().unsubscribe(request.user.email, f"equipe-{slugify(team.trigram)}", False)
|
||||||
if team.students.count() + team.coaches.count() == 0:
|
if team.students.count() + team.coaches.count() == 0:
|
||||||
team.delete()
|
team.delete()
|
||||||
return redirect(reverse_lazy("index"))
|
return redirect(reverse_lazy("index"))
|
||||||
@ -1144,15 +1150,18 @@ class PoolJuryView(VolunteerMixin, FormView, DetailView):
|
|||||||
# Send welcome mail
|
# Send welcome mail
|
||||||
subject = f"[{settings.APP_NAME}] " + str(_("New jury account"))
|
subject = f"[{settings.APP_NAME}] " + str(_("New jury account"))
|
||||||
site = Site.objects.first()
|
site = Site.objects.first()
|
||||||
message = render_to_string('registration/mails/add_organizer.txt', dict(user=user,
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
inviter=self.request.user,
|
message = render_to_string('registration/mails/add_organizer.txt',
|
||||||
password=password,
|
dict(user=user,
|
||||||
domain=site.domain))
|
inviter=self.request.user,
|
||||||
html = render_to_string('registration/mails/add_organizer.html', dict(user=user,
|
password=password,
|
||||||
inviter=self.request.user,
|
domain=site.domain))
|
||||||
password=password,
|
html = render_to_string('registration/mails/add_organizer.html',
|
||||||
domain=site.domain))
|
dict(user=user,
|
||||||
user.email_user(subject, message, html_message=html)
|
inviter=self.request.user,
|
||||||
|
password=password,
|
||||||
|
domain=site.domain))
|
||||||
|
user.email_user(subject, message, html_message=html)
|
||||||
|
|
||||||
# Add the user in the jury
|
# Add the user in the jury
|
||||||
self.object.juries.add(reg)
|
self.object.juries.add(reg)
|
||||||
@ -1842,10 +1851,9 @@ class NotationSheetTemplateView(VolunteerMixin, DetailView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def render_to_response(self, context, **response_kwargs):
|
def render_to_response(self, context, **response_kwargs):
|
||||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
|
||||||
|
|
||||||
template_name = self.get_template_names()[0]
|
template_name = self.get_template_names()[0]
|
||||||
tex = render_to_string(template_name, context=context, request=self.request)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
|
tex = render_to_string(template_name, context=context, request=self.request)
|
||||||
temp_dir = mkdtemp()
|
temp_dir = mkdtemp()
|
||||||
with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
|
with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
|
||||||
f.write(tex)
|
f.write(tex)
|
||||||
@ -1952,6 +1960,13 @@ class NotationSheetsArchiveView(VolunteerMixin, DetailView):
|
|||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class GSheetNotificationsView(View):
|
class GSheetNotificationsView(View):
|
||||||
|
"""
|
||||||
|
Cette vue gère les notifications envoyées par Google Drive en cas de
|
||||||
|
modifications d'un tableur de notes sur Google Sheets.
|
||||||
|
|
||||||
|
Documentation de l'API : https://developers.google.com/calendar/api/guides/push?hl=fr
|
||||||
|
"""
|
||||||
|
|
||||||
async def post(self, request, *args, **kwargs):
|
async def post(self, request, *args, **kwargs):
|
||||||
if not await Tournament.objects.filter(pk=kwargs['pk']).aexists():
|
if not await Tournament.objects.filter(pk=kwargs['pk']).aexists():
|
||||||
return HttpResponse(status=404)
|
return HttpResponse(status=404)
|
||||||
|
@ -7,8 +7,6 @@ from django.contrib.auth.forms import UserCreationForm
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.forms import FileInput
|
from django.forms import FileInput
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.text import format_lazy
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import CoachRegistration, ParticipantRegistration, Payment, \
|
from .models import CoachRegistration, ParticipantRegistration, Payment, \
|
||||||
@ -38,19 +36,6 @@ class SignupForm(UserCreationForm):
|
|||||||
self.add_error("email", _("This email address is already used."))
|
self.add_error("email", _("This email address is already used."))
|
||||||
return email
|
return email
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
# Check that registrations are opened
|
|
||||||
now = timezone.now()
|
|
||||||
if now < settings.REGISTRATION_DATES['open']:
|
|
||||||
self.add_error(None, format_lazy(_("Registrations are not opened yet. "
|
|
||||||
"They will open on the {opening_date:%Y-%m-%d %H:%M}."),
|
|
||||||
opening_date=settings.REGISTRATION_DATES['open']))
|
|
||||||
elif now > settings.REGISTRATION_DATES['close']:
|
|
||||||
self.add_error(None, format_lazy(_("Registrations for this year are closed since "
|
|
||||||
"{closing_date:%Y-%m-%d %H:%M}."),
|
|
||||||
closing_date=settings.REGISTRATION_DATES['close']))
|
|
||||||
return super().clean()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields["first_name"].required = True
|
self.fields["first_name"].required = True
|
||||||
|
@ -308,27 +308,27 @@ class ParticipantRegistration(Registration):
|
|||||||
"""
|
"""
|
||||||
The team is selected for final.
|
The team is selected for final.
|
||||||
"""
|
"""
|
||||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
subject = f"[{settings.APP_NAME}] " + str(_("Team selected for the final tournament"))
|
subject = f"[{settings.APP_NAME}] " + str(_("Team selected for the final tournament"))
|
||||||
site = Site.objects.first()
|
site = Site.objects.first()
|
||||||
from participation.models import Tournament
|
from participation.models import Tournament
|
||||||
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:
|
||||||
verbose_name = _("participant registration")
|
verbose_name = _("participant registration")
|
||||||
@ -802,35 +802,35 @@ class Payment(models.Model):
|
|||||||
return checkout_intent
|
return checkout_intent
|
||||||
|
|
||||||
def send_remind_mail(self):
|
def send_remind_mail(self):
|
||||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
subject = f"[{settings.APP_NAME}] " + str(_("Reminder for your payment"))
|
subject = f"[{settings.APP_NAME}] " + str(_("Reminder for your payment"))
|
||||||
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):
|
||||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
subject = f"[{settings.APP_NAME}] " + str(_("Payment confirmation"))
|
subject = f"[{settings.APP_NAME}] " + str(_("Payment confirmation"))
|
||||||
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):
|
||||||
return reverse_lazy("registration:update_payment", args=(self.pk,))
|
return reverse_lazy("registration:update_payment", args=(self.pk,))
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
from tfjm.lists import get_sympa_client
|
from tfjm.lists import get_sympa_client
|
||||||
|
|
||||||
from .models import Registration, VolunteerRegistration
|
from .models import Registration, VolunteerRegistration
|
||||||
@ -29,8 +30,8 @@ def send_email_link(instance, **_):
|
|||||||
registration.send_email_validation_link()
|
registration.send_email_validation_link()
|
||||||
|
|
||||||
if registration.participates and registration.team:
|
if registration.participates and registration.team:
|
||||||
get_sympa_client().unsubscribe(old_instance.email, f"equipe-{registration.team.trigram.lower()}", False)
|
get_sympa_client().unsubscribe(old_instance.email, f"equipe-{slugify(registration.team.trigram)}", False)
|
||||||
get_sympa_client().subscribe(instance.email, f"equipe-{registration.team.trigram.lower()}", False,
|
get_sympa_client().subscribe(instance.email, f"equipe-{slugify(registration.team.trigram)}", False,
|
||||||
f"{instance.first_name} {instance.last_name}")
|
f"{instance.first_name} {instance.last_name}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% now "c" as now %}
|
{% now "c" as now %}
|
||||||
{% if now < TFJM.REGISTRATION_DATES.open.isoformat %}
|
{% if now < TFJM.REGISTRATION_DATES.open.isoformat and not user.registration.is_admin %}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
{% trans "Thank you for your great interest, but registrations are not opened yet!" %}
|
{% trans "Thank you for your great interest, but registrations are not opened yet!" %}
|
||||||
{% trans "They will open on:" %} {{ TFJM.REGISTRATION_DATES.open|date:'DATETIME_FORMAT' }}.
|
{% trans "They will open on:" %} {{ TFJM.REGISTRATION_DATES.open|date:'DATETIME_FORMAT' }}.
|
||||||
{% trans "Please come back at this time to register!" %}
|
{% trans "Please come back at this time to register!" %}
|
||||||
</div>
|
</div>
|
||||||
{% elif now > TFJM.REGISTRATION_DATES.close.isoformat %}
|
{% elif now > TFJM.REGISTRATION_DATES.close.isoformat and not user.registration.is_admin %}
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
{% trans "Registrations are closed for this year. We hope to see you next year!" %}
|
{% trans "Registrations are closed for this year. We hope to see you next year!" %}
|
||||||
{% trans "If needed, you can contact us by mail." %}
|
{% trans "If needed, you can contact us by mail." %}
|
||||||
|
@ -66,11 +66,19 @@ organisé \`a :
|
|||||||
Iel se rendra au lieu indiqu\'e ci-dessus le samedi matin et quittera les lieux l'après-midi du dimanche par
|
Iel se rendra au lieu indiqu\'e ci-dessus le samedi matin et quittera les lieux l'après-midi du dimanche par
|
||||||
ses propres moyens et sous la responsabilité du/de la représentant\cdt{}e légal\cdt{}e.
|
ses propres moyens et sous la responsabilité du/de la représentant\cdt{}e légal\cdt{}e.
|
||||||
|
|
||||||
|
{% if tournament.name == "Lyon" %}
|
||||||
|
Un hébergement à titre gratuit sera organisée la nuit du 10 au 11 mai 2025.
|
||||||
|
Le/la participant\cdt{}e sera logé\cdt{}e soit dans les résidences de l'ENS de Lyon situées
|
||||||
|
sur les campus de l'école soit dans l'hotel Ibis Gerland Mérieux situé 246 rue Marcel Mérieux – 69007 LYON.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
\vspace{8ex}
|
\vspace{8ex}
|
||||||
|
|
||||||
Fait à \vrule width 10cm height 0pt depth 0.4pt, le \phantom{232323}/\phantom{XXX}/{% now "Y" %},
|
Fait à \vrule width 10cm height 0pt depth 0.4pt, le \phantom{232323}/\phantom{XXX}/{% now "Y" %}
|
||||||
|
|
||||||
|
\vspace{4ex}
|
||||||
|
|
||||||
|
Signature :
|
||||||
|
|
||||||
\vfill
|
\vfill
|
||||||
\vfill
|
\vfill
|
||||||
|
@ -18,7 +18,7 @@ from django.http import FileResponse, Http404
|
|||||||
from django.shortcuts import redirect, resolve_url
|
from django.shortcuts import redirect, resolve_url
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils import translation
|
from django.utils import timezone, translation
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
from django.utils.text import format_lazy
|
from django.utils.text import format_lazy
|
||||||
@ -60,6 +60,22 @@ class SignupView(CreateView):
|
|||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
if self.request.method in ("POST", "PUT") \
|
||||||
|
and (not self.request.user.is_authenticated or not self.request.user.registration.is_admin):
|
||||||
|
# Check that registrations are opened
|
||||||
|
now = timezone.now()
|
||||||
|
if now < settings.REGISTRATION_DATES['open']:
|
||||||
|
form.add_error(None, format_lazy(_("Registrations are not opened yet. "
|
||||||
|
"They will open on the {opening_date:%Y-%m-%d %H:%M}."),
|
||||||
|
opening_date=settings.REGISTRATION_DATES['open']))
|
||||||
|
elif now > settings.REGISTRATION_DATES['close']:
|
||||||
|
form.add_error(None, format_lazy(_("Registrations for this year are closed since "
|
||||||
|
"{closing_date:%Y-%m-%d %H:%M}."),
|
||||||
|
closing_date=settings.REGISTRATION_DATES['close']))
|
||||||
|
return form
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
role = form.cleaned_data["role"]
|
role = form.cleaned_data["role"]
|
||||||
@ -121,16 +137,17 @@ class AddOrganizerView(VolunteerMixin, CreateView):
|
|||||||
form.instance.set_password(password)
|
form.instance.set_password(password)
|
||||||
form.instance.save()
|
form.instance.save()
|
||||||
|
|
||||||
subject = f"[{settings.APP_NAME}] " + str(_("New organizer account"))
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
site = Site.objects.first()
|
subject = f"[{settings.APP_NAME}] " + str(_("New organizer account"))
|
||||||
message = render_to_string('registration/mails/add_organizer.txt', dict(user=registration.user,
|
site = Site.objects.first()
|
||||||
|
message = render_to_string('registration/mails/add_organizer.txt', dict(user=registration.user,
|
||||||
|
inviter=self.request.user,
|
||||||
|
password=password,
|
||||||
|
domain=site.domain))
|
||||||
|
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))
|
||||||
html = render_to_string('registration/mails/add_organizer.html', dict(user=registration.user,
|
|
||||||
inviter=self.request.user,
|
|
||||||
password=password,
|
|
||||||
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:
|
||||||
@ -445,10 +462,9 @@ class AuthorizationTemplateView(TemplateView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def render_to_response(self, context, **response_kwargs):
|
def render_to_response(self, context, **response_kwargs):
|
||||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
|
||||||
|
|
||||||
template_name = self.get_template_names()[0]
|
template_name = self.get_template_names()[0]
|
||||||
tex = render_to_string(template_name, context=context, request=self.request)
|
with translation.override(settings.PREFERRED_LANGUAGE_CODE):
|
||||||
|
tex = render_to_string(template_name, context=context, request=self.request)
|
||||||
temp_dir = mkdtemp()
|
temp_dir = mkdtemp()
|
||||||
with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
|
with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
|
||||||
f.write(tex)
|
f.write(tex)
|
||||||
|
10
tfjm.cron
10
tfjm.cron
@ -11,15 +11,13 @@
|
|||||||
7 3 * * * cd /code && python manage.py fix_sympa_lists &> /dev/null
|
7 3 * * * cd /code && python manage.py fix_sympa_lists &> /dev/null
|
||||||
|
|
||||||
# Check payments from Hello Asso
|
# Check payments from Hello Asso
|
||||||
*/6 * * * * cd /code && python manage.py check_hello_asso &> /dev/null
|
*/30 * * 03-05 * cd /code && python manage.py check_hello_asso -v 0
|
||||||
# Send reminders for payments
|
|
||||||
30 6 * * 1 cd /code && python manage.py remind_payments &> /dev/null
|
|
||||||
|
|
||||||
# Check notation sheets every 15 minutes from 08:00 to 23:00 on fridays to mondays in april and may
|
# Send reminders for payments
|
||||||
# */15 8-23 * 4-5 5,6,7,1 cd /code && python manage.py parse_notation_sheets -v 0
|
30 6 * 03-05 1 cd /code && python manage.py remind_payments -v 0
|
||||||
|
|
||||||
# Update Google Drive notifications daily
|
# Update Google Drive notifications daily
|
||||||
0 0 * * * cd /code && python manage.py renew_gdrive_notifications &> /dev/null
|
0 0 * * * cd /code && python manage.py renew_gdrive_notifications -v 0
|
||||||
|
|
||||||
# Clean temporary files
|
# Clean temporary files
|
||||||
30 * * * * rm -rf /tmp/*
|
30 * * * * rm -rf /tmp/*
|
||||||
|
6
tfjm/static/bootstrap/css/bootstrap.min.css
vendored
6
tfjm/static/bootstrap/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -30,15 +30,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-warning">
|
|
||||||
<h3 class="alert-heading"><i class="fas fa-warning"></i> {% trans "New in 2025" %}</h3>
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
Registration for Ile-de-France tournaments is now unified.
|
|
||||||
If you live in or near the Ile-de-France region, your registration will be pooled with each of the region's tournaments,
|
|
||||||
and the organizers will take care of team allocation. However, date constraints can be indicated in the motivation letter.
|
|
||||||
{% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="jumbotron p-5 border rounded-5">
|
<div class="jumbotron p-5 border rounded-5">
|
||||||
<h5 class="display-4">{% trans "How does it work?" %}</h5>
|
<h5 class="display-4">{% trans "How does it work?" %}</h5>
|
||||||
<p>
|
<p>
|
||||||
|
Reference in New Issue
Block a user