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 %} | ... | ... |