mirror of
https://github.com/FAUSheppy/tmnf-replay-server.git
synced 2025-12-07 23:51:36 +01:00
wip: index & mapnames
This commit is contained in:
52
server.py
52
server.py
@@ -27,6 +27,30 @@ class Map(db.Model):
|
|||||||
map_uid = Column(String, primary_key=True)
|
map_uid = Column(String, primary_key=True)
|
||||||
mapname = Column(String)
|
mapname = Column(String)
|
||||||
|
|
||||||
|
def get_best_replay(self):
|
||||||
|
|
||||||
|
q = db.session.query(ParsedReplay).filter(ParsedReplay.map_uid == self.map_uid)
|
||||||
|
r = q.order_by(asc(ParsedReplay.race_time)).first()
|
||||||
|
return r
|
||||||
|
|
||||||
|
def get_best_replay_for_player(self, player):
|
||||||
|
|
||||||
|
q = db.session.query(ParsedReplay).filter(ParsedReplay.map_uid == self.map_uid)
|
||||||
|
r = q.filter(or_(ParsedReplay.uploader == player, ParsedReplay.login == player)).first()
|
||||||
|
return r
|
||||||
|
|
||||||
|
def get_best_replay_repr(self):
|
||||||
|
r = self.get_best_replay()
|
||||||
|
if not r:
|
||||||
|
return "-"
|
||||||
|
return str(r)
|
||||||
|
|
||||||
|
def get_best_replay_age(self):
|
||||||
|
|
||||||
|
parsed = datetime.datetime.fromisoformat(self.get_best_replay().upload_dt)
|
||||||
|
delta = datetime.datetime.now() - parsed
|
||||||
|
return delta.days
|
||||||
|
|
||||||
class ParsedReplay(db.Model):
|
class ParsedReplay(db.Model):
|
||||||
|
|
||||||
__tablename__ = "replays"
|
__tablename__ = "replays"
|
||||||
@@ -56,9 +80,9 @@ class ParsedReplay(db.Model):
|
|||||||
return t_string[:-4]
|
return t_string[:-4]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{time} on {map_n} by {login}/{uploader}".format(
|
return "{time} on {map_n} by {login}".format(
|
||||||
time=self.get_human_readable_time(),
|
time=self.get_human_readable_time(),
|
||||||
map_n=self.guess_map(), login=self.login, uploader=self.uploader)
|
map_n=self.guess_map(), login=self.login)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
d = dict()
|
d = dict()
|
||||||
@@ -87,10 +111,10 @@ class DataTable():
|
|||||||
# oder variable for use with sqlalchemy
|
# oder variable for use with sqlalchemy
|
||||||
if self.orderAsc:
|
if self.orderAsc:
|
||||||
self.orderAscDbClass = sqlalchemy.asc
|
self.orderAscDbClass = sqlalchemy.asc
|
||||||
self.orderAscDbClassReverse = sqlalchemy.desc
|
self.orderAscDbClassReverse = sqlalchemy.asc
|
||||||
else:
|
else:
|
||||||
self.orderAscDbClass = sqlalchemy.desc
|
self.orderAscDbClass = sqlalchemy.desc
|
||||||
self.orderAscDbClassReverse = sqlalchemy.asc
|
self.orderAscDbClassReverse = sqlalchemy.desc
|
||||||
|
|
||||||
def __build(self, results, total, filtered):
|
def __build(self, results, total, filtered):
|
||||||
|
|
||||||
@@ -170,7 +194,8 @@ def replay_from_path(fullpath, uploader=None):
|
|||||||
race_time=ghost.race_time,
|
race_time=ghost.race_time,
|
||||||
uploader=uploader,
|
uploader=uploader,
|
||||||
filepath=fullpath,
|
filepath=fullpath,
|
||||||
map_uid=ghost.uid,
|
#map_uid=ghost.uid,
|
||||||
|
map_uid=os.path.basename(fullpath).split("_")[1].split(".Replay")[0],
|
||||||
ghost_id=ghost.id,
|
ghost_id=ghost.id,
|
||||||
login=ghost.login,
|
login=ghost.login,
|
||||||
upload_dt=datetime.datetime.now().isoformat(),
|
upload_dt=datetime.datetime.now().isoformat(),
|
||||||
@@ -183,25 +208,24 @@ def replay_from_path(fullpath, uploader=None):
|
|||||||
|
|
||||||
return replay
|
return replay
|
||||||
|
|
||||||
@app.route("/map")
|
@app.route("/map-info")
|
||||||
def list():
|
def list():
|
||||||
# TODO list maps by mapnames
|
|
||||||
# TODO list replays by mapnames
|
|
||||||
# TODO list by user
|
|
||||||
# TODO show all/show only best
|
|
||||||
header_col = ["Player", "Time", "Date", "Replay"]
|
header_col = ["Player", "Time", "Date", "Replay"]
|
||||||
map_uid = flask.request.args.get("map_uid")
|
map_uid = flask.request.args.get("map_uid")
|
||||||
return flask.render_template("index.html", header_col=header_col, map_uid=map_uid)
|
return flask.render_template("map-info.html", header_col=header_col, map_uid=map_uid)
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def mapnames():
|
def mapnames():
|
||||||
|
# TODO list by user
|
||||||
|
maps = db.session.query(Map).order_by(asc(Map.mapname)).all()
|
||||||
|
return flask.render_template("index.html", maps=maps)
|
||||||
|
|
||||||
@app.route("/data-source<path:path>", methods=["POST"])
|
@app.route("/data-source/<path:map_uid>", methods=["POST"])
|
||||||
def source():
|
def source(map_uid):
|
||||||
|
|
||||||
# path = map_uid
|
# path = map_uid
|
||||||
dt = DataTable(flask.request.form.to_dict(), ["login", "race_time", "upload_dt", "filepath" ])
|
dt = DataTable(flask.request.form.to_dict(), ["login", "race_time", "upload_dt", "filepath" ])
|
||||||
jsonDict = dt.get(path)
|
jsonDict = dt.get(map_uid=map_uid)
|
||||||
return flask.Response(json.dumps(jsonDict), 200, mimetype='application/json')
|
return flask.Response(json.dumps(jsonDict), 200, mimetype='application/json')
|
||||||
|
|
||||||
@app.route("/upload", methods = ['GET', 'POST'])
|
@app.route("/upload", methods = ['GET', 'POST'])
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
dt = $('#tableMain').DataTable({
|
dt = $('#tableMain').DataTable({
|
||||||
serverSide: true,
|
serverSide: true,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/data-source',
|
url: '/data-source/{{ map_uid }}',
|
||||||
type: 'POST'
|
type: 'POST'
|
||||||
},
|
},
|
||||||
"columnDefs": [
|
"columnDefs": [
|
||||||
|
|||||||
28
templates/head.html
Normal file
28
templates/head.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="description" content="Image factory providing automatically resized and re-encoded images.">
|
||||||
|
<meta name="author" content="Yannik Schmidt">
|
||||||
|
<meta name="title" content="TM Replays">
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content="Trackmania Replay Server" />
|
||||||
|
<meta property="og:description" content="TM Replay Server developed by Yannik Schmidt" />
|
||||||
|
<meta property="og:url" content="/" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="/defaultFavicon.ico">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Bootstrap core JS -->
|
||||||
|
<script src="https://cdn.atlantishq.de/js/jquery.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<link href="https://cdn.atlantishq.de/fontawesome/css/all.css" rel="stylesheet">
|
||||||
|
<link href="https://cdn.atlantishq.de/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<script defer src="https://cdn.atlantishq.de/js/bootstrap.min.js"></script>
|
||||||
|
<script defer src="https://cdn.atlantishq.de/js/addons/datatables.min.js"></script>
|
||||||
|
|
||||||
|
<!-- mdb -->
|
||||||
|
<link href="https://cdn.atlantishq.de/css/mdb.min.css" rel="stylesheet">
|
||||||
|
<script defer src="https://cdn.atlantishq.de/js/mdb.min.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
31
templates/index.html
Normal file
31
templates/index.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<head>
|
||||||
|
{% include "head.html" %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="px-2">Map</th>
|
||||||
|
{% if player %}
|
||||||
|
<th class="px-2">Personal Best</th>
|
||||||
|
{% endif %}
|
||||||
|
<th class="px-2">Record</th>
|
||||||
|
<th class="px-2">Record Age</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for map in maps %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-2">
|
||||||
|
<a href="/map-info?map_uid={{ map.map_uid }}">{{ map.mapname }}</a>
|
||||||
|
</td>
|
||||||
|
{% if player %}
|
||||||
|
<td class="px-2">{{ map.get_best_for_player(player) }}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="px-2">{{ map.get_best_replay_repr() }}</td>
|
||||||
|
<td class="px-2">{{ map.get_best_replay_age() }} days</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
@@ -1,30 +1,5 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
{% include "head.html" %}
|
||||||
<meta name="description" content="Image factory providing automatically resized and re-encoded images.">
|
|
||||||
<meta name="author" content="Yannik Schmidt">
|
|
||||||
<meta name="title" content="TM Replays">
|
|
||||||
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:title" content="Trackmania Replay Server" />
|
|
||||||
<meta property="og:description" content="TM Replay Server developed by Yannik Schmidt" />
|
|
||||||
<meta property="og:url" content="/" />
|
|
||||||
|
|
||||||
<link rel="shortcut icon" href="/defaultFavicon.ico">
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Bootstrap core JS -->
|
|
||||||
<script src="https://cdn.atlantishq.de/js/jquery.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Bootstrap core CSS -->
|
|
||||||
<link href="https://cdn.atlantishq.de/fontawesome/css/all.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.atlantishq.de/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<script defer src="https://cdn.atlantishq.de/js/bootstrap.min.js"></script>
|
|
||||||
<script defer src="https://cdn.atlantishq.de/js/addons/datatables.min.js"></script>
|
|
||||||
|
|
||||||
<!-- mdb -->
|
|
||||||
<link href="https://cdn.atlantishq.de/css/mdb.min.css" rel="stylesheet">
|
|
||||||
<script defer src="https://cdn.atlantishq.de/js/mdb.min.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% include "datatable.html" %}
|
{% include "datatable.html" %}
|
||||||
|
|||||||
Reference in New Issue
Block a user