Commit 6bd5c0063bc65b6b402407251f3c60fb80836ede

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

converted test generation to be done asynchronously.

Showing 4 changed files with 38 additions and 32 deletions   Show diff stats
BUGS.md
1 1  
2 2 # BUGS
3 3  
  4 +- numeric interval deve converter respostas que usam virgulas para pontos decimais
4 5 - a revisao do teste não mostra as imagens.
5 6 - se aluno tem teste activo e é allowed uma segunda vez, deve manter o mesmo teste. adicionar opcao para eliminar um teste em curso.
6 7 - melhorar o botao de autorizar (desliga-se), usar antes um botao?
... ...
app.py
... ... @@ -136,16 +136,16 @@ class App(object):
136 136 logger.info(f'Student {uid}: logged out.')
137 137  
138 138 # -----------------------------------------------------------------------
139   - def generate_test(self, uid):
  139 + async def generate_test(self, uid):
140 140 if uid in self.online:
141 141 logger.info(f'Student {uid}: generating new test.')
142 142 student_id = self.online[uid]['student']
143   - self.online[uid]['test'] = self.testfactory.generate(student_id)
  143 + self.online[uid]['test'] = await self.testfactory.generate(student_id)
  144 + logger.debug(f'Student {uid}: test ok.')
144 145 return self.online[uid]['test']
145 146 else:
146 147 # this implies an error in the code. should never be here!
147 148 logger.critical(f'Student {uid}: offline, can\'t generate test')
148   - return None
149 149  
150 150 # -----------------------------------------------------------------------
151 151 # ans is a dictionary {question_index: answer, ...}
... ...
serve.py
... ... @@ -24,7 +24,7 @@ from app import App, AppException
24 24 from tools import load_yaml, md_to_html
25 25  
26 26 # ----------------------------------------------------------------------------
27   -# Decorator used to restrict access only to the administrator
  27 +# Decorator used to restrict access to the administrator
28 28 # ----------------------------------------------------------------------------
29 29 def admin_only(func):
30 30 @functools.wraps(func)
... ... @@ -96,7 +96,7 @@ class LoginHandler(BaseHandler):
96 96 self.set_secure_cookie("user", str(uid), expires_days=30)
97 97 self.redirect(self.get_argument("next", "/"))
98 98 else:
99   - self.render("login.html", error='Não autorizado ou número/senha inválido')
  99 + self.render("login.html", error='Não autorizado ou senha inválida')
100 100  
101 101  
102 102 # ----------------------------------------------------------------------------
... ... @@ -199,10 +199,11 @@ class TestHandler(BaseHandler):
199 199  
200 200 # --- GET
201 201 @tornado.web.authenticated
202   - def get(self):
  202 + async def get(self):
203 203 uid = self.current_user
204   - # FIXME make generate async?
205   - t = self.testapp.get_student_test(uid) or self.testapp.generate_test(uid)
  204 + t = self.testapp.get_student_test(uid) # reload page returns same test
  205 + if t is None:
  206 + t = await self.testapp.generate_test(uid)
206 207 self.render('test.html', t=t, md=md_to_html, templ=self._templates)
207 208  
208 209 # --- POST
... ...
test.py
... ... @@ -6,6 +6,7 @@ import random
6 6 from datetime import datetime
7 7 import json
8 8 import logging
  9 +import asyncio
9 10  
10 11 # this project
11 12 import questionfactory as questions
... ... @@ -36,29 +37,30 @@ class TestFactory(dict):
36 37  
37 38 if conf['review']:
38 39 logger.info('Review mode. No questions loaded.')
39   - else:
40   - # loads yaml files to question_factory
41   - self.question_factory = questions.QuestionFactory()
42   - self.question_factory.load_files(files=self['files'], questions_dir=self['questions_dir'])
43   -
44   - # check if all questions exist ('ref' keys are correct?)
45   - errors_found = False
46   - for q in self['questions']:
47   - for r in q['ref']:
48   - logger.info(f'Checking question "{r}".')
49   - try:
50   - self.question_factory.generate(r)
51   - # except questions.QuestionFactoryException:
52   - # logger.critical(f'Can\'t generate question "{r}".')
53   - except:
54   - logger.critical(f'Can\'t generate question "{r}".')
55   - errors_found = True
56   -
57   - if errors_found:
58   - logger.critical('Errors found while generating questions.')
59   - raise TestFactoryException()
  40 + return
60 41  
61   - logger.info(f'Test factory ready for "{self["ref"]}".')
  42 + # loads yaml files to question_factory
  43 + self.question_factory = questions.QuestionFactory()
  44 + self.question_factory.load_files(files=self['files'], questions_dir=self['questions_dir'])
  45 +
  46 + # check if all questions exist ('ref' keys are correct?)
  47 + errors_found = False
  48 + for q in self['questions']:
  49 + for r in q['ref']:
  50 + logger.info(f'Checking question "{r}".')
  51 + try:
  52 + self.question_factory.generate(r)
  53 + # except questions.QuestionFactoryException:
  54 + # logger.critical(f'Can\'t generate question "{r}".')
  55 + except:
  56 + logger.critical(f'Can\'t generate question "{r}".')
  57 + errors_found = True
  58 +
  59 + if errors_found:
  60 + logger.critical('Errors found while generating questions.')
  61 + raise TestFactoryException()
  62 +
  63 + logger.info(f'Test factory ready for "{self["ref"]}".')
62 64  
63 65  
64 66 # -----------------------------------------------------------------------
... ... @@ -144,17 +146,19 @@ class TestFactory(dict):
144 146 # Given a dictionary with a student id {'name':'john', 'number': 123}
145 147 # returns instance of Test() for that particular student
146 148 # -----------------------------------------------------------------------
147   - def generate(self, student):
  149 + async def generate(self, student):
148 150 test = []
149 151 total_points = 0.0
150 152  
151 153 n = 1
  154 + loop = asyncio.get_running_loop()
  155 +
152 156 for qq in self['questions']:
153 157 # generate Question() selected randomly from list of references
154 158 qref = random.choice(qq['ref'])
155 159  
156 160 try:
157   - q = self.question_factory.generate(qref)
  161 + q = await loop.run_in_executor(None, self.question_factory.generate, qref)
158 162 except:
159 163 logger.error(f'Can\'t generate question "{qref}". Skipping.')
160 164 continue
... ...