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
BUGS.md
... ... @@ -3,7 +3,7 @@
3 3  
4 4 - usar thread.Lock para aceder a variaveis de estado?
5 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 8 # TODO
9 9  
... ... @@ -24,6 +24,10 @@
24 24  
25 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 31 - Se aluno fizer logout, o teste não é gravado e ficamos sem registo do teste que o aluno viu.
28 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 33 - sqlalchemy queixa-se de threads.
... ...
app.py
... ... @@ -158,11 +158,12 @@ class App(object):
158 158 def giveup_test(self, uid):
159 159 logger.info('Student {0}: gave up.'.format(uid))
160 160 t = self.online[uid]['test']
161   - t.giveup()
  161 + grade = t.giveup()
162 162 if t['save_answers']:
163 163 fname = ' -- '.join((t['student']['number'], t['ref'], str(t['finish_time']))) + '.json'
164 164 fpath = path.abspath(path.join(t['answers_dir'], fname))
165 165 t.save_json(fpath)
  166 + return grade
166 167  
167 168 # -----------------------------------------------------------------------
168 169  
... ... @@ -245,3 +246,13 @@ class App(object):
245 246  
246 247 def set_user_ip(self, uid, ipaddress=''):
247 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 9  
10 10 handlers:
11 11 default:
12   - level: 'INFO'
  12 + level: 'DEBUG'
13 13 class: 'logging.StreamHandler'
14 14 formatter: 'standard'
15 15 stream: 'ext://sys.stdout'
16 16  
17 17 cherrypy_console:
18   - level: 'INFO'
  18 + level: 'DEBUG'
19 19 class: 'logging.StreamHandler'
20 20 formatter: 'standard'
21 21 stream: 'ext://sys.stdout'
22 22  
23 23 cherrypy_access:
24   - level: 'INFO'
  24 + level: 'DEBUG'
25 25 class: 'logging.handlers.RotatingFileHandler'
26 26 formatter: 'void'
27 27 filename: 'logs/access.log'
... ... @@ -30,7 +30,7 @@ handlers:
30 30 encoding: 'utf8'
31 31  
32 32 cherrypy_error:
33   - level: 'INFO'
  33 + level: 'DEBUG'
34 34 class: 'logging.handlers.RotatingFileHandler'
35 35 formatter: 'void'
36 36 filename: 'logs/errors.log'
... ...
serve.py
... ... @@ -99,12 +99,11 @@ class AdminWebService(object):
99 99 elif args['cmd'] == 'reset':
100 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 105 else:
103 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 109 # Webserver root
... ... @@ -177,7 +176,7 @@ class Root(object):
177 176 # radio - all off -> no key, 1 on -> string '0'
178 177 # text - always returns string. no answer '', otherwise 'dskdjs'
179 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 180 title = self.app.get_test(uid)['title']
182 181 qq = self.app.get_test_qtypes(uid) # {'q1_ref': 'checkbox', ...}
183 182  
... ... @@ -204,7 +203,7 @@ class Root(object):
204 203 # --- Show result to student
205 204 return self.template['grade'].render(
206 205 title=title,
207   - student_id=uid + ' - ' + student_name,
  206 + student_id=uid + ' - ' + name,
208 207 grade=grade,
209 208 allgrades=self.app.get_student_grades_from_all_tests(uid)
210 209 )
... ... @@ -214,7 +213,11 @@ class Root(object):
214 213 @require()
215 214 def giveup(self):
216 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 219 grade = self.app.giveup_test(uid)
  220 + self.app.logout(uid)
218 221  
219 222 # --- Expire session
220 223 cherrypy.lib.sessions.expire() # session coockie expires client side
... ... @@ -222,8 +225,8 @@ class Root(object):
222 225  
223 226 # --- Show result to student
224 227 return self.template['grade'].render(
225   - title='title',
226   - student_id=uid,
  228 + title=title,
  229 + student_id=uid + ' - ' + name,
227 230 grade=grade,
228 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 21 });
22 22 }
23 23 );
24   - $("#novo_aluno").click(
  24 + $("#inserir_novo_aluno").click(
25 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 166  
159 167 populate(); // run once when the page is loaded
160 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 130 <button id="deny_all" class="btn btn-xs btn-danger">Nenhum</button>
131 131 </div>
132 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 134 </div>
135 135 <div class="col-sm-4">
136 136 <div class="input-group input-group-sm">
137 137 <input id="reset_number" type="text" class="form-control" placeholder="Número">
138 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 140 </span>
141   - </div><!-- /input-group -->
  141 + </div>
142 142 </div>
143 143 </div>
144 144 </div>
145 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 166 </div> <!-- container -->
148 167 </body>
149 168 </html>
... ...
templates/test.html
... ... @@ -29,8 +29,8 @@
29 29 <!-- My javascripts -->
30 30 <script src="/static/js/question_disabler.js"></script>
31 31 <script src="/static/js/prevent_enter_submit.js"></script>
32   - <script src="/static/js/detect_unfocus.js"></script>
33 32 <script src="/static/js/tabkey_in_textarea.js"></script>
  33 + <script src="/static/js/detect_unfocus.js"></script>
34 34  
35 35 <style>
36 36 /* Fixes navigation panel overlaying content */
... ... @@ -333,7 +333,5 @@
333 333 </div>
334 334 </div>
335 335 </div>
336   -
337   -
338 336 </body>
339 337 </html>
... ...
test.py
... ... @@ -249,9 +249,3 @@ class Test(dict):
249 249 json.dump(self, f, indent=2, default=str)
250 250 # HACK default=str is required for datetime objects
251 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   -
... ...