Commit 6bd5c0063bc65b6b402407251f3c60fb80836ede
1 parent
d378e701
Exists in
master
and in
1 other branch
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 | ... | ... |