From ed34db4c3f94e62db9d0a52efa31a21acee63133 Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Tue, 28 Feb 2017 16:44:35 +0000 Subject: [PATCH] - save topic state on logout --- BUGS.md | 1 + app.py | 29 ++++++++++++++++++++++++++--- knowledge.py | 2 ++ models.py | 4 ++-- questions.py | 143 +++++++++++------------------------------------------------------------------------------------------------------------------------------------ templates/learn.html | 12 ++++++------ 6 files changed, 48 insertions(+), 143 deletions(-) diff --git a/BUGS.md b/BUGS.md index dd8456d..a94cade 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,5 +1,6 @@ BUGS: +- de vez em quando o browser é redireccionado para /question em vez de fazer um post?? não percebo... - load/save the knowledge state of the student - se students.db não existe, rebenta. - database hardcoded in LearnApp. diff --git a/app.py b/app.py index 7930ecb..84d642c 100644 --- a/app.py +++ b/app.py @@ -66,8 +66,31 @@ class LearnApp(object): # ------------------------------------------------------------------------ # logout def logout(self, uid): + state = self.online[uid]['state'].state # dict {node:level,...} + print(state) + + with self.db_session(autoflush=False) as s: + + # update existing associations and remove from state dict + aa = s.query(StudentTopic).filter_by(student_id=uid).all() + for a in aa: + print('update ', a) + a.level = state.pop(a.topic_id) # update + s.add_all(aa) + + # insert the remaining ones + u = s.query(Student).get(uid) + for n,l in state.items(): + a = StudentTopic(level=l) + t = s.query(Topic).get(n) + if t is None: # create if topic doesn't exist yet + t = Topic(n) + a.topic = t + u.topics.append(a) + s.add(a) + + del self.online[uid] logger.info(f'User "{uid}" logged out') - del self.online[uid] # FIXME save current state # ------------------------------------------------------------------------ def get_student_name(self, uid): @@ -105,8 +128,8 @@ class LearnApp(object): # helper to manage db sessions using the `with` statement, for example # with self.db_session() as s: s.query(...) @contextmanager - def db_session(self): - session = self.Session() + def db_session(self, **kw): + session = self.Session(**kw) try: yield session session.commit() diff --git a/knowledge.py b/knowledge.py index d3ffa15..76e2764 100644 --- a/knowledge.py +++ b/knowledge.py @@ -24,6 +24,8 @@ class Knowledge(object): self.seq = nx.topological_sort(self.depgraph) self.topic = None + self.state = {'a': 3, 'b': 4} + def get_current_question(self): return self.current_question diff --git a/models.py b/models.py index ecf163e..f18342d 100644 --- a/models.py +++ b/models.py @@ -74,5 +74,5 @@ class Topic(Base): # --- students = relationship('StudentTopic', back_populates='topic') - - + def __init__(self, id): + self.id = id diff --git a/questions.py b/questions.py index 99f0d80..09ef653 100644 --- a/questions.py +++ b/questions.py @@ -1,19 +1,18 @@ - -# We start with an empty QuestionFactory() that will be populated with -# question generators that we can load from YAML files. -# To generate an instance of a question we use the method generate(ref) where +# QFactory is a class that can generate question instances, e.g. by shuffling +# options, running a script to generate the question, etc. +# +# To generate an instance of a question we use the method generate() where # the argument is the reference of the question we wish to produce. # # Example: # -# # read everything from question files -# factory = QuestionFactory() -# factory.load_files(['file1.yaml', 'file1.yaml'], '/path/to') -# -# question = factory.generate('some_ref') +# # read question from file +# qdict = tools.load_yaml(filename) +# qfactory = QFactory(question) +# question = qfactory.generate() # # # experiment answering one question and correct it -# question['answer'] = 42 # insert answer +# question.updateAnswer('42') # insert answer # grade = question.correct() # correct answer # An instance of an actual question is an object that inherits from Question() @@ -28,7 +27,6 @@ import random import re -# import subprocess from os import path import logging import sys @@ -419,8 +417,8 @@ class QFactory(object): if q['type'] == 'generator': logger.debug('Running script to generate question "{0}".'.format(q['ref'])) q.setdefault('arg', '') # optional arguments will be sent to stdin - print(q['path']) - print(q['script']) + # print(q['path']) + # print(q['script']) script = path.join(q['path'], q['script']) out = run_script(script=script, stdin=q['arg']) q.update(out) @@ -450,122 +448,3 @@ class QFactory(object): logger.debug('returning') return qinstance - -# =========================================================================== -# This class contains a pool of questions generators from which particular -# Question() instances are generated using QuestionsFactory.generate(ref). -# =========================================================================== -# class QuestionFactory(dict): -# # Depending on the type of question, a different question class will be -# # instantiated. All these classes derive from the base class `Question`. -# _types = { -# 'radio' : QuestionRadio, -# 'checkbox' : QuestionCheckbox, -# 'text' : QuestionText, -# 'text_regex': QuestionTextRegex, -# 'text_numeric': QuestionTextNumeric, -# 'textarea' : QuestionTextArea, -# # informative panels -# 'information': QuestionInformation, -# 'warning' : QuestionInformation, -# 'alert' : QuestionInformation, -# } - -# # ----------------------------------------------------------------------- -# def __init__(self, questions=None): -# super().__init__() -# if isinstance(questions, dict): -# self.add(questions) -# elif isinstance(questions, str): -# self.load_file(questions) - -# # ----------------------------------------------------------------------- -# # Add single question provided in a dictionary. -# # After this, each question will have at least 'ref' and 'type' keys. -# # ----------------------------------------------------------------------- -# def add(self, question): -# # if ref missing try ref='/path/file.yaml:3' -# try: -# question.setdefault('ref', question['filename'] + ':' + str(question['index'])) -# except KeyError: -# logger.error('Missing "ref". Cannot add question to the pool.') -# return - -# # check duplicate references -# if question['ref'] in self: -# logger.error('Duplicate reference "{0}". Replacing the original one!'.format(question['ref'])) - -# question.setdefault('type', 'information') - -# self[question['ref']] = question -# logger.debug('Added question "{0}" to the pool.'.format(question['ref'])) - -# # ----------------------------------------------------------------------- -# # load single YAML questions file -# # ----------------------------------------------------------------------- -# def load_file(self, filename, questions_dir=''): -# f = path.normpath(path.join(questions_dir, filename)) -# questions = load_yaml(f, default=[]) - -# n = 0 -# for i, q in enumerate(questions): -# if isinstance(q, dict): -# q.update({ -# 'filename': filename, -# 'path': questions_dir, -# 'index': i # position in the file, 0 based -# }) -# self.add(q) # add question -# n += 1 # counter -# else: -# logger.error('Question index {0} from file {1} is not a dictionary. Skipped!'.format(i, filename)) - -# logger.info('Loaded {0} questions from "{1}".'.format(n, filename)) - -# # ----------------------------------------------------------------------- -# # load multiple YAML question files -# # ----------------------------------------------------------------------- -# def load_files(self, files, questions_dir=''): -# for filename in files: -# self.load_file(filename, questions_dir) - -# # ----------------------------------------------------------------------- -# # Given a ref returns an instance of a descendent of Question(), -# # i.e. a question object (radio, checkbox, ...). -# # ----------------------------------------------------------------------- -# def generate(self, ref): - -# # Shallow copy so that script generated questions will not replace -# # the original generators -# q = self[ref].copy() - -# # If question is of generator type, an external program will be run -# # which will print a valid question in yaml format to stdout. This -# # output is then converted to a dictionary and `q` becomes that dict. -# if q['type'] == 'generator': -# logger.debug('Running script to generate question "{0}".'.format(q['ref'])) -# q.setdefault('arg', '') # optional arguments will be sent to stdin -# script = path.normpath(path.join(q['path'], q['script'])) -# out = run_script(script=script, stdin=q['arg']) -# try: -# q.update(out) -# except: -# q.update({ -# 'type': 'alert', -# 'title': 'Erro interno', -# 'text': 'Ocorreu um erro a gerar esta pergunta.' -# }) -# # The generator was replaced by a question but not yet instantiated - -# # Finally we create an instance of Question() -# try: -# qinstance = self._types[q['type']](q) # instance with correct class -# except KeyError as e: -# logger.error('Unknown question type "{0}" in "{1}:{2}".'.format(q['type'], q['filename'], q['ref'])) -# raise e -# except: -# logger.error('Failed to create question "{0}" from file "{1}".'.format(q['ref'], q['filename'])) -# else: -# logger.debug('Generated question "{}".'.format(ref)) -# return qinstance - diff --git a/templates/learn.html b/templates/learn.html index 0b0ff44..5f056ac 100644 --- a/templates/learn.html +++ b/templates/learn.html @@ -111,13 +111,13 @@ function updateQuestion(response){ getQuestion(); } }); - var audio = new Audio('/static/sounds/correct.mp3'); - audio.play(); + // var audio = new Audio('/static/sounds/correct.mp3'); + // audio.play(); $('#question_div').animateCSS('pulse'); break; case "shake": - var audio = new Audio('/static/sounds/wrong.mp3'); - audio.play(); + // var audio = new Audio('/static/sounds/wrong.mp3'); + // audio.play(); $('#question_div').animateCSS('shake'); break; } @@ -138,8 +138,8 @@ function getQuestion() { } $(document).ready(function() { - var audio = new Audio('/static/sounds/intro.mp3'); - audio.play(); + // var audio = new Audio('/static/sounds/intro.mp3'); + // audio.play(); $("#submit").click(getQuestion); }); -- libgit2 0.21.2