From b9fa5accff8759bfdf783e76a9acaa119120c248 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 04:17:53 +0100 Subject: [PATCH] dashboard: add graph score --- admin/static/js/app.js | 46 +++++++++++- admin/static/views/public.html | 27 ++++++- dashboard/static.go | 5 +- dashboard/static/index.html | 23 +++++- dashboard/static/js/dashboard.js | 123 +++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 8 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 42771667..a4841b8f 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -871,9 +871,53 @@ angular.module("FICApp") type: "table", params: { kind: "levels", levels: [1,2,3,4,5], themes: $scope.themes.map(function(z, i) { return z.id; }), total: true }, }, + { + type: "graph", + params: { teams: [], height: 400, legend: true }, + }, + ]; + angular.forEach($scope.teams, function(team, tid) { + if (team.rank >= 1 && team.rank <= 9) + $scope.display.scenes[1].params.teams.push(tid) + }); + $scope.display.side = [ + { + type: "exercice_follow", + params: { }, + }, + ]; + } + else if (scene == "summary2") { + $scope.display.scenes = [ + { + type: "graph", + params: { teams: [], height: 400, legend: false }, + }, { type: "rank", - params: { limit: 10, which: "general" }, + params: { limit: 10, which: "general", legend: true }, + }, + ]; + angular.forEach($scope.teams, function(team, tid) { + if (team.rank >= 1 && team.rank <= 9) + $scope.display.scenes[0].params.teams.push(tid) + }); + $scope.display.side = [ + { + type: "exercice_follow", + params: { }, + }, + ]; + } + else if (scene == "summary3") { + $scope.display.scenes = [ + { + type: "table", + params: { kind: "levels", levels: [1,2,3,4,5], themes: $scope.themes.map(function(z, i) { return z.id; }), total: true }, + }, + { + type: "rank", + params: { limit: 10, which: "general", legend: false }, }, ]; $scope.display.side = [ diff --git a/admin/static/views/public.html b/admin/static/views/public.html index 17f762e3..45bb5b71 100644 --- a/admin/static/views/public.html +++ b/admin/static/views/public.html @@ -12,6 +12,8 @@ Lancement Résumé + Résumé 2 + Résumé 3 Free Hint Quarter Happy Hour @@ -67,7 +69,7 @@ -
+
@@ -161,7 +163,7 @@
-
+
+
+
+ +
+
+
+
+ +
+
+
+
@@ -222,7 +243,7 @@ -
+
diff --git a/dashboard/static.go b/dashboard/static.go index 3f272339..5e21583e 100644 --- a/dashboard/static.go +++ b/dashboard/static.go @@ -93,12 +93,11 @@ func init() { http.ServeFile(w, r, path.Join(TeamsDir, "public", "my.json")) } }) - api.Router().GET("/stats.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + api.Router().GET("/api/teams/:tid/score-grid.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if forwarder != nil { fwd_request(w, r, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "public", "stats.json")) + fwd_request(w, r, "http://127.0.0.1:8081/") } }) api.Router().GET("/api/teams/:tid/stats.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { diff --git a/dashboard/static/index.html b/dashboard/static/index.html index feb51ded..c9f75867 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -13,6 +13,17 @@ +
@@ -166,13 +177,22 @@ {{ r.rank }}ere - {{ r.name }} +    {{ r.name }} {{ r.score | number:0 }}
+
+
+

+

+
+
+
+
+

@@ -560,6 +580,7 @@ + diff --git a/dashboard/static/js/dashboard.js b/dashboard/static/js/dashboard.js index d0966693..07eb3e5e 100644 --- a/dashboard/static/js/dashboard.js +++ b/dashboard/static/js/dashboard.js @@ -167,4 +167,127 @@ angular.module("FICApp", ["ngSanitize", "ngAnimate"]) $http.get("/api/teams/" + $scope.team.id + "/stats.json").then(function(response) { $scope.mystats = response.data; }); + }) + .controller("RankGraphController", function($scope, $q) { + var margin = {left: 50, right: 20, top: 20, bottom: 50 }; + + var width = $("#rank_graph").width() - margin.left - margin.right; + var height = $scope.s.params.height - margin.top - margin.bottom; + + var xNudge = 50; + var yNudge = 20; + + var max = 0; + var minDate = new Date(); + var maxDate = new Date("2017-12-01"); + if ($scope.settings && $scope.settings.end) + maxDate = $scope.settings.end; + + + var teams = {} + + var loopPromises = []; + $scope.s.params.teams.forEach(function (teamid) { + var deferred = $q.defer(); + loopPromises.push(deferred.promise); + d3.json("api/teams/" + teamid + "/score-grid.json", function (rows) { + if (rows == null) return; + rows.sort(function (a,b) { return a.time > b.time ? 1 : -1 }) + var nrows = {}; + var sum = 0; + rows.forEach(function (row) { + if (!nrows[row.time]) + nrows[row.time] = 0; + var pts = row.points * row.coeff; + sum += pts; + nrows[row.time] = sum; + if (sum > max) + max = sum + }) + teams[teamid] = [] + Object.keys(nrows).forEach(function (t) { + var d = new Date(t) + if (d < minDate) + minDate = d; + if (d > maxDate) + maxDate = d; + teams[teamid].push({time: d, points: nrows[t]}) + }) + deferred.resolve(); + }); + }) + + $q.all(loopPromises).then(function(){ + var y = d3.scale.linear() + .domain([0,max]) + .range([height,0]); + + var x = d3.time.scale() + .domain([minDate,maxDate]) + .range([0,width]); + + var yAxis = d3.svg.axis() + .orient("left") + .scale(y); + + var xAxis = d3.svg.axis() + .orient("bottom") + .tickFormat(d3.time.format("%H:%M")) + .scale(x); + + var line = d3.svg.line() + .x(function(d){ return x(d.time); }) + .y(function(d){ return y(d.points); }) + .interpolate("cardinal"); + + var svg = d3.select("#rank_graph").append("svg").attr("id","svg").attr("height",$scope.s.params.height).attr("width","100%"); + var chartGroup = svg.append("g").attr("class","chartGroup").attr("transform","translate("+xNudge+","+yNudge+")"); + + chartGroup.append("g") + .attr("class","axis x") + .attr("transform","translate(0,"+height+")") + .call(xAxis); + + chartGroup.append("g") + .attr("class","axis y") + .call(yAxis); + + angular.forEach(teams, function(rows, tid) { + var team = $scope.teams[tid] + chartGroup.append("path") + .attr("style","fill: none; stroke: " + team.color + "; stroke-width: 2px;") + .attr("d",function(d){ return line(rows); }) + }) + + if ($scope.s.params.legend) { + var legend = svg.append("g") + .attr("style", "fill: white;") + .attr("height", 100) + .attr("width", 100) + .attr('transform', 'translate(70,20)') + + legend.selectAll('rect') + .data(Object.keys(teams)) + .enter() + .append("rect") + .attr("x", 0) + .attr("y", function(d, i){ return i * 23;}) + .attr("width", 15) + .attr("height", 15) + .style("fill", function(d) { + return $scope.teams[d].color; + }) + + legend.selectAll('text') + .data(Object.keys(teams)) + .enter() + .append("text") + .attr("font-size", "23px") + .attr("x", 20) + .attr("y", function(d, i){ return i * 23 + 14;}) + .text(function(d) { + return $scope.teams[d].name + " (" + $scope.teams[d].rank + "e : " + Math.ceil($scope.teams[d].score) + " pts)"; + }); + } }); + })