diff --git a/makefile b/makefile index 755c103..bded4b4 100644 --- a/makefile +++ b/makefile @@ -31,6 +31,8 @@ logs: ps: $(COMPOSE) ps +re: destroy up + help: @echo "Usage:" @echo " make build [c=service] # Build images" diff --git a/pong/game/migrations/0002_player_best_score_player_m_duration_and_more.py b/pong/game/migrations/0002_player_best_score_player_m_duration_and_more.py new file mode 100644 index 0000000..df55fa5 --- /dev/null +++ b/pong/game/migrations/0002_player_best_score_player_m_duration_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 5.0.7 on 2024-07-24 16:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('game', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='player', + name='best_score', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='player', + name='m_duration', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AddField( + model_name='player', + name='m_nbr_ball_touch', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AddField( + model_name='player', + name='m_score_adv_match', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AddField( + model_name='player', + name='m_score_match', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AddField( + model_name='player', + name='num_participated_tournaments', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='player', + name='num_won_tournaments', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='player', + name='p_win', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AddField( + model_name='player', + name='total_duration', + field=models.DurationField(blank=True, null=True), + ), + migrations.AddField( + model_name='player', + name='total_match', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='player', + name='total_win', + field=models.PositiveSmallIntegerField(default=0), + ), + ] diff --git a/pong/game/models.py b/pong/game/models.py index 6068904..483e955 100644 --- a/pong/game/models.py +++ b/pong/game/models.py @@ -8,6 +8,17 @@ User.add_to_class('auth_token', models.CharField(max_length=100, null=True, blan class Player(models.Model): name = models.CharField(max_length=100) + total_match = models.PositiveSmallIntegerField(default=0) + total_win = models.PositiveSmallIntegerField(default=0) + p_win = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + m_score_match = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + m_score_adv_match = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + best_score = models.PositiveSmallIntegerField(default=0) + m_nbr_ball_touch = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + total_duration = models.DurationField(null=True, blank=True) + m_duration = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + num_participated_tournaments = models.PositiveSmallIntegerField(default=0) + num_won_tournaments = models.PositiveSmallIntegerField(default=0) def __str__(self): return self.name diff --git a/pong/game/templates/pong/match_list.html b/pong/game/templates/pong/match_list.html new file mode 100644 index 0000000..72c1315 --- /dev/null +++ b/pong/game/templates/pong/match_list.html @@ -0,0 +1,51 @@ + + + + + + Matches List + + +

Matches List

+ + + + + + + + + + + + + + + + + + + {% for match in matches %} + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
IDPlayer 1Player 2Score Player 1Score Player 2WinnerBall Touches Player 1Ball Touches Player 2DurationDateIs TournamentTournament
{{ match.id }}{{ match.player1.name }}{{ match.player2.name }}{{ match.score_player1 }}{{ match.score_player2 }}{{ match.winner.name }}{{ match.nbr_ball_touch_p1 }}{{ match.nbr_ball_touch_p2 }}{{ match.duration }}{{ match.date }}{{ match.is_tournoi }}{{ match.tournoi.name }}
No matches found.
+ + diff --git a/pong/game/templates/pong/player_list.html b/pong/game/templates/pong/player_list.html new file mode 100644 index 0000000..b9fbb42 --- /dev/null +++ b/pong/game/templates/pong/player_list.html @@ -0,0 +1,53 @@ + + + + + + Players List + + +

Players List

+ + + + + + + + + + + + + + + + + + + + {% for player in players %} + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
IDNameTotal MatchesTotal WinsWin PercentageAverage Match ScoreAverage Opponent ScoreBest ScoreAverage Ball TouchesTotal DurationAverage DurationParticipated TournamentsWon Tournaments
{{ player.id }}{{ player.name }}{{ player.total_match }}{{ player.total_win }}{{ player.p_win }}{{ player.m_score_match }}{{ player.m_score_adv_match }}{{ player.best_score }}{{ player.m_nbr_ball_touch }}{{ player.total_duration }}{{ player.m_duration }}{{ player.num_participated_tournaments }}{{ player.num_won_tournaments }}
No players found.
+ + diff --git a/pong/game/templates/pong/tournoi_list.html b/pong/game/templates/pong/tournoi_list.html new file mode 100644 index 0000000..e5718f5 --- /dev/null +++ b/pong/game/templates/pong/tournoi_list.html @@ -0,0 +1,37 @@ + + + + + + Tournaments List + + +

Tournaments List

+ + + + + + + + + + + + {% for tournoi in tournois %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
IDNameNumber of PlayersDateWinner
{{ tournoi.id }}{{ tournoi.name }}{{ tournoi.nbr_player }}{{ tournoi.date }}{{ tournoi.winner.name }}
No tournaments found.
+ + diff --git a/pong/game/urls.py b/pong/game/urls.py index 96488b0..43f7305 100644 --- a/pong/game/urls.py +++ b/pong/game/urls.py @@ -2,10 +2,17 @@ from django.urls import path from . import views +from .views import player_list, tournoi_list, match_list urlpatterns = [ path('', views.index, name='index'), path('check_user_exists/', views.check_user_exists, name='check_user_exists'), path('register_user/', views.register_user, name='register_user'), path('authenticate_user/', views.authenticate_user, name='authenticate_user'), + path('create_player/', views.create_player_view, name='create_player'), + path('create_tournoi/', views.create_tournoi_view, name='create_tournoi'), + path('create_match/', views.create_match_view, name='create_match'), + path('players/', player_list, name='player_list'), + path('matches/', match_list, name='match_list'), + path('tournois/', tournoi_list, name='tournoi_list'), ] diff --git a/pong/game/utils.py b/pong/game/utils.py index c02c194..add9877 100644 --- a/pong/game/utils.py +++ b/pong/game/utils.py @@ -1,33 +1,69 @@ -from myapp.models import Player, Tournoi, Match +from .models import Player, Tournoi, Match from django.core.exceptions import ValidationError -def create_player(name): - player = Player(name=name) +def create_player( + name, + total_match=0, + total_win=0, + p_win= None, + m_score_match= None, + m_score_adv_match= None, + best_score=0, + m_nbr_ball_touch= None, + total_duration= None, + m_duration= None, + num_participated_tournaments=0, + num_won_tournaments=0 +): + if Player.objects.filter(name=name).exists(): + raise ValueError(f"A player with the name '{name}' already exists.") + + player = Player( + name=name, + total_match = total_match, + total_win = total_win, + p_win = p_win, + m_score_match = m_score_match, + m_score_adv_match = m_score_adv_match, + best_score = best_score, + m_nbr_ball_touch = m_nbr_ball_touch, + total_duration = total_duration, + m_duration = m_duration, + num_participated_tournaments = num_participated_tournaments, + num_won_tournaments = num_won_tournaments + ) player.save() return player -def create_tournoi(name, nbr_player, date, winner=None): +def create_tournoi(name, nbr_player, date, winner): tournoi = Tournoi(name=name, nbr_player=nbr_player, date=date, winner=winner) tournoi.save() return tournoi -def create_match(player1, player2, score_player1=0, score_player2=0, winner=None, nbr_ball_touch_p1=0, nbr_ball_touch_p2=0, duration=None, is_tournoi=False, tournoi=None): +def create_match(player1, player2, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration, is_tournoi, tournoi): match = Match( player1=player1, player2=player2, score_player1=score_player1, score_player2=score_player2, - winner=winner, nbr_ball_touch_p1=nbr_ball_touch_p1, nbr_ball_touch_p2=nbr_ball_touch_p2, duration=duration, is_tournoi=is_tournoi, tournoi=tournoi ) + + if score_player1 > score_player2: + match.winner = match.player1 + elif score_player2 > score_player1: + match.winner = match.player2 + else: + match.winner = None + match.save() return match -def complete_match(match_id, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration): +""" def complete_match(match_id, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration): try: match = Match.objects.get(id=match_id) except Match.DoesNotExist: @@ -47,9 +83,9 @@ def complete_match(match_id, score_player1, score_player2, nbr_ball_touch_p1, nb match.winner = None match.save() - return match + return match """ -def complete_tournoi(tournoi_id, player): +""" def complete_tournoi(tournoi_id, player): try: tournoi = Tournoi.objects.get(id = tournoi_id) except Tournoi.DoesNotExist: @@ -57,7 +93,7 @@ def complete_tournoi(tournoi_id, player): tournoi.winner = player tournoi.save() - return tournoi + return tournoi """ def player_statistics(request, player_name): player = get_object_or_404(Player, nam = player_name) @@ -92,15 +128,15 @@ def player_statistics(request, player_name): data = { 'player_name': player.name, - 'number of match played' : total_match - 'number of win (matches)' : total_win - 'pourcentage of victory' : p_win - 'mean score per match' : m_score_match - 'mean score adv per match' : m_score_adv_match - 'best score' : best_score - 'mean nbr ball touch' : m_nbr_ball_touch - 'total duration played' : total_duration - 'mean duration per match' : m_duration + 'number of match played' : total_match, + 'number of win (matches)' : total_win, + 'pourcentage of victory' : p_win, + 'mean score per match' : m_score_match, + 'mean score adv per match' : m_score_adv_match, + 'best score' : best_score, + 'mean nbr ball touch' : m_nbr_ball_touch, + 'total duration played' : total_duration, + 'mean duration per match' : m_duration, 'num_participated_tournaments': num_participated_tournaments, 'num_won_tournaments': num_won_tournaments } diff --git a/pong/game/views.py b/pong/game/views.py index 279a317..298fa5b 100644 --- a/pong/game/views.py +++ b/pong/game/views.py @@ -2,9 +2,9 @@ from django.shortcuts import render -def index(request): - return render(request, 'index.html') - +from django.core.exceptions import ObjectDoesNotExist +from .models import Player, Tournoi, Match +from .utils import create_player, create_tournoi, create_match from django.http import JsonResponse from django.contrib.auth.models import User from django.contrib.auth import authenticate @@ -12,6 +12,11 @@ from django.views.decorators.csrf import csrf_exempt import json import uuid + +def index(request): + return render(request, 'index.html') + + @csrf_exempt def register_user(request): if request.method == 'POST': @@ -62,3 +67,138 @@ def get_or_create_token(user): user.save() break return user.auth_token + + +####################### THEOUCHE PART ############################ + + +@csrf_exempt +def create_player_view(request): + if request.method == 'POST': + try: + data = json.loads(request.body) + name = data.get('name') + + # Vérifier que le nom est présent + if not name: + return JsonResponse({'error': 'Name is required'}, status=400) + + # Appeler la fonction create_player et traiter les exceptions + player = create_player( + name=name, + total_match=data.get('total_match', 0), + total_win=data.get('total_win', 0), + p_win=data.get('p_win'), + m_score_match=data.get('m_score_match'), + m_score_adv_match=data.get('m_score_adv_match'), + best_score=data.get('best_score', 0), + m_nbr_ball_touch=data.get('m_nbr_ball_touch'), + total_duration=data.get('total_duration'), + m_duration=data.get('m_duration'), + num_participated_tournaments=data.get('num_participated_tournaments', 0), + num_won_tournaments=data.get('num_won_tournaments', 0) + ) + return JsonResponse({'id': player.id, 'name': player.name}) + + except ValueError as e: + # Erreur spécifique à la validation + return JsonResponse({'error': str(e)}, status=400) + except json.JSONDecodeError: + # Erreur de décodage JSON + return JsonResponse({'error': 'Invalid JSON format'}, status=400) + except Exception as e: + # Erreur générale + return JsonResponse({'error': 'An unexpected error occurred', 'details': str(e)}, status=500) + + # Méthode HTTP non supportée + return JsonResponse({'error': 'Invalid HTTP method'}, status=405) + + + +@csrf_exempt +def create_tournoi_view(request): + if request.method == 'POST': + data = json.loads(request.body) + name = data.get('name') + nbr_player = data.get('nbr_player') + date = data.get('date') + winner_id = data.get('winner_id') + if not (name and nbr_player and date): + return JsonResponse({'error': 'Name, number of players, and date are required'}, status=400) + + winner = None + if winner_id: + try: + winner = Player.objects.get(id=winner_id) + except Player.DoesNotExist: + return JsonResponse({'error': 'Winner not found'}, status=404) + + tournoi = create_tournoi(name, nbr_player, date, winner) + return JsonResponse({ + 'id': tournoi.id, + 'name': tournoi.name, + 'nbr_player': tournoi.nbr_player, + 'date': tournoi.date.isoformat(), + 'winner': winner.id if winner else None + }) + return JsonResponse({'error': 'Invalid HTTP method'}, status=405) + +@csrf_exempt +def create_match_view(request): + if request.method == 'POST': + data = json.loads(request.body) + player1_id = data.get('player1_id') + player2_id = data.get('player2_id') + score_player1 = data.get('score_player1', 0) + score_player2 = data.get('score_player2', 0) + nbr_ball_touch_p1 = data.get('nbr_ball_touch_p1', 0) + nbr_ball_touch_p2 = data.get('nbr_ball_touch_p2', 0) + duration = data.get('duration') + is_tournoi = data.get('is_tournoi', False) + tournoi_id = data.get('tournoi_id') + + if not (player1_id and player2_id and duration): + return JsonResponse({'error': 'Player IDs and duration are required'}, status=400) + + try: + player1 = Player.objects.get(id=player1_id) + player2 = Player.objects.get(id=player2_id) + except Player.DoesNotExist: + return JsonResponse({'error': 'One or both players not found'}, status=404) + + tournoi = None + if tournoi_id: + try: + tournoi = Tournoi.objects.get(id=tournoi_id) + except Tournoi.DoesNotExist: + return JsonResponse({'error': 'Tournoi not found'}, status=404) + + match = create_match(player1, player2, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration, is_tournoi, tournoi) + return JsonResponse({ + 'id': match.id, + 'player1': match.player1.id, + 'player2': match.player2.id, + 'score_player1': match.score_player1, + 'score_player2': match.score_player2, + 'nbr_ball_touch_p1': match.nbr_ball_touch_p1, + 'nbr_ball_touch_p2': match.nbr_ball_touch_p2, + 'duration': str(match.duration), + 'is_tournoi': match.is_tournoi, + 'tournoi': match.tournoi.id if match.tournoi else None + }) + return JsonResponse({'error': 'Invalid HTTP method'}, status=405) + + +def player_list(request): + players = Player.objects.all() + return render(request, 'pong/player_list.html', {'players': players}) + +def match_list(request): + matches = Match.objects.select_related('player1', 'player2', 'winner', 'tournoi').all() + return render(request, 'pong/match_list.html', {'matches': matches}) + +def tournoi_list(request): + tournois = Tournoi.objects.select_related('winner').all() + return render(request, 'pong/tournoi_list.html', {'tournois': tournois}) + +####################### THEOUCHE PART ############################ diff --git a/pong/static/game.js b/pong/static/game.js index 4c985aa..2287657 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -19,6 +19,86 @@ document.addEventListener('DOMContentLoaded', () => { registerButton.addEventListener('click', handleRegister); loginButton.addEventListener('click', handleLogin); + + /// THEOUCHE NOT CERTAIN /// + async function createPlayer(name, totalMatch = 0, totalWin = 0, pWin = null, mScoreMatch = null, mScoreAdvMatch = null, bestScore = 0, mNbrBallTouch = null, totalDuration = null, mDuration = null, numParticipatedTournaments = 0, numWonTournaments = 0) { + try { + const response = await fetch('/api/create_player/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name, + total_match: totalMatch, + total_win: totalWin, + p_win: pWin, + m_score_match: mScoreMatch, + m_score_adv_match: mScoreAdvMatch, + best_score: bestScore, + m_nbr_ball_touch: mNbrBallTouch, + total_duration: totalDuration, + m_duration: mDuration, + num_participated_tournaments: numParticipatedTournaments, + num_won_tournaments: numWonTournaments + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || 'Network response was not ok'); + } + + const data = await response.json(); + return data; + + } catch (error) { + // Afficher l'erreur avec un message plus spécifique + console.error('Error creating player:', error.message); + alert(`Failed to create player: ${error.message}`); + } + } + + async function createTournoi(name, nbr_player, date, winner_id) { + try { + const response = await fetch('/api/create_tournoi/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ name, nbr_player, date, winner_id }) + }); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const data = await response.json(); + return data; + } catch (error) { + console.error('Error creating tournoi:', error); + } + } + + async function createMatch(player1_id, player2_id, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration, is_tournoi, tournoi_id) { + try { + const response = await fetch('/api/create_match/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ player1_id, player2_id, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration, is_tournoi, tournoi_id }) + }); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const data = await response.json(); + return data; + } catch (error) { + console.error('Error creating match:', error); + } + } + + /// THEOUCHE NOT CERTAIN /// + async function handleCheckNickname() { const nickname = nicknameInput.value.trim(); if (nickname) { @@ -161,6 +241,7 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('player1-name').textContent = `${player1_name}`; document.getElementById('player2-name').textContent = `${player2_name}`; document.addEventListener('keydown', handleKeyDown); + } function handleKeyDown(event) { diff --git a/pong/static/index.html b/pong/static/index.html index 1aad5b6..5f8b5a9 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -26,7 +26,7 @@