Commit 9e3600ef49e63cb0db817c25fe76d2bbd52e0a70

Authored by Miguel Barao
1 parent 3d24dd0f
Exists in master and in 1 other branch dev

- internal changes in how a test is configured.

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 # ===========================================================================
... ...