diff --git a/demo/questions/questions-tutorial.yaml b/demo/questions/questions-tutorial.yaml index bbe1fc1..c210cf2 100644 --- a/demo/questions/questions-tutorial.yaml +++ b/demo/questions/questions-tutorial.yaml @@ -330,9 +330,10 @@ podem ser úteis por exemplo para introduzir código. A resposta é enviada para um programa externo para ser avaliada. - O programa externo é um programa qualquer executável pelo sistema. - Este recebe a resposta submetida pelo aluno via `stdin` e devolve a - classificação via `stdout`. + O programa externo é um programa escrito numa linguagem qualquer, desde que + seja executável pelo sistema operativo (pode ser um script ou binário). + Este programa recebe a resposta submetida pelo aluno via `stdin` e devolve + a classificação via `stdout`. Exemplo: ```yaml @@ -348,12 +349,13 @@ Neste exemplo, o programa de avaliação é um script python que verifica se a resposta contém as três palavras red, green e blue, e calcula uma nota no intervalo 0.0 a 1.0. - O programa externo é um programa executável no sistema, escrito em - qualquer linguagem de programação. A interacção com o servidor faz-se + O programa externo é executado num processo separado do sistema operativo. + Pode escrito em qualquer linguagem de programação, desde que . A interacção com o servidor faz-se sempre via stdin/stdout. Se o programa externo exceder o `timeout` indicado (em segundos), - é automaticamente cancelado e é atribuída a classificação de 0.0 valores. + este é automaticamente terminado e é atribuída a classificação de 0.0 + valores na pergunta. Após terminar a correcção, o programa externo deve enviar a classificação para o stdout. @@ -377,8 +379,9 @@ O comentário é mostrado na revisão de prova. answer: | + Aqui o aluno escreve a resposta. Esta caixa aumenta de tamanho automaticamente e - pode estar previamente preenchida (use answer: texto). + pode estar previamente preenchida como neste caso (use `answer: texto`). correct: correct/correct-question.py timeout: 5 @@ -423,7 +426,7 @@ text: | Também não conta para avaliação. É apenas o aspecto gráfico que muda. - Além das fórmulas LaTeX, também se pode escrever troços de código: + Além das fórmulas LaTeX, também se podem escrever troços de código: ```C int main() { diff --git a/package-lock.json b/package-lock.json index b04e633..ca36c14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3,9 +3,9 @@ "lockfileVersion": 1, "dependencies": { "@fortawesome/fontawesome-free": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.8.1.tgz", - "integrity": "sha512-GJtx6e55qLEOy2gPOsok2lohjpdWNGrYGtQx0FFT/++K4SYx+Z8LlPHdQBaFzKEwH5IbBB4fNgb//uyZjgYXoA==" + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.11.1.tgz", + "integrity": "sha512-DtXLVYAkDU0ce1cFUgLvZaMd1R2J/LviBYih9xr4ZLhQMrgvYX7w2vOxlpKLRALfIj5GyC5zoVrcACOkLcFgvg==" }, "bootstrap": { "version": "4.3.1", @@ -13,9 +13,14 @@ "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" }, "codemirror": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.45.0.tgz", - "integrity": "sha512-c19j644usCE8gQaXa0jqn2B/HN9MnB2u6qPIrrhrMkB+QAP42y8G4QnTwuwbVSoUS1jEl7JU9HZMGhCDL0nsAw==" + "version": "5.48.4", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.48.4.tgz", + "integrity": "sha512-pUhZXDQ6qXSpWdwlgAwHEkd4imA0kf83hINmUEzJpmG80T/XLtDDEzZo8f6PQLuRCcUQhmzqqIo3ZPTRaWByRA==" + }, + "commander": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.1.tgz", + "integrity": "sha512-UNgvDd+csKdc9GD4zjtkHKQbT8Aspt2jCBqNSPp53vAS0L1tS9sXB2TCEOPHJ7kt9bN/niWkYj8T3RQSoMXdSQ==" }, "datatables": { "version": "1.10.18", @@ -25,20 +30,62 @@ "jquery": ">=1.7" } }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" }, "mathjax": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.5.tgz", - "integrity": "sha512-OzsJNitEHAJB3y4IIlPCAvS0yoXwYjlo2Y4kmm9KQzyIBZt2d8yKRalby3uTRNN4fZQiGL2iMXjpdP1u2Rq2DQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.0.0.tgz", + "integrity": "sha512-z4uLbDHNbs/aRuR6zCcnzwFQuMixkHCcWqgVaommfK/3cA1Ahq7OXemn+m8JwTYcBApSHgcrSbPr9sm3sZFL+A==", + "requires": { + "mathjax-full": "git://github.com/mathjax/MathJax-src.git" + } + }, + "mathjax-full": { + "version": "git://github.com/mathjax/MathJax-src.git#0d74266e1820220d33cb6b29d4ca3575b352ac0d", + "from": "git://github.com/mathjax/MathJax-src.git", + "requires": { + "esm": "^3.2.25", + "mj-context-menu": "^0.2.0", + "speech-rule-engine": "^3.0.0-beta.6" + } + }, + "mj-context-menu": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.2.0.tgz", + "integrity": "sha512-yJxrWBHCjFZEHsZgfs7m5g9OSCNzsVYadW6f6lX3pgZL67vmodtSW/4zhsYmuDKweXfHs0M1kJge1uQIasWA+g==" }, "popper.js": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" + }, + "speech-rule-engine": { + "version": "3.0.0-beta.6", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-3.0.0-beta.6.tgz", + "integrity": "sha512-B7gcT53jAsKpx7WvFYQcyUlFmgS3Wa9KlDy0FY8SOTa+Wz5EqmI0MpCD5/fYm8/2qiCPp8HwZg+H3cBgM+sNVw==", + "requires": { + "commander": "*", + "wicked-good-xpath": "*", + "xmldom-sre": "^0.1.31" + } + }, + "wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha1-gbDpXoZQ5JyUsiKY//hoa1VTz2w=" + }, + "xmldom-sre": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom-sre/-/xmldom-sre-0.1.31.tgz", + "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==" } } } diff --git a/package.json b/package.json index e41ecff..1f191e6 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,12 @@ "description": "Javascript libraries required to run the server", "email": "mjsb@uevora.pt", "dependencies": { - "@fortawesome/fontawesome-free": "^5.8.1", - "bootstrap": "^4.3.1", - "codemirror": "^5.45.0", - "datatables": "^1.10.18", - "jquery": "^3.3.1", - "mathjax": "^2.7.5", - "popper.js": "^1.15.0" + "@fortawesome/fontawesome-free": "^5.11.1", + "bootstrap": "^4.3", + "codemirror": "^5.48", + "datatables": "^1.10", + "jquery": "^3.4.1", + "mathjax": "^3", + "popper.js": "^1.15" } } diff --git a/perguntations/serve.py b/perguntations/serve.py index 60dff56..0d232b5 100644 --- a/perguntations/serve.py +++ b/perguntations/serve.py @@ -17,6 +17,7 @@ import ssl # user installed libraries import tornado.ioloop import tornado.web +# import tornado.websocket import tornado.httpserver # this project @@ -27,19 +28,6 @@ from perguntations.parser_markdown import md_to_html # ---------------------------------------------------------------------------- -# Decorator used to restrict access to the administrator -# ---------------------------------------------------------------------------- -def admin_only(func): - @functools.wraps(func) - async def wrapper(self, *args, **kwargs): - if self.current_user != '0': - raise tornado.web.HTTPError(403) # forbidden - else: - await func(self, *args, **kwargs) - return wrapper - - -# ---------------------------------------------------------------------------- # Web Application. Routes to handler classes. # ---------------------------------------------------------------------------- class WebApplication(tornado.web.Application): @@ -51,7 +39,8 @@ class WebApplication(tornado.web.Application): (r'/review', ReviewHandler), (r'/admin', AdminHandler), (r'/file', FileHandler), - # (r'/ws', AdminWebSocketHandler), + # (r'/root', MainHandler), # FIXME + # (r'/ws', AdminSocketHandler), (r'/', RootHandler), # TODO multiple tests ] @@ -69,6 +58,19 @@ class WebApplication(tornado.web.Application): # ---------------------------------------------------------------------------- +# Decorator used to restrict access to the administrator +# ---------------------------------------------------------------------------- +def admin_only(func): + @functools.wraps(func) + async def wrapper(self, *args, **kwargs): + if self.current_user != '0': + raise tornado.web.HTTPError(403) # forbidden + else: + await func(self, *args, **kwargs) + return wrapper + + +# ---------------------------------------------------------------------------- # Base handler. Other handlers will inherit this one. # ---------------------------------------------------------------------------- class BaseHandler(tornado.web.RequestHandler): @@ -83,6 +85,113 @@ class BaseHandler(tornado.web.RequestHandler): # ---------------------------------------------------------------------------- +# class MainHandler(BaseHandler): + +# @tornado.web.authenticated +# @admin_only +# def get(self): +# self.render("admin-ws.html", students=self.testapp.get_students_state()) + + +# # ---------------------------------------------------------------------------- +# class AdminSocketHandler(tornado.websocket.WebSocketHandler): +# waiters = set() +# # cache = [] + +# # def get_compression_options(self): +# # return {} # Non-None enables compression with default options. + +# # called when opening connection +# def open(self): +# logging.debug('[AdminSocketHandler.open]') +# AdminSocketHandler.waiters.add(self) + +# # called when closing connection +# def on_close(self): +# logging.debug('[AdminSocketHandler.on_close]') +# AdminSocketHandler.waiters.remove(self) + +# # @classmethod +# # def update_cache(cls, chat): +# # logging.debug(f'[AdminSocketHandler.update_cache] "{chat}"') +# # cls.cache.append(chat) + +# # @classmethod +# # def send_updates(cls, chat): +# # logging.info("sending message to %d waiters", len(cls.waiters)) +# # for waiter in cls.waiters: +# # try: +# # waiter.write_message(chat) +# # except Exception: +# # logging.error("Error sending message", exc_info=True) + +# # handle incomming messages +# def on_message(self, message): +# logging.info(f"[AdminSocketHandler.onmessage] got message {message}") +# parsed = tornado.escape.json_decode(message) +# print(parsed) +# chat = {"id": str(uuid.uuid4()), "body": parsed["body"]} +# print(chat) +# chat["html"] = tornado.escape.to_basestring( +# '