token-validator: update dashboard: include visualization for individual student

This commit is contained in:
nemunaire 2020-04-26 23:56:44 +02:00
parent 9f909b9cba
commit 85ad80a671
5 changed files with 130 additions and 39 deletions

View File

@ -9,6 +9,14 @@
font-size: 13px;
margin-top: 0.2vh;
}
.dropdown a {
display: block;
color: black;
}
.dropdown a:hover {
background-color: #ccc;
text-decoration: none;
}
.student {
background-position: center;
background-repeat: no-repeat;
@ -46,24 +54,70 @@
<base href="/">
</head>
<body>
<div ng-controller="StudentsProgressionController" class="d-flex flex-wrap" ng-cloak>
<div class="card student student-title d-flex flex-column justify-content-around" style="background-image: url('https://srs.epita.fr/assets/images/logo-srs.png');">
<h5 class="text-center" title="SRS" ng-cloak>ADLIN TP {{tutoid+1}}</h5>
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
<span class="badge" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-success': stats[ch].success > 0 || (tutoid == 0 && stats[ch].warning > 0), 'badge-warning': tutoid != 0 && stats[ch].success == 0 && stats[ch].warning > 0, 'badge-danger': !stats[ch] || (stats[ch].success == 0 && stats[ch].warning == 0)}" title="{{ t.title }}: {{ stats[ch].success }} - {{ stats[ch].warning }}/{{ stats.total }}" ng-cloak>{{ stats[ch].warning * 100 / stats.total | number:0 }} %</span>
</div>
<div ng-cloak ng-if="onestudent">
<div ng-controller="StudentProgressionController" class="m-4">
<div class="card-group col-6 offset-3">
<div class="card">
<img src="https://photos.cri.epita.fr/thumb/{{ img | lowercase }}" class="card-img-top" alt="{{ student.login }}">
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ student.login }}</h5>
<p class="card-text" ng-controller="PingController">
<span class="badge" ng-class="{'badge-success': PING && PING < 120, 'badge-info': PING && PING >= 120 && PING < 300, 'badge-warning': PING && PING >= 300 && PING < 900, 'badge-danger': PING && PING >= 900, 'badge-dark': !PING}" title="{{ PING_time }}">
&#x1f4bb;
</span>
<ng-pluralize count="PING" when="{'NaN': 'jamais vu', 'one': 'vu il y a {} seconde', 'other': 'vu il y a {} secondes'}"></ng-pluralize>
<ul ng-if="ips" style="padding-left:0">
<li><strong>Domaine&nbsp;:</strong> <a href="http://{{ips.adn}}/">{{ips.adn}}</a></li>
<li><strong>Délégation&nbsp;:</strong> <a href="http://{{ips.ddn}}/">{{ips.ddn}}</a></li>
<li><strong>IPv6&nbsp;:</strong> {{ips.wg}}1/96</li>
</ul>
<div ng-repeat="(tutoid,tuto) in tuto_progress">
<hr>
<h6>TP {{tutoid+1}}</h6>
<span class="badge mr-1" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
</div>
</p>
</div>
</div>
</div>
</div>
</div>
<div ng-cloak ng-if="!onestudent">
<div ng-controller="StudentsProgressionController" class="d-flex flex-wrap">
<div class="card student student-title d-flex flex-column justify-content-around" style="background-image: url('https://srs.epita.fr/assets/images/logo-srs.png');">
<h5 class="text-center" title="SRS" ng-cloak>
ADLIN TP {{tutoid+1}}
<span ng-click="toogleDropdown()">
<svg class="bi bi-chevron-down" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 01.708 0L8 10.293l5.646-5.647a.5.5 0 01.708.708l-6 6a.5.5 0 01-.708 0l-6-6a.5.5 0 010-.708z" clip-rule="evenodd"/>
</svg>
</span>
</h5>
<div style="padding: 0 60px; position: absolute" class="student-title" ng-if="show_dropdown">
<div style="background-color: #ddd" class="dropdown">
<a href="/dashboard/1" ng-click="toogleDropdown()">TP 1</a>
<a href="/dashboard/2" ng-click="toogleDropdown()">TP 2</a>
<a href="/dashboard/3" ng-click="toogleDropdown()">TP 3</a>
</div>
</div>
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
<span class="badge" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-success': stats[ch].success > 0 || (tutoid == 0 && stats[ch].warning > 0), 'badge-warning': tutoid != 0 && stats[ch].success == 0 && stats[ch].warning > 0, 'badge-danger': !stats[ch] || (stats[ch].success == 0 && stats[ch].warning == 0)}" title="{{ t.title }}: {{ stats[ch].success }} - {{ stats[ch].warning }}/{{ stats.total }}" ng-cloak>{{ stats[ch].warning * 100 / stats.total | number:0 }} %</span>
</div>
</div>
<div class="card student d-flex flex-column justify-content-between" ng-repeat="(login, mychallenges) in students" style="background-image: url('https://photos.cri.epita.fr/square/{{ mychallenges.img | lowercase }}')">
<h5 class="login text-truncate" title="{{ login }}">
<span class="badge" ng-class="{'badge-success': mychallenges['ping'] && mychallenges['ping'].recent < 120, 'badge-info': mychallenges['ping'] && mychallenges['ping'].recent >= 120 && mychallenges['ping'].recent < 300, 'badge-warning': mychallenges['ping'] && mychallenges['ping'].recent >= 300 && mychallenges['ping'].recent < 900, 'badge-danger': mychallenges['ping'] && mychallenges['ping'].recent >= 900, 'badge-dark': !mychallenges['ping']}" title="{{ mychallenges['ping'].time }}">
&#x1f4bb;
</span>
{{ login }}
</h5>
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
<span class="badge" style="margin-left: .07rem" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
</div>
<div class="card student d-flex flex-column justify-content-between" ng-repeat="(login, mychallenges) in students" style="background-image: url('https://photos.cri.epita.fr/square/{{ mychallenges.img | lowercase }}')">
<h5 class="login text-truncate" title="{{ login }}">
<span class="badge" ng-class="{'badge-success': mychallenges['ping'] && mychallenges['ping'].recent < 120, 'badge-info': mychallenges['ping'] && mychallenges['ping'].recent >= 120 && mychallenges['ping'].recent < 300, 'badge-warning': mychallenges['ping'] && mychallenges['ping'].recent >= 300 && mychallenges['ping'].recent < 900, 'badge-danger': mychallenges['ping'] && mychallenges['ping'].recent >= 900, 'badge-dark': !mychallenges['ping']}" title="{{ mychallenges['ping'].time }}">
&#x1f4bb;
</span>
<a class="text-dark" href="/dashboard/{{login}}">{{ login }}</a>
</h5>
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
<span class="badge" style="margin-left: .07rem" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
</div>
</div>
</div>
</div>

View File

@ -36,7 +36,7 @@ angular.module("AdLinApp", ["ngResource", "ngSanitize"])
return $resource("/api/progress")
})
.factory("StudentProgression", function($resource) {
return $resource("/api/dashboard.json")
return $resource("/api/students/:studentId/progress", { studentId: '@id' })
})
.factory("Challenge", function($resource) {
return $resource("/challenge/:challengeId", { challengeId: '@id' })
@ -46,11 +46,18 @@ angular.module("AdLinApp")
.run(function($rootScope, $location) {
if (window.location.pathname.split("/").length >= 3) {
$rootScope.tutoid = parseInt(window.location.pathname.split("/")[2], 10);
if (isNaN($rootScope.tutoid)) {
$rootScope.onestudent = window.location.pathname.split("/")[2];
}
}
if (!$rootScope.tutoid || isNaN($rootScope.tutoid)) {
$rootScope.tutoid = 1;
}
$rootScope.tutoid--;
$rootScope.show_dropdown = false;
$rootScope.toogleDropdown = function() {
$rootScope.show_dropdown = !$rootScope.show_dropdown;
}
})
.controller("StudentsController", function($scope, $interval, Student) {
$scope.students = Student.query();
@ -103,19 +110,48 @@ angular.module("AdLinApp")
refreshStd();
$interval(refreshStd, 9750);
})
.controller("StudentProgressionController", function($scope, $interval, $http, Student, StudentProgression) {
$scope.tuto_progress = tuto_progress;
var refreshStd = function() {
$scope.student = Student.get({studentId: $scope.onestudent})
$scope.img = $scope.onestudent == "nemunaire" ? "mercie_d" : $scope.onestudent
$scope.mychallenges = StudentProgression.get({studentId: $scope.onestudent})
$scope.mychallenges.$promise.then(function(mychallenges) {
angular.forEach(mychallenges, function(ch, chid) {
if (ch.time) {
mychallenges[chid].time = new Date(ch.time);
mychallenges[chid].recent = (Date.now() - mychallenges[chid].time)/1000;
}
});
})
$scope.student.$promise.then(function(student) {
$http.get("/api/students/" + $scope.student.id + "/ips").then(function(response) {
$scope.ips = response.data;
});
})
}
$scope.$watch("onestudent", function(onestudent) {
refreshStd();
$interval(refreshStd, 15000);
})
})
.controller("PingController", function($scope, $interval, $http) {
$scope.PING = false;
$scope.PING_time = '';
$scope.PING_ok = false;
var refreshPing = function() {
var refreshPing = function() {
$http.get("/api/students/" + $scope.student.id + "/ping").then(function(response) {
$scope.PING_ok = response.data.State;
$scope.PING_time = new Date(response.data.Date);
$scope.PING = (Date.now() - $scope.PING_time)/1000;
});
}
refreshPing();
$interval(refreshPing, 15000);
$scope.$watch("student", function(student) {
student.$promise.then(function(std) {
refreshPing();
$interval(refreshPing, 15000);
})
})
})
.controller("SSHController", function($scope, $interval, $http) {
$scope.SSH = false;

View File

@ -11,13 +11,24 @@
</h2>
<h2 ng-if="isLogged">
Qu'allons-nous faire aujourd'hui&nbsp;?
Qu'allons-nous faire aujourd'hui&nbsp;? <a href="/dashboard/{{ isLogged.login }}" class="text-muted">Voir l'avancement...</a>
</h2>
<div ng-controller="ProgressionController" ng-if="isLogged">
<span class="badge" ng-class="{'badge-success': mychallenges[100], 'badge-danger': !mychallenges[100]}">HTTP</span>
<span class="badge" ng-class="{'badge-success': mychallenges[101], 'badge-danger': !mychallenges[101]}">HTTPS</span>
<span class="badge" ng-class="{'badge-success': mychallenges[102], 'badge-danger': !mychallenges[102]}">DNS</span>
<div ng-controller="ProgressionController" class="row" ng-if="isLogged">
<div class="col">
<strong>TP 2&nbsp;</strong>
<span class="badge" ng-class="{'badge-success': mychallenges[100], 'badge-danger': !mychallenges[100]}">HTTP</span>
<span class="badge" ng-class="{'badge-success': mychallenges[101], 'badge-danger': !mychallenges[101]}">HTTPS</span>
<span class="badge" ng-class="{'badge-success': mychallenges[102], 'badge-danger': !mychallenges[102]}">DNS</span>
<span class="badge" ng-class="{'badge-success': mychallenges[103], 'badge-danger': !mychallenges[103]}">Matrix</span>
</div>
<div class="col">
<strong>TP 3&nbsp;</strong>
<span class="badge" ng-class="{'badge-success': mychallenges[200], 'badge-danger': !mychallenges[200]}">HTTP</span>
<span class="badge" ng-class="{'badge-success': mychallenges[201], 'badge-danger': !mychallenges[201]}">HTTPS</span>
<span class="badge" ng-class="{'badge-success': mychallenges[202], 'badge-danger': !mychallenges[202]}">DNS</span>
<span class="badge" ng-class="{'badge-success': mychallenges[203], 'badge-danger': !mychallenges[203]}">Matrix</span>
</div>
</div>
</div>

View File

@ -8,7 +8,6 @@ import (
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/julienschmidt/httprouter"
@ -49,11 +48,7 @@ func init() {
http.ServeFile(w, r, path.Join(StaticDir, "dashboard.html"))
})
Router().GET("/dashboard/:where", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if _, err := strconv.ParseInt(string(ps.ByName("where")), 10, 64); err != nil {
http.NotFound(w, r)
} else {
http.ServeFile(w, r, path.Join(StaticDir, "dashboard.html"))
}
http.ServeFile(w, r, path.Join(StaticDir, "dashboard.html"))
})
Router().GET("/dashboard/:where/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
r.URL.Path = strings.TrimPrefix(r.URL.Path, "/dashboard")

View File

@ -7,7 +7,6 @@ import (
"net/http"
"path"
"strings"
"strconv"
"github.com/julienschmidt/httprouter"
)
@ -91,14 +90,10 @@ func init() {
}
})
Router().GET("/dashboard/:where", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if _, err := strconv.ParseInt(string(ps.ByName("where")), 10, 64); err != nil {
http.NotFound(w, r)
if data, err := Asset("htdocs/dashboard.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
if data, err := Asset("htdocs/dashboard.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
w.Write(data)
}
w.Write(data)
}
})
Router().GET("/dashboard/:where/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {