import random from contextlib import contextmanager # `with` statement in db sessions from datetime import datetime import logging # user installed libraries try: import bcrypt from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker except ImportError: logger.critical('Python package missing. See README.md for instructions.') sys.exit(1) # this project import questions from models import Student, Answer # setup logger for this module logger = logging.getLogger(__name__) # ============================================================================ # LearnApp - application logic # ============================================================================ class LearnApp(object): def __init__(self): self.factory = questions.QuestionFactory() self.factory.load_files(['questions.yaml'], 'demo') # FIXME self.online = {} # connect to database and check registered students db = 'students.db' # FIXME engine = create_engine(f'sqlite:///{db}', echo=False) # self.Session = scoped_session(sessionmaker(bind=engine)) self.Session = sessionmaker(bind=engine) try: with self.db_session() as s: n = s.query(Student).count() # filter(Student.id != '0'). except Exception as e: logger.critical('Database not usable.') raise e else: logger.info(f'Database has {n} students registered.') # ------------------------------------------------------------------------ def login(self, uid, try_pw): with self.db_session() as s: student = s.query(Student).filter(Student.id == uid).one_or_none() if student is None: logger.info(f'User "{uid}" does not exist.') return False # student does not exist or already loggeg in hashedtry = bcrypt.hashpw(try_pw.encode('utf-8'), student.password) if hashedtry != student.password: logger.info(f'User "{uid}" wrong password.') return False # wrong password # success self.online[uid] = { 'name': student.name, 'number': student.id, 'current': None, } print(self.online) return True # ------------------------------------------------------------------------ # logout def logout(self, uid): del self.online[uid] # FIXME save current question? # ------------------------------------------------------------------------ def get_current_question(self, uid): return self.online[uid].get('current', None) # ------------------------------------------------------------------------ def get_student_name(self, uid): return self.online[uid].get('name', '') # ------------------------------------------------------------------------ # given the currect state, generates a new question for the student def new_question_for(self, uid): # FIXME questions = list(self.factory) nextquestion = self.factory.generate(random.choice(questions)) print(nextquestion) self.online[uid]['current'] = nextquestion return nextquestion # ------------------------------------------------------------------------ # check answer and if correct returns new question, otherise returns None def check_answer(self, uid, answer): question = self.get_current_question(uid) print(question) print(answer) if question is not None: question['finish_time'] = datetime.now() grade = question.correct(answer) # correct answer with self.db_session() as s: s.add(Answer( ref=question['ref'], grade=question['grade'], starttime=str(question['start_time']), finishtime=str(question['finish_time']), student_id=uid)) s.commit() correct = grade > 0.99999 if correct: print('CORRECT') question = self.new_question_for(uid) question['start_time'] = datetime.now() return question else: print('WRONG') return None else: print('FIRST QUESTION') question = self.new_question_for(uid) question['start_time'] = datetime.now() return question # ------------------------------------------------------------------------ # 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() try: yield session session.commit() except: session.rollback() raise finally: session.close()