mirror of
https://github.com/FAUSheppy/open-web-leaderboard.git
synced 2025-12-06 15:11:35 +01:00
Merge branch 'master' of github.com:FAUSheppy/open-web-leaderboard
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
*.swp
|
*.swp
|
||||||
|
*.sqlite
|
||||||
config.py
|
config.py
|
||||||
*.txt
|
*.txt
|
||||||
*.ncsv
|
*.ncsv
|
||||||
@@ -11,3 +12,5 @@ __pychache__/
|
|||||||
build/
|
build/
|
||||||
configparse_wrapper/
|
configparse_wrapper/
|
||||||
static/bootstrap/
|
static/bootstrap/
|
||||||
|
static/moment.js
|
||||||
|
*.exe
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ The system was developed to be used with the [skillbird-framwork](https://github
|
|||||||
- [MDB Jquery](https://mdbootstrap.com/docs/jquery/getting-started/download/) (unpack to ./static/bootstrap/)
|
- [MDB Jquery](https://mdbootstrap.com/docs/jquery/getting-started/download/) (unpack to ./static/bootstrap/)
|
||||||
- [Fontawesome](https://fontawesome.com/download) (move to static/boostrap/fontawesome.css)
|
- [Fontawesome](https://fontawesome.com/download) (move to static/boostrap/fontawesome.css)
|
||||||
- ``python3 -m pip install -r req.txt``
|
- ``python3 -m pip install -r req.txt``
|
||||||
|
- [Moment.js](https://momentjs.com/downloads/moment.js) (directly into static/)
|
||||||
|
|
||||||
|
|
||||||
# How to run
|
# How to run
|
||||||
|
|||||||
12
Round.py
12
Round.py
@@ -12,8 +12,9 @@ class Round:
|
|||||||
losersParsed = json.loads(losers)
|
losersParsed = json.loads(losers)
|
||||||
|
|
||||||
self.startTime = startTime
|
self.startTime = startTime
|
||||||
self.winners = [ player.playerFromDict(wp) for wp in winnersParsed ]
|
self.id = int(float(timestamp))
|
||||||
self.losers = [ player.playerFromDict(lp) for lp in losersParsed ]
|
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.winnerSide = winnerSide
|
||||||
self.duration = datetime.timedelta(seconds=int(duration))
|
self.duration = datetime.timedelta(seconds=int(duration))
|
||||||
|
|
||||||
@@ -28,9 +29,12 @@ class Round:
|
|||||||
else:
|
else:
|
||||||
self.mapName = "unavailiable"
|
self.mapName = "unavailiable"
|
||||||
|
|
||||||
self.confidence = int(confidence * 100)
|
|
||||||
self.numericPrediction = prediction
|
self.numericPrediction = prediction
|
||||||
if prediction == 0:
|
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
|
self.prediction = self.winnerSideString
|
||||||
elif prediction == 1:
|
elif prediction == 1:
|
||||||
self.prediction = self.loserSideString
|
self.prediction = self.loserSideString
|
||||||
|
|||||||
34
database.py
34
database.py
@@ -3,6 +3,7 @@ import json
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import player
|
import player
|
||||||
import os
|
import os
|
||||||
|
import datetime
|
||||||
import Round
|
import Round
|
||||||
|
|
||||||
DATABASE_PLAYERS = "players.sqlite"
|
DATABASE_PLAYERS = "players.sqlite"
|
||||||
@@ -101,8 +102,7 @@ class DatabaseConnection:
|
|||||||
cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,))
|
cursor.execute("SELECT * FROM players WHERE name LIKE ?", (playerNamePrepared,))
|
||||||
playerRow = cursor.fetchone()
|
playerRow = cursor.fetchone()
|
||||||
if not playerRow:
|
if not playerRow:
|
||||||
conn.close()
|
return None
|
||||||
return (None, None)
|
|
||||||
|
|
||||||
playerInLeaderboard = player.PlayerInLeaderboard(playerRow)
|
playerInLeaderboard = player.PlayerInLeaderboard(playerRow)
|
||||||
playerInLeaderboard.rank = self.getPlayerRank(playerInLeaderboard)
|
playerInLeaderboard.rank = self.getPlayerRank(playerInLeaderboard)
|
||||||
@@ -127,6 +127,7 @@ class DatabaseConnection:
|
|||||||
|
|
||||||
cursor = self.connRounds.cursor()
|
cursor = self.connRounds.cursor()
|
||||||
cursor.execute('''SELECT * FROM rounds WHERE timestamp between ? and ?
|
cursor.execute('''SELECT * FROM rounds WHERE timestamp between ? and ?
|
||||||
|
AND duration > 120.0
|
||||||
order by timestamp DESC''', (start.timestamp(), end.timestamp()))
|
order by timestamp DESC''', (start.timestamp(), end.timestamp()))
|
||||||
|
|
||||||
rounds = []
|
rounds = []
|
||||||
@@ -139,8 +140,16 @@ class DatabaseConnection:
|
|||||||
|
|
||||||
cursorHist = self.connHistorical.cursor()
|
cursorHist = self.connHistorical.cursor()
|
||||||
for p in roundObj.winners + roundObj.losers:
|
for p in roundObj.winners + roundObj.losers:
|
||||||
|
cursorHist.execute('''SELECT count(*) FROM playerHistoricalData
|
||||||
|
WHERE timestamp < ? AND id = ?''',
|
||||||
|
(roundObj.startTime.timestamp(), p.playerId))
|
||||||
|
|
||||||
|
if(cursorHist.fetchone()[0] < 10):
|
||||||
|
p.ratingChangeString = "Placements"
|
||||||
|
continue
|
||||||
|
|
||||||
cursorHist.execute('''SELECT mu,sima FROM playerHistoricalData
|
cursorHist.execute('''SELECT mu,sima FROM playerHistoricalData
|
||||||
WHERE timestamp < ? AND id = ? LIMIT 1 ''',
|
WHERE timestamp < ? AND id = ? order by timestamp DESC LIMIT 1 ''',
|
||||||
(roundObj.startTime.timestamp(), p.playerId))
|
(roundObj.startTime.timestamp(), p.playerId))
|
||||||
tupelPrev = cursorHist.fetchone()
|
tupelPrev = cursorHist.fetchone()
|
||||||
cursorHist.execute('''SELECT mu,sima FROM playerHistoricalData
|
cursorHist.execute('''SELECT mu,sima FROM playerHistoricalData
|
||||||
@@ -154,7 +163,24 @@ class DatabaseConnection:
|
|||||||
p.sigma = sigmaPrev
|
p.sigma = sigmaPrev
|
||||||
p.muChange = muAfter - muPrev
|
p.muChange = muAfter - muPrev
|
||||||
p.sigmaChange = sigmaAfter - sigmaPrev
|
p.sigmaChange = sigmaAfter - sigmaPrev
|
||||||
p.ratingChangeString = str( ( muAfter-2*sigmaAfter ) - ( muPrev-2*sigmaPrev) )
|
ratingChange = int( (muAfter-muPrev) - 2*(sigmaAfter-sigmaPrev) )
|
||||||
|
if abs(ratingChange) > 500:
|
||||||
|
p.ratingChangeString = "N/A"
|
||||||
|
continue
|
||||||
|
if(ratingChange < 0):
|
||||||
|
p.ratingChangeString = "- {:x>5}".format(abs(ratingChange))
|
||||||
|
else:
|
||||||
|
p.ratingChangeString = "+ {:x>5}".format(ratingChange)
|
||||||
|
p.ratingChangeString = p.ratingChangeString.replace("x", " ")
|
||||||
|
|
||||||
|
roundObj.invalid = ""
|
||||||
|
roundObj.teamPtRatio = 0
|
||||||
|
if roundObj.teamPtRatio > 2.1:
|
||||||
|
roundObj.invalid += "Not rated because of playtime imbalance."
|
||||||
|
if roundObj.duration < datetime.timedelta(seconds=120):
|
||||||
|
if roundObj.invalid:
|
||||||
|
roundObj.invalid += "<br>"
|
||||||
|
roundObj.invalid += "Not rated because too short."
|
||||||
|
|
||||||
return roundObj
|
return roundObj
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
def playerFromDict(d):
|
def playerFromDict(d, duration):
|
||||||
return PlayerInLeaderboard([d["id"], d["name"], None, 0, 0, 0, 0])
|
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:
|
class PlayerInLeaderboard:
|
||||||
def __init__(self, dbRow):
|
def __init__(self, dbRow):
|
||||||
@@ -21,10 +23,11 @@ class PlayerInLeaderboard:
|
|||||||
self.loses = self.games - self.wins
|
self.loses = self.games - self.wins
|
||||||
self.rank = None
|
self.rank = None
|
||||||
self.lastGame = lastGame
|
self.lastGame = lastGame
|
||||||
|
self.participation = -1
|
||||||
|
|
||||||
self.muChange = None
|
self.muChange = None
|
||||||
self.sigmaChange = None
|
self.sigmaChange = None
|
||||||
self.ratingChangeString = "--"
|
self.ratingChangeString = "N/A"
|
||||||
|
|
||||||
# determine winratio #
|
# determine winratio #
|
||||||
if self.games == 0:
|
if self.games == 0:
|
||||||
|
|||||||
38
server.py
38
server.py
@@ -35,14 +35,21 @@ def singleRound():
|
|||||||
|
|
||||||
timestamp = flask.request.args.get("id")
|
timestamp = flask.request.args.get("id")
|
||||||
if not timestamp:
|
if not timestamp:
|
||||||
return ("", 404)
|
return ("ID Missing", 404)
|
||||||
|
if not timestamp.endswith(".0"):
|
||||||
|
timestamp = timestamp + ".0"
|
||||||
db = DatabaseConnection(app.config["DB_PATH"])
|
db = DatabaseConnection(app.config["DB_PATH"])
|
||||||
r = db.getRoundByTimestamp(timestamp)
|
r = db.getRoundByTimestamp(timestamp)
|
||||||
|
if not r:
|
||||||
|
return ("Round not found", 404)
|
||||||
r = db.calcRatingChanges(r)
|
r = db.calcRatingChanges(r)
|
||||||
|
|
||||||
if not r:
|
if not r:
|
||||||
return ("", 404)
|
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)
|
return flask.render_template("single_round.html", r=r)
|
||||||
|
|
||||||
@app.route("/maps")
|
@app.route("/maps")
|
||||||
@@ -71,7 +78,7 @@ def rounds():
|
|||||||
end = flask.request.args.get("end")
|
end = flask.request.args.get("end")
|
||||||
|
|
||||||
if not start or not end:
|
if not start or not end:
|
||||||
start = datetime.datetime.now() - datetime.timedelta(days=4000)
|
start = datetime.datetime.now() - datetime.timedelta(days=7)
|
||||||
end = datetime.datetime.now()
|
end = datetime.datetime.now()
|
||||||
else:
|
else:
|
||||||
start = datetime.datetime.fromtimestamp(start)
|
start = datetime.datetime.fromtimestamp(start)
|
||||||
@@ -108,6 +115,7 @@ def player():
|
|||||||
|
|
||||||
csv_month_year = []
|
csv_month_year = []
|
||||||
csv_ratings = []
|
csv_ratings = []
|
||||||
|
csv_timestamps = []
|
||||||
|
|
||||||
minRating = 3000
|
minRating = 3000
|
||||||
maxRating = 0
|
maxRating = 0
|
||||||
@@ -115,15 +123,20 @@ def player():
|
|||||||
if histData:
|
if histData:
|
||||||
datapoints = histData[playerId]
|
datapoints = histData[playerId]
|
||||||
if datapoints:
|
if datapoints:
|
||||||
for dpk in datapoints.keys():
|
|
||||||
|
|
||||||
ratingString = str(int(datapoints[dpk]["mu"]) - 2*int(datapoints[dpk]["sigma"]))
|
tickCounter = 10
|
||||||
ratingAmored = '"' + ratingString + '"'
|
for dpk in datapoints.keys():
|
||||||
csv_ratings += [ratingAmored]
|
|
||||||
t = datetime.datetime.fromtimestamp(int(float(dpk)))
|
t = datetime.datetime.fromtimestamp(int(float(dpk)))
|
||||||
tString = t.strftime("%m %Y")
|
tsMs = str(int(t.timestamp() * 1000))
|
||||||
tStringAmored = '"' + tString + '"'
|
ratingString = str(int(datapoints[dpk]["mu"]) - 2*int(datapoints[dpk]["sigma"]))
|
||||||
csv_month_year += [tStringAmored]
|
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))
|
minRating = min(minRating, int(ratingString))
|
||||||
maxRating = max(maxRating, int(ratingString))
|
maxRating = max(maxRating, int(ratingString))
|
||||||
@@ -135,6 +148,7 @@ def player():
|
|||||||
|
|
||||||
return flask.render_template("player.html", player=player, CSV_RATINGS=",".join(csv_ratings),
|
return flask.render_template("player.html", player=player, CSV_RATINGS=",".join(csv_ratings),
|
||||||
CSV_MONTH_YEAR_OF_RATINGS=",".join(csv_month_year),
|
CSV_MONTH_YEAR_OF_RATINGS=",".join(csv_month_year),
|
||||||
|
CSV_TIMESTAMPS=csv_timestamps,
|
||||||
Y_MIN=yMin, Y_MAX=yMax)
|
Y_MIN=yMin, Y_MAX=yMax)
|
||||||
|
|
||||||
@app.route('/leaderboard')
|
@app.route('/leaderboard')
|
||||||
@@ -163,13 +177,13 @@ def leaderboard():
|
|||||||
|
|
||||||
if playerName:
|
if playerName:
|
||||||
playerInLeaderboard = db.findPlayerByName(playerName)
|
playerInLeaderboard = db.findPlayerByName(playerName)
|
||||||
if(playerInLeaderboard.games < 10):
|
|
||||||
return flask.redirect("/player?id={}".format(playerInLeaderboard.playerId))
|
|
||||||
rank = playerInLeaderboard.rank
|
|
||||||
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
|
||||||
else:
|
else:
|
||||||
|
rank = playerInLeaderboard.rank
|
||||||
|
if(playerInLeaderboard.games < 10):
|
||||||
|
return flask.redirect("/player?id={}".format(playerInLeaderboard.playerId))
|
||||||
searchName = playerInLeaderboard.name
|
searchName = playerInLeaderboard.name
|
||||||
start = rank - (rank % SEGMENT)
|
start = rank - (rank % SEGMENT)
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Leaderboard</title>
|
<title>Leaderboard</title>
|
||||||
<meta name="Description" content="Open Web Leaderboard">
|
<meta name="Description" content="Insurgency Leaderboard">
|
||||||
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
|
||||||
<script src="static/buttons.js" defer></script>
|
<script src="static/buttons.js" defer></script>
|
||||||
|
{% include "default_head_content.html" %}
|
||||||
<!-- needed for @media-css mofiers -->
|
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/bootstrap/fontawesome.css">
|
|
||||||
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="/static/bootstrap/css/mdb.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
|
||||||
|
|
||||||
<script src="/static/bootstrap/js/jquery.js"></script>
|
|
||||||
<script src="/static/bootstrap/js/popper.js"></script>
|
|
||||||
<script src="/static/bootstrap/js/bootstrap.js"></script>
|
|
||||||
<script src="/static/bootstrap/js/mdb.min.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% include 'navbar_leaderboard.html' %}
|
{% include 'navbar_leaderboard.html' %}
|
||||||
|
|||||||
19
templates/default_head_content.html
Normal file
19
templates/default_head_content.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
||||||
|
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
||||||
|
|
||||||
|
<!-- needed for @media-css mofiers -->
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||||
|
|
||||||
|
<!-- Font Awesome -->
|
||||||
|
<script src="static/moment.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<link href="static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Material Design Bootstrap -->
|
||||||
|
<link href="static/bootstrap/css/mdb.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="static/bootstrap/js/jquery.js"></script>
|
||||||
|
<script src="static/bootstrap/js/popper.js"></script>
|
||||||
|
<script src="static/bootstrap/js/bootstrap.js"></script>
|
||||||
|
<script src="static/bootstrap/js/mdb.min.js"></script>
|
||||||
|
<script src="static/bootstrap/js/addons/datatables.min.js" type="text/javascript"></script>
|
||||||
@@ -10,7 +10,10 @@
|
|||||||
<!-- left side -->
|
<!-- left side -->
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/">Home</a>
|
<a class="nav-link" href="/">Leaderboard</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/rounds">Rounds</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,8 +20,11 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item right mr-2">
|
||||||
|
<a class="nav-link" href="/rounds">Rounds</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item right">
|
<li class="nav-item right">
|
||||||
<input id="getPlayer" type="text" aria-label="search for player"
|
<input id="getPlayer" type="text" aria-label="search for player" class="mt-1"
|
||||||
placeholder="search player...">
|
placeholder="search player...">
|
||||||
</li>
|
</li>
|
||||||
{{ findPlayer }}
|
{{ findPlayer }}
|
||||||
|
|||||||
@@ -3,24 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Player: {{ player.name }}</title>
|
<title>Player: {{ player.name }}</title>
|
||||||
<meta name="Description" content="Player: {{ player.name }}">
|
<meta name="Description" content="Player: {{ player.name }}">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
{% include "default_head_content.html" %}
|
||||||
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
|
||||||
|
|
||||||
<!-- needed for @media-css mofiers -->
|
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
|
||||||
<!-- Bootstrap core CSS -->
|
|
||||||
<link href="static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<!-- Material Design Bootstrap -->
|
|
||||||
<link href="static/bootstrap/css/mdb.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="static/bootstrap/js/jquery.js"></script>
|
|
||||||
<script src="static/bootstrap/js/popper.js"></script>
|
|
||||||
<script src="static/bootstrap/js/bootstrap.js"></script>
|
|
||||||
<script src="static/bootstrap/js/mdb.min.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-special">
|
<body class="bg-special">
|
||||||
{% include 'navbar.html' %}
|
{% include 'navbar.html' %}
|
||||||
@@ -49,8 +32,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
<script defer>
|
<script defer>
|
||||||
var canvas = document.getElementById("lineChart").getContext('2d');
|
var canvas = document.getElementById("lineChart")
|
||||||
var historicalRank = new Chart(canvas, {
|
var ctx = canvas.getContext('2d');
|
||||||
|
var historicalRank = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [ {{ CSV_MONTH_YEAR_OF_RATINGS | safe }} ],
|
labels: [ {{ CSV_MONTH_YEAR_OF_RATINGS | safe }} ],
|
||||||
@@ -61,21 +45,47 @@
|
|||||||
borderColor: [ 'rgba(200, 99, 132, .7)' ],
|
borderColor: [ 'rgba(200, 99, 132, .7)' ],
|
||||||
borderWidth: 2
|
borderWidth: 2
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
scales: {
|
scales: {
|
||||||
yAxes: [{
|
yAxes: [{
|
||||||
ticks : {
|
ticks : {
|
||||||
suggestedMin : {{ Y_MIN }},
|
suggestedMin : {{ Y_MIN }},
|
||||||
suggestedMax : {{ Y_MAX }},
|
suggestedMax : {{ Y_MAX }},
|
||||||
min : {{ Y_MIN }},
|
min : {{ Y_MIN }},
|
||||||
max : {{ Y_MAX }}
|
max : {{ Y_MAX }}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
distribution: 'series',
|
||||||
|
time: {
|
||||||
|
round : false,
|
||||||
|
unit: "day"
|
||||||
|
},
|
||||||
|
ticks : {
|
||||||
|
autoSkip : true,
|
||||||
|
source : "labels"
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
display: false,
|
||||||
|
offsetGridLines: true
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
responsive: true
|
responsive: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
var datasetHelper = {{ CSV_TIMESTAMPS | safe }}
|
||||||
|
canvas.onclick = function (evt) {
|
||||||
|
var points = historicalRank.getElementsAtEvent(evt);
|
||||||
|
if(points.length != 1){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var timestampMs = datasetHelper[points[0]._index] / 1000
|
||||||
|
var idString = timestampMs.toString()
|
||||||
|
window.location.href = "/round-info?id=" + idString
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,24 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Rounds Played</title>
|
<title>Rounds Played</title>
|
||||||
<meta name="Description" content="Insurgency games played on the AtlantisHQ">
|
<meta name="Description" content="Insurgency games played on the AtlantisHQ">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
{% include "default_head_content.html" %}
|
||||||
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
|
||||||
|
|
||||||
<!-- needed for @media-css mofiers -->
|
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
|
||||||
<!-- Bootstrap core CSS -->
|
|
||||||
<link href="static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<!-- Material Design Bootstrap -->
|
|
||||||
<link href="static/bootstrap/css/mdb.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="static/bootstrap/js/jquery.js"></script>
|
|
||||||
<script src="static/bootstrap/js/popper.js"></script>
|
|
||||||
<script src="static/bootstrap/js/bootstrap.js"></script>
|
|
||||||
<script src="static/bootstrap/js/mdb.min.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-special">
|
<body class="bg-special">
|
||||||
{% include 'navbar.html' %}
|
{% include 'navbar.html' %}
|
||||||
@@ -29,12 +12,13 @@
|
|||||||
cellspacing="0" width="100%">
|
cellspacing="0" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="th-sm">Map</th>
|
<th class="th-sm font-weight-bold">Map</th>
|
||||||
<th class="th-sm">Winner</th>
|
<th class="th-sm font-weight-bold">Winner</th>
|
||||||
<th class="th-sm">Duration</th>
|
<th class="th-sm font-weight-bold">Duration</th>
|
||||||
<th class="th-sm">Start Time</th>
|
<th class="th-sm font-weight-bold">Start Time</th>
|
||||||
<th class="th-sm">Rating System Winner Prediction</th>
|
<th class="th-sm font-weight-bold">Winner Prediction</th>
|
||||||
<th class="th-sm">Confidence</th>
|
<th class="th-sm font-weight-bold">Prediction confidence</th>
|
||||||
|
<th class="th-sm font-weight-bold">Game-ID</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -43,14 +27,29 @@
|
|||||||
<td>{{ r.mapName }}</td>
|
<td>{{ r.mapName }}</td>
|
||||||
<td>{{ r.winnerSideString }}</td>
|
<td>{{ r.winnerSideString }}</td>
|
||||||
<td>{{ r.duration }}</td>
|
<td>{{ r.duration }}</td>
|
||||||
<td>{{ r.startTime.strftime('%H:%M %d.%m.%Y') }}</td>
|
<td>{{ r.startTime.strftime('%d.%m.%Y %H:%M') }}</td>
|
||||||
<td>{{ r.prediction }}</td>
|
<td
|
||||||
|
{% if r.winnerSideString == r.prediction %}
|
||||||
|
style="color: green";
|
||||||
|
{% elif r.prediction != '-' %}
|
||||||
|
style="color: red";
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
{{ r.prediction }}
|
||||||
|
</td>
|
||||||
<td>{{ r.confidence }}%</td>
|
<td>{{ r.confidence }}%</td>
|
||||||
|
<td><a href="/round-info?id={{ r.id }}">{{ r.id }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<script defer>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('#tableMain').DataTable();
|
||||||
|
$('.dataTables_length').addClass('bs-select');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,34 +3,82 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Rounds Played</title>
|
<title>Rounds Played</title>
|
||||||
<meta name="Description" content="Insurgency games played on the AtlantisHQ">
|
<meta name="Description" content="Insurgency games played on the AtlantisHQ">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
{% include 'default_head_content.html' %}
|
||||||
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
|
||||||
|
|
||||||
<!-- needed for @media-css mofiers -->
|
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
|
||||||
<!-- Bootstrap core CSS -->
|
|
||||||
<link href="static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<!-- Material Design Bootstrap -->
|
|
||||||
<link href="static/bootstrap/css/mdb.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="static/bootstrap/js/jquery.js"></script>
|
|
||||||
<script src="static/bootstrap/js/popper.js"></script>
|
|
||||||
<script src="static/bootstrap/js/bootstrap.js"></script>
|
|
||||||
<script src="static/bootstrap/js/mdb.min.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-special">
|
<body class="bg-special">
|
||||||
{% include 'navbar.html' %}
|
{% include 'navbar.html' %}
|
||||||
<div class="container mt-3 mb-3" role="main">
|
<div class="container mt-3 mb-3" role="main">
|
||||||
{% for p in r.winners %}
|
|
||||||
<h4>{{ p.name }}{{ p.ratingChangeString }}</h4>
|
<div class="row">
|
||||||
{% endfor %}
|
<div class="col-sm">
|
||||||
{% for p in r.losers %}
|
<h1>Round {{ r.id }}</h1>
|
||||||
<h4>{{ p.name }}{{ p.ratingChangeString }}</h4>
|
<h4>{{ r.startTime }}</h4>
|
||||||
{% endfor %}
|
<h4>Map: {{ r.mapName }}</h4>
|
||||||
|
<h4>Duration: {{ r.duration }}</h4>
|
||||||
|
|
||||||
|
{% if r.invalid %}
|
||||||
|
<div>
|
||||||
|
<h5 style="color: red;">{{ r.invalid | safe }}</h5>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<h5>Winners ({{ r.winnerSideString }})</h5>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<h5>Losers ({{ r.loserSideString }})</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
{% for p in r.winners %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm" style="overflow: hidden;">
|
||||||
|
<a href="/player?id={{ p.playerId }}">{{ p.name }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
{{ p.participation }}%
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
{% if not r.invalid %}
|
||||||
|
<small style="color: green;">
|
||||||
|
{{ p.ratingChangeString | safe }}
|
||||||
|
</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
{% for p in r.losers %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm" style="overflow: hidden;">
|
||||||
|
<a href="/player?id={{ p.playerId }}">{{ p.name }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
{{ p.participation }}%
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
{% if not r.invalid %}
|
||||||
|
<small style="color: red;">
|
||||||
|
{{ p.ratingChangeString | safe }}
|
||||||
|
</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="mt-3">
|
||||||
|
<small style="font-weight: bold;">Stats for nerds</small><br>
|
||||||
|
<small>Balance: {{ 100 - r.confidence }}%</small><br>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user