diff --git a/static/moderator.js b/static/moderator.js index c3309dc26e204cf7b90ea81afcaa1a27d8f71f15..858b5f57d4708a44272801546d04f6aa35c01b25 100644 --- a/static/moderator.js +++ b/static/moderator.js @@ -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)}); +}); diff --git a/static/style.css b/static/style.css index cc1b3e312d2b0825f614e115c4a892be895a5e96..5b442c6921d0b5cb7d428121ce3b1bdc0b7d5e39 100644 --- a/static/style.css +++ b/static/style.css @@ -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); } +} diff --git a/stats.py b/stats.py index e383645a7b798541a118becf93ce78e1319cdbf5..48c1022df6c61d09b76934334c177458beeb4a21 100644 --- a/stats.py +++ b/stats.py @@ -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') diff --git a/templates/course.html b/templates/course.html index 0b02b864137f8d22d7876be65c13388a89bb50ab..ba850fc9c1b35e02f602554d7b15aaecd20058bb 100644 --- a/templates/course.html +++ b/templates/course.html @@ -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> diff --git a/templates/lecture.html b/templates/lecture.html index d7705bf32a98c3be61c1a478c8e04cda1a670dd8..d3561fb71fe7fc33952b60436d9e3833dae3339c 100644 --- a/templates/lecture.html +++ b/templates/lecture.html @@ -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> diff --git a/templates/macros.html b/templates/macros.html index a7de6566a945f814e5d51c8f9ac53830d90e1281..87de4e31bdd5d04859a769a5d9d800ef8ff21905 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -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 %} diff --git a/templates/stats.html b/templates/stats.html index eafcd20a26e98009981bea31db7945d3036b6fc0..c7cb96aab7b11b643767610fe6534465afd8ae93 100644 --- a/templates/stats.html +++ b/templates/stats.html @@ -1,11 +1,4 @@ -{% 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 %}