Commit c6d8e81d authored by Julian Rother's avatar Julian Rother
Browse files

Rewrote frontend part of statistics and adapted backend

parent 5b8e3965
......@@ -248,3 +248,46 @@ var moderator = {
};
$( document ).ready( function () { moderator.init(); } );
$( document ).ready( function () {
var l = $(".plot-view");
for (var i = 0; i < l.length; i ++) {
if (!l[i].id)
l[i].id = "plot-"+i;
$(l[i]).html('<div class="plot-loader"></div>');
$.ajax({
divobj: l[i],
method: "GET",
url: l[i].dataset.url,
dataType: "json",
error: function (jqXHR, textStatus, errorThrow) {
$(this.divobj).html('<div class="plot-error">'+errorThrow+'</div>');
},
success: function (traces) {
var layout = {margin: {l: 30, r: 30, t: 10, b: 70, pad: 0}};
for (var i = 0; i < traces.length; i ++) {
traces[i].type = this.divobj.dataset.type;
}
if (this.divobj.dataset.type == "pie")
layout.showlegend = false;
traces.sort(function (a, b) {
asum = 0;
bsum = 0;
for (var i = 0; i < a.y.length; i++)
asum += a.y[i]
for (var i = 0; i < b.y.length; i++)
bsum += b.y[i]
return bsum-asum;
});
for (var i = 0; i < traces.length; i++)
if (i > 20)
traces[i].visible = "legendonly";
$(this.divobj).html("");
Plotly.newPlot(this.divobj.id, traces, layout, { "modeBarButtonsToRemove": ['sendDataToCloud','hoverCompareCartesian'], "displaylogo": false});
}
});
};
});
$(window).on("resize", function () {
$(".plot-view").each(function () {Plotly.Plots.resize(this)});
});
......@@ -72,3 +72,49 @@
background-color: #f5f5f5;
}
}
.plot-view {
height: 600px;
}
.plot-loader {
position: absolute;
left: 50%;
top: 50%;
margin: -75px 0 0 -75px;
border: 10px solid #f3f3f3;
border-radius: 50%;
border-top: 10px solid #3498db;
width: 100px;
height: 100px;
-webkit-animation: spin 1s linear infinite;
animation: spin 1s linear infinite;
}
.plot-error {
position: absolute;
left: 50%;
top: 50%;
margin-right: -50%;
margin-bottom: -50%;
transform: translate(-50%, -50%);
}
.plot-error {
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
......@@ -10,12 +10,12 @@ def stats():
return render_template('stats.html')
statsqueries = {}
statsqueries['formats_views'] = "SELECT formats.description AS x, count(DISTINCT log.id) AS y FROM log JOIN videos ON (videos.id = log.video) 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) WHERE lectures.visible GROUP BY courses.subject ORDER BY y DESC LIMIT 100"
statsqueries['formats_views'] = "SELECT formats.description AS labels, count(DISTINCT log.id) AS `values` FROM log JOIN videos ON (videos.id = log.video) 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 WHERE semester != "" 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) WHERE semester != "" GROUP BY semester'
statsqueries['categories_courses'] = "SELECT courses.subject AS labels, count(courses.id) AS `values` FROM courses GROUP BY courses.subject ORDER BY labels DESC LIMIT 100"
statsqueries['organizer_courses'] = "SELECT courses.organizer AS labels, count(courses.id) AS `values` FROM courses GROUP BY courses.organizer ORDER BY labels DESC LIMIT 100"
statsqueries['categories_lectures'] = "SELECT courses.subject AS labels, count(lectures.id) AS `values` FROM lectures JOIN courses ON (courses.id = lectures.course_id) WHERE lectures.visible GROUP BY courses.subject ORDER BY `values` DESC LIMIT 100"
statsqueries['lecture_views'] = "SELECT lectures.time AS x, count(DISTINCT log.id) AS y FROM log JOIN videos ON (videos.id = log.video) 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):
......@@ -28,14 +28,15 @@ 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':[]}
res = {}
for row in rows:
if row['x'] != '':
res['x'].append(row['x'])
else:
res['x'].append('leer')
res['y'].append(row['y'])
return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json')
for key, val in row.items():
if key not in res:
res[key] = []
res[key].append(val)
import time
time.sleep(10)
return Response(json.dumps([res], default=plotly_date_handler), mimetype='application/json')
@app.route('/stats/viewsperday/<req>')
@app.route('/stats/viewsperday/<req>/<param>')
......@@ -70,12 +71,11 @@ def stats_viewsperday(req, param=""):
data[row['date']] = {}
data[row['date']][row['trace']] = row['y']
end = date.today()
x = []
y = [{'name': trace, 'vals': []} for trace in traces]
res = [{'name': trace, 'x': [], 'y': []} for trace in traces]
while start and start <= end:
x.append(start)
for trace in y:
trace['vals'].append(data.get(start, {}).get(trace['name'], 0))
for trace in res:
trace['x'].append(start)
trace['y'].append(data.get(start, {}).get(trace['name'], 0))
start += timedelta(days=1)
return Response(json.dumps({'times': x, 'views': y}, default=plotly_date_handler), mimetype='application/json')
return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json')
......@@ -4,7 +4,6 @@
{% 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 %}
......@@ -47,10 +46,8 @@
</tbody>
</table>
</div>
<div id="statview" class="col-xs-6" style="height:600px"></div>
{{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")}}
<div class="col-xs-6 plot-view" data-url="{{url_for('stats_viewsperday', req="course", param=course.id)}}"></div>
<div class="col-xs-6 plot-view" data-type="bar" data-url="{{url_for('stats_generic', req="lecture_views", param=course.id)}}"></div>
{% endif %}
</div>
</div>
......
......@@ -5,7 +5,6 @@
{% 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" %}
......@@ -59,8 +58,7 @@
{% endfor %}
</table>
</div>
<div id="statview" class="col-xs-12" style="height:600px"></div>
{{stats_viewsperday("statview", "lecture", "Abrufer pro Tag", param=lecture.id)}}
<div class="col-xs-12 plot-view" data-url="{{url_for('stats_viewsperday', req="lecture", param=lecture.id)}}"></div>
{% endif %}
</div>
</div>
......
......@@ -305,39 +305,3 @@ $('#embedcodebtn').popover(
});
</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 stats_viewsperday, stats_generic, stats_pie %}
{% extends "base.html" %}
{% block header %}
{{ super() }}
<script src="{{url_for('static', filename='plotly.min.js')}}"></script>
{% endblock %}
{% block content %}
<div class="panel-group">
<div class="panel panel-default">
......@@ -14,24 +7,17 @@
</div>
<div class="panel-body">
<div class="row col-xs-12">
<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", "global", "Abrufer pro Tag")}}
<div id="courseviewsperday" class="col-xs-12" style="height:600px"></div>
{{stats_viewsperday("courseviewsperday", "courses", "Abrufer pro Tag")}}
<div class="col-xs-12 col-md-6 plot-view" data-url="{{url_for('stats_generic', req="course_count")}}"></div>
<div class="col-xs-12 col-md-6 plot-view" data-url="{{url_for('stats_generic', req="lectures_count")}}"></div>
<div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="categories_courses")}}"></div>
<div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="categories_lectures")}}"></div>
<!--<div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="organizer_courses")}}"></div>-->
<!--<div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="formats_views")}}"></div>-->
<div class="col-xs-12 plot-view" data-url="{{url_for('stats_viewsperday', req="global")}}"></div>
<div class="col-xs-12 plot-view" data-url="{{url_for('stats_viewsperday', req="courses")}}"></div>
</div>
i </div>
</div>
</div>
</div>
</script>
{% endblock %}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment