#!/usr/bin/python3 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 from database import DatabaseConnection app = flask.Flask("open-leaderboard") 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() def prettifyMinMaxY(computedMin, computedMax): if computedMax > 0 and computedMin > 0: return (0, 4000) else: return (computedMin - 100, 4000) @app.route("/players-online") def playersOnline(): '''Calc and return the online players''' playerTotal = 0 error = "" 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("/maps") def maps(): '''Show an overview of maps''' db = DatabaseConnection(app.config["DB_PATH"]) start = datetime.datetime.now() - datetime.timedelta(days=4000) end = datetime.datetime.now() rounds = db.roundsBetweenDates(start, end) distinctMaps = db.distinctMaps() maps = [] for mapName in [ tupel[0] for tupel in distinctMaps]: roundsWithMap = list(filter(lambda r: r.mapName == mapName , rounds)) maps += [MapSummary.MapSummary(roundsWithMap)] allMaps = MapSummary.MapSummary(rounds) allMaps.mapName = "All Maps*" maps += [allMaps] mapsFiltered = filter(lambda x: x.mapName, maps) return flask.render_template("maps.html", maps=mapsFiltered) @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=7) 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 Player: def __init__(self, name, prio): self.name = name self.prio = prio # TODO submission = dict() @app.route("/role-submission", methods=['GET', 'POST']) def roleSubmissionTool(): positions=["Top", "Jungle", "Mid", "Support", "Bottom"] ident = flask.request.args.get("id") 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)] }) 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 existed: submission[ident] += [tmp] return flask.redirect("/balance-tool?id={}".format(ident)) else: 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') @app.route("/balance-tool", methods=['GET', 'POST']) def balanceTool(): positions=["Top", "Jungle", "Mid", "Support", "Bottom"] if flask.request.method == 'POST': players = [] for k,v in flask.request.json.items(): for i in range(5): if v[i] in positions: v[i] = 5 else: v[i] = int(v[i]) players += [Player(k, v)] # theoretical minnimum # theoMin = sum([ min(p.prio) for p in players ]) permutations = itertools.permutations(players) best = 100 bestOption = None for option in permutations: cur = 0 for i in range(len(option)): cur += option[i].prio[i%5] if cur < best: best = cur bestOption = option print(cur) retDict = { "left" : {}, "right" : {} } bestOption = list(bestOption) if len(bestOption) < 10: for x in range(10-len(bestOption)): bestOption += [Player("", [0,0,0,0,0])] for i in range(5): retDict["left"].update( { positions[i] : bestOption[i].name }) retDict["right"].update({ positions[i] : bestOption[i+5].name }) renderContent = flask.render_template("balance_response_partial.html", d=retDict, requests=flask.request.json, positions=positions, quality=int(theoMin/best*100)) return flask.Response( json.dumps({ "content": renderContent }), 200, mimetype='application/json') else: givenIdent = flask.request.args.get("id") if givenIdent: ident = givenIdent else: ident = secrets.token_urlsafe(16) return flask.render_template("json_builder.html", positions=positions, sides=["left", "right"], ident=ident) @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') @app.route('/') @cache.cached(timeout=600, 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("