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 | 2 | # BUGS |
3 | 3 | |
4 | +- image brand da universidade está esbatida. | |
4 | 5 | - generators e correct scripts que durem muito tempo podem bloquear o loop do tornado? |
5 | 6 | - detect questions in questions.yaml without ref -> error ou generate default. |
6 | 7 | - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores. |
7 | 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 | 9 | - session management. close after inactive time. |
10 | 10 | - generators not working: bcrypt (ver blog) |
11 | 11 | - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | - each topic only loads a sample of K questions (max) in random order. |
17 | 17 | - servir imagens/ficheiros. |
18 | 18 | - pertuntas tipo tristate: (sim, não, não sei |
19 | -- forçar reload das perguntas sem ter de deitar abaixo o servidor. | |
20 | 19 | - reload das perguntas enquanto online. |
21 | 20 | - tabela de progresso de todos os alunos por topico. |
22 | 21 | - tabela com perguntas / quantidade de respostas certas/erradas. |
... | ... | @@ -31,6 +30,7 @@ |
31 | 30 | |
32 | 31 | # FIXED |
33 | 32 | |
33 | +- error if demo.yaml has no topics | |
34 | 34 | - update de fontawesome para versão 5.0.6. |
35 | 35 | - remover learn.css uma vez que nao é usado em lado nenhum? |
36 | 36 | - check if user already logged in | ... | ... |
knowledge.py
... | ... | @@ -32,7 +32,7 @@ class StudentKnowledge(object): |
32 | 32 | now = datetime.now() |
33 | 33 | for s in state.values(): |
34 | 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 | 37 | # compute recommended sequence of topics ['a', 'b', ...] |
38 | 38 | self.topic_sequence = list(nx.topological_sort(self.deps)) |
... | ... | @@ -45,7 +45,7 @@ class StudentKnowledge(object): |
45 | 45 | def unlock_topics(self): |
46 | 46 | # minimum level that the dependencies of a topic must have |
47 | 47 | # for the topic to be unlocked. |
48 | - min_level = 0.01 | |
48 | + min_level = 0.2 | |
49 | 49 | |
50 | 50 | for topic in self.topic_sequence: |
51 | 51 | if topic not in self.state: # if locked |
... | ... | @@ -75,7 +75,6 @@ class StudentKnowledge(object): |
75 | 75 | return False |
76 | 76 | |
77 | 77 | self.current_topic = topic |
78 | - # logger.info(f'Topic set to "{topic}"') | |
79 | 78 | |
80 | 79 | # generate question instances for current topic |
81 | 80 | factory = self.deps.node[topic]['factory'] |
... | ... | @@ -84,9 +83,14 @@ class StudentKnowledge(object): |
84 | 83 | self.questions = [factory[qref].generate() for qref in questionlist] |
85 | 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 | 96 | # The topic has finished and there are no more questions. | ... | ... |
learnapp.py
... | ... | @@ -305,6 +305,6 @@ def build_dependency_graph(config={}): |
305 | 305 | q['path'] = fullpath # fullpath added to each question |
306 | 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 | 310 | return g | ... | ... |
serve.py
... | ... | @@ -142,6 +142,7 @@ class TopicHandler(BaseHandler): |
142 | 142 | self.redirect('/') |
143 | 143 | |
144 | 144 | # ---------------------------------------------------------------------------- |
145 | +# FIXME | |
145 | 146 | class FileHandler(BaseHandler): |
146 | 147 | @tornado.web.authenticated |
147 | 148 | def get(self, filename): |
... | ... | @@ -176,36 +177,6 @@ class QuestionHandler(BaseHandler): |
176 | 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 | 180 | @tornado.web.authenticated |
210 | 181 | def get(self): |
211 | 182 | logging.debug('QuestionHandler.get()') |
... | ... | @@ -236,7 +207,7 @@ class QuestionHandler(BaseHandler): |
236 | 207 | else: |
237 | 208 | # answers returned in a list. fix depending on question type |
238 | 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 | 211 | answer = None |
241 | 212 | elif qtype != 'checkbox': # radio, text, textarea, ... |
242 | 213 | answer = answer[0] |
... | ... | @@ -244,13 +215,35 @@ class QuestionHandler(BaseHandler): |
244 | 215 | grade = self.learn.check_answer(user, answer) |
245 | 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 | 249 | # Tornado web server | ... | ... |
static/css/topic.css
... | ... | @@ -7,16 +7,16 @@ |
7 | 7 | body { |
8 | 8 | margin: 0; |
9 | 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 | 13 | .footer { |
14 | 14 | position: absolute; |
15 | 15 | bottom: 0; |
16 | 16 | width: 100%; |
17 | - height: 40px; | |
17 | + height: 70px; | |
18 | 18 | line-height: 60px; |
19 | - background-color: #f5f5f5; | |
19 | + /*background-color: #f5f5f5;*/ | |
20 | 20 | } |
21 | 21 | |
22 | 22 | html { | ... | ... |
static/js/maintopics.js
static/js/topic.js
... | ... | @@ -12,6 +12,7 @@ function updateQuestion(response){ |
12 | 12 | switch (response["method"]) { |
13 | 13 | case "new_question": |
14 | 14 | $("#question_div").html(response["params"]["question"]); |
15 | + $("#comments").html(""); | |
15 | 16 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); |
16 | 17 | |
17 | 18 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); |
... | ... | @@ -46,6 +47,8 @@ function updateQuestion(response){ |
46 | 47 | case "shake": |
47 | 48 | $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]); |
48 | 49 | $('#question_div').animateCSS('shake'); |
50 | + $('#comments').html(response['params']['comments']); | |
51 | + MathJax.Hub.Queue(["Typeset",MathJax.Hub,"#comments"]); | |
49 | 52 | break; |
50 | 53 | |
51 | 54 | case "finished_topic": | ... | ... |
templates/notification.html
templates/topic.html
... | ... | @@ -83,14 +83,17 @@ |
83 | 83 | <div id="question_div"></div> |
84 | 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 | 88 | </div> |
88 | 89 | </div> |
89 | 90 | |
90 | 91 | <footer class="footer"> |
91 | 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 | 95 | <span class="text-muted"></span> |
93 | - </div> | |
96 | + --> </div> | |
94 | 97 | </footer> |
95 | 98 | |
96 | 99 | </body> | ... | ... |