From 4e29b4830a451580bdf700badc9a7e9e7934f070 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 31 Dec 2020 12:13:42 +0100 Subject: [PATCH] Create tournaments --- apps/participation/forms.py | 26 +++++++++- apps/participation/migrations/0001_initial.py | 4 +- ...228_1816.py => 0002_auto_20201230_1302.py} | 23 +++++--- apps/participation/models.py | 5 ++ .../participation/tournament_form.html | 13 +++++ .../participation/tournament_list.html | 2 +- apps/participation/urls.py | 6 ++- apps/participation/views.py | 35 ++++++++++++- apps/registration/migrations/0001_initial.py | 52 +++++++++++-------- .../migrations/0002_auto_20201228_2219.py | 48 ----------------- apps/registration/models.py | 2 +- tfjm/settings.py | 3 ++ tfjm/templates/base.html | 18 ++----- 13 files changed, 140 insertions(+), 97 deletions(-) rename apps/participation/migrations/{0002_auto_20201228_1816.py => 0002_auto_20201230_1302.py} (79%) create mode 100644 apps/participation/templates/participation/tournament_form.html delete mode 100644 apps/registration/migrations/0002_auto_20201228_2219.py diff --git a/apps/participation/forms.py b/apps/participation/forms.py index 7d17e5f..cce1358 100644 --- a/apps/participation/forms.py +++ b/apps/participation/forms.py @@ -3,11 +3,13 @@ import re +from bootstrap_datepicker_plus import DatePickerInput, DateTimePickerInput from django import forms from django.core.exceptions import ValidationError +from django.utils import formats from django.utils.translation import gettext_lazy as _ -from .models import Participation, Team +from .models import Participation, Team, Tournament class TeamForm(forms.ModelForm): @@ -85,3 +87,25 @@ class ValidateParticipationForm(forms.Form): label=_("Message to address to the team:"), widget=forms.Textarea(), ) + + +class TournamentForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.fields["date_start"].widget = DatePickerInput( + format=formats.get_format_lazy(format_type="DATE_INPUT_FORMATS", use_l10n=True)[0]) + self.fields["date_end"].widget = DatePickerInput( + format=formats.get_format_lazy(format_type="DATE_INPUT_FORMATS", use_l10n=True)[0]) + self.fields["inscription_limit"].widget = DateTimePickerInput( + format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0]) + self.fields["solution_limit"].widget = DateTimePickerInput( + format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0]) + self.fields["syntheses_first_phase_limit"].widget = DateTimePickerInput( + format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0]) + self.fields["syntheses_second_phase_limit"].widget = DateTimePickerInput( + format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0]) + + class Meta: + model = Tournament + fields = '__all__' diff --git a/apps/participation/migrations/0001_initial.py b/apps/participation/migrations/0001_initial.py index 5a646c4..8807016 100644 --- a/apps/participation/migrations/0001_initial.py +++ b/apps/participation/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.4 on 2020-12-28 17:16 +# Generated by Django 3.0.11 on 2020-12-30 12:02 import django.core.validators from django.db import migrations, models @@ -79,7 +79,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), ('date_start', models.DateField(default=django.utils.timezone.now, verbose_name='start')), - ('date_end', models.DateField(default=django.utils.timezone.now, verbose_name='start')), + ('date_end', models.DateField(default=django.utils.timezone.now, verbose_name='end')), ('inscription_limit', models.DateTimeField(default=django.utils.timezone.now, verbose_name='limit date for registrations')), ('solution_limit', models.DateTimeField(default=django.utils.timezone.now, verbose_name='limit date to upload solutions')), ('syntheses_first_phase_limit', models.DateTimeField(default=django.utils.timezone.now, verbose_name='limit date to upload the syntheses for the first phase')), diff --git a/apps/participation/migrations/0002_auto_20201228_1816.py b/apps/participation/migrations/0002_auto_20201230_1302.py similarity index 79% rename from apps/participation/migrations/0002_auto_20201228_1816.py rename to apps/participation/migrations/0002_auto_20201230_1302.py index f9e165a..afa485f 100644 --- a/apps/participation/migrations/0002_auto_20201228_1816.py +++ b/apps/participation/migrations/0002_auto_20201230_1302.py @@ -1,5 +1,6 @@ -# Generated by Django 3.1.4 on 2020-12-28 17:16 +# Generated by Django 3.0.11 on 2020-12-30 12:02 +import address.models from django.db import migrations, models import django.db.models.deletion @@ -9,8 +10,9 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('participation', '0001_initial'), ('registration', '0001_initial'), + ('address', '0003_auto_20200830_1851'), + ('participation', '0001_initial'), ] operations = [ @@ -19,6 +21,11 @@ class Migration(migrations.Migration): name='organizers', field=models.ManyToManyField(related_name='organized_tournaments', to='registration.VolunteerRegistration', verbose_name='organizers'), ), + migrations.AddField( + model_name='tournament', + name='place', + field=address.models.AddressField(on_delete=django.db.models.deletion.CASCADE, to='address.Address', verbose_name='place'), + ), migrations.AddIndex( model_name='team', index=models.Index(fields=['trigram'], name='participati_trigram_239255_idx'), @@ -26,17 +33,17 @@ class Migration(migrations.Migration): migrations.AddField( model_name='synthesis', name='participation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='participation.participation', verbose_name='participation'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='participation.Participation', verbose_name='participation'), ), migrations.AddField( model_name='synthesis', name='pool', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='syntheses', to='participation.pool', verbose_name='pool'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='syntheses', to='participation.Pool', verbose_name='pool'), ), migrations.AddField( model_name='solution', name='participation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='participation.participation', verbose_name='participation'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='participation.Participation', verbose_name='participation'), ), migrations.AddField( model_name='pool', @@ -51,17 +58,17 @@ class Migration(migrations.Migration): migrations.AddField( model_name='pool', name='tournament', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pools', to='participation.tournament', verbose_name='tournament'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pools', to='participation.Tournament', verbose_name='tournament'), ), migrations.AddField( model_name='participation', name='team', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='participation.team', verbose_name='team'), + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='participation.Team', verbose_name='team'), ), migrations.AddField( model_name='participation', name='tournament', - field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participation.tournament', verbose_name='tournament'), + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participation.Tournament', verbose_name='tournament'), ), migrations.AddIndex( model_name='tournament', diff --git a/apps/participation/models.py b/apps/participation/models.py index 647530d..05113b7 100644 --- a/apps/participation/models.py +++ b/apps/participation/models.py @@ -3,6 +3,7 @@ import os +from address.models import AddressField from django.core.validators import RegexValidator from django.db import models from django.db.models import Index @@ -133,6 +134,10 @@ class Tournament(models.Model): default=timezone.now, ) + place = AddressField( + verbose_name=_("place"), + ) + inscription_limit = models.DateTimeField( verbose_name=_("limit date for registrations"), default=timezone.now, diff --git a/apps/participation/templates/participation/tournament_form.html b/apps/participation/templates/participation/tournament_form.html new file mode 100644 index 0000000..1c93537 --- /dev/null +++ b/apps/participation/templates/participation/tournament_form.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% load crispy_forms_filters i18n %} + +{% block content %} +
+
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+{% endblock content %} diff --git a/apps/participation/templates/participation/tournament_list.html b/apps/participation/templates/participation/tournament_list.html index 70a79d7..860342d 100644 --- a/apps/participation/templates/participation/tournament_list.html +++ b/apps/participation/templates/participation/tournament_list.html @@ -10,7 +10,7 @@
{% render_table table %} {% if user.registration.is_admin %} - {% trans "Add tournament" %} + {% trans "Add tournament" %} {% endif %}
{% endblock %} diff --git a/apps/participation/urls.py b/apps/participation/urls.py index e02cb6e..ae45494 100644 --- a/apps/participation/urls.py +++ b/apps/participation/urls.py @@ -6,7 +6,8 @@ from django.views.generic import TemplateView from .views import CreateTeamView, JoinTeamView, \ MyParticipationDetailView, MyTeamDetailView, ParticipationDetailView, TeamAuthorizationsView, \ - TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, TournamentListView + TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, TournamentCreateView, TournamentDetailView, \ + TournamentListView, TournamentUpdateView app_name = "participation" @@ -23,5 +24,8 @@ urlpatterns = [ path("detail/", MyParticipationDetailView.as_view(), name="my_participation_detail"), path("detail//", ParticipationDetailView.as_view(), name="participation_detail"), path("tournament/", TournamentListView.as_view(), name="tournament_list"), + path("tournament/create/", TournamentCreateView.as_view(), name="tournament_create"), + path("tournament//", TournamentDetailView.as_view(), name="tournament_detail"), + path("tournament//update/", TournamentUpdateView.as_view(), name="tournament_update"), path("chat/", TemplateView.as_view(template_name="participation/chat.html"), name="chat") ] diff --git a/apps/participation/views.py b/apps/participation/views.py index cc567e1..c75d05d 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -23,7 +23,8 @@ from tfjm.lists import get_sympa_client from tfjm.matrix import Matrix from tfjm.views import AdminMixin -from .forms import JoinTeamForm, ParticipationForm, RequestValidationForm, TeamForm, ValidateParticipationForm +from .forms import JoinTeamForm, ParticipationForm, RequestValidationForm, TeamForm, ValidateParticipationForm, \ + TournamentForm from .models import Participation, Team, Tournament from .tables import TeamTable, TournamentTable @@ -404,5 +405,37 @@ class ParticipationDetailView(LoginRequiredMixin, DetailView): class TournamentListView(SingleTableView): + """ + Display the list of all tournaments. + """ model = Tournament table_class = TournamentTable + + +class TournamentCreateView(AdminMixin, CreateView): + """ + Create a new tournament. + """ + model = Tournament + form_class = TournamentForm + + def get_success_url(self): + return reverse_lazy("participation:tournament_detail", args=(self.object.pk,)) + + +class TournamentUpdateView(AdminMixin, UpdateView): + """ + Update tournament detail. + """ + model = Tournament + form_class = TournamentForm + + def get_success_url(self): + return reverse_lazy("participation:tournament_detail", args=(self.object.pk,)) + + +class TournamentDetailView(DetailView): + """ + Display tournament detail. + """ + model = Tournament diff --git a/apps/registration/migrations/0001_initial.py b/apps/registration/migrations/0001_initial.py index bae47af..d87bb4e 100644 --- a/apps/registration/migrations/0001_initial.py +++ b/apps/registration/migrations/0001_initial.py @@ -1,8 +1,11 @@ -# Generated by Django 3.1.4 on 2020-12-28 17:16 +# Generated by Django 3.0.11 on 2020-12-30 12:02 +import address.models +import datetime from django.conf import settings from django.db import migrations, models import django.db.models.deletion +import phonenumber_field.modelfields import registration.models @@ -11,9 +14,10 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('participation', '0001_initial'), ('contenttypes', '0002_remove_content_type_name'), + ('address', '0003_auto_20200830_1851'), + ('participation', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -23,7 +27,7 @@ class Migration(migrations.Migration): ('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')), + ('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={ @@ -31,25 +35,16 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'registrations', }, ), - migrations.CreateModel( - name='AdminRegistration', - 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')), - ('role', models.TextField(verbose_name='role of the administrator')), - ], - options={ - 'verbose_name': 'admin registration', - 'verbose_name_plural': 'admin registrations', - }, - bases=('registration.registration',), - ), 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')), + ('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')), + ('phone_number', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None, verbose_name='phone number')), ('photo_authorization', models.FileField(blank=True, default='', upload_to=registration.models.get_random_photo_filename, verbose_name='photo authorization')), ('health_sheet', models.FileField(blank=True, default='', upload_to=registration.models.get_random_health_filename, verbose_name='health sheet')), - ('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')), + ('address', address.models.AddressField(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='address.Address', verbose_name='address')), + ('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, @@ -60,7 +55,7 @@ class Migration(migrations.Migration): 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')), + ('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={ @@ -69,10 +64,22 @@ class Migration(migrations.Migration): }, 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')), + ('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={ @@ -84,9 +91,12 @@ class Migration(migrations.Migration): 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')), + ('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')), ], options={ diff --git a/apps/registration/migrations/0002_auto_20201228_2219.py b/apps/registration/migrations/0002_auto_20201228_2219.py deleted file mode 100644 index 95e1b8c..0000000 --- a/apps/registration/migrations/0002_auto_20201228_2219.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.0.11 on 2020-12-28 21:19 - -import address.models -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone -import phonenumber_field.modelfields - - -class Migration(migrations.Migration): - - dependencies = [ - ('address', '0003_auto_20200830_1851'), - ('registration', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='participantregistration', - name='address', - field=address.models.AddressField(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='address.Address', verbose_name='address'), - ), - migrations.AddField( - model_name='participantregistration', - name='birth_date', - field=models.DateField(default=django.utils.timezone.now, verbose_name='birth date'), - ), - migrations.AddField( - model_name='participantregistration', - name='phone_number', - field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None, verbose_name='phone number'), - ), - migrations.AddField( - model_name='studentregistration', - name='responsible_email', - field=models.EmailField(default='', max_length=254, verbose_name='responsible email address'), - ), - migrations.AddField( - model_name='studentregistration', - name='responsible_name', - field=models.CharField(default='', max_length=255, verbose_name='responsible name'), - ), - migrations.AddField( - model_name='studentregistration', - name='responsible_phone', - field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None, verbose_name='responsible phone number'), - ), - ] diff --git a/apps/registration/models.py b/apps/registration/models.py index ff1cdc4..532538e 100644 --- a/apps/registration/models.py +++ b/apps/registration/models.py @@ -259,7 +259,7 @@ class VolunteerRegistration(Registration): return VolunteerRegistrationForm -class AdminRegistration(Registration): +class AdminRegistration(VolunteerRegistration): """ Specific registration for admins. They have a field to justify they status. diff --git a/tfjm/settings.py b/tfjm/settings.py index 75f98ca..bfa8bd5 100644 --- a/tfjm/settings.py +++ b/tfjm/settings.py @@ -233,3 +233,6 @@ PHONENUMBER_DB_FORMAT = 'NATIONAL' PHONENUMBER_DEFAULT_REGION = 'FR' GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") + +# Use local Jquery +JQUERY_URL = False diff --git a/tfjm/templates/base.html b/tfjm/templates/base.html index b233191..f2b23a0 100644 --- a/tfjm/templates/base.html +++ b/tfjm/templates/base.html @@ -19,23 +19,15 @@ {% endif %} {# Bootstrap CSS #} - + {# JQuery, Bootstrap and Turbolinks JavaScript #} - - - + +