app.py 3.94 KB

import random
from contextlib import contextmanager  # `with` statement in db sessions

# libs
import bcrypt
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session

# this project
import questions
from models import Student


# ============================================================================
# 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
        engine = create_engine('sqlite:///{}'.format('students.db'), echo=False)
        self.Session = scoped_session(sessionmaker(bind=engine))
        try:
            with self.db_session() as s:
                n = s.query(Student).filter(Student.id != '0').count()
        except Exception as e:
            print('Database not usable.')
            raise e
        else:
            print('Database has {} students registered.'.format(n))

    # ------------------------------------------------------------------------
    def login_ok(self, uid, try_pw):
        print('LearnApp.login')

        with self.db_session() as s:
            student =  s.query(Student).filter(Student.id == uid).one_or_none()

        if student is None or student in self.online:
            # student does not exist
            return False

        # hashedtry = yield executor.submit(bcrypt.hashpw,
            # try_pw.encode('utf-8'), student.password)
        hashedtry = bcrypt.hashpw(try_pw.encode('utf-8'), student.password)

        if hashedtry != student.password:
            # wrong password
            return False

        # 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?

    # ------------------------------------------------------------------------
    # given the currect state, generates a new question for the student
    def new_question_for(self, uid):
        questions = list(self.factory)
        nextquestion = self.factory.generate(random.choice(questions))
        self.online[uid]['current'] = nextquestion
        return nextquestion

    # ------------------------------------------------------------------------
    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', '')

    # ------------------------------------------------------------------------
    # check answer and if correct returns new question, otherise returns None
    def check_answer(self, uid, answer):
        question = self.get_current_question(uid)
        print('------------------------------')
        print(question)
        print(answer)

        if question is not None:
            grade = question.correct(answer)       # correct answer
            correct = grade > 0.99999
            if correct:
                print('CORRECT')
                return self.new_question_for(uid)
            else:
                print('WRONG')
                return None
        else:
            print('FIRST QUESTION')
            return self.new_question_for(uid)

    # ------------------------------------------------------------------------
    # helper to manage db sessions using the `with` statement, for example
    #   with self.db_session() as s:  s.query(...)
    @contextmanager
    def db_session(self):
        try:
            yield self.Session()
        finally:
            self.Session.remove()