implement basic db support for player stats

This commit is contained in:
Yannik Schmidt
2020-09-26 23:55:53 +02:00
parent 34e9a4dbf6
commit 3f3e137fda
3 changed files with 125 additions and 46 deletions

View File

@@ -2,56 +2,112 @@
import json import json
import sqlite3 import sqlite3
import player import player
import os
DB_BASE = "file:{}?mode=ro" DATABASE_PLAYERS = "players.sqlite"
DATABASE_ROUNDS = "rounds.sqlite"
DATABASE_HISTORICAL = "players.sqlite"
def getTotalPlayers(database): class DatabaseConnection:
'''Get the total number of players in the database''' 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) self.connPlayers = sqlite3.connect(self.databasePlayers, uri=True)
cursor = conn.cursor() self.connRounds = sqlite3.connect(self.databaseRounds, uri=True)
cursor.execute("SELECT Count(*) FROM players") self.connHistorical = sqlite3.connect(self.databaseHistorical, uri=True)
count = cursor.fetchone()[0]
conn.close()
return count
def __del__(self):
self.connPlayers.close();
self.connRounds.close();
self.connHistorical.close();
def getRankRange(database, start, end): def getTotalPlayers(self):
'''Get a range of players by rank''' '''Get the total number of players in the database'''
conn = sqlite3.connect(DB_BASE.format(database), uri=True) cursor = self.connPlayers.cursor()
cursor = conn.cursor() cursor.execute("SELECT Count(*) FROM players")
limit = end - start count = cursor.fetchone()[0]
cursor.execute("Select * FROM players ORDER BY (mu - 2*sigma) DESC LIMIT ? OFFSET ?", (limit, start)) return count
rows = cursor.fetchall()
conn.close()
playerList = []
for row in rows:
playerList += [player.PlayerInLeaderboard(row)]
return playerList def getHistoricalForPlayerId(self, playerId):
'''Get historical data for a player'''
def findPlayerByName(database, playerName): cursor = self.connHistorical.cursor()
'''Find a player by his name (prefer fullmatch)''' cursor.execute("SELECT * FROM playerHistoricalData where id = ?", (playerId,))
rows = cursor.fetchall()
conn = sqlite3.connect(DB_BASE.format(database), uri=True) PLAYER_ID = 0
cursor = conn.cursor() TIMESTAMP = 1
playerNamePrepared = "%{}%".format(playerName.replace("%", "%%")) MU = 2
cursor.execute("SELECT * FROM players WHERE name == ?", (playerName,)) SIGMA = 3
row = cursor.fetchone()
playerRow = None playerIdDict = dict()
if not playerRow: for r in rows:
cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,)) timestampDict = dict()
playerRow = cursor.fetchone() 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: if not playerRow:
conn.close() cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,))
return (None, None) 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) def getPlayerRank(self, player):
# compte rank '''Calculate player rank - a player rank may change rapidly and
cursor.execute("SELECT COUNT(*) from players where (mu-2*sigma) > (?-2*?);", can't and shouldn't be used to identify a player'''
(playerInLeaderboard.mu, playerInLeaderboard.sigma))
rank = cursor.fetchone()[0] cursor = connPlayers.cursor()
conn.close() cursor.execute("SELECT COUNT(*) from players where (mu-2*sigma) > (?-2*?);",
return (playerInLeaderboard, rank) (playerInLeaderboard.mu, playerInLeaderboard.sigma))
rank = cursor.fetchone()[0]
return rank

View File

@@ -16,6 +16,7 @@ class PlayerInLeaderboard:
self.games = int(games) self.games = int(games)
self.wins = int(wins) self.wins = int(wins)
self.loses = self.games - self.wins self.loses = self.games - self.wins
self.rank = None
# determine winratio # # determine winratio #
if self.games == 0: if self.games == 0:

View File

@@ -4,9 +4,10 @@ import requests
import argparse import argparse
import flask_caching as fcache import flask_caching as fcache
import json import json
import database as db
import os import os
from database import DatabaseConnection
app = flask.Flask("open-leaderboard") app = flask.Flask("open-leaderboard")
@@ -18,6 +19,27 @@ cache.init_app(app)
SEGMENT=100 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('/leaderboard')
@app.route('/') @app.route('/')
@cache.cached(timeout=600, query_string=True) @cache.cached(timeout=600, query_string=True)
@@ -27,7 +49,7 @@ def leaderboard():
# parse parameters # # parse parameters #
page = flask.request.args.get("page") page = flask.request.args.get("page")
playerName = flask.request.args.get("string") playerName = flask.request.args.get("string")
db = DatabaseConnection(app.config["DB_PATH"])
if page: if page:
start = SEGMENT * int(page) start = SEGMENT * int(page)
@@ -39,7 +61,7 @@ def leaderboard():
searchName = "" searchName = ""
if playerName: if playerName:
playerInLeaderboard, rank = db.findPlayerByName(app.config["DB_PATH"], playerName) playerInLeaderboard, rank = db.findPlayerByName(playerName)
if not playerInLeaderboard: if not playerInLeaderboard:
cannotFindPlayer = flask.Markup("<div class=noPlayerFound>No player of that name</div>") cannotFindPlayer = flask.Markup("<div class=noPlayerFound>No player of that name</div>")
start = 0 start = 0
@@ -51,14 +73,14 @@ def leaderboard():
# compute range # # compute range #
maxEntry = db.getTotalPlayers(app.config["DB_PATH"]) maxEntry = db.getTotalPlayers()
reachedEnd = False reachedEnd = False
if end > maxEntry: if end > maxEntry:
start = maxEntry - ( maxEntry % SEGMENT ) - 1 start = maxEntry - ( maxEntry % SEGMENT ) - 1
end = maxEntry - 1 end = maxEntry - 1
reachedEnd = True reachedEnd = True
playerList = db.getRankRange(app.config["DB_PATH"], start, end) playerList = db.getRankRange(start, end)
columContent = flask.Markup(flask.render_template("playerLine.html", \ columContent = flask.Markup(flask.render_template("playerLine.html", \
playerRank="Rank", \ playerRank="Rank", \