Commit ef9f327f796b63a6873f4170d0b1db691c2fd71e
1 parent
d1410958
Exists in
master
and in
1 other branch
- another version not working...
Showing
15 changed files
with
304 additions
and
120 deletions
Show diff stats
config/logger-debug.yaml
| @@ -34,6 +34,11 @@ loggers: | @@ -34,6 +34,11 @@ loggers: | ||
| 34 | level: 'DEBUG' | 34 | level: 'DEBUG' |
| 35 | propagate: False | 35 | propagate: False |
| 36 | 36 | ||
| 37 | + 'questionfactory': | ||
| 38 | + handlers: ['default'] | ||
| 39 | + level: 'DEBUG' | ||
| 40 | + propagate: False | ||
| 41 | + | ||
| 37 | 'tools': | 42 | 'tools': |
| 38 | handlers: ['default'] | 43 | handlers: ['default'] |
| 39 | level: 'DEBUG' | 44 | level: 'DEBUG' |
| @@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
| 1 | +title: Example | ||
| 2 | +database: students.db | ||
| 3 | + | ||
| 4 | +# path prefix applied to the topics | ||
| 5 | +path: ./demo | ||
| 6 | + | ||
| 7 | +# Representation of the edges of the dependency graph. | ||
| 8 | +# Example: A depends on B and C | ||
| 9 | +# A: | ||
| 10 | +# name: Topic A | ||
| 11 | +# deps: | ||
| 12 | +# - B | ||
| 13 | +# - C | ||
| 14 | +topics: | ||
| 15 | + | ||
| 16 | + # topic without dependencies | ||
| 17 | + math: | ||
| 18 | + name: Matemática | ||
| 19 | + | ||
| 20 | + # topic with one dependency | ||
| 21 | + solar_system: | ||
| 22 | + name: Solar system | ||
| 23 | + deps: | ||
| 24 | + - math |
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | + | ||
| 3 | +from random import randint | ||
| 4 | +import sys | ||
| 5 | + | ||
| 6 | +arg = sys.stdin.read() # read arguments | ||
| 7 | + | ||
| 8 | +a,b = (int(n) for n in arg.split(',')) | ||
| 9 | + | ||
| 10 | +q = ''' | ||
| 11 | +type: checkbox | ||
| 12 | +text: | | ||
| 13 | + Indique quais das seguintes adições resultam em overflow quando se considera a adição de números com sinal (complemento para 2) em registos de 8 bits. | ||
| 14 | + | ||
| 15 | + Os números foram gerados aleatoriamente no intervalo de {0} a {1}. | ||
| 16 | +options: | ||
| 17 | +'''.format(a,b) | ||
| 18 | + | ||
| 19 | +correct = [] | ||
| 20 | +for i in range(5): | ||
| 21 | + x = randint(a, b) | ||
| 22 | + y = randint(a, b) | ||
| 23 | + q += '- "`{} + {}`"\n'.format(x, y) | ||
| 24 | + correct.append(1 if x + y > 127 else -1) | ||
| 25 | + | ||
| 26 | +q += 'correct: ' + str(correct) | ||
| 27 | + | ||
| 28 | +print(q) |
| @@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
| 1 | +- | ||
| 2 | + ref: prime_numbers | ||
| 3 | + type: radio | ||
| 4 | + title: Números primos | ||
| 5 | + text: Qual dos seguintes números é primo? | ||
| 6 | + options: | ||
| 7 | + - 13 | ||
| 8 | + - 12 | ||
| 9 | + - 14 | ||
| 10 | + - 1, a **unidade** | ||
| 11 | + | ||
| 12 | +# # --------------------------------------------------------------------------- | ||
| 13 | +# - | ||
| 14 | +# ref: distributive_property | ||
| 15 | +# type: radio | ||
| 16 | +# title: Propriedade distributiva | ||
| 17 | +# text: | | ||
| 18 | +# Qual das seguintes opções usa a propriedade distributiva para calcular $9\times 2 - 2\times 3$? | ||
| 19 | +# options: | ||
| 20 | +# - $2\times(9 - 3)$ | ||
| 21 | +# - $18 - 6 = 12$ | ||
| 22 | +# - $2\times 9 - 2\times 3$ | ||
| 23 | +# - $2\times(9-2\times 3)$ | ||
| 24 | + | ||
| 25 | +# # --------------------------------------------------------------------------- | ||
| 26 | +# - | ||
| 27 | +# ref: math-expressions | ||
| 28 | +# type: checkbox | ||
| 29 | +# title: Expressões matemáticas | ||
| 30 | +# text: Quais das seguintes expressões são verdadeiras? | ||
| 31 | +# options: | ||
| 32 | +# - $1 > 0$ | ||
| 33 | +# - $\sqrt{3} > \sqrt{2}$ | ||
| 34 | +# - $e^{i\pi} + 1 = 0$ | ||
| 35 | +# - $\frac{\partial f(x,y)}{\partial z} = 1$ | ||
| 36 | +# - $-1 > 1$ | ||
| 37 | +# # how many points for each checkmark (normalized afterwards): | ||
| 38 | +# correct: [1, 1, 1, -1, -1] | ||
| 39 | + | ||
| 40 | +# # --------------------------------------------------------------------------- | ||
| 41 | +# - | ||
| 42 | +# ref: overflow | ||
| 43 | +# type: generator | ||
| 44 | +# script: generate-overflow.py | ||
| 45 | +# # opcional | ||
| 46 | +# arg: "11,120" | ||
| 47 | +# # the script should print a question dict in yaml format. |
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | + | ||
| 3 | +import re | ||
| 4 | +import sys | ||
| 5 | + | ||
| 6 | +s = sys.stdin.read() | ||
| 7 | + | ||
| 8 | +# set of words converted to lowercase | ||
| 9 | +answer = set(re.findall(r'[\w]+', s.lower())) | ||
| 10 | +answer.difference_update({'e', 'a', 'planeta', 'planetas'}) # ignore these | ||
| 11 | + | ||
| 12 | +# correct set of colors | ||
| 13 | +planets = set(['mercúrio', 'vénus', 'terra']) | ||
| 14 | + | ||
| 15 | +correct = set.intersection(answer, planets) # os que acertei | ||
| 16 | +wrong = set.difference(answer, planets) # os que errei | ||
| 17 | +# allnames = set.union(answer, planets) | ||
| 18 | + | ||
| 19 | +grade = (len(correct) - len(wrong)) / len(planets) | ||
| 20 | + | ||
| 21 | +out = f'grade: {grade}' | ||
| 22 | + | ||
| 23 | +if grade < 1.0: | ||
| 24 | + out += '\ncomments: A resposta correcta é `Mercúrio, Vénus e Terra`.' | ||
| 25 | + | ||
| 26 | +print(out) | ||
| 27 | +exit(0) | ||
| 0 | \ No newline at end of file | 28 | \ No newline at end of file |
419 KB
| @@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
| 1 | + | ||
| 2 | +# --------------------------------------------------------------------------- | ||
| 3 | +- | ||
| 4 | + ref: solar-system | ||
| 5 | + type: radio | ||
| 6 | + title: Sistema solar | ||
| 7 | + text: Qual é o maior planeta do Sistema Solar? | ||
| 8 | + options: | ||
| 9 | + - Mercúrio | ||
| 10 | + - Marte | ||
| 11 | + - Júpiter | ||
| 12 | + - Têm todos o mesmo tamanho | ||
| 13 | + # opcional | ||
| 14 | + correct: 2 | ||
| 15 | + shuffle: False | ||
| 16 | + discount: True | ||
| 17 | + | ||
| 18 | +# --------------------------------------------------------------------------- | ||
| 19 | +- | ||
| 20 | + ref: home-planet | ||
| 21 | + type: text | ||
| 22 | + title: Sistema solar | ||
| 23 | + text: O nosso planeta chama-se planeta... | ||
| 24 | + correct: ['Terra', 'terra'] | ||
| 25 | + # opcional | ||
| 26 | + answer: Não é Marte... | ||
| 27 | + | ||
| 28 | +# --------------------------------------------------------------------------- | ||
| 29 | +- | ||
| 30 | + ref: saturn | ||
| 31 | + type: text-regex | ||
| 32 | + title: Sistema solar | ||
| 33 | + text: O planeta do nosso sistema solar conhecido por ter aneis chama-se planeta... | ||
| 34 | + correct: !regex '[Ss]aturno' | ||
| 35 | + | ||
| 36 | +# --------------------------------------------------------------------------- | ||
| 37 | +- | ||
| 38 | + ref: first_3_planets | ||
| 39 | + type: textarea | ||
| 40 | + title: Sistema solar | ||
| 41 | + text: Escreva o nome dos três planetas mais próximos do Sol. (Exemplo `A, B e C`) | ||
| 42 | + correct: correct-first_3_planets.py | ||
| 43 | + # opcional | ||
| 44 | + lines: 3 | ||
| 45 | + timeout: 5 |
| @@ -0,0 +1,93 @@ | @@ -0,0 +1,93 @@ | ||
| 1 | +# QFactory is a class that can generate question instances, e.g. by shuffling | ||
| 2 | +# options, running a script to generate the question, etc. | ||
| 3 | +# | ||
| 4 | +# To generate an instance of a question we use the method generate() where | ||
| 5 | +# the argument is the reference of the question we wish to produce. | ||
| 6 | +# | ||
| 7 | +# Example: | ||
| 8 | +# | ||
| 9 | +# # read question from file | ||
| 10 | +# qdict = tools.load_yaml(filename) | ||
| 11 | +# qfactory = QFactory(qdict) | ||
| 12 | +# question = qfactory.generate() | ||
| 13 | +# | ||
| 14 | +# # experiment answering one question and correct it | ||
| 15 | +# question.updateAnswer('42') # insert answer | ||
| 16 | +# grade = question.correct() # correct answer | ||
| 17 | + | ||
| 18 | +# An instance of an actual question is an object that inherits from Question() | ||
| 19 | +# | ||
| 20 | +# Question - base class inherited by other classes | ||
| 21 | +# QuestionInformation - not a question, just a box with content | ||
| 22 | +# QuestionRadio - single choice from a list of options | ||
| 23 | +# QuestionCheckbox - multiple choice, equivalent to multiple true/false | ||
| 24 | +# QuestionText - line of text compared to a list of acceptable answers | ||
| 25 | +# QuestionTextRegex - line of text matched against a regular expression | ||
| 26 | +# QuestionTextArea - corrected by an external program | ||
| 27 | +# QuestionNumericInterval - line of text parsed as a float | ||
| 28 | + | ||
| 29 | +# base | ||
| 30 | +from os import path | ||
| 31 | +import logging | ||
| 32 | + | ||
| 33 | +# project | ||
| 34 | +from tools import run_script | ||
| 35 | +from questions import QuestionInformation, QuestionRadio, QuestionCheckbox, QuestionText, QuestionTextRegex, QuestionTextArea, QuestionNumericInterval | ||
| 36 | + | ||
| 37 | +# setup logger for this module | ||
| 38 | +logger = logging.getLogger(__name__) | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | +# =========================================================================== | ||
| 43 | +# Question Factory | ||
| 44 | +# =========================================================================== | ||
| 45 | +class QFactory(object): | ||
| 46 | + # Depending on the type of question, a different question class will be | ||
| 47 | + # instantiated. All these classes derive from the base class `Question`. | ||
| 48 | + _types = { | ||
| 49 | + 'radio' : QuestionRadio, | ||
| 50 | + 'checkbox' : QuestionCheckbox, | ||
| 51 | + 'text' : QuestionText, | ||
| 52 | + 'text-regex': QuestionTextRegex, | ||
| 53 | + 'numeric-interval': QuestionNumericInterval, | ||
| 54 | + 'textarea' : QuestionTextArea, | ||
| 55 | + # -- informative panels -- | ||
| 56 | + 'information': QuestionInformation, 'info': QuestionInformation, | ||
| 57 | + 'warning' : QuestionInformation, 'warn': QuestionInformation, | ||
| 58 | + 'alert' : QuestionInformation, | ||
| 59 | + 'success' : QuestionInformation, | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + def __init__(self, question_dict): | ||
| 63 | + self.question = question_dict | ||
| 64 | + | ||
| 65 | + # ----------------------------------------------------------------------- | ||
| 66 | + # Given a ref returns an instance of a descendent of Question(), | ||
| 67 | + # i.e. a question object (radio, checkbox, ...). | ||
| 68 | + # ----------------------------------------------------------------------- | ||
| 69 | + def generate(self): | ||
| 70 | + logger.debug(f'Generating "{self.question["ref"]}"') | ||
| 71 | + # Shallow copy so that script generated questions will not replace | ||
| 72 | + # the original generators | ||
| 73 | + q = self.question.copy() | ||
| 74 | + | ||
| 75 | + # If question is of generator type, an external program will be run | ||
| 76 | + # which will print a valid question in yaml format to stdout. This | ||
| 77 | + # output is then yaml parsed into a dictionary `q`. | ||
| 78 | + if q['type'] == 'generator': | ||
| 79 | + logger.debug(f' \_ Running script "{q["script"]}"...') | ||
| 80 | + q.setdefault('arg', '') # optional arguments will be sent to stdin | ||
| 81 | + script = path.join(q['path'], q['script']) | ||
| 82 | + out = run_script(script=script, stdin=q['arg']) | ||
| 83 | + q.update(out) | ||
| 84 | + | ||
| 85 | + # Finally we create an instance of Question() | ||
| 86 | + try: | ||
| 87 | + qinstance = self._types[q['type']](q) # instance with correct class | ||
| 88 | + except KeyError as e: | ||
| 89 | + logger.error(f'Failed to generate question "{q["ref"]}"') | ||
| 90 | + raise e | ||
| 91 | + else: | ||
| 92 | + return qinstance | ||
| 93 | + |
knowledge.py
| @@ -86,11 +86,11 @@ class StudentKnowledge(object): | @@ -86,11 +86,11 @@ class StudentKnowledge(object): | ||
| 86 | # current_question: the current question to be presented | 86 | # current_question: the current question to be presented |
| 87 | # ------------------------------------------------------------------------ | 87 | # ------------------------------------------------------------------------ |
| 88 | def init_topic(self, topic=''): | 88 | def init_topic(self, topic=''): |
| 89 | - # self.unlock_topics() # FIXME needed? | 89 | + logger.debug(f'StudentKnowledge.init_topic({topic})') |
| 90 | 90 | ||
| 91 | if not topic: | 91 | if not topic: |
| 92 | topic = self.recommended_topic() | 92 | topic = self.recommended_topic() |
| 93 | - | 93 | + print(f'recommended {topic}') |
| 94 | 94 | ||
| 95 | self.current_topic = topic | 95 | self.current_topic = topic |
| 96 | logger.info(f'Topic set to "{topic}"') | 96 | logger.info(f'Topic set to "{topic}"') |
| @@ -134,9 +134,11 @@ class StudentKnowledge(object): | @@ -134,9 +134,11 @@ class StudentKnowledge(object): | ||
| 134 | 134 | ||
| 135 | # if answer is wrong, keep same question and add a similar one at the end | 135 | # if answer is wrong, keep same question and add a similar one at the end |
| 136 | else: | 136 | else: |
| 137 | + print('failed') | ||
| 137 | factory = self.deps.node[self.current_topic]['factory'] | 138 | factory = self.deps.node[self.current_topic]['factory'] |
| 138 | self.questions.append(factory[q['ref']].generate()) | 139 | self.questions.append(factory[q['ref']].generate()) |
| 139 | 140 | ||
| 141 | + | ||
| 140 | # returns answered and corrected question | 142 | # returns answered and corrected question |
| 141 | return q | 143 | return q |
| 142 | 144 | ||
| @@ -154,10 +156,9 @@ class StudentKnowledge(object): | @@ -154,10 +156,9 @@ class StudentKnowledge(object): | ||
| 154 | return self.current_topic | 156 | return self.current_topic |
| 155 | 157 | ||
| 156 | # ------------------------------------------------------------------------ | 158 | # ------------------------------------------------------------------------ |
| 157 | - # Return list of tuples (topic, level). | ||
| 158 | - # Levels are in the interval [0, 1] or None if the topic is locked. | 159 | + # Return list of {ref: 'xpto', name: 'long name', leve: 0.5} |
| 160 | + # Levels are in the interval [0, 1] if unlocked or None if locked. | ||
| 159 | # Topics unlocked but not yet done have level 0.0. | 161 | # Topics unlocked but not yet done have level 0.0. |
| 160 | - # Example: [('topic_A', 0.9), ('topic_B', None), ...] | ||
| 161 | # ------------------------------------------------------------------------ | 162 | # ------------------------------------------------------------------------ |
| 162 | def get_knowledge_state(self): | 163 | def get_knowledge_state(self): |
| 163 | return [{ | 164 | return [{ |
| @@ -166,15 +167,6 @@ class StudentKnowledge(object): | @@ -166,15 +167,6 @@ class StudentKnowledge(object): | ||
| 166 | 'level': self.state[ref]['level'] if ref in self.state else None | 167 | 'level': self.state[ref]['level'] if ref in self.state else None |
| 167 | } for ref in self.topic_sequence ] | 168 | } for ref in self.topic_sequence ] |
| 168 | 169 | ||
| 169 | - # ts = [] | ||
| 170 | - # for ref in self.topic_sequence: | ||
| 171 | - # ts.append({ | ||
| 172 | - # 'ref': ref, | ||
| 173 | - # 'name': self.deps.nodes[ref]['name'], | ||
| 174 | - # 'level': self.state[ref]['level'] if ref in self.state else None | ||
| 175 | - # }) | ||
| 176 | - # return ts | ||
| 177 | - | ||
| 178 | # ------------------------------------------------------------------------ | 170 | # ------------------------------------------------------------------------ |
| 179 | def get_topic_progress(self): | 171 | def get_topic_progress(self): |
| 180 | return len(self.finished_questions) / (1 + len(self.finished_questions) + len(self.questions)) | 172 | return len(self.finished_questions) / (1 + len(self.finished_questions) + len(self.questions)) |
learnapp.py
| @@ -15,7 +15,7 @@ import yaml | @@ -15,7 +15,7 @@ import yaml | ||
| 15 | # this project | 15 | # this project |
| 16 | from models import Student, Answer, Topic, StudentTopic | 16 | from models import Student, Answer, Topic, StudentTopic |
| 17 | from knowledge import StudentKnowledge | 17 | from knowledge import StudentKnowledge |
| 18 | -from questionfactory import QFactory | 18 | +from factory import QFactory |
| 19 | from tools import load_yaml | 19 | from tools import load_yaml |
| 20 | 20 | ||
| 21 | # setup logger for this module | 21 | # setup logger for this module |
questionfactory.py
| @@ -1,93 +0,0 @@ | @@ -1,93 +0,0 @@ | ||
| 1 | -# QFactory is a class that can generate question instances, e.g. by shuffling | ||
| 2 | -# options, running a script to generate the question, etc. | ||
| 3 | -# | ||
| 4 | -# To generate an instance of a question we use the method generate() where | ||
| 5 | -# the argument is the reference of the question we wish to produce. | ||
| 6 | -# | ||
| 7 | -# Example: | ||
| 8 | -# | ||
| 9 | -# # read question from file | ||
| 10 | -# qdict = tools.load_yaml(filename) | ||
| 11 | -# qfactory = QFactory(qdict) | ||
| 12 | -# question = qfactory.generate() | ||
| 13 | -# | ||
| 14 | -# # experiment answering one question and correct it | ||
| 15 | -# question.updateAnswer('42') # insert answer | ||
| 16 | -# grade = question.correct() # correct answer | ||
| 17 | - | ||
| 18 | -# An instance of an actual question is an object that inherits from Question() | ||
| 19 | -# | ||
| 20 | -# Question - base class inherited by other classes | ||
| 21 | -# QuestionInformation - not a question, just a box with content | ||
| 22 | -# QuestionRadio - single choice from a list of options | ||
| 23 | -# QuestionCheckbox - multiple choice, equivalent to multiple true/false | ||
| 24 | -# QuestionText - line of text compared to a list of acceptable answers | ||
| 25 | -# QuestionTextRegex - line of text matched against a regular expression | ||
| 26 | -# QuestionTextArea - corrected by an external program | ||
| 27 | -# QuestionNumericInterval - line of text parsed as a float | ||
| 28 | - | ||
| 29 | -# base | ||
| 30 | -from os import path | ||
| 31 | -import logging | ||
| 32 | - | ||
| 33 | -# project | ||
| 34 | -from tools import run_script | ||
| 35 | -from questions import QuestionInformation, QuestionRadio, QuestionCheckbox, QuestionText, QuestionTextRegex, QuestionTextArea, QuestionNumericInterval | ||
| 36 | - | ||
| 37 | -# setup logger for this module | ||
| 38 | -logger = logging.getLogger(__name__) | ||
| 39 | - | ||
| 40 | - | ||
| 41 | - | ||
| 42 | -# =========================================================================== | ||
| 43 | -# Question Factory | ||
| 44 | -# =========================================================================== | ||
| 45 | -class QFactory(object): | ||
| 46 | - # Depending on the type of question, a different question class will be | ||
| 47 | - # instantiated. All these classes derive from the base class `Question`. | ||
| 48 | - _types = { | ||
| 49 | - 'radio' : QuestionRadio, | ||
| 50 | - 'checkbox' : QuestionCheckbox, | ||
| 51 | - 'text' : QuestionText, | ||
| 52 | - 'text-regex': QuestionTextRegex, | ||
| 53 | - 'numeric-interval': QuestionNumericInterval, | ||
| 54 | - 'textarea' : QuestionTextArea, | ||
| 55 | - # -- informative panels -- | ||
| 56 | - 'information': QuestionInformation, 'info': QuestionInformation, | ||
| 57 | - 'warning' : QuestionInformation, 'warn': QuestionInformation, | ||
| 58 | - 'alert' : QuestionInformation, | ||
| 59 | - 'success' : QuestionInformation, | ||
| 60 | - } | ||
| 61 | - | ||
| 62 | - def __init__(self, question_dict): | ||
| 63 | - self.question = question_dict | ||
| 64 | - | ||
| 65 | - # ----------------------------------------------------------------------- | ||
| 66 | - # Given a ref returns an instance of a descendent of Question(), | ||
| 67 | - # i.e. a question object (radio, checkbox, ...). | ||
| 68 | - # ----------------------------------------------------------------------- | ||
| 69 | - def generate(self): | ||
| 70 | - logger.debug(f'Generating "{self.question["ref"]}"') | ||
| 71 | - # Shallow copy so that script generated questions will not replace | ||
| 72 | - # the original generators | ||
| 73 | - q = self.question.copy() | ||
| 74 | - | ||
| 75 | - # If question is of generator type, an external program will be run | ||
| 76 | - # which will print a valid question in yaml format to stdout. This | ||
| 77 | - # output is then yaml parsed into a dictionary `q`. | ||
| 78 | - if q['type'] == 'generator': | ||
| 79 | - logger.debug(f' \_ Running script "{q["script"]}"...') | ||
| 80 | - q.setdefault('arg', '') # optional arguments will be sent to stdin | ||
| 81 | - script = path.join(q['path'], q['script']) | ||
| 82 | - out = run_script(script=script, stdin=q['arg']) | ||
| 83 | - q.update(out) | ||
| 84 | - | ||
| 85 | - # Finally we create an instance of Question() | ||
| 86 | - try: | ||
| 87 | - qinstance = self._types[q['type']](q) # instance with correct class | ||
| 88 | - except KeyError as e: | ||
| 89 | - logger.error(f'Failed to generate question "{q["ref"]}"') | ||
| 90 | - raise e | ||
| 91 | - else: | ||
| 92 | - return qinstance | ||
| 93 | - |
serve.py
| @@ -135,7 +135,6 @@ class TopicHandler(BaseHandler): | @@ -135,7 +135,6 @@ class TopicHandler(BaseHandler): | ||
| 135 | self.render('topic.html', | 135 | self.render('topic.html', |
| 136 | uid=uid, | 136 | uid=uid, |
| 137 | name=self.learn.get_student_name(uid), | 137 | name=self.learn.get_student_name(uid), |
| 138 | - # title=self.learn.get_title() | ||
| 139 | ) | 138 | ) |
| 140 | 139 | ||
| 141 | 140 | ||
| @@ -175,7 +174,7 @@ class QuestionHandler(BaseHandler): | @@ -175,7 +174,7 @@ class QuestionHandler(BaseHandler): | ||
| 175 | } | 174 | } |
| 176 | 175 | ||
| 177 | def new_question(self, user): | 176 | def new_question(self, user): |
| 178 | - | 177 | + logger.debug(f'new_question({user})') |
| 179 | # state = self.learn.get_student_state(user) # [{ref, name, level},...] | 178 | # state = self.learn.get_student_state(user) # [{ref, name, level},...] |
| 180 | # current_topic = self.learn.get_student_topic(user) # str | 179 | # current_topic = self.learn.get_student_topic(user) # str |
| 181 | 180 | ||
| @@ -192,6 +191,7 @@ class QuestionHandler(BaseHandler): | @@ -192,6 +191,7 @@ class QuestionHandler(BaseHandler): | ||
| 192 | } | 191 | } |
| 193 | 192 | ||
| 194 | def wrong_answer(self, user): | 193 | def wrong_answer(self, user): |
| 194 | + logger.debug(f'wrong_answer({user})') | ||
| 195 | progress = self.learn.get_student_progress(user) # in the current topic | 195 | progress = self.learn.get_student_progress(user) # in the current topic |
| 196 | return { | 196 | return { |
| 197 | 'method': 'shake', | 197 | 'method': 'shake', |
| @@ -201,6 +201,8 @@ class QuestionHandler(BaseHandler): | @@ -201,6 +201,8 @@ class QuestionHandler(BaseHandler): | ||
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | def finished_topic(self, user): | 203 | def finished_topic(self, user): |
| 204 | + logger.debug(f'finished_topic({user})') | ||
| 205 | + | ||
| 204 | # state = self.learn.get_student_state(user) # all topics | 206 | # state = self.learn.get_student_state(user) # all topics |
| 205 | # current_topic = self.learn.get_student_topic(uid) | 207 | # current_topic = self.learn.get_student_topic(uid) |
| 206 | 208 | ||
| @@ -214,7 +216,7 @@ class QuestionHandler(BaseHandler): | @@ -214,7 +216,7 @@ class QuestionHandler(BaseHandler): | ||
| 214 | 216 | ||
| 215 | @tornado.web.authenticated | 217 | @tornado.web.authenticated |
| 216 | def get(self): | 218 | def get(self): |
| 217 | - logging.debug('QuestionHandler.get') | 219 | + logging.debug('QuestionHandler.get()') |
| 218 | user = self.current_user | 220 | user = self.current_user |
| 219 | 221 | ||
| 220 | question = self.learn.get_student_question(user) | 222 | question = self.learn.get_student_question(user) |
| @@ -232,9 +234,10 @@ class QuestionHandler(BaseHandler): | @@ -232,9 +234,10 @@ class QuestionHandler(BaseHandler): | ||
| 232 | # handles answer posted | 234 | # handles answer posted |
| 233 | @tornado.web.authenticated | 235 | @tornado.web.authenticated |
| 234 | def post(self): | 236 | def post(self): |
| 235 | - logging.debug('QuestionHandler.post') | 237 | + logging.debug('QuestionHandler.post()') |
| 236 | user = self.current_user | 238 | user = self.current_user |
| 237 | 239 | ||
| 240 | + print(self.get_body_arguments()) | ||
| 238 | # if self.learn.get_student_question(user) is None: | 241 | # if self.learn.get_student_question(user) is None: |
| 239 | 242 | ||
| 240 | answer = self.get_body_argument('answer') | 243 | answer = self.get_body_argument('answer') |
templates/question-text.html
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | {% block answer %} | 3 | {% block answer %} |
| 4 | <fieldset data-role="controlgroup"> | 4 | <fieldset data-role="controlgroup"> |
| 5 | {% if question['answer'] %} | 5 | {% if question['answer'] %} |
| 6 | - <input type="text" class="form-control" id="answer" name="answer" value="{{ question['answer'][0] }}" autofocus> | 6 | + <input type="text" class="form-control" id="answer" name="answer" value="{{ question['answer'] }}" autofocus> |
| 7 | {% else %} | 7 | {% else %} |
| 8 | <input type="text" class="form-control" id="answer" name="answer" value="" autofocus> | 8 | <input type="text" class="form-control" id="answer" name="answer" value="" autofocus> |
| 9 | {% end %} | 9 | {% end %} |
templates/topic.html
| @@ -82,13 +82,15 @@ | @@ -82,13 +82,15 @@ | ||
| 82 | 82 | ||
| 83 | <div id="notifications"></div> | 83 | <div id="notifications"></div> |
| 84 | 84 | ||
| 85 | - <form action="/question" method="post" id="question_form" autocomplete="off"> | ||
| 86 | - {% module xsrf_form_html() %} | 85 | + <div id="content"> |
| 86 | + <form action="/question" method="post" id="question_form" autocomplete="off"> | ||
| 87 | + {% module xsrf_form_html() %} | ||
| 87 | 88 | ||
| 88 | - <div id="question_div"></div> | 89 | + <div id="question_div"></div> |
| 89 | 90 | ||
| 90 | - </form> | ||
| 91 | - <button class="btn btn-primary" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button> | 91 | + </form> |
| 92 | + <button class="btn btn-primary" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button> | ||
| 93 | + </div> | ||
| 92 | </div> | 94 | </div> |
| 93 | 95 | ||
| 94 | <!-- ===================================================================== --> | 96 | <!-- ===================================================================== --> |
| @@ -153,8 +155,8 @@ | @@ -153,8 +155,8 @@ | ||
| 153 | 155 | ||
| 154 | case "finished_topic": | 156 | case "finished_topic": |
| 155 | $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); | 157 | $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); |
| 156 | - $("#container").html('<img src="/static/trophy.png" alt="trophy" class="img-fluid mx-auto my-5 d-block" width="25%">'); | ||
| 157 | - $("#container").animateCSS('tada'); | 158 | + $("#content").html('<img src="/static/trophy.png" alt="trophy" class="img-fluid mx-auto my-5 d-block" width="25%">'); |
| 159 | + $("#content").animateCSS('tada'); | ||
| 158 | setTimeout(function(){ window.location.replace('/'); }, 2000); | 160 | setTimeout(function(){ window.location.replace('/'); }, 2000); |
| 159 | break; | 161 | break; |
| 160 | } | 162 | } |