From 6bd5c0063bc65b6b402407251f3c60fb80836ede Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Sun, 18 Nov 2018 16:10:27 +0000 Subject: [PATCH] converted test generation to be done asynchronously. --- BUGS.md | 1 + app.py | 6 +++--- serve.py | 11 ++++++----- test.py | 52 ++++++++++++++++++++++++++++------------------------ 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/BUGS.md b/BUGS.md index 4030ae7..3ba06fa 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,6 +1,7 @@ # BUGS +- numeric interval deve converter respostas que usam virgulas para pontos decimais - a revisao do teste não mostra as imagens. - se aluno tem teste activo e é allowed uma segunda vez, deve manter o mesmo teste. adicionar opcao para eliminar um teste em curso. - melhorar o botao de autorizar (desliga-se), usar antes um botao? diff --git a/app.py b/app.py index 0d7acc1..756b5a6 100644 --- a/app.py +++ b/app.py @@ -136,16 +136,16 @@ class App(object): logger.info(f'Student {uid}: logged out.') # ----------------------------------------------------------------------- - def generate_test(self, uid): + async def generate_test(self, uid): if uid in self.online: logger.info(f'Student {uid}: generating new test.') student_id = self.online[uid]['student'] - self.online[uid]['test'] = self.testfactory.generate(student_id) + self.online[uid]['test'] = await self.testfactory.generate(student_id) + logger.debug(f'Student {uid}: test ok.') return self.online[uid]['test'] else: # this implies an error in the code. should never be here! logger.critical(f'Student {uid}: offline, can\'t generate test') - return None # ----------------------------------------------------------------------- # ans is a dictionary {question_index: answer, ...} diff --git a/serve.py b/serve.py index 8077fb7..5809783 100755 --- a/serve.py +++ b/serve.py @@ -24,7 +24,7 @@ from app import App, AppException from tools import load_yaml, md_to_html # ---------------------------------------------------------------------------- -# Decorator used to restrict access only to the administrator +# Decorator used to restrict access to the administrator # ---------------------------------------------------------------------------- def admin_only(func): @functools.wraps(func) @@ -96,7 +96,7 @@ class LoginHandler(BaseHandler): self.set_secure_cookie("user", str(uid), expires_days=30) self.redirect(self.get_argument("next", "/")) else: - self.render("login.html", error='Não autorizado ou número/senha inválido') + self.render("login.html", error='Não autorizado ou senha inválida') # ---------------------------------------------------------------------------- @@ -199,10 +199,11 @@ class TestHandler(BaseHandler): # --- GET @tornado.web.authenticated - def get(self): + async def get(self): uid = self.current_user - # FIXME make generate async? - t = self.testapp.get_student_test(uid) or self.testapp.generate_test(uid) + t = self.testapp.get_student_test(uid) # reload page returns same test + if t is None: + t = await self.testapp.generate_test(uid) self.render('test.html', t=t, md=md_to_html, templ=self._templates) # --- POST diff --git a/test.py b/test.py index 76f410a..a9d8a83 100644 --- a/test.py +++ b/test.py @@ -6,6 +6,7 @@ import random from datetime import datetime import json import logging +import asyncio # this project import questionfactory as questions @@ -36,29 +37,30 @@ class TestFactory(dict): if conf['review']: logger.info('Review mode. No questions loaded.') - else: - # loads yaml files to question_factory - self.question_factory = questions.QuestionFactory() - self.question_factory.load_files(files=self['files'], questions_dir=self['questions_dir']) - - # check if all questions exist ('ref' keys are correct?) - errors_found = False - for q in self['questions']: - for r in q['ref']: - logger.info(f'Checking question "{r}".') - try: - self.question_factory.generate(r) - # except questions.QuestionFactoryException: - # logger.critical(f'Can\'t generate question "{r}".') - except: - logger.critical(f'Can\'t generate question "{r}".') - errors_found = True - - if errors_found: - logger.critical('Errors found while generating questions.') - raise TestFactoryException() + return - logger.info(f'Test factory ready for "{self["ref"]}".') + # loads yaml files to question_factory + self.question_factory = questions.QuestionFactory() + self.question_factory.load_files(files=self['files'], questions_dir=self['questions_dir']) + + # check if all questions exist ('ref' keys are correct?) + errors_found = False + for q in self['questions']: + for r in q['ref']: + logger.info(f'Checking question "{r}".') + try: + self.question_factory.generate(r) + # except questions.QuestionFactoryException: + # logger.critical(f'Can\'t generate question "{r}".') + except: + logger.critical(f'Can\'t generate question "{r}".') + errors_found = True + + if errors_found: + logger.critical('Errors found while generating questions.') + raise TestFactoryException() + + logger.info(f'Test factory ready for "{self["ref"]}".') # ----------------------------------------------------------------------- @@ -144,17 +146,19 @@ class TestFactory(dict): # Given a dictionary with a student id {'name':'john', 'number': 123} # returns instance of Test() for that particular student # ----------------------------------------------------------------------- - def generate(self, student): + async def generate(self, student): test = [] total_points = 0.0 n = 1 + loop = asyncio.get_running_loop() + for qq in self['questions']: # generate Question() selected randomly from list of references qref = random.choice(qq['ref']) try: - q = self.question_factory.generate(qref) + q = await loop.run_in_executor(None, self.question_factory.generate, qref) except: logger.error(f'Can\'t generate question "{qref}". Skipping.') continue -- libgit2 0.21.2