diff --git a/BUGS.md b/BUGS.md index 5b52e79..8b4c93e 100644 --- a/BUGS.md +++ b/BUGS.md @@ -31,6 +31,7 @@ # FIXED +- mesma ref no mesmo ficheiro não é detectado. - enter nas respostas mostra json - apos clicar no botao responder, inactivar o input (importante quando o tempo de correcção é grande) - double click submits twice. diff --git a/aprendizations/learnapp.py b/aprendizations/learnapp.py index 9d410d3..bbb3083 100644 --- a/aprendizations/learnapp.py +++ b/aprendizations/learnapp.py @@ -90,7 +90,6 @@ class LearnApp(object): for c, d in self.courses.items(): for goal in d['goals']: if goal not in self.deps.nodes(): - # logger.error(f'Goal "{goal}" of "{c}"" not in the graph') raise LearnException(f'Goal "{goal}" from course "{c}" ' ' does not exist') @@ -409,14 +408,24 @@ class LearnApp(object): fullpath: str = path.join(topicpath, t['file']) logger.debug(f' Loading {fullpath}') - questions: List[QDict] = load_yaml(fullpath, default=[]) + # questions: List[QDict] = load_yaml(fullpath, default=[]) + try: + questions: List[QDict] = load_yaml(fullpath) + except Exception: + raise LearnException(f'Failed to load "{fullpath}"') # update refs to include topic as prefix. # refs are required to be unique only within the file. # undefined are set to topic:n, where n is the question number # within the file + localrefs = set() # refs in current file for i, q in enumerate(questions): qref = q.get('ref', str(i)) # ref or number + if qref in localrefs: + msg = f'Duplicate ref "{qref}" in "{topicpath}"' + raise LearnException(msg) + localrefs.add(qref) + q['ref'] = f'{tref}:{qref}' q['path'] = topicpath q.setdefault('append_wrong', t['append_wrong']) diff --git a/aprendizations/questions.py b/aprendizations/questions.py index 93d639f..9a266ef 100644 --- a/aprendizations/questions.py +++ b/aprendizations/questions.py @@ -74,7 +74,14 @@ class QuestionRadio(Question): def __init__(self, q: QDict) -> None: super().__init__(q) - n = len(self['options']) + try: + n = len(self['options']) + except KeyError: + msg = f'Missing `options` in radio question. See {self["path"]}' + raise QuestionException(msg) + except TypeError: + msg = f'`options` must be a list. See {self["path"]}' + raise QuestionException(msg) self.set_defaults(QDict({ 'text': '', @@ -185,7 +192,14 @@ class QuestionCheckbox(Question): def __init__(self, q: QDict) -> None: super().__init__(q) - n = len(self['options']) + try: + n = len(self['options']) + except KeyError: + msg = f'Missing `options` in radio question. See {self["path"]}' + raise QuestionException(msg) + except TypeError: + msg = f'`options` must be a list. See {self["path"]}' + raise QuestionException(msg) # set defaults if missing self.set_defaults(QDict({ diff --git a/aprendizations/tools.py b/aprendizations/tools.py index f592545..0ec43f3 100644 --- a/aprendizations/tools.py +++ b/aprendizations/tools.py @@ -111,14 +111,13 @@ class HighlightRenderer(mistune.Renderer): def table(self, header, body): return ('' - f'{header}{body}
') + f'{header}{body}') def image(self, src, title, alt): alt = mistune.escape(alt, quote=True) title = mistune.escape(title or '', quote=True) return (f'{alt}') - # class="img-fluid mx-auto d-block" + f'class="img-fluid">') # class="img-fluid mx-auto d-block" # Pass math through unaltered - mathjax does the rendering in the browser def block_math(self, text): @@ -150,25 +149,22 @@ def load_yaml(filename: str, default: Any = None) -> Any: filename = path.expanduser(filename) try: f = open(filename, 'r', encoding='utf-8') - except FileNotFoundError: - logger.error(f'Cannot open "{filename}": not found') - except PermissionError: - logger.error(f'Cannot open "{filename}": no permission') - except OSError: - logger.error(f'Cannot open file "{filename}"') - else: - with f: - try: - default = yaml.safe_load(f) - except yaml.YAMLError as e: - if hasattr(e, 'problem_mark'): - mark = e.problem_mark - logger.error(f'File "{filename}" near line {mark.line+1}, ' - f'column {mark.column+1}') - else: - logger.error(f'File "{filename}"') - finally: - return default + except Exception as e: + logger.error(e) + if default is not None: + return default + else: + raise + + with f: + try: + return yaml.safe_load(f) + except yaml.YAMLError as e: + logger.error(str(e).replace('\n', ' ')) + if default is not None: + return default + else: + raise # --------------------------------------------------------------------------- -- libgit2 0.21.2