Commit 9e3600ef49e63cb0db817c25fe76d2bbd52e0a70
1 parent
3d24dd0f
Exists in
master
and in
1 other branch
- internal changes in how a test is configured.
Showing
5 changed files
with
64 additions
and
38 deletions
Show diff stats
BUGS.md
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 | - hints nao funciona |
7 | 7 | - uniformizar question.py com a de aprendizations... |
8 | 8 | - permitir eliminar teste a decorrer |
9 | +- eventos unfocus? | |
9 | 10 | - servidor nao esta a lidar com eventos scroll/resize. ignorar? |
10 | 11 | |
11 | 12 | # TODO |
... | ... | @@ -18,7 +19,7 @@ |
18 | 19 | node_modules/mathjax-node-cli/bin/tex2svg '\sqrt{x}' |
19 | 20 | usar isto para gerar svg que passa a fazer parte do texto da pergunta (markdown suporta tags svg?) |
20 | 21 | fazer funçao tex() que recebe formula e converte para svg. exemplo: |
21 | - fr'''A formula é {tex(\sqrt{x]})}''' | |
22 | + fr'''A formula é {tex("\sqrt{x]}")}''' | |
22 | 23 | |
23 | 24 | - Gerar pdf's com todos os testes no final (pdfkit). |
24 | 25 | - manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento | ... | ... |
app.py
... | ... | @@ -12,6 +12,7 @@ from sqlalchemy.orm import sessionmaker, scoped_session |
12 | 12 | # this project |
13 | 13 | from models import Student, Test, Question |
14 | 14 | import test |
15 | +from tools import load_yaml | |
15 | 16 | |
16 | 17 | logger = logging.getLogger(__name__) |
17 | 18 | |
... | ... | @@ -23,7 +24,7 @@ class AppException(Exception): |
23 | 24 | # Application |
24 | 25 | # ============================================================================ |
25 | 26 | class App(object): |
26 | - def __init__(self, filename, conf): | |
27 | + def __init__(self, conf={}): | |
27 | 28 | # online = { |
28 | 29 | # uid1: { |
29 | 30 | # 'student': {'number': 123, 'name': john, ...}, |
... | ... | @@ -35,8 +36,17 @@ class App(object): |
35 | 36 | self.online = dict() # {uid: {'student':{}}} |
36 | 37 | self.allowed = set([]) # '0' is hardcoded to allowed elsewhere |
37 | 38 | |
39 | + # build test configuration dictionary | |
40 | + testconf = {} | |
41 | + if conf['filename']: | |
42 | + logger.info(f'Loading test configuration "{conf["filename"]}".') | |
43 | + testconf.update(load_yaml(conf['filename'])) | |
44 | + | |
45 | + testconf.update(conf) # configuration overrides | |
46 | + | |
47 | + # start test factory | |
38 | 48 | try: |
39 | - self.testfactory = test.TestFactory(filename, conf=conf) | |
49 | + self.testfactory = test.TestFactory(testconf) | |
40 | 50 | except test.TestFactoryException: |
41 | 51 | logger.critical('Can\'t create test factory.') |
42 | 52 | raise AppException() | ... | ... |
serve.py
... | ... | @@ -8,6 +8,8 @@ import logging.config |
8 | 8 | import json |
9 | 9 | import base64 |
10 | 10 | import uuid |
11 | +# from mimetypes import guess_type | |
12 | + | |
11 | 13 | |
12 | 14 | # packages |
13 | 15 | import tornado.ioloop |
... | ... | @@ -28,7 +30,7 @@ class WebApplication(tornado.web.Application): |
28 | 30 | (r'/test', TestHandler), |
29 | 31 | (r'/review', ReviewHandler), |
30 | 32 | (r'/admin', AdminHandler), |
31 | - (r'/static/(.+)', FileHandler), # FIXME | |
33 | + (r'/file/(.+)', FileHandler), # FIXME | |
32 | 34 | (r'/', RootHandler), # TODO multiple tests |
33 | 35 | ] |
34 | 36 | |
... | ... | @@ -93,17 +95,33 @@ class LogoutHandler(BaseHandler): |
93 | 95 | # FIXME checkit |
94 | 96 | class FileHandler(BaseHandler): |
95 | 97 | @tornado.web.authenticated |
96 | - def get(self, filename): | |
98 | + def get(self): | |
97 | 99 | uid = self.current_user |
98 | - public_dir = self.learn.get_current_public_dir(uid) # FIXME!!! | |
99 | - filepath = path.expanduser(path.join(public_dir, filename)) | |
100 | - try: | |
101 | - f = open(filepath, 'rb') | |
102 | - except FileNotFoundError: | |
103 | - raise tornado.web.HTTPError(404) | |
104 | - else: | |
105 | - self.write(f.read()) | |
106 | - f.close() | |
100 | + qref = self.get_query_argument('ref') | |
101 | + qfile = self.get_query_argument('filename') | |
102 | + self.write(self.testapp.get_file(ref, filename)) | |
103 | + | |
104 | + | |
105 | + # if not os.path.isfile(file_location): | |
106 | + # raise tornado.web.HTTPError(status_code=404) | |
107 | + | |
108 | + # content_type, _ = guess_type(file_location) | |
109 | + # self.add_header('Content-Type', content_type) | |
110 | + # with open(file_location) as source_file: | |
111 | + # self.write(source_file.read()) | |
112 | + | |
113 | + | |
114 | + | |
115 | + | |
116 | + # public_dir = self.learn.get_current_public_dir(uid) # FIXME!!! | |
117 | + # filepath = path.expanduser(path.join(public_dir, filename)) | |
118 | + # try: | |
119 | + # f = open(filepath, 'rb') | |
120 | + # except FileNotFoundError: | |
121 | + # raise tornado.web.HTTPError(404) | |
122 | + # else: | |
123 | + # self.write(f.read()) | |
124 | + # f.close() | |
107 | 125 | |
108 | 126 | |
109 | 127 | # ------------------------------------------------------------------------- |
... | ... | @@ -309,7 +327,7 @@ def main(): |
309 | 327 | logger_file = 'logger-debug.yaml' if arg.debug else 'logger.yaml' |
310 | 328 | SERVER_PATH = path.dirname(path.realpath(__file__)) |
311 | 329 | LOGGER_CONF = path.join(SERVER_PATH, f'config/{logger_file}') |
312 | - | |
330 | + | |
313 | 331 | try: |
314 | 332 | logging.config.dictConfig(load_yaml(LOGGER_CONF)) |
315 | 333 | except: |
... | ... | @@ -318,10 +336,14 @@ def main(): |
318 | 336 | logging.info('===============================================') |
319 | 337 | |
320 | 338 | # --- start application |
321 | - filename = path.abspath(path.expanduser(arg.testfile[0])) | |
339 | + config = { | |
340 | + 'filename': arg.testfile[0] or '', | |
341 | + 'debug': arg.debug, | |
342 | + 'allow_all': arg.allow_all, | |
343 | + } | |
322 | 344 | |
323 | 345 | try: |
324 | - app = App(filename, vars(arg)) | |
346 | + app = App(config) | |
325 | 347 | except AppException: |
326 | 348 | logging.critical('Failed to start application.') |
327 | 349 | sys.exit(1) | ... | ... |
templates/admin.html
... | ... | @@ -66,10 +66,10 @@ |
66 | 66 | <table class="table table-sm" id="students_table"> |
67 | 67 | <thead class="thead thead-light"> |
68 | 68 | <tr> |
69 | - <th class="col-md-2">Número</th> | |
70 | - <th class="col-md-6">Nome</th> | |
71 | - <th class="col-md-2">Estado</th> | |
72 | - <th class="col-md-2">Nota</th> | |
69 | + <th>Número</th> | |
70 | + <th>Nome</th> | |
71 | + <th>Estado</th> | |
72 | + <th>Nota</th> | |
73 | 73 | </tr> |
74 | 74 | </thead> |
75 | 75 | </table> | ... | ... |
test.py
... | ... | @@ -9,7 +9,7 @@ import logging |
9 | 9 | |
10 | 10 | # project |
11 | 11 | import questions |
12 | -from tools import load_yaml | |
12 | +# from tools import load_yaml | |
13 | 13 | |
14 | 14 | # Logger configuration |
15 | 15 | logger = logging.getLogger(__name__) |
... | ... | @@ -29,17 +29,11 @@ class TestFactory(dict): |
29 | 29 | # some configurations using the conf argument. |
30 | 30 | # base questions are loaded from files into a pool. |
31 | 31 | # ----------------------------------------------------------------------- |
32 | - def __init__(self, filename=None, conf={}): | |
33 | - super().__init__({}) | |
32 | + def __init__(self, conf): | |
33 | + super().__init__(conf) | |
34 | 34 | |
35 | - if filename is not None: | |
36 | - self.update(load_yaml(filename)) | |
37 | - self['filename'] = filename | |
38 | - else: | |
39 | - self['filename'] = '' | |
40 | - | |
41 | - self.update(conf) # overrides configuration | |
42 | - self.sanity_checks() # defaults and sanity checks | |
35 | + # set defaults and sanity checks | |
36 | + self.sanity_checks() | |
43 | 37 | |
44 | 38 | # loads question_factory |
45 | 39 | self.question_factory = questions.QuestionFactory() |
... | ... | @@ -66,7 +60,7 @@ class TestFactory(dict): |
66 | 60 | |
67 | 61 | # --- database |
68 | 62 | if 'database' not in self: |
69 | - logger.critical('Missing "database" key in configuration.') | |
63 | + logger.critical('Missing "database" in configuration.') | |
70 | 64 | raise TestFactoryException() |
71 | 65 | elif not path.isfile(path.expanduser(self['database'])): |
72 | 66 | logger.critical(f'Can\'t find database {self["database"]}.') |
... | ... | @@ -74,7 +68,7 @@ class TestFactory(dict): |
74 | 68 | |
75 | 69 | # --- answers_dir |
76 | 70 | if 'answers_dir' not in self: |
77 | - logger.warning('Missing "answers_dir".') | |
71 | + logger.critical('Missing "answers_dir".') | |
78 | 72 | raise TestFactoryException() |
79 | 73 | try: # check if answers_dir is a writable directory |
80 | 74 | f = open(path.join(path.expanduser(self['answers_dir']),'REMOVE-ME'), 'w') |
... | ... | @@ -127,7 +121,7 @@ class TestFactory(dict): |
127 | 121 | |
128 | 122 | # --- defaults for optional keys |
129 | 123 | self.setdefault('title', '') |
130 | - self.setdefault('show_hints', False) | |
124 | + self.setdefault('show_hints', False) # FIXME not implemented yet | |
131 | 125 | self.setdefault('show_points', False) |
132 | 126 | self.setdefault('scale_points', True) |
133 | 127 | self.setdefault('scale_max', 20.0) |
... | ... | @@ -178,7 +172,6 @@ class TestFactory(dict): |
178 | 172 | 'student': student, # student id |
179 | 173 | 'questions': test, # list of questions |
180 | 174 | 'answers_dir': self['answers_dir'], |
181 | - # 'total_points': total_points, | |
182 | 175 | |
183 | 176 | # FIXME which ones are required? |
184 | 177 | 'show_hints': self['show_hints'], |
... | ... | @@ -191,8 +184,8 @@ class TestFactory(dict): |
191 | 184 | }) |
192 | 185 | |
193 | 186 | # ----------------------------------------------------------------------- |
194 | - def __repr__(self): | |
195 | - return '{\n' + '\n'.join(' {0:14s}: {1}'.format(k, v) for k,v in self.items()) + '\n}' | |
187 | + # def __repr__(self): | |
188 | + # return '{\n' + '\n'.join(' {0:14s}: {1}'.format(k, v) for k,v in self.items()) + '\n}' | |
196 | 189 | |
197 | 190 | |
198 | 191 | # =========================================================================== | ... | ... |