Commit 6cebde6224e26b4087d20aca6c5212fa0736b676

Authored by Miguel Barao
1 parent f87485b8
Exists in master and in 1 other branch dev

- added functional admin page.

serve.py
... ... @@ -26,11 +26,11 @@ class WebApplication(tornado.web.Application):
26 26 (r'/login', LoginHandler),
27 27 (r'/logout', LogoutHandler),
28 28 (r'/test', TestHandler),
29   - (r'/review', ReviewHandler), # FIXME
30   - # (r'/admin', AdminHandler), # FIXME
  29 + (r'/review', ReviewHandler), # FIXME
  30 + (r'/admin', AdminHandler),
31 31 # (r'/change_password', ChangePasswordHandler),
32   - (r'/static/(.+)', FileHandler),
33   - (r'/', RootHandler),
  32 + (r'/static/(.+)', FileHandler), # FIXME
  33 + (r'/', RootHandler), # TODO multiple tests
34 34 ]
35 35  
36 36 settings = {
... ... @@ -177,7 +177,7 @@ class ReviewHandler(BaseHandler):
177 177 try:
178 178 f = open(path.expanduser(fname))
179 179 except FileNotFoundError:
180   - logging.error('Cannot find "{}" for review.'.format(fname))
  180 + logging.error(f'Cannot find "{fname}" for review.')
181 181 except Exception as e:
182 182 raise e
183 183 else:
... ... @@ -211,72 +211,62 @@ class GiveupHandler(BaseHandler):
211 211 class RootHandler(BaseHandler):
212 212 @tornado.web.authenticated
213 213 def get(self):
214   - # if self.current_user == '0':
215   - # self.redirect('/admin')
216   - # else:
217   - # self.redirect('/test')
218   - # self.write("Hello, world")
219   - self.redirect('/test')
220   -
  214 + if self.current_user == '0':
  215 + self.redirect('/admin')
  216 + else:
  217 + self.redirect('/test')
221 218  
222 219 # ============================================================================
223   -# Admin
  220 +# Admin FIXME
224 221 # ============================================================================
225 222 class AdminHandler(BaseHandler):
  223 +
  224 +
226 225 @tornado.web.authenticated
227 226 def get(self):
228 227 if self.current_user != '0':
229 228 self.redirect('/')
230 229 self.render('admin.html')
231 230  
232   -# # ============================================================================
233   -# # Admin webservice
234   -# # ============================================================================
235   -# class AdminWebService(object):
236   -# exposed = True
237   -# _cp_config = {
238   -# 'auth.require': [name_is('0')]
239   -# }
240 231  
241   -# def __init__(self, app):
242   -# self.app = app
  232 + @tornado.web.authenticated
  233 + def post(self):
  234 + print('admin post')
  235 + if self.current_user != '0':
  236 + self.redirect('/')
243 237  
244   -# @cherrypy.tools.accept(media='application/json') # FIXME
245   -# def GET(self):
246   -# data = {
247   -# 'students': self.app.get_students_state(),
248   -# 'test': self.app.testfactory
249   -# }
250   -# return json.dumps(data, default=str)
  238 + cmd = self.get_body_argument('cmd', None)
  239 + value = self.get_body_argument('value', None)
251 240  
252   -# @cherrypy.tools.accept(media='application/json') # FIXME
253   -# def POST(self, **args):
254   -# if args['cmd'] == 'allow':
255   -# if args['value'] == 'true':
256   -# return self.app.allow_student(args['name'])
257   -# else:
258   -# return self.app.deny_student(args['name'])
  241 + if cmd == 'get_students':
  242 + data = {
  243 + 'students': self.testapp.get_students_state(),
  244 + 'test': self.testapp.testfactory
  245 + }
  246 + self.write(json.dumps(data, default=str))
  247 +
  248 + elif cmd == 'allow':
  249 + self.write(self.testapp.allow_student(value))
259 250  
260   -# elif args['cmd'] == 'reset':
261   -# return self.app.reset_password(args['name'])
  251 + elif cmd == 'deny':
  252 + self.write(self.testapp.deny_student(value))
  253 +
  254 + elif cmd == 'reset_password':
  255 + self.write(self.testapp.reset_password(value))
  256 +
  257 + elif cmd == 'insert_student':
  258 + value = json.loads(value)
  259 + self.write(self.testapp.insert_new_student(uid=value['number'], name=value['name']))
  260 +
  261 + else:
  262 + logging.error(f'Unknown command in post: "{cmd}"')
262 263  
263   -# elif args['cmd'] == 'insert':
264   -# return self.app.insert_new_student(uid=args['number'], name=args['name'])
265 264  
266   -# else:
267   -# print(args) # FIXME
268 265  
269 266 # # ============================================================================
270 267 # # Student webservice
271 268 # # ============================================================================
272 269 # class StudentWebService(object):
273   -# exposed = True
274   -# _cp_config = {
275   -# 'auth.require': []
276   -# }
277   -
278   -# def __init__(self, app):
279   -# self.app = app
280 270  
281 271 # @cherrypy.tools.accept(media='application/json') # FIXME
282 272 # def POST(self, **args):
... ... @@ -285,13 +275,6 @@ class AdminHandler(BaseHandler):
285 275 # v = json.loads(args['value'])
286 276 # self.app.set_student_focus(uid=args['number'], value=v)
287 277  
288   -# ============================================================================
289   -# Webserver root
290   -# ============================================================================
291   -# class Root(object):
292   -# def __init__(self, app):
293   -# self.app = app
294   -
295 278  
296 279  
297 280 # -------------------------------------------------------------------------
... ...
static/js/admin.js
... ... @@ -16,8 +16,8 @@ $(document).ready(function() {
16 16 var number = $("#reset_number").val();
17 17 $.ajax({
18 18 type: "POST",
19   - url: "/adminwebservice",
20   - data: {"cmd": "reset", "name": number}
  19 + url: "/admin",
  20 + data: {"cmd": "reset_password", "value": number}
21 21 });
22 22 }
23 23 );
... ... @@ -25,11 +25,13 @@ $(document).ready(function() {
25 25 function () {
26 26 $.ajax({
27 27 type: "POST",
28   - url: "/adminwebservice",
  28 + url: "/admin",
29 29 data: {
30   - "cmd": "insert",
31   - "number": $("#novo_numero").val(),
32   - "name": $("#novo_nome").val()
  30 + "cmd": "insert_student",
  31 + "value": JSON.stringify({
  32 + "number": $("#novo_numero").val(),
  33 + "name": $("#novo_nome").val()
  34 + })
33 35 }
34 36 });
35 37 }
... ... @@ -39,15 +41,27 @@ $(document).ready(function() {
39 41 // ----------------------------------------------------------------------
40 42 // checkbox handler to allow/deny students individually
41 43 function autorizeStudent(e) {
42   - $.ajax({
43   - type: "POST",
44   - url: "/adminwebservice",
45   - data: {"cmd": "allow", "name": this.name, "value": this.checked}
46   - });
47   - if (this.checked)
  44 + // $.ajax({
  45 + // type: "POST",
  46 + // url: "/admin",
  47 + // data: {"cmd": "allow", "name": this.name, "value": this.checked}
  48 + // });
  49 + if (this.checked) {
48 50 $(this).parent().parent().addClass("active");
49   - else
  51 + $.ajax({
  52 + type: "POST",
  53 + url: "/admin",
  54 + data: {"cmd": "allow", "value": this.name}
  55 + });
  56 + }
  57 + else {
50 58 $(this).parent().parent().removeClass("active");
  59 + $.ajax({
  60 + type: "POST",
  61 + url: "/admin",
  62 + data: {"cmd": "deny", "value": this.name}
  63 + });
  64 + }
51 65 }
52 66  
53 67 // ----------------------------------------------------------------------
... ... @@ -81,13 +95,13 @@ $(document).ready(function() {
81 95 function generate_grade_bar(grade) {
82 96 var barcolor;
83 97 if (grade < 10) {
84   - barcolor = 'progress-bar-danger';
  98 + barcolor = 'bg-danger';
85 99 }
86 100 else if (grade < 15) {
87   - barcolor = 'progress-bar-warning';
  101 + barcolor = 'bg-warning';
88 102 }
89 103 else {
90   - barcolor = 'progress-bar-success';
  104 + barcolor = 'bg-success';
91 105 }
92 106  
93 107 var bar = '<div class="progress"><div class="progress-bar ' + barcolor + '" role="progressbar" aria-valuenow="' + grade + '" aria-valuemin="0" aria-valuemax="20" style="min-width: 2em; width: ' + (5*grade) + '%;">' + grade + '</div></div>';
... ... @@ -133,13 +147,11 @@ $(document).ready(function() {
133 147 // ----------------------------------------------------------------------
134 148 function populate() {
135 149 $.ajax({
136   - url: "/adminwebservice",
  150 + type: "POST",
  151 + url: "/admin",
  152 + data: {"cmd": "get_students", "value": ""},
137 153 dataType: "json",
138 154 success: function(data) {
139   - // show clock on upper left corner
140   - var t = new Date();
141   - $('#currenttime').html(t.getHours() + (t.getMinutes() < 10 ? ':0' : ':') + t.getMinutes());
142   -
143 155 // fill jumbotron data
144 156 $("#title").html(data['test']['title']);
145 157 $("#ref").html(data['test']['ref']);
... ...
templates/admin.html 0 → 100644
... ... @@ -0,0 +1,170 @@
  1 +<!DOCTYPE html>
  2 +<html lang="pt-PT">
  3 +<head>
  4 + <title>Admin</title>
  5 + <meta charset="utf-8">
  6 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  7 + <link rel="icon" href="/static/favicon.ico">
  8 +
  9 + <!-- Bootstrap -->
  10 + <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
  11 +
  12 + <!-- optional -->
  13 + <link rel="stylesheet" href="/static/font-awesome/css/font-awesome.min.css">
  14 + <link rel="stylesheet" href="/static/css/test.css">
  15 +
  16 + <style>
  17 + body {
  18 + padding-top: 100px;
  19 + background: #ccc;
  20 + }
  21 + </style>
  22 +</head>
  23 +<!-- ===================================================================== -->
  24 +<body>
  25 +<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-dark">
  26 + <a class="navbar-brand" href="#">
  27 + <i class="fa fa-clock-o" aria-hidden="true"></i>
  28 + <span id="clock"> --:-- </span>
  29 + </a>
  30 + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
  31 + <span class="navbar-toggler-icon"></span>
  32 + </button>
  33 +
  34 + <div class="collapse navbar-collapse" id="navbarText">
  35 + <ul class="navbar-nav mr-auto">
  36 + </ul>
  37 +
  38 + <span class="navbar-text">
  39 + <i class="fa fa-user" aria-hidden="true"></i>
  40 + <span id="name">Admin</span>
  41 + <span class="caret"></span>
  42 + </span>
  43 + </div>
  44 +</nav>
  45 +<!-- ===================================================================== -->
  46 +<div class="container-fluid">
  47 +<!-- ===================================================================== -->
  48 + <div class="jumbotron">
  49 + <h3 id="title"></h3>
  50 + Ref: <span id="ref"></span><br>
  51 + Enunciado: <span id="filename"></span><br>
  52 + Database: <span id="database"></span><br>
  53 + Testes entregues: <span id="answers_dir"></span>
  54 + </div> <!-- jumbotron -->
  55 +
  56 +<!-- ===================================================================== -->
  57 + <div class="card border-dark">
  58 + <div class="card-header" id="online-header">
  59 + <!-- to be populated -->
  60 + </div>
  61 + <div class="card-body">
  62 + <table class="table table-condensed noleftmargin">
  63 + <thead>
  64 + <tr>
  65 + <th>Número</th>
  66 + <th>Nome</th>
  67 + <!-- <th>Data de início</th> -->
  68 + <th>Início</th>
  69 + <!-- <th>IP</th> -->
  70 + <th>Estado</th>
  71 + </tr>
  72 + </thead>
  73 + <tbody id="online_students">
  74 + <!-- to be populated -->
  75 + </tbody>
  76 + </table>
  77 + </div> <!-- card-body -->
  78 + </div> <!-- card -->
  79 +
  80 +<!-- ===================================================================== -->
  81 + <div class="card border-dark">
  82 + <div class="card-header" id="students-header">
  83 + <!-- to be populated -->
  84 + </div>
  85 +
  86 + <div class="card-body">
  87 + <table class="table noleftmargin">
  88 + <thead>
  89 + <tr>
  90 + <th>Perm.</th>
  91 + <th>Número</th>
  92 + <th>Nome</th>
  93 + <!-- <th>Estado</th> -->
  94 + <th>Nota</th>
  95 + </tr>
  96 + </thead>
  97 + <tbody id="students">
  98 + <!-- to be populated -->
  99 + </tbody>
  100 + </table>
  101 + </div> <!-- card-body -->
  102 +
  103 + <div class="card-footer">
  104 + <div class="row">
  105 + <div class="col-sm-4">
  106 + Permitir
  107 + <button id="allow_all" class="btn btn-xs btn-primary">Todos</button>
  108 + <button id="deny_all" class="btn btn-xs btn-primary">Nenhum</button>
  109 + </div>
  110 + <div class="col-sm-4">
  111 + <button id="novo_aluno" class="btn btn-xs btn-primary" data-toggle="modal" data-target="#novo_aluno_modal">Inserir novo aluno</button>
  112 + </div>
  113 + <div class="col-sm-4">
  114 + <div class="input-group input-group-sm">
  115 + <input id="reset_number" type="text" class="form-control" placeholder="Número">
  116 + <span class="input-group-btn">
  117 + <button id="reset_password" class="btn btn-primary" type="button">Reset password!</button>
  118 + </span>
  119 + </div>
  120 + </div>
  121 + </div> <!-- row -->
  122 + </div> <!-- card-footer -->
  123 + </div> <!-- card -->
  124 +<!-- ===================================================================== -->
  125 +</div> <!-- container -->
  126 +<!-- ===================================================================== -->
  127 +
  128 +
  129 +
  130 + <div class="modal" id="novo_aluno_modal">
  131 + <div class="modal-dialog" role="document">
  132 + <div class="modal-content">
  133 +
  134 + <div class="modal-header">
  135 + <h5 class="modal-title">Inserir novo aluno</h5>
  136 + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  137 + </div>
  138 +
  139 + <div class="modal-body">
  140 + <p>
  141 + Número: <input id="novo_numero" type="text">
  142 + </p>
  143 + <p>
  144 + Nome: <input id="novo_nome" type="text">
  145 + </p>
  146 + </div>
  147 +
  148 + <div class="modal-footer">
  149 + <button type="button" class="btn btn-danger" data-dismiss="modal">Cancelar</button>
  150 + <button id="inserir_novo_aluno" class="btn btn-danger" role="button" data-dismiss="modal">Inserir</button>
  151 + </div>
  152 +
  153 + </div>
  154 + </div>
  155 + </div>
  156 +
  157 +<!-- ===================================================================== -->
  158 +
  159 +
  160 +
  161 +<!-- Scripts -->
  162 + <script src="/static/js/jquery.min.js"></script>
  163 + <script src="/static/popper/umd/popper.min.js"></script>
  164 + <script src="/static/bootstrap/js/bootstrap.min.js"></script>
  165 +
  166 +<!-- My scripts -->
  167 + <script src="/static/js/admin.js"></script>
  168 + <script src="/static/js/clock.js"></script>
  169 +</body>
  170 +</html>
... ...