From a07f4b0695dea38f5de57c06f4383ccee382b98b Mon Sep 17 00:00:00 2001 From: Gabriel Flores Date: Mon, 17 Oct 2022 13:11:14 -0300 Subject: [PATCH] Add Championship --- class_diagram.plantuml | 11 +++- templates/base.html | 1 + tournaments/admin.py | 2 + tournaments/common/tournament_utils.py | 1 + tournaments/forms.py | 6 ++ .../migrations/0005_auto_20221017_1124.py | 27 ++++++++ .../0006_championship_tournament_bots.py | 18 +++++ tournaments/models.py | 19 ++++++ .../tournaments/create_championship.html | 21 ++++++ .../tournaments/create_tournaments.html | 3 +- tournaments/urls.py | 6 ++ tournaments/views.py | 65 +++++++++++++++++++ 12 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 tournaments/migrations/0005_auto_20221017_1124.py create mode 100644 tournaments/migrations/0006_championship_tournament_bots.py create mode 100644 tournaments/templates/tournaments/create_championship.html diff --git a/class_diagram.plantuml b/class_diagram.plantuml index 8e426f67..6e2adf9b 100644 --- a/class_diagram.plantuml +++ b/class_diagram.plantuml @@ -43,13 +43,20 @@ class Match{ id: int game_id: str date: str -tournament_id: str(None) +tournament_id: int|null } class Tournament{ id: int name: str matches_num: int +championship_id: int|null +} + +class Championship{ +id: int +name: str +final_tournament_id: int|null } User --> Bot @@ -60,4 +67,6 @@ Bots_challenged --> Challenge Bots_in_match --> Match Match --> Tournament Challenge --> Tournament +Tournament --> Championship +Championship --> Tournament @enduml \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 994a553f..bfdfa2d1 100644 --- a/templates/base.html +++ b/templates/base.html @@ -73,6 +73,7 @@ {% if user.is_staff %} Create Tournament Generate Tournament + Create Championship Pending Tournaments {% endif %} Tournaments History diff --git a/tournaments/admin.py b/tournaments/admin.py index c113fcde..6da9bfcb 100644 --- a/tournaments/admin.py +++ b/tournaments/admin.py @@ -2,8 +2,10 @@ from .models import ( Tournament, TournamentRegistration, + Championship, ) admin.site.register(Tournament) admin.site.register(TournamentRegistration) +admin.site.register(Championship) diff --git a/tournaments/common/tournament_utils.py b/tournaments/common/tournament_utils.py index ca2298f6..e0609e6c 100644 --- a/tournaments/common/tournament_utils.py +++ b/tournaments/common/tournament_utils.py @@ -47,6 +47,7 @@ def calculate_match_results_by_player(tournament_id): "total_match_lost": 0, "total_score": 0, }) + total_key = "" for bot_name, match_result, quantity in match_members_results: if match_result == WIN: total_key = "total_match_won" diff --git a/tournaments/forms.py b/tournaments/forms.py index aa07bb64..7f6a60ec 100644 --- a/tournaments/forms.py +++ b/tournaments/forms.py @@ -16,3 +16,9 @@ def setup_bots_choices(self): class TournamentGeneratorForm(forms.Form): tournament_name = forms.CharField(label='name', required=True) max_players = forms.IntegerField(label='maxPlayers', required=True) + + +class ChampionshipCreateForm(forms.Form): + championship_name = forms.CharField(label='name', required=True) + tournament_bots = forms.IntegerField(label='Finalist Bots per Tournament', required=True) + max_players = forms.IntegerField(label='maxPlayers', required=True) diff --git a/tournaments/migrations/0005_auto_20221017_1124.py b/tournaments/migrations/0005_auto_20221017_1124.py new file mode 100644 index 00000000..7b913359 --- /dev/null +++ b/tournaments/migrations/0005_auto_20221017_1124.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.20 on 2022-10-17 14:24 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0004_tournament_status'), + ] + + operations = [ + migrations.CreateModel( + name='Championship', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('final_tournament', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='final_tournament', to='tournaments.Tournament')), + ], + ), + migrations.AddField( + model_name='tournament', + name='championship', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tournaments.Championship'), + ), + ] diff --git a/tournaments/migrations/0006_championship_tournament_bots.py b/tournaments/migrations/0006_championship_tournament_bots.py new file mode 100644 index 00000000..308b5393 --- /dev/null +++ b/tournaments/migrations/0006_championship_tournament_bots.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.20 on 2022-10-17 15:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0005_auto_20221017_1124'), + ] + + operations = [ + migrations.AddField( + model_name='championship', + name='tournament_bots', + field=models.IntegerField(default=0), + ), + ] diff --git a/tournaments/models.py b/tournaments/models.py index a8b2de18..4e919c98 100644 --- a/tournaments/models.py +++ b/tournaments/models.py @@ -2,6 +2,19 @@ from django.db import models +class Championship(models.Model): + name = models.CharField(max_length=30) + tournament_bots = models.IntegerField(default=0) + final_tournament = models.OneToOneField( + 'tournaments.Tournament', + on_delete=models.CASCADE, + related_name='final_tournament' + ) + + def __str__(self): + return f'{self.name} ({self.id})' + + class Tournament(models.Model): TOURNAMENT_PENDING_STATUS = 'pending' TOURNAMENT_ACTIVE_STATUS = 'active' @@ -14,6 +27,12 @@ class Tournament(models.Model): name = models.CharField(max_length=30) date_tournament = models.DateTimeField(auto_now_add=True, verbose_name='Date') + championship = models.ForeignKey( + Championship, + on_delete=models.CASCADE, + null=True, + blank=True, + ) status = models.CharField( max_length=8, choices=TOURNAMENT_STATUS, diff --git a/tournaments/templates/tournaments/create_championship.html b/tournaments/templates/tournaments/create_championship.html new file mode 100644 index 00000000..c574e563 --- /dev/null +++ b/tournaments/templates/tournaments/create_championship.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% load bootstrap4 %} +{% bootstrap_css %} +{% block page_content %} + +
+

Create Championship!

+
+ {% csrf_token %} + {% bootstrap_form form %} +

Registered users ({{registrations_count}})

+
    + {% for registration in registrations %} +
  • {{registration.user.username}}
  • + {% endfor %} +
+

+ +
+
+{% endblock page_content %} diff --git a/tournaments/templates/tournaments/create_tournaments.html b/tournaments/templates/tournaments/create_tournaments.html index 274b3e65..ae82025b 100644 --- a/tournaments/templates/tournaments/create_tournaments.html +++ b/tournaments/templates/tournaments/create_tournaments.html @@ -5,7 +5,8 @@

New Tournament

-
{% csrf_token %} + + {% csrf_token %} {% bootstrap_form form %}
diff --git a/tournaments/urls.py b/tournaments/urls.py index b7263894..40bb21e6 100644 --- a/tournaments/urls.py +++ b/tournaments/urls.py @@ -2,6 +2,7 @@ path, ) from .views import ( + ChampionshipCreateView, CreateTournamentView, delete_tournament, PendingTournamentListView, @@ -24,6 +25,11 @@ delete_tournament, name='delete_tournament', ), + path( + 'create_champioship/', + ChampionshipCreateView.as_view(), + name='create_championship', + ), path( 'tournament_generator/', TournamentGeneratorView.as_view(), diff --git a/tournaments/views.py b/tournaments/views.py index b401cb61..1475a59a 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -20,6 +20,7 @@ sort_position_table, ) from tournaments.models import ( + Championship, Tournament, TournamentRegistration, ) @@ -28,6 +29,7 @@ start_tournament, ) from tournaments.forms import ( + ChampionshipCreateForm, TournamentForm, TournamentGeneratorForm, ) @@ -173,6 +175,69 @@ def get_context_data(self, *args, **kwargs): return context +class ChampionshipCreateView(StaffRequiredMixin, FormView): + form_class = ChampionshipCreateForm + success_url = reverse_lazy('tournaments:tournaments_pending') + template_name = 'tournaments/create_championship.html' + + def create_championship(self, championship_name, max_players, tournament_bots): + tournament_registrations_qs = TournamentRegistration.objects.all() + tournament_registrations = list(tournament_registrations_qs) + shuffle(tournament_registrations) + tournament_count = len(tournament_registrations) // max_players + if len(tournament_registrations) % max_players > 0: + tournament_count += 1 + final_tournament = Tournament.objects.create(name='{} FINAL'.format(championship_name)) + championship = Championship.objects.create( + name=championship_name, + final_tournament=final_tournament, + tournament_bots=tournament_bots, + ) + final_tournament.championship = championship + final_tournament.save() + for tournament_index in range(0, tournament_count): + tournament = Tournament.objects.create( + name='{} #{}'.format(championship_name, tournament_index + 1), + championship=championship, + ) + registration_index = tournament_index * max_players + bots = [ + Bot.objects.get(user=tournament_registration.user, name=tournament_registration.user.email) + for tournament_registration + in tournament_registrations[registration_index: registration_index + max_players] + ] + challenges = combinations(bots, 2) + for bot_challenger, bots_challenged in challenges: + challenge = Challenge.objects.create( + bot_challenger=bot_challenger, + tournament=tournament, + ) + challenge.bots_challenged.add(bots_challenged) + + def form_valid(self, form): + championship_name = form.cleaned_data['championship_name'] + if not Championship.objects.filter(name=championship_name).exists(): + max_players = form.cleaned_data['max_players'] + tournament_bots = form.cleaned_data['tournament_bots'] + self.create_championship(championship_name, max_players, tournament_bots) + else: + messages.add_message( + self.request, + messages.ERROR, + 'It is not possible to create this record, a championship already exists with the name ' + '{}. Try a new name'.format(championship_name) + ) + return super().form_invalid(form) + return super().form_valid(form) + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + registrations = TournamentRegistration.objects.all() + context['registrations'] = registrations + context['registrations_count'] = len(registrations) + return context + + class TournamentListView(ListView): template_name = 'tournaments/tournaments_history.html'