Commit 68528695be5a690d9614a33a9ea268e572784789
1 parent
e831c259
Exists in
master
and in
1 other branch
- working! but crashes when topic has no questions
Showing
4 changed files
with
60 additions
and
21 deletions
Show diff stats
BUGS.md
1 | 1 | ||
2 | BUGS: | 2 | BUGS: |
3 | 3 | ||
4 | +- enter faz GET /question, que responde com json no ecran | ||
4 | - tabs em textarea nao funcionam correctamente (insere 1 espaco em vez de 4) | 5 | - tabs em textarea nao funcionam correctamente (insere 1 espaco em vez de 4) |
5 | - reportar comentarios após submeter. | 6 | - reportar comentarios após submeter. |
6 | - os topicos locked devem estar inactivos no sidebar. | 7 | - os topicos locked devem estar inactivos no sidebar. |
knowledge.py
@@ -29,9 +29,6 @@ class Knowledge(object): | @@ -29,9 +29,6 @@ class Knowledge(object): | ||
29 | # compute recommended sequence of topics ['a', 'b',...] | 29 | # compute recommended sequence of topics ['a', 'b',...] |
30 | self.topic_sequence = nx.topological_sort(self.depgraph) | 30 | self.topic_sequence = nx.topological_sort(self.depgraph) |
31 | 31 | ||
32 | - print(self.topic_sequence) | ||
33 | - print(self.depgraph.edges()) | ||
34 | - | ||
35 | # select a topic to do and initialize questions | 32 | # select a topic to do and initialize questions |
36 | self.start_topic() | 33 | self.start_topic() |
37 | 34 | ||
@@ -40,29 +37,42 @@ class Knowledge(object): | @@ -40,29 +37,42 @@ class Knowledge(object): | ||
40 | # If all levels > 0.8, will stay in the last one forever... | 37 | # If all levels > 0.8, will stay in the last one forever... |
41 | # ------------------------------------------------------------------------ | 38 | # ------------------------------------------------------------------------ |
42 | def start_topic(self, topic=''): | 39 | def start_topic(self, topic=''): |
43 | - print(f'knowledge.start_topic "{topic}"') | 40 | + # unlock topics that have satisfied dependencies |
41 | + unlock_topics = [] | ||
42 | + for t in self.topic_sequence: | ||
43 | + if t not in self.state: # is locked | ||
44 | + deps = self.depgraph.predecessors(t) | ||
45 | + if all(d in self.state and self.state[d]['level'] > 0.01 for d in deps): # dependencies done | ||
46 | + unlock_topics.append(t) | ||
47 | + | ||
48 | + for t in unlock_topics: | ||
49 | + self.state[t] = {'level': 0.0, 'date': datetime.now()} | ||
50 | + logger.info(f'User "{self.student}" unlocked "{t}"') | ||
51 | + | ||
52 | + # choose topic | ||
44 | if not topic: | 53 | if not topic: |
45 | for topic in self.topic_sequence: | 54 | for topic in self.topic_sequence: |
46 | unlocked = topic in self.state | 55 | unlocked = topic in self.state |
47 | needs_work = unlocked and self.state[topic]['level'] < 0.8 | 56 | needs_work = unlocked and self.state[topic]['level'] < 0.8 |
48 | factory = self.depgraph.node[topic]['factory'] | 57 | factory = self.depgraph.node[topic]['factory'] |
49 | - print(f'{topic}, unlocked={unlocked}, needs_work={needs_work}') | ||
50 | if factory and (not unlocked or needs_work): | 58 | if factory and (not unlocked or needs_work): |
51 | break | 59 | break |
52 | - # logger.info(f'{self.student} skipped topic "{topic}"') | 60 | + |
61 | + # use given topic if possible | ||
53 | else: | 62 | else: |
63 | + unlocked = topic in self.state | ||
54 | factory = self.depgraph.node[topic]['factory'] | 64 | factory = self.depgraph.node[topic]['factory'] |
55 | - # FIXME if factory is empty??? | 65 | + if not factory or not unlocked: |
66 | + logger.debug(f'User "{self.student}" cannot start topic "{topic}"') | ||
67 | + return | ||
56 | 68 | ||
57 | self.current_topic = topic | 69 | self.current_topic = topic |
58 | - logger.info(f'User "{self.student}" topic = "{topic}"') | 70 | + logger.info(f'User "{self.student}" topic set to "{topic}"') |
59 | 71 | ||
60 | # generate question instances for current topic | 72 | # generate question instances for current topic |
61 | questionlist = self.depgraph.node[topic]['questions'] | 73 | questionlist = self.depgraph.node[topic]['questions'] |
62 | - factory = self.depgraph.node[topic]['factory'] | ||
63 | self.questions = [factory[qref].generate() for qref in questionlist] | 74 | self.questions = [factory[qref].generate() for qref in questionlist] |
64 | - | ||
65 | - self.current_question = self.questions.pop(0) | 75 | + self.current_question = self.questions.pop(0) # FIXME crashes if questions==[] |
66 | self.current_question['start_time'] = datetime.now() | 76 | self.current_question['start_time'] = datetime.now() |
67 | self.finished_questions = [] | 77 | self.finished_questions = [] |
68 | 78 | ||
@@ -78,14 +88,18 @@ class Knowledge(object): | @@ -78,14 +88,18 @@ class Knowledge(object): | ||
78 | # new question if answer is correct | 88 | # new question if answer is correct |
79 | if grade > 0.999: | 89 | if grade > 0.999: |
80 | self.finished_questions.append(q) | 90 | self.finished_questions.append(q) |
91 | + print('questions: ', self.questions) | ||
92 | + print('finished: ', self.finished_questions) | ||
81 | try: | 93 | try: |
82 | self.current_question = self.questions.pop(0) # FIXME empty? | 94 | self.current_question = self.questions.pop(0) # FIXME empty? |
83 | except IndexError: | 95 | except IndexError: |
96 | + print('no more questions!') | ||
84 | self.current_question = None | 97 | self.current_question = None |
85 | self.state[self.current_topic] = { | 98 | self.state[self.current_topic] = { |
86 | 'level': 1.0, | 99 | 'level': 1.0, |
87 | 'date': datetime.now() | 100 | 'date': datetime.now() |
88 | } | 101 | } |
102 | + self.start_topic() | ||
89 | else: | 103 | else: |
90 | self.current_question['start_time'] = datetime.now() | 104 | self.current_question['start_time'] = datetime.now() |
91 | else: | 105 | else: |
@@ -110,13 +124,23 @@ class Knowledge(object): | @@ -110,13 +124,23 @@ class Knowledge(object): | ||
110 | return self.current_topic | 124 | return self.current_topic |
111 | 125 | ||
112 | # ------------------------------------------------------------------------ | 126 | # ------------------------------------------------------------------------ |
113 | - def get_knowledge_state(self): # [('topic', 0.9), ...] | 127 | + # Return list of tuples (topic, level). |
128 | + # Levels are in the interval [0, 1] or None if the topic is locked. | ||
129 | + # Topics unlocked but not yet done have level 0.0. | ||
130 | + # Example: [('topic_A', 0.9), ('topic_B', None), ...] | ||
131 | + # ------------------------------------------------------------------------ | ||
132 | + def get_knowledge_state(self): | ||
114 | ts = [] | 133 | ts = [] |
115 | for t in self.topic_sequence: | 134 | for t in self.topic_sequence: |
116 | if t in self.state: | 135 | if t in self.state: |
117 | - ts.append((t, self.state[t]['level'])) | 136 | + ts.append((t, self.state[t]['level'])) # already done |
118 | else: | 137 | else: |
119 | - ts.append((t, 0.0)) | 138 | + # deps = self.depgraph.predecessors(t) |
139 | + # # print(t, deps) | ||
140 | + # if all(d in self.state for d in deps): | ||
141 | + # ts.append((t, 0.0)) # unlocked not yet done | ||
142 | + # else: | ||
143 | + ts.append((t, None)) # locked | ||
120 | return ts | 144 | return ts |
121 | 145 | ||
122 | # ------------------------------------------------------------------------ | 146 | # ------------------------------------------------------------------------ |
templates/learn.html
@@ -168,6 +168,7 @@ function updateQuestion(response){ | @@ -168,6 +168,7 @@ function updateQuestion(response){ | ||
168 | if (e.keyCode == 13 && e.shiftKey) { | 168 | if (e.keyCode == 13 && e.shiftKey) { |
169 | e.preventDefault(); | 169 | e.preventDefault(); |
170 | postQuestion(); | 170 | postQuestion(); |
171 | + return false; | ||
171 | } | 172 | } |
172 | }); | 173 | }); |
173 | $("textarea").keydown(function (e) { | 174 | $("textarea").keydown(function (e) { |
@@ -205,7 +206,6 @@ function updateQuestion(response){ | @@ -205,7 +206,6 @@ function updateQuestion(response){ | ||
205 | case "finished_topic": | 206 | case "finished_topic": |
206 | $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); | 207 | $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); |
207 | $("#topics").html(response["params"]["state"]); | 208 | $("#topics").html(response["params"]["state"]); |
208 | - | ||
209 | $("#question_div").html('<img src="/static/trophy.png" alt="trophy" class="img-rounded img-responsive center-block">'); // FIXME size | 209 | $("#question_div").html('<img src="/static/trophy.png" alt="trophy" class="img-rounded img-responsive center-block">'); // FIXME size |
210 | break; | 210 | break; |
211 | } | 211 | } |
@@ -228,7 +228,6 @@ function postQuestion() { | @@ -228,7 +228,6 @@ function postQuestion() { | ||
228 | // Get current question | 228 | // Get current question |
229 | function getQuestion() { | 229 | function getQuestion() { |
230 | $.ajax({ | 230 | $.ajax({ |
231 | - // type: "GET", | ||
232 | url: "/question", | 231 | url: "/question", |
233 | // headers: {"X-XSRFToken": token}, | 232 | // headers: {"X-XSRFToken": token}, |
234 | dataType: "json", // expected from server | 233 | dataType: "json", // expected from server |
templates/topics.html
@@ -8,15 +8,30 @@ | @@ -8,15 +8,30 @@ | ||
8 | {% if t[0] == current_topic %} | 8 | {% if t[0] == current_topic %} |
9 | <li class="active"> <!-- class="active" class="disabled" --> | 9 | <li class="active"> <!-- class="active" class="disabled" --> |
10 | 10 | ||
11 | - <a href="#"> {{ gettopicname(t[0]) }}<br> | ||
12 | - {{ round(t[1]*5)*'<i class="fa fa-star text-success" aria-hidden="true"></i>' + round(5-t[1]*5)*'<i class="fa fa-star-o" aria-hidden="true"></i>' }} | 11 | + <a href="#"> |
12 | + {{ gettopicname(t[0]) }} | ||
13 | + </a> | ||
14 | + | ||
15 | + {% elif t[1] is None %} | ||
16 | + | ||
17 | + <li class="disabled"> | ||
18 | + | ||
19 | + <a href="#"> | ||
20 | + {{ gettopicname(t[0]) }} <br> | ||
21 | + <i class="fa fa-lock" aria-hidden="true"></i> | ||
13 | </a> | 22 | </a> |
14 | 23 | ||
15 | {% else %} | 24 | {% else %} |
16 | - <li> <!-- class="active" class="disabled" --> | 25 | + <li> |
26 | + | ||
27 | + <a href="/?topic={{ t[0] }}"> | ||
28 | + {{ gettopicname(t[0]) }} <br> | ||
29 | + {% if t[1] < 0.01 %} | ||
30 | + <i class="fa fa-unlock" aria-hidden="true"></i> | ||
17 | 31 | ||
18 | - <a href="/?topic={{ t[0] }}"> {{ gettopicname(t[0]) }}<br> | ||
19 | - {{ round(t[1]*5)*'<i class="fa fa-star text-success" aria-hidden="true"></i>' + round(5-t[1]*5)*'<i class="fa fa-star-o" aria-hidden="true"></i>' }} | 32 | + {% else %} |
33 | + {{ round(t[1]*5)*'<i class="fa fa-star text-success" aria-hidden="true"></i>' + round(5-t[1]*5)*'<i class="fa fa-star-o" aria-hidden="true"></i>' }} | ||
34 | + {% end %} | ||
20 | </a> | 35 | </a> |
21 | 36 | ||
22 | {% end %} | 37 | {% end %} |