mirror of
https://github.com/FAUSheppy/tmnf-replay-server.git
synced 2025-12-06 23:21:36 +01:00
wip: map info datatables
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ instance/
|
||||
*.sqlite
|
||||
uploads/
|
||||
__pycache__/
|
||||
*.db
|
||||
|
||||
112
server.py
112
server.py
@@ -10,6 +10,7 @@ import datetime
|
||||
|
||||
from pygbx import Gbx, GbxType
|
||||
|
||||
import sqlalchemy
|
||||
from sqlalchemy import Column, Integer, String, Boolean, or_, and_, asc, desc
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
@@ -23,7 +24,7 @@ class Map(db.Model):
|
||||
|
||||
__tablename__ = "maps"
|
||||
|
||||
map_uid = Column(Integer, primary_key=True)
|
||||
map_uid = Column(String, primary_key=True)
|
||||
mapname = Column(String)
|
||||
|
||||
class ParsedReplay(db.Model):
|
||||
@@ -59,6 +60,94 @@ class ParsedReplay(db.Model):
|
||||
time=self.get_human_readable_time(),
|
||||
map_n=self.guess_map(), login=self.login, uploader=self.uploader)
|
||||
|
||||
def to_dict(self):
|
||||
d = dict()
|
||||
d.update({ "login" : self.login })
|
||||
d.update({ "race_time" : self.get_human_readable_time() })
|
||||
d.update({ "filepath" : self.filepath })
|
||||
d.update({ "upload_dt" : self.upload_dt })
|
||||
return d
|
||||
|
||||
class DataTable():
|
||||
|
||||
def __init__(self, d, cols):
|
||||
self.draw = int(d["draw"])
|
||||
self.start = int(d["start"])
|
||||
self.length = int(d["length"])
|
||||
self.trueLength = -1
|
||||
self.searchValue = d["search[value]"]
|
||||
self.searchIsRegex = d["search[regex]"]
|
||||
self.cols = cols
|
||||
self.orderByCol = int(d["order[0][column]"])
|
||||
self.orderDirection = d["order[0][dir]"]
|
||||
|
||||
# order variable for use with pythong sorted etc #
|
||||
self.orderAsc = self.orderDirection == "asc"
|
||||
|
||||
# oder variable for use with sqlalchemy
|
||||
if self.orderAsc:
|
||||
self.orderAscDbClass = sqlalchemy.asc
|
||||
self.orderAscDbClassReverse = sqlalchemy.desc
|
||||
else:
|
||||
self.orderAscDbClass = sqlalchemy.desc
|
||||
self.orderAscDbClassReverse = sqlalchemy.asc
|
||||
|
||||
def __build(self, results, total, filtered):
|
||||
|
||||
self.cacheResults = results
|
||||
|
||||
count = 0
|
||||
resultDicts = [ r.to_dict() for r in results ]
|
||||
|
||||
# data list must have the correct order (same as table scheme) #
|
||||
rows = []
|
||||
for r in resultDicts:
|
||||
singleRow = []
|
||||
for key in self.cols:
|
||||
singleRow.append(r[key])
|
||||
rows.append(singleRow)
|
||||
|
||||
|
||||
d = dict()
|
||||
d.update({ "draw" : self.draw })
|
||||
d.update({ "recordsTotal" : total })
|
||||
d.update({ "recordsFiltered" : filtered })
|
||||
d.update({ "data" : rows })
|
||||
|
||||
return d
|
||||
|
||||
def get(self, map_uid=None):
|
||||
|
||||
filtered = 0
|
||||
total = 0
|
||||
|
||||
# base query
|
||||
query = db.session.query(ParsedReplay)
|
||||
if map_uid:
|
||||
print("Filter for map: {}".format(map_uid))
|
||||
query = query.filter(ParsedReplay.map_uid == map_uid)
|
||||
|
||||
total = query.count()
|
||||
if self.searchValue:
|
||||
|
||||
# search string (search for all substrings individually #
|
||||
filterQuery = query
|
||||
|
||||
for substr in self.searchValue.split(" "):
|
||||
searchSubstr = "%{}%".format(substr.strip())
|
||||
filterQuery = filterQuery.filter(ParsedReplay.tags.like(searchSubstr))
|
||||
|
||||
filtered = filterQuery.count()
|
||||
results = filterQuery.offset(self.start).limit(self.length).all()
|
||||
|
||||
else:
|
||||
|
||||
query = query.order_by(self.orderAscDbClassReverse(ParsedReplay.race_time))
|
||||
results = query.offset(self.start).limit(self.length).all()
|
||||
filtered = total
|
||||
|
||||
return self.__build(results, total, filtered)
|
||||
|
||||
def replay_from_path(fullpath, uploader=None):
|
||||
|
||||
if not fullpath.endswith(".gbx"):
|
||||
@@ -90,17 +179,30 @@ def replay_from_path(fullpath, uploader=None):
|
||||
if uploader in app.config["TRUSTED_UPLOADERS"]:
|
||||
m = Map(map_uid=replay.map_uid, mapname=replay.guess_map())
|
||||
db.session.merge(m)
|
||||
dn.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
return replay
|
||||
|
||||
@app.route("/")
|
||||
@app.route("/map")
|
||||
def list():
|
||||
# TODO list maps by mapnames
|
||||
# TODO list replays by mapnames
|
||||
# TODO list by user
|
||||
# TODO show all/show only best
|
||||
return flask.render_template("index.html")
|
||||
header_col = ["Player", "Time", "Date", "Replay"]
|
||||
map_uid = flask.request.args.get("map_uid")
|
||||
return flask.render_template("index.html", header_col=header_col, map_uid=map_uid)
|
||||
|
||||
@app.route("/")
|
||||
def mapnames():
|
||||
|
||||
@app.route("/data-source<path:path>", methods=["POST"])
|
||||
def source():
|
||||
|
||||
# path = map_uid
|
||||
dt = DataTable(flask.request.form.to_dict(), ["login", "race_time", "upload_dt", "filepath" ])
|
||||
jsonDict = dt.get(path)
|
||||
return flask.Response(json.dumps(jsonDict), 200, mimetype='application/json')
|
||||
|
||||
@app.route("/upload", methods = ['GET', 'POST'])
|
||||
def upload():
|
||||
@@ -111,7 +213,7 @@ def upload():
|
||||
fname = werkzeug.utils.secure_filename(f_storage.filename)
|
||||
fullpath = os.path.join("uploads/", fname)
|
||||
f_storage.save(fullpath)
|
||||
replay = replay_from_path(fullpath)
|
||||
replay = replay_from_path(fullpath, uploader="sheppy")
|
||||
print(replay)
|
||||
db.session.add(replay)
|
||||
db.session.commit()
|
||||
|
||||
34
templates/datatable.html
Normal file
34
templates/datatable.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<div style="font-size: 16px; font-weight: 300;" class="mt-5 mb-3 ml-2 mr-2" role="main">
|
||||
<table id="tableMain" class="table table-striped table-bordered table-sm"
|
||||
cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for item in header_col %}
|
||||
<th class="th-sm font-weighIt-bold">{{ item }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- tbody serverside processing -->
|
||||
</table>
|
||||
<script defer>
|
||||
var dt = null
|
||||
$(document).ready(function () {
|
||||
dt = $('#tableMain').DataTable({
|
||||
serverSide: true,
|
||||
ajax: {
|
||||
url: '/data-source',
|
||||
type: 'POST'
|
||||
},
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": 3,
|
||||
"render": function ( data, type, full, meta ) {
|
||||
return '<a href=\"/static/'+data+'\" download>Download</a>';
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
$('.dataTables_length').addClass('bs-select');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@@ -1,29 +0,0 @@
|
||||
<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="Image Factory">
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Image Factory" />
|
||||
<meta property="og:description" content="Image Factory developed by Yannik Schmidt" />
|
||||
<meta property="og:url" content="/" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="dev-info">
|
||||
Image Factory for internal use,
|
||||
developed by <a href="https://potaris.de">Yannik Schmidt</a><br>
|
||||
Availiable on
|
||||
<a href="https://github.com/FAUSheppy/python-flask-picture-factory">FAUSheppy GitHub</a>
|
||||
</div>
|
||||
<p>
|
||||
Public Images List:
|
||||
</p>
|
||||
<div class="overview">
|
||||
<p>
|
||||
{% for path in paths %}
|
||||
{{ path }}<br>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
31
templates/map-info.html
Normal file
31
templates/map-info.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<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>
|
||||
<body>
|
||||
{% include "datatable.html" %}
|
||||
</body>
|
||||
Reference in New Issue
Block a user