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
knowledge.py
... | ... | @@ -29,9 +29,6 @@ class Knowledge(object): |
29 | 29 | # compute recommended sequence of topics ['a', 'b',...] |
30 | 30 | self.topic_sequence = nx.topological_sort(self.depgraph) |
31 | 31 | |
32 | - print(self.topic_sequence) | |
33 | - print(self.depgraph.edges()) | |
34 | - | |
35 | 32 | # select a topic to do and initialize questions |
36 | 33 | self.start_topic() |
37 | 34 | |
... | ... | @@ -40,29 +37,42 @@ class Knowledge(object): |
40 | 37 | # If all levels > 0.8, will stay in the last one forever... |
41 | 38 | # ------------------------------------------------------------------------ |
42 | 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 | 53 | if not topic: |
45 | 54 | for topic in self.topic_sequence: |
46 | 55 | unlocked = topic in self.state |
47 | 56 | needs_work = unlocked and self.state[topic]['level'] < 0.8 |
48 | 57 | factory = self.depgraph.node[topic]['factory'] |
49 | - print(f'{topic}, unlocked={unlocked}, needs_work={needs_work}') | |
50 | 58 | if factory and (not unlocked or needs_work): |
51 | 59 | break |
52 | - # logger.info(f'{self.student} skipped topic "{topic}"') | |
60 | + | |
61 | + # use given topic if possible | |
53 | 62 | else: |
63 | + unlocked = topic in self.state | |
54 | 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 | 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 | 72 | # generate question instances for current topic |
61 | 73 | questionlist = self.depgraph.node[topic]['questions'] |
62 | - factory = self.depgraph.node[topic]['factory'] | |
63 | 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 | 76 | self.current_question['start_time'] = datetime.now() |
67 | 77 | self.finished_questions = [] |
68 | 78 | |
... | ... | @@ -78,14 +88,18 @@ class Knowledge(object): |
78 | 88 | # new question if answer is correct |
79 | 89 | if grade > 0.999: |
80 | 90 | self.finished_questions.append(q) |
91 | + print('questions: ', self.questions) | |
92 | + print('finished: ', self.finished_questions) | |
81 | 93 | try: |
82 | 94 | self.current_question = self.questions.pop(0) # FIXME empty? |
83 | 95 | except IndexError: |
96 | + print('no more questions!') | |
84 | 97 | self.current_question = None |
85 | 98 | self.state[self.current_topic] = { |
86 | 99 | 'level': 1.0, |
87 | 100 | 'date': datetime.now() |
88 | 101 | } |
102 | + self.start_topic() | |
89 | 103 | else: |
90 | 104 | self.current_question['start_time'] = datetime.now() |
91 | 105 | else: |
... | ... | @@ -110,13 +124,23 @@ class Knowledge(object): |
110 | 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 | 133 | ts = [] |
115 | 134 | for t in self.topic_sequence: |
116 | 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 | 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 | 144 | return ts |
121 | 145 | |
122 | 146 | # ------------------------------------------------------------------------ | ... | ... |
templates/learn.html
... | ... | @@ -168,6 +168,7 @@ function updateQuestion(response){ |
168 | 168 | if (e.keyCode == 13 && e.shiftKey) { |
169 | 169 | e.preventDefault(); |
170 | 170 | postQuestion(); |
171 | + return false; | |
171 | 172 | } |
172 | 173 | }); |
173 | 174 | $("textarea").keydown(function (e) { |
... | ... | @@ -205,7 +206,6 @@ function updateQuestion(response){ |
205 | 206 | case "finished_topic": |
206 | 207 | $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); |
207 | 208 | $("#topics").html(response["params"]["state"]); |
208 | - | |
209 | 209 | $("#question_div").html('<img src="/static/trophy.png" alt="trophy" class="img-rounded img-responsive center-block">'); // FIXME size |
210 | 210 | break; |
211 | 211 | } |
... | ... | @@ -228,7 +228,6 @@ function postQuestion() { |
228 | 228 | // Get current question |
229 | 229 | function getQuestion() { |
230 | 230 | $.ajax({ |
231 | - // type: "GET", | |
232 | 231 | url: "/question", |
233 | 232 | // headers: {"X-XSRFToken": token}, |
234 | 233 | dataType: "json", // expected from server | ... | ... |
templates/topics.html
... | ... | @@ -8,15 +8,30 @@ |
8 | 8 | {% if t[0] == current_topic %} |
9 | 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 | 22 | </a> |
14 | 23 | |
15 | 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 | 35 | </a> |
21 | 36 | |
22 | 37 | {% end %} | ... | ... |