Commit e1ad9bc69057dd6428360b633f4627b4249b6ca6
1 parent
0b1675b0
Exists in
master
and in
1 other branch
- titulo da página configurado no ficheiro de configuração.
- mostra o estado dos tópicos numa coluna do lado esquerdo. - adicionado type: information, para perguntas que são apenas informativas.
Showing
9 changed files
with
106 additions
and
24 deletions
Show diff stats
BUGS.md
1 | 1 | BUGS: |
2 | 2 | |
3 | -- não entra à primeira | |
3 | +- guardar state cada vez que topico termina | |
4 | +- mostrar quantas perguntas faltam | |
4 | 5 | - logs mostram que está a gerar cada pergunta 2 vezes...?? |
5 | 6 | - mostra tópicos do lado esquerdo, indicando quais estão feitos e quantas perguntas contêm. |
6 | 7 | - se students.db não existe, rebenta. |
... | ... | @@ -9,13 +10,14 @@ BUGS: |
9 | 10 | |
10 | 11 | TODO: |
11 | 12 | |
13 | +- usar codemirror | |
12 | 14 | - mostrar comments quando falha a resposta |
13 | -- configuração e linha de comando. | |
14 | -- como gerar uma sequencia de perguntas? | |
15 | 15 | - generators not working: bcrypt (ver blog) |
16 | 16 | |
17 | 17 | SOLVED: |
18 | 18 | |
19 | +- não entra à primeira | |
20 | +- configuração e linha de comando. | |
19 | 21 | - o browser é redireccionado para /question em vez de fazer um post?? quando se pressiona enter numa caixa text edit. |
20 | 22 | - load/save the knowledge state of the student |
21 | 23 | - servir ficheiros de public temporariamente | ... | ... |
app.py
... | ... | @@ -102,6 +102,14 @@ class LearnApp(object): |
102 | 102 | return self.online[uid].get('name', '') |
103 | 103 | |
104 | 104 | # ------------------------------------------------------------------------ |
105 | + def get_student_state(self, uid): | |
106 | + return self.online[uid]['state'].get_knowledge_state() | |
107 | + | |
108 | + # ------------------------------------------------------------------------ | |
109 | + def get_title(self): | |
110 | + return self.depgraph.graph['title'] | |
111 | + | |
112 | + # ------------------------------------------------------------------------ | |
105 | 113 | def get_current_public_dir(self, uid): |
106 | 114 | topic = self.online[uid]['state'].get_current_topic() |
107 | 115 | p = self.depgraph.graph['path'] |
... | ... | @@ -110,7 +118,7 @@ class LearnApp(object): |
110 | 118 | # ------------------------------------------------------------------------ |
111 | 119 | # check answer and if correct returns new question, otherise returns None |
112 | 120 | def check_answer(self, uid, answer): |
113 | - logger.debug(f'check_answer("{uid}", "{answer}")') | |
121 | + # logger.debug(f'check_answer("{uid}", "{answer}")') | |
114 | 122 | knowledge = self.online[uid]['state'] |
115 | 123 | current_question = knowledge.check_answer(answer) |
116 | 124 | |
... | ... | @@ -159,7 +167,8 @@ class LearnApp(object): |
159 | 167 | raise e |
160 | 168 | |
161 | 169 | prefix = config['path'] # FIXME default if does not exist? |
162 | - g = nx.DiGraph(path=prefix) | |
170 | + title = config.get('title', '') | |
171 | + g = nx.DiGraph(path=prefix, title=title) | |
163 | 172 | |
164 | 173 | # Build dependency graph |
165 | 174 | deps = config.get('dependencies', {}) | ... | ... |
knowledge.py
... | ... | @@ -28,8 +28,8 @@ class Knowledge(object): |
28 | 28 | |
29 | 29 | # ------------------------------------------------------------------------ |
30 | 30 | def topic_generator(self): |
31 | - topics = nx.topological_sort(self.depgraph) # FIXME for now... | |
32 | - for t in topics: | |
31 | + self.topic_sequence = nx.topological_sort(self.depgraph) # FIXME for now... | |
32 | + for t in self.topic_sequence: | |
33 | 33 | if self.state.get(t, 0.0) > 0.999: |
34 | 34 | continue |
35 | 35 | self.questions = self.generate_questions_for_topic(t) |
... | ... | @@ -51,7 +51,7 @@ class Knowledge(object): |
51 | 51 | |
52 | 52 | # ------------------------------------------------------------------------ |
53 | 53 | def get_knowledge_state(self): |
54 | - return self.state | |
54 | + return [(t, self.state.get(t, 0.0)) for t in self.topic_sequence] | |
55 | 55 | |
56 | 56 | # --- generates a new question given the current state ------------------- |
57 | 57 | def new_question(self): |
... | ... | @@ -76,7 +76,7 @@ class Knowledge(object): |
76 | 76 | # --- checks answer ------------------------------------------------------ |
77 | 77 | # returns current question with correction, time and comments updated |
78 | 78 | def check_answer(self, answer): |
79 | - logger.debug(f'check_answer("{answer}")') | |
79 | + logger.debug(f'check_answer {answer}') | |
80 | 80 | question = self.current_question |
81 | 81 | if question is not None: |
82 | 82 | question['finish_time'] = datetime.now() | ... | ... |
serve.py
... | ... | @@ -110,7 +110,8 @@ class LearnHandler(BaseHandler): |
110 | 110 | uid = self.current_user |
111 | 111 | self.render('learn.html', |
112 | 112 | uid=uid, |
113 | - name=self.learn.get_student_name(uid) | |
113 | + name=self.learn.get_student_name(uid), | |
114 | + title=self.learn.get_title(), | |
114 | 115 | ) |
115 | 116 | |
116 | 117 | # ---------------------------------------------------------------------------- |
... | ... | @@ -129,6 +130,7 @@ class FileHandler(BaseHandler): |
129 | 130 | # respond to AJAX to get a JSON question |
130 | 131 | class QuestionHandler(BaseHandler): |
131 | 132 | templates = { |
133 | + 'information': 'question-information.html', | |
132 | 134 | 'checkbox': 'question-checkbox.html', |
133 | 135 | 'radio': 'question-radio.html', |
134 | 136 | 'text': 'question-text.html', |
... | ... | @@ -146,16 +148,21 @@ class QuestionHandler(BaseHandler): |
146 | 148 | # ref = self.get_body_arguments('question_ref') |
147 | 149 | user = self.current_user |
148 | 150 | answer = self.get_body_arguments('answer') |
149 | - # logger.debug(f'Answer POST from "{user}"') | |
150 | 151 | next_question = self.learn.check_answer(user, answer) |
152 | + state = self.learn.get_student_state(user) | |
151 | 153 | |
152 | 154 | if next_question is not None: |
153 | - html_out = self.render_string(self.templates[next_question['type']], | |
155 | + question_html = self.render_string(self.templates[next_question['type']], | |
154 | 156 | question=next_question, # dictionary with the question |
155 | 157 | md=md, # function that renders markdown to html |
156 | 158 | ) |
159 | + topics_html = self.render_string('topics.html', state=state) | |
160 | + | |
157 | 161 | self.write({ |
158 | - 'params': tornado.escape.to_unicode(html_out), | |
162 | + 'params': { | |
163 | + 'question': tornado.escape.to_unicode(question_html), | |
164 | + 'state': tornado.escape.to_unicode(topics_html), | |
165 | + }, | |
159 | 166 | 'method': 'new_question', |
160 | 167 | }) |
161 | 168 | else: | ... | ... |
static/css/learn.css
... | ... | @@ -6,4 +6,27 @@ img { |
6 | 6 | display: block; |
7 | 7 | margin: auto; |
8 | 8 | width: 80%; |
9 | -} | |
10 | 9 | \ No newline at end of file |
10 | +} | |
11 | + | |
12 | +/* progress bars have height of 4 pixels */ | |
13 | +.progress {height: 4px;} | |
14 | + | |
15 | +/* make markdown tables beautiful */ | |
16 | +table { | |
17 | + border-collapse: collapse; | |
18 | + margin-left: 50px; | |
19 | +} | |
20 | +thead, tbody, td, th { | |
21 | + padding: 5px; | |
22 | + border-bottom: 1px solid #ddd; | |
23 | +} | |
24 | + | |
25 | +textarea { | |
26 | + font-family: monospace !important; | |
27 | +} | |
28 | + | |
29 | +/* Hack to avoid name clash between pygments and mathjax */ | |
30 | +.MathJax .mo, | |
31 | +.MathJax .mi { | |
32 | + color: inherit; | |
33 | +} | ... | ... |
templates/learn.html
... | ... | @@ -2,7 +2,11 @@ |
2 | 2 | <html> |
3 | 3 | <head> |
4 | 4 | <meta charset="utf-8"> |
5 | - <title>Learn</title> | |
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
6 | + <meta name="viewport" content="width=device-width, initial-scale=1"> | |
7 | + <meta name="author" content="Miguel Barão"> | |
8 | + | |
9 | + <title>iLearn</title> | |
6 | 10 | <link rel="icon" href="/static/favicon.ico"> |
7 | 11 | |
8 | 12 | <!-- MathJax --> |
... | ... | @@ -31,7 +35,7 @@ |
31 | 35 | <body> |
32 | 36 | <!-- ===================================================================== --> |
33 | 37 | <!-- Navbar --> |
34 | -<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> | |
38 | +<nav class="navbar navbar-default navbar-fixed-top" role="navigation"> | |
35 | 39 | <div class="container-fluid"> |
36 | 40 | |
37 | 41 | <div class="navbar-header"> |
... | ... | @@ -41,7 +45,7 @@ |
41 | 45 | <span class="icon-bar"></span> |
42 | 46 | <span class="icon-bar"></span> |
43 | 47 | </button> |
44 | - <a class="navbar-brand" href="#">BrainPower</a> | |
48 | + <a class="navbar-brand" href="#">{{ title }}</a> | |
45 | 49 | </div> |
46 | 50 | |
47 | 51 | <div class="collapse navbar-collapse" id="myNavbar"> |
... | ... | @@ -65,7 +69,17 @@ |
65 | 69 | |
66 | 70 | <!-- ===================================================================== --> |
67 | 71 | <!-- Container --> |
68 | -<div class="container"> | |
72 | +<div class="container-fluid"> | |
73 | +<div class="row"> | |
74 | +<div class="col-lg-2"> | |
75 | + <div id="topics"></div> | |
76 | +</div> | |
77 | + | |
78 | + | |
79 | + | |
80 | + <!-- Blog Post Content Column --> | |
81 | + <div class="col-md-10"> | |
82 | + | |
69 | 83 | <!-- <audio> |
70 | 84 | <source id="snd-intro" src="/static/sounds/intro.mp3" type="audio/mpeg"> |
71 | 85 | <source id="snd-correct" src="/static/sounds/correct.mp3" type="audio/mpeg"> |
... | ... | @@ -76,15 +90,15 @@ |
76 | 90 | {% module xsrf_form_html() %} |
77 | 91 | |
78 | 92 | <div id="question_div"> |
79 | - O jogo está prestes a começar. A bola está do teu lado. | |
93 | + Pronto? | |
80 | 94 | </div> |
81 | 95 | |
82 | 96 | </form> |
83 | -<button class="btn btn-primary" id="submit">Próxima</button> | |
97 | +<button class="btn btn-primary" id="submit">Continuar</button> | |
84 | 98 | |
99 | +</div> <!-- col --> | |
85 | 100 | </div> <!-- container --> |
86 | 101 | |
87 | - | |
88 | 102 | <!-- ===================================================================== --> |
89 | 103 | <!-- JAVASCRIP --> |
90 | 104 | <!-- ===================================================================== --> |
... | ... | @@ -102,7 +116,8 @@ $.fn.extend({ |
102 | 116 | function updateQuestion(response){ |
103 | 117 | switch (response["method"]) { |
104 | 118 | case "new_question": |
105 | - $("#question_div").html(response["params"]); | |
119 | + $("#question_div").html(response["params"]["question"]); | |
120 | + $("#topics").html(response["params"]["state"]); | |
106 | 121 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); |
107 | 122 | |
108 | 123 | $("textarea, input:text, input:radio, input:checkbox").keydown(function (e) { |
... | ... | @@ -142,6 +157,7 @@ function getQuestion() { |
142 | 157 | $(document).ready(function() { |
143 | 158 | // var audio = new Audio('/static/sounds/intro.mp3'); |
144 | 159 | // audio.play(); |
160 | + getQuestion(); | |
145 | 161 | $("#submit").click(getQuestion); |
146 | 162 | }); |
147 | 163 | </script> | ... | ... |
templates/question.html
... | ... | @@ -0,0 +1,21 @@ |
1 | +{% autoescape %} | |
2 | + | |
3 | +<!-- <div class="well"> --> | |
4 | +<div class="panel panel-default"> | |
5 | + <div class="panel-heading">Tópicos</div> | |
6 | + <div class="panel-body"> | |
7 | + <!-- <ul class="list-group"> --> | |
8 | + {% for t in state %} | |
9 | + <!-- <li class="list-group-item"> --> | |
10 | + <p>{{ t[0] }}</p> | |
11 | + | |
12 | + <div class="progress"> | |
13 | + <div class="progress-bar {{ 'progress-bar-success' if t[1]>=0.9 else 'progress-bar-danger' }}" role="progressbar" aria-valuenow="{{ 100*t[1] }}" aria-valuemin="0" aria-valuemax="100" style="min-width: 1em;width: {{ 100*t[1] }}%"> | |
14 | + | |
15 | + </div> | |
16 | + </div> | |
17 | + <!-- </li> --> | |
18 | + {% end %} | |
19 | + <!-- </ul> --> | |
20 | + </div> | |
21 | +</div> | ... | ... |