diff --git a/BUGS.md b/BUGS.md index 9b4ac21..b73c37f 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,7 +1,7 @@ BUGS: -- gravar evolucao na bd no final de cada topico. +- melhorar markdown das tabelas. - servir imagens/ficheiros. - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores. @@ -29,6 +29,7 @@ TODO: FIXED: +- gravar evolucao na bd no final de cada topico. - submeter questoes radio, da erro se nao escolher nenhuma opção. - indentação da primeira linha de código não funciona. - markdown com o mistune. diff --git a/knowledge.py b/knowledge.py index 0b0669d..0da2651 100644 --- a/knowledge.py +++ b/knowledge.py @@ -24,7 +24,7 @@ class StudentKnowledge(object): # graph with topic dependencies shared between all students self.deps = deps - # state only contains information on unlocked topics + # state only contains unlocked topics # {'topic_id': {'level':0.5, 'date': datetime}, ...} self.state = state @@ -32,7 +32,7 @@ class StudentKnowledge(object): now = datetime.now() for s in state.values(): dt = now - s['date'] - s['level'] *= 0.975 ** dt.days + s['level'] *= 0.975 ** dt.days # forgetting factor # compute recommended sequence of topics ['a', 'b', ...] self.topic_sequence = list(nx.topological_sort(self.deps)) @@ -43,44 +43,22 @@ class StudentKnowledge(object): # Unlock topics whose dependencies are satisfied (> min_level) # ------------------------------------------------------------------------ def unlock_topics(self): - min_level = 0.01 # minimum level to unlock + # minimum level that the dependencies of a topic must have + # for the topic to be unlocked. + min_level = 0.01 + for topic in self.topic_sequence: if topic not in self.state: # if locked pred = self.deps.predecessors(topic) - if all(d in self.state and self.state[d]['level'] > min_level for d in pred): # dependencies done + if all(d in self.state and self.state[d]['level'] > min_level for d in pred): # and all dependencies are done self.state[topic] = { - 'level': 0.0, + 'level': 0.0, # then unlock 'date': datetime.now() } logger.debug(f'unlocked {topic}') # ------------------------------------------------------------------------ - # Recommends a topic to practice/learn from the state. - # FIXME untested - # ------------------------------------------------------------------------ - def recommended_topic(self): - return min(self.state.items(), key=lambda x: x[1]['level'])[0] - - - # if not topic: - # for topic in self.topic_sequence: - # unlocked = topic in self.state - # needs_work = unlocked and self.state[topic]['level'] < 0.8 - # factory = self.deps.node[topic]['factory'] - # if factory and (not unlocked or needs_work): - # break - - # use given topic if possible - # else: - # unlocked = topic in self.state - # factory = self.deps.node[topic]['factory'] - # if not factory or not unlocked: - # logger.debug(f'Can\'t start topic "{topic}".') - # return - - - # ------------------------------------------------------------------------ # Start a new topic. If not provided, gets a recommendation. # questions: list of generated questions to do in the topic # finished_questions: [] will contain correctly answered questions @@ -90,7 +68,7 @@ class StudentKnowledge(object): logger.debug(f'StudentKnowledge.init_topic({topic})') if not topic: - topic = self.recommended_topic() + topic = self.get_recommended_topic() self.current_topic = topic logger.info(f'Topic set to "{topic}"') @@ -106,6 +84,22 @@ class StudentKnowledge(object): self.current_question['start_time'] = datetime.now() # ------------------------------------------------------------------------ + # The topic has finished and there are no more questions. + # The topic level is updated in state and unlocks are performed. + # The current topic is unchanged. + # ------------------------------------------------------------------------ + def finish_topic(self): + logger.debug(f'StudentKnowledge.finish_topic({self.current_topic})') + + self.current_question = None + self.state[self.current_topic] = { + 'level': 1.0, + 'date': datetime.now() + } + self.unlock_topics() + + + # ------------------------------------------------------------------------ # returns the current question with correction, time and comments updated # ------------------------------------------------------------------------ def check_answer(self, answer): @@ -124,13 +118,7 @@ class StudentKnowledge(object): try: self.current_question = self.questions.pop(0) # FIXME empty? except IndexError: - # finished topic, no more questions - self.current_question = None - self.state[self.current_topic] = { - 'level': 1.0, - 'date': datetime.now() - } - self.unlock_topics() + self.finish_topic() else: self.current_question['start_time'] = datetime.now() @@ -141,7 +129,7 @@ class StudentKnowledge(object): # returns answered and corrected question - return q + return grade # ======================================================================== @@ -152,6 +140,9 @@ class StudentKnowledge(object): def get_current_question(self): return self.current_question + def get_finished_questions(self): + return self.finished_questions + # ------------------------------------------------------------------------ def get_current_topic(self): return self.current_topic @@ -172,3 +163,16 @@ class StudentKnowledge(object): def get_topic_progress(self): return len(self.finished_questions) / (1 + len(self.finished_questions) + len(self.questions)) + # ------------------------------------------------------------------------ + def get_topic_level(self, topic): + return self.state[topic]['level'] + + # ------------------------------------------------------------------------ + def get_topic_date(self, topic): + return self.state[topic]['date'] + + # ------------------------------------------------------------------------ + # Recommends a topic to practice/learn from the state. + # ------------------------------------------------------------------------ + def get_recommended_topic(self): # FIXME untested + return min(self.state.items(), key=lambda x: x[1]['level'])[0] diff --git a/learnapp.py b/learnapp.py index b64d9f2..0c3413e 100644 --- a/learnapp.py +++ b/learnapp.py @@ -69,8 +69,8 @@ class LearnApp(object): } self.online[uid] = { - 'name': student.name, 'number': student.id, + 'name': student.name, 'state': StudentKnowledge(self.deps, state=state) } return True @@ -79,29 +79,28 @@ class LearnApp(object): # logout # ------------------------------------------------------------------------ def logout(self, uid): - state = self.online[uid]['state'].state # dict {node:level,...} - - # save state to database - with self.db_session(autoflush=False) as s: - - # update existing associations and remove from state dict - for a in s.query(StudentTopic).filter_by(student_id=uid): - if a.topic_id in state: - d = state.pop(a.topic_id) - a.level = d['level'] #state.pop(a.topic_id) # update - a.date = str(d['date']) - s.add(a) - - # insert the remaining ones - u = s.query(Student).get(uid) - for n,d in state.items(): - a = StudentTopic(level=d['level'], date=str(d['date'])) - t = s.query(Topic).get(n) - if t is None: # create if topic doesn't exist yet - t = Topic(id=n) - a.topic = t - u.topics.append(a) - s.add(a) + # state = self.online[uid]['state'].state # dict {node:level,...} + # # save topics state to database + # with self.db_session(autoflush=False) as s: + + # # update existing associations and remove from state dict + # for a in s.query(StudentTopic).filter_by(student_id=uid): + # if a.topic_id in state: + # d = state.pop(a.topic_id) + # a.level = d['level'] #state.pop(a.topic_id) # update + # a.date = str(d['date']) + # s.add(a) + + # # insert the remaining ones + # u = s.query(Student).get(uid) + # for n,d in state.items(): + # a = StudentTopic(level=d['level'], date=str(d['date'])) + # t = s.query(Topic).get(n) + # if t is None: # create if topic doesn't exist yet + # t = Topic(id=n) + # a.topic = t + # u.topics.append(a) + # s.add(a) del self.online[uid] logger.info(f'User "{uid}" logged out') @@ -116,26 +115,51 @@ class LearnApp(object): with self.db_session() as s: u = s.query(Student).get(uid) u.password = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + logger.info(f'User "{uid}" changed password') return True # ------------------------------------------------------------------------ - # check answer and if correct returns new question, otherwise returns None + # checks answer (updating student state) and returns grade. # ------------------------------------------------------------------------ def check_answer(self, uid, answer): knowledge = self.online[uid]['state'] - q = knowledge.check_answer(answer) + grade = knowledge.check_answer(answer) + + # if finished topic, save in database + if knowledge.get_current_question() is None: + finished_topic = knowledge.get_current_topic() + level = knowledge.get_topic_level(finished_topic) + date = str(knowledge.get_topic_date(finished_topic)) + + with self.db_session(autoflush=False) as s: + # save questions from finished_questions list + s.add_all([ + Answer( + ref=q['ref'], + grade=q['grade'], + starttime=str(q['start_time']), + finishtime=str(q['finish_time']), + student_id=uid) + for q in knowledge.get_finished_questions()]) + + # save topic + a = s.query(StudentTopic).filter_by(student_id=uid, topic_id=finished_topic).one_or_none() + if a is None: + # insert new studenttopic into database + u = s.query(Student).get(uid) + a = StudentTopic(level=level, date=date) + t = s.query(Topic).get(finished_topic) + a.topic = t + u.topics.append(a) + s.add(a) + else: + # update studenttopic in database + a.level = level + a.date = date + s.add(a) - with self.db_session() as s: - s.add(Answer( - ref=q['ref'], - grade=q['grade'], - starttime=str(q['start_time']), - finishtime=str(q['finish_time']), - student_id=uid)) - s.commit() - - return q['grade'] + return grade # ------------------------------------------------------------------------ # Start new topic diff --git a/serve.py b/serve.py index 90694a5..c680130 100755 --- a/serve.py +++ b/serve.py @@ -119,7 +119,8 @@ class RootHandler(BaseHandler): self.render('maintopics.html', uid=uid, name=self.learn.get_student_name(uid), - state=self.learn.get_student_state(uid) + state=self.learn.get_student_state(uid), + title=self.learn.get_title() ) # ---------------------------------------------------------------------------- diff --git a/templates/maintopics.html b/templates/maintopics.html index 9a2eb44..f88e862 100644 --- a/templates/maintopics.html +++ b/templates/maintopics.html @@ -61,14 +61,14 @@
-