Merge branch 'main' of codeberg.org:adrien-lsh/ft_transcendence

This commit is contained in:
Xamora 2024-05-13 16:44:33 +02:00
commit ae76b82169
9 changed files with 112 additions and 108 deletions

View File

@ -75,14 +75,14 @@ export class Profile extends AExchangeable
} }
/** /**
* @returns {[Object]} * @returns {Promise<[Object]>}
*/ */
async getGameHistory() async getGameHistory()
{ {
let response = await this.client._get(`/api/games/history/${this.id}`); const response = await this.client._get(`/api/games/history/${this.id}`);
let response_data = await response.json(); const response_data = await response.json();
let games = []; const games = [];
response_data.forEach(game_data => { response_data.forEach(game_data => {
games.push(game_data); games.push(game_data);

View File

@ -20,7 +20,6 @@ import TournamentPageView from "./views/tournament/TournamentPageView.js";
import TournamentsListView from "./views/tournament/TournamentsListView.js"; import TournamentsListView from "./views/tournament/TournamentsListView.js";
import TournamentCreateView from "./views/tournament/TournamentCreateView.js"; import TournamentCreateView from "./views/tournament/TournamentCreateView.js";
import AuthenticationView from "./views/accounts/AuthenticationView.js"; import AuthenticationView from "./views/accounts/AuthenticationView.js";
import GameHistoryView from "./views/GameHistoryView.js";
let client = new Client(location.origin); let client = new Client(location.origin);
let lang = client.lang; let lang = client.lang;
@ -79,7 +78,6 @@ const router = async(uri) => {
const routes = [ const routes = [
{ path: "/", view: HomeView}, { path: "/", view: HomeView},
{ path: "/profiles/:id/history", view: GameHistoryView },
{ path: "/profiles/:username", view: ProfilePageView }, { path: "/profiles/:username", view: ProfilePageView },
{ path: "/tournaments/create", view: TournamentCreateView }, { path: "/tournaments/create", view: TournamentCreateView },
{ path: "/tournaments/:id", view: TournamentPageView }, { path: "/tournaments/:id", view: TournamentPageView },

View File

@ -1,66 +0,0 @@
import { client, lang } from "../index.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, 'homeWindowTitle');
this.id = params.id;
}
async postInit()
{
this.profile = await client.profiles.getProfileId(this.id);
if (this.profile === null)
return 404;
await this.fillHistory();
}
async fillHistory()
{
let games = await this.profile.getGameHistory();
let game_list = document.getElementById("game-list");
games.forEach(game => {
let a = document.createElement("a");
a.href = `/games/${game.id}/0`;
a.setAttribute("data-link", true);
let game_item = document.createElement("div");
game_item.className = "game-item";
game_item.style.backgroundColor = "grey";
if (game.started)
game_item.style.backgroundColor = "yellow";
if (game.finished)
game_item.style.backgroundColor = this.profile.id === game.winner_id ? "green" : "red";
game.players.forEach(player => {
let player_score = document.createElement("a");
player_score.href = `/profiles/${player.username}`;
player_score.innerText = `${player.username}: ${player.score.length}`;
player_score.setAttribute("data-link", true);
game_item.appendChild(player_score);
});
a.appendChild(game_item);
game_list.appendChild(a);
});
}
async getHtml()
{
return /* HTML */ `
<link rel="stylesheet" href="/static/css/gameHistory.css">
<h1>Game History</h1>
<div id="game-list">
</div>
`;
}
}

View File

@ -16,6 +16,13 @@ export default class extends AbstractView {
if (!this.profile) if (!this.profile)
return 404; return 404;
const games = await this.profile.getGameHistory();
console.log(games)
await this.fillHistory(games);
await this.fillStatistics(games);
if (this.profile.id === client.me.id) if (this.profile.id === client.me.id)
return; return;
@ -68,13 +75,69 @@ export default class extends AbstractView {
statusIndicator.classList.add('bg-danger'); statusIndicator.classList.add('bg-danger');
} }
/**
* @param {[Object]} games
*/
async fillStatistics(games)
{
const winrateDiv = document.getElementById("winrate");
const win = 0;
const lose = 0;
games.forEach(game => {
if (game.finished === false)
return
if (client.me.id === game.winner.id)
win++;
else
lose++;
});
winrateDiv.innerText = `winrate: ${win + lose === 0 ? "🤓" : win / (win + lose)}`
}
async fillHistory(games)
{
const game_list = document.getElementById("game-list");
games.forEach(game => {
let a = document.createElement("a");
a.href = `/games/${game.game_type}/${game.id}`;
a.setAttribute("data-link", true);
let game_item = document.createElement("div");
game_item.className = "game-item";
game_item.style.backgroundColor = "grey";
if (game.started)
game_item.style.backgroundColor = "yellow";
if (game.finished)
game_item.style.backgroundColor = this.profile.id === game.winner.id ? "green" : "red";
game.players.forEach(player => {
let player_score = document.createElement("a");
player_score.href = `/profiles/${player.username}`;
player_score.innerText = `${player.username}`;
player_score.setAttribute("data-link", true);
game_item.appendChild(player_score);
});
a.appendChild(game_item);
game_list.appendChild(a);
});
}
async getHtml() { async getHtml() {
this.profile = await client.profiles.getProfile(this.username); this.profile = await client.profiles.getProfile(this.username);
if (!this.profile) if (!this.profile)
return ''; return '';
return ` return /* HTML */ `
<div> <div>
<div class='mb-3' id='profileInfo'> <div class='mb-3' id='profileInfo'>
<h1>${this.username}<span id='statusIndicator' style='height:0.75em; width:0.75em' class='ms-2 rounded-circle border d-inline-block'></span></h1> <h1>${this.username}<span id='statusIndicator' style='height:0.75em; width:0.75em' class='ms-2 rounded-circle border d-inline-block'></span></h1>
@ -88,6 +151,14 @@ export default class extends AbstractView {
<button class='btn btn-sm btn-danger d-none' id='blockButton'>Block</button> <button class='btn btn-sm btn-danger d-none' id='blockButton'>Block</button>
<button class='btn btn-sm btn-secondary d-none' id='unblockButton'>Unblock</button> <button class='btn btn-sm btn-secondary d-none' id='unblockButton'>Unblock</button>
</div> </div>
<h1>Games</h1>
<div>
<h1 id='winrate'></h1>
</div>
<div>
<link rel="stylesheet" href="/static/css/gameHistory.css">
<div id="game-list"></div>
</div>
</div> </div>
`; `;
} }

View File

@ -1,27 +0,0 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions, status
from django.http import HttpRequest
from .models import GameMembersModel, GameModel
from .serializers import GameSerializer
from . import config
class GameHistoryView(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request: HttpRequest, pk: int = None):
member_game_model_list: list[GameMembersModel] = GameMembersModel.objects.filter(player_id = pk)
game_model_list: list[GameModel] = []
for member_game_model in member_game_model_list:
game_model_list.append(GameModel.objects.get(pk = member_game_model.game_id))
games_data: list[dict] = [GameSerializer(game_model).data for game_model in game_model_list]
return Response(games_data)

View File

@ -0,0 +1,28 @@
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import permissions
from django.http import HttpRequest
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from .models import GameMembersModel, GameModel
from .serializers import GameSerializer
class GameHistoryView(ViewSet):
queryset = User.objects.all()
serializer_class = GameSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def retrive(self, request: HttpRequest, pk: int = None):
user: User = get_object_or_404(User, pk=pk)
member_game_model_list: list[GameMembersModel] = GameMembersModel.objects.filter(player=user)
game_model_list: list[GameModel] = [member_game_model.game for member_game_model in member_game_model_list]
games_data: list[dict] = self.serializer_class(game_model_list, many=True).data
return Response(games_data)

View File

@ -38,11 +38,11 @@ class GameModel(models.Model):
self.stop_timestamp = round(time.time() * 1000, 1) self.stop_timestamp = round(time.time() * 1000, 1)
self.save() self.save()
def get_players(self) -> set[User]: def get_players(self) -> list[User]:
return {game_player.player for game_player in GameMembersModel.objects.filter(game = self)} return [game_player.player for game_player in GameMembersModel.objects.filter(game = self)]
def get_players_profiles(self) -> set[User]: def get_players_profiles(self) -> list[User]:
return {game_player.player.profilemodel for game_player in GameMembersModel.objects.filter(game = self)} return [game_player.player.profilemodel for game_player in GameMembersModel.objects.filter(game = self)]
def get_score_by_player_id(self, player_id: int) -> list[int]: def get_score_by_player_id(self, player_id: int) -> list[int]:
query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id) query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id)

View File

@ -9,7 +9,7 @@ from profiles.serializers import ProfileSerializer
class GameSerializer(serializers.ModelSerializer): class GameSerializer(serializers.ModelSerializer):
players = serializers.SerializerMethodField() players = serializers.SerializerMethodField()
winner_id = serializers.ReadOnlyField() winner = serializers.ReadOnlyField()
state = serializers.SerializerMethodField() state = serializers.SerializerMethodField()
started = serializers.ReadOnlyField() started = serializers.ReadOnlyField()
finished = serializers.ReadOnlyField() finished = serializers.ReadOnlyField()
@ -19,7 +19,7 @@ class GameSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = GameModel model = GameModel
fields = ["id", "winner_id", "state", "started", "finished", "players", "start_timestamp", "stop_timestamp", "game_type"] fields = ["id", "winner", "state", "started", "finished", "players", "start_timestamp", "stop_timestamp", "game_type"]
def get_state(self, instance: GameModel): def get_state(self, instance: GameModel):
if (instance.finished): if (instance.finished):

View File

@ -3,11 +3,11 @@ from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from .GameViewSet import GameViewSet from .GameViewSet import GameViewSet
from .GameHistoryView import GameHistoryView from .GameHistoryViewSet import GameHistoryView
from .GameConfigView import GameConfigView from .GameConfigView import GameConfigView
urlpatterns = [ urlpatterns = [
path("<int:pk>", GameViewSet.as_view({"get": "retrieve"}), name="game_page"), path("<int:pk>", GameViewSet.as_view({"get": "retrieve"}), name="game_page"),
path("history/<int:pk>", GameHistoryView.as_view(), name="history_page"), path("history/<int:pk>", GameHistoryView.as_view({"get": "retrive"}), name="history_page"),
path("", GameConfigView.as_view(), name = "game_config") path("", GameConfigView.as_view(), name = "game_config")
] ]