Commit a0ab1672c29cd98e668f8d8be829bbaa224861d1
1 parent
31b2eb9f
Exists in
master
and in
1 other branch
Code cleanup
Remove generate() method from qfactory. Question generation is async.
Showing
3 changed files
with
25 additions
and
68 deletions
Show diff stats
perguntations/app.py
... | ... | @@ -30,7 +30,6 @@ from .questions import question_from |
30 | 30 | # setup logger for this module |
31 | 31 | logger = logging.getLogger(__name__) |
32 | 32 | |
33 | - | |
34 | 33 | async def check_password(password: str, hashed: bytes) -> bool: |
35 | 34 | '''check password in executor''' |
36 | 35 | loop = asyncio.get_running_loop() |
... | ... | @@ -47,7 +46,6 @@ async def hash_password(password: str) -> bytes: |
47 | 46 | class AppException(Exception): |
48 | 47 | '''Exception raised in this module''' |
49 | 48 | |
50 | - | |
51 | 49 | # ============================================================================ |
52 | 50 | # main application |
53 | 51 | # ============================================================================ |
... | ... | @@ -62,6 +60,7 @@ class App(): |
62 | 60 | self._make_test_factory(config['testfile']) |
63 | 61 | self._db_setup() # setup engine and load all students |
64 | 62 | |
63 | + # FIXME get_event_loop will be deprecated in python3.10 | |
65 | 64 | asyncio.get_event_loop().run_until_complete(self._assign_tests()) |
66 | 65 | |
67 | 66 | # command line options: --allow-all, --allow-list filename |
... | ... | @@ -70,7 +69,7 @@ class App(): |
70 | 69 | elif config['allow_list'] is not None: |
71 | 70 | self.allow_from_list(config['allow_list']) |
72 | 71 | else: |
73 | - logger.info('Students login not yet allowed') | |
72 | + logger.info('Students not allowed to login') | |
74 | 73 | |
75 | 74 | if config['correct']: |
76 | 75 | self._correct_tests() |
... | ... | @@ -81,9 +80,9 @@ class App(): |
81 | 80 | Create database engine and checks for admin and students |
82 | 81 | ''' |
83 | 82 | dbfile = os.path.expanduser(self._testfactory['database']) |
84 | - logger.info('Checking database "%s"...', dbfile) | |
83 | + logger.debug('Checking database "%s"...', dbfile) | |
85 | 84 | if not os.path.exists(dbfile): |
86 | - raise AppException('No database. Use "initdb" to create.') | |
85 | + raise AppException('No database, use "initdb" to create') | |
87 | 86 | |
88 | 87 | # connect to database and check for admin & registered students |
89 | 88 | self._engine = create_engine(f'sqlite:///{dbfile}', future=True) |
... | ... | @@ -98,10 +97,10 @@ class App(): |
98 | 97 | logger.error(msg) |
99 | 98 | raise AppException(msg) from None |
100 | 99 | except OperationalError: |
101 | - msg = f'Database "{dbfile}" unusable.' | |
100 | + msg = f'Database "{dbfile}" unusable' | |
102 | 101 | logger.error(msg) |
103 | 102 | raise AppException(msg) from None |
104 | - logger.info('Database has %d students.', len(dbstudents)) | |
103 | + logger.info('Database has %d students', len(dbstudents)) | |
105 | 104 | |
106 | 105 | self._students = {uid: { |
107 | 106 | 'name': name, |
... | ... | @@ -145,15 +144,14 @@ class App(): |
145 | 144 | # success |
146 | 145 | if uid == '0': |
147 | 146 | logger.info('Admin login from %s', headers['remote_ip']) |
148 | - await self._assign_tests() | |
149 | 147 | else: |
150 | 148 | student = self._students[uid] |
151 | 149 | student['test'].start(uid) |
152 | 150 | student['state'] = 'online' |
153 | 151 | student['headers'] = headers |
154 | 152 | student['unfocus'] = False |
155 | - student['area'] = 1.0 | |
156 | - logger.info('"%s" login from %s.', uid, headers['remote_ip']) | |
153 | + student['area'] = 0.0 | |
154 | + logger.info('"%s" login from %s', uid, headers['remote_ip']) | |
157 | 155 | return None |
158 | 156 | |
159 | 157 | # ------------------------------------------------------------------------ |
... | ... | @@ -176,7 +174,7 @@ class App(): |
176 | 174 | student.pop('headers', None) |
177 | 175 | student.pop('unfocus', None) |
178 | 176 | student.pop('area', None) |
179 | - logger.info('"%s" logged out.', uid) | |
177 | + logger.info('"%s" logged out', uid) | |
180 | 178 | |
181 | 179 | # ------------------------------------------------------------------------ |
182 | 180 | def _make_test_factory(self, filename: str) -> None: |
... | ... | @@ -214,19 +212,19 @@ class App(): |
214 | 212 | return |
215 | 213 | |
216 | 214 | # --- submit answers and correct test |
217 | - logger.info('"%s" submitted %d answers.', uid, len(ans)) | |
215 | + logger.info('"%s" submitted %d answers', uid, len(ans)) | |
218 | 216 | test = self._students[uid]['test'] |
219 | 217 | test.submit(ans) |
220 | 218 | |
221 | 219 | if test['autocorrect']: |
222 | 220 | await test.correct_async() |
223 | - logger.info('"%s" grade = %g points.', uid, test['grade']) | |
221 | + logger.info('"%s" grade = %g points', uid, test['grade']) | |
224 | 222 | |
225 | 223 | # --- save test in JSON format |
226 | 224 | fname = f'{uid}--{test["ref"]}--{test["finish_time"]}.json' |
227 | 225 | fpath = os.path.join(test['answers_dir'], fname) |
228 | 226 | test.save_json(fpath) |
229 | - logger.info('"%s" saved JSON.', uid) | |
227 | + logger.info('"%s" saved JSON', uid) | |
230 | 228 | |
231 | 229 | # --- insert test and questions into the database |
232 | 230 | # only corrected questions are added |
... | ... | @@ -258,7 +256,7 @@ class App(): |
258 | 256 | with Session(self._engine, future=True) as session: |
259 | 257 | session.add(test_row) |
260 | 258 | session.commit() |
261 | - logger.info('"%s" database updated.', uid) | |
259 | + logger.info('"%s" database updated', uid) | |
262 | 260 | |
263 | 261 | # ------------------------------------------------------------------------ |
264 | 262 | def _correct_tests(self) -> None: |
... | ... | @@ -293,7 +291,7 @@ class App(): |
293 | 291 | # save JSON file (overwriting the old one) |
294 | 292 | uid = test['student'] |
295 | 293 | test.save_json(dbtest.filename) |
296 | - logger.debug('%s saved JSON file.', uid) | |
294 | + logger.debug('%s saved JSON file', uid) | |
297 | 295 | |
298 | 296 | # update database |
299 | 297 | dbtest.grade = test['grade'] |
... | ... | @@ -337,7 +335,7 @@ class App(): |
337 | 335 | # state=test['state'], |
338 | 336 | # comment='')) |
339 | 337 | |
340 | - # logger.info('"%s" gave up.', uid) | |
338 | + # logger.info('"%s" gave up', uid) | |
341 | 339 | # return test |
342 | 340 | |
343 | 341 | # ------------------------------------------------------------------------ |
... | ... | @@ -453,51 +451,13 @@ class App(): |
453 | 451 | 'grades': self.get_grades(uid, self._testfactory['ref']) } |
454 | 452 | for uid, student in self._students.items()] |
455 | 453 | |
456 | - # ------------------------------------------------------------------------ | |
457 | - # def get_student_grades_from_all_tests(self, uid): | |
458 | - # '''get grades of student from all tests''' | |
459 | - # with self._db_session() as sess: | |
460 | - # return sess.query(Test.title, Test.grade, Test.finishtime)\ | |
461 | - # .filter_by(student_id=uid)\ | |
462 | - # .order_by(Test.finishtime) | |
463 | - | |
464 | - # --- private methods ---------------------------------------------------- | |
465 | - # def _get_all_students(self): | |
466 | - # '''get all students from database''' | |
467 | - # with Session(self._engine, future=True) as session: | |
468 | - # query = select(Student.id, Student.name, Student.password)\ | |
469 | - # .where(Student.id != '0') | |
470 | - # students | |
471 | - # questions = session.execute( | |
472 | - # select(Test.id, Test.student_id, Test.starttime, | |
473 | - # Question.number, Question.grade). | |
474 | - # join(Question). | |
475 | - # where(Test.ref == test_ref) | |
476 | - # ).all() | |
477 | - | |
478 | - | |
479 | - # return session.query(Student.id, Student.name, Student.password)\ | |
480 | - # .filter(Student.id != '0')\ | |
481 | - # .order_by(Student.id) | |
482 | - | |
483 | - # def get_allowed_students(self): | |
484 | - # # set of 'uid' allowed to login | |
485 | - # return self.allowed | |
486 | - | |
487 | - # def get_file(self, uid, ref, key): | |
488 | - # # get filename of (uid, ref, name) if declared in the question | |
489 | - # t = self.get_student_test(uid) | |
490 | - # for q in t['questions']: | |
491 | - # if q['ref'] == ref and key in q['files']: | |
492 | - # return os.path.abspath(os.path.join(q['path'], q['files'][key])) | |
493 | - | |
494 | 454 | # ======================================================================== |
495 | 455 | # SETTERS |
496 | 456 | # ======================================================================== |
497 | 457 | def allow_student(self, uid: str) -> None: |
498 | 458 | '''allow a single student to login''' |
499 | 459 | self._students[uid]['state'] = 'allowed' |
500 | - logger.info('"%s" allowed to login.', uid) | |
460 | + logger.info('"%s" allowed to login', uid) | |
501 | 461 | |
502 | 462 | # ------------------------------------------------------------------------ |
503 | 463 | def deny_student(self, uid: str) -> None: |
... | ... | @@ -512,7 +472,7 @@ class App(): |
512 | 472 | '''allow all students to login''' |
513 | 473 | for student in self._students.values(): |
514 | 474 | student['state'] = 'allowed' |
515 | - logger.info('Allowed %d students.', len(self._students)) | |
475 | + logger.info('Allowed %d students', len(self._students)) | |
516 | 476 | |
517 | 477 | # ------------------------------------------------------------------------ |
518 | 478 | def deny_all_students(self) -> None: | ... | ... |
perguntations/questions.py
... | ... | @@ -5,10 +5,9 @@ Description: Classes the implement several types of questions. |
5 | 5 | |
6 | 6 | |
7 | 7 | # python standard library |
8 | -import asyncio | |
9 | 8 | from datetime import datetime |
10 | 9 | import logging |
11 | -from os import path | |
10 | +import os | |
12 | 11 | import random |
13 | 12 | import re |
14 | 13 | from typing import Any, Dict, NewType |
... | ... | @@ -499,7 +498,7 @@ class QuestionTextArea(Question): |
499 | 498 | 'args': [] |
500 | 499 | })) |
501 | 500 | |
502 | - self['correct'] = path.join(self['path'], self['correct']) | |
501 | + self['correct'] = os.path.join(self['path'], self['correct']) | |
503 | 502 | |
504 | 503 | # ------------------------------------------------------------------------ |
505 | 504 | def correct(self) -> None: |
... | ... | @@ -676,7 +675,7 @@ class QFactory(): |
676 | 675 | logger.debug(' \\_ Running "%s"', qdict['script']) |
677 | 676 | qdict.setdefault('args', []) |
678 | 677 | qdict.setdefault('stdin', '') |
679 | - script = path.join(qdict['path'], qdict['script']) | |
678 | + script = os.path.join(qdict['path'], qdict['script']) | |
680 | 679 | out = await run_script_async(script=script, |
681 | 680 | args=qdict['args'], |
682 | 681 | stdin=qdict['stdin']) |
... | ... | @@ -685,8 +684,3 @@ class QFactory(): |
685 | 684 | question = question_from(qdict) # returns a Question instance |
686 | 685 | question.gen() |
687 | 686 | return question |
688 | - | |
689 | - # ------------------------------------------------------------------------ | |
690 | - def generate(self) -> Question: | |
691 | - '''generate question (synchronous version)''' | |
692 | - return asyncio.get_event_loop().run_until_complete(self.gen_async()) | ... | ... |
perguntations/testfactory.py
... | ... | @@ -3,6 +3,7 @@ TestFactory - generates tests for students |
3 | 3 | ''' |
4 | 4 | |
5 | 5 | # python standard library |
6 | +import asyncio | |
6 | 7 | from os import path |
7 | 8 | import random |
8 | 9 | import logging |
... | ... | @@ -255,10 +256,12 @@ class TestFactory(dict): |
255 | 256 | ''' |
256 | 257 | checks if questions can be correctly generated and corrected |
257 | 258 | ''' |
258 | - logger.info('Checking if questions can be generated and corrected...') | |
259 | + logger.info('Checking questions...') | |
260 | + # FIXME get_event_loop will be deprecated in python3.10 | |
261 | + loop = asyncio.get_event_loop() | |
259 | 262 | for i, (qref, qfact) in enumerate(self['question_factory'].items()): |
260 | 263 | try: |
261 | - question = qfact.generate() | |
264 | + question = loop.run_until_complete(qfact.gen_async()) | |
262 | 265 | except Exception as exc: |
263 | 266 | msg = f'Failed to generate "{qref}"' |
264 | 267 | raise TestFactoryException(msg) from exc | ... | ... |