From 4c5146e66cc5c1a6f3584e5a60f98c6aa043ff84 Mon Sep 17 00:00:00 2001 From: Miguel BarĂ£o Date: Wed, 29 Dec 2021 13:34:03 +0000 Subject: [PATCH] serving questions now working (except review if not in cache) --- perguntations/app.py | 24 +++++++++--------------- perguntations/main.py | 2 +- perguntations/serve.py | 44 ++++++++++++++++++++++++++------------------ perguntations/testfactory.py | 9 +-------- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/perguntations/app.py b/perguntations/app.py index 7808388..82dab7e 100644 --- a/perguntations/app.py +++ b/perguntations/app.py @@ -71,12 +71,15 @@ class App(): # ------------------------------------------------------------------------ def _db_setup(self) -> None: - logger.debug('Checking database...') - - # connect to database and check registered students + ''' + Create database engine and checks for admin and students + ''' dbfile = os.path.expanduser(self._testfactory['database']) + logger.info('Checking database "%s"...', dbfile) if not os.path.exists(dbfile): raise AppException('No database. Use "initdb" to create.') + + # connect to database and check for admin & registered students self._engine = create_engine(f'sqlite:///{dbfile}', future=True) try: with Session(self._engine, future=True) as session: @@ -92,20 +95,11 @@ class App(): msg = f'Database "{dbfile}" unusable.' logger.error(msg) raise AppException(msg) from None - - logger.info('Database "%s" has %d students.', dbfile, len(dbstudents)) + logger.info('Database has %d students.', len(dbstudents)) self._students = {uid: {'name': name, 'state': 'offline', 'test': None} for uid, name in dbstudents} - # self._students = {} - # for uid, name in dbstudents: - # self._students[uid] = { - # 'name': name, - # 'state': 'offline', # offline, allowed, waiting, online - # 'test': None - # } - # ------------------------------------------------------------------------ async def login(self, uid: str, password: str, headers: dict) -> Optional[str]: ''' @@ -172,7 +166,7 @@ class App(): try: testconf = load_yaml(filename) testconf['testfile'] = filename - except (IOError, yaml.YAMLError) as exc: + except (OSError, yaml.YAMLError) as exc: msg = f'Cannot read test configuration "{filename}"' logger.error(msg) raise AppException(msg) from exc @@ -534,7 +528,7 @@ class App(): with open(filename, 'r', encoding='utf-8') as file: allowed = {line.strip() for line in file} allowed.discard('') - except IOError as exc: + except OSError as exc: error_msg = f'Cannot read file {filename}' logger.critical(error_msg) raise AppException(error_msg) from exc diff --git a/perguntations/main.py b/perguntations/main.py index 424e2e9..67fbbf0 100644 --- a/perguntations/main.py +++ b/perguntations/main.py @@ -71,7 +71,7 @@ def get_logger_config(debug=False) -> dict: path = os.path.expanduser(os.environ.get('XDG_CONFIG_HOME', '~/.config/')) try: return load_yaml(os.path.join(path, APP_NAME, file)) - except IOError: + except OSError: print('Using default logger configuration...') if debug: diff --git a/perguntations/serve.py b/perguntations/serve.py index 8f02ecb..c277c1e 100644 --- a/perguntations/serve.py +++ b/perguntations/serve.py @@ -360,6 +360,7 @@ class FileHandler(BaseHandler): Handles static files from questions like images, etc. ''' + _filecache = {} @tornado.web.authenticated async def get(self): @@ -371,35 +372,42 @@ class FileHandler(BaseHandler): ref = self.get_query_argument('ref', None) image = self.get_query_argument('image', None) logger.debug('GET /file (ref=%s, image=%s)', ref, image) + + if ref is None or image is None: + return + content_type = mimetypes.guess_type(image)[0] - if uid != '0': - test = self.testapp.get_student_test(uid) - else: - logger.error('FIXME Cannot serve images for review.') - raise tornado.web.HTTPError(404) # FIXME admin + if (ref, image) in self._filecache: + logger.debug('using cached file') + self.write(self._filecache[(ref, image)]) + if content_type is not None: + self.set_header("Content-Type", content_type) + await self.flush() + return - if test is None: + try: + test = self.testapp.get_test(uid) + except KeyError: + logger.warning('Could not get test to serve image file') raise tornado.web.HTTPError(404) # Not Found for question in test['questions']: # search for the question that contains the image if question['ref'] == ref: - filepath = path.join(question['path'], b'public', image) + filepath = path.join(question['path'], 'public', image) + try: - file = open(filepath, 'rb') - except FileNotFoundError: - logger.error('File not found: %s', filepath) - except PermissionError: - logger.error('No permission: %s', filepath) + with open(filepath, 'rb') as file: + data = file.read() except OSError: - logger.error('Error opening file: %s', filepath) - else: - data = file.read() - file.close() + logger.error('Error reading file "%s"', filepath) + break + self._filecache[(ref, image)] = data + self.write(data) + if content_type is not None: self.set_header("Content-Type", content_type) - self.write(data) - await self.flush() + await self.flush() break diff --git a/perguntations/testfactory.py b/perguntations/testfactory.py index 8272bc2..50cabb8 100644 --- a/perguntations/testfactory.py +++ b/perguntations/testfactory.py @@ -99,13 +99,6 @@ class TestFactory(dict): if question['ref'] in qrefs: question.update(zip(('path', 'filename', 'index'), path.split(fullpath) + (i,))) - # if question['type'] == 'code' and 'server' not in question: - # try: - # question['server'] = self['jobe_server'] - # except KeyError as exc: - # msg = f'Missing JOBE server in "{question["ref"]}"' - # raise TestFactoryException(msg) from exc - self['question_factory'][question['ref']] = QFactory(QDict(question)) qmissing = qrefs.difference(set(self['question_factory'].keys())) @@ -338,4 +331,4 @@ class TestFactory(dict): # ------------------------------------------------------------------------ def __repr__(self): testsettings = '\n'.join(f' {k:14s}: {v}' for k, v in self.items()) - return '{\n' + testsettings + '\n}' + return 'TestFactory({\n' + testsettings + '\n})' -- libgit2 0.21.2