From fa091c84af26c006bf1964d0972a737d737c23ee Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Tue, 12 Mar 2019 13:24:42 +0000 Subject: [PATCH] Allow generator to generate arguments to correction script of textarea. Arguments are no longer sent to stdin, but in the command line instead. Topic sequence shows all the unlocked topics first. Drop compatibility with python 3.6 because of asyncio.get_running_loop(). --- BUGS.md | 1 + aprendizations/factory.py | 5 +++-- aprendizations/knowledge.py | 21 ++++++--------------- aprendizations/questions.py | 20 ++++++++------------ aprendizations/templates/maintopics-table.html | 1 + aprendizations/tools.py | 7 +++++-- demo/math/gen-multiples-of-3.py | 8 +++----- demo/math/questions.yaml | 6 +++--- 8 files changed, 30 insertions(+), 39 deletions(-) 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