Commit 9aeb5485618afee8d771f689cf68305cfc76af77

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

- modified how post questions behave: wait feedback then move to next question.

not yet functional!
1 1
2 # BUGS 2 # BUGS
3 3
  4 +- errar no ultimo topico nao mostra solucao?
4 - nos topicos learn.yaml, qd falha acrescenta no fim. nao faz sentido. 5 - nos topicos learn.yaml, qd falha acrescenta no fim. nao faz sentido.
5 - 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... 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...
6 7
demo/solar_system/questions.yaml
@@ -19,32 +19,32 @@ @@ -19,32 +19,32 @@
19 shuffle: False 19 shuffle: False
20 discount: True 20 discount: True
21 21
22 -# # ---------------------------------------------------------------------------  
23 -# -  
24 -# ref: home-planet  
25 -# type: text  
26 -# title: Sistema solar  
27 -# text: O nosso planeta chama-se planeta...  
28 -# correct: ['Terra', 'terra']  
29 -# # opcional  
30 -# answer: Não é Marte... 22 +# ---------------------------------------------------------------------------
  23 +-
  24 + ref: home-planet
  25 + type: text
  26 + title: Sistema solar
  27 + text: O nosso planeta chama-se planeta...
  28 + correct: ['Terra', 'terra']
  29 + # opcional
  30 + answer: Não é Marte...
31 31
32 -# # ---------------------------------------------------------------------------  
33 -# -  
34 -# ref: saturn  
35 -# type: text-regex  
36 -# title: Sistema solar  
37 -# text: O planeta do sistema solar conhecido por ter aneis é o planeta...  
38 -# correct: !regex '[Ss]aturno' 32 +# ---------------------------------------------------------------------------
  33 +-
  34 + ref: saturn
  35 + type: text-regex
  36 + title: Sistema solar
  37 + text: O planeta do sistema solar conhecido por ter aneis é o planeta...
  38 + correct: !regex '[Ss]aturno'
39 39
40 # --------------------------------------------------------------------------- 40 # ---------------------------------------------------------------------------
41 -# - ref: first_3_planets  
42 -# type: textarea  
43 -# title: Sistema solar  
44 -# text: Escreva o nome dos três planetas mais próximos do Sol. (Exemplo `A, B e C`)  
45 -# correct: correct-first_3_planets.py  
46 -# # correct: correct-timeout.py  
47 -# # opcional  
48 -# answer: Vulcano, Krypton, Plutão  
49 -# lines: 3  
50 -# timeout: 50 41 +- ref: first_3_planets
  42 + type: textarea
  43 + title: Sistema solar
  44 + text: Escreva o nome dos três planetas mais próximos do Sol. (Exemplo `A, B e C`)
  45 + correct: correct-first_3_planets.py
  46 + # correct: correct-timeout.py
  47 + # opcional
  48 + answer: Vulcano, Krypton, Plutão
  49 + lines: 3
  50 + timeout: 50
http-redirect.py
@@ -16,7 +16,7 @@ from tornado import ioloop, web, httpserver @@ -16,7 +16,7 @@ from tornado import ioloop, web, httpserver
16 class WebRedirectApplication(web.Application): 16 class WebRedirectApplication(web.Application):
17 def __init__(self, target='https://localhost'): 17 def __init__(self, target='https://localhost'):
18 handlers = [ 18 handlers = [
19 - (r'/*', RootHandler), # redirect to https 19 + (r'/', RootHandler), # redirect to https
20 ] 20 ]
21 super().__init__(handlers) 21 super().__init__(handlers)
22 self.target = target 22 self.target = target
@@ -28,7 +28,7 @@ class RootHandler(web.RequestHandler): @@ -28,7 +28,7 @@ class RootHandler(web.RequestHandler):
28 SUPPORTED_METHODS = ['GET'] 28 SUPPORTED_METHODS = ['GET']
29 29
30 def get(self): 30 def get(self):
31 - print('Redirecting...') 31 + # print('Redirecting...')
32 self.redirect(self.application.target) 32 self.redirect(self.application.target)
33 33
34 34
@@ -153,7 +153,7 @@ class StudentKnowledge(object): @@ -153,7 +153,7 @@ class StudentKnowledge(object):
153 if self.next_question() is None: 153 if self.next_question() is None:
154 action = 'finished_topic' 154 action = 'finished_topic'
155 else: 155 else:
156 - action = 'new_question' # FIXME show comments 156 + action = 'wrong' # FIXME show comments
157 157
158 else: 158 else:
159 action = 'try_again' 159 action = 'try_again'
@@ -191,7 +191,7 @@ class LearnApp(object): @@ -191,7 +191,7 @@ class LearnApp(object):
191 logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"') 191 logger.warning(f'User "{uid}" tried to open nonexistent topic: "{topic}"')
192 raise e 192 raise e
193 else: 193 else:
194 - logger.info(f'User "{uid}" started "{topic}"') 194 + logger.info(f'User "{uid}" started topic "{topic}"')
195 195
196 # ------------------------------------------------------------------------ 196 # ------------------------------------------------------------------------
197 # Fill db table 'Topic' with topics from the graph if not already there. 197 # Fill db table 'Topic' with topics from the graph if not already there.
@@ -363,11 +363,6 @@ class QuestionTextArea(Question): @@ -363,11 +363,6 @@ class QuestionTextArea(Question):
363 363
364 # =========================================================================== 364 # ===========================================================================
365 class QuestionInformation(Question): 365 class QuestionInformation(Question):
366 - '''An instance of QuestionInformation will always have the keys:  
367 - type (str)  
368 - text (str)  
369 - points (0.0)  
370 - '''  
371 #------------------------------------------------------------------------ 366 #------------------------------------------------------------------------
372 def __init__(self, q): 367 def __init__(self, q):
373 super().__init__(q) 368 super().__init__(q)
@@ -44,7 +44,7 @@ class WebApplication(tornado.web.Application): @@ -44,7 +44,7 @@ class WebApplication(tornado.web.Application):
44 (r'/logout', LogoutHandler), 44 (r'/logout', LogoutHandler),
45 (r'/change_password', ChangePasswordHandler), 45 (r'/change_password', ChangePasswordHandler),
46 (r'/question', QuestionHandler), # renders each question 46 (r'/question', QuestionHandler), # renders each question
47 - (r'/topic/(.+)', TopicHandler), # page for exercising a topic 47 + (r'/topic/(.+)', TopicHandler), # start a topic
48 (r'/file/(.+)', FileHandler), # serve files, images, etc 48 (r'/file/(.+)', FileHandler), # serve files, images, etc
49 (r'/', RootHandler), # show list of topics 49 (r'/', RootHandler), # show list of topics
50 ] 50 ]
@@ -200,6 +200,8 @@ class FileHandler(BaseHandler): @@ -200,6 +200,8 @@ class FileHandler(BaseHandler):
200 # respond to AJAX to get a JSON question 200 # respond to AJAX to get a JSON question
201 # ---------------------------------------------------------------------------- 201 # ----------------------------------------------------------------------------
202 class QuestionHandler(BaseHandler): 202 class QuestionHandler(BaseHandler):
  203 + SUPPORTED_METHODS = ['GET', 'POST']
  204 +
203 templates = { 205 templates = {
204 'checkbox': 'question-checkbox.html', 206 'checkbox': 'question-checkbox.html',
205 'radio': 'question-radio.html', 207 'radio': 'question-radio.html',
@@ -218,20 +220,20 @@ class QuestionHandler(BaseHandler): @@ -218,20 +220,20 @@ class QuestionHandler(BaseHandler):
218 def get(self): 220 def get(self):
219 logging.debug('QuestionHandler.get()') 221 logging.debug('QuestionHandler.get()')
220 user = self.current_user 222 user = self.current_user
  223 + q = self.learn.get_current_question(user)
  224 + question_html = self.render_string(self.templates[q['type']],
  225 + question=q, md=md_to_html)
221 226
222 - question = self.learn.get_current_question(user)  
223 -  
224 - question_html = self.render_string(self.templates[question['type']],  
225 - question=question, md=md_to_html)  
226 -  
227 - self.write({ 227 + response = {
228 'method': 'new_question', 228 'method': 'new_question',
229 'params': { 229 'params': {
  230 + 'type': q['type'],
230 'question': tornado.escape.to_unicode(question_html), 231 'question': tornado.escape.to_unicode(question_html),
231 'progress': self.learn.get_student_progress(user), 232 'progress': self.learn.get_student_progress(user),
232 - 'tries': question['tries'], 233 + 'tries': q['tries'],
233 }, 234 },
234 - }) 235 + }
  236 + self.write(response)
235 237
236 # --- post answer, returns what to do next: shake, new_question, finished 238 # --- post answer, returns what to do next: shake, new_question, finished
237 @tornado.web.authenticated 239 @tornado.web.authenticated
@@ -253,57 +255,72 @@ class QuestionHandler(BaseHandler): @@ -253,57 +255,72 @@ class QuestionHandler(BaseHandler):
253 # and get corrected question 255 # and get corrected question
254 q, action = await self.learn.check_answer(user, answer) 256 q, action = await self.learn.check_answer(user, answer)
255 257
256 - # get next question (same, new or None)  
257 - question = self.learn.get_current_question(user) 258 + print(action)
258 259
259 if action == 'try_again': 260 if action == 'try_again':
260 comments_html = self.render_string('comments.html', 261 comments_html = self.render_string('comments.html',
261 - comments=question['comments'], md=md_to_html)  
262 - self.write({  
263 - 'method': 'try_again', # FIXME js 262 + comments=q['comments'], md=md_to_html)
  263 +
  264 + response = {
  265 + 'method': 'try_again',
264 'params': { 266 'params': {
265 'progress': self.learn.get_student_progress(user), 267 'progress': self.learn.get_student_progress(user),
266 'comments': tornado.escape.to_unicode(comments_html), # FIXME 268 'comments': tornado.escape.to_unicode(comments_html), # FIXME
267 - 'tries': question['tries'], 269 + 'tries': q['tries'],
268 } 270 }
269 - })  
270 -  
271 - # if action == 'wrong':  
272 - # comments_html = self.render_string('comments.html',  
273 - # comments=question['comments'], md=md_to_html)  
274 - # template = self.templates[question['type']]  
275 - # question_html = self.render_string(template, question=question, md=md_to_html)  
276 - # self.write({  
277 - # 'method': 'wrong', # FIXME js  
278 - # 'params': {  
279 - # 'question': tornado.escape.to_unicode(question_html),  
280 - # 'progress': self.learn.get_student_progress(user),  
281 - # 'comments': tornado.escape.to_unicode(comments_html), # FIXME  
282 - # 'tries': question['tries'],  
283 - # }  
284 - # }) 271 + }
  272 + print(response)
  273 + self.write(response)
285 274
286 - elif action == 'new_question': # get next question in the topic  
287 - template = self.templates[question['type']]  
288 - question_html = self.render_string(template,  
289 - question=question, md=md_to_html)  
290 - self.write({  
291 - 'method': 'new_question', 275 + elif action == 'wrong': # no more tries
  276 + comments_html = self.render_string('comments.html',
  277 + comments=q['comments'], md=md_to_html)
  278 + solution_html = self.render_string('solution.html',
  279 + solution=q['solution'], md=md_to_html)
  280 +
  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
292 'params': { 285 'params': {
293 - 'question': tornado.escape.to_unicode(question_html), 286 + # 'question': tornado.escape.to_unicode(question_html),
294 'progress': self.learn.get_student_progress(user), 287 'progress': self.learn.get_student_progress(user),
295 - 'tries': question['tries'], 288 + 'comments': tornado.escape.to_unicode(comments_html), # FIXME
  289 + 'solution': tornado.escape.to_unicode(solution_html), # FIXME
  290 + 'tries': q['tries'],
296 } 291 }
297 - }) 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)
298 314
299 elif action == 'finished_topic': # right answer, finished topic 315 elif action == 'finished_topic': # right answer, finished topic
300 finished_topic_html = self.render_string('finished_topic.html') 316 finished_topic_html = self.render_string('finished_topic.html')
301 - self.write({ 317 + response = {
302 'method': 'finished_topic', 318 'method': 'finished_topic',
303 'params': { 319 'params': {
304 'question': tornado.escape.to_unicode(finished_topic_html) 320 'question': tornado.escape.to_unicode(finished_topic_html)
305 } 321 }
306 - }) 322 + }
  323 + self.write(response)
307 324
308 else: 325 else:
309 logger.error(f'Unknown action {action}') 326 logger.error(f'Unknown action {action}')
static/js/topic.js
@@ -8,56 +8,76 @@ $.fn.extend({ @@ -8,56 +8,76 @@ $.fn.extend({
8 }); 8 });
9 9
10 10
11 -function new_question(question, tries, progress) { 11 +function new_question(type, question, tries, progress) {
  12 + console.log("new_question " + type);
  13 +
12 $("#question_div").html(question); 14 $("#question_div").html(question);
13 $("#comments").html(""); 15 $("#comments").html("");
  16 + $("#solution").html("");
  17 + if (type == "info") {
  18 + $("#submit").html("Continuar");
  19 + }
  20 + else {
  21 + $("#submit").html("Responder");
  22 + }
  23 + $("#submit").off();
  24 + $("#submit").click(postAnswer);
  25 +
14 $("#tries").html(tries); 26 $("#tries").html(tries);
15 $('#topic_progress').css('width', (100*progress)+'%').attr('aria-valuenow', 100*progress); 27 $('#topic_progress').css('width', (100*progress)+'%').attr('aria-valuenow', 100*progress);
16 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); 28 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]);
  29 +
17 $('#question_div').animateCSS('bounceInDown'); 30 $('#question_div').animateCSS('bounceInDown');
18 31
19 - // enable shift+enter to submit and tab to spaces conversion  
20 - $("input:text, input:radio, input:checkbox").keydown(function (e) {  
21 - if (e.keyCode == 13) {  
22 - e.preventDefault();  
23 - if (e.shiftKey) postQuestion();  
24 - return false;  
25 - }}); 32 + // enable shift+enter to submit
  33 + // $("input:text, input:radio, input:checkbox").keydown(function (e) {
  34 + // if (e.keyCode == 13) {
  35 + // e.preventDefault();
  36 + // if (e.shiftKey) postQuestion();
  37 + // return false;
  38 + // }});
26 } 39 }
27 40
  41 +
  42 +
28 // updates question according to the response given by the server 43 // updates question according to the response given by the server
29 function updateQuestion(response){ 44 function updateQuestion(response){
  45 + console.log('updateQuestion '+response["method"]);
  46 +
  47 + var method = response["method"];
  48 + var params = response["params"];
  49 +
30 50
31 - switch (response["method"]) { 51 + switch (method) {
32 case "new_question": 52 case "new_question":
33 - params = response["params"];  
34 - new_question(params["question"], params["tries"], params["progress"]); 53 + console.log(params["type"]);
  54 + new_question(params["type"], params["question"], params["tries"], params["progress"]);
35 break; 55 break;
36 56
37 case "try_again": 57 case "try_again":
38 - $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]);  
39 $('#question_div').animateCSS('shake'); 58 $('#question_div').animateCSS('shake');
40 - $('#comments').html(response['params']['comments']);  
41 - $("#tries").html(response["params"]["tries"]); 59 + $('#topic_progress').css('width', (100*params["progress"])+'%').attr('aria-valuenow', 100*params["progress"]);
  60 + $("#tries").html(params["tries"]);
  61 + $('#comments').html(params['comments']);
42 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]); 62 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]);
43 break; 63 break;
44 64
45 - // case "wrong":  
46 - // $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]);  
47 - // $('#question_div').animateCSS('shake');  
48 - // $('#comments').html(response['params']['comments']);  
49 - // $("#tries").html(response["params"]["tries"]);  
50 - // MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]);  
51 -  
52 - // // setTimeout(function(){  
53 - // new_question(response["params"]["question"], response["params"]["tries"], response["params"]["progress"]);  
54 - // // }, 5000);  
55 - // break;  
56 -  
57 - 65 + case "wrong":
  66 + $('#question_div').animateCSS('shake');
  67 + $('#topic_progress').css('width', (100*params["progress"])+'%').attr('aria-valuenow', 100*params["progress"]);
  68 + $("#tries").html(params["tries"]);
  69 + $('#comments').html(params['comments']);
  70 + MathJax.Hub.Queue(["Typeset", MathJax.Hub, "#comments"]);
  71 + $('#solution').html(params['solution']);
  72 + MathJax.Hub.Queue(["Typeset", MathJax.Hub, "#solution"]);
  73 + $("fieldset").attr("disabled", "disabled");
  74 + $("#submit").html("Continuar");
  75 + $("#submit").off();
  76 + $("#submit").click(getQuestion);
  77 + break;
58 78
59 case "finished_topic": 79 case "finished_topic":
60 - $('#submit').css("visibility", "hidden"); 80 + $('#submit').hide(); //css("visibility", "hidden");
61 $("#content").html(response["params"]["question"]); 81 $("#content").html(response["params"]["question"]);
62 $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); 82 $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100);
63 $("#content").animateCSS('tada'); 83 $("#content").animateCSS('tada');
@@ -68,6 +88,8 @@ function updateQuestion(response){ @@ -68,6 +88,8 @@ function updateQuestion(response){
68 88
69 // Get current question 89 // Get current question
70 function getQuestion() { 90 function getQuestion() {
  91 + console.log("getQuestion");
  92 +
71 $.ajax({ 93 $.ajax({
72 type: "GET", 94 type: "GET",
73 url: "/question", 95 url: "/question",
@@ -80,7 +102,9 @@ function getQuestion() { @@ -80,7 +102,9 @@ function getQuestion() {
80 // Send answer and receive a response. 102 // Send answer and receive a response.
81 // The response can be a new_question or a shake if the answer is wrong, which 103 // The response can be a new_question or a shake if the answer is wrong, which
82 // is then passed to updateQuestion() 104 // is then passed to updateQuestion()
83 -function postQuestion() { 105 +function postAnswer() {
  106 + console.log("postAnswer");
  107 +
84 if (typeof editor === 'object') 108 if (typeof editor === 'object')
85 editor.save(); 109 editor.save();
86 110
@@ -94,7 +118,8 @@ function postQuestion() { @@ -94,7 +118,8 @@ function postQuestion() {
94 }); 118 });
95 } 119 }
96 120
  121 +
97 $(document).ready(function() { 122 $(document).ready(function() {
98 getQuestion(); 123 getQuestion();
99 - $("#submit").click(postQuestion); 124 + $("#submit").click(postAnswer);
100 }); 125 });
templates/comments.html
1 {% autoescape %} 1 {% autoescape %}
2 2
3 {% if comments %} 3 {% if comments %}
4 -<div class="card border-danger mb-3">  
5 - <div class="card-body text-danger">  
6 - <p class="card-text">{{ md(comments) }}</p>  
7 - </div> 4 +<div class="alert alert-warning">
  5 + {{ md(comments) }}
8 </div> 6 </div>
9 {% end %} 7 {% end %}
templates/solution.html 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +{% autoescape %}
  2 +
  3 +{% if solution %}
  4 +<div class="alert alert-danger">
  5 + <h4 class="alert-heading">Solução</h4>
  6 + {{ md(solution) }}
  7 +</div>
  8 +{% end %}
templates/topic.html
@@ -79,13 +79,14 @@ @@ -79,13 +79,14 @@
79 </form> 79 </form>
80 80
81 <div id="comments"></div> 81 <div id="comments"></div>
  82 + <div id="solution"></div>
82 83
83 </div> 84 </div>
84 </div> 85 </div>
85 86
86 <footer class="footer"> 87 <footer class="footer">
87 <div class="container"> 88 <div class="container">
88 - <button class="btn btn-primary btn-lg btn-block my-3" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button> 89 + <button class="btn btn-primary btn-lg btn-block my-3" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Responder</button>
89 </div> 90 </div>
90 </footer> 91 </footer>
91 92