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
Branches
No related tags found
No related merge requests found
......@@ -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 %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment