Compare commits

...

6 Commits

  1. 13
      padelclub_backend/settings.py
  2. 13
      padelclub_backend/settings_app.py
  3. 3
      padelclub_backend/urls.py
  4. 19
      tournaments/migrations/0011_alter_teamstate_score.py
  5. 18
      tournaments/migrations/0012_alter_teamstate_score.py
  6. 19
      tournaments/migrations/0013_teamstate_team_registration.py
  7. 19
      tournaments/migrations/0014_alter_teamstate_team_registration.py
  8. 95
      tournaments/models.py
  9. 1
      tournaments/serializers.py
  10. 33
      tournaments/templates/tournaments/match_cell.html
  11. 26
      tournaments/templates/tournaments/matches.html
  12. 142
      tournaments/templates/tournaments/tournament_stream.html
  13. 16
      tournaments/urls.py
  14. 44
      tournaments/views.py

@ -106,18 +106,6 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
],'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
'rest_framework.permissions.IsAdminUser',
]
}
# Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/
@ -155,3 +143,4 @@ REST_FRAMEWORK = {
}
from .settings_local import *
from .settings_app import *

@ -0,0 +1,13 @@
# Rest Framework configuration
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticated',
# ],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication',
]
}

@ -13,6 +13,7 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from posixpath import basename
from django.contrib import admin
from django.urls import include, path
from rest_framework import routers
@ -29,7 +30,7 @@ router.register(r'matches', views.MatchViewSet)
router.register(r'team-states', views.TeamStateViewSet)
router.register(r'team-registrations', views.TeamRegistrationViewSet)
router.register(r'player-registrations', views.PlayerRegistrationViewSet)
router.register(r'exp-tournaments', views.ExpandedTournamentViewSet)
router.register(r'exp-tournaments', views.ExpandedTournamentViewSet, basename='tournaments-json')
urlpatterns = [
path('api/', include(router.urls)),

@ -0,0 +1,19 @@
# Generated by Django 4.2.11 on 2024-03-08 11:09
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0010_alter_groupstage_tournament_alter_round_tournament'),
]
operations = [
migrations.AlterField(
model_name='teamstate',
name='score',
field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), blank=True, null=True, size=None),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-03-08 11:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0011_alter_teamstate_score'),
]
operations = [
migrations.AlterField(
model_name='teamstate',
name='score',
field=models.CharField(blank=True, max_length=50, null=True),
),
]

@ -0,0 +1,19 @@
# Generated by Django 4.2.11 on 2024-03-08 13:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0012_alter_teamstate_score'),
]
operations = [
migrations.AddField(
model_name='teamstate',
name='team_registration',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tournaments.match'),
),
]

@ -0,0 +1,19 @@
# Generated by Django 4.2.11 on 2024-03-08 13:42
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0013_teamstate_team_registration'),
]
operations = [
migrations.AlterField(
model_name='teamstate',
name='team_registration',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tournaments.teamregistration'),
),
]

@ -4,6 +4,7 @@ from django.contrib.auth.models import AbstractUser
from django.conf import settings
import uuid
import os
from django.utils import timezone
from django.utils.encoding import Promise
@ -254,6 +255,64 @@ class Match(models.Model):
return "no date"
# return str(self.start_date) #.strftime("%H:%M")
def current_duration(self):
if self.end_date:
return (self.end_date - self.start_date).total_seconds()
else:
return (timezone.now() - self.start_date).total_seconds()
def durationPrefix(self):
if self.current_duration() > 0:
return "Temps de jeu"
else:
return "Démarrage prévu dans"
def formatted_duration(self):
_seconds = self.current_duration()
if _seconds > 0:
_hours = int(_seconds / 3600)
_minutes = int((_seconds % 3600) / 60)
return f"{_hours:02d}h{_minutes:02d}min"
else :
_seconds = _seconds * -1
_hours = int(_seconds / 3600)
_minutes = int((_seconds % 3600) / 60)
return f"{_hours:02d}h{_minutes:02d}min"
# def seconds(self):
# return (timezone.now() - self.start_date).total_seconds()
def live_match(self):
title = f"{self.index}"
date = self.formatted_start_date()
duration = self.formatted_duration()
livematch = LiveMatch(title, date, duration)
for team_state in self.team_states:
image = team_state.team_registration.logo
names = team_state.team_names()
scores = team_state.score
weight = team_state.weight
is_winner = False
team = Team(image, names, scores, weight, is_winner)
break
# def __init__(self, image, names, scores, weight, is_winner):
# def __init__(self, title, date, teams, duration):
class TeamRegistration(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
@ -322,6 +381,7 @@ class TeamState(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name="team_states")
player_registrations = models.ManyToManyField(PlayerRegistration, blank=True)
team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, null=True, blank=True)
score = models.CharField(max_length=50, null=True, blank=True)
walk_out = models.IntegerField(null=True, blank=True) #id, int of the walked_out team
lucky_loser = models.BooleanField()
@ -333,13 +393,36 @@ class TeamState(models.Model):
names = map(lambda player: player.name(), self.player_registrations.all())
return " - ".join(names)
# class StageCall:
# def __init__(self, stage):
# self.stage = stage
# self.team_calls = []
def team_names(self):
names = []
if self.team_registration.name:
names.append(self.team_registration.name)
else:
names = self.player_names()
class Team:
def __init__(self, image, names, scores, weight, is_winner):
self.image = image
self.names = []
self.scores = []
self.weight = weight
self.is_winner = is_winner
def add_names(self, name):
self.names.append(name)
def add_set_score(self, score):
self.scores.append(score)
class LiveMatch:
def __init__(self, title, date, duration):
self.title = title
self.date = date
self.teams = []
self.duration = duration
# def add_team(self, team_call):
# self.team_calls.append(team_call)
def add_team(self, team):
self.teams.append(team)
class TeamCall:
def __init__(self, names, date, weight, stage, image):

@ -138,6 +138,7 @@ class ExpandedGroupStageSerializer(serializers.HyperlinkedModelSerializer):
class ExpandedTeamRegistrationSerializer(serializers.HyperlinkedModelSerializer):
playerregistration_set = PlayerRegistrationSerializer(many=True, read_only=True)
call_date = serializers.DateTimeField(format='%H:%M')
class Meta:
# match_id = serializers.PrimaryKeyRelatedField(queryset=Match.objects.all())
group_stage_id = serializers.PrimaryKeyRelatedField(queryset=GroupStage.objects.all())

@ -0,0 +1,33 @@
<div class="bubble">
<div class="flex-row">
<label class="left-label matchtitle">{{ match.title }}</label>
<label class="right-label info">{{ match.formatted_start_date }}</label>
</div>
<div>
{% for team in match.teams %}
<div class="test bottom-border padding-bottom-small">
<div class="left-label">
{% for name in team.names %}
<div class="winner">
{{ name }}
</div>
{% endfor %}
</div>
<div class="">
{% for score in team.scores %}
<span class="score ws {% if team.is_winner %}winner{% endif %}">{{ score }}</span>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="top-margin flex-row">
<label class="left-label minor-info">{{ match.duration }}</label>
<!-- <a href="" class="right-label">détails</a> -->
</div>
</div>

@ -0,0 +1,26 @@
{% extends 'tournaments/base.html' %}
{% block head_title %}Matchs{% endblock %}
{% block title %}Matchs{% endblock %}
{% block content %}
{% if matches %}
<div class="grid-x">
<div class="cell medium-6 large-6 topblock my-block">
{% if matches %}
{% for match in matches %}
{% include 'tournaments/match_cell.html' %}
{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

@ -0,0 +1,142 @@
{% load static %}
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="{% static 'tournaments/css/foundation.min.css' %}" />
<link rel="stylesheet" href="{% static 'tournaments/css/style.css' %}" />
<link rel="icon" type="image/png" href="{% static 'tournaments/images/favicon.png' %}" />
<title>Padel</title>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<!--
TODO: utiliser le nom de l'URL pour le fetch {% url "monurl" tournament.pk %}?format=json
-->
<body x-data="{
tournament: null,
active: 1,
screens: 2,
retrieveTournament() {
fetch('/api/exp-tournaments/{{ tournament.pk }}/?format=json')
.then(res => res.json())
.then((data) => {
this.tournament = data
})
},
loop() {
this.retrieveTournament()
setInterval(() => {
this.retrieveTournament()
this.active = this.active === this.screens ? 1 : this.active+1
}, 3000)
}
}" x-init="loop()">
<div class="wrapper">
<main class="page-body">
<div class="container">
<div class="grid-x">
<div class="cell medium-6 large-6 topblock my-block">
<div class="bubble">
<img
src="{% static 'tournaments/images/PadelClub_logo_512.png' %}"
class="logo inline"
/>
<div class="inline">
<h1 class="club">4Padel Toulouse</h1>
<h1 class="event">Planning</h1>
<!-- <span>Propulsé par Padel Club</span> -->
</div>
</div>
</div>
</div>
<template x-if="tournament">
<div class="grid-x padding-bottom">
<div class="cell medium-6 large-6 topblock my-block">
<div class="bubble" x-show="active === 1">
<label class="title">Équipes</label>
<template x-for="team in tournament.teamregistration_set" >
<div class="table-container bottom-border vertical-padding">
<img src="{% static 'tournaments/images/pc_icon_round_200.png' %}" class="team_image horizontal-margin">
<div class="w50 tight table-cell hpadding10">
<template x-for="player in team.playerregistration_set">
<div>
<div x-text="player.first_name"></div> <div x-text="player.last_name"></div>
</div>
</template>
</div>
<div class="table-cell horizontal-padding">
<span x-text="team.court"></span>
</div>
<div class="table-cell horizontal-padding large">
<span x-text="team.call_date"></span>
</div>
<div class="table-cell"><div class="mybox">Poule A</div></div>
</div>
</template>
</div>
<div class="bubble" x-show="active === 2">
<label class="title">Matchs</label>
<template x-for="round in tournament.round_set">
<template x-for="match in round.match_set">
<div>
<div class="flex-row">
<label class="left-label matchtitle">{{ match.id }}</label>
<label class="right-label info">{{ match.start_date }}</label>
</div>
<div>
{% for team in match.team_states %}
<div class="test bottom-border padding-bottom-small">
<div class="left-label">
{% for name in team.names %}
<div class="winner">
{{ name }}
</div>
{% endfor %}
</div>
<div class="">
{% for score in team.scores %}
<span class="score ws {% if team.is_winner %}winner{% endif %}">{{ score }}</span>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="top-margin flex-row">
<label class="left-label minor-info">{{ match.duration }}</label>
<!-- <a href="" class="right-label">détails</a> -->
</div>
</div>
</template>
</template>
</div>
</div>
</div>
</template>
</div>
</main>
</div>
</body>
</html>

@ -1,9 +1,19 @@
from django.urls import path
from django.urls import include, path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path('tournament/<str:tournament_id>/', views.tournament, name='tournament'),
path('tournament/<str:tournament_id>/planning/', views.tournament_planning, name='tournament-planning'),
# Tournament
path("tournament/<str:tournament_id>/",
include(
[
path("", views.tournament, name="tournament"),
path("planning/", views.tournament_planning, name="tournament-planning"),
path("stream/", views.tournament_stream, name="tournament-stream"),
]
)
)
]

@ -1,5 +1,6 @@
#coding:utf-8
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .serializers import ClubSerializer, TournamentSerializer, ExpandedTournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamStateSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer
from .models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamState, TeamRegistration, PlayerRegistration
from .models import TeamCall
@ -10,21 +11,20 @@ from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from rest_framework.generics import UpdateAPIView
from django.template import loader
from datetime import date
# TODO: 1 app core (avec les models), 1 app web, 1 app API pour séparer les views
##
# Web UI
##
def index(request):
today = date.today()
future_tournaments = Tournament.objects.filter(end_date__isnull=True, start_date__gt=today).order_by('start_date')
live_tournaments = Tournament.objects.filter(end_date__isnull=True, start_date__lte=today).order_by('start_date')
ended_tournaments = Tournament.objects.filter(end_date__isnull=False).order_by('start_date')
# template = loader.get_template('tournaments/tournaments.html')
# context = {
# 'future': future_tournaments,
# 'live': live_tournaments,
# 'ended': ended_tournaments,
# }
# return HttpResponse(template.render(context, request))
return render(
request,
"tournaments/tournaments.html",
@ -38,31 +38,31 @@ def index(request):
def tournament(request, tournament_id):
tournament = get_object_or_404(Tournament, pk=tournament_id)
today = date.today()
future_matches = Match.objects.filter(end_date__isnull=True, start_date__gt=today).order_by('start_date')
live_matches = Match.objects.filter(end_date__isnull=True, start_date__lte=today).order_by('start_date')
ended_matches = Match.objects.filter(end_date__isnull=False).order_by('start_date')
template = loader.get_template('tournaments/tournament.html')
context = {
'future': future_matches,
'live': live_matches,
'ended': ended_matches,
}
return HttpResponse(template.render(context, request))
return render(request, "tournaments/tournament.html", context)
def tournament_planning(request, tournament_id):
def tournament_planning(request, tournament_id):
tournament = get_object_or_404(Tournament, pk=tournament_id)
team_calls = tournament.team_calls()
context = {'team_calls': team_calls}
return render(request, "tournaments/planning.html", context)
template = loader.get_template('tournaments/planning.html')
context = {
'team_calls': team_calls,
}
return HttpResponse(template.render(context, request))
def tournament_stream(request, tournament_id):
tournament = get_object_or_404(Tournament, pk=tournament_id)
return render(request, "tournaments/tournament_stream.html", {
"tournament": tournament,
})
# def index(request):
@ -77,6 +77,11 @@ def tournament_planning(request, tournament_id):
# }
# return HttpResponse(template.render(context, request))
##
# API
##
@api_view(['GET'])
def user_by_token(request):
# return Response({"message": "Hello for today! See you tomorrow!"})
@ -143,3 +148,4 @@ class TeamRegistrationViewSet(viewsets.ModelViewSet):
class PlayerRegistrationViewSet(viewsets.ModelViewSet):
queryset = PlayerRegistration.objects.all()
serializer_class = PlayerRegistrationSerializer

Loading…
Cancel
Save