Commit 13c5be34b19bd4627453b4f075c2c1eb59009270

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

more checks in questions.py

Showing 1 changed file with 58 additions and 19 deletions   Show diff stats
aprendizations/questions.py
... ... @@ -42,7 +42,6 @@ class Question(dict):
42 42 'comments': '',
43 43 'solution': '',
44 44 'files': {},
45   - # 'max_tries': 3,
46 45 }))
47 46  
48 47 def correct(self) -> None:
... ... @@ -220,13 +219,19 @@ class QuestionCheckbox(Question):
220 219 # check grade boundaries
221 220 if self['discount'] and not all(0.0 <= x <= 1.0
222 221 for x in self['correct']):
223   - logger.warning(' * * * BEHAVIOR CHANGE NOTICE * * *')
224   - msg = (f'Correct values must be in the interval [0.0, 1.0] in '
225   - f'"{self["ref"]}"')
226   - logger.warning(msg)
227   - logger.warning('I will convert "correct" to the new behavior, but '
228   - 'you should change it in the questions')
229   - logger.warning(' * * * END OF NOTICE * * *')
  222 +
  223 + msg0 = ('+-------------- BEHAVIOR CHANGE NOTICE --------------+')
  224 + msg1 = ('| Correct values must be in the interval [0.0, 1.0]. |')
  225 + msg2 = ('| I will convert to the new behavior, but you should |')
  226 + msg3 = ('| fix it in the question. |')
  227 + msg4 = ('+----------------------------------------------------+')
  228 + logger.warning(msg0)
  229 + logger.warning(msg1)
  230 + logger.warning(msg2)
  231 + logger.warning(msg3)
  232 + logger.warning(msg4)
  233 + logger.warning(f'please fix "{self["ref"]}"')
  234 +
230 235 # normalize to [0,1]
231 236 self['correct'] = [(x+1)/2 for x in self['correct']]
232 237  
... ... @@ -296,15 +301,21 @@ class QuestionText(Question):
296 301  
297 302 # make sure its always a list of possible correct answers
298 303 if not isinstance(self['correct'], list):
299   - self['correct'] = [self['correct']]
  304 + self['correct'] = [str(self['correct'])]
  305 + else:
  306 + # make sure all elements of the list are strings
  307 + self['correct'] = [str(a) for a in self['correct']]
300 308  
301   - # make sure all elements of the list are strings
302   - self['correct'] = [str(a) for a in self['correct']]
  309 + for f in self['transform']:
  310 + if f not in ('remove_space', 'trim', 'normalize_space', 'lower',
  311 + 'upper'):
  312 + msg = (f'Unknown transform "{f}" in "{self["ref"]}"')
  313 + raise QuestionException(msg)
303 314  
304   - # make sure that the answers are invariant with respect to the filters
  315 + # check if answers are invariant with respect to the transforms
305 316 if any(c != self.transform(c) for c in self['correct']):
306 317 logger.warning(f'in "{self["ref"]}", correct answers are not '
307   - 'invariant wrt transformations')
  318 + 'invariant wrt transformations => never correct')
308 319  
309 320 # ------------------------------------------------------------------------
310 321 # apply optional filters to the answer
... ... @@ -358,8 +369,12 @@ class QuestionTextRegex(Question):
358 369 if not isinstance(self['correct'], list):
359 370 self['correct'] = [self['correct']]
360 371  
361   - # make sure all elements of the list are strings
362   - self['correct'] = [str(a) for a in self['correct']]
  372 + # converts patterns to compiled versions
  373 + try:
  374 + self['correct'] = [re.compile(a) for a in self['correct']]
  375 + except Exception:
  376 + msg = f'Failed to compile regex in "{self["ref"]}"'
  377 + raise QuestionException(msg)
363 378  
364 379 # ------------------------------------------------------------------------
365 380 def correct(self) -> None:
... ... @@ -368,12 +383,12 @@ class QuestionTextRegex(Question):
368 383 self['grade'] = 0.0
369 384 for r in self['correct']:
370 385 try:
371   - if re.match(r, self['answer']):
  386 + if r.match(self['answer']):
372 387 self['grade'] = 1.0
373 388 return
374 389 except TypeError:
375   - logger.error(f'While matching regex {self["correct"]} with'
376   - f' answer "{self["answer"]}".')
  390 + logger.error(f'While matching regex {r.pattern} with '
  391 + f'answer "{self["answer"]}".')
377 392  
378 393  
379 394 # ============================================================================
... ... @@ -395,6 +410,30 @@ class QuestionNumericInterval(Question):
395 410 'correct': [1.0, -1.0], # will always return false
396 411 }))
397 412  
  413 + # if only one number n is given, make an interval [n,n]
  414 + if isinstance(self['correct'], (int, float)):
  415 + self['correct'] = [float(self['correct']), float(self['correct'])]
  416 +
  417 + # make sure its a list of two numbers
  418 + elif isinstance(self['correct'], list):
  419 + if len(self['correct']) != 2:
  420 + msg = (f'Numeric interval must be a list with two numbers, in '
  421 + f'{self["ref"]}')
  422 + raise QuestionException(msg)
  423 +
  424 + try:
  425 + self['correct'] = [float(n) for n in self['correct']]
  426 + except Exception:
  427 + msg = (f'Numeric interval must be a list with two numbers, in '
  428 + f'{self["ref"]}')
  429 + raise QuestionException(msg)
  430 +
  431 + # invalid
  432 + else:
  433 + msg = (f'Numeric interval must be a list with two numbers, in '
  434 + f'{self["ref"]}')
  435 + raise QuestionException(msg)
  436 +
398 437 # ------------------------------------------------------------------------
399 438 def correct(self) -> None:
400 439 super().correct()
... ... @@ -576,7 +615,7 @@ class QFactory(object):
576 615 if q['type'] == 'generator':
577 616 logger.debug(f' \\_ Running "{q["script"]}".')
578 617 q.setdefault('args', [])
579   - q.setdefault('stdin', '') # FIXME is it really necessary?
  618 + q.setdefault('stdin', '')
580 619 script = path.join(q['path'], q['script'])
581 620 out = await run_script_async(script=script, args=q['args'],
582 621 stdin=q['stdin'])
... ...