From 6e677c663be21be3d93ba95fdced5bf61aac2adc Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Mon, 20 Dec 2021 19:07:55 +0100 Subject: [PATCH] cleanups --- MapSummary.py | 63 --------- Round.py | 60 --------- config.py | 1 - config.py.example | 2 - player.py | 49 ------- server.py | 336 ++++++++++------------------------------------ sqlite.init | 5 - 7 files changed, 68 insertions(+), 448 deletions(-) delete mode 100644 MapSummary.py delete mode 100644 Round.py delete mode 100644 config.py delete mode 100644 config.py.example delete mode 100644 player.py delete mode 100644 sqlite.init diff --git a/MapSummary.py b/MapSummary.py deleted file mode 100644 index c4d1ada..0000000 --- a/MapSummary.py +++ /dev/null @@ -1,63 +0,0 @@ -import json -import datetime -import player -import datetime - -class MapSummary: - def __init__(self, rounds): - '''Create a map MapSummary from a Round-Array''' - - self.securityWins = 0 - self.insurgentWins = 0 - self.times = [] - self.predictions = [] - self.totalGames = 0 - self.confidence = [] - self.mapName = None - - for r in rounds: - self.mapName = r.mapName - self.totalGames += 1 - if r.winnerSideString == "Insurgent": - self.insurgentWins += 1 - else: - self.securityWins += 1 - - self.predictions += [r.numericPrediction] - self.confidence += [r.confidence] - self.times += [r.duration] - - self.insurgentWinPercent = "" - self.securityWinPercent = "" - self.ratingSystemDeviation = "-" - self.averageTime = "" - - try: - self.insurgentWinPercent = self.insurgentWins / self.totalGames*100 - self.securityWinPercent = self.securityWins / self.totalGames*100 - averageSeconds = sum([t.total_seconds() for t in self.times]) / len(self.times) - self.averageTime = datetime.timedelta(seconds=int(averageSeconds)) - - mapper = [ 1 if x == 0 else -1 for x in self.predictions ] - reverseMapper = [ 1 if x == 0 else 0 for x in self.predictions ] - self.ratingSystemDeviation = 0 - - confidenceCutoff = 60 - confidenceTupels = list(filter(lambda x: x[1] > confidenceCutoff, - zip(reverseMapper, self.confidence))) - - mapperTupels = list(filter(lambda x: x[1] > confidenceCutoff, - zip(mapper, self.confidence))) - - for i in range(0, len(mapperTupels)): - self.ratingSystemDeviation += mapperTupels[i][0] * max(100, 50+mapperTupels[i][1]) - - self.ratingSystemDeviation /= len(mapperTupels) - self.predictionCorrectPercentage = sum([x[0] for x in confidenceTupels]) - self.predictionCorrectPercentage /= len(confidenceTupels) - self.predictionCorrectPercentage *= 100 - self.predictionCorrectPercentage = round(self.predictionCorrectPercentage) - - except ZeroDivisionError: - pass - diff --git a/Round.py b/Round.py deleted file mode 100644 index de66cde..0000000 --- a/Round.py +++ /dev/null @@ -1,60 +0,0 @@ -import json -import datetime -import player -import json -import os - -class Round: - def __init__(self, dbRow): - '''Create Round Object from cursor database row''' - - timestamp, winners, losers, winnerSide, mapName, duration, prediction, confidence = dbRow - startTime = datetime.datetime.fromtimestamp(int(float(timestamp))) - winnersParsed = json.loads(winners) - losersParsed = json.loads(losers) - - self.startTime = startTime - self.id = int(float(timestamp)) - self.winners = [ player.playerFromDict(wp, int(duration)) for wp in winnersParsed ] - self.losers = [ player.playerFromDict(lp, int(duration)) for lp in losersParsed ] - self.winnerSide = winnerSide - self.duration = datetime.timedelta(seconds=int(duration)) - - self.blacklist = False - blacklistNames = [] - blacklistFile = "blacklist.json" - if os.path.isfile(blacklistFile): - with open(blacklistFile) as f: - blacklistNames = json.load(f)["blacklist"] - - for name in blacklistNames: - for p in self.winners: - if p.name == name: - self.blacklist = True - for p in self.losers: - if p.name == name: - self.blacklist = True - - - if winnerSide == 1: - self.winnerSideString = "Red" - self.loserSideString = "Blue" - else: - self.winnerSideString = "Blue" - self.loserSideString = "Red" - if mapName: - self.mapName = mapName - else: - self.mapName = "unavailiable" - - self.numericPrediction = prediction - self.confidence = (int(confidence * 100) - 50)*2 - self.quality = int(150 - self.confidence) - if self.confidence < 50: - self.prediction = "-" - elif prediction == 0: - self.prediction = self.winnerSideString - elif prediction == 1: - self.prediction = self.loserSideString - else: - self.prediction = "Error" diff --git a/config.py b/config.py deleted file mode 100644 index 2a478d7..0000000 --- a/config.py +++ /dev/null @@ -1 +0,0 @@ -DB_PATH="/home/sheppy-gaming/insurgency-skillbird/python/" diff --git a/config.py.example b/config.py.example deleted file mode 100644 index fafdb74..0000000 --- a/config.py.example +++ /dev/null @@ -1,2 +0,0 @@ -# rename to config.py and remove this line for this file to have effect -DB_PATH="players.sqlite" diff --git a/player.py b/player.py deleted file mode 100644 index e9f6293..0000000 --- a/player.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/python3 -import flask - -def playerFromDict(d, duration): - p = PlayerInLeaderboard([d["id"], d["name"], None, 0, 0, 0, 0]) - p.participation = min(int(d["active_time"]/duration*100), 100) - return p - -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) - 2*int(self.sigma) - self.ratingStr = str(self.rating) - self.games = int(games) - self.wins = int(wins) - self.loses = self.games - self.wins - self.rank = None - self.lastGame = lastGame - self.participation = -1 - - self.muChange = None - self.sigmaChange = None - self.ratingChangeString = "N/A" - - # 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 917f9e2..2515f7a 100755 --- a/server.py +++ b/server.py @@ -3,18 +3,14 @@ import flask import requests import argparse import datetime -import flask_caching as fcache import itertools import json import os -import MapSummary import random import secrets import riotwatcher import time import statistics - -from database import DatabaseConnection import api @@ -23,152 +19,90 @@ app = flask.Flask("open-leaderboard") WATCHER = None KEY = None - if os.path.isfile("config.py"): app.config.from_object("config") -cache = fcache.Cache(app, config={'CACHE_TYPE': 'simple'}) -cache.init_app(app) - SEGMENT=100 SERVERS=list() +from sqlalchemy import Column, Integer, String, Boolean, or_, and_ +from sqlalchemy.orm import sessionmaker +from sqlalchemy.exc import IntegrityError +from sqlalchemy.sql import func +import sqlalchemy +from flask_sqlalchemy import SQLAlchemy +db = SQLAlchemy(app) -def prettifyMinMaxY(computedMin, computedMax): - if computedMax > 0 and computedMin > 0: - return (0, 4000) - else: - return (computedMin - 100, 4000) +POSITIONS_NAMES = ["Top", "Jungle", "Mid", "Bottom", "Support" ] +DATABASE_PRIO_NAMES = ["prioTop", "prioJungle", "prioMid", "prioBot", "prioSupport" ] +TYPE_JSON = 'application/json' -@app.route("/players-online") -def playersOnline(): - '''Calc and return the online players''' +HTTP_OK = 200 - playerTotal = 0 - error = "" +class PlayerInDatabase(db.Model): + __tablename__ = "players" + player = Column(String, primary_key=True) + rating = Column(Integer) + ratingFix = Column(Integer) + lastUpdated = Column(Integer) - for s in SERVERS: - try: - with valve.source.a2s.ServerQuerier((args.host, args.port)) as server: - playerTotal += int(server.info()["player_count"]) - except NoResponseError: - error = "Server Unreachable" - except Exception as e: - error = str(e) - - retDict = { "player_total" : playerTotal, "error" : error } - return flask.Response(json.dumps(retDict), 200, mimetype='application/json') - - - -@app.route("/round-info") -def singleRound(): - '''Display info about a single round itdentified by it's timestamp''' - - timestamp = flask.request.args.get("id") - if not timestamp: - return ("ID Missing", 404) - if not timestamp.endswith(".0"): - timestamp = timestamp + ".0" - db = DatabaseConnection(app.config["DB_PATH"]) - r = db.getRoundByTimestamp(timestamp) - if not r: - return ("Round not found", 404) - elif r.blacklist: - return ("Unavailable due to pending GDPR deletion request", 451) - r = db.calcRatingChanges(r) - - if not r: - return ("", 404) - - r.winners = sorted(r.winners, key=lambda p: p.participation, reverse=True) - r.losers = sorted(r.losers, key=lambda p: p.participation, reverse=True) - - return flask.render_template("single_round.html", r=r) - -@app.route("/livegames") -def liveGames(): - '''Display info about a single round itdentified by it's timestamp''' - - db = DatabaseConnection(app.config["DB_PATH"]) - rounds = db.getLiveGames() - return flask.render_template("livegames.html", liveGameRounds=rounds, noRounds=not bool(rounds)) - -@app.route("/rounds-by-timestamp") -@app.route("/rounds") -def rounds(): - '''Show rounds played on the server''' - - start = flask.request.args.get("start") - end = flask.request.args.get("end") - - if not start or not end: - start = datetime.datetime.now() - datetime.timedelta(days=365) - end = datetime.datetime.now() - else: - start = datetime.datetime.fromtimestamp(start) - end = datetime.datetime.fromtimestamp(end) - - db = DatabaseConnection(app.config["DB_PATH"]) - rounds = db.roundsBetweenDates(start, end) - - return flask.render_template("rounds.html", rounds=rounds) - - - # get timestamp - # display players - # display rating change - # display outcome - # display map +class Submission(db.Model): + __tablename__ = "submissions" + ident = Column(String, primary_key=True) + submissionId = Column(String) + player = Column(String) + prioTop = Column(Integer) + prioJungle = Column(Integer) + prioMid = Column(Integer) + prioBot = Column(Integer) + prioSupport = Column(Integer) class Player: def __init__(self, name, prio): - self.name = name - self.prio = prio + self.name = name + self.prio = prio -# TODO -submission = dict() @app.route("/role-submission", methods=['GET', 'POST']) def roleSubmissionTool(): - positions=["Top", "Jungle", "Mid", "Bottom", "Support" ] - ident = flask.request.args.get("id") + submissionId = flask.request.args.get("id") + player = flask.request.args.get("player") + if flask.request.method == 'POST': - if not ident in submission: - submission.update({ ident : [] }) - - tmp = dict() - tmp.update({ "name" : flask.request.form["playername"] }) - for p in positions: - tmp.update({ p : flask.request.form["prio_{}".format(p)] }) + submissionQuery = db.session.query(PlayerInDatabase) + identKey = "{}-{}".format(submissionId, player) + submission = submissionQuery.filter(PlayerInDatabase.ident == identKey).first() - existed = False - for pl in submission[ident]: - if pl["name"] == tmp["name"]: - for p in positions: - pl.update({ p : flask.request.form["prio_{}".format(p)] }) - existed = True - break; + if not submission: + submission = Submission(identKey, submissionId, player, -1, -1, -1, -1, -1) - if not existed: - submission[ident] += [tmp] + for i in range(0, 5): + formKey = "prio_" + POSITIONS_NAMES[i] + setattr(submission, DATABASE_PRIO_NAMES[i], flask.request.form[formKey]) - return flask.redirect("/balance-tool?id={}".format(ident)) + db.session.merge(submission) + db.session.commit() + + return flask.redirect("/balance-tool?id=" + ident) else: - return flask.render_template("role_submission.html", - positions=positions, - ident=ident) + return flask.render_template("role_submission.html", positions=positions, ident=ident) @app.route("/balance-tool-data") def balanceToolData(): - ident = flask.request.args.get("id") - retDict = dict() - if not ident in submission: - return flask.Response(json.dumps({ "no-data" : False }), 200, mimetype='application/json') - retDict.update({ "submissions" : submission[ident] }) - return flask.Response(json.dumps(retDict), 200, mimetype='application/json') + + submissionId = flask.request.args.get("id") + submissionsQuery = db.session.query(PlayerInDatabase) + submissions = submissionsQuery.filter(PlayerInDatabase.submissionId == submissionId).all() + + if not submissions: + return flask.Response(json.dumps({ "no-data" : False }), HTTP_OK, mimetype=TYPE_JSON) + + retDict.update() + + dicts = [ s.toDict() for s in submissions ] + return flask.Response(json.dumps({ "submissions" : dicts }), HTTP_OK, mimetype=TYPE_JSON) @app.route('/') @@ -323,9 +257,6 @@ def balanceTool(): positions=positions, sides=["left", "right"], ident=ident) -@app.route("/get-cache") -def getCacheLoc(): - return (json.dumps(api.getCache()), 200) @app.route("/player-api") def playerApi(): @@ -343,140 +274,9 @@ def playerApiCache(): else: return ("Nope", 404) -@app.route("/player") -def player(): - '''Show Info about Player''' - - playerId = flask.request.args.get("id") - if(not playerId): - return ("", 404) - - db = DatabaseConnection(app.config["DB_PATH"]) - player = db.getPlayerById(playerId) - - if(not player): - return ("", 404) - - player.rank = db.getPlayerRank(player) - histData = db.getHistoricalForPlayerId(playerId) - - csv_month_year = [] - csv_ratings = [] - csv_timestamps = [] - - minRating = 3000 - maxRating = 0 - - if histData: - datapoints = histData[playerId] - if datapoints: - - tickCounter = 10 - for dpk in datapoints.keys(): - t = datetime.datetime.fromtimestamp(int(float(dpk))) - tsMs = str(int(t.timestamp() * 1000)) - ratingString = str(int(datapoints[dpk]["mu"]) - 2*int(datapoints[dpk]["sigma"])) - ratingAmored = '{ x : ' + tsMs + ', y : ' + ratingString + '}' - csv_timestamps += [str(tsMs)] - csv_ratings += [ratingAmored] - - tickCounter -= 1 - if tickCounter <= 0: - tickCounter = 10 - csv_month_year += ['new Date({})'.format(tsMs)] - - minRating = min(minRating, int(ratingString)) - maxRating = max(maxRating, int(ratingString)) - - yMin, yMax = prettifyMinMaxY(minRating, maxRating) - - # change displayed rank to start from 1 :) - player.rank += 1 - - return flask.render_template("player.html", player=player, CSV_RATINGS=",".join(csv_ratings), - CSV_MONTH_YEAR_OF_RATINGS=",".join(csv_month_year), - CSV_TIMESTAMPS=csv_timestamps, - Y_MIN=yMin, Y_MAX=yMax) - -@app.route('/leaderboard') -@cache.cached(timeout=10, query_string=True) -def leaderboard(): - '''Show main leaderboard page with range dependant on parameters''' - - # parse parameters # - page = flask.request.args.get("page") - playerName = flask.request.args.get("string") - db = DatabaseConnection(app.config["DB_PATH"]) - - if page: - pageInt = int(page) - if pageInt < 0: - pageInt = 0 - start = SEGMENT * int(page) - else: - pageInt = 0 - start = 0 - - # handle find player request # - cannotFindPlayer = "" - searchName = "" - - playerList = None - doNotComputeRank = True - if playerName: - playersInLeaderboard = db.findPlayerByName(playerName) - if not playersInLeaderboard: - cannotFindPlayer = flask.Markup("
No player of that name
") - start = 0 - else: - if len(playersInLeaderboard) == 1: - rank = playersInLeaderboard[0].rank - if(playersInLeaderboard[0].games < 10): - return flask.redirect("/player?id={}".format(playersInLeaderboard[0].playerId)) - searchName = playersInLeaderboard[0].name - start = rank - (rank % SEGMENT) - else: - playerList = playersInLeaderboard - for p in playerList: - if p.rank == -1: - p.rankStr = "N/A" - else: - p.rankStr = str(p.rank) - doNotComputeRank = False - - reachedEnd = False - maxEntry = 0 - if not playerList: - # compute range # - end = start + SEGMENT - maxEntry = db.getTotalPlayers() - reachedEnd = False - if end > maxEntry: - start = maxEntry - ( maxEntry % SEGMENT ) - 1 - end = maxEntry - 1 - print(maxEntry) - reachedEnd = True - - playerList = db.getRankRange(start, end) - - endOfBoardIndicator = "" - if reachedEnd: - endOfBoardHtml = "
- - - End of Board - - -
" - endOfBoardIndicator = flask.Markup(endOfBoardHtml) - - # fix <100 player start at 0 # - if maxEntry <= 100: - start = max(start, 0) - - finalResponse = flask.render_template("base.html", playerList=playerList, \ - doNotComputeRank=doNotComputeRank, \ - start=start, \ - endOfBoardIndicator=endOfBoardIndicator, \ - findPlayer=cannotFindPlayer, \ - searchName=searchName, - nextPageNumber=int(pageInt)+1, - prevPageNumber=int(pageInt)-1) - return finalResponse +@app.route("/get-cache") +def getCacheLoc(): + return (json.dumps(api.getCache()), 200) @app.route('/static/') def send_js(path): @@ -486,22 +286,22 @@ def send_js(path): def init(): global WATCHER + + app.config["DB"] = db + db.create_all() + with open("key.txt","r") as f: key = f.read().strip() WATCHER = riotwatcher.LolWatcher(key) if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Start open-leaderboard', \ formatter_class=argparse.ArgumentDefaultsHelpFormatter) - 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=False, help='skillbird database (overrides web connection if set)') - + parser.add_argument('--interface', default="localhost") + parser.add_argument('--port', default="5002") args = parser.parse_args() - app.config["DB_PATH"] = args.skillbird_db + app.config["TEMPLATES_AUTO_RELOAD"] = True app.run(host=args.interface, port=args.port) diff --git a/sqlite.init b/sqlite.init deleted file mode 100644 index d56cfc3..0000000 --- a/sqlite.init +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE players( - playerName TEXT PRIMARY KEY, - rating INTEGER, - lastupdated TEXT -);