From e1b84e6c06012f8d58d988c1db46a5e2d6d4ccd9 Mon Sep 17 00:00:00 2001 From: ComputasAlex Date: Mon, 8 Jul 2024 14:15:01 +0200 Subject: [PATCH 1/6] Adds difficulty IDs enum to utilities --- src/utilities/difficulties.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/utilities/difficulties.py diff --git a/src/utilities/difficulties.py b/src/utilities/difficulties.py new file mode 100644 index 0000000..65d5ddf --- /dev/null +++ b/src/utilities/difficulties.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class DifficultyId(int, Enum): + Easy = 1 + Medium = 2 + Hard = 3 + Multiplayer = 4 From 7226cdc2c24e81a9d1ec553d4fe8bfe8e839750d Mon Sep 17 00:00:00 2001 From: ComputasAlex Date: Mon, 8 Jul 2024 14:15:42 +0200 Subject: [PATCH 2/6] Adds difficulty_id to Scores model and get highscore methods --- src/webapp/models.py | 66 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/webapp/models.py b/src/webapp/models.py index af1f79b..1f24042 100644 --- a/src/webapp/models.py +++ b/src/webapp/models.py @@ -7,6 +7,7 @@ import os import random from flask_sqlalchemy import SQLAlchemy +from utilities.difficulties import DifficultyId from utilities.exceptions import UserError db = SQLAlchemy() @@ -52,6 +53,8 @@ class Scores(db.Model): player_id = db.Column(db.NVARCHAR(32), db.ForeignKey("players.player_id")) score = db.Column(db.Integer, nullable=False) date = db.Column(db.Date) + difficulty_id = db.Column( + db.Integer, db.ForeignKey("difficulty.id"), default=1) class Players(db.Model): @@ -155,7 +158,7 @@ def insert_into_games(game_id, labels, date, difficulty_id): ) -def insert_into_scores(player_id, score, date): +def insert_into_scores(player_id, score, date, difficulty_id: DifficultyId): """ Insert values into Scores table. @@ -163,6 +166,7 @@ def insert_into_scores(player_id, score, date): name: user name, string score: float date: datetime.date + difficulty_id: integer: For multiplayer: 4 """ score_int_or_float = isinstance(score, float) or isinstance(score, int) @@ -170,9 +174,11 @@ def insert_into_scores(player_id, score, date): isinstance(player_id, str) and score_int_or_float and isinstance(date, datetime.date) + and isinstance(difficulty_id, int) ): try: - score = Scores(player_id=player_id, score=score, date=date) + score = Scores(player_id=player_id, score=score, + date=date, difficulty_id=difficulty_id) db.session.add(score) db.session.commit() return True @@ -181,7 +187,7 @@ def insert_into_scores(player_id, score, date): else: raise UserError( "Name has to be string, score can be int or " - "float and date has to be datetime.date." + "float, difficulty_id has to be an integer and date has to be datetime.date." ) @@ -404,6 +410,60 @@ def delete_old_games(): raise Exception("Couldn't clean up old game records: " + str(e)) +def get_daily_high_score(difficulty_id): + """ + Function for reading all daily scores. + + Returns list of dictionaries. + """ + try: + today = datetime.date.today() + # filter by today and sort by score + top_n_list = ( + Scores.query.filter_by(date=today, difficulty_id=difficulty_id) + .order_by(Scores.score.desc()) + .all() + ) + # structure data + new = [ + {"id": score.score_id, "score": score.score} + for score in top_n_list + ] + return new + + except AttributeError as e: + raise AttributeError( + "Could not read daily highscore from database: " + str(e) + ) + + +def get_top_n_high_score_list(top_n, difficulty_id): + """ + Funtion for reading total top n list from database. + + Parameter: top_n, number of players in top list. + + Returns list of dictionaries. + """ + try: + # read top n high scores + top_n_list = ( + Scores.query.filter_by(difficulty_id=difficulty_id).order_by( + Scores.score.desc()).limit(top_n).all() + ) + # strucutre data + new = [ + {"id": score.score_id, "score": score.score} + for score in top_n_list + ] + return new + + except AttributeError as e: + raise AttributeError( + "Could not read top high score from database: " + str(e) + ) + + def to_norwegian(english_label): """ Reads the labels tabel and return the norwegian translation of the From 33f5e764b89d84aa195500781184e606c308b6f6 Mon Sep 17 00:00:00 2001 From: ComputasAlex Date: Mon, 8 Jul 2024 14:16:33 +0200 Subject: [PATCH 3/6] Adds highscore endpoints to multiplayer --- src/webapp/api.py | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/webapp/api.py b/src/webapp/api.py index af1a6f0..f84b1fb 100644 --- a/src/webapp/api.py +++ b/src/webapp/api.py @@ -11,10 +11,10 @@ from PIL import Image from PIL import ImageChops from io import BytesIO +from datetime import datetime import os import json import uuid -import datetime import time import random @@ -121,7 +121,7 @@ def handle_joinGame(json_data): else: game_id = uuid.uuid4().hex labels = models.get_n_labels(setup.NUM_GAMES, difficulty_id) - today = datetime.datetime.today() + today = datetime.today() models.insert_into_games( game_id, json.dumps(labels), today, difficulty_id) models.insert_into_players(player_id, game_id, "Waiting") @@ -157,6 +157,42 @@ def handle_getLabel(json_data): emit("getLabel", json.dumps(label), room=game_id) +@socketio.on("postScore") +def handle_postScore(json_data): + data = json.loads(json_data) + app.logger.info(data) + player_id = data.get("player_id") + score = float(data.get("score")) + difficulty_id = int(data.get("difficulty_id")) + assert isinstance(difficulty_id, int) + + today = datetime.today() + models.insert_into_scores(player_id, score, today, difficulty_id) + + +@socketio.on("viewHighScore") +def view_high_score(json_data): + """ + Read highscore from database. Return top n of all time and daily high + scores. + """ + difficulty_id = 4 + data = json.loads(json_data) + game_id = data["game_id"] + # read top n overall high score + top_n_high_scores = models.get_top_n_high_score_list( + setup.TOP_N, difficulty_id=difficulty_id) + # read daily high score + daily_high_scores = models.get_daily_high_score( + difficulty_id=difficulty_id) + data = { + "daily": daily_high_scores, + "total": top_n_high_scores, + } + + emit("viewHighScore", json.dumps(data), room=game_id) + + @socketio.on("classify") def handle_classify(data, image, correct_label=None): """ @@ -241,7 +277,6 @@ def handle_endGame(json_data): their scores and the player with the highest score is deemed the winner. The two scores are finally stored in the database. """ - date = datetime.datetime.today() data = json.loads(json_data) # Get data from given player game_id = data["game_id"] @@ -251,7 +286,6 @@ def handle_endGame(json_data): pass # raise excp.BadRequest("Game not finished") # Insert score information into db - models.insert_into_scores(player_id, score_player, date) # Create a list containing player data which is sent out to both players return_data = {"score": score_player, "playerId": player_id} # Retrieve the opponent (client) to pass on the score to From 73e59337086cf97f68961f816cbfb6bf64394c83 Mon Sep 17 00:00:00 2001 From: ComputasAlex Date: Mon, 8 Jul 2024 14:16:55 +0200 Subject: [PATCH 4/6] Adds difficulty to scores tests --- src/test/test_db.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/test_db.py b/src/test/test_db.py index 372a56f..08a1aa3 100644 --- a/src/test/test_db.py +++ b/src/test/test_db.py @@ -8,6 +8,7 @@ import datetime from pytest import raises +from utilities.difficulties import DifficultyId from webapp import api from webapp import models from utilities.exceptions import UserError @@ -47,8 +48,10 @@ def test_insert_into_scores(): """ Check that records exists in Scores table after inserting. """ + easy_difficulty = DifficultyId.Easy with api.app.app_context(): - result = models.insert_into_scores("Test User", 500, TestValues.TODAY) + result = models.insert_into_scores( + "Test User", 500, TestValues.TODAY, easy_difficulty) assert result @@ -94,7 +97,8 @@ def test_illegal_parameter_scores(): into scores table. """ with raises(UserError): - models.insert_into_scores(100, "score", "01.01.2020") + models.insert_into_scores( + 100, "score", "01.01.2020", DifficultyId.Medium) def test_illegal_parameter_labels(): From 7a3274c6f246b1e9a0979b6f27b04fc28aec0a39 Mon Sep 17 00:00:00 2001 From: ComputasAlex Date: Mon, 8 Jul 2024 14:17:41 +0200 Subject: [PATCH 5/6] format --- src/webapp/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/webapp/models.py b/src/webapp/models.py index 1f24042..065fb5c 100644 --- a/src/webapp/models.py +++ b/src/webapp/models.py @@ -187,8 +187,7 @@ def insert_into_scores(player_id, score, date, difficulty_id: DifficultyId): else: raise UserError( "Name has to be string, score can be int or " - "float, difficulty_id has to be an integer and date has to be datetime.date." - ) + "float, difficulty_id has to be an integer and date has to be datetime.date.") def insert_into_players(player_id, game_id, state): From a7bfc6de04e6d2e87233e8900445a93c1a01554f Mon Sep 17 00:00:00 2001 From: ComputasAlex Date: Mon, 8 Jul 2024 15:05:01 +0200 Subject: [PATCH 6/6] Changes difficulty_id magic number 4 to Enum variable --- src/webapp/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webapp/api.py b/src/webapp/api.py index f84b1fb..1be181c 100644 --- a/src/webapp/api.py +++ b/src/webapp/api.py @@ -19,6 +19,7 @@ import random from customvision.classifier import Classifier +from utilities.difficulties import DifficultyId from webapp import models from webapp import storage from utilities.exceptions import UserError @@ -176,7 +177,7 @@ def view_high_score(json_data): Read highscore from database. Return top n of all time and daily high scores. """ - difficulty_id = 4 + difficulty_id = DifficultyId.Multiplayer data = json.loads(json_data) game_id = data["game_id"] # read top n overall high score