From 3f3e137fdaa55e880e38c9315ede0ce7eb907760 Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Sat, 26 Sep 2020 23:55:53 +0200 Subject: [PATCH] implement basic db support for player stats --- database.py | 138 ++++++++++++++++++++++++++++++++++++---------------- player.py | 1 + server.py | 32 ++++++++++-- 3 files changed, 125 insertions(+), 46 deletions(-) diff --git a/database.py b/database.py index 004a0d4..638d68c 100644 --- a/database.py +++ b/database.py @@ -2,56 +2,112 @@ import json import sqlite3 import player +import os -DB_BASE = "file:{}?mode=ro" +DATABASE_PLAYERS = "players.sqlite" +DATABASE_ROUNDS = "rounds.sqlite" +DATABASE_HISTORICAL = "players.sqlite" -def getTotalPlayers(database): - '''Get the total number of players in the database''' +class DatabaseConnection: + def __init__(self, basepath): + self.dbFormatString = "file:{}?mode=ro" + self.databasePlayers = self.dbFormatString.format(os.path.join(basepath, DATABASE_PLAYERS)) + self.databaseRounds = self.dbFormatString.format(os.path.join(basepath, DATABASE_ROUNDS)) + self.databaseHistorical = self.dbFormatString.format( + os.path.join(basepath, DATABASE_HISTORICAL)) - conn = sqlite3.connect(DB_BASE.format(database), uri=True) - cursor = conn.cursor() - cursor.execute("SELECT Count(*) FROM players") - count = cursor.fetchone()[0] - conn.close() - return count + self.connPlayers = sqlite3.connect(self.databasePlayers, uri=True) + self.connRounds = sqlite3.connect(self.databaseRounds, uri=True) + self.connHistorical = sqlite3.connect(self.databaseHistorical, uri=True) + def __del__(self): + self.connPlayers.close(); + self.connRounds.close(); + self.connHistorical.close(); -def getRankRange(database, start, end): - '''Get a range of players by rank''' + def getTotalPlayers(self): + '''Get the total number of players in the database''' - conn = sqlite3.connect(DB_BASE.format(database), uri=True) - cursor = conn.cursor() - limit = end - start - cursor.execute("Select * FROM players ORDER BY (mu - 2*sigma) DESC LIMIT ? OFFSET ?", (limit, start)) - rows = cursor.fetchall() - conn.close() - playerList = [] - for row in rows: - playerList += [player.PlayerInLeaderboard(row)] + cursor = self.connPlayers.cursor() + cursor.execute("SELECT Count(*) FROM players") + count = cursor.fetchone()[0] + return count - return playerList + def getHistoricalForPlayerId(self, playerId): + '''Get historical data for a player''' -def findPlayerByName(database, playerName): - '''Find a player by his name (prefer fullmatch)''' + cursor = self.connHistorical.cursor() + cursor.execute("SELECT * FROM playerHistoricalData where id = ?", (playerId,)) + rows = cursor.fetchall() - conn = sqlite3.connect(DB_BASE.format(database), uri=True) - cursor = conn.cursor() - playerNamePrepared = "%{}%".format(playerName.replace("%", "%%")) - cursor.execute("SELECT * FROM players WHERE name == ?", (playerName,)) - row = cursor.fetchone() + PLAYER_ID = 0 + TIMESTAMP = 1 + MU = 2 + SIGMA = 3 - playerRow = None - if not playerRow: - cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,)) - playerRow = cursor.fetchone() + playerIdDict = dict() + for r in rows: + timestampDict = dict() + timestampDict.update({ "mu" : r[MU] }) + timestampDict.update({ "sigma" : r[SIGMA]}) + playerIdDict.update({ r[TIMESTAMP] : timestampDict }) + + retDict = { rows[0][PLAYER_ID] : playerIdDict } + return retDict + + def getPlayerById(self, playerId): + '''Get a player by his id''' + + cursor = self.connPlayers.cursor() + cursor.execute("SELECT * FROM players where playerId = ?", (playerId,)) + row = cursor.fetchone() + + if(row): + playerInLeaderboard = player.PlayerInLeaderboard(playerRow) + else: + playerInLeaderboard = None + + return row + + def getRankRange(self, start, end): + '''Get a range of players by rank''' + + cursor = self.connPlayers.cursor() + limit = end - start + sqlQuery = "Select * FROM players ORDER BY (mu - 2*sigma) DESC LIMIT ? OFFSET ?" + cursor.execute(sqlQuery, (limit, start)) + rows = cursor.fetchall() + playerList = [] + for row in rows: + playerList += [player.PlayerInLeaderboard(row)] + return playerList + + def findPlayerByName(self, playerName): + '''Find a player by his name (prefer fullmatch)''' + + cursor = self.connPlayers.cursor() + playerNamePrepared = "%{}%".format(playerName.replace("%", "%%")) + cursor.execute("SELECT * FROM players WHERE name == ?", (playerName,)) + row = cursor.fetchone() + + playerRow = None if not playerRow: - conn.close() - return (None, None) + cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,)) + playerRow = cursor.fetchone() + if not playerRow: + conn.close() + return (None, None) + + playerInLeaderboard = player.PlayerInLeaderboard(playerRow) + player.rank = getPlayerRank(playerInLeaderboard) + return playerInLeaderboard - playerInLeaderboard = player.PlayerInLeaderboard(playerRow) - # compte rank - cursor.execute("SELECT COUNT(*) from players where (mu-2*sigma) > (?-2*?);", - (playerInLeaderboard.mu, playerInLeaderboard.sigma)) - rank = cursor.fetchone()[0] - conn.close() - return (playerInLeaderboard, rank) + def getPlayerRank(self, player): + '''Calculate player rank - a player rank may change rapidly and + can't and shouldn't be used to identify a player''' + + cursor = connPlayers.cursor() + cursor.execute("SELECT COUNT(*) from players where (mu-2*sigma) > (?-2*?);", + (playerInLeaderboard.mu, playerInLeaderboard.sigma)) + rank = cursor.fetchone()[0] + return rank diff --git a/player.py b/player.py index 468282b..6139a6c 100644 --- a/player.py +++ b/player.py @@ -16,6 +16,7 @@ class PlayerInLeaderboard: self.games = int(games) self.wins = int(wins) self.loses = self.games - self.wins + self.rank = None # determine winratio # if self.games == 0: diff --git a/server.py b/server.py index 1a7faba..07cce52 100755 --- a/server.py +++ b/server.py @@ -4,9 +4,10 @@ import requests import argparse import flask_caching as fcache import json -import database as db import os +from database import DatabaseConnection + app = flask.Flask("open-leaderboard") @@ -18,6 +19,27 @@ cache.init_app(app) SEGMENT=100 +@app.route('/playerdata') +def playerInfo(): + '''API-Endpoint for Canvas Query''' + + playerId = flask.request.args.get("id") + + db = DatabaseConnection(app.config["DB_PATH"]) + data = db.getHistoricalForPlayerId(playerId) + + return json.dumps(data) + +@app.route("/player") +def player(): + '''Show Info about Player''' + + db = DatabaseConnection(app.config["DB_PATH"]) + player = db.getPlayerById(playerId) + player.rank = db.getPlayerRank(player) + + return flask.render_template("player.html", player=player) + @app.route('/leaderboard') @app.route('/') @cache.cached(timeout=600, query_string=True) @@ -27,7 +49,7 @@ def leaderboard(): # parse parameters # page = flask.request.args.get("page") playerName = flask.request.args.get("string") - + db = DatabaseConnection(app.config["DB_PATH"]) if page: start = SEGMENT * int(page) @@ -39,7 +61,7 @@ def leaderboard(): searchName = "" if playerName: - playerInLeaderboard, rank = db.findPlayerByName(app.config["DB_PATH"], playerName) + playerInLeaderboard, rank = db.findPlayerByName(playerName) if not playerInLeaderboard: cannotFindPlayer = flask.Markup("
No player of that name
") start = 0 @@ -51,14 +73,14 @@ def leaderboard(): # compute range # - maxEntry = db.getTotalPlayers(app.config["DB_PATH"]) + maxEntry = db.getTotalPlayers() reachedEnd = False if end > maxEntry: start = maxEntry - ( maxEntry % SEGMENT ) - 1 end = maxEntry - 1 reachedEnd = True - playerList = db.getRankRange(app.config["DB_PATH"], start, end) + playerList = db.getRankRange(start, end) columContent = flask.Markup(flask.render_template("playerLine.html", \ playerRank="Rank", \