Commit 03210039c66bdcdc9623a43d47c8ffc8fe8e861d

Authored by Miguel Barao
1 parent 13773677
Exists in master and in 1 other branch dev

- main page ok.

@@ -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
@@ -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
@@ -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 -->