diff --git a/BUGS.md b/BUGS.md index 284ed21..55a96a2 100644 --- a/BUGS.md +++ b/BUGS.md @@ -10,6 +10,7 @@ # TODO +- ao fim de 3 tentativas de login, envial email para aluno com link para definir nova password (com timeout de 5 minutos). - mostrar capitulos e subtopicos de forma hierarquica. clicar no capitulo expande as dependencias. - mostrar rankings alunos/perguntas respondidas/% correctas/valor esperado topicos. - botão não sei... diff --git a/aprendizations/factory.py b/aprendizations/factory.py index e499e1f..ea9178a 100644 --- a/aprendizations/factory.py +++ b/aprendizations/factory.py @@ -83,9 +83,10 @@ class QFactory(object): # output is then yaml parsed into a dictionary `q`. if q['type'] == 'generator': logger.debug(f' \\_ Running "{q["script"]}".') - q.setdefault('arg', '') # optional arguments will be sent to stdin + q.setdefault('args', []) + q.setdefault('stdin', '') script = path.join(q['path'], q['script']) - out = run_script(script=script, stdin=q['arg']) + out = run_script(script=script, args=q['args'], stdin=q['stdin']) q.update(out) # Finally we create an instance of Question() diff --git a/aprendizations/knowledge.py b/aprendizations/knowledge.py index c173256..3f7d99a 100644 --- a/aprendizations/knowledge.py +++ b/aprendizations/knowledge.py @@ -31,8 +31,8 @@ class StudentKnowledge(object): self.state = state # {'topic': {'level': 0.5, 'date': datetime}, ...} self.update_topic_levels() # applies forgetting factor - self.topic_sequence = self.recommend_topic_sequence() # ['ref1', ...] self.unlock_topics() # whose dependencies have been completed + self.topic_sequence = self.recommend_topic_sequence() # ['ref1', ...] self.current_topic = None # ------------------------------------------------------------------------ @@ -53,7 +53,7 @@ class StudentKnowledge(object): # for the topic to be unlocked. min_level = 0.01 - for topic in self.topic_sequence: + for topic in self.deps.nodes(): if topic not in self.state: # if locked pred = self.deps.predecessors(topic) if all(d in self.state and self.state[d]['level'] > min_level @@ -185,19 +185,10 @@ class StudentKnowledge(object): # compute recommended sequence of topics ['a', 'b', ...] # ------------------------------------------------------------------------ def recommend_topic_sequence(self, target=None): - return list(nx.topological_sort(self.deps)) - - # if target is None: - # target = list(nx.topological_sort(self.deps))[-1] - - # weights = {} - # tseq = [target] - - # def topic_weights(d, g, n): - # pred = g.predecessors(n) - # for p in pred: - # topic_weights(d, g, p) - # d[n] = 1 + sum(d[m]) + tt = list(nx.topological_sort(self.deps)) + unlocked = [t for t in tt if t in self.state] + locked = [t for t in tt if t not in unlocked] + return unlocked + locked # ------------------------------------------------------------------------ def get_current_question(self): diff --git a/aprendizations/questions.py b/aprendizations/questions.py index ae26fe5..5de9374 100644 --- a/aprendizations/questions.py +++ b/aprendizations/questions.py @@ -49,10 +49,9 @@ class Question(dict): return 0.0 async def correct_async(self) -> float: - # loop = asyncio.get_running_loop() # FIXME python 3.7 only - loop = asyncio.get_event_loop() - grade = await loop.run_in_executor(None, self.correct) - return grade + loop = asyncio.get_running_loop() # FIXME python 3.7 only + # loop = asyncio.get_event_loop() # python 3.6 + return await loop.run_in_executor(None, self.correct) def set_defaults(self, d): 'Add k:v pairs from default dict d for nonexistent keys' @@ -234,7 +233,6 @@ class QuestionText(Question): self['correct'] = [str(a) for a in self['correct']] # ------------------------------------------------------------------------ - # can return negative values for wrong answers def correct(self): super().correct() @@ -263,7 +261,6 @@ class QuestionTextRegex(Question): }) # ------------------------------------------------------------------------ - # can return negative values for wrong answers def correct(self): super().correct() if self['answer'] is not None: @@ -297,7 +294,6 @@ class QuestionNumericInterval(Question): }) # ------------------------------------------------------------------------ - # can return negative values for wrong answers def correct(self): super().correct() if self['answer'] is not None: @@ -333,20 +329,20 @@ class QuestionTextArea(Question): 'text': '', 'lines': 8, 'timeout': 5, # seconds - 'correct': '' # trying to execute this will fail => grade 0.0 + 'correct': '', # trying to execute this will fail => grade 0.0 + 'args': [] }) self['correct'] = path.join(self['path'], self['correct']) # FIXME # ------------------------------------------------------------------------ - # can return negative values for wrong answers def correct(self): super().correct() - if self['answer'] is not None: - # correct answer - out = run_script( # and parse yaml ouput + if self['answer'] is not None: # correct answer and parse yaml ouput + out = run_script( script=self['correct'], + args=self['args'], stdin=self['answer'], timeout=self['timeout'] ) diff --git a/aprendizations/templates/maintopics-table.html b/aprendizations/templates/maintopics-table.html index 80ddc0e..f5985a0 100644 --- a/aprendizations/templates/maintopics-table.html +++ b/aprendizations/templates/maintopics-table.html @@ -102,6 +102,7 @@ {% elif t['type']=='chapter' %}
+
{% else %}
diff --git a/aprendizations/tools.py b/aprendizations/tools.py index e32f48e..9f10cc5 100644 --- a/aprendizations/tools.py +++ b/aprendizations/tools.py @@ -161,10 +161,11 @@ def load_yaml(filename, default=None): # The script is run in another process but this function blocks waiting # for its termination. # --------------------------------------------------------------------------- -def run_script(script, stdin='', timeout=5): +def run_script(script, args=[], stdin='', timeout=5): script = path.expanduser(script) try: - p = subprocess.run([script], + cmd = [script] + [str(a) for a in args] + p = subprocess.run(cmd, input=stdin, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, @@ -179,6 +180,8 @@ def run_script(script, stdin='', timeout=5): logger.error(f'Can not execute script "{script}": unknown reason.') except subprocess.TimeoutExpired: logger.error(f'Timeout {timeout}s exceeded while running "{script}".') + except Exception: + logger.error(f'An Exception ocurred running {script}.') else: if p.returncode != 0: logger.error(f'Return code {p.returncode} running "{script}".') diff --git a/demo/math/gen-multiples-of-3.py b/demo/math/gen-multiples-of-3.py index 8286184..e001745 100755 --- a/demo/math/gen-multiples-of-3.py +++ b/demo/math/gen-multiples-of-3.py @@ -3,9 +3,8 @@ import random import sys -arg = sys.stdin.read() # read arguments +a, b = map(int, sys.argv[1:]) # get command line arguments -a,b = (int(n) for n in arg.split(',')) numbers = list(range(a, b)) random.shuffle(numbers) numbers = numbers[:8] @@ -18,14 +17,13 @@ else: solution = f'Nenhum número mostrado é múltiplo de 3.' -q = f''' +q = f'''--- type: checkbox title: Múltiplos de 3 text: Indique quais dos seguintes números são múltiplos de 3. options: {numbers} correct: {correct} solution: | - {solution} -''' + {solution}''' print(q) diff --git a/demo/math/questions.yaml b/demo/math/questions.yaml index e939542..c354cf6 100644 --- a/demo/math/questions.yaml +++ b/demo/math/questions.yaml @@ -79,6 +79,6 @@ - type: generator ref: overflow script: gen-multiples-of-3.py - arg: "11,120" - # the program should print a question in yaml format. The arg will be - # sent to the stdin when the program is run. + args: [11, 120] + # the program should print a question in yaml format. The args will be + # sent as command line options when the program is run. -- libgit2 0.21.2