mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-21 16:38:23 +02:00
Restructure add juree page
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
@ -6,9 +6,8 @@ from io import StringIO
|
||||
import re
|
||||
from typing import Iterable
|
||||
|
||||
from crispy_forms.bootstrap import InlineField
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Div, Fieldset, Submit
|
||||
from crispy_forms.layout import Div, Submit, Field
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
@ -206,25 +205,28 @@ class PoolTeamsForm(forms.ModelForm):
|
||||
class AddJuryForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['first_name'].required = True
|
||||
self.fields['last_name'].required = True
|
||||
self.fields['email'].required = True
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = 'form-inline'
|
||||
self.helper.layout = Div(
|
||||
Div(
|
||||
Div(
|
||||
InlineField('first_name', autofocus="autofocus"),
|
||||
Field('first_name', autofocus="autofocus"),
|
||||
css_class='col-md-3',
|
||||
),
|
||||
Div(
|
||||
InlineField('last_name'),
|
||||
Field('last_name'),
|
||||
css_class='col-md-3',
|
||||
),
|
||||
Div(
|
||||
InlineField('email'),
|
||||
Field('email'),
|
||||
css_class='col-md-5',
|
||||
),
|
||||
Div(
|
||||
Submit('submit', _("Add")),
|
||||
css_class='col-md-1',
|
||||
css_class='col-md-1 py-md-4',
|
||||
),
|
||||
css_class='row',
|
||||
)
|
||||
@ -236,7 +238,7 @@ class AddJuryForm(forms.ModelForm):
|
||||
"""
|
||||
email = self.data["email"]
|
||||
if User.objects.filter(email=email).exists():
|
||||
self.add_error("email", _("This email address is already used."))
|
||||
self.instance = User.objects.get(email=email)
|
||||
return email
|
||||
|
||||
class Meta:
|
||||
|
@ -1,47 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load crispy_forms_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="alert alert-info">
|
||||
<p>
|
||||
{% trans "You can here register juries for the pool." %}
|
||||
{% trans "Be careful: this form register new users. To add existing users into the jury, please use this form:" %}
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updatePoolModal">{% trans "Update pool" %}</button>
|
||||
</p>
|
||||
<p>
|
||||
{% trans "For now, the registered juries for the tournament are:" %}
|
||||
<ul>
|
||||
{% for jury in pool.juries.all %}
|
||||
<li>{{ jury.user.first_name }} {{ jury.user.last_name }} (<a class="alert-link" href="mailto:{{ jury.user.email }}">{{ jury.user.email }}</a>)</li>
|
||||
{% empty %}
|
||||
<li><i>{% trans "There is no jury yet." %}</i></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
{% crispy form %}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row text-center">
|
||||
<a href="{% url 'participation:pool_detail' pk=pool.pk %}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> {% trans "Back to pool detail" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% trans "Update pool" as modal_title %}
|
||||
{% trans "Update" as modal_button %}
|
||||
{% url "participation:pool_update" pk=pool.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="updatePool" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initModal("updatePool", "{% url "participation:pool_update" pk=pool.pk %}")
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -28,7 +28,7 @@
|
||||
<dt class="col-sm-3">{% trans "Juries:" %}</dt>
|
||||
<dd class="col-sm-9">
|
||||
{{ pool.juries.all|join:", " }}
|
||||
<a class="badge rounded-pill text-bg-info" href="{% url 'participation:pool_add_jurys' pk=pool.pk %}">
|
||||
<a class="badge rounded-pill text-bg-info" href="{% url 'participation:pool_jury' pk=pool.pk %}">
|
||||
<i class="fas fa-plus"></i> {% trans "Add jurys" %}
|
||||
</a>
|
||||
</dd>
|
||||
|
47
participation/templates/participation/pool_jury.html
Normal file
47
participation/templates/participation/pool_jury.html
Normal file
@ -0,0 +1,47 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load crispy_forms_tags crispy_forms_filters %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<hr>
|
||||
|
||||
{% for jury in pool.juries.all %}
|
||||
<div class="row my-3">
|
||||
<div class="col-md-3">
|
||||
<input type="text" class="form-control" value="{{ jury.user.first_name }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="text" class="form-control" value="{{ jury.user.last_name }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<input type="email" class="form-control" value="{{ jury.user.email }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<a href="{% url 'participation:pool_remove_jury' pk=pool.pk jury_id=jury.id %}" class="btn btn-danger">
|
||||
Retirer
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{{ form|as_crispy_errors }}
|
||||
{% crispy form %}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row text-center">
|
||||
<a href="{% url 'participation:pool_detail' pk=pool.pk %}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> {% trans "Back to pool detail" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initModal("updatePool", "{% url "participation:pool_update" pk=pool.pk %}")
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -6,11 +6,11 @@ from django.views.generic import TemplateView
|
||||
|
||||
from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, MyParticipationDetailView, \
|
||||
MyTeamDetailView, NoteUpdateView, ParticipationDetailView, PassageCreateView, PassageDetailView, \
|
||||
PassageUpdateView, PoolAddJurysView, PoolCreateView, PoolDetailView, PoolDownloadView, PoolNotesTemplateView, \
|
||||
PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, SolutionUploadView, \
|
||||
SynthesisUploadView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
|
||||
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
|
||||
TournamentListView, TournamentPaymentsView, TournamentUpdateView
|
||||
PassageUpdateView, PoolCreateView, PoolDetailView, PoolDownloadView, PoolJuryView, PoolNotesTemplateView, \
|
||||
PoolRemoveJuryView, PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, \
|
||||
SolutionUploadView, SynthesisUploadView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, \
|
||||
TeamUpdateView, TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, \
|
||||
TournamentExportCSVView, TournamentListView, TournamentPaymentsView, TournamentUpdateView
|
||||
|
||||
|
||||
app_name = "participation"
|
||||
@ -43,7 +43,8 @@ urlpatterns = [
|
||||
path("pools/<int:pk>/notation/scale/", ScaleNotationSheetTemplateView.as_view(), name="pool_scale_note_sheet"),
|
||||
path("pools/<int:pk>/notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"),
|
||||
path("pools/<int:pk>/update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"),
|
||||
path("pools/<int:pk>/add-jurys/", PoolAddJurysView.as_view(), name="pool_add_jurys"),
|
||||
path("pools/<int:pk>/jury/", PoolJuryView.as_view(), name="pool_jury"),
|
||||
path("pools/<int:pk>/jury/remove/<int:jury_id>/", PoolRemoveJuryView.as_view(), name="pool_remove_jury"),
|
||||
path("pools/<int:pk>/upload-notes/", PoolUploadNotesView.as_view(), name="pool_upload_notes"),
|
||||
path("pools/<int:pk>/upload-notes/template/", PoolNotesTemplateView.as_view(), name="pool_notes_template"),
|
||||
path("pools/passages/add/<int:pk>/", PassageCreateView.as_view(), name="passage_create"),
|
||||
|
@ -12,6 +12,7 @@ from zipfile import ZipFile
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.mail import send_mail
|
||||
@ -776,55 +777,75 @@ class PoolDownloadView(VolunteerMixin, DetailView):
|
||||
return response
|
||||
|
||||
|
||||
class PoolAddJurysView(VolunteerMixin, FormView, DetailView):
|
||||
class PoolJuryView(VolunteerMixin, FormView, DetailView):
|
||||
"""
|
||||
This view lets organizers set jurys for a pool, without multiplying clicks.
|
||||
"""
|
||||
model = Pool
|
||||
form_class = AddJuryForm
|
||||
template_name = 'participation/pool_add_jurys.html'
|
||||
template_name = 'participation/pool_jury.html'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
if request.user.registration.is_admin or request.user.registration.is_volunteer \
|
||||
and self.get_object().tournament in request.user.registration.organized_tournaments.all():
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
return self.handle_no_permission()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Jurys of {pool}").format(pool=self.object)
|
||||
context['title'] = _("Jury of pool {pool} for {tournament} with teams {teams}") \
|
||||
.format(pool=f"{self.object.get_letter_display()}{self.object.round}",
|
||||
tournament=self.object.tournament.name,
|
||||
teams=", ".join(participation.team.trigram for participation in self.object.participations.all()))
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
self.object = self.get_object()
|
||||
|
||||
# Save the user object first
|
||||
form.save()
|
||||
user = form.instance
|
||||
# Create associated registration object to the new user
|
||||
reg = VolunteerRegistration.objects.create(
|
||||
user=user,
|
||||
professional_activity="Juré⋅e du tournoi " + self.object.tournament.name,
|
||||
)
|
||||
if user.id:
|
||||
# The user already exists, so we don't recreate it
|
||||
user.refresh_from_db()
|
||||
reg = user.registration
|
||||
if reg.participates:
|
||||
form.add_error(None, _("This user already exists, but is a participant."))
|
||||
return self.form_invalid(form)
|
||||
else:
|
||||
# Save the user object first
|
||||
form.save()
|
||||
# Create associated registration object to the new user
|
||||
reg = VolunteerRegistration.objects.create(
|
||||
user=user,
|
||||
professional_activity="Juré⋅e du tournoi " + self.object.tournament.name,
|
||||
)
|
||||
|
||||
reg.send_email_validation_link()
|
||||
|
||||
# Generate new password for the user
|
||||
password = get_random_string(16)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
# Send welcome mail
|
||||
subject = "[TFJM²] " + str(_("New TFJM² jury account"))
|
||||
site = Site.objects.first()
|
||||
message = render_to_string('registration/mails/add_organizer.txt', dict(user=user,
|
||||
inviter=self.request.user,
|
||||
password=password,
|
||||
domain=site.domain))
|
||||
html = render_to_string('registration/mails/add_organizer.html', dict(user=user,
|
||||
inviter=self.request.user,
|
||||
password=password,
|
||||
domain=site.domain))
|
||||
user.email_user(subject, message, html_message=html)
|
||||
|
||||
# Add the user in the jury
|
||||
self.object.juries.add(reg)
|
||||
self.object.save()
|
||||
|
||||
reg.send_email_validation_link()
|
||||
|
||||
# Generate new password for the user
|
||||
password = get_random_string(16)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
# Send welcome mail
|
||||
subject = "[TFJM²] " + str(_("New TFJM² jury account"))
|
||||
site = Site.objects.first()
|
||||
message = render_to_string('registration/mails/add_organizer.txt', dict(user=user,
|
||||
inviter=self.request.user,
|
||||
password=password,
|
||||
domain=site.domain))
|
||||
html = render_to_string('registration/mails/add_organizer.html', dict(user=user,
|
||||
inviter=self.request.user,
|
||||
password=password,
|
||||
domain=site.domain))
|
||||
user.email_user(subject, message, html_message=html)
|
||||
|
||||
# Add notification
|
||||
messages.success(self.request, _("The jury {name} has been successfully added!")
|
||||
.format(name=f"{user.first_name} {user.last_name}"))
|
||||
@ -837,7 +858,30 @@ class PoolAddJurysView(VolunteerMixin, FormView, DetailView):
|
||||
return super().form_invalid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('participation:pool_add_jurys', args=(self.kwargs['pk'],))
|
||||
return reverse_lazy('participation:pool_jury', args=(self.kwargs['pk'],))
|
||||
|
||||
|
||||
class PoolRemoveJuryView(VolunteerMixin, DetailView):
|
||||
model = Pool
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
if request.user.registration.is_admin or request.user.registration.is_volunteer \
|
||||
and self.get_object().tournament in request.user.registration.organized_tournaments.all():
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
return self.handle_no_permission()
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
pool = self.get_object()
|
||||
if not pool.juries.filter(pk=kwargs['jury_id']).exists():
|
||||
raise Http404
|
||||
jury = pool.juries.get(pk=kwargs['jury_id'])
|
||||
pool.juries.remove(jury)
|
||||
pool.save()
|
||||
messages.success(request, _("The jury {name} has been successfully removed!")
|
||||
.format(name=f"{jury.user.first_name} {jury.user.last_name}"))
|
||||
return redirect(reverse_lazy('participation:pool_jury', args=(pool.pk,)))
|
||||
|
||||
|
||||
class PoolUploadNotesView(VolunteerMixin, FormView, DetailView):
|
||||
|
Reference in New Issue
Block a user