diff --git a/padel/urls.py b/padel/urls.py index 2b3274e..3520c72 100644 --- a/padel/urls.py +++ b/padel/urls.py @@ -23,6 +23,7 @@ router.register(r'users', views.UserViewSet) router.register(r'clubs', views.ClubViewSet) router.register(r'matches', views.MatchViewSet) router.register(r'tournaments', views.TournamentViewSet) +router.register(r'teams', views.TeamViewSet) urlpatterns = [ path('api/', include(router.urls)), diff --git a/scores/admin.py b/scores/admin.py index 0640956..9338ed1 100644 --- a/scores/admin.py +++ b/scores/admin.py @@ -4,7 +4,9 @@ from django.contrib import admin from .models import Club from .models import Match from .models import Tournament +from .models import Team admin.site.register(Club) admin.site.register(Tournament) admin.site.register(Match) +admin.site.register(Team) diff --git a/scores/migrations/0011_match_match_index_tournament_auto_scrolling_team.py b/scores/migrations/0011_match_match_index_tournament_auto_scrolling_team.py new file mode 100644 index 0000000..e283c11 --- /dev/null +++ b/scores/migrations/0011_match_match_index_tournament_auto_scrolling_team.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2 on 2023-07-28 11:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0010_alter_tournament_club'), + ] + + operations = [ + migrations.AddField( + model_name='match', + name='match_index', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='tournament', + name='auto_scrolling', + field=models.BooleanField(default=False), + ), + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('playerOne', models.CharField(blank=True, max_length=200)), + ('playerTwo', models.CharField(blank=True, max_length=200)), + ('rank', models.IntegerField(default=0)), + ('position', models.IntegerField(default=0)), + ('bracket', models.BooleanField(default=False)), + ('positionLabel', models.CharField(blank=True, max_length=200)), + ('match', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='scores.match')), + ], + ), + ] diff --git a/scores/migrations/0012_rename_playerone_team_name_remove_team_playertwo.py b/scores/migrations/0012_rename_playerone_team_name_remove_team_playertwo.py new file mode 100644 index 0000000..9f996b4 --- /dev/null +++ b/scores/migrations/0012_rename_playerone_team_name_remove_team_playertwo.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2 on 2023-07-28 12:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0011_match_match_index_tournament_auto_scrolling_team'), + ] + + operations = [ + migrations.RenameField( + model_name='team', + old_name='playerOne', + new_name='name', + ), + migrations.RemoveField( + model_name='team', + name='playerTwo', + ), + ] diff --git a/scores/migrations/0013_team_backgroundcolor.py b/scores/migrations/0013_team_backgroundcolor.py new file mode 100644 index 0000000..b6afbc1 --- /dev/null +++ b/scores/migrations/0013_team_backgroundcolor.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2023-07-28 12:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0012_rename_playerone_team_name_remove_team_playertwo'), + ] + + operations = [ + migrations.AddField( + model_name='team', + name='backgroundColor', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/scores/migrations/0014_team_shortname.py b/scores/migrations/0014_team_shortname.py new file mode 100644 index 0000000..40e1b1a --- /dev/null +++ b/scores/migrations/0014_team_shortname.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2023-07-28 13:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0013_team_backgroundcolor'), + ] + + operations = [ + migrations.AddField( + model_name='team', + name='shortname', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/scores/migrations/0015_remove_team_shortname_tournament_shortname.py b/scores/migrations/0015_remove_team_shortname_tournament_shortname.py new file mode 100644 index 0000000..69ba255 --- /dev/null +++ b/scores/migrations/0015_remove_team_shortname_tournament_shortname.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2 on 2023-07-28 13:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0014_team_shortname'), + ] + + operations = [ + migrations.RemoveField( + model_name='team', + name='shortname', + ), + migrations.AddField( + model_name='tournament', + name='shortname', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/scores/migrations/0016_team_datelabel.py b/scores/migrations/0016_team_datelabel.py new file mode 100644 index 0000000..16facd1 --- /dev/null +++ b/scores/migrations/0016_team_datelabel.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2023-07-28 15:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0015_remove_team_shortname_tournament_shortname'), + ] + + operations = [ + migrations.AddField( + model_name='team', + name='dateLabel', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/scores/migrations/0017_match_refreshrate_tournament_tournament_index.py b/scores/migrations/0017_match_refreshrate_tournament_tournament_index.py new file mode 100644 index 0000000..70ed489 --- /dev/null +++ b/scores/migrations/0017_match_refreshrate_tournament_tournament_index.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2 on 2023-07-28 16:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scores', '0016_team_datelabel'), + ] + + operations = [ + migrations.AddField( + model_name='match', + name='refreshrate', + field=models.IntegerField(default=10), + ), + migrations.AddField( + model_name='tournament', + name='tournament_index', + field=models.IntegerField(default=0), + ), + ] diff --git a/scores/models.py b/scores/models.py index ce3bec9..8836a42 100644 --- a/scores/models.py +++ b/scores/models.py @@ -12,6 +12,9 @@ class Club(models.Model): class Tournament(models.Model): club = models.ForeignKey(Club, on_delete=models.CASCADE, default=None) name = models.CharField(max_length=200) + auto_scrolling = models.BooleanField(default=False) + shortname = models.CharField(max_length=200, blank=True) + tournament_index = models.IntegerField(default=0) def __str__(self): return self.name @@ -27,6 +30,8 @@ class Match(models.Model): date = models.DateTimeField('start date') enddate = models.DateTimeField('end date', null=True, blank=True) court = models.IntegerField(default=0) + match_index = models.IntegerField(default=0) + refreshrate = models.IntegerField(default=10) title = models.CharField(max_length=200, blank=True) team1 = models.CharField(max_length=200, blank=True) @@ -46,6 +51,9 @@ class Match(models.Model): team2scorecolumn4 = models.CharField(max_length=200, blank=True) team2scorecolumn5 = models.CharField(max_length=200, blank=True) + def teams(self): + return self.team_set.order_by('position') + def poule(self): return self.court / 100 @@ -100,3 +108,15 @@ class Match(models.Model): def seconds(self): return (timezone.now() - self.date).total_seconds() + + +class Team(models.Model): + name = models.CharField(max_length=200, blank=True) + rank = models.IntegerField(default=0) + position = models.IntegerField(default=0) + bracket = models.BooleanField(default=False) + positionLabel = models.CharField(max_length=200, blank=True) + dateLabel = models.CharField(max_length=200, blank=True) + backgroundColor = models.CharField(max_length=200, blank=True) + match = models.ForeignKey(Match, on_delete=models.CASCADE, default=None) + diff --git a/scores/serializers.py b/scores/serializers.py index aee1a9b..7295715 100644 --- a/scores/serializers.py +++ b/scores/serializers.py @@ -1,6 +1,6 @@ from django.contrib.auth.models import User from rest_framework import serializers -from .models import Match, Club, Tournament +from .models import Match, Club, Tournament, Team class UserSerializer(serializers.HyperlinkedModelSerializer): @@ -16,11 +16,17 @@ class ClubSerializer(serializers.HyperlinkedModelSerializer): class TournamentSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Tournament - fields = ['id', 'name', 'club'] + fields = ['id', 'name', 'club', 'auto_scrolling', 'shortname', 'tournament_index'] class MatchSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Match - fields = ['id', 'tournament', 'date', 'enddate', 'court', 'title', 'team1', 'team2', 'team3', 'team4', + fields = ['id', 'match_index', 'tournament', 'date', 'enddate', 'title', 'refreshrate', 'team1', 'team2', 'team3', 'team4', 'team5', 'team1scorecolumn1', 'team1scorecolumn2', 'team1scorecolumn3', 'team1scorecolumn4', 'team1scorecolumn5', 'team2scorecolumn1', 'team2scorecolumn2', 'team2scorecolumn3', 'team2scorecolumn4', 'team2scorecolumn5'] + +class TeamSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Team + fields = ['id', 'name', 'brackgroundColor', 'rank', 'position', 'bracket', 'positionLabel', 'dateLabel', 'match'] + diff --git a/scores/static/scores/style.css b/scores/static/scores/style.css index c7f10d2..0a83649 100644 --- a/scores/static/scores/style.css +++ b/scores/static/scores/style.css @@ -105,6 +105,7 @@ td { width: 28%; padding: 0px 20px; vertical-align: top; /* here */ + margin-bottom: 40px; } @@ -134,6 +135,13 @@ td { font-size: 150%; } +.teams { + padding-top: 20px; + width: 1000px; + margin: 0 auto; + align-items: center; +} + .smatch { padding-top: 20px; width: 800px; diff --git a/scores/templates/scores/index.html b/scores/templates/scores/index.html index 22cc4e2..8d8eb90 100644 --- a/scores/templates/scores/index.html +++ b/scores/templates/scores/index.html @@ -3,12 +3,51 @@ {% load static %} + {% if tv %} + + + + {% endif %} + Padel
+ {% if live_matches.count|add:ended_matches.count > 6 and auto_scrolling and tv %} + + {% endif %} {% if club.header %}
{{ club.header }}
diff --git a/scores/templates/scores/match.html b/scores/templates/scores/match.html index 715f5db..86aef7d 100644 --- a/scores/templates/scores/match.html +++ b/scores/templates/scores/match.html @@ -3,12 +3,59 @@ {% load static %} - - Padel - + {% if tv %} + + + + {% endif %} + + + Padel + {% if match.teams %} + + {% else %} + + {% endif %} - +{% if tv %} +
+{% else %}
+{% endif %} + + {% if tv %} + + {% endif %} {% if match.tournament.club.header %}
{{ match.tournament.club.header }}
@@ -19,15 +66,40 @@
{{ match.tournament.name }}
-
- - -

{{ match.title }}

- - +
+ {% if match.teams %} +

Liste des équipes

+ + + + + + + + + {% for team in teams %} + + + + + + {% if team.bracket %} + + {% else %} + + {% endif %} + + {% if team.dateLabel %} + + {% else %} + + {% endif %} + + {% endfor %} +
RangNomPoidsEntréeJour
#{{ team.position }}{{ team.name|linebreaksbr }}{{ team.rank }}Poule{{ team.positionLabel }}{{ team.dateLabel }} -
+ + {% else %} +

{{ match.title }}

{% if match.team3 %} @@ -87,6 +159,11 @@

{% endif %} + + + {% endif %} + +
diff --git a/scores/templates/scores/tournament.html b/scores/templates/scores/tournament.html index 2fb05c8..dd69ed5 100644 --- a/scores/templates/scores/tournament.html +++ b/scores/templates/scores/tournament.html @@ -3,12 +3,51 @@ {% load static %} + {% if tv %} + + + + {% endif %} + Padel
+ {% if live_matches.count|add:ended_matches.count > 6 and auto_scrolling and tv %} + + {% endif %} {% if tournament.club.header %}
{{ tournament.club.header }}
diff --git a/scores/urls.py b/scores/urls.py index 37e44a3..ccd8e54 100644 --- a/scores/urls.py +++ b/scores/urls.py @@ -19,10 +19,32 @@ from django.urls import path from . import views urlpatterns = [ + path('match//', views.match, name='match'), + + path('tv/club///equipes/', views.tv_teams_club_name_tournament, name='match_scrolling'), + path('tv/tournoi//equipes/', views.tv_teams_tournament_name, name='match_scrolling'), + path('tv/club///equipes/', views.tv_teams_club_name_tournament_name, name='match_scrolling'), + + path('tv/', views.index, name='index_scrolling'), + path('tv/tournoi//', views.tv_tournament_name, name='tournament_scrolling'), + path('tv/club//', views.club_name, name='club'), + path('tv/club///', views.tv_club_name_tournament, name='tournament_scrolling'), + path('tv/club///', views.tv_club_name_tournament_name, name='tournament_scrolling'), + + path('club///equipes/', views.teams_club_name_tournament_name, name='match'), + path('tournoi//equipes/', views.teams_tournament_name, name='match'), + path('tournoi//equipes/', views.teams_tournament, name='match'), + path('club///equipes/', views.teams_club_name_tournament_name, name='match'), + path('club///equipes', views.teams_club_name_tournament, name='match'), + path('club///', views.club_name_tournament_name, name='tournament'), + path('club///', views.club_name_tournament, name='tournament'), + path('club//', views.club_name, name='club'), + path('', views.index, name='index'), path('club//', views.club, name='club'), path('club//', views.club_name, name='club'), - path('tournament//', views.tournament, name='tournament'), - path('club///', views.tournament_club, name='tournament'), - path('match//', views.match, name='match'), + path('club///', views.club_name_tournament, name='tournament'), + path('club///', views.club_name_tournament_name, name='tournament'), + path('tournoi//', views.tournament, name='tournament'), + path('tournoi//', views.tournament_name, name='tournament'), ] diff --git a/scores/views.py b/scores/views.py index f3641dd..b202854 100644 --- a/scores/views.py +++ b/scores/views.py @@ -2,8 +2,8 @@ from django.shortcuts import render, get_object_or_404 from django.http import HttpResponse from django.template import loader from django.contrib.auth.models import User -from .models import Match, Club, Tournament -from .serializers import UserSerializer, MatchSerializer, ClubSerializer, TournamentSerializer +from .models import Match, Club, Tournament, Team +from .serializers import UserSerializer, MatchSerializer, ClubSerializer, TournamentSerializer, TeamSerializer from rest_framework import viewsets from rest_framework import permissions @@ -41,9 +41,18 @@ def club_name(request, club_name): } return HttpResponse(template.render(context, request)) -def tournament_club(request, club_name, tournament_id): +def tournament_name(request, tournament_shortname): + tournamentFound = Tournament.objects.filter(shortname__iexact=tournament_shortname.lower()).first() + return tournament(request, tournamentFound.id) + +def club_name_tournament(request, club_name, tournament_id): return tournament(request, tournament_id) +def club_name_tournament_name(request, club_name, tournament_shortname): + club = get_object_or_404(Club, name__iexact=club_name.lower()) + tournamentFound = Tournament.objects.filter(club_id=club.id, shortname__iexact=tournament_shortname.lower()).first() + return tournament(request, tournamentFound.id) + def tournament(request, tournament_id): tournament = get_object_or_404(Tournament, pk=tournament_id) @@ -61,12 +70,107 @@ def tournament(request, tournament_id): def match(request, match_id): match = get_object_or_404(Match, pk=match_id) + teams = Team.objects.filter(match=match.id).order_by('position') template = loader.get_template('scores/match.html') context = { 'match': match, + 'teams': teams, } return HttpResponse(template.render(context, request)) + +def teams_tournament_name(request, tournament_shortname): + tournament = get_object_or_404(Tournament, shortname__iexact=tournament_shortname.lower()) + return teams_tournament(request, tournament.id) + +def teams_club_name_tournament(request, club_name, tournament_id): + club = get_object_or_404(Club, name__iexact=club_name.lower()) + tournament = get_object_or_404(Tournament, pk=tournament_id) + return teams_tournament(request, tournament.id) + +def teams_club_name_tournament_name(request, club_name, tournament_shortname): + club = get_object_or_404(Club, name__iexact=club_name.lower()) + tournament = Tournament.objects.filter(club_id=club.id, shortname__iexact=tournament_shortname.lower()).first() + return teams_tournament(request, tournament.id) + +def teams_tournament(request, tournament_id): + + tournament = get_object_or_404(Tournament, pk=tournament_id) + match = Match.objects.filter(tournament=tournament.id).filter(match_index=0).first() + teams = Team.objects.filter(match=match.id).order_by('position') + template = loader.get_template('scores/match.html') + context = { + 'match': match, + 'teams': teams, + } + return HttpResponse(template.render(context, request)) + +def tv_index(request): + club = Club.objects.first() + live_matches = Match.objects.filter(enddate__isnull=True).order_by('court') + ended_matches = Match.objects.filter(enddate__isnull=False).order_by('court') + template = loader.get_template('scores/index.html') + context = { + 'club': club, + 'live_matches': live_matches, + 'ended_matches': ended_matches, + 'tv': True, + } + return HttpResponse(template.render(context, request)) + +def tv_tournament_name(request, tournament_shortname): + tournamentFound = Tournament.objects.filter(shortname__iexact=tournament_shortname.lower()).first() + return tv_tournament(request, tournamentFound.id) + +def tv_club_name_tournament(request, club_name, tournament_id): + return tv_tournament(request, tournament_id) + +def tv_club_name_tournament_name(request, club_name, tournament_shortname): + club = get_object_or_404(Club, name__iexact=club_name.lower()) + tournamentFound = Tournament.objects.filter(club_id=club.id, shortname__iexact=tournament_shortname.lower()).first() + return tv_tournament(request, tournamentFound.id) + +def tv_tournament(request, tournament_id): + + tournament = get_object_or_404(Tournament, pk=tournament_id) + live_matches = Match.objects.filter(tournament=tournament.id, enddate__isnull=True).order_by('court') + ended_matches = Match.objects.filter(tournament=tournament.id, enddate__isnull=False).order_by('court') + + template = loader.get_template('scores/tournament.html') + context = { + 'tournament': tournament, + 'live_matches': live_matches, + 'ended_matches': ended_matches, + 'tv': True, + } + return HttpResponse(template.render(context, request)) + +def tv_teams_tournament_name(request, tournament_shortname): + tournament = get_object_or_404(Tournament, shortname__iexact=tournament_shortname.lower()) + return tv_teams_tournament(request, tournament.id) + +def tv_teams_club_name_tournament(request, club_name, tournament_id): + club = get_object_or_404(Club, name__iexact=club_name.lower()) + tournament = get_object_or_404(Tournament, pk=tournament_id) + return tv_teams_tournament(request, tournament.id) + +def tv_teams_club_name_tournament_name(request, club_name, tournament_shortname): + club = get_object_or_404(Club, name__iexact=club_name.lower()) + tournament = Tournament.objects.filter(club_id=club.id, shortname__iexact=tournament_shortname.lower()).first() + return tv_teams_tournament(request, tournament.id) + +def tv_teams_tournament(request, tournament_id): + + tournament = get_object_or_404(Tournament, pk=tournament_id) + match = Match.objects.filter(tournament=tournament.id).filter(match_index=0).first() + teams = Team.objects.filter(match=match.id).order_by('position') + template = loader.get_template('scores/match.html') + context = { + 'match': match, + 'teams': teams, + 'tv': True, + } + return HttpResponse(template.render(context, request)) class UserViewSet(viewsets.ModelViewSet): """ @@ -96,6 +200,14 @@ class MatchViewSet(viewsets.ModelViewSet): """ API endpoint that allows matches to be viewed or edited. """ - queryset = Match.objects.all().order_by('court') + queryset = Match.objects.all().order_by('match_index') serializer_class = MatchSerializer permission_classes = [permissions.IsAuthenticated] + +class TeamViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows teams to be viewed or edited. + """ + queryset = Team.objects.all().order_by('id') + serializer_class = TeamSerializer + permission_classes = [permissions.IsAuthenticated]