diff --git a/aprendizations/questions.py b/aprendizations/questions.py index 474860a..62fc9d3 100644 --- a/aprendizations/questions.py +++ b/aprendizations/questions.py @@ -42,7 +42,6 @@ class Question(dict): 'comments': '', 'solution': '', 'files': {}, - # 'max_tries': 3, })) def correct(self) -> None: @@ -220,13 +219,19 @@ class QuestionCheckbox(Question): # check grade boundaries if self['discount'] and not all(0.0 <= x <= 1.0 for x in self['correct']): - logger.warning(' * * * BEHAVIOR CHANGE NOTICE * * *') - msg = (f'Correct values must be in the interval [0.0, 1.0] in ' - f'"{self["ref"]}"') - logger.warning(msg) - logger.warning('I will convert "correct" to the new behavior, but ' - 'you should change it in the questions') - logger.warning(' * * * END OF NOTICE * * *') + + msg0 = ('+-------------- BEHAVIOR CHANGE NOTICE --------------+') + msg1 = ('| Correct values must be in the interval [0.0, 1.0]. |') + msg2 = ('| I will convert to the new behavior, but you should |') + msg3 = ('| fix it in the question. |') + msg4 = ('+----------------------------------------------------+') + logger.warning(msg0) + logger.warning(msg1) + logger.warning(msg2) + logger.warning(msg3) + logger.warning(msg4) + logger.warning(f'please fix "{self["ref"]}"') + # normalize to [0,1] self['correct'] = [(x+1)/2 for x in self['correct']] @@ -296,15 +301,21 @@ class QuestionText(Question): # make sure its always a list of possible correct answers if not isinstance(self['correct'], list): - self['correct'] = [self['correct']] + self['correct'] = [str(self['correct'])] + else: + # make sure all elements of the list are strings + self['correct'] = [str(a) for a in self['correct']] - # make sure all elements of the list are strings - self['correct'] = [str(a) for a in self['correct']] + for f in self['transform']: + if f not in ('remove_space', 'trim', 'normalize_space', 'lower', + 'upper'): + msg = (f'Unknown transform "{f}" in "{self["ref"]}"') + raise QuestionException(msg) - # make sure that the answers are invariant with respect to the filters + # check if answers are invariant with respect to the transforms if any(c != self.transform(c) for c in self['correct']): logger.warning(f'in "{self["ref"]}", correct answers are not ' - 'invariant wrt transformations') + 'invariant wrt transformations => never correct') # ------------------------------------------------------------------------ # apply optional filters to the answer @@ -358,8 +369,12 @@ class QuestionTextRegex(Question): if not isinstance(self['correct'], list): self['correct'] = [self['correct']] - # make sure all elements of the list are strings - self['correct'] = [str(a) for a in self['correct']] + # converts patterns to compiled versions + try: + self['correct'] = [re.compile(a) for a in self['correct']] + except Exception: + msg = f'Failed to compile regex in "{self["ref"]}"' + raise QuestionException(msg) # ------------------------------------------------------------------------ def correct(self) -> None: @@ -368,12 +383,12 @@ class QuestionTextRegex(Question): self['grade'] = 0.0 for r in self['correct']: try: - if re.match(r, self['answer']): + if r.match(self['answer']): self['grade'] = 1.0 return except TypeError: - logger.error(f'While matching regex {self["correct"]} with' - f' answer "{self["answer"]}".') + logger.error(f'While matching regex {r.pattern} with ' + f'answer "{self["answer"]}".') # ============================================================================ @@ -395,6 +410,30 @@ class QuestionNumericInterval(Question): 'correct': [1.0, -1.0], # will always return false })) + # if only one number n is given, make an interval [n,n] + if isinstance(self['correct'], (int, float)): + self['correct'] = [float(self['correct']), float(self['correct'])] + + # make sure its a list of two numbers + elif isinstance(self['correct'], list): + if len(self['correct']) != 2: + msg = (f'Numeric interval must be a list with two numbers, in ' + f'{self["ref"]}') + raise QuestionException(msg) + + try: + self['correct'] = [float(n) for n in self['correct']] + except Exception: + msg = (f'Numeric interval must be a list with two numbers, in ' + f'{self["ref"]}') + raise QuestionException(msg) + + # invalid + else: + msg = (f'Numeric interval must be a list with two numbers, in ' + f'{self["ref"]}') + raise QuestionException(msg) + # ------------------------------------------------------------------------ def correct(self) -> None: super().correct() @@ -576,7 +615,7 @@ class QFactory(object): if q['type'] == 'generator': logger.debug(f' \\_ Running "{q["script"]}".') q.setdefault('args', []) - q.setdefault('stdin', '') # FIXME is it really necessary? + q.setdefault('stdin', '') script = path.join(q['path'], q['script']) out = await run_script_async(script=script, args=q['args'], stdin=q['stdin']) -- libgit2 0.21.2