Commit 4f21e22aa2809b58662a64bb70bd114952abbbd4

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

- changed debug logs to show student id

- fixed enter in textarea, tabs still need fix
1 1
2 BUGS: 2 BUGS:
3 3
  4 +- pymips: activar/desactivar instruções
  5 +- tabs em textarea nao funcionam correctamente (insere 1 espaco em vez de 4)
  6 +- reportar comentarios após submeter.
  7 +- logs debug mostrar user
  8 +- logs mostrar fim de topico
4 - textarea, text devem mostrar no html os valores iniciais de ans, se existir 9 - textarea, text devem mostrar no html os valores iniciais de ans, se existir
5 - detect questions in questions.yaml without ref -> error ou generate default. 10 - detect questions in questions.yaml without ref -> error ou generate default.
6 - error if demo.yaml has no topics 11 - error if demo.yaml has no topics
7 -- pymips a funcionar  
8 - reload da página rebenta o estado. 12 - reload da página rebenta o estado.
9 - guardar state cada vez que topico termina 13 - guardar state cada vez que topico termina
10 - indicar o topico actual no sidebar 14 - indicar o topico actual no sidebar
11 - session management. close after inactive time. 15 - session management. close after inactive time.
12 - implementar xsrf. Ver [http://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection]() 16 - implementar xsrf. Ver [http://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection]()
  17 +- titulos das perguntas não suportam markdown
13 18
14 TODO: 19 TODO:
15 20
@@ -21,6 +26,7 @@ TODO: @@ -21,6 +26,7 @@ TODO:
21 26
22 FIXED: 27 FIXED:
23 28
  29 +- pymips a funcionar
24 - logs mostram que está a gerar cada pergunta 2 vezes...?? 30 - logs mostram que está a gerar cada pergunta 2 vezes...??
25 - letsencrypt.org 31 - letsencrypt.org
26 - alterar password. 32 - alterar password.
@@ -69,7 +69,7 @@ class LearnApp(object): @@ -69,7 +69,7 @@ class LearnApp(object):
69 self.online[uid] = { 69 self.online[uid] = {
70 'name': student.name, 70 'name': student.name,
71 'number': student.id, 71 'number': student.id,
72 - 'state': Knowledge(self.depgraph, state), 72 + 'state': Knowledge(self.depgraph, state=state, student=student.id)
73 } 73 }
74 logger.info(f'User "{uid}" logged in') 74 logger.info(f'User "{uid}" logged in')
75 return True 75 return True
@@ -16,7 +16,8 @@ logger = logging.getLogger(__name__) @@ -16,7 +16,8 @@ logger = logging.getLogger(__name__)
16 # ---------------------------------------------------------------------------- 16 # ----------------------------------------------------------------------------
17 # contains the kowledge state of each student. 17 # contains the kowledge state of each student.
18 class Knowledge(object): 18 class Knowledge(object):
19 - def __init__(self, depgraph, state={}): 19 + def __init__(self, depgraph, state={}, student=''):
  20 + self.student = student
20 self.depgraph = depgraph 21 self.depgraph = depgraph
21 self.topic_sequence = nx.topological_sort(self.depgraph) # FIXME 22 self.topic_sequence = nx.topological_sort(self.depgraph) # FIXME
22 23
@@ -28,12 +29,12 @@ class Knowledge(object): @@ -28,12 +29,12 @@ class Knowledge(object):
28 29
29 # ------------------------------------------------------------------------ 30 # ------------------------------------------------------------------------
30 def new_topic(self, topic=None): 31 def new_topic(self, topic=None):
31 - logger.debug(f'-> Knowledge.new_topic({topic})')  
32 if topic is None: 32 if topic is None:
33 # select the first topic that has level < 0.9 33 # select the first topic that has level < 0.9
34 for topic in self.topic_sequence: 34 for topic in self.topic_sequence:
35 if topic not in self.state or self.state[topic]['level'] < 0.9: 35 if topic not in self.state or self.state[topic]['level'] < 0.9:
36 break 36 break
  37 + logger.debug(f'Student {self.student}: new_topic({topic})')
37 38
38 # FIXME if all are > 0.9, will stay in the last one forever... 39 # FIXME if all are > 0.9, will stay in the last one forever...
39 self.current_topic = topic 40 self.current_topic = topic
@@ -44,7 +45,8 @@ class Knowledge(object): @@ -44,7 +45,8 @@ class Knowledge(object):
44 45
45 # ------------------------------------------------------------------------ 46 # ------------------------------------------------------------------------
46 def generate_questions_for_topic(self, topic): 47 def generate_questions_for_topic(self, topic):
47 - logger.debug(f'-> Knowledge.generate_questions_for_topic({topic})') 48 + logger.debug(f'Student {self.student}: generate_questions_for_topic "{topic}"')
  49 +
48 factory_list = self.depgraph.node[topic]['factory'] 50 factory_list = self.depgraph.node[topic]['factory']
49 return [q.generate() for q in factory_list] 51 return [q.generate() for q in factory_list]
50 52
@@ -58,7 +60,7 @@ class Knowledge(object): @@ -58,7 +60,7 @@ class Knowledge(object):
58 60
59 # ------------------------------------------------------------------------ 61 # ------------------------------------------------------------------------
60 def get_knowledge_state(self): 62 def get_knowledge_state(self):
61 - logger.debug('-> Knowledge.get_knowledge_state()') 63 + # logger.debug('-> Knowledge.get_knowledge_state()')
62 ts = [] 64 ts = []
63 for t in self.topic_sequence: 65 for t in self.topic_sequence:
64 if t in self.state: 66 if t in self.state:
@@ -69,15 +71,13 @@ class Knowledge(object): @@ -69,15 +71,13 @@ class Knowledge(object):
69 71
70 # ------------------------------------------------------------------------ 72 # ------------------------------------------------------------------------
71 def get_topic_progress(self): 73 def get_topic_progress(self):
72 - logger.debug('-> Knowledge.get_topic_progress()') 74 + # logger.debug('-> Knowledge.get_topic_progress()')
73 return len(self.finished_questions) / (len(self.finished_questions) + len(self.questions)) 75 return len(self.finished_questions) / (len(self.finished_questions) + len(self.questions))
74 76
75 # ------------------------------------------------------------------------ 77 # ------------------------------------------------------------------------
76 # if answer to current question is correct generates a new question 78 # if answer to current question is correct generates a new question
77 - # otherwise returns none 79 + # otherwise returns None
78 def new_question(self): 80 def new_question(self):
79 - logger.debug('-> Knowledge.new_question()')  
80 -  
81 if self.current_question is None or \ 81 if self.current_question is None or \
82 self.current_question.get('grade', 0.0) > 0.9: 82 self.current_question.get('grade', 0.0) > 0.9:
83 83
@@ -85,7 +85,7 @@ class Knowledge(object): @@ -85,7 +85,7 @@ class Knowledge(object):
85 # keep going if there are no questions in the next topics 85 # keep going if there are no questions in the next topics
86 while not self.questions: 86 while not self.questions:
87 self.state[self.current_topic] = { 87 self.state[self.current_topic] = {
88 - 'level': 1.0, 88 + 'level': 1.0, # FIXME depends on how many are correct
89 'date': datetime.now() 89 'date': datetime.now()
90 } 90 }
91 self.new_topic() 91 self.new_topic()
@@ -94,15 +94,17 @@ class Knowledge(object): @@ -94,15 +94,17 @@ class Knowledge(object):
94 self.current_question['start_time'] = datetime.now() 94 self.current_question['start_time'] = datetime.now()
95 self.finished_questions.append(self.current_question) 95 self.finished_questions.append(self.current_question)
96 96
  97 + logger.debug(f'Student {self.student}: new_question({self.current_question["ref"]})')
97 return self.current_question 98 return self.current_question
98 99
99 # --- checks answer ------------------------------------------------------ 100 # --- checks answer ------------------------------------------------------
100 # returns current question with correction, time and comments updated 101 # returns current question with correction, time and comments updated
101 def check_answer(self, answer): 102 def check_answer(self, answer):
102 - logger.debug(f'-> Knowledge.check_answer({answer})')  
103 question = self.current_question 103 question = self.current_question
104 if question is not None: 104 if question is not None:
105 question['finish_time'] = datetime.now() 105 question['finish_time'] = datetime.now()
106 question.correct(answer) 106 question.correct(answer)
107 107
  108 + logger.debug(f'Student {self.student}: check_answer({answer}) = {question["grade"]}')
  109 +
108 return question 110 return question
@@ -316,7 +316,7 @@ class QuestionTextArea(Question): @@ -316,7 +316,7 @@ class QuestionTextArea(Question):
316 316
317 #------------------------------------------------------------------------ 317 #------------------------------------------------------------------------
318 def __init__(self, q): 318 def __init__(self, q):
319 - logger.debug('QuestionTextArea.__init__()') 319 + # logger.debug('QuestionTextArea.__init__()')
320 super().__init__(q) 320 super().__init__(q)
321 321
322 self.set_defaults({ 322 self.set_defaults({
templates/learn.html
@@ -163,17 +163,32 @@ function updateQuestion(response){ @@ -163,17 +163,32 @@ function updateQuestion(response){
163 163
164 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); 164 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]);
165 165
166 - $("textarea, input:text, input:radio, input:checkbox").keydown(function (e) { 166 +
  167 + $("input:text, input:radio, input:checkbox").keydown(function (e) {
167 if (e.keyCode == 13 && e.shiftKey) { 168 if (e.keyCode == 13 && e.shiftKey) {
168 e.preventDefault(); 169 e.preventDefault();
169 getQuestion(); 170 getQuestion();
170 } 171 }
171 - // if (e.keyCode == 13) {  
172 - // e.preventDefault();  
173 - // if (e.shiftKey) {  
174 - // getQuestion();  
175 - // }  
176 - // } 172 + });
  173 +
  174 + $("textarea").keydown(function (e) {
  175 + if (e.keyCode == 13 && e.shiftKey) { // shift enter
  176 + e.preventDefault();
  177 + getQuestion();
  178 + }
  179 + else if (e.keyCode === 9) { // tab
  180 + // get caret position/selection
  181 + var start = this.selectionStart;
  182 + var end = this.selectionEnd;
  183 + var value = $(this).val();
  184 +
  185 + // set textarea value to: text before caret + tab + text after caret
  186 + $(this).val(value.substring(0, start) + " " + value.substring(end));
  187 +
  188 + // put caret at right position again (add one for the tab)
  189 + this.selectionStart = this.selectionEnd = start + 4;
  190 + e.preventDefault(); // prevent the focus lose
  191 + }
177 }); 192 });
178 // var audio = new Audio('/static/sounds/correct.mp3'); 193 // var audio = new Audio('/static/sounds/correct.mp3');
179 // audio.play(); 194 // audio.play();