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 | BUGS: | 1 | BUGS: |
2 | 2 | ||
3 | -- não entra à primeira | 3 | +- guardar state cada vez que topico termina |
4 | +- mostrar quantas perguntas faltam | ||
4 | - logs mostram que está a gerar cada pergunta 2 vezes...?? | 5 | - logs mostram que está a gerar cada pergunta 2 vezes...?? |
5 | - mostra tópicos do lado esquerdo, indicando quais estão feitos e quantas perguntas contêm. | 6 | - mostra tópicos do lado esquerdo, indicando quais estão feitos e quantas perguntas contêm. |
6 | - se students.db não existe, rebenta. | 7 | - se students.db não existe, rebenta. |
@@ -9,13 +10,14 @@ BUGS: | @@ -9,13 +10,14 @@ BUGS: | ||
9 | 10 | ||
10 | TODO: | 11 | TODO: |
11 | 12 | ||
13 | +- usar codemirror | ||
12 | - mostrar comments quando falha a resposta | 14 | - mostrar comments quando falha a resposta |
13 | -- configuração e linha de comando. | ||
14 | -- como gerar uma sequencia de perguntas? | ||
15 | - generators not working: bcrypt (ver blog) | 15 | - generators not working: bcrypt (ver blog) |
16 | 16 | ||
17 | SOLVED: | 17 | SOLVED: |
18 | 18 | ||
19 | +- não entra à primeira | ||
20 | +- configuração e linha de comando. | ||
19 | - o browser é redireccionado para /question em vez de fazer um post?? quando se pressiona enter numa caixa text edit. | 21 | - o browser é redireccionado para /question em vez de fazer um post?? quando se pressiona enter numa caixa text edit. |
20 | - load/save the knowledge state of the student | 22 | - load/save the knowledge state of the student |
21 | - servir ficheiros de public temporariamente | 23 | - servir ficheiros de public temporariamente |
app.py
@@ -102,6 +102,14 @@ class LearnApp(object): | @@ -102,6 +102,14 @@ class LearnApp(object): | ||
102 | return self.online[uid].get('name', '') | 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 | def get_current_public_dir(self, uid): | 113 | def get_current_public_dir(self, uid): |
106 | topic = self.online[uid]['state'].get_current_topic() | 114 | topic = self.online[uid]['state'].get_current_topic() |
107 | p = self.depgraph.graph['path'] | 115 | p = self.depgraph.graph['path'] |
@@ -110,7 +118,7 @@ class LearnApp(object): | @@ -110,7 +118,7 @@ class LearnApp(object): | ||
110 | # ------------------------------------------------------------------------ | 118 | # ------------------------------------------------------------------------ |
111 | # check answer and if correct returns new question, otherise returns None | 119 | # check answer and if correct returns new question, otherise returns None |
112 | def check_answer(self, uid, answer): | 120 | def check_answer(self, uid, answer): |
113 | - logger.debug(f'check_answer("{uid}", "{answer}")') | 121 | + # logger.debug(f'check_answer("{uid}", "{answer}")') |
114 | knowledge = self.online[uid]['state'] | 122 | knowledge = self.online[uid]['state'] |
115 | current_question = knowledge.check_answer(answer) | 123 | current_question = knowledge.check_answer(answer) |
116 | 124 | ||
@@ -159,7 +167,8 @@ class LearnApp(object): | @@ -159,7 +167,8 @@ class LearnApp(object): | ||
159 | raise e | 167 | raise e |
160 | 168 | ||
161 | prefix = config['path'] # FIXME default if does not exist? | 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 | # Build dependency graph | 173 | # Build dependency graph |
165 | deps = config.get('dependencies', {}) | 174 | deps = config.get('dependencies', {}) |
knowledge.py
@@ -28,8 +28,8 @@ class Knowledge(object): | @@ -28,8 +28,8 @@ class Knowledge(object): | ||
28 | 28 | ||
29 | # ------------------------------------------------------------------------ | 29 | # ------------------------------------------------------------------------ |
30 | def topic_generator(self): | 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 | if self.state.get(t, 0.0) > 0.999: | 33 | if self.state.get(t, 0.0) > 0.999: |
34 | continue | 34 | continue |
35 | self.questions = self.generate_questions_for_topic(t) | 35 | self.questions = self.generate_questions_for_topic(t) |
@@ -51,7 +51,7 @@ class Knowledge(object): | @@ -51,7 +51,7 @@ class Knowledge(object): | ||
51 | 51 | ||
52 | # ------------------------------------------------------------------------ | 52 | # ------------------------------------------------------------------------ |
53 | def get_knowledge_state(self): | 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 | # --- generates a new question given the current state ------------------- | 56 | # --- generates a new question given the current state ------------------- |
57 | def new_question(self): | 57 | def new_question(self): |
@@ -76,7 +76,7 @@ class Knowledge(object): | @@ -76,7 +76,7 @@ class Knowledge(object): | ||
76 | # --- checks answer ------------------------------------------------------ | 76 | # --- checks answer ------------------------------------------------------ |
77 | # returns current question with correction, time and comments updated | 77 | # returns current question with correction, time and comments updated |
78 | def check_answer(self, answer): | 78 | def check_answer(self, answer): |
79 | - logger.debug(f'check_answer("{answer}")') | 79 | + logger.debug(f'check_answer {answer}') |
80 | question = self.current_question | 80 | question = self.current_question |
81 | if question is not None: | 81 | if question is not None: |
82 | question['finish_time'] = datetime.now() | 82 | question['finish_time'] = datetime.now() |
serve.py
@@ -110,7 +110,8 @@ class LearnHandler(BaseHandler): | @@ -110,7 +110,8 @@ class LearnHandler(BaseHandler): | ||
110 | uid = self.current_user | 110 | uid = self.current_user |
111 | self.render('learn.html', | 111 | self.render('learn.html', |
112 | uid=uid, | 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,6 +130,7 @@ class FileHandler(BaseHandler): | ||
129 | # respond to AJAX to get a JSON question | 130 | # respond to AJAX to get a JSON question |
130 | class QuestionHandler(BaseHandler): | 131 | class QuestionHandler(BaseHandler): |
131 | templates = { | 132 | templates = { |
133 | + 'information': 'question-information.html', | ||
132 | 'checkbox': 'question-checkbox.html', | 134 | 'checkbox': 'question-checkbox.html', |
133 | 'radio': 'question-radio.html', | 135 | 'radio': 'question-radio.html', |
134 | 'text': 'question-text.html', | 136 | 'text': 'question-text.html', |
@@ -146,16 +148,21 @@ class QuestionHandler(BaseHandler): | @@ -146,16 +148,21 @@ class QuestionHandler(BaseHandler): | ||
146 | # ref = self.get_body_arguments('question_ref') | 148 | # ref = self.get_body_arguments('question_ref') |
147 | user = self.current_user | 149 | user = self.current_user |
148 | answer = self.get_body_arguments('answer') | 150 | answer = self.get_body_arguments('answer') |
149 | - # logger.debug(f'Answer POST from "{user}"') | ||
150 | next_question = self.learn.check_answer(user, answer) | 151 | next_question = self.learn.check_answer(user, answer) |
152 | + state = self.learn.get_student_state(user) | ||
151 | 153 | ||
152 | if next_question is not None: | 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 | question=next_question, # dictionary with the question | 156 | question=next_question, # dictionary with the question |
155 | md=md, # function that renders markdown to html | 157 | md=md, # function that renders markdown to html |
156 | ) | 158 | ) |
159 | + topics_html = self.render_string('topics.html', state=state) | ||
160 | + | ||
157 | self.write({ | 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 | 'method': 'new_question', | 166 | 'method': 'new_question', |
160 | }) | 167 | }) |
161 | else: | 168 | else: |
static/css/learn.css
@@ -6,4 +6,27 @@ img { | @@ -6,4 +6,27 @@ img { | ||
6 | display: block; | 6 | display: block; |
7 | margin: auto; | 7 | margin: auto; |
8 | width: 80%; | 8 | width: 80%; |
9 | -} | ||
10 | \ No newline at end of file | 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,7 +2,11 @@ | ||
2 | <html> | 2 | <html> |
3 | <head> | 3 | <head> |
4 | <meta charset="utf-8"> | 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 | <link rel="icon" href="/static/favicon.ico"> | 10 | <link rel="icon" href="/static/favicon.ico"> |
7 | 11 | ||
8 | <!-- MathJax --> | 12 | <!-- MathJax --> |
@@ -31,7 +35,7 @@ | @@ -31,7 +35,7 @@ | ||
31 | <body> | 35 | <body> |
32 | <!-- ===================================================================== --> | 36 | <!-- ===================================================================== --> |
33 | <!-- Navbar --> | 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 | <div class="container-fluid"> | 39 | <div class="container-fluid"> |
36 | 40 | ||
37 | <div class="navbar-header"> | 41 | <div class="navbar-header"> |
@@ -41,7 +45,7 @@ | @@ -41,7 +45,7 @@ | ||
41 | <span class="icon-bar"></span> | 45 | <span class="icon-bar"></span> |
42 | <span class="icon-bar"></span> | 46 | <span class="icon-bar"></span> |
43 | </button> | 47 | </button> |
44 | - <a class="navbar-brand" href="#">BrainPower</a> | 48 | + <a class="navbar-brand" href="#">{{ title }}</a> |
45 | </div> | 49 | </div> |
46 | 50 | ||
47 | <div class="collapse navbar-collapse" id="myNavbar"> | 51 | <div class="collapse navbar-collapse" id="myNavbar"> |
@@ -65,7 +69,17 @@ | @@ -65,7 +69,17 @@ | ||
65 | 69 | ||
66 | <!-- ===================================================================== --> | 70 | <!-- ===================================================================== --> |
67 | <!-- Container --> | 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 | <!-- <audio> | 83 | <!-- <audio> |
70 | <source id="snd-intro" src="/static/sounds/intro.mp3" type="audio/mpeg"> | 84 | <source id="snd-intro" src="/static/sounds/intro.mp3" type="audio/mpeg"> |
71 | <source id="snd-correct" src="/static/sounds/correct.mp3" type="audio/mpeg"> | 85 | <source id="snd-correct" src="/static/sounds/correct.mp3" type="audio/mpeg"> |
@@ -76,15 +90,15 @@ | @@ -76,15 +90,15 @@ | ||
76 | {% module xsrf_form_html() %} | 90 | {% module xsrf_form_html() %} |
77 | 91 | ||
78 | <div id="question_div"> | 92 | <div id="question_div"> |
79 | - O jogo está prestes a começar. A bola está do teu lado. | 93 | + Pronto? |
80 | </div> | 94 | </div> |
81 | 95 | ||
82 | </form> | 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 | </div> <!-- container --> | 100 | </div> <!-- container --> |
86 | 101 | ||
87 | - | ||
88 | <!-- ===================================================================== --> | 102 | <!-- ===================================================================== --> |
89 | <!-- JAVASCRIP --> | 103 | <!-- JAVASCRIP --> |
90 | <!-- ===================================================================== --> | 104 | <!-- ===================================================================== --> |
@@ -102,7 +116,8 @@ $.fn.extend({ | @@ -102,7 +116,8 @@ $.fn.extend({ | ||
102 | function updateQuestion(response){ | 116 | function updateQuestion(response){ |
103 | switch (response["method"]) { | 117 | switch (response["method"]) { |
104 | case "new_question": | 118 | case "new_question": |
105 | - $("#question_div").html(response["params"]); | 119 | + $("#question_div").html(response["params"]["question"]); |
120 | + $("#topics").html(response["params"]["state"]); | ||
106 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); | 121 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]); |
107 | 122 | ||
108 | $("textarea, input:text, input:radio, input:checkbox").keydown(function (e) { | 123 | $("textarea, input:text, input:radio, input:checkbox").keydown(function (e) { |
@@ -142,6 +157,7 @@ function getQuestion() { | @@ -142,6 +157,7 @@ function getQuestion() { | ||
142 | $(document).ready(function() { | 157 | $(document).ready(function() { |
143 | // var audio = new Audio('/static/sounds/intro.mp3'); | 158 | // var audio = new Audio('/static/sounds/intro.mp3'); |
144 | // audio.play(); | 159 | // audio.play(); |
160 | + getQuestion(); | ||
145 | $("#submit").click(getQuestion); | 161 | $("#submit").click(getQuestion); |
146 | }); | 162 | }); |
147 | </script> | 163 | </script> |
templates/question.html
@@ -3,8 +3,7 @@ | @@ -3,8 +3,7 @@ | ||
3 | <!-- <div class="panel panel-default"> | 3 | <!-- <div class="panel panel-default"> |
4 | <div class="panel-body"> | 4 | <div class="panel-body"> |
5 | --> | 5 | --> |
6 | - <h3>{{ question['title'] }}</h3> | ||
7 | - | 6 | + <h1 class="page-header">{{ question['title'] }}</h1> |
8 | <div id="text"> | 7 | <div id="text"> |
9 | {{ md(question['text']) }} | 8 | {{ md(question['text']) }} |
10 | </div> | 9 | </div> |
@@ -0,0 +1,21 @@ | @@ -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> |