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(): 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 diff --git a/src/webapp/api.py b/src/webapp/api.py index a1c1ef4..552ee34 100644 --- a/src/webapp/api.py +++ b/src/webapp/api.py @@ -11,14 +11,15 @@ 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 from customvision.classifier import Classifier +from utilities.difficulties import DifficultyId from utilities.languages import Language from webapp import models from webapp import storage @@ -122,7 +123,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") @@ -158,6 +159,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 = DifficultyId.Multiplayer + 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): """ @@ -254,7 +291,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"] @@ -264,7 +300,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 diff --git a/src/webapp/models.py b/src/webapp/models.py index af1f79b..065fb5c 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,8 +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.") def insert_into_players(player_id, game_id, state): @@ -404,6 +409,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