diff --git a/database.py b/database.py new file mode 100644 index 0000000..9315b1a --- /dev/null +++ b/database.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +import json +import sqlite3 +import player + +DB_BASE = "file:{}?mode=ro" + +def getTotalPlayers(database): + '''Get the total number of players in the database''' + + print(DB_BASE.format(database)) + 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 + + +def getRankRange(database, start, end): + '''Get a range of players by rank''' + + 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)] + + return playerList + +def findPlayerByName(playerName): + '''Find a player by his name (prefer fullmatch)''' + + 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.fetone() + + playerRow = None + if row: + cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,)) + row = cursor.fetchone()[0] + if not row: + conn.close() + return None + + conn.close() + return players.Leaderboard(playerRow) diff --git a/player.py b/player.py new file mode 100644 index 0000000..e0756cb --- /dev/null +++ b/player.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +import flask + +class PlayerInLeaderboard: + def __init__(self, dbRow): + '''Initialize a player object later to be serialized to HTML''' + + playerID, name, lastGame, wins, mu, sigma, games = dbRow + + # set relevant values # + self.name = name + self.playerID = playerID + self.mu = mu + self.sigma = sigma + self.rating = int(self.mu) - int(self.sigma) + self.games = int(games) + self.wins = int(wins) + self.loses = self.games - self.wins + + # determine winratio # + if self.games == 0: + self.winratio = "N/A" + else: + self.winratio = str(int(self.wins/self.games * 100)) + + def getLineHTML(self, rank): + '''Build a single line for a specific player in the leaderboard''' + + string = flask.render_template("playerLine.html", \ + playerRank = rank, \ + playerName = self.name, \ + playerRating = self.rating, \ + playerGames = self.games, \ + playerWinratio = self.winratio) + + return flask.Markup(string) diff --git a/server.py b/server.py index 5569763..cedc528 100755 --- a/server.py +++ b/server.py @@ -4,33 +4,20 @@ import requests import argparse import flask_caching as fcache import json +import database as db app = flask.Flask("open-leaderboard") cache = fcache.Cache(app, config={'CACHE_TYPE': 'simple'}) cache.init_app(app) -SERVER = "localhost:5001" -LOCATION = "/rankrange" -PARAM_START = "start" -PARAM_END = "end" +SEGMENT=100 -BASE_URL = "http://{server}{path}?{paramStart}={start}&{paramEnd}={end}" -MAX_ENTRY = "http://{server}/getmaxentries" -FIND_PLAYER = "http://{server}/getplayer?name={pname}" -SEGMENT = 100 -SEPERATOR = ',' - -class Player: - def __init__(self, line): +class PlayerInLeaderboard: + def __init__(self, dbRow): '''Initialize a player object later to be serialized to HTML''' - # parse input line # - try: - name, playerID, rating, games, wins = line.split(SEPERATOR) - except ValueError as e: - print("Failed to parse line: {}".format(line)) - raise e + name, playerID, rating, games, wins = dbRow # set relevant values # self.name = name @@ -58,21 +45,6 @@ class Player: # mark returned string as preformated html # return flask.Markup(string) - -def requestRange(start, end): - '''Request a range from the rating server''' - - start = max(start, 0) - - # request information from rating server # - requestURL = BASE_URL.format(server=SERVER, \ - path=LOCATION, \ - paramStart=PARAM_START, \ - paramEnd=PARAM_END, \ - start=start, \ - end=end) - - return str(requests.get(requestURL).content, "utf-8") @app.route('/leaderboard') @app.route('/') @@ -109,24 +81,15 @@ def leaderboard(): end = start + SEGMENT - # request and check if we are within range # - maxEntryUrl = MAX_ENTRY.format(server=SERVER) - maxEntry = int(requests.get(maxEntryUrl).content) + # compute range # + maxEntry = db.getTotalPlayers(app.config["DB_PATH"]) reachedEnd = False if end > maxEntry: start = maxEntry - ( maxEntry % SEGMENT ) - 1 end = maxEntry - 1 reachedEnd = True - # do the actual request # - responseString = requestRange(start, end) - - # create relevant html-lines from player - players = [Player(line) for line in responseString.split("\n")] - - # sanity check reponse # - if len(players) > 100: - raise ValueError("Bad reponse from rating server") + playerList = db.getRankRange(app.config["DB_PATH"], start, end) columContent = flask.Markup(flask.render_template("playerLine.html", \ playerRank="Rank", \ @@ -143,8 +106,9 @@ def leaderboard(): # fix <100 player start at 0 # if maxEntry <= 100: start = max(start, 0) - - finalResponse = flask.render_template("base.html", playerList=players, \ + + print(playerList) + finalResponse = flask.render_template("base.html", playerList=playerList, \ columNames=columContent, \ start=start, \ endOfBoardIndicator=endOfBoardIndicator, \ @@ -163,23 +127,14 @@ def init(): if __name__ == "__main__": parser = argparse.ArgumentParser(description='Start open-leaderboard', \ formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('--rating-server', default=SERVER, \ - help='Compatible rating server to query') - parser.add_argument('--request-url', default=LOCATION, \ - help='API location for rating range') - parser.add_argument('--param-start', default=PARAM_START, \ - help='Name of parameter annotating the start of the rating range') - parser.add_argument('--param-end', default=PARAM_END, \ - help='Name of parameter annotating the end of the rating range') parser.add_argument('--interface', default="localhost", \ help='Interface on which flask (this server) will take requests on') parser.add_argument('--port', default="5002", \ help='Port on which flask (this server) will take requests on') + + parser.add_argument('--skillbird-db', required=True, help='skillbird database (overrides web connection if set)') + + args = parser.parse_args() - - SERVER = args.rating_server - LOCATION = args.request_url - PARAM_START = args.param_start - PARAM_END = args.param_end - + app.config["DB_PATH"] = args.skillbird_db app.run(host=args.interface, port=args.port)