import logging from os import path import sqlite3 import bcrypt import test import database logger = logging.getLogger(__name__) # ============================================================================ # Application # ============================================================================ class App(object): def __init__(self, filename, conf): # online = { # uid1: { # 'student': {'number': 123, 'name': john}, # 'test': ... # } # uid2: {...} # } logger.info('============= Running perguntations =============') self.online = dict() # {uid: {'student':{}}} self.allowed = set([]) # '0' is hardcoded to allowed elsewhere self.testfactory = test.TestFactory(filename, conf=conf) self.db = database.Database(self.testfactory['database']) # FIXME try: n = self.db.get_count_students() except sqlite3.OperationalError as e: logger.critical('Database not usable {}.'.format(self.db.db)) raise e else: logger.info('Database has {} students registered.'.format(n)) def exit(self): logger.critical('----------- !!! Server terminated !!! -----------') def login(self, uid, try_pw): if uid not in self.allowed and uid != '0': # not allowed logger.warning('Student {}: not allowed to login.'.format(uid)) return False student = self.db.get_student(uid) if student is None: # not found logger.warning('Student {}: not found in database.'.format(uid)) return False # uid found in database name, pw = student if pw == '': # update password on first login hashed_pw = bcrypt.hashpw(try_pw.encode('utf-8'), bcrypt.gensalt()) self.db.update_password(uid, hashed_pw) logger.warning('Student {}: first login, password updated.'.format(uid)) elif bcrypt.hashpw(try_pw.encode('utf-8'), pw) != pw: # wrong password logger.info('Student {}: wrong password.'.format(uid)) return False # success self.allowed.discard(uid) if uid in self.online: logger.warning('Student {}: already logged in.'.format(uid)) else: self.online[uid] = {'student': {'name': name, 'number': uid}} logger.info('Student {}: logged in.'.format(uid)) return True def logout(self, uid): if uid not in self.online: # this should never happen logger.error('Student {}: tried to logout, but is not logged in.'.format(uid)) return False else: logger.info('Student {}: logged out.'.format(uid)) del self.online[uid] return True def generate_test(self, uid): if uid in self.online: logger.info('Student {}: generating new test.'.format(uid)) student_id = self.online[uid]['student'] self.online[uid]['test'] = self.testfactory.generate(student_id) return self.online[uid]['test'] else: logger.error('Student {}: offline, can''t generate test'.format(uid)) return None def correct_test(self, uid, ans): t = self.online[uid]['test'] t.update_answers(ans) grade = t.correct() logger.info('Student {0}: finished with {1} points.'.format(uid, grade)) if t['save_answers']: fname = ' -- '.join((t['student']['number'], t['ref'], str(t['finish_time']))) + '.json' fpath = path.abspath(path.join(t['answers_dir'], fname)) t.save_json(fpath) self.db.save_test(t) self.db.save_questions(t) return grade # --- helpers (getters) def get_student_name(self, uid): return self.online[uid]['student']['name'] def get_test(self, uid, default=None): return self.online[uid].get('test', default) def get_test_qtypes(self, uid): return {q['ref']:q['type'] for q in self.online[uid]['test']['questions']} def get_student_grades_from_all_tests(self, uid): return self.db.get_student_grades_from_all_tests(uid) # def get_student_grades_from_test(self, uid, testid): # return self.db.get_student_grades_from_test(uid, testid) # def get_online_students(self): # # list of ('uid', 'name', 'start_time') sorted by start time # return sorted( # ((k, v['student']['name'], str(v.get('test', {}).get('start_time', '---'))) for k,v in self.online.items() if k != '0'), # key=lambda k: k[2] # sort key # ) def get_online_students(self): # {'123': '2016-12-02 12:04:12.344243', ...} return [(k, v['student']['name'], str(v.get('test', {}).get('start_time', '---'))) for k,v in self.online.items() if k != '0'] def get_offline_students(self): # list of ('uid', 'name') sorted by number return sorted((s[:2] for s in self.db.get_all_students() if s[0] not in self.online), key=lambda k: k[0]) def get_all_students(self): # list of ('uid', 'name') sorted by number return sorted((s[:2] for s in self.db.get_all_students() if s[0] != '0'), key=lambda k: k[0]) def get_students_state(self): # {'123': {'name': 'John', 'start_time':'', 'grades':[10.2, 13.1], ...}} d = {} for s in self.db.get_all_students(): uid, name, pw = s if uid == '0': continue d[uid] = {'name': name} d[uid]['allowed'] = uid in self.allowed d[uid]['online'] = uid in self.online d[uid]['start_time'] = self.online.get(uid, {}).get('test', {}).get('start_time','') d[uid]['password_defined'] = pw != '' d[uid]['grades'] = self.db.get_student_grades_from_test(uid, self.testfactory['ref']) d[uid]['ip_address'] = self.online.get(uid, {}).get('student', {}).get('ip_address','') d[uid]['user_agent'] = self.online.get(uid, {}).get('student', {}).get('user_agent','') return d # def get_this_students_grades(self): # # list of ('uid', 'name') sorted by number # return self.db.get_students_grades(self.testfactory['ref']) def get_allowed_students(self): # set of 'uid' allowed to login return self.allowed # --- helpers (change state) def allow_student(self, uid): self.allowed.add(uid) logger.info('Student {}: allowed to login.'.format(uid)) def deny_student(self, uid): self.allowed.discard(uid) logger.info('Student {}: denied to login'.format(uid)) def reset_password(self, uid): self.db.reset_password(uid) logger.info('Student {}: password reset to ""'.format(uid)) def set_user_agent(self, uid, user_agent=''): self.online[uid]['student']['user_agent'] = user_agent def set_user_ip(self, uid, ipaddress=''): self.online[uid]['student']['ip_address'] = ipaddress