mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-21 03:18:27 +02:00
Move apps in main directory
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
4
registration/__init__.py
Normal file
4
registration/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'registration.apps.RegistrationConfig'
|
37
registration/admin.py
Normal file
37
registration/admin.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import ModelAdmin
|
||||
from polymorphic.admin import PolymorphicChildModelAdmin, PolymorphicParentModelAdmin
|
||||
|
||||
from .models import CoachRegistration, Payment, Registration, StudentRegistration, VolunteerRegistration
|
||||
|
||||
|
||||
@admin.register(Registration)
|
||||
class RegistrationAdmin(PolymorphicParentModelAdmin):
|
||||
child_models = (StudentRegistration, CoachRegistration, VolunteerRegistration,)
|
||||
list_display = ("user", "type", "email_confirmed",)
|
||||
polymorphic_list = True
|
||||
|
||||
|
||||
@admin.register(StudentRegistration)
|
||||
class StudentRegistrationAdmin(PolymorphicChildModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(CoachRegistration)
|
||||
class CoachRegistrationAdmin(PolymorphicChildModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(VolunteerRegistration)
|
||||
class VolunteerRegistrationAdmin(PolymorphicChildModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(Payment)
|
||||
class PaymentAdmin(ModelAdmin):
|
||||
list_display = ('registration', 'type', 'valid', )
|
||||
search_fields = ('registration__user__last_name', 'registration__user__first_name', 'registration__user__email',)
|
||||
list_filter = ('type', 'valid',)
|
2
registration/api/__init__.py
Normal file
2
registration/api/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
40
registration/api/serializers.py
Normal file
40
registration/api/serializers.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_polymorphic.serializers import PolymorphicSerializer
|
||||
|
||||
from ..models import CoachRegistration, ParticipantRegistration, \
|
||||
StudentRegistration, VolunteerRegistration
|
||||
|
||||
|
||||
class CoachSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CoachRegistration
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class ParticipantSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ParticipantRegistration
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class StudentSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = StudentRegistration
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class VolunteerSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = VolunteerRegistration
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class RegistrationSerializer(PolymorphicSerializer):
|
||||
model_serializer_mapping = {
|
||||
CoachRegistration: CoachSerializer,
|
||||
StudentRegistration: StudentSerializer,
|
||||
VolunteerRegistration: VolunteerSerializer,
|
||||
}
|
11
registration/api/urls.py
Normal file
11
registration/api/urls.py
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .views import RegistrationViewSet
|
||||
|
||||
|
||||
def register_registration_urls(router, path):
|
||||
"""
|
||||
Configure router for registration REST API.
|
||||
"""
|
||||
router.register(path + "/registration", RegistrationViewSet)
|
15
registration/api/views.py
Normal file
15
registration/api/views.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from .serializers import RegistrationSerializer
|
||||
from ..models import Registration
|
||||
|
||||
|
||||
class RegistrationViewSet(ModelViewSet):
|
||||
queryset = Registration.objects.all()
|
||||
serializer_class = RegistrationSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['user', 'participantregistration__team', ]
|
22
registration/apps.py
Normal file
22
registration/apps.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
|
||||
|
||||
class RegistrationConfig(AppConfig):
|
||||
"""
|
||||
Registration app contains the detail about users only.
|
||||
"""
|
||||
name = 'registration'
|
||||
|
||||
def ready(self):
|
||||
from registration.signals import create_admin_registration, create_payment, \
|
||||
set_username, send_email_link
|
||||
pre_save.connect(set_username, "auth.User")
|
||||
pre_save.connect(send_email_link, "auth.User")
|
||||
post_save.connect(create_admin_registration, "auth.User")
|
||||
post_save.connect(create_payment, "registration.Registration")
|
||||
post_save.connect(create_payment, "registration.StudentRegistration")
|
||||
post_save.connect(create_payment, "registration.CoachRegistration")
|
17
registration/auth.py
Normal file
17
registration/auth.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from cas_server.auth import DjangoAuthUser # pragma: no cover
|
||||
|
||||
|
||||
class CustomAuthUser(DjangoAuthUser): # pragma: no cover
|
||||
"""
|
||||
Override Django Auth User model to define a custom Matrix username.
|
||||
"""
|
||||
|
||||
def attributs(self):
|
||||
d = super().attributs()
|
||||
if self.user:
|
||||
d["matrix_username"] = self.user.registration.matrix_username
|
||||
d["display_name"] = str(self.user.registration)
|
||||
return d
|
26
registration/fixtures/initial.json
Normal file
26
registration/fixtures/initial.json
Normal file
@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"model": "cas_server.servicepattern",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"pos": 100,
|
||||
"name": "Plateforme du TFJM²",
|
||||
"pattern": "^https://tfjm.org(:8448)?/.*$",
|
||||
"user_field": "matrix_username",
|
||||
"restrict_users": false,
|
||||
"proxy": true,
|
||||
"proxy_callback": true,
|
||||
"single_log_out": true,
|
||||
"single_log_out_callback": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "cas_server.replaceattributname",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "display_name",
|
||||
"replace": "",
|
||||
"service_pattern": 1
|
||||
}
|
||||
}
|
||||
]
|
240
registration/forms.py
Normal file
240
registration/forms.py
Normal file
@ -0,0 +1,240 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms import FileInput
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import CoachRegistration, ParticipantRegistration, Payment, \
|
||||
StudentRegistration, VolunteerRegistration
|
||||
|
||||
|
||||
class SignupForm(UserCreationForm):
|
||||
"""
|
||||
Signup form to registers participants and coaches
|
||||
They can choose the role at the registration.
|
||||
"""
|
||||
|
||||
role = forms.ChoiceField(
|
||||
label=lambda: _("role").capitalize(),
|
||||
choices=lambda: [
|
||||
("participant", _("participant").capitalize()),
|
||||
("coach", _("coach").capitalize()),
|
||||
],
|
||||
)
|
||||
|
||||
def clean_email(self):
|
||||
"""
|
||||
Ensure that the email address is unique.
|
||||
"""
|
||||
email = self.data["email"]
|
||||
if User.objects.filter(email=email).exists():
|
||||
self.add_error("email", _("This email address is already used."))
|
||||
return email
|
||||
|
||||
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
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('first_name', 'last_name', 'email', 'password1', 'password2', 'role',)
|
||||
|
||||
|
||||
class AddOrganizerForm(forms.ModelForm):
|
||||
"""
|
||||
Signup form to registers volunteers
|
||||
"""
|
||||
|
||||
def clean_email(self):
|
||||
"""
|
||||
Ensure that the email address is unique.
|
||||
"""
|
||||
email = self.data["email"]
|
||||
if User.objects.filter(email=email).exists():
|
||||
self.add_error("email", _("This email address is already used."))
|
||||
return email
|
||||
|
||||
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
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('first_name', 'last_name', 'email',)
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
"""
|
||||
Replace the default user form to require the first name, last name and the email.
|
||||
The username is always equal to the email.
|
||||
"""
|
||||
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
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('first_name', 'last_name', 'email',)
|
||||
|
||||
|
||||
class StudentRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
A student can update its class, its school and if it allows Animath to contact him/her later.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["birth_date"].widget = forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d')
|
||||
|
||||
class Meta:
|
||||
model = StudentRegistration
|
||||
fields = ('team', 'student_class', 'birth_date', 'gender', 'address', 'zip_code', 'city', 'phone_number',
|
||||
'health_issues', 'school', 'responsible_name', 'responsible_phone', 'responsible_email',
|
||||
'give_contact_to_animath', 'email_confirmed',)
|
||||
|
||||
|
||||
class PhotoAuthorizationForm(forms.ModelForm):
|
||||
"""
|
||||
Form to send a photo authorization.
|
||||
"""
|
||||
def clean_photo_authorization(self):
|
||||
if "photo_authorization" in self.files:
|
||||
file = self.files["photo_authorization"]
|
||||
if file.size > 2e6:
|
||||
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
||||
if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
|
||||
raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
|
||||
return self.cleaned_data["photo_authorization"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["photo_authorization"].widget = FileInput()
|
||||
|
||||
class Meta:
|
||||
model = ParticipantRegistration
|
||||
fields = ('photo_authorization',)
|
||||
|
||||
|
||||
class HealthSheetForm(forms.ModelForm):
|
||||
"""
|
||||
Form to send a health sheet.
|
||||
"""
|
||||
def clean_health_sheet(self):
|
||||
if "health_sheet" in self.files:
|
||||
file = self.files["health_sheet"]
|
||||
if file.size > 2e6:
|
||||
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
||||
if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
|
||||
raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
|
||||
return self.cleaned_data["health_sheet"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["health_sheet"].widget = FileInput()
|
||||
|
||||
class Meta:
|
||||
model = StudentRegistration
|
||||
fields = ('health_sheet',)
|
||||
|
||||
|
||||
class VaccineSheetForm(forms.ModelForm):
|
||||
"""
|
||||
Form to send a vaccine sheet.
|
||||
"""
|
||||
def clean_vaccine_sheet(self):
|
||||
if "vaccine_sheet" in self.files:
|
||||
file = self.files["vaccine_sheet"]
|
||||
if file.size > 2e6:
|
||||
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
||||
if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
|
||||
raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
|
||||
return self.cleaned_data["vaccine_sheet"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["vaccine_sheet"].widget = FileInput()
|
||||
|
||||
class Meta:
|
||||
model = StudentRegistration
|
||||
fields = ('vaccine_sheet',)
|
||||
|
||||
|
||||
class ParentalAuthorizationForm(forms.ModelForm):
|
||||
"""
|
||||
Form to send a parental authorization.
|
||||
"""
|
||||
def clean_parental_authorization(self):
|
||||
if "parental_authorization" in self.files:
|
||||
file = self.files["parental_authorization"]
|
||||
if file.size > 2e6:
|
||||
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
||||
if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
|
||||
raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
|
||||
return self.cleaned_data["parental_authorization"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["parental_authorization"].widget = FileInput()
|
||||
|
||||
class Meta:
|
||||
model = StudentRegistration
|
||||
fields = ('parental_authorization',)
|
||||
|
||||
|
||||
class CoachRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
A coach can tell its professional activity.
|
||||
"""
|
||||
class Meta:
|
||||
model = CoachRegistration
|
||||
fields = ('team', 'gender', 'address', 'zip_code', 'city', 'phone_number', 'health_issues',
|
||||
'professional_activity', 'give_contact_to_animath', 'email_confirmed',)
|
||||
|
||||
|
||||
class VolunteerRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
A volunteer can also tell its professional activity.
|
||||
"""
|
||||
class Meta:
|
||||
model = VolunteerRegistration
|
||||
fields = ('professional_activity', 'admin', 'give_contact_to_animath', 'email_confirmed',)
|
||||
|
||||
|
||||
class PaymentForm(forms.ModelForm):
|
||||
"""
|
||||
Indicate payment information
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["valid"].widget.choices[0] = ('unknown', _("Pending"))
|
||||
|
||||
def clean_scholarship_file(self):
|
||||
if "scholarship_file" in self.files:
|
||||
file = self.files["scholarship_file"]
|
||||
if file.size > 2e6:
|
||||
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
||||
if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
|
||||
raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
|
||||
return self.cleaned_data["scholarship_file"]
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if "type" in cleaned_data and cleaned_data["type"] == "scholarship" \
|
||||
and "scholarship_file" not in cleaned_data and not self.instance.scholarship_file:
|
||||
self.add_error("scholarship_file", _("You must upload your scholarship attestation."))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = Payment
|
||||
fields = ('type', 'scholarship_file', 'additional_information', 'valid',)
|
126
registration/migrations/0001_initial.py
Normal file
126
registration/migrations/0001_initial.py
Normal file
@ -0,0 +1,126 @@
|
||||
# Generated by Django 3.2.13 on 2023-01-10 19:22
|
||||
|
||||
import datetime
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import phonenumber_field.modelfields
|
||||
import registration.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('participation', '0001_initial'),
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Registration',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('give_contact_to_animath', models.BooleanField(default=False, verbose_name='Grant Animath to contact me in the future about other actions')),
|
||||
('email_confirmed', models.BooleanField(default=False, verbose_name='email confirmed')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_registration.registration_set+', to='contenttypes.contenttype')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'registration',
|
||||
'verbose_name_plural': 'registrations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ParticipantRegistration',
|
||||
fields=[
|
||||
('registration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registration')),
|
||||
('birth_date', models.DateField(default=datetime.date.today, verbose_name='birth date')),
|
||||
('gender', models.CharField(choices=[('female', 'Female'), ('male', 'Male'), ('other', 'Other')], default='other', max_length=6, verbose_name='gender')),
|
||||
('address', models.CharField(max_length=255, verbose_name='address')),
|
||||
('zip_code', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1000), django.core.validators.MaxValueValidator(99999)], verbose_name='zip code')),
|
||||
('city', models.CharField(max_length=255, verbose_name='city')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None, verbose_name='phone number')),
|
||||
('health_issues', models.TextField(blank=True, help_text='You can indicate here your allergies or anything that is important to know for organizers', verbose_name='health issues')),
|
||||
('photo_authorization', models.FileField(blank=True, default='', upload_to=registration.models.get_random_photo_filename, verbose_name='photo authorization')),
|
||||
('team', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='participants', to='participation.team', verbose_name='team')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('registration.registration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VolunteerRegistration',
|
||||
fields=[
|
||||
('registration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registration')),
|
||||
('professional_activity', models.TextField(verbose_name='professional activity')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('registration.registration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AdminRegistration',
|
||||
fields=[
|
||||
('volunteerregistration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.volunteerregistration')),
|
||||
('role', models.TextField(verbose_name='role of the administrator')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'admin registration',
|
||||
'verbose_name_plural': 'admin registrations',
|
||||
},
|
||||
bases=('registration.volunteerregistration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CoachRegistration',
|
||||
fields=[
|
||||
('participantregistration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.participantregistration')),
|
||||
('professional_activity', models.TextField(verbose_name='professional activity')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'coach registration',
|
||||
'verbose_name_plural': 'coach registrations',
|
||||
},
|
||||
bases=('registration.participantregistration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StudentRegistration',
|
||||
fields=[
|
||||
('participantregistration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.participantregistration')),
|
||||
('student_class', models.IntegerField(choices=[(12, '12th grade'), (11, '11th grade'), (10, '10th grade or lower')], verbose_name='student class')),
|
||||
('school', models.CharField(max_length=255, verbose_name='school')),
|
||||
('responsible_name', models.CharField(default='', max_length=255, verbose_name='responsible name')),
|
||||
('responsible_phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None, verbose_name='responsible phone number')),
|
||||
('responsible_email', models.EmailField(default='', max_length=254, verbose_name='responsible email address')),
|
||||
('parental_authorization', models.FileField(blank=True, default='', upload_to=registration.models.get_random_parental_filename, verbose_name='parental authorization')),
|
||||
('health_sheet', models.FileField(blank=True, default='', upload_to=registration.models.get_random_health_filename, verbose_name='health sheet')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'student registration',
|
||||
'verbose_name_plural': 'student registrations',
|
||||
},
|
||||
bases=('registration.participantregistration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(blank=True, choices=[('', 'No payment'), ('helloasso', 'Hello Asso'), ('scholarship', 'Scholarship'), ('bank_transfer', 'Bank transfer'), ('other', 'Other (please indicate)'), ('free', 'The tournament is free')], default='', max_length=16, verbose_name='type')),
|
||||
('scholarship_file', models.FileField(blank=True, default='', help_text='only if you have a scholarship.', upload_to=registration.models.get_scholarship_filename, verbose_name='scholarship file')),
|
||||
('additional_information', models.TextField(blank=True, default='', help_text='To help us to find your payment.', verbose_name='additional information')),
|
||||
('valid', models.BooleanField(default=False, null=True, verbose_name='valid')),
|
||||
('registration', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='payment', to='registration.participantregistration', verbose_name='registration')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'payment',
|
||||
'verbose_name_plural': 'payments',
|
||||
},
|
||||
),
|
||||
]
|
29
registration/migrations/0002_auto_20230110_2031.py
Normal file
29
registration/migrations/0002_auto_20230110_2031.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Generated by Django 3.2.13 on 2023-01-10 19:31
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
import phonenumber_field.modelfields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registration', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='participantregistration',
|
||||
name='birth_date',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='studentregistration',
|
||||
name='birth_date',
|
||||
field=models.DateField(default=datetime.date.today, verbose_name='birth date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='studentregistration',
|
||||
name='responsible_phone',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(default='', max_length=128, region=None, verbose_name='responsible phone number'),
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 3.2.13 on 2023-01-16 22:13
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registration', '0002_auto_20230110_2031'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='participantregistration',
|
||||
name='zip_code',
|
||||
field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1000), django.core.validators.MaxValueValidator(99999)], verbose_name='zip code'),
|
||||
),
|
||||
]
|
51
registration/migrations/0004_volunteer_admin.py
Normal file
51
registration/migrations/0004_volunteer_admin.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Generated by Django 3.2.18 on 2023-02-19 22:13
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import migrations, models
|
||||
from django.db.models import F
|
||||
|
||||
|
||||
def merge_admins(apps, schema_editor):
|
||||
AdminRegistration = apps.get_model('registration', 'AdminRegistration')
|
||||
VolunteerRegistration = apps.get_model('registration', 'VolunteerRegistration')
|
||||
db_alias = schema_editor.connection.alias
|
||||
AdminRegistration.objects.using(db_alias).update(admin=True)
|
||||
for admin in AdminRegistration.objects.all():
|
||||
admin.professional_activity = admin.role
|
||||
admin.polymorphic_ctype_id = ContentType.objects.get_for_model(VolunteerRegistration).id
|
||||
admin.save()
|
||||
|
||||
|
||||
def separate_admins(apps, schema_editor):
|
||||
AdminRegistration = apps.get_model('registration', 'AdminRegistration')
|
||||
VolunteerRegistration = apps.get_model('registration', 'VolunteerRegistration')
|
||||
for admin in VolunteerRegistration.objects.filter(admin=True).all():
|
||||
admin.delete()
|
||||
AdminRegistration.objects.create(user=admin.user,
|
||||
professional_activity=admin.professional_activity,
|
||||
role=admin.professional_activity)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('participation', '0003_alter_team_trigram'),
|
||||
('registration', '0003_alter_participantregistration_zip_code'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='volunteerregistration',
|
||||
name='admin',
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="An administrator has all rights. Please don't give this right to all juries and volunteers.",
|
||||
verbose_name='administrator'),
|
||||
),
|
||||
migrations.RunPython(
|
||||
merge_admins,
|
||||
separate_admins,
|
||||
elidable=True,
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AdminRegistration',
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 3.2.18 on 2023-02-19 23:38
|
||||
|
||||
from django.db import migrations, models
|
||||
import registration.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registration', '0004_volunteer_admin'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='studentregistration',
|
||||
name='vaccine_sheet',
|
||||
field=models.FileField(blank=True, default='', upload_to=registration.models.get_random_vaccine_filename, verbose_name='vaccine sheet'),
|
||||
),
|
||||
]
|
2
registration/migrations/__init__.py
Normal file
2
registration/migrations/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
377
registration/models.py
Normal file
377
registration/models.py
Normal file
@ -0,0 +1,377 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import date
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.template import loader
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from tfjm.tokens import email_validation_token
|
||||
|
||||
|
||||
class Registration(PolymorphicModel):
|
||||
"""
|
||||
Registrations store extra content that are not asked in the User Model.
|
||||
This is specific to the role of the user, see StudentRegistration,
|
||||
CoachRegistration or VolunteerRegistration.
|
||||
"""
|
||||
user = models.OneToOneField(
|
||||
"auth.User",
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("user"),
|
||||
)
|
||||
|
||||
give_contact_to_animath = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("Grant Animath to contact me in the future about other actions"),
|
||||
)
|
||||
|
||||
email_confirmed = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("email confirmed"),
|
||||
)
|
||||
|
||||
def send_email_validation_link(self):
|
||||
"""
|
||||
The account got created or the email got changed.
|
||||
Send an email that contains a link to validate the address.
|
||||
"""
|
||||
subject = "[TFJM²] " + str(_("Activate your TFJM² account"))
|
||||
token = email_validation_token.make_token(self.user)
|
||||
uid = urlsafe_base64_encode(force_bytes(self.user.pk))
|
||||
site = Site.objects.first()
|
||||
message = loader.render_to_string('registration/mails/email_validation_email.txt',
|
||||
{
|
||||
'user': self.user,
|
||||
'domain': site.domain,
|
||||
'token': token,
|
||||
'uid': uid,
|
||||
})
|
||||
html = loader.render_to_string('registration/mails/email_validation_email.html',
|
||||
{
|
||||
'user': self.user,
|
||||
'domain': site.domain,
|
||||
'token': token,
|
||||
'uid': uid,
|
||||
})
|
||||
self.user.email_user(subject, message, html_message=html)
|
||||
|
||||
@property
|
||||
def type(self): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def form_class(self): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def participates(self):
|
||||
return isinstance(self, ParticipantRegistration)
|
||||
|
||||
@property
|
||||
def is_admin(self):
|
||||
return isinstance(self, VolunteerRegistration) and self.admin or self.user.is_superuser
|
||||
|
||||
@property
|
||||
def is_volunteer(self):
|
||||
return isinstance(self, VolunteerRegistration)
|
||||
|
||||
@property
|
||||
def matrix_username(self):
|
||||
return f"tfjm_{self.user.pk}"
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.user_id,))
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.first_name} {self.user.last_name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("registration")
|
||||
verbose_name_plural = _("registrations")
|
||||
|
||||
|
||||
def get_random_photo_filename(instance, filename):
|
||||
return "authorization/photo/" + get_random_string(64)
|
||||
|
||||
|
||||
def get_random_health_filename(instance, filename):
|
||||
return "authorization/health/" + get_random_string(64)
|
||||
|
||||
|
||||
def get_random_vaccine_filename(instance, filename):
|
||||
return "authorization/vaccine/" + get_random_string(64)
|
||||
|
||||
|
||||
def get_random_parental_filename(instance, filename):
|
||||
return "authorization/parental/" + get_random_string(64)
|
||||
|
||||
|
||||
class ParticipantRegistration(Registration):
|
||||
team = models.ForeignKey(
|
||||
"participation.Team",
|
||||
related_name="participants",
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None,
|
||||
verbose_name=_("team"),
|
||||
)
|
||||
|
||||
gender = models.CharField(
|
||||
max_length=6,
|
||||
verbose_name=_("gender"),
|
||||
choices=[
|
||||
("female", _("Female")),
|
||||
("male", _("Male")),
|
||||
("other", _("Other")),
|
||||
],
|
||||
default="other",
|
||||
)
|
||||
|
||||
address = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("address"),
|
||||
)
|
||||
|
||||
zip_code = models.PositiveIntegerField(
|
||||
verbose_name=_("zip code"),
|
||||
validators=[MinValueValidator(1000), MaxValueValidator(99999)],
|
||||
)
|
||||
|
||||
city = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("city"),
|
||||
)
|
||||
|
||||
phone_number = PhoneNumberField(
|
||||
verbose_name=_("phone number"),
|
||||
blank=True,
|
||||
)
|
||||
|
||||
health_issues = models.TextField(
|
||||
verbose_name=_("health issues"),
|
||||
blank=True,
|
||||
help_text=_("You can indicate here your allergies or anything that is important to know for organizers"),
|
||||
)
|
||||
|
||||
photo_authorization = models.FileField(
|
||||
verbose_name=_("photo authorization"),
|
||||
upload_to=get_random_photo_filename,
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
@property
|
||||
def under_18(self):
|
||||
if isinstance(self, CoachRegistration):
|
||||
return False # In normal case
|
||||
important_date = timezone.now().date()
|
||||
if self.team and self.team.participation.tournament:
|
||||
important_date = self.team.participation.tournament.date_start
|
||||
if self.team.participation.final:
|
||||
from participation.models import Tournament
|
||||
important_date = Tournament.final_tournament().date_start
|
||||
return (important_date - self.birth_date).days < 18 * 365.24
|
||||
|
||||
@property
|
||||
def type(self): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def form_class(self): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class StudentRegistration(ParticipantRegistration):
|
||||
"""
|
||||
Specific registration for students.
|
||||
They have a team, a student class and a school.
|
||||
"""
|
||||
birth_date = models.DateField(
|
||||
verbose_name=_("birth date"),
|
||||
default=date.today,
|
||||
)
|
||||
|
||||
student_class = models.IntegerField(
|
||||
choices=[
|
||||
(12, _("12th grade")),
|
||||
(11, _("11th grade")),
|
||||
(10, _("10th grade or lower")),
|
||||
],
|
||||
verbose_name=_("student class"),
|
||||
)
|
||||
|
||||
school = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("school"),
|
||||
)
|
||||
|
||||
responsible_name = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("responsible name"),
|
||||
default="",
|
||||
)
|
||||
|
||||
responsible_phone = PhoneNumberField(
|
||||
verbose_name=_("responsible phone number"),
|
||||
default="",
|
||||
)
|
||||
|
||||
responsible_email = models.EmailField(
|
||||
verbose_name=_("responsible email address"),
|
||||
default="",
|
||||
)
|
||||
|
||||
parental_authorization = models.FileField(
|
||||
verbose_name=_("parental authorization"),
|
||||
upload_to=get_random_parental_filename,
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
health_sheet = models.FileField(
|
||||
verbose_name=_("health sheet"),
|
||||
upload_to=get_random_health_filename,
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
vaccine_sheet = models.FileField(
|
||||
verbose_name=_("vaccine sheet"),
|
||||
upload_to=get_random_vaccine_filename,
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return _("student")
|
||||
|
||||
@property
|
||||
def form_class(self):
|
||||
from registration.forms import StudentRegistrationForm
|
||||
return StudentRegistrationForm
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("student registration")
|
||||
verbose_name_plural = _("student registrations")
|
||||
|
||||
|
||||
class CoachRegistration(ParticipantRegistration):
|
||||
"""
|
||||
Specific registration for coaches.
|
||||
They have a team and a professional activity.
|
||||
"""
|
||||
professional_activity = models.TextField(
|
||||
verbose_name=_("professional activity"),
|
||||
)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return _("coach")
|
||||
|
||||
@property
|
||||
def form_class(self):
|
||||
from registration.forms import CoachRegistrationForm
|
||||
return CoachRegistrationForm
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("coach registration")
|
||||
verbose_name_plural = _("coach registrations")
|
||||
|
||||
|
||||
class VolunteerRegistration(Registration):
|
||||
"""
|
||||
Specific registration for organizers and juries.
|
||||
"""
|
||||
professional_activity = models.TextField(
|
||||
verbose_name=_("professional activity"),
|
||||
)
|
||||
|
||||
admin = models.BooleanField(
|
||||
verbose_name=_("administrator"),
|
||||
help_text=_("An administrator has all rights. Please don't give this right to all juries and volunteers."),
|
||||
default=False,
|
||||
)
|
||||
|
||||
@property
|
||||
def interesting_tournaments(self) -> set:
|
||||
return set(self.organized_tournaments.all()).union(map(lambda pool: pool.tournament, self.jury_in.all()))
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return _('admin') if self.is_admin else _('volunteer')
|
||||
|
||||
@property
|
||||
def form_class(self):
|
||||
from registration.forms import VolunteerRegistrationForm
|
||||
return VolunteerRegistrationForm
|
||||
|
||||
|
||||
def get_scholarship_filename(instance, filename):
|
||||
return f"authorization/scholarship/scholarship_{instance.registration.pk}"
|
||||
|
||||
|
||||
class Payment(models.Model):
|
||||
registration = models.OneToOneField(
|
||||
ParticipantRegistration,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="payment",
|
||||
verbose_name=_("registration"),
|
||||
)
|
||||
|
||||
type = models.CharField(
|
||||
verbose_name=_("type"),
|
||||
max_length=16,
|
||||
choices=[
|
||||
('', _("No payment")),
|
||||
('helloasso', "Hello Asso"),
|
||||
('scholarship', _("Scholarship")),
|
||||
('bank_transfer', _("Bank transfer")),
|
||||
('other', _("Other (please indicate)")),
|
||||
('free', _("The tournament is free")),
|
||||
],
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
scholarship_file = models.FileField(
|
||||
verbose_name=_("scholarship file"),
|
||||
help_text=_("only if you have a scholarship."),
|
||||
upload_to=get_scholarship_filename,
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
additional_information = models.TextField(
|
||||
verbose_name=_("additional information"),
|
||||
help_text=_("To help us to find your payment."),
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
valid = models.BooleanField(
|
||||
verbose_name=_("valid"),
|
||||
null=True,
|
||||
default=False,
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.registration.user.id,))
|
||||
|
||||
def __str__(self):
|
||||
return _("Payment of {registration}").format(registration=self.registration)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("payment")
|
||||
verbose_name_plural = _("payments")
|
16
registration/search_indexes.py
Normal file
16
registration/search_indexes.py
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from haystack import indexes
|
||||
|
||||
from .models import Registration
|
||||
|
||||
|
||||
class RegistrationIndex(indexes.ModelSearchIndex, indexes.Indexable):
|
||||
"""
|
||||
Registrations are indexed by the user detail.
|
||||
"""
|
||||
text = indexes.NgramField(document=True, use_template=True)
|
||||
|
||||
class Meta:
|
||||
model = Registration
|
56
registration/signals.py
Normal file
56
registration/signals.py
Normal file
@ -0,0 +1,56 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from tfjm.lists import get_sympa_client
|
||||
|
||||
from .models import Payment, Registration, VolunteerRegistration
|
||||
|
||||
|
||||
def set_username(instance, **_):
|
||||
"""
|
||||
Ensure that the user username is always equal to the user email address.
|
||||
"""
|
||||
instance.username = instance.email
|
||||
|
||||
|
||||
def send_email_link(instance, **_):
|
||||
"""
|
||||
If the email address got changed, send a new validation link
|
||||
and update the registration status in the team mailing list.
|
||||
"""
|
||||
if instance.pk:
|
||||
old_instance = User.objects.get(pk=instance.pk)
|
||||
if old_instance.email != instance.email:
|
||||
registration = Registration.objects.get(user=instance)
|
||||
registration.email_confirmed = False
|
||||
registration.save()
|
||||
registration.user = instance
|
||||
registration.send_email_validation_link()
|
||||
|
||||
if registration.participates and registration.team:
|
||||
get_sympa_client().unsubscribe(old_instance.email, f"equipe-{registration.team.trigram.lower()}", False)
|
||||
get_sympa_client().subscribe(instance.email, f"equipe-{registration.team.trigram.lower()}", False,
|
||||
f"{instance.first_name} {instance.last_name}")
|
||||
|
||||
|
||||
def create_admin_registration(instance, **_):
|
||||
"""
|
||||
When a super user got created through console,
|
||||
ensure that an admin registration is created.
|
||||
"""
|
||||
if instance.is_superuser:
|
||||
VolunteerRegistration.objects.get_or_create(user=instance, admin=True)
|
||||
|
||||
|
||||
def create_payment(instance: Registration, **_):
|
||||
"""
|
||||
When a user is saved, create the associated payment.
|
||||
For a free tournament, the payment is valid.
|
||||
"""
|
||||
if instance.participates:
|
||||
payment = Payment.objects.get_or_create(registration=instance)[0]
|
||||
if instance.team and instance.team.participation.valid and instance.team.participation.tournament.price == 0:
|
||||
payment.valid = True
|
||||
payment.type = "free"
|
||||
payment.save()
|
31
registration/tables.py
Normal file
31
registration/tables.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import django_tables2 as tables
|
||||
|
||||
from .models import Registration
|
||||
|
||||
|
||||
class RegistrationTable(tables.Table):
|
||||
"""
|
||||
Table of all registrations.
|
||||
"""
|
||||
last_name = tables.LinkColumn(
|
||||
'registration:user_detail',
|
||||
args=[tables.A("user_id")],
|
||||
verbose_name=lambda: _("last name").capitalize(),
|
||||
accessor="user__last_name",
|
||||
)
|
||||
|
||||
def order_type(self, queryset, desc):
|
||||
types = ["volunteerregistration", "-volunteerregistration__admin", "participantregistration"]
|
||||
return queryset.order_by(*(("-" if desc else "") + t for t in types)), True
|
||||
|
||||
class Meta:
|
||||
attrs = {
|
||||
'class': 'table table-condensed table-striped',
|
||||
}
|
||||
model = Registration
|
||||
fields = ('last_name', 'user__first_name', 'user__email', 'type',)
|
||||
order_by = ('type', 'last_name', 'first_name',)
|
31
registration/templates/registration/add_organizer.html
Normal file
31
registration/templates/registration/add_organizer.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!-- templates/signup.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add organizer" %}{% endblock %}
|
||||
|
||||
{% block extracss %}
|
||||
{{ volunteer_registration_form.media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans "Add organizer" %}</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div id="registration_form">
|
||||
{{ volunteer_registration_form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-success" type="submit">
|
||||
{% trans "Add organizer" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div id="volunteer_registration_form" class="d-none">
|
||||
{{ volunteer_registration_form|crispy }}
|
||||
</div>
|
||||
<div id="admin_registration_form" class="d-none">
|
||||
{{ admin_registration_form|crispy }}
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,31 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card bg-body">
|
||||
<h3 class="card-header text-center">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
{% if validlink %}
|
||||
<p>
|
||||
{% trans "Your email have successfully been validated." %}
|
||||
</p>
|
||||
<p>
|
||||
{% blocktrans %}You can now <a href="{{ login_url }}">log in</a>.{% endblocktrans %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{% if user.is_authenticated and user.registration.email_confirmed %}
|
||||
{% trans "The link was invalid. The token may have expired, or your account is already activated. However, your account seems to be already valid." %}
|
||||
{% else %}
|
||||
{% trans "The link was invalid. The token may have expired, or your account is already activated. Please send us an email to activate your account." %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card bg-body">
|
||||
<h3 class="card-header text-center">
|
||||
{% trans "Account activation" %}
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
{% trans "An email has been sent. Please click on the link to activate your account." %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
38
registration/templates/registration/mails/add_organizer.html
Normal file
38
registration/templates/registration/mails/add_organizer.html
Normal file
@ -0,0 +1,38 @@
|
||||
{% load i18n %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Bonjour {{ user.registration }},
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Vous avez été invités par {{ inviter.registration }} à rejoindre la plateforme du TFJM², accessible à l'adresse
|
||||
<a href="https://{{ domain }}/">https://{{ domain }}/</a>. Vous disposez d'un compte d'organisateur.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Un mot de passe aléatoire a été défini : <strong>{{ password }}</strong>.
|
||||
Par sécurité, merci de le changer dès votre connexion.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
En cas de problème, merci de nous contacter soit par mail à l'adresse
|
||||
<a href="mailto:contact@tfjm.org">contact@tfjm.org</a>, soit sur la plateforme de chat accessible sur
|
||||
<a href="https://element.tfjm.org/">https://element.tfjm.org/</a> en vous connectant avec les mêmes identifiants.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Bien cordialement,
|
||||
</p>
|
||||
|
||||
--
|
||||
<p>
|
||||
{% trans "The TFJM² team." %}<br>
|
||||
</p>
|
17
registration/templates/registration/mails/add_organizer.txt
Normal file
17
registration/templates/registration/mails/add_organizer.txt
Normal file
@ -0,0 +1,17 @@
|
||||
{% load i18n %}
|
||||
|
||||
Bonjour {{ user.registration }},
|
||||
|
||||
Vous avez été invités par {{ inviter.registration }} à rejoindre la plateforme du TFJM², accessible à l'adresse
|
||||
https://{{ domain }}/. Vous disposez d'un compte d'organisateur.
|
||||
|
||||
Un mot de passe aléatoire a été défini : {{ password }}.
|
||||
Par sécurité, merci de le changer dès votre connexion.
|
||||
|
||||
En cas de problème, merci de nous contacter soit par mail à l'adresse contact@tfjm.org, soit sur la plateforme
|
||||
de chat accessible sur https://element.tfjm.org/ en vous connectant avec les mêmes identifiants.
|
||||
|
||||
Bien cordialement,
|
||||
|
||||
--
|
||||
{% trans "The TFJM² team." %}
|
@ -0,0 +1,36 @@
|
||||
{% load i18n %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
{% trans "Hi" %} {{ user.registration }},
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "You recently registered on the TFJM² platform. Please click on the link below to confirm your registration." %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %}">
|
||||
https://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "Thanks" %},
|
||||
</p>
|
||||
|
||||
--
|
||||
<p>
|
||||
{% trans "The TFJM² team." %}<br>
|
||||
</p>
|
@ -0,0 +1,13 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% trans "Hi" %} {{ user.registration }},
|
||||
|
||||
{% trans "You recently registered on the TFJM² platform. Please click on the link below to confirm your registration." %}
|
||||
|
||||
https://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %}
|
||||
|
||||
{% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %}
|
||||
|
||||
{% trans "Thanks" %},
|
||||
|
||||
{% trans "The TFJM² team." %}
|
@ -0,0 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans 'Your password was changed.' %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">{% csrf_token %}
|
||||
<p>{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}</p>
|
||||
{{ form | crispy }}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans 'Change my password' %}">
|
||||
</form>
|
||||
{% endblock %}
|
@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
|
||||
<p>
|
||||
<a href="{{ login_url }}" class="btn btn-success">{% trans 'Log in' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@ -0,0 +1,17 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
{% if validlink %}
|
||||
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form | crispy }}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans 'Change my password' %}">
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
10
registration/templates/registration/password_reset_done.html
Normal file
10
registration/templates/registration/password_reset_done.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}</p>
|
||||
<p>{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}</p>
|
||||
{% endblock %}
|
13
registration/templates/registration/password_reset_form.html
Normal file
13
registration/templates/registration/password_reset_form.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}</p>
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form | crispy }}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans 'Reset my password' %}">
|
||||
</form>
|
||||
{% endblock %}
|
52
registration/templates/registration/payment_form.html
Normal file
52
registration/templates/registration/payment_form.html
Normal file
@ -0,0 +1,52 @@
|
||||
{% extends request.content_only|yesno:"empty.html,base.html" %}
|
||||
|
||||
{% load crispy_forms_filters i18n %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div id="form-content">
|
||||
<div class="alert alert-info text-justify">
|
||||
<p>
|
||||
{% blocktrans trimmed with price=payment.registration.team.participation.tournament.price %}
|
||||
The price of the tournament is {{ price }} €. The participation fee is offered for coaches
|
||||
and for students who have a scholarship. If so, please send us your scholarship attestation.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can pay with a credit card through
|
||||
<a class="alert-link" href="https://www.helloasso.com/associations/animath/evenements/tfjm-2023-tournois-regionaux">our Hello Asso page</a>.
|
||||
To make the validation of the payment easier, <span class="text-danger">please use the same e-mail
|
||||
address that you use on this platform.</span> The payment verification will be checked automatically
|
||||
under 10 minutes, you don't necessary need to fill this form.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can also send a bank transfer to the bank account of Animath. You must put in the reference of the
|
||||
transfer the mention "TFJMpu" followed by the last name and the first name of the student.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
IBAN : FR76 1027 8065 0000 0206 4290 127<br>
|
||||
BIC : CMCIFR2A
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
If any payment mean is available to you, please contact us at <a class="alert-link" href="mailto:contact@tfjm.org">contact@tfjm.org</a>
|
||||
to find a solution to your difficulties.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
46
registration/templates/registration/signup.html
Normal file
46
registration/templates/registration/signup.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!-- templates/signup.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Sign up" %}{% endblock %}
|
||||
|
||||
{% block extracss %}
|
||||
{{ student_registration_form.media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans "Sign up" %}</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div id="registration_form"></div>
|
||||
<button class="btn btn-success" type="submit">
|
||||
{% trans "Sign up" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div id="student_registration_form" class="d-none">
|
||||
{{ student_registration_form|crispy }}
|
||||
</div>
|
||||
<div id="coach_registration_form" class="d-none">
|
||||
{{ coach_registration_form|crispy }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
let role_elem = document.getElementById("id_role")
|
||||
function updateView () {
|
||||
let selected_role = role_elem.options[role_elem.selectedIndex].value
|
||||
if (selected_role === "participant")
|
||||
document.getElementById("registration_form").innerHTML = document.getElementById("student_registration_form").innerHTML
|
||||
else
|
||||
document.getElementById("registration_form").innerHTML = document.getElementById("coach_registration_form").innerHTML
|
||||
}
|
||||
role_elem.addEventListener('change', updateView)
|
||||
updateView()
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
@ -0,0 +1,127 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[french]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{/code/static/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
\vfill
|
||||
|
||||
\begin{center}
|
||||
|
||||
|
||||
\LARGE
|
||||
Autorisation d'enregistrement et de diffusion de l'image ({{ tournament.name }})
|
||||
\end{center}
|
||||
\normalsize
|
||||
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\bigskip
|
||||
|
||||
|
||||
|
||||
Je soussign\'e {{ registration|safe|default:"\dotfill" }}\\
|
||||
demeurant au {{ registration.address|safe|default:"\dotfill" }}
|
||||
|
||||
\medskip
|
||||
Cochez la/les cases correspondantes.\\
|
||||
\medskip
|
||||
|
||||
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ de {{ tournament.name }}
|
||||
du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }}, \`a me photographier ou \`a me
|
||||
filmer et \`a diffuser les photos et/ou les vid\'eos r\'ealis\'ees \`a cette occasion sur son site et sur les sites
|
||||
partenaires. D\'eclare c\'eder \`a titre gracieux \`a Animath le droit d’utiliser mon image sur tous ses supports
|
||||
d'information : brochures, sites web, r\'eseaux sociaux. Animath devient, par la pr\'esente, cessionnaire des droits
|
||||
pendant toute la dur\'ee pour laquelle ont \'et\'e acquis les droits d'auteur de ces photographies.\\
|
||||
|
||||
\medskip
|
||||
Animath s'engage, conform\'ement aux dispositions l\'egales en vigueur relatives au droit \`a l'image, \`a ce que la
|
||||
publication et la diffusion de l'image ainsi que des commentaires l'accompagnant ne portent pas atteinte \`a la vie
|
||||
priv\'ee, \`a la dignit\'e et \`a la r\'eputation de la personne photographiée.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise la diffusion dans les medias (Presse, T\'el\'evision, Internet) de photographies
|
||||
prises \`a l'occasion d’une \'eventuelle m\'ediatisation de cet événement.\\
|
||||
|
||||
\medskip
|
||||
|
||||
Conform\'ement \`a la loi informatique et libert\'es du 6 janvier 1978, vous disposez d'un droit de libre acc\`es,
|
||||
de rectification, de modification et de suppression des donn\'ees qui vous concernent.
|
||||
Cette autorisation est donc r\'evocable \`a tout moment sur volont\'e express\'ement manifest\'ee par lettre
|
||||
recommand\'ee avec accus\'e de r\'eception adress\'ee \`a
|
||||
Animath, IHP, 11 rue Pierre et Marie Curie, 75231 Paris cedex 05.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise Animath à conserver mes données personnelles, dans le cadre défini par
|
||||
la loi n 78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés et les textes la modifiant,
|
||||
pendant une durée de quatre ans à compter de ma dernière participation à un événement organisé par Animath.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} J'accepte d'être tenu informé d'autres activités organisées par l'association et ses
|
||||
partenaires.
|
||||
|
||||
\bigskip
|
||||
|
||||
Signature pr\'ec\'ed\'ee de la mention \og lu et approuv\'e \fg{}
|
||||
|
||||
\medskip
|
||||
|
||||
|
||||
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
|
||||
\underline{Le participant :}\\
|
||||
|
||||
Fait \`a :\\
|
||||
le
|
||||
\end{minipage}
|
||||
|
||||
|
||||
\vfill
|
||||
\vfill
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018
|
||||
\end{minipage}
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize
|
||||
\begin{flushright}
|
||||
Association agréée par\\le Ministère de l'éducation nationale.
|
||||
\end{flushright}
|
||||
\end{minipage}
|
||||
\end{document}
|
@ -0,0 +1,136 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[french]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{/code/static/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
\vfill
|
||||
|
||||
\begin{center}
|
||||
|
||||
|
||||
\LARGE
|
||||
Autorisation d'enregistrement et de diffusion de l'image
|
||||
({{ tournament.name }})
|
||||
\end{center}
|
||||
\normalsize
|
||||
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\bigskip
|
||||
|
||||
|
||||
|
||||
Je soussign\'e \dotfill (p\`ere, m\`ere, responsable l\'egal) \\
|
||||
agissant en qualit\'e de repr\'esentant de {{ registration|safe|default:"\dotfill" }}\\
|
||||
demeurant au {{ registration.address|safe|default:"\dotfill" }}
|
||||
|
||||
\medskip
|
||||
Cochez la/les cases correspondantes.\\
|
||||
\medskip
|
||||
|
||||
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ de {{ tournament.name }}
|
||||
du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }}, \`a photographier ou \`a filmer
|
||||
l'enfant et \`a diffuser les photos et/ou les vid\'eos r\'ealis\'ees \`a cette occasion sur son site et sur les sites
|
||||
partenaires. D\'eclare c\'eder \`a titre gracieux \`a Animath le droit d’utiliser l'image de l'enfant sur tous ses
|
||||
supports d'information : brochures, sites web, r\'eseaux sociaux. Animath devient, par la pr\'esente, cessionnaire des
|
||||
droits pendant toute la dur\'ee pour laquelle ont \'et\'e acquis les droits d'auteur de ces photographies.\\
|
||||
|
||||
\medskip
|
||||
Animath s'engage, conform\'ement aux dispositions l\'egales en vigueur relatives au droit \`a l'image, \`a ce que la
|
||||
publication et la diffusion de l'image de l'enfant ainsi que des commentaires l'accompagnant ne portent pas atteinte
|
||||
\`a la vie priv\'ee, \`a la dignit\'e et \`a la r\'eputation de l’enfant.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise la diffusion dans les medias (Presse, T\'el\'evision, Internet) de
|
||||
photographies de mon enfant prises \`a l'occasion d’une \'eventuelle m\'ediatisation de cet événement.\\
|
||||
|
||||
\medskip
|
||||
|
||||
Conform\'ement \`a la loi informatique et libert\'es du 6 janvier 1978, vous disposez d'un droit de libre acc\`es, de
|
||||
rectification, de modification et de suppression des donn\'ees qui vous concernent.
|
||||
Cette autorisation est donc r\'evocable \`a tout moment sur volont\'e express\'ement manifest\'ee par lettre
|
||||
recommand\'ee avec accus\'e de r\'eception adress\'ee \`a
|
||||
Animath, IHP, 11 rue Pierre et Marie Curie, 75231 Paris cedex 05.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise Animath à conserver mes données personnelles, dans le cadre défini par
|
||||
la loi n 78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés et les textes la modifiant,
|
||||
pendant une durée de quatre ans à compter de ma dernière participation à un événement organisé par Animath.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} J'accepte d'être tenu informé d'autres activités organisées par l'association et ses
|
||||
partenaires.
|
||||
|
||||
\bigskip
|
||||
|
||||
Signatures pr\'ec\'ed\'ees de la mention \og lu et approuv\'e \fg{}
|
||||
|
||||
\medskip
|
||||
|
||||
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
|
||||
\underline{Le responsable l\'egal :}\\
|
||||
|
||||
Fait \`a :\\
|
||||
le :
|
||||
|
||||
\end{minipage}
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
|
||||
\underline{L'\'el\`eve :}\\
|
||||
|
||||
Fait \`a :\\
|
||||
le
|
||||
\end{minipage}
|
||||
|
||||
|
||||
\vfill
|
||||
\vfill
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018
|
||||
\end{minipage}
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize
|
||||
\begin{flushright}
|
||||
Association agréée par\\le Ministère de l'éducation nationale.
|
||||
\end{flushright}
|
||||
\end{minipage}
|
||||
\end{document}
|
@ -0,0 +1,68 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[french]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{/code/static/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
\vfill
|
||||
|
||||
\begin{center}
|
||||
\Large \bf Autorisation parentale pour les mineurs ({{ tournament.name }})
|
||||
\end{center}
|
||||
|
||||
Je soussigné(e) \hrulefill,\\
|
||||
responsable légal, demeurant \writingsep\hrulefill\\
|
||||
\writingsep\hrulefill,\\
|
||||
\writingsep autorise {{ registration|default:"\hrulefill" }},\\
|
||||
né(e) le {{ registration.birth_date }},
|
||||
à participer au Tournoi Français des Jeunes Mathématiciennes et Mathématiciens ($\mathbb{TFJM}^2$) organisé \`a :
|
||||
{{ tournament.place }}, du {{ tournament.date_start }} au {{ tournament.date_end }}.
|
||||
|
||||
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 représentant légal.
|
||||
|
||||
|
||||
|
||||
\vspace{8ex}
|
||||
|
||||
Fait à \vrule width 10cm height 0pt depth 0.4pt, le \phantom{232323}/\phantom{XXX}/{% now "Y" %},
|
||||
|
||||
\vfill
|
||||
\vfill
|
||||
|
||||
\end{document}
|
101
registration/templates/registration/tex/Instructions.tex
Normal file
101
registration/templates/registration/tex/Instructions.tex
Normal file
@ -0,0 +1,101 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[frenchb]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{hyperref}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{/code/static/logo_animath.png}\hfill{\fontsize{50pt}{50pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
|
||||
|
||||
\begin{center}
|
||||
\Large \bf Instructions ({{ tournament.name }})
|
||||
\end{center}
|
||||
|
||||
\section{Documents}
|
||||
\subsection{Autorisation parentale}
|
||||
Elle est nécessaire si l'élève est mineur au moment du tournoi (y compris si son anniversaire est pendant le tournoi).
|
||||
|
||||
\subsection{Autorisation de prise de vue}
|
||||
Si l'élève est mineur \textbf{au moment de la signature}, il convient de remplir l'autorisation pour les mineurs.
|
||||
En revanche, s'il est majeur \textbf{au moment de la signature}, il convient de remplir la fiche pour majeur.
|
||||
|
||||
\subsection{Fiche sanitaire}
|
||||
Elle est nécessaire si l'élève est mineur au moment du tournoi (y compris si son anniversaire est pendant le tournoi).
|
||||
|
||||
|
||||
\section{Paiement}
|
||||
|
||||
{% if tournament.price %}
|
||||
\subsection{Montant}
|
||||
Les frais d'inscription sont fixés à {{ tournament.price }} euros. Vous devez vous en acquitter
|
||||
\textbf{avant le {{ tournament.inscription_limit.date }}}. Si l'élève est boursier, il en est dispensé, vous devez alors
|
||||
fournir une copie de sa notification de bourse directement sur la plateforme
|
||||
\textbf{avant le {{ tournament.inscription_limit.date }}}.
|
||||
|
||||
\subsection{Procédure}
|
||||
|
||||
Si le paiement de plusieurs élèves est fait en une seule opération, merci de contacter
|
||||
\href{mailto: contact@tfjm.org}{contact@tfjm.org} \textbf{avant le paiement} pour garantir l'identification de ce dernier.
|
||||
|
||||
\subsubsection*{Carte bancaire (uniquement les cartes françaises)}
|
||||
Le paiement s'effectue en ligne via la plateforme à l'adresse : \url{https://www.helloasso.com/associations/animath/evenements/tfjm-2023-tournois-regionaux}
|
||||
|
||||
Vous devez impérativement indiquer dans le champ "Référence" la mention "TFJMpu" suivie des noms et prénoms \textbf{de l'élève}.
|
||||
|
||||
\subsubsection*{Virement}
|
||||
\textbf{Si vous ne pouvez pas utiliser le paiement par carte}, vous pouvez faire un virement sur le compte ci-dessous en
|
||||
indiquant bien dans le champ "motif" (ou autre champ propre à votre banque dont le contenu est communiqué au destinataire)
|
||||
la mention "TFJMpu" suivie des noms et prénoms \textbf{de l'élève}.
|
||||
|
||||
IBAN FR76 1027 8065 0000 0206 4290 127
|
||||
|
||||
BIC CMCIFR2A
|
||||
|
||||
\subsubsection*{Autre}
|
||||
|
||||
Si aucune de ces procédures n'est possible pour vous, envoyez un mail à \href{mailto: contact@tfjm.org}{contact@tfjm.org}
|
||||
pour que nous trouvions une solution à vos difficultés.
|
||||
|
||||
{% else %}
|
||||
Le tournoi est gratuit, vous n'avez aucun frais à avoir.
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
\end{document}
|
19
registration/templates/registration/update_user.html
Normal file
19
registration/templates/registration/update_user.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends request.content_only|yesno:"empty.html,base.html" %}
|
||||
|
||||
{% load crispy_forms_filters i18n %}
|
||||
|
||||
{% block extracss %}
|
||||
{{ registration_form.media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
<div id="form-content">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{{ registration_form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-success" type="submit">{% trans "Update" %}</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
19
registration/templates/registration/upload_health_sheet.html
Normal file
19
registration/templates/registration/upload_health_sheet.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
<a class="btn btn-info" href="{% url "registration:user_detail" pk=object.user.pk %}"><i class="fas fa-arrow-left"></i> {% trans "Back to the user detail" %}</a>
|
||||
<hr>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div id="form-content">
|
||||
<div class="alert alert-info">
|
||||
{% trans "Health sheet template:" %}
|
||||
<a class="alert-link" href="{% static "Fiche_sanitaire.pdf" %}">{% trans "Download" %}</a>
|
||||
</div>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
@ -0,0 +1,19 @@
|
||||
{% extends request.content_only|yesno:"empty.html,base.html" %}
|
||||
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
<a class="btn btn-info" href="{% url "registration:user_detail" pk=object.user.pk %}"><i class="fas fa-arrow-left"></i> {% trans "Back to the user detail" %}</a>
|
||||
<hr>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div id="form-content">
|
||||
<div class="alert alert-info">
|
||||
{% trans "Authorization template:" %}
|
||||
<a class="alert-link" href="{% url "registration:parental_authorization_template" %}?registration_id={{ object.pk }}&tournament_id={{ object.team.participation.tournament.pk }}">{% trans "Download" %}</a>
|
||||
</div>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
@ -0,0 +1,20 @@
|
||||
{% extends request.content_only|yesno:"empty.html,base.html" %}
|
||||
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
<a class="btn btn-info" href="{% url "registration:user_detail" pk=object.user.pk %}"><i class="fas fa-arrow-left"></i> {% trans "Back to the user detail" %}</a>
|
||||
<hr>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div id="form-content">
|
||||
<div class="alert alert-info">
|
||||
{% trans "Authorization templates:" %}
|
||||
<a class="alert-link" href="{% url "registration:photo_authorization_adult_template" %}?registration_id={{ object.pk }}&tournament_id={{ object.team.participation.tournament.pk }}">{% trans "Adult" %}</a> —
|
||||
<a class="alert-link" href="{% url "registration:photo_authorization_child_template" %}?registration_id={{ object.pk }}&tournament_id={{ object.team.participation.tournament.pk }}">{% trans "Child" %}</a>
|
||||
</div>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
@ -0,0 +1,15 @@
|
||||
{% extends request.content_only|yesno:"empty.html,base.html" %}
|
||||
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
<a class="btn btn-info" href="{% url "registration:user_detail" pk=object.user.pk %}"><i class="fas fa-arrow-left"></i> {% trans "Back to the user detail" %}</a>
|
||||
<hr>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div id="form-content">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
225
registration/templates/registration/user_detail.html
Normal file
225
registration/templates/registration/user_detail.html
Normal file
@ -0,0 +1,225 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
{% trans "any" as any %}
|
||||
|
||||
<div class="card bg-body shadow">
|
||||
<div class="card-header text-center">
|
||||
<h4>{{ user_object.first_name }} {{ user_object.last_name }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6 text-end">{% trans "Last name:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.last_name }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "First name:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.first_name }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Email:" %}</dt>
|
||||
<dd class="col-sm-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a>
|
||||
{% if not user_object.registration.email_confirmed %} (<em>{% trans "Not confirmed" %}, <a href="{% url "registration:email_validation_resend" pk=user_object.pk %}">{% trans "resend the validation link" %}</a></em>){% endif %}</dd>
|
||||
|
||||
{% if user_object == user %}
|
||||
<dt class="col-sm-6 text-end">{% trans "Password:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
<a href="{% url 'password_change' %}" class="btn btn-sm btn-secondary">
|
||||
<i class="fas fa-edit"></i> {% trans "Change password" %}
|
||||
</a>
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if user_object.registration.participates %}
|
||||
<dt class="col-sm-6 text-end">{% trans "Team:" %}</dt>
|
||||
{% trans "any" as any %}
|
||||
<dd class="col-sm-6">
|
||||
<a href="{% if user_object.registration.team %}{% url "participation:team_detail" pk=user_object.registration.team.pk %}{% else %}#{% endif %}">
|
||||
{{ user_object.registration.team|default:any }}
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
{% if user_object.registration.studentregistration %}
|
||||
<dt class="col-sm-6 text-end">{% trans "Birth date:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.birth_date }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Gender:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.get_gender_display }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Address:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.address }}, {{ user_object.registration.zip_code|stringformat:'05d' }} {{ user_object.registration.city }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Phone number:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.phone_number }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Health issues:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.health_issues|default:any }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Photo authorization:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if user_object.registration.photo_authorization %}
|
||||
<a href="{{ user_object.registration.photo_authorization.url }}">{% trans "Download" %}</a>
|
||||
{% endif %}
|
||||
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadPhotoAuthorizationModal">{% trans "Replace" %}</button>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if user_object.registration.studentregistration %}
|
||||
{% if user_object.registration.under_18 and user_object.registration.team.participation.tournament and not user_object.registration.team.participation.tournament.remote %}
|
||||
<dt class="col-sm-6 text-end">{% trans "Health sheet:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if user_object.registration.health_sheet %}
|
||||
<a href="{{ user_object.registration.health_sheet.url }}">{% trans "Download" %}</a>
|
||||
{% endif %}
|
||||
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadHealthSheetModal">{% trans "Replace" %}</button>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Vaccine sheet:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if user_object.registration.vaccine_sheet %}
|
||||
<a href="{{ user_object.registration.vaccine_sheet.url }}">{% trans "Download" %}</a>
|
||||
{% endif %}
|
||||
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadVaccineSheetModal">{% trans "Replace" %}</button>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Parental authorization:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if user_object.registration.parental_authorization %}
|
||||
<a href="{{ user_object.registration.parental_authorization.url }}">{% trans "Download" %}</a>
|
||||
{% endif %}
|
||||
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadParentalAuthorizationModal">{% trans "Replace" %}</button>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Student class:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.get_student_class_display }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "School:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.school }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Responsible name:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.responsible_name }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Responsible phone number:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.responsible_phone }}</dd>
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Responsible email address:" %}</dt>
|
||||
{% with user_object.registration.responsible_email as email %}
|
||||
<dd class="col-sm-6"><a href="mailto:{{ email }}">{{ email }}</a></dd>
|
||||
{% endwith %}
|
||||
{% elif user_object.registration.coachregistration or user_object.registration.is_volunteer %}
|
||||
<dt class="col-sm-6 text-end">{% trans "Profesional activity:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.professional_activity }}</dd>
|
||||
|
||||
{% if user_object.registration.is_volunteer %}
|
||||
<dt class="col-sm-6 text-end">{% trans "Admin:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.is_admin|yesno }}</dd>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-sm-6 text-end">{% trans "Grant Animath to contact me in the future about other actions:" %}</dt>
|
||||
<dd class="col-sm-6">{{ user_object.registration.give_contact_to_animath|yesno }}</dd>
|
||||
</dl>
|
||||
|
||||
{% if user_object.registration.participates and user_object.registration.team.participation.valid %}
|
||||
<hr>
|
||||
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6 text-end">{% trans "Payment information:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% trans "yes,no,pending" as yesnodefault %}
|
||||
{% with info=user_object.registration.payment.additional_information %}
|
||||
{% if info %}
|
||||
<abbr title="{{ info }}">
|
||||
{{ user_object.registration.payment.get_type_display }}, {% trans "valid:" %} {{ user_object.registration.payment.valid|yesno:yesnodefault }}
|
||||
</abbr>
|
||||
{% else %}
|
||||
{{ user_object.registration.payment.get_type_display }}, {% trans "valid:" %} {{ user_object.registration.payment.valid|yesno:yesnodefault }}
|
||||
{% endif %}
|
||||
{% if user.registration.is_admin or user_object.registration.payment.valid is False %}
|
||||
<button class="btn-sm btn-secondary" data-bs-toggle="modal" data-bs-target="#updatePaymentModal">
|
||||
<i class="fas fa-money-bill-wave"></i> {% trans "Update payment" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if user_object.registration.payment.type == "scholarship" %}
|
||||
{% if user.registration.is_admin or user == user_object %}
|
||||
<a href="{{ user_object.registration.payment.scholarship_file.url }}" class="btn btn-info">
|
||||
<i class="fas fa-file-pdf"></i> {% trans "Download scholarship attestation" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user.pk == user_object.pk or user.registration.is_admin %}
|
||||
<div class="card-footer text-center">
|
||||
<a class="btn btn-primary" href="{% url "registration:update_user" pk=user_object.pk %}">{% trans "Update" %}</a>
|
||||
{% if user.registration.is_admin %}
|
||||
<a class="btn btn-info" href="{% url "registration:user_impersonate" pk=user_object.pk %}">{% trans "Impersonate" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
|
||||
{% trans "Upload photo authorization" as modal_title %}
|
||||
{% trans "Upload" as modal_button %}
|
||||
{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="uploadPhotoAuthorization" modal_enctype="multipart/form-data" %}
|
||||
|
||||
{% trans "Upload health sheet" as modal_title %}
|
||||
{% trans "Upload" as modal_button %}
|
||||
{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="uploadHealthSheet" modal_enctype="multipart/form-data" %}
|
||||
|
||||
{% trans "Upload vaccine sheet" as modal_title %}
|
||||
{% trans "Upload" as modal_button %}
|
||||
{% url "registration:upload_user_vaccine_sheet" pk=user_object.registration.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="uploadVaccineSheet" modal_enctype="multipart/form-data" %}
|
||||
|
||||
{% trans "Upload parental authorization" as modal_title %}
|
||||
{% trans "Upload" as modal_button %}
|
||||
{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
|
||||
|
||||
{% trans "Upload parental authorization" as modal_title %}
|
||||
{% trans "Upload" as modal_button %}
|
||||
{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
|
||||
{% endif %}
|
||||
|
||||
{% if user_object.registration.team.participation.valid %}
|
||||
{% trans "Update payment" as modal_title %}
|
||||
{% trans "Update" as modal_button %}
|
||||
{% url "registration:update_payment" pk=user_object.registration.payment.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="updatePayment" modal_additional_class="modal-xl" modal_enctype="multipart/form-data" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
|
||||
initModal("uploadPhotoAuthorization", "{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk %}")
|
||||
initModal("uploadHealthSheet", "{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk %}")
|
||||
initModal("uploadVaccineSheet", "{% url "registration:upload_user_vaccine_sheet" pk=user_object.registration.pk %}")
|
||||
initModal("uploadParentalAuthorization", "{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk %}")
|
||||
{% endif %}
|
||||
|
||||
{% if user_object.registration.team.participation.valid %}
|
||||
initModal("updatePayment", "{% url "registration:update_payment" pk=user_object.registration.payment.pk %}")
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
16
registration/templates/registration/user_list.html
Normal file
16
registration/templates/registration/user_list.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load django_tables2 i18n %}
|
||||
|
||||
{% block content %}
|
||||
{% if user.registration.is_volunteer %}
|
||||
<div class="d-grid">
|
||||
<a href="{% url "registration:add_organizer" %}" class="btn gap-0 btn-secondary">
|
||||
<i class="fas fa-user-plus"></i> {% trans "Add organizer" %}
|
||||
</a>
|
||||
</div>
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
{% render_table table %}
|
||||
{% endblock %}
|
@ -0,0 +1,5 @@
|
||||
{{ object.user.last_name }}
|
||||
{{ object.user.first_name }}
|
||||
{{ object.user.email }}
|
||||
{{ object.type }}
|
||||
{{ object.role }}
|
@ -0,0 +1,11 @@
|
||||
{{ object.user.first_name }}
|
||||
{{ object.user.last_name }}
|
||||
{{ object.user.email }}
|
||||
{{ object.type }}
|
||||
{{ object.professional_activity }}
|
||||
{{ object.address }}
|
||||
{{ object.zip_code }}
|
||||
{{ object.city }}
|
||||
{{ object.phone_number }}
|
||||
{{ object.team.name }}
|
||||
{{ object.team.trigram }}
|
@ -0,0 +1,16 @@
|
||||
{{ object.user.first_name }}
|
||||
{{ object.user.last_name }}
|
||||
{{ object.user.email }}
|
||||
{{ object.type }}
|
||||
{{ object.get_student_class_display }}
|
||||
{{ object.school }}
|
||||
{{ object.birth_date }}
|
||||
{{ object.address }}
|
||||
{{ object.zip_code }}
|
||||
{{ object.city }}
|
||||
{{ object.phone_number }}
|
||||
{{ object.responsible_name }}
|
||||
{{ object.reponsible_phone }}
|
||||
{{ object.reponsible_email }}
|
||||
{{ object.team.name }}
|
||||
{{ object.team.trigram }}
|
@ -0,0 +1,5 @@
|
||||
{{ object.user.last_name }}
|
||||
{{ object.user.first_name }}
|
||||
{{ object.user.email }}
|
||||
{{ object.type }}
|
||||
{{ object.professional_activity }}
|
2
registration/templatetags/__init__.py
Normal file
2
registration/templatetags/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
14
registration/templatetags/getconfig.py
Normal file
14
registration/templatetags/getconfig.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from django import template
|
||||
|
||||
|
||||
def get_env(key: str) -> str:
|
||||
return os.getenv(key)
|
||||
|
||||
|
||||
register = template.Library()
|
||||
register.filter("get_env", get_env)
|
28
registration/templatetags/search_results_tables.py
Normal file
28
registration/templatetags/search_results_tables.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django import template
|
||||
from django_tables2 import Table
|
||||
from participation.models import Participation, Team, Tournament
|
||||
from participation.tables import ParticipationTable, TeamTable, TournamentTable
|
||||
|
||||
from ..models import Registration
|
||||
from ..tables import RegistrationTable
|
||||
|
||||
|
||||
def search_table(results):
|
||||
model_class = results[0].object.__class__
|
||||
table_class = Table
|
||||
if issubclass(model_class, Registration):
|
||||
table_class = RegistrationTable
|
||||
elif issubclass(model_class, Team):
|
||||
table_class = TeamTable
|
||||
elif issubclass(model_class, Participation):
|
||||
table_class = ParticipationTable
|
||||
elif issubclass(model_class, Tournament):
|
||||
table_class = TournamentTable
|
||||
return table_class([result.object for result in results], prefix=model_class._meta.model_name)
|
||||
|
||||
|
||||
register = template.Library()
|
||||
register.filter("search_table", search_table)
|
430
registration/tests.py
Normal file
430
registration/tests.py
Normal file
@ -0,0 +1,430 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
from participation.models import Team
|
||||
from tfjm.tokens import email_validation_token
|
||||
|
||||
from .models import CoachRegistration, StudentRegistration, VolunteerRegistration
|
||||
|
||||
|
||||
class TestIndexPage(TestCase):
|
||||
def test_index(self) -> None:
|
||||
"""
|
||||
Display the index page, without any right.
|
||||
"""
|
||||
response = self.client.get(reverse("index"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_not_authenticated(self):
|
||||
"""
|
||||
Try to load some pages without being authenticated.
|
||||
"""
|
||||
response = self.client.get(reverse("registration:reset_admin"))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("registration:reset_admin"), 302, 200)
|
||||
|
||||
User.objects.create()
|
||||
response = self.client.get(reverse("registration:user_detail", args=(1,)))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("registration:user_detail", args=(1,)))
|
||||
|
||||
Team.objects.create()
|
||||
response = self.client.get(reverse("participation:team_detail", args=(1,)))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:team_detail", args=(1,)))
|
||||
response = self.client.get(reverse("participation:update_team", args=(1,)))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:update_team", args=(1,)))
|
||||
response = self.client.get(reverse("participation:create_team"))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:create_team"))
|
||||
response = self.client.get(reverse("participation:join_team"))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:join_team"))
|
||||
response = self.client.get(reverse("participation:team_authorizations", args=(1,)))
|
||||
self.assertRedirects(response, reverse("login") + "?next="
|
||||
+ reverse("participation:team_authorizations", args=(1,)))
|
||||
response = self.client.get(reverse("participation:participation_detail", args=(1,)))
|
||||
self.assertRedirects(response, reverse("login") + "?next="
|
||||
+ reverse("participation:participation_detail", args=(1,)))
|
||||
|
||||
|
||||
class TestRegistration(TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.user = User.objects.create_superuser(
|
||||
username="admin",
|
||||
password="admin",
|
||||
email="admin@example.com",
|
||||
)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
self.student = User.objects.create(email="student@example.com")
|
||||
StudentRegistration.objects.create(
|
||||
user=self.student,
|
||||
student_class=11,
|
||||
school="Earth",
|
||||
address="1 Rue de Rivoli",
|
||||
zip_code=75001,
|
||||
city="Paris",
|
||||
)
|
||||
self.coach = User.objects.create(email="coach@example.com")
|
||||
CoachRegistration.objects.create(
|
||||
user=self.coach,
|
||||
address="1 Rue de Rivoli",
|
||||
zip_code=75001,
|
||||
city="Paris",
|
||||
professional_activity="Teacher",
|
||||
)
|
||||
|
||||
def test_admin_pages(self):
|
||||
"""
|
||||
Check that admin pages are rendering successfully.
|
||||
"""
|
||||
response = self.client.get(reverse("admin:index") + "registration/registration/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.get(reverse("admin:index")
|
||||
+ f"registration/registration/{self.user.registration.pk}/change/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(reverse("admin:index") +
|
||||
f"r/{ContentType.objects.get_for_model(VolunteerRegistration).id}/"
|
||||
f"{self.user.registration.pk}/")
|
||||
self.assertRedirects(response, "http://" + Site.objects.get().domain +
|
||||
str(self.user.registration.get_absolute_url()), 302, 200)
|
||||
|
||||
response = self.client.get(reverse("admin:index")
|
||||
+ f"registration/registration/{self.student.registration.pk}/change/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(reverse("admin:index") +
|
||||
f"r/{ContentType.objects.get_for_model(StudentRegistration).id}/"
|
||||
f"{self.student.registration.pk}/")
|
||||
self.assertRedirects(response, "http://" + Site.objects.get().domain +
|
||||
str(self.student.registration.get_absolute_url()), 302, 200)
|
||||
|
||||
response = self.client.get(reverse("admin:index")
|
||||
+ f"registration/registration/{self.coach.registration.pk}/change/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(reverse("admin:index") +
|
||||
f"r/{ContentType.objects.get_for_model(CoachRegistration).id}/"
|
||||
f"{self.coach.registration.pk}/")
|
||||
self.assertRedirects(response, "http://" + Site.objects.get().domain +
|
||||
str(self.coach.registration.get_absolute_url()), 302, 200)
|
||||
|
||||
def test_registration(self):
|
||||
"""
|
||||
Ensure that the signup form is working successfully.
|
||||
"""
|
||||
response = self.client.get(reverse("registration:signup"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Incomplete form
|
||||
response = self.client.post(reverse("registration:signup"), data=dict(
|
||||
last_name="Toto",
|
||||
first_name="Toto",
|
||||
email="toto@example.com",
|
||||
password1="azertyuiopazertyuiop",
|
||||
password2="azertyuiopazertyuiop",
|
||||
role="participant",
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.post(reverse("registration:signup"), data=dict(
|
||||
last_name="Toto",
|
||||
first_name="Toto",
|
||||
email="toto@example.com",
|
||||
password1="azertyuiopazertyuiop",
|
||||
password2="azertyuiopazertyuiop",
|
||||
role="participant",
|
||||
student_class=12,
|
||||
school="God",
|
||||
birth_date="2000-01-01",
|
||||
gender="other",
|
||||
address="1 Rue de Rivoli",
|
||||
zip_code=75001,
|
||||
city="Paris",
|
||||
phone_number="0123456789",
|
||||
responsible_name="Toto",
|
||||
responsible_phone="0123456789",
|
||||
responsible_email="toto@example.com",
|
||||
give_contact_to_animath=False,
|
||||
))
|
||||
self.assertRedirects(response, reverse("registration:email_validation_sent"), 302, 200)
|
||||
self.assertTrue(User.objects.filter(
|
||||
email="toto@example.com",
|
||||
registration__participantregistration__studentregistration__responsible_name="Toto").exists())
|
||||
|
||||
# Email is already used
|
||||
response = self.client.post(reverse("registration:signup"), data=dict(
|
||||
last_name="Toto",
|
||||
first_name="Toto",
|
||||
email="toto@example.com",
|
||||
password1="azertyuiopazertyuiop",
|
||||
password2="azertyuiopazertyuiop",
|
||||
role="participant",
|
||||
student_class=12,
|
||||
school="God",
|
||||
birth_date="2000-01-01",
|
||||
gender="other",
|
||||
address="1 Rue de Rivoli",
|
||||
zip_code=75001,
|
||||
city="Paris",
|
||||
phone_number="0123456789",
|
||||
responsible_name="Toto",
|
||||
responsible_phone="0123456789",
|
||||
responsible_email="toto@example.com",
|
||||
give_contact_to_animath=False,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.get(reverse("registration:email_validation_sent"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.post(reverse("registration:signup"), data=dict(
|
||||
last_name="Toto",
|
||||
first_name="Coach",
|
||||
email="coachtoto@example.com",
|
||||
password1="azertyuiopazertyuiop",
|
||||
password2="azertyuiopazertyuiop",
|
||||
role="coach",
|
||||
gender="other",
|
||||
address="1 Rue de Rivoli",
|
||||
zip_code=75001,
|
||||
city="Paris",
|
||||
phone_number="0123456789",
|
||||
professional_activity="God",
|
||||
give_contact_to_animath=True,
|
||||
))
|
||||
self.assertRedirects(response, reverse("registration:email_validation_sent"), 302, 200)
|
||||
self.assertTrue(User.objects.filter(email="coachtoto@example.com").exists())
|
||||
|
||||
user = User.objects.get(email="coachtoto@example.com")
|
||||
token = email_validation_token.make_token(user)
|
||||
uid = urlsafe_base64_encode(force_bytes(user.pk))
|
||||
response = self.client.get(reverse("registration:email_validation", kwargs=dict(uidb64=uid, token=token)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
user.registration.refresh_from_db()
|
||||
self.assertTrue(user.registration.email_confirmed)
|
||||
|
||||
# Token has expired
|
||||
response = self.client.get(reverse("registration:email_validation", kwargs=dict(uidb64=uid, token=token)))
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
# Uid does not exist
|
||||
response = self.client.get(reverse("registration:email_validation", kwargs=dict(uidb64=0, token="toto")))
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
response = self.client.get(reverse("registration:email_validation_resend", args=(user.pk,)))
|
||||
self.assertRedirects(response, reverse("registration:email_validation_sent"), 302, 200)
|
||||
|
||||
def test_login(self):
|
||||
"""
|
||||
With a registered user, try to log in
|
||||
"""
|
||||
response = self.client.get(reverse("login"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.client.logout()
|
||||
|
||||
response = self.client.post(reverse("login"), data=dict(
|
||||
username="admin",
|
||||
password="toto",
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.post(reverse("login"), data=dict(
|
||||
username="admin@example.com",
|
||||
password="admin",
|
||||
))
|
||||
self.assertRedirects(response, reverse("index"), 302, 200)
|
||||
|
||||
def test_user_detail(self):
|
||||
"""
|
||||
Load a user detail page.
|
||||
"""
|
||||
response = self.client.get(reverse("registration:my_account_detail"))
|
||||
self.assertRedirects(response, reverse("registration:user_detail", args=(self.user.pk,)))
|
||||
|
||||
response = self.client.get(reverse("registration:user_detail", args=(self.user.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_user_list(self):
|
||||
"""
|
||||
Display the list of all users.
|
||||
"""
|
||||
response = self.client.get(reverse("registration:user_list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_update_user(self):
|
||||
"""
|
||||
Update the user information, for each type of user.
|
||||
"""
|
||||
# To test the modification of mailing lists
|
||||
from participation.models import Team
|
||||
self.student.registration.team = Team.objects.create(
|
||||
name="toto",
|
||||
trigram="TOT",
|
||||
)
|
||||
self.student.registration.save()
|
||||
|
||||
for user, data in [(self.user, dict(professional_activity="Bot", admin=True)),
|
||||
(self.student, dict(student_class=11, school="Sky", birth_date="2001-01-01",
|
||||
gender="female", address="1 Rue de Rivoli", zip_code=75001,
|
||||
city="Paris", responsible_name="Toto",
|
||||
responsible_phone="0123456789",
|
||||
responsible_email="toto@example.com")),
|
||||
(self.coach, dict(professional_activity="God", gender="male",
|
||||
address="1 Rue de Rivoli", zip_code=75001, city="Paris"))]:
|
||||
response = self.client.get(reverse("registration:update_user", args=(user.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.post(reverse("registration:update_user", args=(user.pk,)), data=dict(
|
||||
first_name="Changed",
|
||||
last_name="Name",
|
||||
email="new_" + user.email,
|
||||
give_contact_to_animath=True,
|
||||
email_confirmed=True,
|
||||
team_id="",
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
data.update(
|
||||
first_name="Changed",
|
||||
last_name="Name",
|
||||
email="new_" + user.email,
|
||||
give_contact_to_animath=True,
|
||||
email_confirmed=True,
|
||||
team_id="",
|
||||
)
|
||||
response = self.client.post(reverse("registration:update_user", args=(user.pk,)), data=data)
|
||||
self.assertRedirects(response, reverse("registration:user_detail", args=(user.pk,)), 302, 200)
|
||||
user.refresh_from_db()
|
||||
self.assertEqual(user.email, user.username)
|
||||
self.assertFalse(user.registration.email_confirmed)
|
||||
self.assertEqual(user.first_name, "Changed")
|
||||
|
||||
def test_upload_photo_authorization(self):
|
||||
"""
|
||||
Try to upload a photo authorization.
|
||||
"""
|
||||
for auth_type in ["photo_authorization", "health_sheet", "parental_authorization"]:
|
||||
response = self.client.get(reverse("registration:upload_user_photo_authorization",
|
||||
args=(self.student.registration.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# README is not a valid PDF file
|
||||
response = self.client.post(reverse(f"registration:upload_user_{auth_type}",
|
||||
args=(self.student.registration.pk,)), data={
|
||||
auth_type: open("README.md", "rb"),
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Don't send too large files
|
||||
response = self.client.post(reverse(f"registration:upload_user_{auth_type}",
|
||||
args=(self.student.registration.pk,)), data={
|
||||
auth_type: SimpleUploadedFile("file.pdf", content=int(0).to_bytes(2000001, "big"),
|
||||
content_type="application/pdf"),
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.post(reverse(f"registration:upload_user_{auth_type}",
|
||||
args=(self.student.registration.pk,)), data={
|
||||
auth_type: open("tfjm/static/Fiche_sanitaire.pdf", "rb"),
|
||||
})
|
||||
self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200)
|
||||
|
||||
self.student.registration.refresh_from_db()
|
||||
self.assertTrue(getattr(self.student.registration, auth_type))
|
||||
|
||||
response = self.client.get(reverse(
|
||||
auth_type, args=(getattr(self.student.registration, auth_type).name.split('/')[-1],)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
from participation.models import Team
|
||||
team = Team.objects.create(name="Test", trigram="TES")
|
||||
self.student.registration.team = team
|
||||
self.student.registration.save()
|
||||
response = self.client.get(reverse("participation:team_authorizations", args=(team.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["content-type"], "application/zip")
|
||||
|
||||
# Do it twice, ensure that the previous authorization got deleted
|
||||
old_authoratization = self.student.registration.photo_authorization.path
|
||||
response = self.client.post(reverse("registration:upload_user_photo_authorization",
|
||||
args=(self.student.registration.pk,)), data=dict(
|
||||
photo_authorization=open("tfjm/static/Fiche_sanitaire.pdf", "rb"),
|
||||
))
|
||||
self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200)
|
||||
self.assertFalse(os.path.isfile(old_authoratization))
|
||||
|
||||
self.student.registration.refresh_from_db()
|
||||
self.student.registration.photo_authorization.delete()
|
||||
self.student.registration.save()
|
||||
|
||||
def test_user_detail_forbidden(self):
|
||||
"""
|
||||
Create a new user and ensure that it can't see the detail of another user.
|
||||
"""
|
||||
self.client.force_login(self.coach)
|
||||
|
||||
response = self.client.get(reverse("registration:user_detail", args=(self.user.pk,)))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
response = self.client.get(reverse("registration:update_user", args=(self.user.pk,)))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
response = self.client.get(reverse("registration:upload_user_photo_authorization",
|
||||
args=(self.student.registration.pk,)))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
response = self.client.get(reverse("photo_authorization", args=("inexisting-authorization",)))
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
with open("media/authorization/photo/example", "w") as f:
|
||||
f.write("I lost the game.")
|
||||
self.student.registration.photo_authorization = "authorization/photo/example"
|
||||
self.student.registration.save()
|
||||
response = self.client.get(reverse("photo_authorization", args=("example",)))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
os.remove("media/authorization/photo/example")
|
||||
|
||||
def test_impersonate(self):
|
||||
"""
|
||||
Admin can impersonate other people to act as them.
|
||||
"""
|
||||
response = self.client.get(reverse("registration:user_impersonate", args=(0x7ffff42ff,)))
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
# Impersonate student account
|
||||
response = self.client.get(reverse("registration:user_impersonate", args=(self.student.pk,)))
|
||||
self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200)
|
||||
self.assertEqual(self.client.session["_fake_user_id"], self.student.id)
|
||||
|
||||
# Reset admin view
|
||||
response = self.client.get(reverse("registration:reset_admin"))
|
||||
self.assertRedirects(response, reverse("index"), 302, 200)
|
||||
self.assertFalse("_fake_user_id" in self.client.session)
|
||||
|
||||
def test_research(self):
|
||||
"""
|
||||
Try to search some things.
|
||||
"""
|
||||
call_command("rebuild_index", "--noinput", "-v", 0)
|
||||
|
||||
response = self.client.get(reverse("haystack_search") + "?q=" + self.user.email)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["object_list"])
|
||||
|
||||
response = self.client.get(reverse("haystack_search") + "?q=" +
|
||||
str(self.coach.registration.professional_activity))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["object_list"])
|
||||
|
||||
response = self.client.get(reverse("haystack_search") + "?q=" +
|
||||
self.student.registration.get_student_class_display())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["object_list"])
|
43
registration/urls.py
Normal file
43
registration/urls.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from .views import AddOrganizerView, AdultPhotoAuthorizationTemplateView, ChildPhotoAuthorizationTemplateView, \
|
||||
InstructionsTemplateView, MyAccountDetailView, ParentalAuthorizationTemplateView, PaymentUpdateView, \
|
||||
ResetAdminView, SignupView, UserDetailView, UserImpersonateView, UserListView, UserResendValidationEmailView, \
|
||||
UserUpdateView, UserUploadHealthSheetView, UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, \
|
||||
UserUploadVaccineSheetView, UserValidateView, UserValidationEmailSentView
|
||||
|
||||
app_name = "registration"
|
||||
|
||||
urlpatterns = [
|
||||
path("signup/", SignupView.as_view(), name="signup"),
|
||||
path("add-organizer/", AddOrganizerView.as_view(), name="add_organizer"),
|
||||
path('validate_email/sent/', UserValidationEmailSentView.as_view(), name='email_validation_sent'),
|
||||
path('validate_email/resend/<int:pk>/', UserResendValidationEmailView.as_view(),
|
||||
name='email_validation_resend'),
|
||||
path('validate_email/<uidb64>/<token>/', UserValidateView.as_view(), name='email_validation'),
|
||||
path("user/", MyAccountDetailView.as_view(), name="my_account_detail"),
|
||||
path("user/<int:pk>/", UserDetailView.as_view(), name="user_detail"),
|
||||
path("user/<int:pk>/update/", UserUpdateView.as_view(), name="update_user"),
|
||||
path("user/<int:pk>/upload-photo-authorization/", UserUploadPhotoAuthorizationView.as_view(),
|
||||
name="upload_user_photo_authorization"),
|
||||
path("parental-authorization-template/", ParentalAuthorizationTemplateView.as_view(),
|
||||
name="parental_authorization_template"),
|
||||
path("photo-authorization-template/adult/", AdultPhotoAuthorizationTemplateView.as_view(),
|
||||
name="photo_authorization_adult_template"),
|
||||
path("photo-authorization-template/child/", ChildPhotoAuthorizationTemplateView.as_view(),
|
||||
name="photo_authorization_child_template"),
|
||||
path("instructions-template/", InstructionsTemplateView.as_view(), name="instructions_template"),
|
||||
path("user/<int:pk>/upload-health-sheet/", UserUploadHealthSheetView.as_view(),
|
||||
name="upload_user_health_sheet"),
|
||||
path("user/<int:pk>/upload-vaccine-sheet/", UserUploadVaccineSheetView.as_view(),
|
||||
name="upload_user_vaccine_sheet"),
|
||||
path("user/<int:pk>/upload-parental-authorization/", UserUploadParentalAuthorizationView.as_view(),
|
||||
name="upload_user_parental_authorization"),
|
||||
path("update-payment/<int:pk>/", PaymentUpdateView.as_view(), name="update_payment"),
|
||||
path("user/<int:pk>/impersonate/", UserImpersonateView.as_view(), name="user_impersonate"),
|
||||
path("user/list/", UserListView.as_view(), name="user_list"),
|
||||
path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"),
|
||||
]
|
669
registration/views.py
Normal file
669
registration/views.py
Normal file
@ -0,0 +1,669 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from tempfile import mkdtemp
|
||||
|
||||
from django.conf import settings
|
||||
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, ValidationError
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.http import FileResponse, Http404
|
||||
from django.shortcuts import redirect, resolve_url
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.http import urlsafe_base64_decode
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
|
||||
from django_tables2 import SingleTableView
|
||||
from magic import Magic
|
||||
from participation.models import Passage, Solution, Synthesis, Tournament
|
||||
from tfjm.tokens import email_validation_token
|
||||
from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin
|
||||
|
||||
from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, \
|
||||
ParentalAuthorizationForm, PaymentForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
|
||||
VaccineSheetForm, VolunteerRegistrationForm
|
||||
from .models import ParticipantRegistration, Payment, Registration, StudentRegistration
|
||||
from .tables import RegistrationTable
|
||||
|
||||
|
||||
class SignupView(CreateView):
|
||||
"""
|
||||
Signup, as a participant or a coach.
|
||||
"""
|
||||
model = User
|
||||
form_class = SignupForm
|
||||
template_name = "registration/signup.html"
|
||||
extra_context = dict(title=_("Sign up"))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data()
|
||||
|
||||
context["student_registration_form"] = StudentRegistrationForm(self.request.POST or None)
|
||||
context["coach_registration_form"] = CoachRegistrationForm(self.request.POST or None)
|
||||
|
||||
del context["student_registration_form"].fields["team"]
|
||||
del context["student_registration_form"].fields["email_confirmed"]
|
||||
del context["coach_registration_form"].fields["team"]
|
||||
del context["coach_registration_form"].fields["email_confirmed"]
|
||||
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
role = form.cleaned_data["role"]
|
||||
if role == "participant":
|
||||
registration_form = StudentRegistrationForm(self.request.POST)
|
||||
else:
|
||||
registration_form = CoachRegistrationForm(self.request.POST)
|
||||
del registration_form.fields["team"]
|
||||
del registration_form.fields["email_confirmed"]
|
||||
|
||||
if not registration_form.is_valid():
|
||||
return self.form_invalid(form)
|
||||
|
||||
ret = super().form_valid(form)
|
||||
registration = registration_form.instance
|
||||
registration.user = form.instance
|
||||
registration.save()
|
||||
registration.send_email_validation_link()
|
||||
|
||||
return ret
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:email_validation_sent")
|
||||
|
||||
|
||||
class AddOrganizerView(VolunteerMixin, CreateView):
|
||||
model = User
|
||||
form_class = AddOrganizerForm
|
||||
template_name = "registration/add_organizer.html"
|
||||
extra_context = dict(title=_("Add organizer"))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data()
|
||||
|
||||
context["volunteer_registration_form"] = VolunteerRegistrationForm(self.request.POST or None)
|
||||
del context["volunteer_registration_form"].fields["email_confirmed"]
|
||||
if not self.request.user.registration.is_admin:
|
||||
del context["volunteer_registration_form"].fields["admin"]
|
||||
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
registration_form = VolunteerRegistrationForm(self.request.POST)
|
||||
del registration_form.fields["email_confirmed"]
|
||||
if not self.request.user.registration.is_admin:
|
||||
del registration_form.fields["admin"]
|
||||
|
||||
if not registration_form.is_valid():
|
||||
return self.form_invalid(form)
|
||||
|
||||
ret = super().form_valid(form)
|
||||
registration = registration_form.instance
|
||||
registration.user = form.instance
|
||||
registration.save()
|
||||
registration.send_email_validation_link()
|
||||
|
||||
password = get_random_string(16)
|
||||
form.instance.set_password(password)
|
||||
form.instance.save()
|
||||
|
||||
subject = "[TFJM²] " + str(_("New TFJM² organizer account"))
|
||||
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,
|
||||
password=password,
|
||||
domain=site.domain))
|
||||
registration.user.email_user(subject, message, html_message=html)
|
||||
|
||||
if registration.is_admin:
|
||||
registration.user.is_superuser = True
|
||||
registration.user.save()
|
||||
|
||||
return ret
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:email_validation_sent")
|
||||
|
||||
|
||||
class UserValidateView(TemplateView):
|
||||
"""
|
||||
A view to validate the email address.
|
||||
"""
|
||||
title = _("Email validation")
|
||||
template_name = 'registration/email_validation_complete.html'
|
||||
extra_context = dict(title=_("Validate email"))
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""
|
||||
With a given token and user id (in params), validate the email address.
|
||||
"""
|
||||
assert 'uidb64' in kwargs and 'token' in kwargs
|
||||
|
||||
self.validlink = False
|
||||
user = self.get_user(kwargs['uidb64'])
|
||||
token = kwargs['token']
|
||||
|
||||
# Validate the token
|
||||
if user is not None and email_validation_token.check_token(user, token):
|
||||
self.validlink = True
|
||||
user.registration.email_confirmed = True
|
||||
user.registration.save()
|
||||
return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400)
|
||||
|
||||
def get_user(self, uidb64):
|
||||
"""
|
||||
Get user from the base64-encoded string.
|
||||
"""
|
||||
try:
|
||||
# urlsafe_base64_decode() decodes to bytestring
|
||||
uid = urlsafe_base64_decode(uidb64).decode()
|
||||
user = User.objects.get(pk=uid)
|
||||
except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
|
||||
user = None
|
||||
return user
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['user_object'] = self.get_user(self.kwargs["uidb64"])
|
||||
context['login_url'] = resolve_url(settings.LOGIN_URL)
|
||||
if self.validlink:
|
||||
context['validlink'] = True
|
||||
else:
|
||||
context.update({
|
||||
'title': _('Email validation unsuccessful'),
|
||||
'validlink': False,
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class UserValidationEmailSentView(TemplateView):
|
||||
"""
|
||||
Display the information that the validation link has been sent.
|
||||
"""
|
||||
template_name = 'registration/email_validation_email_sent.html'
|
||||
extra_context = dict(title=_('Email validation email sent'))
|
||||
|
||||
|
||||
class UserResendValidationEmailView(LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
Rensend the email validation link.
|
||||
"""
|
||||
model = User
|
||||
extra_context = dict(title=_("Resend email validation link"))
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user = self.get_object()
|
||||
user.registration.send_email_validation_link()
|
||||
return redirect('registration:email_validation_sent')
|
||||
|
||||
|
||||
class MyAccountDetailView(LoginRequiredMixin, RedirectView):
|
||||
"""
|
||||
Redirect to our own profile detail page.
|
||||
"""
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))
|
||||
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
Display the detail about a user.
|
||||
"""
|
||||
|
||||
model = User
|
||||
context_object_name = "user_object"
|
||||
template_name = "registration/user_detail.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
me = request.user
|
||||
if not me.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
user = self.get_object()
|
||||
if user == me or me.registration.is_admin or me.registration.is_volunteer \
|
||||
and user.registration.participates and user.registration.team \
|
||||
and (user.registration.team.participation.tournament in me.registration.organized_tournaments.all()
|
||||
or user.registration.team.participation.final
|
||||
and Tournament.final_tournament() in me.registration.organized_tournaments.all()) \
|
||||
or user.registration.is_volunteer and me.registration.is_volunteer \
|
||||
and me.registration.interesting_tournaments.intersection(user.registration.interesting_tournaments):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["title"] = _("Detail of user {user}").format(user=str(self.object.registration))
|
||||
return context
|
||||
|
||||
|
||||
class UserListView(VolunteerMixin, SingleTableView):
|
||||
"""
|
||||
Display the list of all registered users.
|
||||
"""
|
||||
model = Registration
|
||||
table_class = RegistrationTable
|
||||
template_name = "registration/user_list.html"
|
||||
|
||||
|
||||
class UserUpdateView(UserMixin, UpdateView):
|
||||
"""
|
||||
Update the detail about a user and its registration.
|
||||
"""
|
||||
model = User
|
||||
form_class = UserForm
|
||||
template_name = "registration/update_user.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user = self.get_object()
|
||||
context["title"] = _("Update user {user}").format(user=str(self.object.registration))
|
||||
context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
|
||||
instance=self.object.registration)
|
||||
if not self.request.user.registration.is_admin:
|
||||
if "team" in context["registration_form"].fields:
|
||||
del context["registration_form"].fields["team"]
|
||||
del context["registration_form"].fields["email_confirmed"]
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
user = form.instance
|
||||
registration_form = user.registration.form_class(data=self.request.POST or None,
|
||||
instance=self.object.registration)
|
||||
if not self.request.user.registration.is_admin:
|
||||
if "team" in registration_form.fields:
|
||||
del registration_form.fields["team"]
|
||||
del registration_form.fields["email_confirmed"]
|
||||
|
||||
if not registration_form.is_valid():
|
||||
return self.form_invalid(form)
|
||||
|
||||
registration_form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.object.pk,))
|
||||
|
||||
|
||||
class UserUploadPhotoAuthorizationView(UserRegistrationMixin, UpdateView):
|
||||
"""
|
||||
A participant can send its photo authorization.
|
||||
"""
|
||||
model = ParticipantRegistration
|
||||
form_class = PhotoAuthorizationForm
|
||||
template_name = "registration/upload_photo_authorization.html"
|
||||
extra_context = dict(title=_("Upload photo authorization"))
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
old_instance = ParticipantRegistration.objects.get(pk=self.object.pk)
|
||||
if old_instance.photo_authorization:
|
||||
old_instance.photo_authorization.delete()
|
||||
old_instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
|
||||
|
||||
|
||||
class UserUploadHealthSheetView(UserRegistrationMixin, UpdateView):
|
||||
"""
|
||||
A participant can send its health sheet.
|
||||
"""
|
||||
model = StudentRegistration
|
||||
form_class = HealthSheetForm
|
||||
template_name = "registration/upload_health_sheet.html"
|
||||
extra_context = dict(title=_("Upload health sheet"))
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
|
||||
if old_instance.health_sheet:
|
||||
old_instance.health_sheet.delete()
|
||||
old_instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
|
||||
|
||||
|
||||
class UserUploadVaccineSheetView(UserRegistrationMixin, UpdateView):
|
||||
"""
|
||||
A participant can send its vaccine sheet.
|
||||
"""
|
||||
model = StudentRegistration
|
||||
form_class = VaccineSheetForm
|
||||
template_name = "registration/upload_vaccine_sheet.html"
|
||||
extra_context = dict(title=_("Upload vaccine sheet"))
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
|
||||
if old_instance.vaccine_sheet:
|
||||
old_instance.vaccine_sheet.delete()
|
||||
old_instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
|
||||
|
||||
|
||||
class UserUploadParentalAuthorizationView(UserRegistrationMixin, UpdateView):
|
||||
"""
|
||||
A participant can send its parental authorization.
|
||||
"""
|
||||
model = StudentRegistration
|
||||
form_class = ParentalAuthorizationForm
|
||||
template_name = "registration/upload_parental_authorization.html"
|
||||
extra_context = dict(title=_("Upload parental authorization"))
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
|
||||
if old_instance.parental_authorization:
|
||||
old_instance.parental_authorization.delete()
|
||||
old_instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
|
||||
|
||||
|
||||
class AuthorizationTemplateView(TemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
if "registration_id" in self.request.GET:
|
||||
registration = Registration.objects.get(pk=self.request.GET.get("registration_id"))
|
||||
# Don't get unwanted information
|
||||
if registration.user == self.request.user \
|
||||
or self.request.user.is_authenticated and self.request.user.registration.is_admin:
|
||||
context["registration"] = registration
|
||||
if "tournament_id" in self.request.GET and self.request.GET.get("tournament_id").isnumeric():
|
||||
if not Tournament.objects.filter(pk=self.request.GET.get("tournament_id")).exists():
|
||||
raise PermissionDenied("Ce tournoi n'existe pas.")
|
||||
context["tournament"] = Tournament.objects.get(pk=self.request.GET.get("tournament_id"))
|
||||
else:
|
||||
raise PermissionDenied("Merci d'indiquer un tournoi.")
|
||||
|
||||
return context
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
tex = render_to_string(self.template_name, context=context, request=self.request)
|
||||
temp_dir = mkdtemp()
|
||||
with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
|
||||
f.write(tex)
|
||||
process = subprocess.Popen(["pdflatex", "-interaction=nonstopmode", f"-output-directory={temp_dir}",
|
||||
os.path.join(temp_dir, "texput.tex"), ])
|
||||
process.wait()
|
||||
return FileResponse(open(os.path.join(temp_dir, "texput.pdf"), "rb"),
|
||||
content_type="application/pdf",
|
||||
filename=self.template_name.split("/")[-1][:-3] + "pdf")
|
||||
|
||||
|
||||
class AdultPhotoAuthorizationTemplateView(AuthorizationTemplateView):
|
||||
template_name = "registration/tex/Autorisation_droit_image_majeur.tex"
|
||||
|
||||
|
||||
class ChildPhotoAuthorizationTemplateView(AuthorizationTemplateView):
|
||||
template_name = "registration/tex/Autorisation_droit_image_mineur.tex"
|
||||
|
||||
|
||||
class ParentalAuthorizationTemplateView(AuthorizationTemplateView):
|
||||
template_name = "registration/tex/Autorisation_parentale.tex"
|
||||
|
||||
|
||||
class InstructionsTemplateView(AuthorizationTemplateView):
|
||||
template_name = "registration/tex/Instructions.tex"
|
||||
|
||||
|
||||
class PaymentUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Payment
|
||||
form_class = PaymentForm
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not self.request.user.is_authenticated or \
|
||||
not self.request.user.registration.is_admin \
|
||||
and (self.request.user != self.get_object().registration.user
|
||||
or self.get_object().valid is not False):
|
||||
return self.handle_no_permission()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
if not self.request.user.registration.is_admin:
|
||||
del form.fields["type"].widget.choices[-1]
|
||||
del form.fields["valid"]
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.registration.is_admin:
|
||||
form.instance.valid = None
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class PhotoAuthorizationView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent photo authorization.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/authorization/photo/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
student = ParticipantRegistration.objects.get(photo_authorization__endswith=filename)
|
||||
user = request.user
|
||||
if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
|
||||
and student.team.participation.tournament in user.registration.organized_tournaments.all()):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = _("Photo authorization of {student}.{ext}").format(student=str(student), ext=ext)
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class HealthSheetView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent health sheet.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/authorization/health/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
student = StudentRegistration.objects.get(health_sheet__endswith=filename)
|
||||
user = request.user
|
||||
if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
|
||||
and student.team.participation.tournament in user.registration.organized_tournaments.all()):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = _("Health sheet of {student}.{ext}").format(student=str(student), ext=ext)
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class VaccineSheetView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent health sheet.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/authorization/vaccine/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
student = StudentRegistration.objects.get(vaccine_sheet__endswith=filename)
|
||||
user = request.user
|
||||
if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
|
||||
and student.team.participation.tournament in user.registration.organized_tournaments.all()):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = _("Vaccine sheet of {student}.{ext}").format(student=str(student), ext=ext)
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class ParentalAuthorizationView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent parental authorization.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/authorization/parental/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
student = StudentRegistration.objects.get(parental_authorization__endswith=filename)
|
||||
user = request.user
|
||||
if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
|
||||
and student.team.participation.tournament in user.registration.organized_tournaments.all()):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = _("Parental authorization of {student}.{ext}").format(student=str(student), ext=ext)
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class ScholarshipView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent scholarship paper.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/authorization/scholarship/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
payment = Payment.objects.get(scholarship_file__endswith=filename)
|
||||
user = request.user
|
||||
if not (payment.registration.user == user or user.registration.is_admin):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = _("Scholarship attestation of {user}.{ext}").format(user=str(user.registration), ext=ext)
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class SolutionView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent solution.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/solutions/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
solution = Solution.objects.get(file__endswith=filename)
|
||||
user = request.user
|
||||
if user.registration.participates:
|
||||
passage_participant_qs = Passage.objects.filter(Q(defender=user.registration.team.participation)
|
||||
| Q(opponent=user.registration.team.participation)
|
||||
| Q(reporter=user.registration.team.participation),
|
||||
defender=solution.participation,
|
||||
solution_number=solution.problem)
|
||||
else:
|
||||
passage_participant_qs = Passage.objects.none()
|
||||
if not (user.registration.is_admin
|
||||
or user.registration.is_volunteer and user.registration
|
||||
in (solution.participation.tournament
|
||||
if not solution.final_solution else Tournament.final_tournament()).organizers.all()
|
||||
or user.registration.is_volunteer
|
||||
and Passage.objects.filter(Q(pool__juries=user.registration)
|
||||
| Q(pool__tournament__in=user.registration.organized_tournaments.all()),
|
||||
defender=solution.participation,
|
||||
solution_number=solution.problem).exists()
|
||||
or user.registration.participates and user.registration.team
|
||||
and (solution.participation.team == user.registration.team or
|
||||
any(passage.pool.round == 1
|
||||
or timezone.now() >= passage.pool.tournament.solutions_available_second_phase
|
||||
for passage in passage_participant_qs.all()))):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = str(solution) + f".{ext}"
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class SynthesisView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Display the sent synthesis.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/syntheses/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
synthesis = Synthesis.objects.get(file__endswith=filename)
|
||||
user = request.user
|
||||
if not (user.registration.is_admin or user.registration.is_volunteer
|
||||
and (user.registration in synthesis.passage.pool.juries.all()
|
||||
or user.registration in synthesis.passage.pool.tournament.organizers.all())
|
||||
or user.registration.participates and user.registration.team == synthesis.participation.team):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
mime_type = mime.from_file(path)
|
||||
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
|
||||
# Replace file name
|
||||
true_file_name = str(synthesis) + f".{ext}"
|
||||
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
|
||||
|
||||
|
||||
class UserImpersonateView(LoginRequiredMixin, RedirectView):
|
||||
"""
|
||||
An administrator can log in through this page as someone else, and act as this other person.
|
||||
"""
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if self.request.user.registration.is_admin:
|
||||
if not User.objects.filter(pk=kwargs["pk"]).exists():
|
||||
raise Http404
|
||||
session = request.session
|
||||
session["admin"] = request.user.pk
|
||||
session["_fake_user_id"] = kwargs["pk"]
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse_lazy("registration:user_detail", args=(kwargs["pk"],))
|
||||
|
||||
|
||||
class ResetAdminView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Return to admin view, clear the session field that let an administrator to log in as someone else.
|
||||
"""
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
if "_fake_user_id" in request.session:
|
||||
del request.session["_fake_user_id"]
|
||||
return redirect(request.GET.get("path", reverse_lazy("index")))
|
Reference in New Issue
Block a user