Commit 1699423aa0ac21473f0346b5dff77ee9f588a04e
1 parent
b339e748
Exists in
master
and in
1 other branch
- show browser focus/unfocus events in /admin
- disabled content-security-policy because of MathJax - removed column from tables in /admin to simplify
Showing
9 changed files
with
79 additions
and
49 deletions
Show diff stats
BUGS.md
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | |
23 | 23 | # FIXED |
24 | 24 | |
25 | +- server nao esta a receber eventos focus/blur dos utilizadores diferentes de '0', estranho... | |
25 | 26 | - permitir adicionar imagens nas perguntas. |
26 | 27 | - detect_unfocus.js so funciona se estiver inline no html. porquê??? |
27 | 28 | - inserir novo aluno /admin não fecha. | ... | ... |
app.py
... | ... | @@ -218,7 +218,8 @@ class App(object): |
218 | 218 | 'password_defined': pw != '', |
219 | 219 | 'grades': self.get_student_grades_from_test(uid, self.testfactory['ref']), |
220 | 220 | 'ip_address': self.online.get(uid, {}).get('student', {}).get('ip_address',''), |
221 | - 'user_agent': self.online.get(uid, {}).get('student', {}).get('user_agent','') | |
221 | + 'user_agent': self.online.get(uid, {}).get('student', {}).get('user_agent',''), | |
222 | + 'focus': self.online.get(uid, {}).get('student', {}).get('focus', True), | |
222 | 223 | }) |
223 | 224 | return l |
224 | 225 | |
... | ... | @@ -263,3 +264,6 @@ class App(object): |
263 | 264 | logger.error('Insert failed: student {} already exists.'.format(uid)) |
264 | 265 | else: |
265 | 266 | logger.info('New student inserted into database: {}, {}'.format(uid, name)) |
267 | + | |
268 | + def set_student_focus(self, uid, value): | |
269 | + self.online[uid]['student']['focus'] = value | ... | ... |
serve.py
... | ... | @@ -63,7 +63,8 @@ def secureheaders(): |
63 | 63 | headers = cherrypy.response.headers |
64 | 64 | headers['X-Frame-Options'] = 'DENY' |
65 | 65 | headers['X-XSS-Protection'] = '1; mode=block' |
66 | - headers['Content-Security-Policy'] = "default-src='self'" | |
66 | + # FIXME disabled because MathJax requires unsafe javascript eval: | |
67 | + # headers['Content-Security-Policy'] = "default-src 'self'" | |
67 | 68 | if (cherrypy.server.ssl_certificate != None and cherrypy.server.ssl_private_key != None): |
68 | 69 | headers['Strict-Transport-Security'] = 'max-age=31536000' # one year |
69 | 70 | |
... | ... | @@ -88,6 +89,7 @@ class AdminWebService(object): |
88 | 89 | } |
89 | 90 | return json.dumps(data, default=str) |
90 | 91 | |
92 | + @cherrypy.tools.accept(media='application/json') # FIXME | |
91 | 93 | def POST(self, **args): |
92 | 94 | # print('POST', args) # FIXME |
93 | 95 | if args['cmd'] == 'allow': |
... | ... | @@ -106,6 +108,25 @@ class AdminWebService(object): |
106 | 108 | print(args) |
107 | 109 | |
108 | 110 | # ============================================================================ |
111 | +# Student webservice | |
112 | +# ============================================================================ | |
113 | +class StudentWebService(object): | |
114 | + exposed = True | |
115 | + _cp_config = { | |
116 | + 'auth.require': [] | |
117 | + } | |
118 | + | |
119 | + def __init__(self, app): | |
120 | + self.app = app | |
121 | + | |
122 | + @cherrypy.tools.accept(media='application/json') # FIXME | |
123 | + def POST(self, **args): | |
124 | + uid = cherrypy.session.get(SESSION_KEY) | |
125 | + if args['cmd'] == 'focus': | |
126 | + v = json.loads(args['value']) | |
127 | + self.app.set_student_focus(uid=args['number'], value=v) | |
128 | + | |
129 | +# ============================================================================ | |
109 | 130 | # Webserver root |
110 | 131 | # ============================================================================ |
111 | 132 | class Root(object): |
... | ... | @@ -287,6 +308,7 @@ if __name__ == '__main__': |
287 | 308 | # --- create webserver |
288 | 309 | webapp = Root(app) |
289 | 310 | webapp.adminwebservice = AdminWebService(app) |
311 | + webapp.studentwebservice = StudentWebService(app) | |
290 | 312 | |
291 | 313 | # --- site wide configuration (valid for all apps) |
292 | 314 | cherrypy.tools.secureheaders = cherrypy.Tool('before_finalize', secureheaders, priority=60) |
... | ... | @@ -312,6 +334,11 @@ if __name__ == '__main__': |
312 | 334 | 'tools.response_headers.on': True, |
313 | 335 | 'tools.response_headers.headers': [('Content-Type', 'text/plain')], |
314 | 336 | }, |
337 | + '/studentwebservice': { | |
338 | + 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), | |
339 | + 'tools.response_headers.on': True, | |
340 | + 'tools.response_headers.headers': [('Content-Type', 'text/plain')], | |
341 | + }, | |
315 | 342 | '/static': { |
316 | 343 | 'tools.auth.on': False, # everything in /static is public |
317 | 344 | 'tools.staticdir.on': True, | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +/* Fixes navigation panel overlaying content */ | |
2 | +body { | |
3 | + padding-top: 80px; | |
4 | + background: #aaa; | |
5 | +} | |
6 | +/* Hack to avoid name clash between pygments and mathjax */ | |
7 | +.MathJax .mo, | |
8 | +.MathJax .mi { | |
9 | + color: inherit; | |
10 | +} | |
11 | +.drop-shadow { | |
12 | + -webkit-box-shadow: 0 0 5px 2px rgba(0, 0, 0, .5); | |
13 | + box-shadow: 0px 2px 10px 3px rgba(0, 0, 0, .2); | |
14 | + border-radius:5px; | |
15 | +} | |
16 | +textarea { | |
17 | + font-family: monospace !important; | |
18 | +} | ... | ... |
static/js/admin.js
... | ... | @@ -57,23 +57,24 @@ $(document).ready(function() { |
57 | 57 | var active = []; |
58 | 58 | $.each(students, function(i, r) { |
59 | 59 | if (r['start_time'] != '') { |
60 | - active.push([r['uid'], r['name'], r['start_time'], r['ip_address'], r['user_agent']]); | |
60 | + active.push([r['uid'], r['name'], r['start_time'], r['ip_address'], r['user_agent'], r['focus']]); | |
61 | 61 | } |
62 | 62 | }); |
63 | 63 | // sort by start time |
64 | 64 | active.sort(function(a,b){return a[2] < b[2] ? -1 : (a[2] == b[2] ? 0 : 1);}); |
65 | 65 | n = active.length; |
66 | 66 | for(var i = 0; i < n; i++) { |
67 | - rows += "<tr>\ | |
68 | - <td>" + active[i][0] + "</td>\ | |
69 | - <td>" + active[i][1] + "</td>\ | |
70 | - <td>" + active[i][2].slice(0,10) + "</td>\ | |
71 | - <td>" + active[i][2].slice(11,19) + '</td>\ | |
72 | - <td><div data-toggle="tooltip" data-placement="top" title="' + active[i][4] + '">' + active[i][3] + '</div></td>\ | |
67 | + rows += '<tr' + (active[i][5]? '' : ' class="danger"') + '>\ | |
68 | + <td>' + active[i][0] + '</td>\ | |
69 | + <td>' + active[i][1] + '</td>\ | |
70 | + <td>' + active[i][2].slice(11,19) + '</td>\ | |
71 | + <td><div data-toggle="tooltip" data-placement="top" title="'+active[i][4]+'">' + active[i][3] + '</div></td>\ | |
72 | + <td>' + (active[i][5]? '' : '<span class="label label-danger">unfocus</span>') + '</td>\ | |
73 | 73 | </tr>'; |
74 | 74 | } |
75 | 75 | $("#online_students").html(rows); |
76 | 76 | $("#online-header").html(n + " Activo(s)"); |
77 | + | |
77 | 78 | } |
78 | 79 | |
79 | 80 | // ---------------------------------------------------------------------- |
... | ... | @@ -111,14 +112,12 @@ $(document).ready(function() { |
111 | 112 | rows += '<tr id="' + uid + '" + class="">'; |
112 | 113 | |
113 | 114 | rows += '\ |
114 | - <td><input type="checkbox" name="' + uid + '" value="true"' + (d['allowed'] ? 'checked' : '') + '></td>\ | |
115 | - <td>' + uid + '</td>\ | |
116 | - <td>' + d['name'] + (d['password_defined'] ? ' <span class="label label-default">pw</span>' : '') +'</td>\ | |
117 | - <td>' + | |
118 | - | |
115 | + <td><input type="checkbox" name="' + uid + '" value="true"' + (d['allowed'] ? 'checked' : '') + '>' + | |
116 | + (d['start_time']=='' ? '' : ' <span class="label label-success">teste</span>') + | |
119 | 117 | // (d['online'] ? '<span class="label label-warning">online</span>' : '') + |
120 | - (d['start_time']==''?'':'<span class="label label-success">teste</span>') + | |
121 | 118 | '</td>\ |
119 | + <td>' + uid + '</td>\ | |
120 | + <td>' + d['name'] + (d['password_defined'] ? ' <span class="label label-default">pw</span>' : '') +'</td>\ | |
122 | 121 | <td>'; |
123 | 122 | var g = d['grades']; |
124 | 123 | var glength = g.length; | ... | ... |
static/js/detect_unfocus.js
... | ... | @@ -2,10 +2,11 @@ $(document).ready(function() { |
2 | 2 | $(window).focus(function(){ |
3 | 3 | $.ajax({ |
4 | 4 | type: "POST", |
5 | - url: "/adminwebservice", | |
5 | + url: "/studentwebservice", | |
6 | 6 | data: { |
7 | 7 | "cmd": "focus", |
8 | - "name": $("#number").text() | |
8 | + "number": $("#number").text(), | |
9 | + "value": true | |
9 | 10 | } |
10 | 11 | }); |
11 | 12 | }); |
... | ... | @@ -13,10 +14,11 @@ $(document).ready(function() { |
13 | 14 | $(window).blur(function(e){ |
14 | 15 | $.ajax({ |
15 | 16 | type: "POST", |
16 | - url: "/adminwebservice", | |
17 | + url: "/studentwebservice", | |
17 | 18 | data: { |
18 | - "cmd": "blur", | |
19 | - "name": $("#number").text() | |
19 | + "cmd": "focus", | |
20 | + "number": $("#number").text(), | |
21 | + "value": false | |
20 | 22 | } |
21 | 23 | }); |
22 | 24 | }); | ... | ... |
templates/admin.html
... | ... | @@ -90,9 +90,10 @@ |
90 | 90 | <tr> |
91 | 91 | <th>Número</th> |
92 | 92 | <th>Nome</th> |
93 | - <th>Data de início</th> | |
93 | + <!-- <th>Data de início</th> --> | |
94 | 94 | <th>Hora de início</th> |
95 | 95 | <th>IP</th> |
96 | + <th>Estado</th> | |
96 | 97 | </tr> |
97 | 98 | </thead> |
98 | 99 | <tbody id="online_students"> |
... | ... | @@ -113,7 +114,7 @@ |
113 | 114 | <th>Perm.</th> |
114 | 115 | <th>Número</th> |
115 | 116 | <th>Nome</th> |
116 | - <th>Estado</th> | |
117 | + <!-- <th>Estado</th> --> | |
117 | 118 | <th>Nota</th> |
118 | 119 | </tr> |
119 | 120 | </thead> | ... | ... |
templates/test.html
... | ... | @@ -10,18 +10,19 @@ |
10 | 10 | <!-- MathJax --> |
11 | 11 | <script type="text/x-mathjax-config"> |
12 | 12 | MathJax.Hub.Config({ |
13 | - tex2jax: {inlineMath: [["$$$","$$$"], ["$","$"], ["\\(","\\)"]]} | |
13 | + tex2jax: { | |
14 | + inlineMath: [["$$$","$$$"], ["$","$"], ["\\(","\\)"]] | |
15 | + } | |
14 | 16 | }); |
15 | 17 | </script> |
16 | - | |
17 | - <script type="text/javascript" src="/static/js/mathjax/MathJax.js?config=TeX-AMS_CHTML-full"> | |
18 | - </script> | |
18 | + <script type="text/javascript" src="/static/js/mathjax/MathJax.js?config=TeX-AMS_CHTML-full"></script> | |
19 | 19 | |
20 | 20 | <!-- Bootstrap --> |
21 | 21 | <link rel="stylesheet" href="/static/css/bootstrap.min.css"> |
22 | 22 | <link rel="stylesheet" href="/static/css/bootstrap-theme.min.css"> <!-- optional --> |
23 | 23 | <link rel="stylesheet" href="/static/css/github.css"> <!-- syntax highlight --> |
24 | 24 | <link rel="stylesheet" href="/static/css/sticky-footer-navbar.css"> |
25 | + <link rel="stylesheet" href="/static/css/test.css"> | |
25 | 26 | |
26 | 27 | <script src="/static/js/jquery.min.js"></script> |
27 | 28 | <script src="/static/js/bootstrap.min.js"></script> |
... | ... | @@ -32,26 +33,6 @@ |
32 | 33 | <script src="/static/js/tabkey_in_textarea.js"></script> |
33 | 34 | <script src="/static/js/detect_unfocus.js"></script> |
34 | 35 | |
35 | - <style> | |
36 | - /* Fixes navigation panel overlaying content */ | |
37 | - body { | |
38 | - padding-top: 80px; | |
39 | - background: #aaa; | |
40 | - } | |
41 | - /* Hack to avoid name clash between pygments and mathjax */ | |
42 | - .MathJax .mo, | |
43 | - .MathJax .mi { | |
44 | - color: inherit; | |
45 | - } | |
46 | - .drop-shadow { | |
47 | - -webkit-box-shadow: 0 0 5px 2px rgba(0, 0, 0, .5); | |
48 | - box-shadow: 0px 2px 10px 3px rgba(0, 0, 0, .2); | |
49 | - border-radius:5px; | |
50 | - } | |
51 | - textarea { | |
52 | - font-family: monospace !important; | |
53 | - } | |
54 | - </style> | |
55 | 36 | </head> |
56 | 37 | <!-- ===================================================================== --> |
57 | 38 | <body> |
... | ... | @@ -59,7 +40,6 @@ |
59 | 40 | <div class="container-fluid drop-shadow"> |
60 | 41 | <div class="navbar-header"> |
61 | 42 | <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar"> |
62 | - <!-- <span class="glyphicon glyphicon-menu-hamburger"></span> --> | |
63 | 43 | <span class="icon-bar"></span> |
64 | 44 | <span class="icon-bar"></span> |
65 | 45 | <span class="icon-bar"></span> |
... | ... | @@ -85,7 +65,6 @@ |
85 | 65 | <li><a href="/results">Ver resultados</a></li> |
86 | 66 | % endif |
87 | 67 | <li><a data-toggle="modal" data-target="#sair" id="form-button-submit"><span class="glyphicon glyphicon-log-out" aria-hidden="true"></span> Sair</a></li> |
88 | - <!-- <li><a href="#">Change password</a></li> --> | |
89 | 68 | </ul> |
90 | 69 | </li> |
91 | 70 | </ul> | ... | ... |
tools.py
... | ... | @@ -22,7 +22,6 @@ def load_yaml(filename, default=None): |
22 | 22 | try: |
23 | 23 | return yaml.load(f) |
24 | 24 | except yaml.YAMLError as e: |
25 | - # except yaml.parser.ParserError: | |
26 | 25 | mark = e.problem_mark |
27 | 26 | logger.error('In YAML file "{0}" near line {1}, column {2}.'.format(filename, mark.line, mark.column+1)) |
28 | 27 | return default | ... | ... |