Commit 77dc4f5e21a0e7d35b6205d029473642b6a9d2f3
1 parent
bab5f360
Exists in
master
and in
1 other branch
refactor checking questions.
add check for correction of code and textarea.
Showing
3 changed files
with
42 additions
and
30 deletions
Show diff stats
BUGS.md
| 1 | 1 | ||
| 2 | # BUGS | 2 | # BUGS |
| 3 | 3 | ||
| 4 | +- JOBE correct async | ||
| 4 | - em caso de timeout na submissão (e.g. JOBE ou script nao responde) a correcção não termina e o teste não é guardado. | 5 | - em caso de timeout na submissão (e.g. JOBE ou script nao responde) a correcção não termina e o teste não é guardado. |
| 5 | - QuestionCode falta reportar nos comments os vários erros que podem ocorrer (timeout, etc) | 6 | - QuestionCode falta reportar nos comments os vários erros que podem ocorrer (timeout, etc) |
| 6 | - permitir remover alunos que estão online para poderem comecar de novo. | 7 | - permitir remover alunos que estão online para poderem comecar de novo. |
perguntations/__init__.py
| @@ -32,7 +32,7 @@ proof of submission and for review. | @@ -32,7 +32,7 @@ proof of submission and for review. | ||
| 32 | ''' | 32 | ''' |
| 33 | 33 | ||
| 34 | APP_NAME = 'perguntations' | 34 | APP_NAME = 'perguntations' |
| 35 | -APP_VERSION = '2020.11.dev3' | 35 | +APP_VERSION = '2020.11.dev4' |
| 36 | APP_DESCRIPTION = __doc__ | 36 | APP_DESCRIPTION = __doc__ |
| 37 | 37 | ||
| 38 | __author__ = 'Miguel Barão' | 38 | __author__ = 'Miguel Barão' |
perguntations/testfactory.py
| @@ -43,7 +43,7 @@ class TestFactory(dict): | @@ -43,7 +43,7 @@ class TestFactory(dict): | ||
| 43 | super().__init__({ # defaults | 43 | super().__init__({ # defaults |
| 44 | 'title': '', | 44 | 'title': '', |
| 45 | 'show_points': True, | 45 | 'show_points': True, |
| 46 | - 'scale': None, # or [0, 20] | 46 | + 'scale': None, |
| 47 | 'duration': 0, # 0=infinite | 47 | 'duration': 0, # 0=infinite |
| 48 | 'autosubmit': False, | 48 | 'autosubmit': False, |
| 49 | 'debug': False, | 49 | 'debug': False, |
| @@ -66,12 +66,10 @@ class TestFactory(dict): | @@ -66,12 +66,10 @@ class TestFactory(dict): | ||
| 66 | len(qrefs), len(self["questions"])) | 66 | len(qrefs), len(self["questions"])) |
| 67 | 67 | ||
| 68 | # --- load and build question factories | 68 | # --- load and build question factories |
| 69 | - self.question_factory = {} | 69 | + self['question_factory'] = {} |
| 70 | 70 | ||
| 71 | - counter = 1 | ||
| 72 | for file in self["files"]: | 71 | for file in self["files"]: |
| 73 | fullpath = path.normpath(path.join(self["questions_dir"], file)) | 72 | fullpath = path.normpath(path.join(self["questions_dir"], file)) |
| 74 | - (dirname, filename) = path.split(fullpath) | ||
| 75 | 73 | ||
| 76 | logger.info('Loading "%s"...', fullpath) | 74 | logger.info('Loading "%s"...', fullpath) |
| 77 | questions = load_yaml(fullpath) # , default=[]) | 75 | questions = load_yaml(fullpath) # , default=[]) |
| @@ -88,8 +86,8 @@ class TestFactory(dict): | @@ -88,8 +86,8 @@ class TestFactory(dict): | ||
| 88 | logger.warning('Missing ref set to "%s"', question["ref"]) | 86 | logger.warning('Missing ref set to "%s"', question["ref"]) |
| 89 | 87 | ||
| 90 | # check for duplicate refs | 88 | # check for duplicate refs |
| 91 | - if question['ref'] in self.question_factory: | ||
| 92 | - other = self.question_factory[question['ref']] | 89 | + if question['ref'] in self['question_factory']: |
| 90 | + other = self['question_factory'][question['ref']] | ||
| 93 | otherfile = path.join(other.question['path'], | 91 | otherfile = path.join(other.question['path'], |
| 94 | other.question['filename']) | 92 | other.question['filename']) |
| 95 | msg = (f'Duplicate reference "{question["ref"]}" in files ' | 93 | msg = (f'Duplicate reference "{question["ref"]}" in files ' |
| @@ -98,36 +96,23 @@ class TestFactory(dict): | @@ -98,36 +96,23 @@ class TestFactory(dict): | ||
| 98 | 96 | ||
| 99 | # make factory only for the questions used in the test | 97 | # make factory only for the questions used in the test |
| 100 | if question['ref'] in qrefs: | 98 | if question['ref'] in qrefs: |
| 101 | - # question.setdefault('type', 'information') | ||
| 102 | - question.update({ | ||
| 103 | - 'filename': filename, | ||
| 104 | - 'path': dirname, | ||
| 105 | - 'index': i # position in the file, 0 based | ||
| 106 | - }) | 99 | + question.update(zip(('path', 'filename', 'index'), |
| 100 | + path.split(fullpath) + (i,))) | ||
| 107 | if question['type'] == 'code' and 'server' not in question: | 101 | if question['type'] == 'code' and 'server' not in question: |
| 108 | try: | 102 | try: |
| 109 | question['server'] = self['jobe_server'] | 103 | question['server'] = self['jobe_server'] |
| 110 | except KeyError as exc: | 104 | except KeyError as exc: |
| 111 | msg = f'Missing JOBE server in "{question["ref"]}"' | 105 | msg = f'Missing JOBE server in "{question["ref"]}"' |
| 112 | - raise TestFactoryException(msg) | ||
| 113 | - | ||
| 114 | - self.question_factory[question['ref']] = QFactory(question) | ||
| 115 | - | ||
| 116 | - # check if all the questions can be correctly generated | ||
| 117 | - # TODO and corrected | ||
| 118 | - try: | ||
| 119 | - self.question_factory[question['ref']].generate() | ||
| 120 | - except Exception as exc: | ||
| 121 | - msg = f'Failed to generate "{question["ref"]}"' | ||
| 122 | - raise TestFactoryException(msg) from exc | ||
| 123 | - else: | ||
| 124 | - logger.info('%4d. "%s" Ok.', counter, question["ref"]) | ||
| 125 | - counter += 1 | ||
| 126 | - | ||
| 127 | - qmissing = qrefs.difference(set(self.question_factory.keys())) | 106 | + raise TestFactoryException(msg) from exc |
| 107 | + | ||
| 108 | + self['question_factory'][question['ref']] = QFactory(question) | ||
| 109 | + | ||
| 110 | + qmissing = qrefs.difference(set(self['question_factory'].keys())) | ||
| 128 | if qmissing: | 111 | if qmissing: |
| 129 | raise TestFactoryException(f'Could not find questions {qmissing}.') | 112 | raise TestFactoryException(f'Could not find questions {qmissing}.') |
| 130 | 113 | ||
| 114 | + self.check_questions() | ||
| 115 | + | ||
| 131 | logger.info('Test factory ready. No errors found.') | 116 | logger.info('Test factory ready. No errors found.') |
| 132 | 117 | ||
| 133 | 118 | ||
| @@ -231,6 +216,32 @@ class TestFactory(dict): | @@ -231,6 +216,32 @@ class TestFactory(dict): | ||
| 231 | self.check_grade_scaling() | 216 | self.check_grade_scaling() |
| 232 | 217 | ||
| 233 | # ------------------------------------------------------------------------ | 218 | # ------------------------------------------------------------------------ |
| 219 | + def check_questions(self) -> None: | ||
| 220 | + ''' | ||
| 221 | + checks if questions can be correctly generated and corrected | ||
| 222 | + ''' | ||
| 223 | + logger.info('Checking if questions can be generated and corrected...') | ||
| 224 | + for i, (qref, qfact) in enumerate(self['question_factory'].items()): | ||
| 225 | + logger.info('%4d. %s:', i, qref) | ||
| 226 | + try: | ||
| 227 | + question = qfact.generate() | ||
| 228 | + except Exception as exc: | ||
| 229 | + msg = f'Failed to generate "{qref}"' | ||
| 230 | + raise TestFactoryException(msg) from exc | ||
| 231 | + else: | ||
| 232 | + logger.info(' generate Ok') | ||
| 233 | + | ||
| 234 | + if question['type'] in ('code', 'textarea'): | ||
| 235 | + try: | ||
| 236 | + question.set_answer('') | ||
| 237 | + question.correct() | ||
| 238 | + except Exception as exc: | ||
| 239 | + msg = f'Failed to correct "{qref}"' | ||
| 240 | + raise TestFactoryException(msg) from exc | ||
| 241 | + else: | ||
| 242 | + logger.info(' correct Ok') | ||
| 243 | + | ||
| 244 | + # ------------------------------------------------------------------------ | ||
| 234 | async def generate(self): | 245 | async def generate(self): |
| 235 | ''' | 246 | ''' |
| 236 | Given a dictionary with a student dict {'name':'john', 'number': 123} | 247 | Given a dictionary with a student dict {'name':'john', 'number': 123} |
| @@ -250,7 +261,7 @@ class TestFactory(dict): | @@ -250,7 +261,7 @@ class TestFactory(dict): | ||
| 250 | for qref in qrefs: | 261 | for qref in qrefs: |
| 251 | # generate instance of question | 262 | # generate instance of question |
| 252 | try: | 263 | try: |
| 253 | - question = await self.question_factory[qref].gen_async() | 264 | + question = await self['question_factory'][qref].gen_async() |
| 254 | except QuestionException: | 265 | except QuestionException: |
| 255 | logger.error('Can\'t generate question "%s". Skipping.', qref) | 266 | logger.error('Can\'t generate question "%s". Skipping.', qref) |
| 256 | nerr += 1 | 267 | nerr += 1 |