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

Rewrote frontend part of statistics and adapted backend

parent 5b8e3965
No related branches found
No related tags found
No related merge requests found
...@@ -248,3 +248,46 @@ var moderator = { ...@@ -248,3 +248,46 @@ var moderator = {
}; };
$( document ).ready( function () { moderator.init(); } ); $( 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 @@ ...@@ -72,3 +72,49 @@
background-color: #f5f5f5; 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(): ...@@ -10,12 +10,12 @@ def stats():
return render_template('stats.html') return render_template('stats.html')
statsqueries = {} 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['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 GROUP BY semester" 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) 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 x, count(courses.id) AS y FROM courses GROUP BY courses.subject ORDER BY y DESC LIMIT 100" 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 x, count(courses.id) AS y FROM courses GROUP BY courses.organizer ORDER BY y 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 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['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" 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): def plotly_date_handler(obj):
...@@ -28,14 +28,15 @@ def stats_generic(req, param=None): ...@@ -28,14 +28,15 @@ def stats_generic(req, param=None):
if req not in statsqueries: if req not in statsqueries:
return 404, 'Not found' return 404, 'Not found'
rows = query(statsqueries[req], *(statsqueries[req].count('?')*[param])) rows = query(statsqueries[req], *(statsqueries[req].count('?')*[param]))
res = {'x':[], 'y':[]} res = {}
for row in rows: for row in rows:
if row['x'] != '': for key, val in row.items():
res['x'].append(row['x']) if key not in res:
else: res[key] = []
res['x'].append('leer') res[key].append(val)
res['y'].append(row['y']) import time
return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json') 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>')
@app.route('/stats/viewsperday/<req>/<param>') @app.route('/stats/viewsperday/<req>/<param>')
...@@ -70,12 +71,11 @@ def stats_viewsperday(req, param=""): ...@@ -70,12 +71,11 @@ def stats_viewsperday(req, param=""):
data[row['date']] = {} data[row['date']] = {}
data[row['date']][row['trace']] = row['y'] data[row['date']][row['trace']] = row['y']
end = date.today() end = date.today()
x = [] res = [{'name': trace, 'x': [], 'y': []} for trace in traces]
y = [{'name': trace, 'vals': []} for trace in traces]
while start and start <= end: while start and start <= end:
x.append(start) for trace in res:
for trace in y: trace['x'].append(start)
trace['vals'].append(data.get(start, {}).get(trace['name'], 0)) trace['y'].append(data.get(start, {}).get(trace['name'], 0))
start += timedelta(days=1) 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 @@ ...@@ -4,7 +4,6 @@
{% from 'macros.html' import moderator_checkbox %} {% from 'macros.html' import moderator_checkbox %}
{% from 'macros.html' import preview %} {% from 'macros.html' import preview %}
{% from 'macros.html' import moderator_permissioneditor %} {% from 'macros.html' import moderator_permissioneditor %}
{% from 'macros.html' import stats_viewsperday, stats_generic %}
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}- {{course.title}}{% endblock %} {% block title %}- {{course.title}}{% endblock %}
...@@ -47,10 +46,8 @@ ...@@ -47,10 +46,8 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div id="statview" class="col-xs-6" style="height:600px"></div> <div class="col-xs-6 plot-view" data-url="{{url_for('stats_viewsperday', req="course", param=course.id)}}"></div>
{{stats_viewsperday("statview", "course", "Abrufer pro Tag", param=course.id)}} <div class="col-xs-6 plot-view" data-type="bar" data-url="{{url_for('stats_generic', req="lecture_views", param=course.id)}}"></div>
<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 %} {% endif %}
</div> </div>
</div> </div>
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
{% from 'macros.html' import moderator_editor %} {% from 'macros.html' import moderator_editor %}
{% from 'macros.html' import moderator_delete %} {% from 'macros.html' import moderator_delete %}
{% from 'macros.html' import moderator_checkbox %} {% from 'macros.html' import moderator_checkbox %}
{% from 'macros.html' import stats_viewsperday %}
{% set page_border = 1 -%} {% set page_border = 1 -%}
{% extends "base.html" %} {% extends "base.html" %}
...@@ -59,8 +58,7 @@ ...@@ -59,8 +58,7 @@
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<div id="statview" class="col-xs-12" style="height:600px"></div> <div class="col-xs-12 plot-view" data-url="{{url_for('stats_viewsperday', req="lecture", param=lecture.id)}}"></div>
{{stats_viewsperday("statview", "lecture", "Abrufer pro Tag", param=lecture.id)}}
{% endif %} {% endif %}
</div> </div>
</div> </div>
......
...@@ -305,39 +305,3 @@ $('#embedcodebtn').popover( ...@@ -305,39 +305,3 @@ $('#embedcodebtn').popover(
}); });
</script> </script>
{% endmacro %} {% 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" %} {% extends "base.html" %}
{% block header %}
{{ super() }}
<script src="{{url_for('static', filename='plotly.min.js')}}"></script>
{% endblock %}
{% block content %} {% block content %}
<div class="panel-group"> <div class="panel-group">
<div class="panel panel-default"> <div class="panel panel-default">
...@@ -14,24 +7,17 @@ ...@@ -14,24 +7,17 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="row col-xs-12"> <div class="row col-xs-12">
<div id="course_count" class="col-xs-6" style="height:600px"></div> <div class="col-xs-12 col-md-6 plot-view" data-url="{{url_for('stats_generic', req="course_count")}}"></div>
{{stats_generic("course_count", "course_count", "Veranstaltungen pro Jahr")}} <div class="col-xs-12 col-md-6 plot-view" data-url="{{url_for('stats_generic', req="lectures_count")}}"></div>
<div id="lectures_count" class="col-xs-6" style="height:600px"></div> <div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="categories_courses")}}"></div>
{{stats_generic("lectures_count", "lectures_count", "Aufnahmen pro Jahr")}} <div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="categories_lectures")}}"></div>
<div id="formats_views" class="col-xs-6" style="height:600px"></div> <!--<div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="organizer_courses")}}"></div>-->
{{stats_pie("formats_views", "formats_views", "Formatnutzung")}} <!--<div class="col-xs-12 col-md-6 plot-view" data-type="pie" data-url="{{url_for('stats_generic', req="formats_views")}}"></div>-->
<div id="organizer_courses" class="col-xs-6" style="height:600px"></div> <div class="col-xs-12 plot-view" data-url="{{url_for('stats_viewsperday', req="global")}}"></div>
{{stats_pie("organizer_courses", "organizer_courses", "Veranstaltungne nach Dozent", showlegend=False)}} <div class="col-xs-12 plot-view" data-url="{{url_for('stats_viewsperday', req="courses")}}"></div>
<div id="categories_courses" class="col-xs-6" style="height:600px"></div> </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> </div>
i </div>
</div> </div>
</div> </div>
</script>
{% endblock %} {% endblock %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment