diff --git a/BUGS.md b/BUGS.md index bf2d5fd..483457c 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,6 +1,8 @@ # BUGS +- apos clicar no botao responder, inactivar o input (importante quando o tempo de correcção é grande) +- devia mostrar timout para o aluno saber a razao. - permitir configuracao para escolher entre static files locais ou remotos - sqlalchemy.pool.impl.NullPool: Exception during reset or similar sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. diff --git a/aprendizations/learnapp.py b/aprendizations/learnapp.py index 02df6d5..ae9950c 100644 --- a/aprendizations/learnapp.py +++ b/aprendizations/learnapp.py @@ -102,7 +102,7 @@ class LearnApp(object): continue # to next test if errors > 0: - logger.info(f'{errors:>6} errors found.') + logger.error(f'{errors:>6} errors found.') raise LearnException('Sanity checks') else: logger.info('No errors found.') diff --git a/aprendizations/questions.py b/aprendizations/questions.py index 3bbc7df..81128e5 100644 --- a/aprendizations/questions.py +++ b/aprendizations/questions.py @@ -4,12 +4,11 @@ import random import re from os import path import logging -import asyncio from typing import Any, Dict, NewType import uuid # this project -from .tools import run_script +from .tools import run_script, run_script_async # setup logger for this module logger = logging.getLogger(__name__) @@ -50,8 +49,9 @@ class Question(dict): self['grade'] = 0.0 async def correct_async(self) -> None: - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, self.correct) + self.correct() + # loop = asyncio.get_running_loop() + # await loop.run_in_executor(None, self.correct) def set_defaults(self, d: QDict) -> None: 'Add k:v pairs from default dict d for nonexistent keys' @@ -305,7 +305,7 @@ class QuestionNumericInterval(Question): answer = float(self['answer'].replace(',', '.', 1)) except ValueError: self['comments'] = ('A resposta tem de ser numérica, ' - 'por exemplo 12.345.') + 'por exemplo `12.345`.') self['grade'] = 0.0 else: self['grade'] = 1.0 if lower <= answer <= upper else 0.0 @@ -327,13 +327,13 @@ class QuestionTextArea(Question): self.set_defaults(QDict({ 'text': '', - 'lines': 8, + # 'lines': 8, 'timeout': 5, # seconds 'correct': '', # trying to execute this will fail => grade 0.0 'args': [] })) - self['correct'] = path.join(self['path'], self['correct']) # FIXME + self['correct'] = path.join(self['path'], self['correct']) # ------------------------------------------------------------------------ def correct(self) -> None: @@ -347,7 +347,39 @@ class QuestionTextArea(Question): timeout=self['timeout'] ) - if isinstance(out, dict): + if out is None: + logger.warning(f'No grade after running "{self["correct"]}".') + self['grade'] = 0.0 + elif isinstance(out, dict): + self['comments'] = out.get('comments', '') + try: + self['grade'] = float(out['grade']) + except ValueError: + logger.error(f'Output error in "{self["correct"]}".') + except KeyError: + logger.error(f'No grade in "{self["correct"]}".') + else: + try: + self['grade'] = float(out) + except (TypeError, ValueError): + logger.error(f'Invalid grade in "{self["correct"]}".') + + # ------------------------------------------------------------------------ + async def correct_async(self) -> None: + super().correct() + + if self['answer'] is not None: # correct answer and parse yaml ouput + out = await run_script_async( + script=self['correct'], + args=self['args'], + stdin=self['answer'], + timeout=self['timeout'] + ) + + if out is None: + logger.warning(f'No grade after running "{self["correct"]}".') + self['grade'] = 0.0 + elif isinstance(out, dict): self['comments'] = out.get('comments', '') try: self['grade'] = float(out['grade']) @@ -372,7 +404,6 @@ class QuestionInformation(Question): })) # ------------------------------------------------------------------------ - # can return negative values for wrong answers def correct(self) -> None: super().correct() self['grade'] = 1.0 # always "correct" but points should be zero! diff --git a/aprendizations/serve.py b/aprendizations/serve.py index d0ebd6c..6822962 100644 --- a/aprendizations/serve.py +++ b/aprendizations/serve.py @@ -1,17 +1,17 @@ #!/usr/bin/env python3 # python standard library -import sys +import argparse +import asyncio import base64 -import uuid +import functools import logging.config -import argparse import mimetypes +from os import path, environ import signal -import functools import ssl -import asyncio -from os import path, environ +import sys +import uuid # third party libraries import tornado.ioloop @@ -20,9 +20,9 @@ import tornado.web from tornado.escape import to_unicode # this project +from . import APP_NAME from .learnapp import LearnApp from .tools import load_yaml, md_to_html -from . import APP_NAME # ---------------------------------------------------------------------------- diff --git a/aprendizations/tools.py b/aprendizations/tools.py index d08d1c1..b91a239 100644 --- a/aprendizations/tools.py +++ b/aprendizations/tools.py @@ -1,17 +1,18 @@ # python standard library -from os import path -import subprocess +import asyncio import logging +from os import path import re +import subprocess from typing import Any, List # third party libraries -import yaml import mistune from pygments import highlight from pygments.lexers import get_lexer_by_name from pygments.formatters import HtmlFormatter +import yaml # setup logger for this module logger = logging.getLogger(__name__) @@ -100,14 +101,14 @@ class HighlightRenderer(mistune.Renderer): return highlight(code, lexer, formatter) def table(self, header, body): - return '