Commit 4c5146e66cc5c1a6f3584e5a60f98c6aa043ff84
1 parent
7ce13d39
Exists in
master
and in
1 other branch
serving questions now working (except review if not in cache)
Showing
4 changed files
with
37 additions
and
42 deletions
Show diff stats
perguntations/app.py
... | ... | @@ -71,12 +71,15 @@ class App(): |
71 | 71 | |
72 | 72 | # ------------------------------------------------------------------------ |
73 | 73 | def _db_setup(self) -> None: |
74 | - logger.debug('Checking database...') | |
75 | - | |
76 | - # connect to database and check registered students | |
74 | + ''' | |
75 | + Create database engine and checks for admin and students | |
76 | + ''' | |
77 | 77 | dbfile = os.path.expanduser(self._testfactory['database']) |
78 | + logger.info('Checking database "%s"...', dbfile) | |
78 | 79 | if not os.path.exists(dbfile): |
79 | 80 | raise AppException('No database. Use "initdb" to create.') |
81 | + | |
82 | + # connect to database and check for admin & registered students | |
80 | 83 | self._engine = create_engine(f'sqlite:///{dbfile}', future=True) |
81 | 84 | try: |
82 | 85 | with Session(self._engine, future=True) as session: |
... | ... | @@ -92,20 +95,11 @@ class App(): |
92 | 95 | msg = f'Database "{dbfile}" unusable.' |
93 | 96 | logger.error(msg) |
94 | 97 | raise AppException(msg) from None |
95 | - | |
96 | - logger.info('Database "%s" has %d students.', dbfile, len(dbstudents)) | |
98 | + logger.info('Database has %d students.', len(dbstudents)) | |
97 | 99 | |
98 | 100 | self._students = {uid: {'name': name, 'state': 'offline', 'test': None} |
99 | 101 | for uid, name in dbstudents} |
100 | 102 | |
101 | - # self._students = {} | |
102 | - # for uid, name in dbstudents: | |
103 | - # self._students[uid] = { | |
104 | - # 'name': name, | |
105 | - # 'state': 'offline', # offline, allowed, waiting, online | |
106 | - # 'test': None | |
107 | - # } | |
108 | - | |
109 | 103 | # ------------------------------------------------------------------------ |
110 | 104 | async def login(self, uid: str, password: str, headers: dict) -> Optional[str]: |
111 | 105 | ''' |
... | ... | @@ -172,7 +166,7 @@ class App(): |
172 | 166 | try: |
173 | 167 | testconf = load_yaml(filename) |
174 | 168 | testconf['testfile'] = filename |
175 | - except (IOError, yaml.YAMLError) as exc: | |
169 | + except (OSError, yaml.YAMLError) as exc: | |
176 | 170 | msg = f'Cannot read test configuration "{filename}"' |
177 | 171 | logger.error(msg) |
178 | 172 | raise AppException(msg) from exc |
... | ... | @@ -534,7 +528,7 @@ class App(): |
534 | 528 | with open(filename, 'r', encoding='utf-8') as file: |
535 | 529 | allowed = {line.strip() for line in file} |
536 | 530 | allowed.discard('') |
537 | - except IOError as exc: | |
531 | + except OSError as exc: | |
538 | 532 | error_msg = f'Cannot read file {filename}' |
539 | 533 | logger.critical(error_msg) |
540 | 534 | raise AppException(error_msg) from exc | ... | ... |
perguntations/main.py
... | ... | @@ -71,7 +71,7 @@ def get_logger_config(debug=False) -> dict: |
71 | 71 | path = os.path.expanduser(os.environ.get('XDG_CONFIG_HOME', '~/.config/')) |
72 | 72 | try: |
73 | 73 | return load_yaml(os.path.join(path, APP_NAME, file)) |
74 | - except IOError: | |
74 | + except OSError: | |
75 | 75 | print('Using default logger configuration...') |
76 | 76 | |
77 | 77 | if debug: | ... | ... |
perguntations/serve.py
... | ... | @@ -360,6 +360,7 @@ class FileHandler(BaseHandler): |
360 | 360 | Handles static files from questions like images, etc. |
361 | 361 | ''' |
362 | 362 | |
363 | + _filecache = {} | |
363 | 364 | |
364 | 365 | @tornado.web.authenticated |
365 | 366 | async def get(self): |
... | ... | @@ -371,35 +372,42 @@ class FileHandler(BaseHandler): |
371 | 372 | ref = self.get_query_argument('ref', None) |
372 | 373 | image = self.get_query_argument('image', None) |
373 | 374 | logger.debug('GET /file (ref=%s, image=%s)', ref, image) |
375 | + | |
376 | + if ref is None or image is None: | |
377 | + return | |
378 | + | |
374 | 379 | content_type = mimetypes.guess_type(image)[0] |
375 | 380 | |
376 | - if uid != '0': | |
377 | - test = self.testapp.get_student_test(uid) | |
378 | - else: | |
379 | - logger.error('FIXME Cannot serve images for review.') | |
380 | - raise tornado.web.HTTPError(404) # FIXME admin | |
381 | + if (ref, image) in self._filecache: | |
382 | + logger.debug('using cached file') | |
383 | + self.write(self._filecache[(ref, image)]) | |
384 | + if content_type is not None: | |
385 | + self.set_header("Content-Type", content_type) | |
386 | + await self.flush() | |
387 | + return | |
381 | 388 | |
382 | - if test is None: | |
389 | + try: | |
390 | + test = self.testapp.get_test(uid) | |
391 | + except KeyError: | |
392 | + logger.warning('Could not get test to serve image file') | |
383 | 393 | raise tornado.web.HTTPError(404) # Not Found |
384 | 394 | |
385 | 395 | for question in test['questions']: |
386 | 396 | # search for the question that contains the image |
387 | 397 | if question['ref'] == ref: |
388 | - filepath = path.join(question['path'], b'public', image) | |
398 | + filepath = path.join(question['path'], 'public', image) | |
399 | + | |
389 | 400 | try: |
390 | - file = open(filepath, 'rb') | |
391 | - except FileNotFoundError: | |
392 | - logger.error('File not found: %s', filepath) | |
393 | - except PermissionError: | |
394 | - logger.error('No permission: %s', filepath) | |
401 | + with open(filepath, 'rb') as file: | |
402 | + data = file.read() | |
395 | 403 | except OSError: |
396 | - logger.error('Error opening file: %s', filepath) | |
397 | - else: | |
398 | - data = file.read() | |
399 | - file.close() | |
404 | + logger.error('Error reading file "%s"', filepath) | |
405 | + break | |
406 | + self._filecache[(ref, image)] = data | |
407 | + self.write(data) | |
408 | + if content_type is not None: | |
400 | 409 | self.set_header("Content-Type", content_type) |
401 | - self.write(data) | |
402 | - await self.flush() | |
410 | + await self.flush() | |
403 | 411 | break |
404 | 412 | |
405 | 413 | ... | ... |
perguntations/testfactory.py
... | ... | @@ -99,13 +99,6 @@ class TestFactory(dict): |
99 | 99 | if question['ref'] in qrefs: |
100 | 100 | question.update(zip(('path', 'filename', 'index'), |
101 | 101 | path.split(fullpath) + (i,))) |
102 | - # if question['type'] == 'code' and 'server' not in question: | |
103 | - # try: | |
104 | - # question['server'] = self['jobe_server'] | |
105 | - # except KeyError as exc: | |
106 | - # msg = f'Missing JOBE server in "{question["ref"]}"' | |
107 | - # raise TestFactoryException(msg) from exc | |
108 | - | |
109 | 102 | self['question_factory'][question['ref']] = QFactory(QDict(question)) |
110 | 103 | |
111 | 104 | qmissing = qrefs.difference(set(self['question_factory'].keys())) |
... | ... | @@ -338,4 +331,4 @@ class TestFactory(dict): |
338 | 331 | # ------------------------------------------------------------------------ |
339 | 332 | def __repr__(self): |
340 | 333 | testsettings = '\n'.join(f' {k:14s}: {v}' for k, v in self.items()) |
341 | - return '{\n' + testsettings + '\n}' | |
334 | + return 'TestFactory({\n' + testsettings + '\n})' | ... | ... |