From f576dc208f392900b55f012a2ed18bcd0c19be82 Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 13 May 2024 13:46:51 +0200 Subject: [PATCH 1/5] game: online: fix set -> list --- games/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/games/models.py b/games/models.py index edd26f7..b708a72 100644 --- a/games/models.py +++ b/games/models.py @@ -38,11 +38,11 @@ class GameModel(models.Model): self.stop_timestamp = round(time.time() * 1000, 1) self.save() - def get_players(self) -> set[User]: - return {game_player.player for game_player in GameMembersModel.objects.filter(game = self)} + def get_players(self) -> list[User]: + return [game_player.player for game_player in GameMembersModel.objects.filter(game = self)] - def get_players_profiles(self) -> set[User]: - return {game_player.player.profilemodel for game_player in GameMembersModel.objects.filter(game = self)} + def get_players_profiles(self) -> list[User]: + 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]: query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id) From 5675f64268599ab79bc10260813eb155b39b57d5 Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 13 May 2024 13:50:33 +0200 Subject: [PATCH 2/5] profile: add: game history --- frontend/static/js/api/Profile.js | 8 +-- frontend/static/js/views/GameHistoryView.js | 66 --------------------- frontend/static/js/views/ProfilePageView.js | 52 +++++++++++++++- games/GameHistoryView.py | 27 --------- games/GameHistoryViewSet.py | 28 +++++++++ games/urls.py | 4 +- 6 files changed, 85 insertions(+), 100 deletions(-) delete mode 100644 frontend/static/js/views/GameHistoryView.js delete mode 100644 games/GameHistoryView.py create mode 100644 games/GameHistoryViewSet.py diff --git a/frontend/static/js/api/Profile.js b/frontend/static/js/api/Profile.js index faeef20..68b8ea4 100644 --- a/frontend/static/js/api/Profile.js +++ b/frontend/static/js/api/Profile.js @@ -75,14 +75,14 @@ export class Profile extends AExchangeable } /** - * @returns {[Object]} + * @returns {Promise<[Object]>} */ async getGameHistory() { - let response = await this.client._get(`/api/games/history/${this.id}`); - let response_data = await response.json(); + const response = await this.client._get(`/api/games/history/${this.id}`); + const response_data = await response.json(); - let games = []; + const games = []; response_data.forEach(game_data => { games.push(game_data); diff --git a/frontend/static/js/views/GameHistoryView.js b/frontend/static/js/views/GameHistoryView.js deleted file mode 100644 index 60e7850..0000000 --- a/frontend/static/js/views/GameHistoryView.js +++ /dev/null @@ -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 */ ` - -

Game History

-
-
- `; - } -} diff --git a/frontend/static/js/views/ProfilePageView.js b/frontend/static/js/views/ProfilePageView.js index 7ebc850..d1270f1 100644 --- a/frontend/static/js/views/ProfilePageView.js +++ b/frontend/static/js/views/ProfilePageView.js @@ -16,6 +16,13 @@ export default class extends AbstractView { if (!this.profile) 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) return; @@ -68,13 +75,51 @@ export default class extends AbstractView { statusIndicator.classList.add('bg-danger'); } + async fillStatistics(games) + { + + } + + async fillHistory(games) + { + 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.setAttribute("data-link", true); + + game_item.appendChild(player_score); + }); + + a.appendChild(game_item); + game_list.appendChild(a); + }); + } + async getHtml() { this.profile = await client.profiles.getProfile(this.username); if (!this.profile) return ''; - return ` + return /* HTML */ `

${this.username}

@@ -88,6 +133,11 @@ export default class extends AbstractView {
+
+ +

Game History

+
+
`; } diff --git a/games/GameHistoryView.py b/games/GameHistoryView.py deleted file mode 100644 index b7a1a10..0000000 --- a/games/GameHistoryView.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/games/GameHistoryViewSet.py b/games/GameHistoryViewSet.py new file mode 100644 index 0000000..52560b8 --- /dev/null +++ b/games/GameHistoryViewSet.py @@ -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) \ No newline at end of file diff --git a/games/urls.py b/games/urls.py index 34e28c2..ebceaac 100644 --- a/games/urls.py +++ b/games/urls.py @@ -3,11 +3,11 @@ from django.conf import settings from django.conf.urls.static import static from .GameViewSet import GameViewSet -from .GameHistoryView import GameHistoryView +from .GameHistoryViewSet import GameHistoryView from .GameConfigView import GameConfigView urlpatterns = [ path("", GameViewSet.as_view({"get": "retrieve"}), name="game_page"), - path("history/", GameHistoryView.as_view(), name="history_page"), + path("history/", GameHistoryView.as_view({"get": "retrive"}), name="history_page"), path("", GameConfigView.as_view(), name = "game_config") ] \ No newline at end of file From d75732d8b3b2fb56998b4079e9769f6eedeef3c1 Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 13 May 2024 13:53:14 +0200 Subject: [PATCH 3/5] fix: game history: remove delete file import --- frontend/static/js/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index 9a62e1e..bea273b 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -20,7 +20,6 @@ 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"; -import GameHistoryView from "./views/GameHistoryView.js"; let client = new Client(location.origin); let lang = client.lang; @@ -79,7 +78,6 @@ const router = async(uri) => { const routes = [ { path: "/", view: HomeView}, - { path: "/profiles/:id/history", view: GameHistoryView }, { path: "/profiles/:username", view: ProfilePageView }, { path: "/tournaments/create", view: TournamentCreateView }, { path: "/tournaments/:id", view: TournamentPageView }, From 570b19134fdfb36337576b383d779011e45c143c Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 13 May 2024 13:53:30 +0200 Subject: [PATCH 4/5] gamehistory: fix: url to game is right --- frontend/static/js/views/ProfilePageView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/static/js/views/ProfilePageView.js b/frontend/static/js/views/ProfilePageView.js index d1270f1..43df461 100644 --- a/frontend/static/js/views/ProfilePageView.js +++ b/frontend/static/js/views/ProfilePageView.js @@ -87,7 +87,7 @@ export default class extends AbstractView { games.forEach(game => { let a = document.createElement("a"); - a.href = `/games/${game.id}/0`; + a.href = `/games/${game.game_type}/${game.id}`; a.setAttribute("data-link", true); let game_item = document.createElement("div"); From c38ca69dd215f5994687d5bc4e43b3327b35932a Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 13 May 2024 16:35:41 +0200 Subject: [PATCH 5/5] profile: add winrate --- frontend/static/js/views/ProfilePageView.js | 27 ++++++++++++++++++--- games/serializers.py | 4 +-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/frontend/static/js/views/ProfilePageView.js b/frontend/static/js/views/ProfilePageView.js index 43df461..41405c8 100644 --- a/frontend/static/js/views/ProfilePageView.js +++ b/frontend/static/js/views/ProfilePageView.js @@ -75,14 +75,32 @@ export default class extends AbstractView { 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) { - let game_list = document.getElementById("game-list"); + const game_list = document.getElementById("game-list"); games.forEach(game => { @@ -96,7 +114,7 @@ export default class extends AbstractView { if (game.started) game_item.style.backgroundColor = "yellow"; if (game.finished) - game_item.style.backgroundColor = this.profile.id === game.winner_id ? "green" : "red"; + game_item.style.backgroundColor = this.profile.id === game.winner.id ? "green" : "red"; game.players.forEach(player => { let player_score = document.createElement("a"); @@ -133,9 +151,12 @@ export default class extends AbstractView { +

Games

+
+

+
-

Game History

diff --git a/games/serializers.py b/games/serializers.py index 746b02d..2c8200c 100644 --- a/games/serializers.py +++ b/games/serializers.py @@ -9,7 +9,7 @@ from profiles.serializers import ProfileSerializer class GameSerializer(serializers.ModelSerializer): players = serializers.SerializerMethodField() - winner_id = serializers.ReadOnlyField() + winner = serializers.ReadOnlyField() state = serializers.SerializerMethodField() started = serializers.ReadOnlyField() finished = serializers.ReadOnlyField() @@ -19,7 +19,7 @@ class GameSerializer(serializers.ModelSerializer): class Meta: 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): if (instance.finished):