Commit bbe90cadaea87632f8df2251a493f1cd720c82ae
1 parent
62e18fdc
Exists in
master
and in
1 other branch
- fix crash when topic has no questions (but not yet ok).
- shows comments on wrong answer.
Showing
9 changed files
with
56 additions
and
55 deletions
Show diff stats
BUGS.md
1 | 1 | ||
2 | # BUGS | 2 | # BUGS |
3 | 3 | ||
4 | +- image brand da universidade está esbatida. | ||
4 | - generators e correct scripts que durem muito tempo podem bloquear o loop do tornado? | 5 | - generators e correct scripts que durem muito tempo podem bloquear o loop do tornado? |
5 | - detect questions in questions.yaml without ref -> error ou generate default. | 6 | - detect questions in questions.yaml without ref -> error ou generate default. |
6 | - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores. | 7 | - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores. |
7 | - Criar outra estrutura organizada em capítulos (conjuntos de tópicos). Permitir capítulos de capítulos, etc. talvez usar grafos de grafos... | 8 | - Criar outra estrutura organizada em capítulos (conjuntos de tópicos). Permitir capítulos de capítulos, etc. talvez usar grafos de grafos... |
8 | -- error if demo.yaml has no topics | ||
9 | - session management. close after inactive time. | 9 | - session management. close after inactive time. |
10 | - generators not working: bcrypt (ver blog) | 10 | - generators not working: bcrypt (ver blog) |
11 | - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. | 11 | - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. |
@@ -16,7 +16,6 @@ | @@ -16,7 +16,6 @@ | ||
16 | - each topic only loads a sample of K questions (max) in random order. | 16 | - each topic only loads a sample of K questions (max) in random order. |
17 | - servir imagens/ficheiros. | 17 | - servir imagens/ficheiros. |
18 | - pertuntas tipo tristate: (sim, não, não sei | 18 | - pertuntas tipo tristate: (sim, não, não sei |
19 | -- forçar reload das perguntas sem ter de deitar abaixo o servidor. | ||
20 | - reload das perguntas enquanto online. | 19 | - reload das perguntas enquanto online. |
21 | - tabela de progresso de todos os alunos por topico. | 20 | - tabela de progresso de todos os alunos por topico. |
22 | - tabela com perguntas / quantidade de respostas certas/erradas. | 21 | - tabela com perguntas / quantidade de respostas certas/erradas. |
@@ -31,6 +30,7 @@ | @@ -31,6 +30,7 @@ | ||
31 | 30 | ||
32 | # FIXED | 31 | # FIXED |
33 | 32 | ||
33 | +- error if demo.yaml has no topics | ||
34 | - update de fontawesome para versão 5.0.6. | 34 | - update de fontawesome para versão 5.0.6. |
35 | - remover learn.css uma vez que nao é usado em lado nenhum? | 35 | - remover learn.css uma vez que nao é usado em lado nenhum? |
36 | - check if user already logged in | 36 | - check if user already logged in |
knowledge.py
@@ -32,7 +32,7 @@ class StudentKnowledge(object): | @@ -32,7 +32,7 @@ class StudentKnowledge(object): | ||
32 | now = datetime.now() | 32 | now = datetime.now() |
33 | for s in state.values(): | 33 | for s in state.values(): |
34 | dt = now - s['date'] | 34 | dt = now - s['date'] |
35 | - s['level'] *= 0.975 ** dt.days # forgetting factor | 35 | + s['level'] *= 0.8 ** dt.days # forgetting factor 0.95 |
36 | 36 | ||
37 | # compute recommended sequence of topics ['a', 'b', ...] | 37 | # compute recommended sequence of topics ['a', 'b', ...] |
38 | self.topic_sequence = list(nx.topological_sort(self.deps)) | 38 | self.topic_sequence = list(nx.topological_sort(self.deps)) |
@@ -45,7 +45,7 @@ class StudentKnowledge(object): | @@ -45,7 +45,7 @@ class StudentKnowledge(object): | ||
45 | def unlock_topics(self): | 45 | def unlock_topics(self): |
46 | # minimum level that the dependencies of a topic must have | 46 | # minimum level that the dependencies of a topic must have |
47 | # for the topic to be unlocked. | 47 | # for the topic to be unlocked. |
48 | - min_level = 0.01 | 48 | + min_level = 0.2 |
49 | 49 | ||
50 | for topic in self.topic_sequence: | 50 | for topic in self.topic_sequence: |
51 | if topic not in self.state: # if locked | 51 | if topic not in self.state: # if locked |
@@ -75,7 +75,6 @@ class StudentKnowledge(object): | @@ -75,7 +75,6 @@ class StudentKnowledge(object): | ||
75 | return False | 75 | return False |
76 | 76 | ||
77 | self.current_topic = topic | 77 | self.current_topic = topic |
78 | - # logger.info(f'Topic set to "{topic}"') | ||
79 | 78 | ||
80 | # generate question instances for current topic | 79 | # generate question instances for current topic |
81 | factory = self.deps.node[topic]['factory'] | 80 | factory = self.deps.node[topic]['factory'] |
@@ -84,9 +83,14 @@ class StudentKnowledge(object): | @@ -84,9 +83,14 @@ class StudentKnowledge(object): | ||
84 | self.questions = [factory[qref].generate() for qref in questionlist] | 83 | self.questions = [factory[qref].generate() for qref in questionlist] |
85 | self.finished_questions = [] | 84 | self.finished_questions = [] |
86 | 85 | ||
87 | - self.current_question = self.questions.pop(0) # FIXME crash if empty | ||
88 | - self.current_question['start_time'] = datetime.now() | ||
89 | - return True | 86 | + try: |
87 | + self.current_question = self.questions.pop(0) # FIXME crash if empty | ||
88 | + except IndexError: | ||
89 | + self.finish_topic() | ||
90 | + return False | ||
91 | + else: | ||
92 | + self.current_question['start_time'] = datetime.now() | ||
93 | + return True | ||
90 | 94 | ||
91 | # ------------------------------------------------------------------------ | 95 | # ------------------------------------------------------------------------ |
92 | # The topic has finished and there are no more questions. | 96 | # The topic has finished and there are no more questions. |
learnapp.py
@@ -305,6 +305,6 @@ def build_dependency_graph(config={}): | @@ -305,6 +305,6 @@ def build_dependency_graph(config={}): | ||
305 | q['path'] = fullpath # fullpath added to each question | 305 | q['path'] = fullpath # fullpath added to each question |
306 | tnode['factory'][q['ref']] = QFactory(q) | 306 | tnode['factory'][q['ref']] = QFactory(q) |
307 | 307 | ||
308 | - logger.info(f'{len(tnode["questions"]):6} from {ref}') | 308 | + logger.info(f'{len(tnode["questions"]):6} {ref}') |
309 | 309 | ||
310 | return g | 310 | return g |
serve.py
@@ -142,6 +142,7 @@ class TopicHandler(BaseHandler): | @@ -142,6 +142,7 @@ class TopicHandler(BaseHandler): | ||
142 | self.redirect('/') | 142 | self.redirect('/') |
143 | 143 | ||
144 | # ---------------------------------------------------------------------------- | 144 | # ---------------------------------------------------------------------------- |
145 | +# FIXME | ||
145 | class FileHandler(BaseHandler): | 146 | class FileHandler(BaseHandler): |
146 | @tornado.web.authenticated | 147 | @tornado.web.authenticated |
147 | def get(self, filename): | 148 | def get(self, filename): |
@@ -176,36 +177,6 @@ class QuestionHandler(BaseHandler): | @@ -176,36 +177,6 @@ class QuestionHandler(BaseHandler): | ||
176 | # 'alert': '', FIXME | 177 | # 'alert': '', FIXME |
177 | } | 178 | } |
178 | 179 | ||
179 | - def new_question(self, user): | ||
180 | - question = self.learn.get_student_question(user) # Question | ||
181 | - template = self.templates[question['type']] | ||
182 | - question_html = self.render_string(template, question=question, md=md_to_html) | ||
183 | - | ||
184 | - return { | ||
185 | - 'method': 'new_question', | ||
186 | - 'params': { | ||
187 | - 'question': tornado.escape.to_unicode(question_html), | ||
188 | - 'progress': self.learn.get_student_progress(user), | ||
189 | - } | ||
190 | - } | ||
191 | - | ||
192 | - def wrong_answer(self, user): | ||
193 | - progress = self.learn.get_student_progress(user) # in the current topic | ||
194 | - return { | ||
195 | - 'method': 'shake', | ||
196 | - 'params': { | ||
197 | - 'progress': progress, | ||
198 | - } | ||
199 | - } | ||
200 | - | ||
201 | - def finished_topic(self, user): # FIXME user unused | ||
202 | - return { | ||
203 | - 'method': 'finished_topic', | ||
204 | - 'params': { # FIXME no html here please! | ||
205 | - 'question': f'<img src="/static/trophy.svg" alt="trophy" class="img-fluid mx-auto d-block" width="35%">' | ||
206 | - } | ||
207 | - } | ||
208 | - | ||
209 | @tornado.web.authenticated | 180 | @tornado.web.authenticated |
210 | def get(self): | 181 | def get(self): |
211 | logging.debug('QuestionHandler.get()') | 182 | logging.debug('QuestionHandler.get()') |
@@ -236,7 +207,7 @@ class QuestionHandler(BaseHandler): | @@ -236,7 +207,7 @@ class QuestionHandler(BaseHandler): | ||
236 | else: | 207 | else: |
237 | # answers returned in a list. fix depending on question type | 208 | # answers returned in a list. fix depending on question type |
238 | qtype = self.learn.get_student_question_type(user) | 209 | qtype = self.learn.get_student_question_type(user) |
239 | - if qtype in ('success', 'information', 'info'): # FIXME danger... | 210 | + if qtype in ('success', 'information', 'info'): # FIXME unused? |
240 | answer = None | 211 | answer = None |
241 | elif qtype != 'checkbox': # radio, text, textarea, ... | 212 | elif qtype != 'checkbox': # radio, text, textarea, ... |
242 | answer = answer[0] | 213 | answer = answer[0] |
@@ -244,13 +215,35 @@ class QuestionHandler(BaseHandler): | @@ -244,13 +215,35 @@ class QuestionHandler(BaseHandler): | ||
244 | grade = self.learn.check_answer(user, answer) | 215 | grade = self.learn.check_answer(user, answer) |
245 | question = self.learn.get_student_question(user) | 216 | question = self.learn.get_student_question(user) |
246 | 217 | ||
247 | - if question is None: | ||
248 | - self.write(self.finished_topic(user)) | ||
249 | - elif grade > 0.999: | ||
250 | - self.write(self.new_question(user)) | ||
251 | - else: | ||
252 | - self.write(self.wrong_answer(user)) | ||
253 | - | 218 | + if grade <= 0.999: # wrong answer |
219 | + comments_html = self.render_string('comments.html', comments=question['comments'], md=md_to_html) | ||
220 | + self.write({ | ||
221 | + 'method': 'shake', | ||
222 | + 'params': { | ||
223 | + 'progress': self.learn.get_student_progress(user), | ||
224 | + 'comments': tornado.escape.to_unicode(comments_html), # FIXME | ||
225 | + } | ||
226 | + }) | ||
227 | + else: # answer is correct | ||
228 | + if question is None: # finished topic | ||
229 | + finished_topic_html = self.render_string('finished_topic.html') | ||
230 | + self.write({ | ||
231 | + 'method': 'finished_topic', | ||
232 | + 'params': { | ||
233 | + 'question': tornado.escape.to_unicode(finished_topic_html) | ||
234 | + } | ||
235 | + }) | ||
236 | + | ||
237 | + else: # continue with a new question | ||
238 | + template = self.templates[question['type']] | ||
239 | + question_html = self.render_string(template, question=question, md=md_to_html) | ||
240 | + self.write({ | ||
241 | + 'method': 'new_question', | ||
242 | + 'params': { | ||
243 | + 'question': tornado.escape.to_unicode(question_html), | ||
244 | + 'progress': self.learn.get_student_progress(user), | ||
245 | + } | ||
246 | + }) | ||
254 | 247 | ||
255 | # ------------------------------------------------------------------------- | 248 | # ------------------------------------------------------------------------- |
256 | # Tornado web server | 249 | # Tornado web server |
static/css/topic.css
@@ -7,16 +7,16 @@ | @@ -7,16 +7,16 @@ | ||
7 | body { | 7 | body { |
8 | margin: 0; | 8 | margin: 0; |
9 | padding-top: 0px; | 9 | padding-top: 0px; |
10 | - margin-bottom: 60px; /* Margin bottom by footer height */ | 10 | + margin-bottom: 90px; /* Margin bottom by footer height */ |
11 | } | 11 | } |
12 | 12 | ||
13 | .footer { | 13 | .footer { |
14 | position: absolute; | 14 | position: absolute; |
15 | bottom: 0; | 15 | bottom: 0; |
16 | width: 100%; | 16 | width: 100%; |
17 | - height: 40px; | 17 | + height: 70px; |
18 | line-height: 60px; | 18 | line-height: 60px; |
19 | - background-color: #f5f5f5; | 19 | + /*background-color: #f5f5f5;*/ |
20 | } | 20 | } |
21 | 21 | ||
22 | html { | 22 | html { |
static/js/maintopics.js
@@ -9,8 +9,6 @@ function getCookie(name) { | @@ -9,8 +9,6 @@ function getCookie(name) { | ||
9 | } | 9 | } |
10 | 10 | ||
11 | function change_password() { | 11 | function change_password() { |
12 | - // alert('hello'); | ||
13 | - // notify('hello!'); | ||
14 | var token = getCookie('_xsrf'); | 12 | var token = getCookie('_xsrf'); |
15 | $.ajax({ | 13 | $.ajax({ |
16 | type: "POST", | 14 | type: "POST", |
static/js/topic.js
@@ -12,6 +12,7 @@ function updateQuestion(response){ | @@ -12,6 +12,7 @@ function updateQuestion(response){ | ||
12 | switch (response["method"]) { | 12 | switch (response["method"]) { |
13 | case "new_question": | 13 | case "new_question": |
14 | $("#question_div").html(response["params"]["question"]); | 14 | $("#question_div").html(response["params"]["question"]); |
15 | + $("#comments").html(""); | ||
15 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); | 16 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); |
16 | 17 | ||
17 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); | 18 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); |
@@ -46,6 +47,8 @@ function updateQuestion(response){ | @@ -46,6 +47,8 @@ function updateQuestion(response){ | ||
46 | case "shake": | 47 | case "shake": |
47 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); | 48 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); |
48 | $('#question_div').animateCSS('shake'); | 49 | $('#question_div').animateCSS('shake'); |
50 | + $('#comments').html(response['params']['comments']); | ||
51 | + MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]); | ||
49 | break; | 52 | break; |
50 | 53 | ||
51 | case "finished_topic": | 54 | case "finished_topic": |
templates/notification.html
templates/topic.html
@@ -83,14 +83,17 @@ | @@ -83,14 +83,17 @@ | ||
83 | <div id="question_div"></div> | 83 | <div id="question_div"></div> |
84 | </form> | 84 | </form> |
85 | 85 | ||
86 | - <button class="btn btn-primary btn-lg btn-block my-3" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button> | 86 | + <div id="comments"></div> |
87 | + | ||
87 | </div> | 88 | </div> |
88 | </div> | 89 | </div> |
89 | 90 | ||
90 | <footer class="footer"> | 91 | <footer class="footer"> |
91 | <div class="container"> | 92 | <div class="container"> |
93 | + <button class="btn btn-primary btn-lg btn-block my-3" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button> | ||
94 | +<!-- | ||
92 | <span class="text-muted"></span> | 95 | <span class="text-muted"></span> |
93 | - </div> | 96 | + --> </div> |
94 | </footer> | 97 | </footer> |
95 | 98 | ||
96 | </body> | 99 | </body> |