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.

@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 - hints nao funciona 6 - hints nao funciona
7 - uniformizar question.py com a de aprendizations... 7 - uniformizar question.py com a de aprendizations...
8 - permitir eliminar teste a decorrer 8 - permitir eliminar teste a decorrer
  9 +- eventos unfocus?
9 - servidor nao esta a lidar com eventos scroll/resize. ignorar? 10 - servidor nao esta a lidar com eventos scroll/resize. ignorar?
10 11
11 # TODO 12 # TODO
@@ -18,7 +19,7 @@ @@ -18,7 +19,7 @@
18 node_modules/mathjax-node-cli/bin/tex2svg '\sqrt{x}' 19 node_modules/mathjax-node-cli/bin/tex2svg '\sqrt{x}'
19 usar isto para gerar svg que passa a fazer parte do texto da pergunta (markdown suporta tags svg?) 20 usar isto para gerar svg que passa a fazer parte do texto da pergunta (markdown suporta tags svg?)
20 fazer funçao tex() que recebe formula e converte para svg. exemplo: 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 - Gerar pdf's com todos os testes no final (pdfkit). 24 - Gerar pdf's com todos os testes no final (pdfkit).
24 - manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento 25 - manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento
@@ -12,6 +12,7 @@ from sqlalchemy.orm import sessionmaker, scoped_session @@ -12,6 +12,7 @@ from sqlalchemy.orm import sessionmaker, scoped_session
12 # this project 12 # this project
13 from models import Student, Test, Question 13 from models import Student, Test, Question
14 import test 14 import test
  15 +from tools import load_yaml
15 16
16 logger = logging.getLogger(__name__) 17 logger = logging.getLogger(__name__)
17 18
@@ -23,7 +24,7 @@ class AppException(Exception): @@ -23,7 +24,7 @@ class AppException(Exception):
23 # Application 24 # Application
24 # ============================================================================ 25 # ============================================================================
25 class App(object): 26 class App(object):
26 - def __init__(self, filename, conf): 27 + def __init__(self, conf={}):
27 # online = { 28 # online = {
28 # uid1: { 29 # uid1: {
29 # 'student': {'number': 123, 'name': john, ...}, 30 # 'student': {'number': 123, 'name': john, ...},
@@ -35,8 +36,17 @@ class App(object): @@ -35,8 +36,17 @@ class App(object):
35 self.online = dict() # {uid: {'student':{}}} 36 self.online = dict() # {uid: {'student':{}}}
36 self.allowed = set([]) # '0' is hardcoded to allowed elsewhere 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 try: 48 try:
39 - self.testfactory = test.TestFactory(filename, conf=conf) 49 + self.testfactory = test.TestFactory(testconf)
40 except test.TestFactoryException: 50 except test.TestFactoryException:
41 logger.critical('Can\'t create test factory.') 51 logger.critical('Can\'t create test factory.')
42 raise AppException() 52 raise AppException()
@@ -8,6 +8,8 @@ import logging.config @@ -8,6 +8,8 @@ import logging.config
8 import json 8 import json
9 import base64 9 import base64
10 import uuid 10 import uuid
  11 +# from mimetypes import guess_type
  12 +
11 13
12 # packages 14 # packages
13 import tornado.ioloop 15 import tornado.ioloop
@@ -28,7 +30,7 @@ class WebApplication(tornado.web.Application): @@ -28,7 +30,7 @@ class WebApplication(tornado.web.Application):
28 (r'/test', TestHandler), 30 (r'/test', TestHandler),
29 (r'/review', ReviewHandler), 31 (r'/review', ReviewHandler),
30 (r'/admin', AdminHandler), 32 (r'/admin', AdminHandler),
31 - (r'/static/(.+)', FileHandler), # FIXME 33 + (r'/file/(.+)', FileHandler), # FIXME
32 (r'/', RootHandler), # TODO multiple tests 34 (r'/', RootHandler), # TODO multiple tests
33 ] 35 ]
34 36
@@ -93,17 +95,33 @@ class LogoutHandler(BaseHandler): @@ -93,17 +95,33 @@ class LogoutHandler(BaseHandler):
93 # FIXME checkit 95 # FIXME checkit
94 class FileHandler(BaseHandler): 96 class FileHandler(BaseHandler):
95 @tornado.web.authenticated 97 @tornado.web.authenticated
96 - def get(self, filename): 98 + def get(self):
97 uid = self.current_user 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,7 +327,7 @@ def main():
309 logger_file = 'logger-debug.yaml' if arg.debug else 'logger.yaml' 327 logger_file = 'logger-debug.yaml' if arg.debug else 'logger.yaml'
310 SERVER_PATH = path.dirname(path.realpath(__file__)) 328 SERVER_PATH = path.dirname(path.realpath(__file__))
311 LOGGER_CONF = path.join(SERVER_PATH, f'config/{logger_file}') 329 LOGGER_CONF = path.join(SERVER_PATH, f'config/{logger_file}')
312 - 330 +
313 try: 331 try:
314 logging.config.dictConfig(load_yaml(LOGGER_CONF)) 332 logging.config.dictConfig(load_yaml(LOGGER_CONF))
315 except: 333 except:
@@ -318,10 +336,14 @@ def main(): @@ -318,10 +336,14 @@ def main():
318 logging.info('===============================================') 336 logging.info('===============================================')
319 337
320 # --- start application 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 try: 345 try:
324 - app = App(filename, vars(arg)) 346 + app = App(config)
325 except AppException: 347 except AppException:
326 logging.critical('Failed to start application.') 348 logging.critical('Failed to start application.')
327 sys.exit(1) 349 sys.exit(1)
templates/admin.html
@@ -66,10 +66,10 @@ @@ -66,10 +66,10 @@
66 <table class="table table-sm" id="students_table"> 66 <table class="table table-sm" id="students_table">
67 <thead class="thead thead-light"> 67 <thead class="thead thead-light">
68 <tr> 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 </tr> 73 </tr>
74 </thead> 74 </thead>
75 </table> 75 </table>
@@ -9,7 +9,7 @@ import logging @@ -9,7 +9,7 @@ import logging
9 9
10 # project 10 # project
11 import questions 11 import questions
12 -from tools import load_yaml 12 +# from tools import load_yaml
13 13
14 # Logger configuration 14 # Logger configuration
15 logger = logging.getLogger(__name__) 15 logger = logging.getLogger(__name__)
@@ -29,17 +29,11 @@ class TestFactory(dict): @@ -29,17 +29,11 @@ class TestFactory(dict):
29 # some configurations using the conf argument. 29 # some configurations using the conf argument.
30 # base questions are loaded from files into a pool. 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 # loads question_factory 38 # loads question_factory
45 self.question_factory = questions.QuestionFactory() 39 self.question_factory = questions.QuestionFactory()
@@ -66,7 +60,7 @@ class TestFactory(dict): @@ -66,7 +60,7 @@ class TestFactory(dict):
66 60
67 # --- database 61 # --- database
68 if 'database' not in self: 62 if 'database' not in self:
69 - logger.critical('Missing "database" key in configuration.') 63 + logger.critical('Missing "database" in configuration.')
70 raise TestFactoryException() 64 raise TestFactoryException()
71 elif not path.isfile(path.expanduser(self['database'])): 65 elif not path.isfile(path.expanduser(self['database'])):
72 logger.critical(f'Can\'t find database {self["database"]}.') 66 logger.critical(f'Can\'t find database {self["database"]}.')
@@ -74,7 +68,7 @@ class TestFactory(dict): @@ -74,7 +68,7 @@ class TestFactory(dict):
74 68
75 # --- answers_dir 69 # --- answers_dir
76 if 'answers_dir' not in self: 70 if 'answers_dir' not in self:
77 - logger.warning('Missing "answers_dir".') 71 + logger.critical('Missing "answers_dir".')
78 raise TestFactoryException() 72 raise TestFactoryException()
79 try: # check if answers_dir is a writable directory 73 try: # check if answers_dir is a writable directory
80 f = open(path.join(path.expanduser(self['answers_dir']),'REMOVE-ME'), 'w') 74 f = open(path.join(path.expanduser(self['answers_dir']),'REMOVE-ME'), 'w')
@@ -127,7 +121,7 @@ class TestFactory(dict): @@ -127,7 +121,7 @@ class TestFactory(dict):
127 121
128 # --- defaults for optional keys 122 # --- defaults for optional keys
129 self.setdefault('title', '') 123 self.setdefault('title', '')
130 - self.setdefault('show_hints', False) 124 + self.setdefault('show_hints', False) # FIXME not implemented yet
131 self.setdefault('show_points', False) 125 self.setdefault('show_points', False)
132 self.setdefault('scale_points', True) 126 self.setdefault('scale_points', True)
133 self.setdefault('scale_max', 20.0) 127 self.setdefault('scale_max', 20.0)
@@ -178,7 +172,6 @@ class TestFactory(dict): @@ -178,7 +172,6 @@ class TestFactory(dict):
178 'student': student, # student id 172 'student': student, # student id
179 'questions': test, # list of questions 173 'questions': test, # list of questions
180 'answers_dir': self['answers_dir'], 174 'answers_dir': self['answers_dir'],
181 - # 'total_points': total_points,  
182 175
183 # FIXME which ones are required? 176 # FIXME which ones are required?
184 'show_hints': self['show_hints'], 177 'show_hints': self['show_hints'],
@@ -191,8 +184,8 @@ class TestFactory(dict): @@ -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 # ===========================================================================