Commit 775dd8eb5a7e4ef72e414a250667a8a28c0f877a

Authored by Miguel Barão
1 parent 9aeb5485
Exists in master and in 1 other branch dev

- reorganized how the state machine of question answering to be in two steps (post and get).

BUGS.md
1 1  
2 2 # BUGS
3 3  
4   -- errar no ultimo topico nao mostra solucao?
  4 +- falha no file handler de vez em quando, nao sei porquê...
5 5 - nos topicos learn.yaml, qd falha acrescenta no fim. nao faz sentido.
6 6 - ocorreu uma vez o sqlalchemy dar mesg erro a indicar que as threads sao diferents quando se faz o get da primeira pergunta do topico. Muitas vezes nao mostar erro, mas a pagina da erro ou fica em branco...
7 7  
... ... @@ -32,6 +32,7 @@
32 32  
33 33 # FIXED
34 34  
  35 +- errar no ultimo topico nao mostra solucao?
35 36 - quando a pergunta devolve comments, este é apresentado, mas fica persistente nas tentativas seguintes. devia ser limpo apos a segunda submissao.
36 37 - na definicao dos topicos, indicar:
37 38 "file: questions.yaml" (default questions.yaml)
... ...
demo/math/questions.yaml
... ... @@ -15,48 +15,48 @@
15 15 correct: [1,1,0,0,0]
16 16 choose: 3
17 17  
18   -# - ref: numbers
19   -# type: checkbox
20   -# title: Números pares e primos
21   -# text: Indique as afirmações verdadeiras.
22   -# options:
23   -# - ['3 é primo', '4 é primo']
24   -# - ['2 é par', '3 é par']
25   -# - ['1 é ímpar', '2 é ímpar']
26   -# correct: [1,1,1]
  18 +- ref: numbers
  19 + type: checkbox
  20 + title: Números pares e primos
  21 + text: Indique as afirmações verdadeiras.
  22 + options:
  23 + - ['3 é primo', '4 é primo']
  24 + - ['2 é par', '3 é par']
  25 + - ['1 é ímpar', '2 é ímpar']
  26 + correct: [1,1,1]
27 27  
28   -# -
29   -# ref: prime_numbers
30   -# type: radio
31   -# title: Números primos
32   -# text: Qual dos seguintes números é primo?
33   -# options:
34   -# - 13
35   -# - 12
36   -# - 14
37   -# - 1, a **unidade**
  28 +-
  29 + ref: prime_numbers
  30 + type: radio
  31 + title: Números primos
  32 + text: Qual dos seguintes números é primo?
  33 + options:
  34 + - 13
  35 + - 12
  36 + - 14
  37 + - 1, a **unidade**
38 38  
39 39  
40   -# # ---------------------------------------------------------------------------
41   -# -
42   -# ref: math-expressions
43   -# type: checkbox
44   -# title: Expressões matemáticas
45   -# text: Quais das seguintes expressões são verdadeiras?
46   -# options:
47   -# - $1 > 0$
48   -# - $\sqrt{3} > \sqrt{2}$
49   -# - $e^{i\pi} + 1 = 0$
50   -# - $\frac{\partial f(x,y)}{\partial z} = 1$
51   -# - $-1 > 1$
52   -# # how many points for each checkmark (normalized afterwards):
53   -# correct: [1, 1, 1, -1, -1]
  40 +# ---------------------------------------------------------------------------
  41 +-
  42 + ref: math-expressions
  43 + type: checkbox
  44 + title: Expressões matemáticas
  45 + text: Quais das seguintes expressões são verdadeiras?
  46 + options:
  47 + - $1 > 0$
  48 + - $\sqrt{3} > \sqrt{2}$
  49 + - $e^{i\pi} + 1 = 0$
  50 + - $\frac{\partial f(x,y)}{\partial z} = 1$
  51 + - $-1 > 1$
  52 + # how many points for each checkmark (normalized afterwards):
  53 + correct: [1, 1, 1, -1, -1]
54 54  
55   -# # ---------------------------------------------------------------------------
56   -# -
57   -# ref: overflow
58   -# type: generator
59   -# script: generate-overflow.py
60   -# # opcional
61   -# arg: "11,120"
62   -# # the script should print a question dict in yaml format.
  55 +# ---------------------------------------------------------------------------
  56 +-
  57 + ref: overflow
  58 + type: generator
  59 + script: generate-overflow.py
  60 + # opcional
  61 + arg: "11,120"
  62 + # the script should print a question dict in yaml format.
... ...
knowledge.py
... ... @@ -135,28 +135,22 @@ class StudentKnowledge(object):
135 135  
136 136 if grade > 0.999:
137 137 self.correct_answers += 1
138   - if self.next_question() is None:
139   - action = 'finished_topic'
140   - else:
141   - action = 'new_question'
  138 + self.next_question()
  139 + action = 'right'
142 140  
143 141 else:
144 142 self.wrong_answers += 1
145 143 self.current_question['tries'] -= 1
146 144 logger.debug(f'Wrong answers = {self.wrong_answers}; Tries = {self.current_question["tries"]}')
147 145  
148   - if self.current_question['tries'] <= 0:
  146 + if self.current_question['tries'] > 0:
  147 + action = 'try_again'
  148 + else:
  149 + action = 'wrong'
149 150 if self.current_question['append_wrong']:
150 151 logger.debug("Appending new instance of this question to the end")
151 152 self.questions.append(self.factory[q['ref']].generate())
152   -
153   - if self.next_question() is None:
154   - action = 'finished_topic'
155   - else:
156   - action = 'wrong' # FIXME show comments
157   -
158   - else:
159   - action = 'try_again'
  153 + self.next_question()
160 154  
161 155 # returns corrected question (not new one) which might include comments
162 156 return q, action
... ...
questions.py
... ... @@ -39,6 +39,7 @@ class Question(dict):
39 39 'title': '',
40 40 'answer': None,
41 41 'comments': '',
  42 + 'solution': '',
42 43 'files': {},
43 44 })
44 45  
... ...
serve.py
... ... @@ -170,6 +170,9 @@ class TopicHandler(BaseHandler):
170 170 # Serves files from the /public subdir of the topics.
171 171 # Based on https://bhch.github.io/posts/2017/12/serving-large-files-with-tornado-safely-without-blocking/
172 172 # ----------------------------------------------------------------------------
  173 +
  174 +# FIXME error in many situations... images are not shown...
  175 +
173 176 class FileHandler(BaseHandler):
174 177 SUPPORTED_METHODS = ['GET']
175 178  
... ... @@ -221,18 +224,29 @@ class QuestionHandler(BaseHandler):
221 224 logging.debug('QuestionHandler.get()')
222 225 user = self.current_user
223 226 q = self.learn.get_current_question(user)
224   - question_html = self.render_string(self.templates[q['type']],
225   - question=q, md=md_to_html)
226   -
227   - response = {
228   - 'method': 'new_question',
229   - 'params': {
230   - 'type': q['type'],
231   - 'question': tornado.escape.to_unicode(question_html),
232   - 'progress': self.learn.get_student_progress(user),
233   - 'tries': q['tries'],
234   - },
235   - }
  227 +
  228 + if q is not None:
  229 + question_html = self.render_string(self.templates[q['type']],
  230 + question=q, md=md_to_html)
  231 + response = {
  232 + 'method': 'new_question',
  233 + 'params': {
  234 + 'type': q['type'],
  235 + 'question': tornado.escape.to_unicode(question_html),
  236 + 'progress': self.learn.get_student_progress(user),
  237 + 'tries': q['tries'],
  238 + },
  239 + }
  240 +
  241 + else:
  242 + finished_topic_html = self.render_string('finished_topic.html')
  243 + response = {
  244 + 'method': 'finished_topic',
  245 + 'params': {
  246 + 'question': tornado.escape.to_unicode(finished_topic_html)
  247 + }
  248 + }
  249 +
236 250 self.write(response)
237 251  
238 252 # --- post answer, returns what to do next: shake, new_question, finished
... ... @@ -251,26 +265,30 @@ class QuestionHandler(BaseHandler):
251 265 elif qtype != 'checkbox': # radio, text, textarea, ...
252 266 answer = answer[0]
253 267  
254   - # check answer in another thread (nonblocking)
255   - # and get corrected question
  268 + # check answer (nonblocking) and get corrected question
256 269 q, action = await self.learn.check_answer(user, answer)
257 270  
258   - print(action)
  271 + response = {'method': action, 'params': {}}
  272 +
  273 + if action == 'right': # get next question in the topic
  274 + comments_html = self.render_string('comments-right.html',
  275 + comments=q['comments'], md=md_to_html)
  276 +
  277 + response['params'] = {
  278 + 'progress': self.learn.get_student_progress(user),
  279 + 'comments': tornado.escape.to_unicode(comments_html),
  280 + 'tries': q['tries'],
  281 + }
259 282  
260   - if action == 'try_again':
  283 + elif action == 'try_again':
261 284 comments_html = self.render_string('comments.html',
262 285 comments=q['comments'], md=md_to_html)
263 286  
264   - response = {
265   - 'method': 'try_again',
266   - 'params': {
  287 + response['params'] = {
267 288 'progress': self.learn.get_student_progress(user),
268 289 'comments': tornado.escape.to_unicode(comments_html), # FIXME
269 290 'tries': q['tries'],
270 291 }
271   - }
272   - print(response)
273   - self.write(response)
274 292  
275 293 elif action == 'wrong': # no more tries
276 294 comments_html = self.render_string('comments.html',
... ... @@ -278,52 +296,17 @@ class QuestionHandler(BaseHandler):
278 296 solution_html = self.render_string('solution.html',
279 297 solution=q['solution'], md=md_to_html)
280 298  
281   - # template = self.templates[question['type']]
282   - # question_html = self.render_string(template, question=question, md=md_to_html)
283   - response = {
284   - 'method': 'wrong', # FIXME js
285   - 'params': {
286   - # 'question': tornado.escape.to_unicode(question_html),
287   - 'progress': self.learn.get_student_progress(user),
288   - 'comments': tornado.escape.to_unicode(comments_html), # FIXME
289   - 'solution': tornado.escape.to_unicode(solution_html), # FIXME
290   - 'tries': q['tries'],
291   - }
292   - }
293   - print(response)
294   - self.write(response)
295   -
296   - elif action == 'new_question': # get next question in the topic
297   - self.get()
298   - # question = self.learn.get_current_question(user)
299   -
300   - # template = self.templates[question['type']]
301   - # question_html = self.render_string(template,
302   - # question=question, md=md_to_html)
303   - # response = {
304   - # 'method': 'new_question',
305   - # 'params': {
306   - # 'type': question['type'],
307   - # 'question': tornado.escape.to_unicode(question_html),
308   - # 'progress': self.learn.get_student_progress(user),
309   - # 'tries': question['tries'],
310   - # }
311   - # }
312   - # print(response)
313   - # self.write(response)
314   -
315   - elif action == 'finished_topic': # right answer, finished topic
316   - finished_topic_html = self.render_string('finished_topic.html')
317   - response = {
318   - 'method': 'finished_topic',
319   - 'params': {
320   - 'question': tornado.escape.to_unicode(finished_topic_html)
321   - }
  299 + response['params'] = {
  300 + 'progress': self.learn.get_student_progress(user),
  301 + 'comments': tornado.escape.to_unicode(comments_html),
  302 + 'solution': tornado.escape.to_unicode(solution_html),
  303 + 'tries': q['tries'],
322 304 }
323   - self.write(response)
324 305  
325 306 else:
326   - logger.error(f'Unknown action {action}')
  307 + logger.error(f'Unknown action: {action}')
  308 +
  309 + self.write(response)
327 310  
328 311  
329 312 # ----------------------------------------------------------------------------
... ...
static/js/topic.js
... ... @@ -7,6 +7,38 @@ $.fn.extend({
7 7 }
8 8 });
9 9  
  10 +// Get current question
  11 +function getQuestion() {
  12 + $.ajax({
  13 + type: "GET",
  14 + url: "/question",
  15 + dataType: "json", // expected from server
  16 + success: updateQuestion,
  17 + error: function() {alert("O servidor não responde.");}
  18 + });
  19 +}
  20 +
  21 +
  22 +// updates question according to the response given by the server
  23 +function updateQuestion(response) {
  24 + var method = response["method"];
  25 + var params = response["params"];
  26 +
  27 + switch (method) {
  28 + case "new_question":
  29 + console.log(params["type"]);
  30 + new_question(params["type"], params["question"], params["tries"], params["progress"]);
  31 + break;
  32 + case "finished_topic":
  33 + $('#submit').hide();
  34 + $("#content").html(response["params"]["question"]);
  35 + $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100);
  36 + $("#content").animateCSS('tada');
  37 + setTimeout(function(){ window.location.replace('/'); }, 2000);
  38 + break;
  39 + }
  40 +}
  41 +
10 42  
11 43 function new_question(type, question, tries, progress) {
12 44 console.log("new_question " + type);
... ... @@ -39,19 +71,21 @@ function new_question(type, question, tries, progress) {
39 71 }
40 72  
41 73  
42   -
43   -// updates question according to the response given by the server
44   -function updateQuestion(response){
  74 +function getFeedback(response) {
45 75 console.log('updateQuestion '+response["method"]);
46 76  
47 77 var method = response["method"];
48 78 var params = response["params"];
49 79  
50   -
51 80 switch (method) {
52   - case "new_question":
53   - console.log(params["type"]);
54   - new_question(params["type"], params["question"], params["tries"], params["progress"]);
  81 + case "right":
  82 + console.log(params['comments']);
  83 + $('#comments').html(params['comments']);
  84 +
  85 + // MathJax.Hub.Queue(["Typeset", MathJax.Hub, "#comments"]);
  86 + $("#submit").html("Continuar");
  87 + $("#submit").off();
  88 + $("#submit").click(getQuestion);
55 89 break;
56 90  
57 91 case "try_again":
... ... @@ -75,30 +109,9 @@ function updateQuestion(response){
75 109 $("#submit").off();
76 110 $("#submit").click(getQuestion);
77 111 break;
78   -
79   - case "finished_topic":
80   - $('#submit').hide(); //css("visibility", "hidden");
81   - $("#content").html(response["params"]["question"]);
82   - $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100);
83   - $("#content").animateCSS('tada');
84   - setTimeout(function(){ window.location.replace('/'); }, 2000);
85   - break;
86 112 }
87 113 }
88 114  
89   -// Get current question
90   -function getQuestion() {
91   - console.log("getQuestion");
92   -
93   - $.ajax({
94   - type: "GET",
95   - url: "/question",
96   - dataType: "json", // expected from server
97   - success: updateQuestion,
98   - error: function() {alert("O servidor não responde.");}
99   - });
100   -}
101   -
102 115 // Send answer and receive a response.
103 116 // The response can be a new_question or a shake if the answer is wrong, which
104 117 // is then passed to updateQuestion()
... ... @@ -113,7 +126,7 @@ function postAnswer() {
113 126 url: "/question",
114 127 data: $("#question_form").serialize(), // {'a':10,'b':20},
115 128 dataType: "json", // expected from server
116   - success: updateQuestion,
  129 + success: getFeedback,
117 130 error: function() {alert("O servidor não responde.");}
118 131 });
119 132 }
... ...
templates/comments-right.html 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +{% autoescape %}
  2 +
  3 +<div class="alert alert-success">
  4 + <i class="fas fa-thumbs-up fa-3x"></i> Certo!
  5 + {% if comments %}
  6 + <hr>
  7 + {{ md(comments) }}
  8 + {% end %}
  9 +</div>
... ...