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
1 1
2 # BUGS 2 # BUGS
3 3
  4 +- numeric interval deve converter respostas que usam virgulas para pontos decimais
4 - a revisao do teste não mostra as imagens. 5 - a revisao do teste não mostra as imagens.
5 - se aluno tem teste activo e é allowed uma segunda vez, deve manter o mesmo teste. adicionar opcao para eliminar um teste em curso. 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 - melhorar o botao de autorizar (desliga-se), usar antes um botao? 7 - melhorar o botao de autorizar (desliga-se), usar antes um botao?
@@ -136,16 +136,16 @@ class App(object): @@ -136,16 +136,16 @@ class App(object):
136 logger.info(f'Student {uid}: logged out.') 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 if uid in self.online: 140 if uid in self.online:
141 logger.info(f'Student {uid}: generating new test.') 141 logger.info(f'Student {uid}: generating new test.')
142 student_id = self.online[uid]['student'] 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 return self.online[uid]['test'] 145 return self.online[uid]['test']
145 else: 146 else:
146 # this implies an error in the code. should never be here! 147 # this implies an error in the code. should never be here!
147 logger.critical(f'Student {uid}: offline, can\'t generate test') 148 logger.critical(f'Student {uid}: offline, can\'t generate test')
148 - return None  
149 149
150 # ----------------------------------------------------------------------- 150 # -----------------------------------------------------------------------
151 # ans is a dictionary {question_index: answer, ...} 151 # ans is a dictionary {question_index: answer, ...}
@@ -24,7 +24,7 @@ from app import App, AppException @@ -24,7 +24,7 @@ from app import App, AppException
24 from tools import load_yaml, md_to_html 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 def admin_only(func): 29 def admin_only(func):
30 @functools.wraps(func) 30 @functools.wraps(func)
@@ -96,7 +96,7 @@ class LoginHandler(BaseHandler): @@ -96,7 +96,7 @@ class LoginHandler(BaseHandler):
96 self.set_secure_cookie("user", str(uid), expires_days=30) 96 self.set_secure_cookie("user", str(uid), expires_days=30)
97 self.redirect(self.get_argument("next", "/")) 97 self.redirect(self.get_argument("next", "/"))
98 else: 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,10 +199,11 @@ class TestHandler(BaseHandler):
199 199
200 # --- GET 200 # --- GET
201 @tornado.web.authenticated 201 @tornado.web.authenticated
202 - def get(self): 202 + async def get(self):
203 uid = self.current_user 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 self.render('test.html', t=t, md=md_to_html, templ=self._templates) 207 self.render('test.html', t=t, md=md_to_html, templ=self._templates)
207 208
208 # --- POST 209 # --- POST
@@ -6,6 +6,7 @@ import random @@ -6,6 +6,7 @@ import random
6 from datetime import datetime 6 from datetime import datetime
7 import json 7 import json
8 import logging 8 import logging
  9 +import asyncio
9 10
10 # this project 11 # this project
11 import questionfactory as questions 12 import questionfactory as questions
@@ -36,29 +37,30 @@ class TestFactory(dict): @@ -36,29 +37,30 @@ class TestFactory(dict):
36 37
37 if conf['review']: 38 if conf['review']:
38 logger.info('Review mode. No questions loaded.') 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,17 +146,19 @@ class TestFactory(dict):
144 # Given a dictionary with a student id {'name':'john', 'number': 123} 146 # Given a dictionary with a student id {'name':'john', 'number': 123}
145 # returns instance of Test() for that particular student 147 # returns instance of Test() for that particular student
146 # ----------------------------------------------------------------------- 148 # -----------------------------------------------------------------------
147 - def generate(self, student): 149 + async def generate(self, student):
148 test = [] 150 test = []
149 total_points = 0.0 151 total_points = 0.0
150 152
151 n = 1 153 n = 1
  154 + loop = asyncio.get_running_loop()
  155 +
152 for qq in self['questions']: 156 for qq in self['questions']:
153 # generate Question() selected randomly from list of references 157 # generate Question() selected randomly from list of references
154 qref = random.choice(qq['ref']) 158 qref = random.choice(qq['ref'])
155 159
156 try: 160 try:
157 - q = self.question_factory.generate(qref) 161 + q = await loop.run_in_executor(None, self.question_factory.generate, qref)
158 except: 162 except:
159 logger.error(f'Can\'t generate question "{qref}". Skipping.') 163 logger.error(f'Can\'t generate question "{qref}". Skipping.')
160 continue 164 continue