Commit e1ad9bc69057dd6428360b633f4627b4249b6ca6

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

- 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.
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-information.html 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +{% extends "question.html" %}
  2 +
  3 +{% block answer %}
  4 +<input type="hidden" name="question_ref" value="{{ question['ref'] }}">
  5 +{% end %}
... ...
templates/question.html
... ... @@ -3,8 +3,7 @@
3 3 <!-- <div class="panel panel-default">
4 4 <div class="panel-body">
5 5 -->
6   - <h3>{{ question['title'] }}</h3>
7   -
  6 + <h1 class="page-header">{{ question['title'] }}</h1>
8 7 <div id="text">
9 8 {{ md(question['text']) }}
10 9 </div>
... ...
templates/topics.html 0 → 100644
... ... @@ -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>
... ...