dashboard: add graph score

This commit is contained in:
nemunaire 2020-01-30 04:17:53 +01:00
parent d66de6fb3c
commit b9fa5accff
5 changed files with 216 additions and 8 deletions

View File

@ -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 = [

View File

@ -12,6 +12,8 @@
<a class="dropdown-item" ng-click="presetScene('start')">Lancement</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" ng-click="presetScene('summary')">Résumé</a>
<a class="dropdown-item" ng-click="presetScene('summary2')">Résumé 2</a>
<a class="dropdown-item" ng-click="presetScene('summary3')">Résumé 3</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" ng-click="presetScene('freehintquarter')">Free Hint Quarter</a>
<a class="dropdown-item" ng-click="presetScene('happyhour')">Happy Hour</a>
@ -67,7 +69,7 @@
</div>
</div>
<div class="form-group row" ng-if="scene.type == 'message' || scene.type == 'panel' || scene.type == 'carousel' || scene.type == 'countdown'">
<div class="form-group row" ng-if="scene.type == 'message' || scene.type == 'panel' || scene.type == 'carousel' || scene.type == 'countdown' || scene.type == 'graph'">
<label for="mtitle" class="col-sm-2 col-form-label col-form-label-sm">Titre</label>
<div class="col-sm-10">
<input type="text" id="mtitle" ng-model="scene.params.title" class="form-control form-control-sm">
@ -161,7 +163,7 @@
</div>
</div>
<div class="form-group row" ng-if="scene.type == 'table' && scene.params.kind == 'teams'">
<div class="form-group row" ng-if="(scene.type == 'table' && scene.params.kind == 'teams') || scene.type == 'graph'">
<label for="tteams" class="col-sm-2 col-form-label col-form-label-sm">Équipes à afficher</label>
<div class="col-sm-10">
<select class="custom-select custom-select-sm" id="tteams" multiple="1" ng-model="scene.params.teams" ng-options="tid as (t.rank + 'e - ' + t.name) for (tid,t) in teams">
@ -201,6 +203,25 @@
</div>
</div>
<div class="form-group row" ng-if="scene.type == 'graph'">
<label for="gheight" class="col-sm-2 col-form-label col-form-label-sm">Hauteur</label>
<div class="col-sm-10">
<input type="text" id="gheight" ng-model="scene.params.height" class="form-control form-control-sm" integer>
</div>
</div>
<div class="form-group row" ng-if="scene.type == 'graph' || (scene.type == 'rank' && scene.params.which == 'general')">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<div class="form-check">
<label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" ng-model="scene.params.legend">
<span class="custom-control-label">Afficher la légende</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
@ -222,7 +243,7 @@
<select class="custom-select custom-select-sm" id="type" ng-model="scene.type" ng-options="k as v for (k, v) in typeside"></select>
</div>
<div class="form-group row" ng-if="scene.type == 'panel' || scene.type == 'carousel' || scene.type == 'countdown'">
<div class="form-group row" ng-if="scene.type == 'panel' || scene.type == 'carousel' || scene.type == 'countdown' || scene.type == 'graph'">
<label for="ptype" class="col-sm-4 col-form-label col-form-label-sm">Couleur</label>
<div class="col-sm-8">
<select class="custom-select custom-select-sm" id="ptype" ng-model="scene.params.color" ng-options="k as v for (k, v) in colors"></select>

View File

@ -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) {

View File

@ -13,6 +13,17 @@
<link href="{{.urlbase}}css/glyphicon.css" type="text/css" rel="stylesheet" media="screen">
<link href="{{.urlbase}}css/fic.css" type="text/css" rel="stylesheet" media="screen">
<script src="{{.urlbase}}js/angular.min.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: slategray;
shape-rendering: crispEdges;
}
.axis .tick text {
fill: white;
}
</style>
</head>
<body class="bg-light bg-public" style="overflow: hidden;" class="container-fluid" ng-controller="DataController">
<div class="row" style="margin:10px 0">
@ -166,13 +177,22 @@
<tbody ng-if="s.params.which == 'general'">
<tr ng-repeat="r in rank | orderBy: 'rank' | limitTo: s.params.limit: s.params.begin">
<th class="text-right">{{ r.rank }}<sup ng-if="r.rank == 1">er</sup><sup ng-if="r.rank > 1">e</sup></th>
<td>{{ r.name }}</td>
<td><span class="badge" style="background-color: {{r.color}}" ng-if="s.params.legend">&nbsp;&nbsp;</span> {{ r.name }}</td>
<td>{{ r.score | number:0 }}</td>
</tr>
</tbody>
</table>
</div>
<div class="card niceborder bg-dark" ng-if="s.type == 'graph' && !s.params.hide">
<div class="card-header bg-{{ s.params.color }} text-white" ng-if="s.params.title">
<h3 style="margin:0" ng-bind="s.params.title" class="text-dark" ng-if="s.params.color == 'light'"></h3>
<h3 style="margin:0" ng-bind="s.params.title" ng-if="s.params.color != 'light'"></h3>
</div>
<div class="card-body bg-dark" id="rank_graph" ng-controller="RankGraphController">
</div>
</div>
<div class="card niceborder bg-dark" ng-if="s.type == 'carousel' && !s.params.hide">
<div class="card-header bg-{{ s.params.color }} text-white" ng-if="s.params.title">
<h3 style="margin:0" ng-bind="s.params.title" class="text-dark" ng-if="s.params.color == 'light'"></h3>
@ -560,6 +580,7 @@
<script src="{{.urlbase}}js/angular-route.min.js"></script>
<script src="{{.urlbase}}js/angular-sanitize.min.js"></script>
<script src="{{.urlbase}}js/i18n/angular-locale_fr-fr.js"></script>
<script src="{{.urlbase}}js/d3.v3.min.js"></script>
<script src="{{.urlbase}}js/dashboard.js"></script>
<script src="{{.urlbase}}js/common.js"></script>
</body>

View File

@ -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)";
});
}
});
})