Commit 03210039c66bdcdc9623a43d47c8ffc8fe8e861d
1 parent
13773677
Exists in
master
and in
1 other branch
- main page ok.
Showing
4 changed files
with
71 additions
and
40 deletions
Show diff stats
knowledge.py
@@ -42,45 +42,63 @@ class StudentKnowledge(object): | @@ -42,45 +42,63 @@ class StudentKnowledge(object): | ||
42 | # select a topic to do and initialize questions | 42 | # select a topic to do and initialize questions |
43 | # self.start_topic() | 43 | # self.start_topic() |
44 | 44 | ||
45 | + | ||
46 | + # ------------------------------------------------------------------------ | ||
47 | + # Unlock all topics whose dependencies are satisfied | ||
48 | + # ------------------------------------------------------------------------ | ||
49 | + def unlock_topics(self): | ||
50 | + min_level = 0.01 # minimum level to unlock | ||
51 | + for topic in self.topic_sequence: | ||
52 | + if topic not in self.state: # if locked | ||
53 | + pred = self.deps.predecessors(topic) | ||
54 | + if all(d in self.state and self.state[d]['level'] > min_level for d in pred): # dependencies done | ||
55 | + self.state[topic] = { | ||
56 | + 'level': 0.0, | ||
57 | + 'date': datetime.now() | ||
58 | + } | ||
59 | + logger.debug(f'unlocked {topic}') | ||
60 | + | ||
61 | + | ||
62 | + # ------------------------------------------------------------------------ | ||
63 | + # Recommends a topic to practice/learn from the state | ||
64 | + # ------------------------------------------------------------------------ | ||
65 | + def recommend_topic(self): | ||
66 | + pass # FIXME | ||
67 | + | ||
45 | # ------------------------------------------------------------------------ | 68 | # ------------------------------------------------------------------------ |
46 | # Start a new topic. If not provided, selects the first with level < 0.8 | 69 | # Start a new topic. If not provided, selects the first with level < 0.8 |
47 | # If all levels > 0.8, will stay in the last one forever... | 70 | # If all levels > 0.8, will stay in the last one forever... |
48 | # ------------------------------------------------------------------------ | 71 | # ------------------------------------------------------------------------ |
49 | def start_topic(self, topic=''): | 72 | def start_topic(self, topic=''): |
50 | # unlock topics whose dependencies are done | 73 | # unlock topics whose dependencies are done |
51 | - unlocked_topics = [] | ||
52 | - for t in self.topic_sequence: | ||
53 | - if t not in self.state: # is locked | ||
54 | - deps = self.depgraph.predecessors(t) | ||
55 | - if all(d in self.state and self.state[d]['level'] > 0.01 for d in deps): # dependencies done | ||
56 | - unlocked_topics.append(t) | 74 | + logger.debug('start_topic ' + topic) |
57 | 75 | ||
58 | - for t in unlocked_topics: | ||
59 | - self.state[t] = {'level': 0.0, 'date': datetime.now()} | ||
60 | - logger.info(f'Unlocked "{t}"') | 76 | + self.unlock_topics() |
61 | 77 | ||
62 | # choose topic | 78 | # choose topic |
63 | if not topic: | 79 | if not topic: |
64 | for topic in self.topic_sequence: | 80 | for topic in self.topic_sequence: |
65 | unlocked = topic in self.state | 81 | unlocked = topic in self.state |
66 | needs_work = unlocked and self.state[topic]['level'] < 0.8 | 82 | needs_work = unlocked and self.state[topic]['level'] < 0.8 |
67 | - factory = self.depgraph.node[topic]['factory'] | 83 | + factory = self.deps.node[topic]['factory'] |
68 | if factory and (not unlocked or needs_work): | 84 | if factory and (not unlocked or needs_work): |
69 | break | 85 | break |
70 | 86 | ||
71 | # use given topic if possible | 87 | # use given topic if possible |
72 | else: | 88 | else: |
73 | unlocked = topic in self.state | 89 | unlocked = topic in self.state |
74 | - factory = self.depgraph.node[topic]['factory'] | 90 | + factory = self.deps.node[topic]['factory'] |
75 | if not factory or not unlocked: | 91 | if not factory or not unlocked: |
76 | logger.debug(f'Can\'t start topic "{topic}".') | 92 | logger.debug(f'Can\'t start topic "{topic}".') |
77 | return | 93 | return |
78 | 94 | ||
95 | + | ||
96 | + | ||
79 | self.current_topic = topic | 97 | self.current_topic = topic |
80 | logger.info(f'Topic set to "{topic}"') | 98 | logger.info(f'Topic set to "{topic}"') |
81 | 99 | ||
82 | # generate question instances for current topic | 100 | # generate question instances for current topic |
83 | - questionlist = self.depgraph.node[topic]['questions'] | 101 | + questionlist = self.deps.node[topic]['questions'] |
84 | self.questions = [factory[qref].generate() for qref in questionlist] | 102 | self.questions = [factory[qref].generate() for qref in questionlist] |
85 | self.current_question = self.questions.pop(0) # FIXME crashes if questions==[] | 103 | self.current_question = self.questions.pop(0) # FIXME crashes if questions==[] |
86 | self.current_question['start_time'] = datetime.now() | 104 | self.current_question['start_time'] = datetime.now() |
@@ -110,7 +128,7 @@ class StudentKnowledge(object): | @@ -110,7 +128,7 @@ class StudentKnowledge(object): | ||
110 | else: | 128 | else: |
111 | self.current_question['start_time'] = datetime.now() | 129 | self.current_question['start_time'] = datetime.now() |
112 | else: | 130 | else: |
113 | - factory = self.depgraph.node[self.current_topic]['factory'] | 131 | + factory = self.deps.node[self.current_topic]['factory'] |
114 | self.questions.append(factory[q['ref']].generate()) | 132 | self.questions.append(factory[q['ref']].generate()) |
115 | 133 | ||
116 | return q | 134 | return q |
learnapp.py
@@ -289,6 +289,6 @@ def build_dependency_graph(config_file): | @@ -289,6 +289,6 @@ def build_dependency_graph(config_file): | ||
289 | q['path'] = fullpath | 289 | q['path'] = fullpath |
290 | tnode['factory'][q['ref']] = QFactory(q) | 290 | tnode['factory'][q['ref']] = QFactory(q) |
291 | 291 | ||
292 | - logger.info(f' {len(tnode["questions"])} questions from "{tref}"') | 292 | + logger.info(f'{len(tnode["questions"]):4} questions from {tref}') |
293 | 293 | ||
294 | return g | 294 | return g |
serve.py
@@ -33,7 +33,7 @@ class WebApplication(tornado.web.Application): | @@ -33,7 +33,7 @@ class WebApplication(tornado.web.Application): | ||
33 | (r'/change_password', ChangePasswordHandler), | 33 | (r'/change_password', ChangePasswordHandler), |
34 | (r'/question', QuestionHandler), | 34 | (r'/question', QuestionHandler), |
35 | (r'/', LearnHandler), | 35 | (r'/', LearnHandler), |
36 | - (r'/topic', TopicHandler), | 36 | + (r'/topic/(.+)', TopicHandler), |
37 | (r'/(.+)', FileHandler), | 37 | (r'/(.+)', FileHandler), |
38 | ] | 38 | ] |
39 | settings = { | 39 | settings = { |
@@ -109,7 +109,7 @@ class ChangePasswordHandler(BaseHandler): | @@ -109,7 +109,7 @@ class ChangePasswordHandler(BaseHandler): | ||
109 | self.write({'msg': notification}) | 109 | self.write({'msg': notification}) |
110 | 110 | ||
111 | # ---------------------------------------------------------------------------- | 111 | # ---------------------------------------------------------------------------- |
112 | -# main page / | 112 | +# Main page: / |
113 | # Shows a list of topics and proficiency (stars, locked). | 113 | # Shows a list of topics and proficiency (stars, locked). |
114 | # ---------------------------------------------------------------------------- | 114 | # ---------------------------------------------------------------------------- |
115 | class LearnHandler(BaseHandler): | 115 | class LearnHandler(BaseHandler): |
@@ -122,12 +122,14 @@ class LearnHandler(BaseHandler): | @@ -122,12 +122,14 @@ class LearnHandler(BaseHandler): | ||
122 | state=self.learn.get_student_state(uid) | 122 | state=self.learn.get_student_state(uid) |
123 | ) | 123 | ) |
124 | 124 | ||
125 | - | 125 | +# ---------------------------------------------------------------------------- |
126 | +# Start a given topic: /topic | ||
127 | +# ---------------------------------------------------------------------------- | ||
126 | class TopicHandler(BaseHandler): | 128 | class TopicHandler(BaseHandler): |
127 | @tornado.web.authenticated | 129 | @tornado.web.authenticated |
128 | - def get(self): | 130 | + def get(self, topic): |
129 | uid = self.current_user | 131 | uid = self.current_user |
130 | - topic = self.get_query_argument('topic', default='') | 132 | + # topic = self.get_query_argument('topic', default='') |
131 | self.learn.start_topic(uid, topic) | 133 | self.learn.start_topic(uid, topic) |
132 | self.render('topic.html', | 134 | self.render('topic.html', |
133 | uid=uid, | 135 | uid=uid, |
templates/maintopics.html
@@ -42,28 +42,39 @@ | @@ -42,28 +42,39 @@ | ||
42 | </nav> | 42 | </nav> |
43 | <div class="container"> | 43 | <div class="container"> |
44 | 44 | ||
45 | - <h3>Tópicos</h3> | 45 | + <h3>Tópicos</h3> |
46 | 46 | ||
47 | - <div class="list-group"> | ||
48 | - {% for t in state %} | ||
49 | - {% if t['level'] is None %} | ||
50 | - <a class="list-group-item list-group-item-action disabled" href="#"> | ||
51 | - {{ t['name'] }} | ||
52 | - <i class="fa fa-lock" aria-hidden="true"></i> | ||
53 | - </a> | ||
54 | - {% else %} | ||
55 | - <a class="list-group-item list-group-item-action" href="/?topic={{ t['ref'] }}"> | ||
56 | - {{ t['name'] }} | ||
57 | - {% if t['level'] < 0.01 %} | ||
58 | - <i class="fa fa-unlock" aria-hidden="true"></i> | ||
59 | - | ||
60 | - {% else %} | ||
61 | - {{ round(t['level']*5)*'<i class="fa fa-star text-success" aria-hidden="true"></i>' + round(5-t['level']*5)*'<i class="fa fa-star-o" aria-hidden="true"></i>' }} | ||
62 | - {% end %} | ||
63 | - </a> | ||
64 | - {% end %} | ||
65 | - {% end %} | ||
66 | - </div> | 47 | + <div class="list-group"> |
48 | + {% for t in state %} | ||
49 | + {% if t['level'] is None %} | ||
50 | + <a class="list-group-item list-group-item-action disabled" href="#"> | ||
51 | + <div class="d-flex justify-content-start"> | ||
52 | + <div class="p-2"> | ||
53 | + {{ t['name'] }} | ||
54 | + </div> | ||
55 | + <div class="ml-auto p-2"> | ||
56 | + <i class="fa fa-lock" aria-hidden="true"></i> | ||
57 | + </div> | ||
58 | + </div> | ||
59 | + </a> | ||
60 | + {% else %} | ||
61 | + <a class="list-group-item list-group-item-action" href="/topic/{{t['ref']}}"> | ||
62 | + <div class="d-flex justify-content-start"> | ||
63 | + <div class="p-2"> | ||
64 | + {{ t['name'] }} | ||
65 | + </div> | ||
66 | + <div class="ml-auto p-2"> | ||
67 | + {% if t['level'] < 0.01 %} | ||
68 | + <i class="fa fa-unlock float-right" aria-hidden="true"></i> | ||
69 | + {% else %} | ||
70 | + {{ round(t['level']*5)*'<i class="fa fa-star text-success" aria-hidden="true"></i>' + round(5-t['level']*5)*'<i class="fa fa-star-o" aria-hidden="true"></i>' }} | ||
71 | + {% end %} | ||
72 | + </div> | ||
73 | + </div> | ||
74 | + </a> | ||
75 | + {% end %} | ||
76 | + {% end %} | ||
77 | + </div> <!-- list-group --> | ||
67 | </div> | 78 | </div> |
68 | 79 | ||
69 | <!-- Scripts --> | 80 | <!-- Scripts --> |