Commit 77dc4f5e21a0e7d35b6205d029473642b6a9d2f3

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

refactor checking questions.

add check for correction of code and textarea.
BUGS.md
1 1  
2 2 # BUGS
3 3  
  4 +- JOBE correct async
4 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 6 - QuestionCode falta reportar nos comments os vários erros que podem ocorrer (timeout, etc)
6 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 32 '''
33 33  
34 34 APP_NAME = 'perguntations'
35   -APP_VERSION = '2020.11.dev3'
  35 +APP_VERSION = '2020.11.dev4'
36 36 APP_DESCRIPTION = __doc__
37 37  
38 38 __author__ = 'Miguel Barão'
... ...
perguntations/testfactory.py
... ... @@ -43,7 +43,7 @@ class TestFactory(dict):
43 43 super().__init__({ # defaults
44 44 'title': '',
45 45 'show_points': True,
46   - 'scale': None, # or [0, 20]
  46 + 'scale': None,
47 47 'duration': 0, # 0=infinite
48 48 'autosubmit': False,
49 49 'debug': False,
... ... @@ -66,12 +66,10 @@ class TestFactory(dict):
66 66 len(qrefs), len(self["questions"]))
67 67  
68 68 # --- load and build question factories
69   - self.question_factory = {}
  69 + self['question_factory'] = {}
70 70  
71   - counter = 1
72 71 for file in self["files"]:
73 72 fullpath = path.normpath(path.join(self["questions_dir"], file))
74   - (dirname, filename) = path.split(fullpath)
75 73  
76 74 logger.info('Loading "%s"...', fullpath)
77 75 questions = load_yaml(fullpath) # , default=[])
... ... @@ -88,8 +86,8 @@ class TestFactory(dict):
88 86 logger.warning('Missing ref set to "%s"', question["ref"])
89 87  
90 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 91 otherfile = path.join(other.question['path'],
94 92 other.question['filename'])
95 93 msg = (f'Duplicate reference "{question["ref"]}" in files '
... ... @@ -98,36 +96,23 @@ class TestFactory(dict):
98 96  
99 97 # make factory only for the questions used in the test
100 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 101 if question['type'] == 'code' and 'server' not in question:
108 102 try:
109 103 question['server'] = self['jobe_server']
110 104 except KeyError as exc:
111 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 111 if qmissing:
129 112 raise TestFactoryException(f'Could not find questions {qmissing}.')
130 113  
  114 + self.check_questions()
  115 +
131 116 logger.info('Test factory ready. No errors found.')
132 117  
133 118  
... ... @@ -231,6 +216,32 @@ class TestFactory(dict):
231 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 245 async def generate(self):
235 246 '''
236 247 Given a dictionary with a student dict {'name':'john', 'number': 123}
... ... @@ -250,7 +261,7 @@ class TestFactory(dict):
250 261 for qref in qrefs:
251 262 # generate instance of question
252 263 try:
253   - question = await self.question_factory[qref].gen_async()
  264 + question = await self['question_factory'][qref].gen_async()
254 265 except QuestionException:
255 266 logger.error('Can\'t generate question "%s". Skipping.', qref)
256 267 nerr += 1
... ...