Commit 778c815e58fb217dbdd3986ff662fc43c06f779e

Authored by Miguel Barão
1 parent 870abe3c
Exists in master and in 1 other branch dev

- fixed debug loglevel when command line option --debug is active.

- fixed giveup() not logging out stude
- insert new student on /admin
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 3
4 - usar thread.Lock para aceder a variaveis de estado? 4 - usar thread.Lock para aceder a variaveis de estado?
5 - permitir adicionar imagens nas perguntas. 5 - permitir adicionar imagens nas perguntas.
6 -- debug mode: log levels not working 6 +- detect_unfocus.js so funciona se estiver inline no html. porquê???
7 7
8 # TODO 8 # TODO
9 9
@@ -24,6 +24,10 @@ @@ -24,6 +24,10 @@
24 24
25 # FIXED 25 # FIXED
26 26
  27 +- inserir novo aluno /admin não fecha.
  28 +- se aluno desistir, ainda fica marcado como online
  29 +- give dá None em vez de 0.0
  30 +- debug mode: log levels not working
27 - Se aluno fizer logout, o teste não é gravado e ficamos sem registo do teste que o aluno viu. 31 - Se aluno fizer logout, o teste não é gravado e ficamos sem registo do teste que o aluno viu.
28 - criar sqlalchemy sessions dentro de app de modo a estarem associadas a requests. ver se é facil usar with db:(...) para criar e fechar sessão. 32 - criar sqlalchemy sessions dentro de app de modo a estarem associadas a requests. ver se é facil usar with db:(...) para criar e fechar sessão.
29 - sqlalchemy queixa-se de threads. 33 - sqlalchemy queixa-se de threads.
@@ -158,11 +158,12 @@ class App(object): @@ -158,11 +158,12 @@ class App(object):
158 def giveup_test(self, uid): 158 def giveup_test(self, uid):
159 logger.info('Student {0}: gave up.'.format(uid)) 159 logger.info('Student {0}: gave up.'.format(uid))
160 t = self.online[uid]['test'] 160 t = self.online[uid]['test']
161 - t.giveup() 161 + grade = t.giveup()
162 if t['save_answers']: 162 if t['save_answers']:
163 fname = ' -- '.join((t['student']['number'], t['ref'], str(t['finish_time']))) + '.json' 163 fname = ' -- '.join((t['student']['number'], t['ref'], str(t['finish_time']))) + '.json'
164 fpath = path.abspath(path.join(t['answers_dir'], fname)) 164 fpath = path.abspath(path.join(t['answers_dir'], fname))
165 t.save_json(fpath) 165 t.save_json(fpath)
  166 + return grade
166 167
167 # ----------------------------------------------------------------------- 168 # -----------------------------------------------------------------------
168 169
@@ -245,3 +246,13 @@ class App(object): @@ -245,3 +246,13 @@ class App(object):
245 246
246 def set_user_ip(self, uid, ipaddress=''): 247 def set_user_ip(self, uid, ipaddress=''):
247 self.online[uid]['student']['ip_address'] = ipaddress 248 self.online[uid]['student']['ip_address'] = ipaddress
  249 +
  250 + def insert_new_student(self, uid, name):
  251 + try:
  252 + with self.db_session() as s:
  253 + s.add(Student(id=uid, name=name, password=''))
  254 + s.commit()
  255 + except Exception:
  256 + logger.error('Insert failed: student {} already exists.'.format(uid))
  257 + else:
  258 + logger.info('New student inserted into database: {}, {}'.format(uid, name))
config/logger-debug.yaml
@@ -9,19 +9,19 @@ formatters: @@ -9,19 +9,19 @@ formatters:
9 9
10 handlers: 10 handlers:
11 default: 11 default:
12 - level: 'INFO' 12 + level: 'DEBUG'
13 class: 'logging.StreamHandler' 13 class: 'logging.StreamHandler'
14 formatter: 'standard' 14 formatter: 'standard'
15 stream: 'ext://sys.stdout' 15 stream: 'ext://sys.stdout'
16 16
17 cherrypy_console: 17 cherrypy_console:
18 - level: 'INFO' 18 + level: 'DEBUG'
19 class: 'logging.StreamHandler' 19 class: 'logging.StreamHandler'
20 formatter: 'standard' 20 formatter: 'standard'
21 stream: 'ext://sys.stdout' 21 stream: 'ext://sys.stdout'
22 22
23 cherrypy_access: 23 cherrypy_access:
24 - level: 'INFO' 24 + level: 'DEBUG'
25 class: 'logging.handlers.RotatingFileHandler' 25 class: 'logging.handlers.RotatingFileHandler'
26 formatter: 'void' 26 formatter: 'void'
27 filename: 'logs/access.log' 27 filename: 'logs/access.log'
@@ -30,7 +30,7 @@ handlers: @@ -30,7 +30,7 @@ handlers:
30 encoding: 'utf8' 30 encoding: 'utf8'
31 31
32 cherrypy_error: 32 cherrypy_error:
33 - level: 'INFO' 33 + level: 'DEBUG'
34 class: 'logging.handlers.RotatingFileHandler' 34 class: 'logging.handlers.RotatingFileHandler'
35 formatter: 'void' 35 formatter: 'void'
36 filename: 'logs/errors.log' 36 filename: 'logs/errors.log'
@@ -99,12 +99,11 @@ class AdminWebService(object): @@ -99,12 +99,11 @@ class AdminWebService(object):
99 elif args['cmd'] == 'reset': 99 elif args['cmd'] == 'reset':
100 return self.app.reset_password(args['name']) 100 return self.app.reset_password(args['name'])
101 101
  102 + elif args['cmd'] == 'insert':
  103 + return self.app.insert_new_student(uid=args['number'], name=args['name'])
  104 +
102 else: 105 else:
103 print(args) 106 print(args)
104 - # if args['cmd'] == 'focus':  
105 - # print('FOCUS', args['name'])  
106 - # elif args['cmd'] == 'blur':  
107 - # print('FOCUS', args['name'])  
108 107
109 # ============================================================================ 108 # ============================================================================
110 # Webserver root 109 # Webserver root
@@ -177,7 +176,7 @@ class Root(object): @@ -177,7 +176,7 @@ class Root(object):
177 # radio - all off -> no key, 1 on -> string '0' 176 # radio - all off -> no key, 1 on -> string '0'
178 # text - always returns string. no answer '', otherwise 'dskdjs' 177 # text - always returns string. no answer '', otherwise 'dskdjs'
179 uid = cherrypy.session.get(SESSION_KEY) 178 uid = cherrypy.session.get(SESSION_KEY)
180 - student_name = self.app.get_student_name(uid) 179 + name = self.app.get_student_name(uid)
181 title = self.app.get_test(uid)['title'] 180 title = self.app.get_test(uid)['title']
182 qq = self.app.get_test_qtypes(uid) # {'q1_ref': 'checkbox', ...} 181 qq = self.app.get_test_qtypes(uid) # {'q1_ref': 'checkbox', ...}
183 182
@@ -204,7 +203,7 @@ class Root(object): @@ -204,7 +203,7 @@ class Root(object):
204 # --- Show result to student 203 # --- Show result to student
205 return self.template['grade'].render( 204 return self.template['grade'].render(
206 title=title, 205 title=title,
207 - student_id=uid + ' - ' + student_name, 206 + student_id=uid + ' - ' + name,
208 grade=grade, 207 grade=grade,
209 allgrades=self.app.get_student_grades_from_all_tests(uid) 208 allgrades=self.app.get_student_grades_from_all_tests(uid)
210 ) 209 )
@@ -214,7 +213,11 @@ class Root(object): @@ -214,7 +213,11 @@ class Root(object):
214 @require() 213 @require()
215 def giveup(self): 214 def giveup(self):
216 uid = cherrypy.session.get(SESSION_KEY) 215 uid = cherrypy.session.get(SESSION_KEY)
  216 + name = self.app.get_student_name(uid)
  217 + title = self.app.get_test(uid)['title']
  218 +
217 grade = self.app.giveup_test(uid) 219 grade = self.app.giveup_test(uid)
  220 + self.app.logout(uid)
218 221
219 # --- Expire session 222 # --- Expire session
220 cherrypy.lib.sessions.expire() # session coockie expires client side 223 cherrypy.lib.sessions.expire() # session coockie expires client side
@@ -222,8 +225,8 @@ class Root(object): @@ -222,8 +225,8 @@ class Root(object):
222 225
223 # --- Show result to student 226 # --- Show result to student
224 return self.template['grade'].render( 227 return self.template['grade'].render(
225 - title='title',  
226 - student_id=uid, 228 + title=title,
  229 + student_id=uid + ' - ' + name,
227 grade=grade, 230 grade=grade,
228 allgrades=self.app.get_student_grades_from_all_tests(uid) 231 allgrades=self.app.get_student_grades_from_all_tests(uid)
229 ) 232 )
static/js/admin.js
@@ -21,9 +21,17 @@ $(document).ready(function() { @@ -21,9 +21,17 @@ $(document).ready(function() {
21 }); 21 });
22 } 22 }
23 ); 23 );
24 - $("#novo_aluno").click( 24 + $("#inserir_novo_aluno").click(
25 function () { 25 function () {
26 - alert('Não implementado!'); 26 + $.ajax({
  27 + type: "POST",
  28 + url: "/adminwebservice",
  29 + data: {
  30 + "cmd": "insert",
  31 + "number": $("#novo_numero").val(),
  32 + "name": $("#novo_nome").val()
  33 + }
  34 + });
27 } 35 }
28 ); 36 );
29 } 37 }
@@ -158,5 +166,5 @@ $(document).ready(function() { @@ -158,5 +166,5 @@ $(document).ready(function() {
158 166
159 populate(); // run once when the page is loaded 167 populate(); // run once when the page is loaded
160 define_buttons_handlers(); 168 define_buttons_handlers();
161 - setInterval(populate, 5000); // poll server on 5s interval 169 + setInterval(populate, 5000); // poll server on 5s interval
162 }); 170 });
templates/admin.html
@@ -130,20 +130,39 @@ @@ -130,20 +130,39 @@
130 <button id="deny_all" class="btn btn-xs btn-danger">Nenhum</button> 130 <button id="deny_all" class="btn btn-xs btn-danger">Nenhum</button>
131 </div> 131 </div>
132 <div class="col-sm-4"> 132 <div class="col-sm-4">
133 - <button id="novo_aluno" class="btn btn-xs btn-danger">Inserir novo aluno</button> 133 + <button id="novo_aluno" class="btn btn-xs btn-danger" data-toggle="modal" data-target="#novo_aluno_modal">Inserir novo aluno</button>
134 </div> 134 </div>
135 <div class="col-sm-4"> 135 <div class="col-sm-4">
136 <div class="input-group input-group-sm"> 136 <div class="input-group input-group-sm">
137 <input id="reset_number" type="text" class="form-control" placeholder="Número"> 137 <input id="reset_number" type="text" class="form-control" placeholder="Número">
138 <span class="input-group-btn"> 138 <span class="input-group-btn">
139 - <button id="reset_password" class="btn btn-danger" type="button">Reset password!</button> 139 + <button id="reset_password" class="btn btn-danger" type="button">Reset password!</button>
140 </span> 140 </span>
141 - </div><!-- /input-group --> 141 + </div>
142 </div> 142 </div>
143 </div> 143 </div>
144 </div> 144 </div>
145 </div> 145 </div>
146 146
  147 + <div class="modal fade" id="novo_aluno_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  148 + <div class="modal-dialog">
  149 + <div class="modal-content">
  150 + <div class="modal-header">
  151 + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  152 + <h4 class="modal-title">Inserir novo aluno</h4>
  153 + </div>
  154 + <div class="modal-body">
  155 + Número: <input id="novo_numero" type="text">
  156 + Nome: <input id="novo_nome" type="text">
  157 + </div>
  158 + <div class="modal-footer">
  159 + <button type="button" class="btn btn-danger" data-dismiss="modal">Cancelar</button>
  160 + <button id="inserir_novo_aluno" class="btn btn-danger" role="button" data-dismiss="modal">Inserir</button>
  161 + </div>
  162 + </div>
  163 + </div>
  164 + </div>
  165 +
147 </div> <!-- container --> 166 </div> <!-- container -->
148 </body> 167 </body>
149 </html> 168 </html>
templates/test.html
@@ -29,8 +29,8 @@ @@ -29,8 +29,8 @@
29 <!-- My javascripts --> 29 <!-- My javascripts -->
30 <script src="/static/js/question_disabler.js"></script> 30 <script src="/static/js/question_disabler.js"></script>
31 <script src="/static/js/prevent_enter_submit.js"></script> 31 <script src="/static/js/prevent_enter_submit.js"></script>
32 - <script src="/static/js/detect_unfocus.js"></script>  
33 <script src="/static/js/tabkey_in_textarea.js"></script> 32 <script src="/static/js/tabkey_in_textarea.js"></script>
  33 + <script src="/static/js/detect_unfocus.js"></script>
34 34
35 <style> 35 <style>
36 /* Fixes navigation panel overlaying content */ 36 /* Fixes navigation panel overlaying content */
@@ -333,7 +333,5 @@ @@ -333,7 +333,5 @@
333 </div> 333 </div>
334 </div> 334 </div>
335 </div> 335 </div>
336 -  
337 -  
338 </body> 336 </body>
339 </html> 337 </html>
@@ -249,9 +249,3 @@ class Test(dict): @@ -249,9 +249,3 @@ class Test(dict):
249 json.dump(self, f, indent=2, default=str) 249 json.dump(self, f, indent=2, default=str)
250 # HACK default=str is required for datetime objects 250 # HACK default=str is required for datetime objects
251 logger.info('Student {}: saved JSON file.'.format(self['student']['number'])) 251 logger.info('Student {}: saved JSON file.'.format(self['student']['number']))
252 -  
253 - # -----------------------------------------------------------------------  
254 - # def generate_html(self):  
255 - # for q in self['questions']:  
256 - # q['text_html'] = markdown.markdown(q['text'])  
257 -