From f841a3a6a6fe191cde00471ea9759c3f16de2105 Mon Sep 17 00:00:00 2001 From: Julian Rother <julianr@fsmpi.rwth-aachen.de> Date: Sun, 19 Feb 2017 00:55:22 +0100 Subject: [PATCH] Optimized time-based stats --- db_schema.sql | 1 + server.py | 2 +- stats.py | 67 ++++++++++++++++++------------------------ templates/course.html | 2 +- templates/lecture.html | 2 +- templates/macros.html | 25 ++++++++++++---- templates/stats.html | 4 ++- 7 files changed, 54 insertions(+), 49 deletions(-) diff --git a/db_schema.sql b/db_schema.sql index f85b8c5..27af157 100644 --- a/db_schema.sql +++ b/db_schema.sql @@ -128,6 +128,7 @@ CREATE TABLE IF NOT EXISTS `log` ( `ip` varchar(64), `id` varchar(64), `time` datetime NOT NULL, + `date` datetime NOT NULL, `source` varchar(8), `object` varchar(10), `obj_id` INTEGER, diff --git a/server.py b/server.py index 9bd4889..1f04049 100644 --- a/server.py +++ b/server.py @@ -552,7 +552,7 @@ def auth(): # For use with nginx auth_request password = auth.password if checkperm(perms, username=username, password=password): try: - modify('INSERT INTO log (ip, id, `time`, object, obj_id, path) VALUES (?, ?, ?, "video", ?, ?)', ip, cookie, datetime.now(), perms[0]['vid'], url) + modify('INSERT INTO log (ip, id, `time`, `date`, object, obj_id, path) VALUES (?, ?, ?, "video", ?, ?)', ip, cookie, datetime.now(), datetime.combine(date.today(), time()), perms[0]['vid'], url) except: pass r = make_response('OK', 200) diff --git a/stats.py b/stats.py index 0f188af..e0b3e4e 100644 --- a/stats.py +++ b/stats.py @@ -36,46 +36,35 @@ def stats_generic(req, param=None): res['y'].append(row['y']) return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json') -@app.route('/stats/viewsperday') -@mod_required -def stats_viewsperday(): - start = None - end = None - courseid = request.args.get('course', None) - lectureid = request.args.get('lecture', None) - videoid = request.args.get('video', None) - rows = query('''SELECT log.id AS id, log.time AS time, formats.description AS fmt - FROM log - JOIN videos ON (videos.id = log.obj_id) - JOIN lectures ON (lectures.id = videos.lecture_id) - JOIN courses ON (courses.id = lectures.course_id) - JOIN formats ON (formats.id = videos.video_format) - WHERE (? OR courses.id = ?) AND (? OR lectures.id = ?) AND (? OR videos.id = ?)''', - courseid == None, courseid, lectureid == None, lectureid, videoid == None, videoid) +@app.route('/stats/viewsperday/<req>') +@app.route('/stats/viewsperday/<req>/<param>') +def stats_viewsperday(req, param=None): + queries = { + 'lecture': '(SELECT log.date AS date, formats.description AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN formats ON formats.id = videos.video_format WHERE videos.lecture_id = ? GROUP BY log.date, videos.video_format) UNION (SELECT log.date AS date, "total" AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id WHERE videos.lecture_id = ? GROUP BY log.date)', + 'course': '(SELECT log.date AS date, formats.description AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN lectures ON lectures.id = videos.lecture_id JOIN formats ON formats.id = videos.video_format WHERE lectures.course_id = ? GROUP BY log.date, videos.video_format) UNION (SELECT log.date AS date, "total" AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN lectures ON lectures.id = videos.lecture_id WHERE lectures.course_id = ? GROUP BY log.date)', + 'global': '(SELECT log.date AS date, formats.description AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN formats ON formats.id = videos.video_format GROUP BY log.date, videos.video_format) UNION (SELECT log.date AS date, "total" AS trace, COUNT(DISTINCT log.id) AS y FROM log GROUP BY log.date)', + 'courses': 'SELECT log.date AS date, courses.handle AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN lectures ON lectures.id = videos.lecture_id JOIN courses ON courses.id = lectures.course_id GROUP BY log.date, courses.id' + } + if req not in queries: + return 404, 'Not found!' + rows = query(queries[req], *(queries[req].count('?')*[param])) + traces = set() data = {} - if not end: - end = date.today() - first = None - fmts = ['total'] - for e in rows: - key = e['time'].date() - if not first or key < first: - first = key - if key not in data: - data[key] = {'total': set()} - if e['fmt'] not in data[key]: - data[key][e['fmt']] = set() - data[key]['total'].add(e['id']) - data[key][e['fmt']].add(e['id']) - if e['fmt'] not in fmts: - fmts.append(e['fmt']) - if not start: - start = first - res = {'times': [], 'views': [{'name': name, 'vals': []} for name in fmts]} + start = None + for row in rows: + if not start or row['date'] < start: + start = row['date'] + traces.add(row['trace']) + if row['date'] not in data: + data[row['date']] = {} + data[row['date']][row['trace']] = row['y'] + end = date.today() + x = [] + y = [{'name': trace, 'vals': []} for trace in traces] while start and start <= end: - res['times'].append(start.isoformat()) - for d in res['views']: - d['vals'].append(len(data.get(start, {}).get(d['name'], set()))) + x.append(start) + for trace in y: + trace['vals'].append(data.get(start, {}).get(trace['name'], 0)) start += timedelta(days=1) - return Response(json.dumps(res, default=date_json_handler), mimetype='application/json') + return Response(json.dumps({'times': x, 'views': y}, default=plotly_date_handler), mimetype='application/json') diff --git a/templates/course.html b/templates/course.html index 5cb4f7c..0b02b86 100644 --- a/templates/course.html +++ b/templates/course.html @@ -48,7 +48,7 @@ </table> </div> <div id="statview" class="col-xs-6" style="height:600px"></div> - {{stats_viewsperday("statview", "Abrufe pro Tag", course=course.id)}} + {{stats_viewsperday("statview", "course", "Abrufer pro Tag", param=course.id)}} <div id="lecture_views" class="col-xs-6" style="height:600px"></div> {{stats_generic("lecture_views", "lecture_views", "Abrufe pro Aufnahme", param=course.id, type="bar")}} {% endif %} diff --git a/templates/lecture.html b/templates/lecture.html index b7589bb..d7705bf 100644 --- a/templates/lecture.html +++ b/templates/lecture.html @@ -60,7 +60,7 @@ </table> </div> <div id="statview" class="col-xs-12" style="height:600px"></div> - {{stats_viewsperday("statview", "Abrufe pro Tag", lecture=lecture.id)}} + {{stats_viewsperday("statview", "lecture", "Abrufer pro Tag", param=lecture.id)}} {% endif %} </div> </div> diff --git a/templates/macros.html b/templates/macros.html index 757ce31..8550225 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -246,20 +246,33 @@ $('#embedcodebtn').popover( {% macro vtttime(time) %}{{ '%02d:%02d:%02d.000'|format( time//3600, (time//60)%60, time%60) }}{% endmacro %} -{% macro stats_viewsperday(id, title, type="scatter", course=None, lecture=None, video=None) %} +{% macro stats_viewsperday(id, req, title, type="scatter", param=None, maxtraces=7) %} <script> $.ajax({ method: "GET", - url: "{{url_for('stats_viewsperday', course=course, lecture=lecture, video=video)}}", + url: "{{url_for('stats_viewsperday', req=req, param=param)}}", dataType: "json", error: moderator.api.handleapierror, success: function (data) { var traces = []; - for (var i = 1; i < data.views.length; i++) { - traces.push({"x": data.times, "y": data.views[i].vals, "type": "{{type}}", "name": data.views[i].name, line: {"width": 1}}); + for (var i = 0; i < data.views.length; i++) { + if (data.views[i].name == 'total' && data.views.length > 2) + traces.push({"x": data.times, "y": data.views[i].vals, "type": "{{type}}", "name": "gesamt", line: {"color": "black", "width": 2}}); + else + traces.push({"x": data.times, "y": data.views[i].vals, "type": "{{type}}", "name": data.views[i].name, line: {"width": 1}}); } - if (data.views.length > 2) - traces.push({"x": data.times, "y": data.views[0].vals, "type": "{{type}}", "name": "gesamt", line: {"color": "black", "width": 2}}); + traces.sort(function (a, b) { + amax = 0; + bmax = 0; + for (var i = 0; i < a.y.length; i++) + amax = Math.max(amax, a.y[i]); + for (var i = 0; i < b.y.length; i++) + bmax = Math.max(bmax, b.y[i]); + return bmax-amax; + }); + for (var i = 0; i < traces.length; i++) + if (i > {{maxtraces}}) + traces[i].visible = "legendonly"; var layout = { "title": "{{title}}", "showlegend": (traces.length != 1) diff --git a/templates/stats.html b/templates/stats.html index b96585a..eafcd20 100644 --- a/templates/stats.html +++ b/templates/stats.html @@ -27,7 +27,9 @@ <div id="categories_lectures" class="col-xs-6" style="height:600px"></div> {{stats_pie("categories_lectures", "categories_lectures", "Aufnahmen nach Kategorie", showlegend=False)}} <div id="statview" class="col-xs-12" style="height:600px"></div> - {{stats_viewsperday("statview", "Abrufe pro Tag")}} + {{stats_viewsperday("statview", "global", "Abrufer pro Tag")}} + <div id="courseviewsperday" class="col-xs-12" style="height:600px"></div> + {{stats_viewsperday("courseviewsperday", "courses", "Abrufer pro Tag")}} </div> i </div> </div> -- GitLab