Skip to content
Snippets Groups Projects
Commit d605c114 authored by Julian Rother's avatar Julian Rother
Browse files

Merge branch 'access_stats'

parents 55c0f1fe 25cd2a31
No related branches found
No related tags found
No related merge requests found
......@@ -527,6 +527,11 @@ def auth(): # For use with nginx auth_request
return 'Internal Server Error', 500
url = request.headers['X-Original-Uri'].lstrip(config['VIDEOPREFIX'])
ip = request.headers.get('X-Real-IP', '')
if 'tracking' in request.cookies:
cookie = request.cookies['tracking']
else:
cookie = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(64))
# r.set_cookie('tracking', request.cookies.get('tracking', ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(64))), max_age=2147483647)
if url.endswith('jpg') or ismod():
return "OK", 200
perms = query('''SELECT videos.path, videos.id AS vid, perm.*
......@@ -546,8 +551,13 @@ def auth(): # For use with nginx auth_request
username = auth.username
password = auth.password
if checkperm(perms, username=username, password=password):
return 'OK', 200
modify('INSERT INTO log VALUES (?, "", ?, "video", ?, ?)', ip, datetime.now(), perms[0]['vid'], url)
try:
modify('INSERT INTO log VALUES (?, ?, ?, "video", ?, ?)', ip, cookie, datetime.now(), perms[0]['vid'], url)
except:
pass
r = make_response('OK', 200)
r.set_cookie('tracking', cookie, max_age=2147483647) # Many many years
return r
password_auth = False
for perm in perms:
if perm['type'] == 'password':
......
......@@ -8,17 +8,70 @@ from jobs import date_json_handler
def stats():
return render_template('stats.html')
statsqueries = {}
statsqueries['course_count'] = "SELECT count(id) as count, semester FROM courses GROUP BY semester;"
statsqueries['lectures_count'] = "SELECT count(lectures.id) as count, semester FROM lectures JOIN courses on courses.id=lectures.course_id GROUP BY semester;"
statsqueries['formats_views'] = "SELECT formats.description AS x, count(log.id) AS y FROM log JOIN videos ON (videos.id = log.obj_id) JOIN formats ON (formats.id = videos.video_format) GROUP BY formats.id"
statsqueries['course_count'] = "SELECT semester AS x, count(id) AS y FROM courses GROUP BY semester"
statsqueries['lectures_count'] = "SELECT semester AS x, count(lectures.id) AS y FROM lectures JOIN courses ON (courses.id = lectures.course_id) GROUP BY semester"
statsqueries['categories_courses'] = "SELECT courses.subject AS x, count(courses.id) AS y FROM courses GROUP BY courses.subject ORDER BY y DESC LIMIT 100"
statsqueries['organizer_courses'] = "SELECT courses.organizer AS x, count(courses.id) AS y FROM courses GROUP BY courses.organizer ORDER BY y DESC LIMIT 100"
statsqueries['categories_lectures'] = "SELECT courses.subject AS x, count(lectures.id) AS y FROM lectures JOIN courses ON (courses.id = lectures.course_id) GROUP BY courses.subject ORDER BY y DESC LIMIT 100"
statsqueries['lecture_views'] = "SELECT lectures.time AS x, count(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 lectures.id ORDER BY lectures.time"
def plotly_date_handler(obj):
return obj.strftime("%Y-%m-%d %H:%M:%S")
@app.route('/stats/data/<dataname>')
@app.route('/stats/data/<dataname>/<parameter>')
@app.route('/stats/generic/<req>')
@app.route('/stats/generic/<req>/<param>')
@mod_required
def stats_data(dataname, parameter=None):
if parameter:
data = query(statsqueries[dataname],parameter)
else:
data = query(statsqueries[dataname])
return Response(json.dumps(data, default=date_json_handler), mimetype='application/json')
def stats_generic(req, param=None):
if req not in statsqueries:
return 404, 'Not found'
rows = query(statsqueries[req], *(statsqueries[req].count('?')*[param]))
res = {'x':[], 'y':[]}
for row in rows:
res['x'].append(row['x'])
res['y'].append(row['y'])
return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json')
@app.route('/stats/viewsperday')
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)
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]}
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())))
start += timedelta(days=1)
return Response(json.dumps(res, default=date_json_handler), mimetype='application/json')
......@@ -27,6 +27,7 @@
<script src="{{url_for('static', filename='js.cookie.js')}}"></script>
{%if ismod() %}
<script src="{{url_for('static', filename='moderator.js')}}"></script>
<script src="{{url_for('static', filename='plotly.min.js')}}"></script>
{% endif %}
<script src="{{url_for('static', filename='videojs/video.js')}}"></script>
<script src="{{url_for('static', filename='videojs/ie8/videojs-ie8.js')}}"></script>
......
......@@ -4,6 +4,7 @@
{% from 'macros.html' import moderator_checkbox %}
{% from 'macros.html' import preview %}
{% from 'macros.html' import moderator_permissioneditor %}
{% from 'macros.html' import stats_viewsperday, stats_generic %}
{% extends "base.html" %}
{% block title %}- {{course.title}}{% endblock %}
......@@ -46,6 +47,10 @@
</tbody>
</table>
</div>
<div id="statview" class="col-xs-6" style="height:600px"></div>
{{stats_viewsperday("statview", "Abrufe pro Tag", course=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 %}
</div>
</div>
......
......@@ -5,6 +5,7 @@
{% from 'macros.html' import moderator_editor %}
{% from 'macros.html' import moderator_delete %}
{% from 'macros.html' import moderator_checkbox %}
{% from 'macros.html' import stats_viewsperday %}
{% set page_border = 1 -%}
{% extends "base.html" %}
......@@ -58,6 +59,8 @@
{% endfor %}
</table>
</div>
<div id="statview" class="col-xs-12" style="height:600px"></div>
{{stats_viewsperday("statview", "Abrufe pro Tag", lecture=lecture.id)}}
{% endif %}
</div>
</div>
......
......@@ -245,3 +245,63 @@ $('#embedcodebtn').popover(
{% endmacro %}
{% 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) %}
<script>
$.ajax({
method: "GET",
url: "{{url_for('stats_viewsperday', course=course, lecture=lecture, video=video)}}",
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}});
}
if (data.views.length > 2)
traces.push({"x": data.times, "y": data.views[0].vals, "type": "{{type}}", "name": "gesamt", line: {"color": "black", "width": 2}});
var layout = {
"title": "{{title}}",
"showlegend": (traces.length != 1)
};
Plotly.newPlot("{{id}}", traces, layout, { "modeBarButtonsToRemove": ['sendDataToCloud','hoverCompareCartesian'], "displaylogo": false});
}
});
</script>
{% endmacro %}
{% macro stats_generic(id, req, title, param=None, type="scatter") %}
<script>
$.ajax({
method: "GET",
url: "{{url_for('stats_generic', req=req, param=param)}}",
dataType: "json",
error: moderator.api.handleapierror,
success: function (data) {
var layout = {
"title": "{{title}}",
"showlegend": false
};
Plotly.newPlot("{{id}}", [{"x": data.x, "y": data.y, "type": "{{type}}"}], layout, { "modeBarButtonsToRemove": ['sendDataToCloud','hoverCompareCartesian'], "displaylogo": false});
}
});
</script>
{% endmacro %}
{% macro stats_pie(id, req, title, showlegend=True, param=None) %}
<script>
$.ajax({
method: "GET",
url: "{{url_for('stats_generic', req=req, param=param)}}",
dataType: "json",
error: moderator.api.handleapierror,
success: function (data) {
var layout = {
"title": "{{title}}",
"showlegend": {% if showledgend %}true{% else %}false{% endif %}
};
Plotly.newPlot("{{id}}", [{"labels": data.x, "values": data.y, "type": "pie"}], layout, { "modeBarButtonsToRemove": ['sendDataToCloud','hoverCompareCartesian'], "displaylogo": false});
}
});
</script>
{% endmacro %}
{% from 'macros.html' import preview %}
{% from 'macros.html' import stats_viewsperday, stats_generic, stats_pie %}
{% extends "base.html" %}
{% block header %}
......@@ -14,52 +14,20 @@
</div>
<div class="panel-body">
<div class="row col-xs-12">
<div id="coursespersemester" style="height:600px;" class="col-xs-6" ></div>
<script>
$.ajax({
method: "GET",
url: "/stats/data/course_count",
dataType: "json",
error: moderator.api.handleapierror,
success: function (data) {
var trace = {"y": [], "x": [], "type": "scatter"};
for (var i=0; i < data.length; i++) {
if (data[i].semester != "") {
trace.x.push(data[i].semester)
trace.y.push(data[i].count)
}
}
var layout = {
"title": 'Veranstaltungen pro Semester',
"showlegend": false
};
Plotly.newPlot('coursespersemester', [trace], layout, { "modeBarButtonsToRemove": ['sendDataToCloud','hoverCompareCartesian'], "displaylogo": false});
}
});
</script>
<div id="lecturespersemester" style="height:600px" class="col-xs-6"></div>
<script>
$.ajax({
method: "GET",
url: "/stats/data/lectures_count",
dataType: "json",
error: moderator.api.handleapierror,
success: function (data) {
var trace = {"y": [], "x": [], "type": "scatter"};
for (var i=0; i < data.length; i++) {
if (data[i].semester != "") {
trace.x.push(data[i].semester)
trace.y.push(data[i].count)
}
}
var layout = {
"title": 'Aufnahmen pro Semester',
"showlegend": false
};
Plotly.newPlot('lecturespersemester', [trace], layout, { "modeBarButtonsToRemove": ['sendDataToCloud','hoverCompareCartesian'], "displaylogo": false});
}
});
</script>
<div id="course_count" class="col-xs-6" style="height:600px"></div>
{{stats_generic("course_count", "course_count", "Veranstaltungen pro Jahr")}}
<div id="lectures_count" class="col-xs-6" style="height:600px"></div>
{{stats_generic("lectures_count", "lectures_count", "Aufnahmen pro Jahr")}}
<div id="formats_views" class="col-xs-6" style="height:600px"></div>
{{stats_pie("formats_views", "formats_views", "Formatnutzung")}}
<div id="organizer_courses" class="col-xs-6" style="height:600px"></div>
{{stats_pie("organizer_courses", "organizer_courses", "Veranstaltungne nach Dozent", showlegend=False)}}
<div id="categories_courses" class="col-xs-6" style="height:600px"></div>
{{stats_pie("categories_courses", "categories_courses", "Veranstaltungen nach Kategorie", showlegend=False)}}
<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")}}
</div>
i </div>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment