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 | } |