dashboard: add graph score
This commit is contained in:
parent
d66de6fb3c
commit
b9fa5accff
|
@ -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 = [
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"> </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>
|
||||
|
|
|
@ -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)";
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue