diff --git a/frontend/static/js/api/Client.js b/frontend/static/js/api/Client.js index 6476084..4166ec4 100644 --- a/frontend/static/js/api/Client.js +++ b/frontend/static/js/api/Client.js @@ -3,7 +3,6 @@ import { MatchMaking } from "./Matchmaking.js"; import { Profiles } from "./Profiles.js"; import { Channels } from './chat/Channels.js'; import { MyProfile } from "./MyProfile.js"; -import { Tourmanents } from "./tournament/Tournaments.js"; import { Channel } from "./chat/Channel.js"; import Notice from "./Notice.js"; import LanguageManager from './LanguageManager.js'; @@ -46,11 +45,6 @@ class Client */ this.matchmaking = new MatchMaking(this); - /** - * @type {Tourmanents} - */ - this.tournaments = new Tourmanents(this); - /** * @type {Boolean} A private var represent if the is is log NEVER USE IT use await isAuthenticated() */ diff --git a/frontend/static/js/api/tournament/Tournament.js b/frontend/static/js/api/tournament/Tournament.js deleted file mode 100644 index 848e4cc..0000000 --- a/frontend/static/js/api/tournament/Tournament.js +++ /dev/null @@ -1,201 +0,0 @@ -import { AExchangeable } from "../AExchangable.js"; -import { Client } from "../Client.js"; -import { Profile } from "../Profile.js"; - -class Tourmanent extends AExchangeable -{ - /** - * - * @param {Client} client - * @param {Number} id the id of the tournament - */ - constructor(client, id) - { - super(); - - /** - * @type {Number} - */ - this.id = id; - - /** - * @type {Client} - */ - this.client = client; - - /** - * @type {Number} - */ - this.nb_participants; - - /** - * @type {[Profile]} proutman à encore frappé - */ - this.participantList = [] - - /** - * @type {Boolean} - */ - this.started; - - /** - * @type {Number} - */ - this.finished; - - /** - * @type {"finished" | "started" | "waiting"} must be "finished", or "started", or "waiting". Any other return all elements - */ - this.state; - - /** - * @type {Boolean} the client is a participant of the tournament - */ - this.is_participating; - } - - /** - * @param {Boolean} newParticipation - */ - async setParticipation(newParticipation) - { - if (this.isParticipating == newParticipation) - return; - - this.isParticipating = newParticipation; - - this._socket.send(JSON.stringify({"detail": "update_participating", - "is_participating": newParticipation}) - ); - - } - /** - * - * @returns {Promise} - */ - async init() - { - let response = await this.client._get(`/api/tournaments/${this.id}`); - - if (response.status !== 200) - return response.status; - - let response_data = await response.json(); - - this.import(response_data); - } - - leave(event) - { - if (this.connected == false) - return; - this.connected = false; - this._socket.close(); - if (this.disconnectHandler != null) - this.disconnectHandler(event); - } - - /** - * @param {Object} data - */ - async _receiveAddParticipant(data) - { - const participant = new Profile(this.client, undefined, data.participant.user_id); - participant.import(data.participant) - - this.participantList.push(participant); - - await this._addParticipantHandler(this.participantList.length) - } - - /** - * @param {Object} data - */ - async _receiveDelParticipant(data) - { - const index = this.participantList.indexOf((profile) => profile.id === data.profile.user_id) - - this.participantList.splice(index, 1); - - await this._delParticipantHandler(this.participantList.length); - } - - async _receiveError(data) - { - await this.errorHandler(data); - } - - async _receiveGoTo(data) - { - await this._goToHandler(data) - } - - /** - * - * @param {MessageEvent} event - */ - async onReceive(event) - { - const data = JSON.parse(event.data); - - switch (data.detail) { - case "error": - await this._receiveError(data) - break; - - case "add_participant": - await this._receiveAddParticipant(data); - break; - - case "del_participant": - await this._receiveDelParticipant(data); - break; - - case "go_to": - await this._receiveGoTo(data); - break - - default: - break; - } - } - - /** - * Join the tournament Websocket - * @param {CallableFunction} errorHandler - * @param {CallableFunction} addParticipantHandler called when a participants join the tournament - * @param {CallableFunction} delParticipantHandler called when a participants leave the tournament - * @param {CallableFunction} disconnectHandler - * @param {CallableFunction} goToHandler called when the next game will start - * @param {CallableFunction} startHandler called when tournament start - * @param {CallableFunction} finishHandler called when tournament finish - * @returns {Promise} - */ - async join(addParticipantHandler, delParticipantHandler, startHandler, finishHandler, errorHandler, goToHandler, disconnectHandler) - { - if (!await this.client.isAuthenticated()) - return null; - - let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/tournaments/${this.id}`; - - this._socket = new WebSocket(url); - - this.connected = true; - this.isParticipating = false; - - this._startHandler = startHandler; - this._finishHandler = finishHandler; - this._addParticipantHandler = addParticipantHandler; - this._delParticipantHandler = delParticipantHandler; - this._errorHandler = errorHandler; - this._disconnectHandler = disconnectHandler; - this._goToHandler = goToHandler; - - this._socket.onmessage = this.onReceive.bind(this); - - this._socket.onclose = this.leave.bind(this); - } - -} - -export { Tourmanent }; diff --git a/frontend/static/js/api/tournament/Tournaments.js b/frontend/static/js/api/tournament/Tournaments.js deleted file mode 100644 index 688cc82..0000000 --- a/frontend/static/js/api/tournament/Tournaments.js +++ /dev/null @@ -1,80 +0,0 @@ -import { Client } from "../Client.js"; -import { Tourmanent } from "./Tournament.js"; - -class Tourmanents -{ - /** - * @param {Client} client - */ - constructor(client) - { - /** - * @type {Client} - */ - this.client = client; - } - - /** - * - * @param {Number} id - * @returns {Promise} - */ - async getTournament(id) - { - let tournament = new Tourmanent(this.client, id); - if (await tournament.init()) - return null; - return tournament; - } - - /** - * - * @param {Number} nb_participants - * @param {String} name - * @returns {Response} - */ - async createTournament(nb_participants, name = "") - { - let response = await this.client._post("/api/tournaments/", {nb_participants: nb_participants, name: name}); - - return response; - } - - /** - * @param {String} state must be "finished", or "started", or "waiting". Any other return all elements - * @returns {?Promise<[Tourmanent]>} - */ - async search(state) - { - let response = await this.client._get(`/api/tournaments/search/${state}`); - let response_data = await response.json(); - - if (response.status === 403) - { - this.client._update_logged(false); - return null; - } - - let tournaments = [];`` - - response_data.forEach(tournament_data => { - let tournament = new Tourmanent(this.client, tournament_data.id); - tournament.import(tournament_data); - tournaments.push(tournament); - }); - - return tournaments; - } - - /** - * Get all tournaments - * @returns {?Promise<[Tourmanent]>} - */ - async all() - { - return await this.search(""); - } - -} - -export { Tourmanents }; \ No newline at end of file diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index bea273b..97e325c 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -16,9 +16,6 @@ import AbstractRedirectView from "./views/abstracts/AbstractRedirectView.js"; import SettingsView from "./views/SettingsView.js"; import ProfilePageView from "./views/ProfilePageView.js"; import MatchMakingView from "./views/MatchMakingView.js"; -import TournamentPageView from "./views/tournament/TournamentPageView.js"; -import TournamentsListView from "./views/tournament/TournamentsListView.js"; -import TournamentCreateView from "./views/tournament/TournamentCreateView.js"; import AuthenticationView from "./views/accounts/AuthenticationView.js"; let client = new Client(location.origin); @@ -30,9 +27,9 @@ let lastPageUrlBeforeLogin; const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$"); const getParams = match => { + const values = match.result.slice(1); const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(result => result[1]); - return Object.fromEntries(keys.map((key, i) => { return [key, values[i]]; })); @@ -79,9 +76,6 @@ const router = async(uri) => { const routes = [ { path: "/", view: HomeView}, { path: "/profiles/:username", view: ProfilePageView }, - { path: "/tournaments/create", view: TournamentCreateView }, - { path: "/tournaments/:id", view: TournamentPageView }, - { path: "/tournaments/", view: TournamentsListView }, { path: "/login", view: AuthenticationView }, { path: "/register", view: AuthenticationView }, { path: "/logout", view: LogoutView }, diff --git a/frontend/static/js/views/tournament/TournamentCreateView.js b/frontend/static/js/views/tournament/TournamentCreateView.js deleted file mode 100644 index 24752f1..0000000 --- a/frontend/static/js/views/tournament/TournamentCreateView.js +++ /dev/null @@ -1,71 +0,0 @@ -import {client, lang, navigateTo} from "../../index.js"; -import { clearIds, fill_errors } from "../../utils/formUtils.js"; -import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js"; - -export default class extends AbstractAuthenticatedView -{ - constructor(params) - { - super(params, "Create tournament"); - this.id = params.id; - } - - async create() - { - let name = document.getElementById("name-input").value; - if (name.length == 0) - name = lang.get("TournamentCreateTournamentName"); - - let nb_participant = document.getElementById("nb-participant-input").value; - - let response = await client.tournaments.createTournament(nb_participant, name); - let response_data = await response.json(); - - let id = response_data.id; - if (id !== undefined) - { - navigateTo(`/tournaments/${id}`); - return; - } - - clearIds("innerHTML", ["name", "nb_participants"]); - fill_errors(response_data, "innerHTML"); - } - - async postInit() - { - document.getElementById("create-button").onclick = this.create; - } - - async getHtml() { - - return /* HTML */ ` -
-
-

${lang.get("TournamentCreateTitle")}

-
- - - -
-
- - - -
-
- - -
-
-
- `; - } -} diff --git a/frontend/static/js/views/tournament/TournamentPageView.js b/frontend/static/js/views/tournament/TournamentPageView.js deleted file mode 100644 index 6f50d46..0000000 --- a/frontend/static/js/views/tournament/TournamentPageView.js +++ /dev/null @@ -1,173 +0,0 @@ -import { Profile } from "../../api/Profile.js"; -import { Tourmanent } from "../../api/tournament/Tournament.js"; -import {client, navigateTo} from "../../index.js"; -import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js"; - -const TEXT_CONVENTION = { - "error": "[ERROR]" -} - -export default class extends AbstractAuthenticatedView -{ - constructor(params) - { - super(params, "Tournament"); - this.id = params.id; - } - - pressButton() - { - this.tournament.setParticipation(!this.tournament.isParticipating); - this.updateParticipating() - } - - updateParticipating() - { - document.getElementById("button").value = this.tournament.isParticipating ? `Leave ${this.tournament.name}` : `Join ${this.tournament.name}`; - document.getElementById("display").innerText = this.tournament.isParticipating ? "You are a particpant" : "You are not a participant"; - } - - async onDisconnect(event) - { - - } - - async onError(data) - { - this.addChatMessage(`${TEXT_CONVENTION} data.error_message`); - } - - async onFinish() - { - document.getElementById("state").innerText = "finished" - } - - async onStart() - { - document.getElementById("state").innerText = "started" - } - - async onGoTo(data) - { - await navigateTo(`/games/pong/${data.game_id}`) - } - - async onAddParticipant(nb_participants) - { - document.getElementById("nb_participants").innerText = nb_participants; - } - - async onDelParticipant(nb_participants) - { - document.getElementById("nb_participants").innerText = nb_participants; - } - - async postInit() - { - /** - * @type {Tourmanent} - */ - this.tournament = await client.tournaments.getTournament(this.id); - - if (this.tournament === null) - return 404; - - this.tournament.join(this.onAddParticipant, this.onDelParticipant, this.onStart, this.onFinish, this.onError, this.onGoTo, this.onDisconnect); - - let button = document.getElementById("button"); - - button.onclick = this.pressButton.bind(this); - - document.getElementById("name").innerText = this.tournament.name; - document.getElementById("nb_participants").innerText = this.tournament.participantList.length; - document.getElementById("expected_nb_participants").innerText = this.tournament.nb_participants; - document.getElementById("round").innerText = this.tournament.round; - document.getElementById("state").innerText = this.tournament.state; - - if (this.tournament.started === false) - button.disabled = false; - - this.chat = document.getElementById("chat"); - - console.log(this.tournament); - this.display_tree_tournament(this.tournament.nb_participants, this.tournament.participantList); - } - - addChatMessage(message) - { - this.chat.innerText += message; - } - - async display_tree_tournament(nb_participants, participants) { - const svg = document.getElementById('tree'); - - svg.innerHTML = ''; - - let width = 100; - let height = 25; - let gap_y = height + 25; - let gap_x = width + 25; - let start_y = height / 2; - - let rounds = Math.log2(nb_participants) + 1; - - for (let i = 0; i < rounds; i++) { - let number_square = nb_participants / Math.pow(2, i) - for(let j = 0; j < number_square; j++) { - const y = start_y + gap_y * j * Math.pow(2, i) + (gap_y / 2 * Math.pow(2, i)); - svg.appendChild(await this.create_square(gap_x * i, y, width, height, "white", "black")); - } - } - - } - - async create_square(x, y, width, height, fill, stroke) { - const square = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - square.setAttribute("id", square) - square.setAttribute("x", x); - square.setAttribute("y", y); - square.setAttribute("width", width); - square.setAttribute("height", height); - square.setAttribute("fill", fill); - square.setAttribute("stroke", stroke); - return square - } - - async getHtml() - { - return ` - - - - - - - - - - - - - - - - - - - - - - - - - -
Loading...
Number of roundLoading...
Number of participantsLoading...
Expected number of participantsLoading...
stateLoading...
- - -
- - - `; - } -} diff --git a/frontend/static/js/views/tournament/TournamentsListView.js b/frontend/static/js/views/tournament/TournamentsListView.js deleted file mode 100644 index 8f6fa74..0000000 --- a/frontend/static/js/views/tournament/TournamentsListView.js +++ /dev/null @@ -1,103 +0,0 @@ -import {client, navigateTo} from "../../index.js"; -import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js"; - -export default class extends AbstractAuthenticatedView -{ - constructor(params) - { - super(params, "Tournament"); - this.id = params.id; - } - - async external_search() - { - let state = document.getElementById("state-select").value; - this.tournaments = await client.tournaments.search(state); - //console.log(this.tournaments); - } - - internal_search() - { - - this.display_tournaments = []; - this.tournaments.forEach(tournament => { - this.display_tournaments.push(tournament); - }); - } - - display_result() - { - const tournaments_list = document.getElementById("tournaments-list"); - - const new_children = []; - - console.log(this.display_tournaments); - this.display_tournaments.forEach(tournament => { - - let tr = document.createElement("tr"); - - // name - let td = document.createElement("td"); - let button_tournament = document.createElement("a"); - button_tournament.innerText = tournament.name; - button_tournament.onclick = async () => { - await navigateTo(String(tournament.id)); - } - button_tournament.href = ""; - td.appendChild(button_tournament); - tr.appendChild(td); - - // state - td = document.createElement("td"); - td.innerText = tournament.state; - tr.appendChild(td); - - // nb_participants - td = document.createElement("td"); - td.innerText = tournament.nb_participants; - tr.appendChild(td); - - new_children.push(tr); - }); - tournaments_list.replaceChildren(...new_children); - } - - async update_query() - { - this.internal_search(); - this.display_result(); - } - - async update_search() - { - await this.external_search(); - this.update_query(); - } - - async postInit() - { - await this.update_search(); - document.getElementById("state-select").onchange = this.update_search.bind(this); - } - - async getHtml() - { - return ` - - - - - - - - - -
NameStatusMax numbers of participants
- `; - } -} diff --git a/games/models.py b/games/models.py index b708a72..fe4718e 100644 --- a/games/models.py +++ b/games/models.py @@ -7,11 +7,6 @@ from django.contrib.auth.models import User import time -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from tournament.models import TournamentGameModel - class GameModel(models.Model): finished = models.BooleanField(default = False) diff --git a/games/objects/AGame.py b/games/objects/AGame.py index 6fe0583..6839a61 100644 --- a/games/objects/AGame.py +++ b/games/objects/AGame.py @@ -6,8 +6,6 @@ from .ASpectator import ASpectator from ..models import GameModel -from tournament.models import TournamentGameModel - from django.contrib.auth.models import User class AGame(AbstractRoom): @@ -18,11 +16,7 @@ class AGame(AbstractRoom): self.game_manager = game_manager - query = TournamentGameModel.objects.filter(pk = game_id, game_type = game_type) - if (query.exists()): - self.model: TournamentGameModel | GameModel = query[0] - else: - self.model: TournamentGameModel | GameModel = GameModel.objects.get(pk = game_id, game_type = game_type) + self.model: GameModel = GameModel.objects.get(pk = game_id, game_type = game_type) players: list[User] = self.model.get_players() diff --git a/games/objects/GameManager.py b/games/objects/GameManager.py index 94de967..be34ed8 100644 --- a/games/objects/GameManager.py +++ b/games/objects/GameManager.py @@ -3,8 +3,6 @@ from ..models import GameModel from .pong.PongGame import PongGame from .tictactoe.TicTacToeGame import TicTacToeGame -from tournament.models import TournamentGameModel - class GameManager(): def __init__(self) -> None: diff --git a/run.sh b/run.sh index b8fbb27..9860154 100755 --- a/run.sh +++ b/run.sh @@ -6,7 +6,6 @@ pip install -r requirements.txt python manage.py makemigrations games python manage.py makemigrations profiles python manage.py makemigrations chat -python manage.py makemigrations tournament python manage.py makemigrations notice python manage.py migrate python manage.py compilemessages diff --git a/tournament/__init__.py b/tournament/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tournament/admin.py b/tournament/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/tournament/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/tournament/apps.py b/tournament/apps.py deleted file mode 100644 index 15ea9fb..0000000 --- a/tournament/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class TournamentConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'tournament' diff --git a/tournament/consumers.py b/tournament/consumers.py deleted file mode 100644 index 7d15241..0000000 --- a/tournament/consumers.py +++ /dev/null @@ -1,235 +0,0 @@ -from __future__ import annotations - -from channels.generic.websocket import WebsocketConsumer - -from django.contrib.auth.models import User -from django.db.models import QuerySet -from django.utils.translation import gettext as _ - -from profiles.models import ProfileModel -from profiles.serializers import ProfileSerializer -from .models import TournamentModel - -from .models import TournamentGameModel -from .serializers import TournamentGameSerializer - -import json - -class TournamentMember: - - def __init__(self, socket: TournamentWebConsumer, room: TournamentRoom, is_participating: bool = False) -> None: - self._socket: TournamentWebConsumer = socket - self._room: TournamentRoom = room - self.is_participating: bool = is_participating - - def send(self, detail: str, data: dict = {}): - data_to_send: dict = {"detail": detail} - data_to_send.update(data) - - self._socket.send(json.dumps(data_to_send)) - - def send_error(self, error_message: str, data: dict = {}): - data_to_send: dict = {"error_message": error_message} - data_to_send.update(data) - - self.send("error", data_to_send) - - def send_goto(self, game: TournamentGameModel): - self.send("go_to", {"game_id": game.pk}) - - def _receive_participating(self, data: dict) -> None: - - is_participating: bool | None = data.get("is_participating") - if (is_participating is None): - self.send_error(_("Missing is_participating statement.")) - return - - self._room.set_participation(self, is_participating) - - def receive(self, data: dict): - - detail: str | None = data.get("detail") - if (detail is None): - return - - match(detail): - case "update_participating": - self._receive_participating(data) - case _: - print("bozo_send") - - -class TournamentRoomManager: - - def __init__(self): - self._room_list: list[TournamentRoom] = [] - - def get(self, tournament: TournamentModel) -> TournamentRoom: - - for room in self._room_list: - if room._model is tournament: - return room - - room: TournamentRoom = TournamentRoom(self, tournament) - - self._room_list.append(room) - - return room - - def remove(self, room: TournamentRoom) -> None: - self._room_list.remove(room) - -class TournamentRoom: - - def __init__(self, room_manager: TournamentRoomManager, tournament: TournamentModel): - self._room_manager: TournamentRoomManager = room_manager - self._member_list: set[TournamentMember] = set() - self._model: TournamentModel = tournament - self._game_in_progress_list: set[TournamentGameModel] = set() - self._current_round = 0 - - def join(self, socket: TournamentWebConsumer) -> TournamentMember: - - member: TournamentMember = TournamentMember(socket, self) - self._member_list.add(member) - - return member - - def set_game_as_finished(self, game: TournamentGameModel): - - self._game_in_progress_list.remove(game) - - self.broadcast("game_update", TournamentGameSerializer(game).data) - - if len(self._game_in_progress_list) == 0: - self._round_finished() - - def set_game_as_started(self, game: TournamentGameModel): - - self._game_in_progress_list.add(game) - - self.broadcast("game_update", TournamentGameSerializer(game).data) - - def _finish(self, winner: User): - self._model.finish(winner) - - def _round_finished(self): - - if self._current_round == self._model.round: - last_game: TournamentGameSerializer = self._model.get_games_by_round(self._current_round)[0] - self._finish(last_game.winner) - return - - self._start_round() - - def _start_round(self): - - self._current_round += 1 - - participant_list: set[User] = self._model.get_participants_by_round(self._current_round) - - game_list = self._model.create_round(participant_list, self._current_round) - - for game in game_list: - for player in game.get_players(): - participant: TournamentMember = self.get_participant_by_profile(player) - participant.send_goto(game) - - def get_participants_profiles(self) -> list[ProfileModel]: - return [participant._socket.user.profilemodel for participant in self.get_participants()] - - def start(self) -> None: - - self._model.start() - - self.broadcast("start") - - self._start_round() - - def get_participant_by_profile(self, profile: ProfileModel): - for participant in self.get_participants(): - if (participant._socket.user.profilemodel == profile): - return participant - - def leave(self, member: TournamentMember) -> None: - - # Delete room if nobody connected, no cringe memory leak - if (len(self._member_list) == 1): - self._room_manager.remove(self) - return - - self._member_list.remove(member) - - self.set_participation(member, False) - - def everybody_is_here(self): - return len(self.get_participants()) == self._model.nb_participants - - def broadcast(self, detail: str, data: dict, excludes: set[TournamentMember] = set()) -> None: - - member_list: list[TournamentMember] = self._member_list - excludes - - for member in member_list: - member.send(detail, data) - - def get_participants(self) -> list[TournamentMember]: - return [member for member in self._member_list if member.is_participating] - - def set_participation(self, member: TournamentMember, is_participating: bool) -> None: - - if (self._model.started): - return - - if (member.is_participating == is_participating): - return - - if (is_participating == True): - self.broadcast("add_participant", {"participant": ProfileSerializer(member._socket.user.profilemodel).data}) - else: - self.broadcast("del_participant", {"participant": ProfileSerializer(member._socket.user.profilemodel).data}) - - member.is_participating = is_participating - - if self.everybody_is_here(): - self.start() - -tournament_manager: TournamentRoomManager = TournamentRoomManager() - -class TournamentWebConsumer(WebsocketConsumer): - - def connect(self): - - self.user: User = self.scope["user"] - if (not self.user.is_authenticated): - return - - tournament_id: int = int(self.scope['url_route']['kwargs']['tournament_id']) - - query: QuerySet = TournamentModel.objects.filter(pk=tournament_id) - - if (not query.exists()): - return - - tournament: TournamentModel = query[0] - - self.room = tournament_manager.get(tournament) - - self.member: TournamentMember = self.room.join(self) - - self.accept() - - def receive(self, text_data: str = None, bytes_data: bytes = None): - - user: User = self.scope["user"] - if (user != self.user): - return - - data: dict = json.loads(text_data) - - self.member.receive(data) - - def disconnect(self, close_code): - - self.room.leave(self.member) - - super().disconnect(close_code) # proutman à encore frappé diff --git a/tournament/models.py b/tournament/models.py deleted file mode 100644 index fd9199e..0000000 --- a/tournament/models.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import annotations -from typing import Any - -from games.models import GameModel - -from django.contrib.auth.models import User -from django.db.models import CASCADE -from django.db import models - - -class TournamentModel(models.Model): - - name = models.CharField(max_length = 100) - nb_participants = models.IntegerField() - round = models.IntegerField() - started = models.BooleanField(default = False) - finished = models.BooleanField(default = False) - winner = models.ForeignKey(User, on_delete=CASCADE, blank=True, null=True) - - def _register_participant(self, participant: User) -> None: - TournamentParticipantModel(participant=participant, tournament=self).save() - - def create_round(self, participants: set[User], round: int) -> set[GameModel]: - - game_list: set[GameModel] = set() - - for i, (participant1, participant2) in enumerate(zip(participants[0::2], participants[1::2])): - game: GameModel = self.create_game([participant1, participant2], round, i) - game_list.add(game) - - return game_list - - def start(self, participant_list: set[User]) -> None: - - self.started = True - self.round = 1 - - for participant in participant_list: - self._register_participant(participant) - - self.save() - - def finish(self, winner: User): - - self.finished = True - - self.winner = winner - - self.save() - - def create_game(self, participants: set[User], round: int, pos: int) -> GameModel: - - if (self.started == False): - return None - - if (len(participants) != 2): - return None - - from games.models import GameModel - - game: GameModel = GameModel().create(participants) - - TournamentGameModel(tournament=self, game=game, round=round, pos=pos).save() - - return game - - def get_games(self) -> set[GameModel]: - return [{games for games in self.get_games_by_round(i)} for i in range(1, self.round)] - - def get_games_by_round(self, round: int) -> set[GameModel]: - return {tournament_game for tournament_game in TournamentGameModel.objects.filter(tournament=self, round=round)} - - def get_participants_by_round(self, round: int) -> set[User]: - if round == 1: - return self.get_participants() - return {game.winner for game in self.get_games_by_round(round - 1)} - - def get_winners_by_round(self, round: int) -> set[User]: - return {game.winner for game in self.get_games_by_round(round)} - - def get_participants(self) -> set[User]: - return {tournament_participant.participant for tournament_participant in TournamentParticipantModel.objects.filter(tournament=self.pk)} - - def get_state(self) -> str: - return ("waiting to start", "in progress", "finish")[self.started + self.finished] - - def is_participating(self, profile: User) -> bool: - return TournamentParticipantModel.objects.filter(participant=profile, tournament=self).exists() - -class TournamentParticipantModel(models.Model): - participant = models.ForeignKey(User, on_delete=CASCADE) - tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE) - -class TournamentGameModel(GameModel): - - tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE, null=True, blank=True) - round = models.IntegerField() - pos = models.IntegerField() - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - from .consumers import tournament_manager - self.room = tournament_manager.get(self.tournament) - - - def start(self): - super().start() - - self.room.set_game_as_started(self) - - - def finish(self, winner_id): - super().finish(winner_id) - - self.room.set_game_as_finished(self) \ No newline at end of file diff --git a/tournament/routing.py b/tournament/routing.py deleted file mode 100644 index 805d8a2..0000000 --- a/tournament/routing.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.urls import re_path -from . import consumers - -websocket_urlpatterns = [ - re_path(r'ws/tournaments/(?P\d+)$', consumers.TournamentWebConsumer.as_asgi()) -] diff --git a/tournament/serializers.py b/tournament/serializers.py deleted file mode 100644 index cb84e87..0000000 --- a/tournament/serializers.py +++ /dev/null @@ -1,63 +0,0 @@ -from rest_framework import serializers - -from .models import TournamentModel, TournamentGameModel - -from profiles.serializers import ProfileSerializer -from games.serializers import GameSerializer - -nb_participants = [2 ** i for i in range(1, 6)] - -class TournamentSerializer(serializers.ModelSerializer): - - state = serializers.SerializerMethodField(read_only=True, required=False) - participants = serializers.SerializerMethodField(read_only=True, required=False) - round = serializers.ReadOnlyField() - games = serializers.SerializerMethodField(read_only=True, required=False) - started = serializers.ReadOnlyField() - finished = serializers.ReadOnlyField() - name = serializers.CharField(default="") - - class Meta: - model = TournamentModel - fields = ["name", "nb_participants", "round", "started", "finished", "id", "state", "participants", "games"] - - def get_participants(self, instance: TournamentModel): - return ProfileSerializer(instance.get_participants(), many=True).data - - def get_games(self, instance: TournamentModel): - return [GameSerializer(games, many=True).data for games in instance.get_games()] - - def get_state(self, instance: TournamentModel): - return ["waiting", "started", "finished"][instance.started + instance.finished] - - def validate_nb_participants(self, value: int): - if (value not in nb_participants): - raise serializers.ValidationError(f"The numbers of participants must be {str(nb_participants)}.") - return value - -class TournamentGameSerializer(serializers.ModelSerializer): - - players = serializers.SerializerMethodField() - winner_id = serializers.ReadOnlyField() - state = serializers.SerializerMethodField() - started = serializers.ReadOnlyField() - finished = serializers.ReadOnlyField() - start_timestamp = serializers.ReadOnlyField() - stop_timestamp = serializers.ReadOnlyField() - gamemode = serializers.ReadOnlyField() - round = serializers.ReadOnlyField() - pos = serializers.ReadOnlyField() - - class Meta: - model = TournamentGameModel - fields = ["id", "winner_id", "state", "started", "finished", "players", "start_timestamp", "stop_timestamp", "game_type", "round", "pos"] - - def get_state(self, instance: TournamentGameModel): - if (instance.finished): - return "finished" - if (instance.started): - return "started" - return "waiting" - - def get_players(self, instance: TournamentGameModel): - return ProfileSerializer(instance.get_players_profiles(), many=True).data diff --git a/tournament/test.py b/tournament/test.py deleted file mode 100644 index 077f4e4..0000000 --- a/tournament/test.py +++ /dev/null @@ -1,44 +0,0 @@ -from django.test import TestCase - -# Create your tests here. -from django.test.client import Client -from django.http import HttpResponse -from django.contrib.auth.models import User - -import json -import uuid - -class CreateTest(TestCase): - def setUp(self): - self.client = Client() - - self.url = "/api/tournaments/" - - self.username = str(uuid.uuid4()) - self.password = str(uuid.uuid4()) - - self.nb_players_by_game = 2 - self.nb_players = 8 - - user: User = User.objects.create_user(username=self.username, password=self.password) - self.client.login(username=self.username, password=self.password) - - def test_normal(self): - response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": self.nb_players_by_game, "nb_players": self.nb_players}) - response_data: dict = json.loads(response.content) - self.assertDictContainsSubset({"name": ""}, response_data) - - def test_too_small_nb_players_by_game(self): - response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": 1, "nb_players": self.nb_players}) - response_data = json.loads(response.content) - self.assertDictEqual(response_data, {'nb_players_by_game': ['The numbers of players by game must be greather than 2.']}) - - def test_too_small_nb_players(self): - response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": self.nb_players_by_game, "nb_players": 1}) - response_data = json.loads(response.content) - self.assertDictEqual(response_data, {'nb_players': ['The numbers of players must be greather than 2.'], 'nb_players_by_game': ['The numbers of players by game must be smaller than the numbers of players.']}) - - def test_nb_players_smaller_nb_players_by_game(self): - response: HttpResponse = self.client.post(self.url, {"nb_players_by_game": 5, "nb_players": 3}) - response_data = json.loads(response.content) - self.assertDictEqual(response_data, {'nb_players_by_game': ['The numbers of players by game must be smaller than the numbers of players.']}) \ No newline at end of file diff --git a/tournament/tests.py b/tournament/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/tournament/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/tournament/urls.py b/tournament/urls.py deleted file mode 100644 index 3aeecb5..0000000 --- a/tournament/urls.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.urls import path, re_path -from django.conf import settings -from django.conf.urls.static import static - -from .viewset import TournamentViewSet - -urlpatterns = [ - path("", TournamentViewSet.as_view({"get": "retrieve"}), name="tournament_page"), - path("", TournamentViewSet.as_view({"post": "create"}), name="tournament_page"), - re_path(r"search/(?P\w*)", TournamentViewSet.as_view({"get": "list", }), name="tournaments"), -] \ No newline at end of file diff --git a/tournament/viewset.py b/tournament/viewset.py deleted file mode 100644 index a2957bb..0000000 --- a/tournament/viewset.py +++ /dev/null @@ -1,49 +0,0 @@ -from rest_framework import viewsets -from rest_framework.response import Response -from rest_framework import permissions, status -from rest_framework.authentication import SessionAuthentication - -from django.http import HttpRequest -from django.contrib.auth import login -from django.db.models import QuerySet - -from .models import TournamentModel -from .serializers import TournamentSerializer - -import math - -# Create your views here. -class TournamentViewSet(viewsets.ModelViewSet): - - queryset = TournamentModel.objects.all - serializer_class = TournamentSerializer - permission_classes = (permissions.IsAuthenticated,) - authentication_classes = (SessionAuthentication,) - - def perform_create(self, serializer: TournamentSerializer): - tournament = serializer.save(round=math.log2(serializer.validated_data['nb_participants']) + 1) - - return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED) - - def list(self, request: HttpRequest, state: str = ""): - query: QuerySet - match state: - case "started": - query = TournamentModel.objects.filter(started=True, finished=False) - case "finished": - query = TournamentModel.objects.filter(finished=True) - case "waiting": - query = TournamentModel.objects.filter(started=False, finished=False) - case _: - query = TournamentModel.objects.all() - serializer = self.serializer_class(query, many=True) - return Response(serializer.data) - - def retrieve(self, request: HttpRequest, pk): - - if (not TournamentModel.objects.filter(pk=pk).exists()): - return Response({"detail": "Tournament not found."}, status=status.HTTP_404_NOT_FOUND) - - tournament = TournamentModel.objects.get(pk=pk) - - return Response(self.serializer_class(tournament).data, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/transcendence/asgi.py b/transcendence/asgi.py index 11ca834..0bcd1e6 100644 --- a/transcendence/asgi.py +++ b/transcendence/asgi.py @@ -13,7 +13,6 @@ from channels.auth import AuthMiddlewareStack import chat.routing import matchmaking.routing -import tournament.routing import games.routing import notice.routing @@ -27,7 +26,6 @@ application = ProtocolTypeRouter({ URLRouter( chat.routing.websocket_urlpatterns + matchmaking.routing.websocket_urlpatterns + - tournament.routing.websocket_urlpatterns + games.routing.websocket_urlpatterns + notice.routing.websocket_urlpatterns ) diff --git a/transcendence/settings.py b/transcendence/settings.py index 6e7f774..68129c4 100644 --- a/transcendence/settings.py +++ b/transcendence/settings.py @@ -44,7 +44,6 @@ INSTALLED_APPS = [ 'channels', 'daphne', - 'tournament.apps.TournamentConfig', 'matchmaking.apps.MatchmakingConfig', 'games.apps.GamesConfig', 'accounts.apps.AccountsConfig', diff --git a/transcendence/urls.py b/transcendence/urls.py index fd3d6d9..7516754 100644 --- a/transcendence/urls.py +++ b/transcendence/urls.py @@ -23,7 +23,6 @@ urlpatterns = [ path('api/profiles/', include('profiles.urls')), path('api/accounts/', include('accounts.urls')), path('api/chat/', include('chat.urls')), - path('api/tournaments/', include('tournament.urls')), path('api/games/', include('games.urls')), re_path(r'^api/', handler_404_view), path('', include('frontend.urls')),