Commit 89e08ad8067a6de0b3394f3b35399d2db2dc1b7e

Authored by Miguel Barão
1 parent bbe90cad
Exists in master and in 1 other branch dev

- added missing comments.html template.

- added missing finished_topic.html template.
- support information "questions" again. They are useful to finish chapters.
knowledge.py
... ... @@ -15,29 +15,38 @@ logger = logging.getLogger(__name__)
15 15  
16 16 # ----------------------------------------------------------------------------
17 17 # kowledge state of each student....??
  18 +# Contains:
  19 +# state - dict of topics with state of unlocked topics
  20 +# deps - dependency graph
  21 +# topic_sequence - list with the order of recommended topics
18 22 # ----------------------------------------------------------------------------
19 23 class StudentKnowledge(object):
20 24 # =======================================================================
21 25 # methods that update state
22 26 # =======================================================================
23 27 def __init__(self, deps, state={}):
24   - # graph with topic dependencies shared between all students
25   - self.deps = deps
  28 + self.deps = deps # dependency graph shared among students
  29 + self.state = state # {'topic': {'level':0.5, 'date': datetime}, ...}
  30 + self.update_topic_levels() # forgetting factor
  31 + self.topic_sequence = self.recommend_topic_sequence() # ['a', 'b', ...]
  32 + self.unlock_topics()
26 33  
27   - # state only contains unlocked topics
28   - # {'topic_id': {'level':0.5, 'date': datetime}, ...}
29   - self.state = state
  34 + # ------------------------------------------------------------------------
  35 + # compute recommended sequence of topics ['a', 'b', ...]
  36 + # ------------------------------------------------------------------------
  37 + def recommend_topic_sequence(self):
  38 + return list(nx.topological_sort(self.deps))
30 39  
31   - # update state level based on the elapsed time (no dependencies... FIXME)
  40 + # ------------------------------------------------------------------------
  41 + # Updates the proficiency levels of the topics, with forgetting factor
  42 + # FIXME no dependencies are considered yet...
  43 + # ------------------------------------------------------------------------
  44 + def update_topic_levels(self):
32 45 now = datetime.now()
33   - for s in state.values():
  46 + for s in self.state.values():
34 47 dt = now - s['date']
35 48 s['level'] *= 0.8 ** dt.days # forgetting factor 0.95
36 49  
37   - # compute recommended sequence of topics ['a', 'b', ...]
38   - self.topic_sequence = list(nx.topological_sort(self.deps))
39   -
40   - self.unlock_topics()
41 50  
42 51 # ------------------------------------------------------------------------
43 52 # Unlock topics whose dependencies are satisfied (> min_level)
... ... @@ -86,7 +95,8 @@ class StudentKnowledge(object):
86 95 try:
87 96 self.current_question = self.questions.pop(0) # FIXME crash if empty
88 97 except IndexError:
89   - self.finish_topic()
  98 + # self.current_question = None
  99 + self.finish_topic() # FIXME if no questions, what should be done?
90 100 return False
91 101 else:
92 102 self.current_question['start_time'] = datetime.now()
... ...
learnapp.py
... ... @@ -104,7 +104,7 @@ class LearnApp(object):
104 104 # ------------------------------------------------------------------------
105 105 def check_answer(self, uid, answer):
106 106 knowledge = self.online[uid]['state']
107   - grade = knowledge.check_answer(answer)
  107 + grade = knowledge.check_answer(answer) # also moves to next question
108 108  
109 109 if knowledge.get_current_question() is None:
110 110 # finished topic, save into database
... ... @@ -129,6 +129,7 @@ class LearnApp(object):
129 129 a.date = date
130 130  
131 131 s.add(a)
  132 + logger.debug(f'Saved topic "{finished_topic}" into database')
132 133  
133 134 # save answered questions from finished_questions list
134 135 s.add_all([
... ... @@ -140,7 +141,7 @@ class LearnApp(object):
140 141 student_id=uid,
141 142 topic_id=finished_topic)
142 143 for q in finished_questions])
143   -
  144 + logger.debug(f'Saved {len(finished_questions)} answers into database')
144 145 return grade
145 146  
146 147 # ------------------------------------------------------------------------
... ...
serve.py
... ... @@ -169,9 +169,9 @@ class QuestionHandler(BaseHandler):
169 169 'numeric-interval': 'question-text.html',
170 170 'textarea': 'question-textarea.html',
171 171 # -- information panels --
172   - # 'information': 'question-information.html',
173   - # 'info': 'question-information.html',
174   - # 'success': 'question-success.html',
  172 + 'information': 'question-information.html',
  173 + 'info': 'question-information.html',
  174 + 'success': 'question-success.html',
175 175 # 'warning': '', FIXME
176 176 # 'warn': '', FIXME
177 177 # 'alert': '', FIXME
... ... @@ -270,15 +270,19 @@ def main():
270 270 logging.info('====================================================')
271 271  
272 272 # --- start application
273   - logging.info('Starting App.')
274   - learnapp = LearnApp(arg.conffile[0])
  273 + logging.info('Starting App')
  274 + try:
  275 + learnapp = LearnApp(arg.conffile[0])
  276 + except Exception as e:
  277 + logging.critical('Failed to start backend application')
  278 + raise e
275 279  
276 280 # --- create web application
277 281 logging.info('Starting Web App (tornado)')
278 282 try:
279 283 webapp = WebApplication(learnapp, debug=arg.debug)
280 284 except Exception as e:
281   - logging.critical('Failed to start application.')
  285 + logging.critical('Failed to start web application.')
282 286 raise e
283 287  
284 288 # --- create webserver
... ...
templates/comments.html 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +{% autoescape %}
  2 +
  3 +{% if comments %}
  4 +<div class="card border-danger mb-3">
  5 + <div class="card-body text-danger">
  6 + <p class="card-text">{{md(comments)}}</p>
  7 + </div>
  8 +</div>
  9 +{% end %}
... ...
templates/finished_topic.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<img src="/static/trophy.svg" alt="trophy" class="img-fluid mx-auto d-block" width="35%">
0 2 \ No newline at end of file
... ...
tools.py
... ... @@ -137,18 +137,18 @@ def load_yaml(filename, default=None):
137 137 try:
138 138 f = open(filename, 'r', encoding='utf-8')
139 139 except FileNotFoundError:
140   - logger.error(f'Can\'t open "{filename}": not found.')
  140 + logger.error(f'Can\'t open "{filename}": not found')
141 141 except PermissionError:
142   - logger.error(f'Can\'t open "{filename}": no permission.')
  142 + logger.error(f'Can\'t open "{filename}": no permission')
143 143 except IOError:
144   - logger.error(f'Can\'t open file "{filename}".')
  144 + logger.error(f'Can\'t open file "{filename}"')
145 145 else:
146 146 with f:
147 147 try:
148 148 default = yaml.load(f)
149 149 except yaml.YAMLError as e:
150 150 mark = e.problem_mark
151   - logger.error(f'In YAML file "{filename}" near line {mark.line}, column {mark.column+1}.')
  151 + logger.error(f'In YAML file "{filename}" near line {mark.line}, column {mark.column+1}')
152 152 finally:
153 153 return default
154 154  
... ...