diff --git a/BUGS.md b/BUGS.md index 2ac7692..fa12301 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,192 +1,66 @@ +# Perguntations -# BUGS +## BUGS -- correct devia poder ser corrido mais que uma vez (por exemplo para alterar cotacoes, corrigir perguntas) -- guardar testes em JSON assim que sao atribuidos aos alunos (ou guardados inicialmente com um certo nome, e atribuidos posteriormente ao aluno). -- cookies existe um perguntations_user e um user. De onde vem o user? -- QuestionCode falta reportar nos comments os vários erros que podem ocorrer (timeout, etc) -- algumas vezes a base de dados guarda o mesmo teste em duplicado. ver se dois submits dao origem a duas correcções. -talvez a base de dados devesse ter como chave do teste um id que fosse único desse teste particular (não um auto counter, nem ref do teste) -- em caso de timeout na submissão (e.g. JOBE ou script nao responde) a correcção não termina e o teste não é guardado. -- grade gives internal server error?? +- talvez a base de dados devesse ter como chave do teste um id que fosse único + desse teste particular (não um auto counter, nem ref do teste) +- em caso de timeout na submissão (e.g. JOBE ou script nao responde) a correcção + não termina e o teste não é guardado. - reload do teste recomeça a contagem no inicio do tempo. -- em admin, quando scale_max não é 20, as cores das barras continuam a reflectir a escala 0,20. a tabela teste na DB não tem a escala desse teste. -- em grade.html as barras estao normalizadas para os limites scale_min e max do teste actual e nao dos testes realizados no passado (tabela test devia guardar a escala). -- codigo `hello world` nao esta a preservar o whitespace. O renderer de markdown gera a tag que não preserva whitespace. Necessario adicionar
.
-- mensagems de erro do assembler aparecem na mesma linha na correcao e nao fazerm rendering do `$t`, ver se servidor faz parse do markdown dessas mensagens.
-- a revisao do teste não mostra as imagens.
-- Test.reset_answers() unused.
-- teste nao esta a mostrar imagens de vez em quando.???
-- show-ref nao esta a funcionar na correccao (pelo menos)
+- em admin, quando scale_max não é 20, as cores das barras continuam a reflectir
+  a escala 0,20. a tabela teste na DB não tem a escala desse teste.
+- mensagems de erro do assembler aparecem na mesma linha na correcao e nao
+  fazerm rendering do `$t`, ver se servidor faz parse do markdown dessas
+  mensagens.
+- a revisao do teste não mostra as imagens que nao estejam ja em cache.
 
-# TODO
+## TODO
 
-- JOBE correct async
-- esta a corrigir código JOBE mesmo que nao tenha respondido???
-- permitir remover alunos que estão online para poderem comecar de novo.
-- guardar nota final grade truncado em zero e sem ser truncado (quando é necessário fazer correcções à mão às perguntas, é necessário o valor não truncado)
-- stress tests. use https://locust.io
-- wait for admin to start test. (students can be allowed earlier)
-- impedir os eventos copy/paste. alunos usam isso para trazer codigo ja feito nos computadores. Obrigar a fazer reset? fazer um copy automaticamente?
-- na pagina admin, mostrar com cor vermelha as horas de entrada dos alunos que ja tenham excedido o tempo
-- retornar None quando nao ha alteracoes relativamente à última vez.
-ou usar push (websockets?)
+- QuestionTextArea falta reportar nos comments os vários erros que podem ocorrer
+  (timeout, etc)
+- pergunta com varias partes.
+- /review incluir identificacao do aluno no jumbotron (para poder gerar pdf).
+- corrir mais que uma vez (por ex. para alterar cotacoes, corrigir perguntas)
+- guardar testes em JSON assim que sao atribuidos aos alunos.
+- impedir os eventos copy/paste. alunos usam isso para trazer codigo ja feito
+  nos computadores. Obrigar a fazer reset? fazer um copy automaticamente?
+- na pagina admin, mostrar com cor vermelha as horas de entrada dos alunos que
+  ja tenham excedido o tempo
+- /admin long pooling.
 - mudar ref do test para test_id (ref já é usado nas perguntas)
 - servidor ntpd no x220 para configurar a data/hora dos portateis dell
-- sala de espera: autorização dada, mas teste não disponível até que seja dada ordem para começar.
-- alunos com necessidades especiais nao podem ter autosubmit. ter um autosubmit_exceptions: ['123', '456']
-- submissao fazer um post ajax?
-- adicionar opcao para eliminar um teste em curso.
+- sala de espera: autorização dada, mas teste não disponível até que seja dada
+  ordem para começar.
+- alunos com necessidades especiais nao podem ter autosubmit. ter um
+  autosubmit_exceptions: ['123', '456']
 - enviar resposta de cada pergunta individualmente.
-- experimentar gerador de svg que inclua no markdown da pergunta e ver se funciona.
-- quando ha varias perguntas para escolher, escolher sucessivamente em vez de aleatoriamente.
-- como refrescar a tabela de admin sem fazer reload da pagina?
-- botao "testar resposta" que valida codigo relativamente a syntax, mas nao classifica. perguntas devem ter opcao validate: script.py. Aluno pressiona botao e codigo é enviado para servidor para validação, feedback é mostrado na pagina de teste.
-- test: botao submeter valida se esta online com um post willing_to_submit, se estiver online, mostra mensagem de confirmacao, caso contrario avisa que nao esta online.
+- experimentar gerador de svg que inclua no markdown da pergunta.
+- botao "testar resposta" que valida syntax mas nao classifica.
+  perguntas devem ter opcao validate: script.py.
+  Aluno pressiona botao e codigo é enviado para servidor para validação,
+- test: botao submeter valida se esta online com um post willing_to_submit,
+  se estiver online, mostra mensagem de confirmacao, caso contrario avisa que
+  nao esta online.
 - test: Cada pergunta respondida é logo submetida.
-- test: calculadora javascript.
 - admin: histograma das notas.
 - admin: mostrar as horas a que o teste terminou para os testes terminados.
 - admin: histograma das notas.
 - admin: mostrar teste gerado para aluno (tipo review).
-- fazer renderer para formulas com mathjax serverside (mathjax-node) ou usar katex.
-- fazer renderer para imagens, com links /file?ref=xpto;name=zzz.jpg
-- fazer renderer para linguagem assembly mips?
-- cancelar teste no menu admin. Dado o numero de aluno remove teste e faz logout do aluno.
+- admin: Cancelar teste e fazer logout do aluno (e.g. fraude)
 - mathjax-node:
     sudo pkg install node npm
     npm install mathjax-node mathjax-node-cli    # pacotes em ~/node_modules
     node_modules/mathjax-node-cli/bin/tex2svg '\sqrt{x}'
-  usar isto para gerar svg que passa a fazer parte do texto da pergunta (markdown suporta tags svg?)
+  usar isto para gerar svg que passa a fazer parte do texto da pergunta
+  (markdown suporta tags svg?)
   fazer funçao tex() que recebe formula e converte para svg. exemplo:
   fr'''A formula é {tex("\sqrt{x]}")}'''
-- Gerar pdf's com todos os testes no final (pdfkit).
-- manter registo dos unfocus durante o teste e de qual a pergunta visivel nesse momento
+- Gerar pdf's com todos os testes no final (pdfkit, firefox marionette).
+- registos unfocus durante o teste e qual a pergunta visivel nesse momento
 - permitir varios testes, aluno escolhe qual o teste que quer fazer.
 - se ocorrer um erro na correcçao avisar aluno para contactar o professor.
-- abrir o teste numa janela maximizada e que nao permite que o aluno a redimensione/mova?
-- detectar scroll e enviar posição para servidor (analise de scroll para detectar copianço? ou simplesmente para analisar como os alunos percorrem o teste)
-- aviso na pagina principal para quem usa browser incompativel ou settings esquisitos... Apos login pode ser enviado e submetido um exemplo de teste para verificar se browser consegue submeter? ha alunos com javascript bloqueado?
-- criar perguntas de outros tipos, e.g. associação, ordenação, varios textinput
-- perguntas para professor corrigir mais tarde. permitir que review possa alterar as notas
-- fazer uma calculadora javascript e por no menu. surge como modal
-
-# FIXED
-
-- testar as perguntas todas no início do teste como o aprendizations.
-- adicionar identificacao do aluno no jumbotron inicial do teste, para que ao imprimir para pdf a identificacao do aluno venha escrita no documento.
-- internal server error quando em --review, download csv detalhado.
-- perguntas repetidas (mesma ref) dao asneira, porque a referencia é usada como chave em varios sitios e as chaves nao podem ser dupplicadas.
-  da asneira pelo menos na funcao get_questions_csv. na base de dados tem de estar registado tb o numero da pergunta, caso contrario é impossível saber a qual corresponde.
-- mostrar unfocus e window area em /admin
-- CRITICAL se answer for `i 8080. documentacao
-- barras com notas em grade estão desalinhadas.
-- erros nos generators devem ser ERROR e não WARNING.
-- se directorio "logs" não existir no directorio actual aborta com mensagem de erro.
-- se um teste tiver a mesma pergunta repetida (ref igual), rebenta na correcçao. As respostas são agregadas numa lista para cada ref. Ex: {'ref1': 'resposta1', 'ref2': ['resposta2a', 'resposta2b']}
-- usar http://fontawesome.io/examples/ em vez dos do bootstrap3
-- se pergunta tiver 'type:' errado, rebenta.
-- se submeter um teste so com information, da divisao por zero.
-- se save_answers nao existir, da warning que nao serao gravados, mas sao sempre gravados! pagina de administracao diz --not being saved--
-- first login é INFO e não WARNING
-- /review não mostra imagens porque precisa que teste esteja a decorrer...
-- visualizar um teste ja realizado na página de administração
-- Depois da correcção, mostra testes realizados que não foram realizados pelo próprio
-- detectar se janela perde focus e alertar o prof (http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active)
-- server nao esta a receber eventos focus/blur dos utilizadores diferentes de '0', estranho...
-- permitir adicionar imagens nas perguntas.
-- detect_unfocus.js so funciona se estiver inline no html. porquê???
-- inserir novo aluno /admin não fecha.
-- se aluno desistir, ainda fica marcado como online
-- give dá None em vez de 0.0
-- debug mode: log levels not working
-- Se aluno fizer logout, o teste não é gravado e ficamos sem registo do teste que o aluno viu.
-- criar sqlalchemy sessions dentro de app de modo a estarem associadas a requests. ver se é facil usar with db:(...) para criar e fechar sessão.
-- sqlalchemy queixa-se de threads.
-- SQLAlchemy em vez da classe database.
-- replace sys.exit calls
-- if does not find questions, aborts silently
-- argumentos da linha de comando a funcionar.
-- configuracao dos logs cherrypy para se darem bem com os outros
-- browser e ip usados gravado no test.
-- botões allow all/deny all.
-- mostrar botão de reset apenas no final da pagina, com edit para escrever o número.
-- aluno faz login, mas fecha browser, ficando no estado (online,deny). Ao tentar login com outro browser está deny e o prof não consegue pô-lo em allow pois já não está na lista. => solucao é manter todos os alunos numa tabela.
-- pagina de login nao esta a apresentar bem. parece que precisa de autorizacao para aceder a /static...
-- Não mostrar Professor nos activos em /admin
-- /admin mostrar actualizações automaticamente?
-- se no teste uma das "ref" nao existir nos ficheiros de perguntas, rebenta.
-- alunos podem estar online, mas browser perder sessao => nao conseguem mais entrar porque a App pensa que estão online. Permitir login e dar o mesmo teste.
-- pagina de management dos alunos.
-    mostrar online ordenados por hora de login, offline por número.
-    permitir reset da pw e allow/disallow
-- script de correcção pode enviar dicionario yaml com grade e comentarios. ex:
-    grade: 0.5
-    comments: Falhou na função xpto.
-    os comentários são guardados no teste (ficheiro) ou enviados para o browser no modo practice.
-- testar regex na definicao das perguntas. como se faz rawstring em yaml?
-    singlequote? problemas de backslash??? sim... necessário fazer \\ em varios casos, mas não é claro! e.g. \n é convertido em espaço mas \w é convertido em \\ e w. Solução (http://stackoverflow.com/questions/10771163/python-interpreting-a-regex-from-a-yaml-config-file) é fazer
-            correct: !regex '^(yes|no)'
+- abrir o teste numa janela maximizada e que nao permite que o aluno a
+  redimensione/mova? modo kiosk?
+- detectar scroll e enviar posição para servidor (analise de scroll para detectar copianço?)
+- criar perguntas de outros tipos, e.g. associação, ordenação.
+- stress tests. use https://locust.io
diff --git a/demo/demo.yaml b/demo/demo.yaml
index 3db985b..65ec5b6 100644
--- a/demo/demo.yaml
+++ b/demo/demo.yaml
@@ -47,7 +47,7 @@ scale: [0, 20]
 # ----------------------------------------------------------------------------
 # Files to import. Each file contains a list of questions in yaml format.
 files:
-  - questions/questions-tutorial.yaml
+  - questions/tutorial.yaml
 
 # This is the list of questions that will make up the test.
 # The order is preserved.
diff --git a/demo/questions/correct-question.py b/demo/questions/correct-question.py
new file mode 100755
index 0000000..df55039
--- /dev/null
+++ b/demo/questions/correct-question.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+'''
+Demonstação de um script de correcção
+'''
+
+import re
+import sys
+
+s = sys.stdin.read()
+
+ans = set(re.findall(r'[\w]+', s.lower()))  # get words in lowercase
+rgb = set(['red', 'green', 'blue'])     # the correct answer
+
+# a nota é o número de cores certas menos o número de erradas
+grade = max(0,
+            len(rgb.intersection(ans)) - len(ans.difference(rgb))) / 3
+
+if ans == rgb:
+    print('---\n'
+          'grade: 1.0\n'
+          'comments:  Muito bem!')
+
+else:
+    print('---\n'
+          f'grade: {grade}\n'
+          'comments: A resposta correcta é "red green blue".')
diff --git a/demo/questions/correct-timeout.py b/demo/questions/correct-timeout.py
new file mode 100755
index 0000000..c9eb0bf
--- /dev/null
+++ b/demo/questions/correct-timeout.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import sys
+import time
+
+s = sys.stdin.read()
+
+# sleep a lot of time to generate timeout during correction
+time.sleep(100)
+
+# probably this result will not be seen because the script will be interrupted
+print(0.5)
diff --git a/demo/questions/correct/correct-question.py b/demo/questions/correct/correct-question.py
deleted file mode 100755
index df55039..0000000
--- a/demo/questions/correct/correct-question.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env python3
-
-'''
-Demonstação de um script de correcção
-'''
-
-import re
-import sys
-
-s = sys.stdin.read()
-
-ans = set(re.findall(r'[\w]+', s.lower()))  # get words in lowercase
-rgb = set(['red', 'green', 'blue'])     # the correct answer
-
-# a nota é o número de cores certas menos o número de erradas
-grade = max(0,
-            len(rgb.intersection(ans)) - len(ans.difference(rgb))) / 3
-
-if ans == rgb:
-    print('---\n'
-          'grade: 1.0\n'
-          'comments:  Muito bem!')
-
-else:
-    print('---\n'
-          f'grade: {grade}\n'
-          'comments: A resposta correcta é "red green blue".')
diff --git a/demo/questions/correct/correct-timeout.py b/demo/questions/correct/correct-timeout.py
deleted file mode 100755
index c9eb0bf..0000000
--- a/demo/questions/correct/correct-timeout.py
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import time
-
-s = sys.stdin.read()
-
-# sleep a lot of time to generate timeout during correction
-time.sleep(100)
-
-# probably this result will not be seen because the script will be interrupted
-print(0.5)
diff --git a/demo/questions/questions-tutorial.yaml b/demo/questions/questions-tutorial.yaml
deleted file mode 100644
index e999397..0000000
--- a/demo/questions/questions-tutorial.yaml
+++ /dev/null
@@ -1,658 +0,0 @@
----
-# ----------------------------------------------------------------------------
-- type: information
-  ref: tut-test
-  title: Configuração do teste
-  text: |
-    O teste é configurado num ficheiro `yaml`
-    (ver especificação [aqui](https://yaml.org)).
-    A configuração contém a identificação do teste, base de dados dos alunos,
-    ficheiros de perguntas a importar e uma selecção de perguntas e respectivas
-    cotações.
-
-    Exemplo:
-
-    ```yaml
-    ---
-    # --------------------------------------------------------------------------
-    ref: tutorial              # referência, pode ser reusada em vários turnos
-    title: Demonstração        # título da prova
-    database: students.db      # base de dados previamente criada com initdb
-    answers_dir: ans           # directório onde ficam os testes dos alunos
-
-    # opcionais
-    duration: 60               # duração da prova em minutos (default: inf)
-    autosubmit: true           # submissão automática (default: false)
-    show_points: true          # mostra cotação das perguntas (default: true)
-    scale: [0, 20]             # normaliza cotações para o intervalo indicado.
-                               # não normaliza por defeito (default: None)
-
-    # --------------------------------------------------------------------------
-    # Ficheiros de perguntas a importar (relativamente a `questions_dir`)
-    files:
-      - tabelas.yaml
-      - topic1/questions.yaml
-      - topic2/part1/questions.yaml
-      - topic2/part2/questions.yaml
-
-    # --------------------------------------------------------------------------
-    # Especificação das perguntas do teste e respectivas cotações.
-    # O teste é uma lista de perguntas, onde cada pergunta é especificada num
-    # dicionário com a referência da pergunta e a respectiva cotação.
-    questions:
-      - ref: pergunta1
-        points: 3.5
-
-      - ref: pergunta2
-        points: 2.0
-
-      # por defeinto, a cotação da pergunta é 1.0 valor
-      - ref: pergunta3
-
-      # escolhe aleatoriamente uma das variantes da pergunta
-      - ref: [pergunta3a, pergunta3b]
-        points: 0.5
-
-    # --------------------------------------------------------------------------
-    ```
-
-    A ordem das perguntas é mantida quando apresentada no teste.
-
-    O mesmo teste pode ser realizado várias vezes em turnos diferentes, não é
-    necessário alterar nada.
-
-# ----------------------------------------------------------------------------
-- type: information
-  ref: tut-questions
-  title: Especificação das perguntas
-  text: |
-    As perguntas estão definidas num ou mais ficheiros `yaml` como uma lista de
-    perguntas, onde cada pergunta é um dicionário.
-
-    Por exemplo, um ficheiro com o conteúdo abaixo contém duas perguntas, uma
-    de escolha múltipla e outra apenas informativa:
-
-    ```yaml
-    ---
-    #---------------------------------------------------------------------------
-    - type: radio
-      ref: chave-unica-1
-      text: Quanto é $1+1$?
-      options:
-        - 1
-        - 2
-        - 3
-
-    #---------------------------------------------------------------------------
-    - type: information
-      ref: chave-unica-2
-      text: |
-        Quando o texto da pergunta tem várias linhas, dá jeito usar o símbolo
-        `|` de pipe, para indicar que tudo o que estiver indentado faz parte do
-        texto. É o caso desta pergunta.
-
-        O texto das perguntas é escrito em `markdown` e suporta fórmulas em
-        LaTeX.
-
-    #---------------------------------------------------------------------------
-    ```
-
-    As chaves são usadas para construir o teste e não se podem repetir, mesmo
-    em ficheiros diferentes.
-    De seguida mostram-se exemplos dos vários tipos de perguntas.
-
-# ----------------------------------------------------------------------------
-- type: radio
-  ref: tut-radio
-  title: Escolha simples, uma opção correcta.
-  text: |
-    As perguntas de escolha simples, permitem fazer uma pergunta e apresentar
-    várias opções de resposta em que apenas uma delas está certa.
-    A utilização mais simples é a seguinte:
-
-    ```yaml
-    - type: radio
-      ref: pergunta-1
-      title: Escolha simples, uma opção correcta.
-      text: |
-        Bla bla bla.
-      options:
-        - Opção 0
-        - Opção 1
-        - Opção 2
-        - Opção 3
-        - Opção 4
-    ```
-
-    Sem outras configurações, assume-se que a primeira opção é a resposta
-    correcta ("Opção 0" neste caso) e as 5 opções são apresentadas por ordem
-    aleatória.
-
-    Para evitar que os alunos memorizem os textos das opções, podem definir-se
-    várias opções correctas com escrita ligeiramente diferente, sendo escolhida
-    apenas uma delas para apresentação.
-    Por exemplo, se as 2 primeiras opções estiverem correctas e as restantes
-    erradas, e quisermos apresentar ao aluno 3 opções no total, acrescenta-se:
-
-    ```yaml
-      correct: [1, 1, 0, 0, 0]
-      choose: 3
-    ```
-
-    Neste caso, será escolhida uma opção certa e duas erradas.
-    Os valores em `correct` representam o grau de correcção no intervalo [0, 1]
-    onde 1 representa 100% certo e 0 representa 0%. Podem ser usados valores
-    entre 0 e 1, sendo atribuída a respectiva cotação, mas só o valor 1
-    representa uma opção certa.
-
-    Por defeito, as opções são apresentadas por ordem aleatória, mas é possível
-    usar a ordem predefinida. Por exemplo, para manter a ordem e indicar que a
-    resposta correcta é a do meio define-se:
-
-    ```yaml
-      correct: [0, 0, 1, 0, 0]
-      shuffle: false
-    ```
-
-    As respostas erradas descontam, tendo uma cotação de $-1/(n-1)$ do valor da
-    pergunta, onde $n$ é o número de opções apresentadas ao aluno (a ideia é o
-    valor esperado ser zero quando as respostas são aleatórias e uniformemente
-    distribuídas). Para não descontar acrescenta-se:
-
-    ```yaml
-      discount: false
-    ```
-  options:
-    - Opção 0 (certa)
-    - Opção 1 (certa)
-    - Opção 2
-    - Opção 3
-    - Opção 4
-  correct: [1, 1, 0, 0, 0]
-  choose: 3
-  solution: |
-    A solução correcta é a **Opção 0** ou a **Opção 1**.
-
-# ----------------------------------------------------------------------------
-- type: checkbox
-  ref: tut-checkbox
-  title: Escolha múltipla, várias opções correctas
-  text: |
-    As perguntas de escolha múltipla permitem apresentar um conjunto de opções
-    podendo ser seleccionadas várias em simultâneo.
-    Funcionam como múltiplas perguntas independentes de resposta sim/não.
-
-    As respostas que devem ou não ser seleccionadas são indicadas com `1` e `0`
-    ou com booleanos `true` e `false`.
-    Cada resposta errada desconta um valor que é o simétrico da resposta certa.
-    Se acertar uma opção ganha `+1`, se errar obtém `-1`.
-
-    ```yaml
-    - type: checkbox
-      ref: tut-checkbox
-      title: Escolha múltipla, várias opções correctas
-      text: |
-        Bla bla bla.
-      options:
-        - Opção 0 (certa)
-        - Opção 1
-        - Opção 2
-        - Opção 3 (certa)
-        - Opção 4
-      correct: [1, 0, 0, 1, 0]
-    ```
-
-    Neste exemplo, seleccionando as opções 0 e 3 obtém-se cotação `+1` em cada
-    uma, enquanto que seleccionando erradamente as opções 1, 2 e 4 obtém-se
-    cotação `-1`.
-    Do mesmo modo, não seleccionando as opções certas 0 e 3 obtém-se a cotação
-    `-1` em cada uma, e não seleccionando (correctamente) as 1, 2 e 4 obtém-se
-    `+1` em cada.
-
-    *(Neste tipo de perguntas não há forma de responder a apenas algumas delas,
-    são sempre todas corrigidas. Se um aluno só sabe a resposta a algumas das
-    opções, deve ter cuidado porque as restantes também serão classificadas e
-    arrisca-se a ter cotação negativa)*
-
-    Cada opção pode opcionalmente ser escrita como uma afirmação e o seu
-    contrário, de maneira a aumentar a variabilidade dos textos.
-    Por exemplo:
-
-    ```yaml
-      options:
-        - ['O céu é azul', 'O céu não é azul']
-        - ['Um triangulo tem 3 lados', 'Um triangulo tem 2 lados']
-        - O nosso planeta tem um satélite natural
-      correct: [1, 1, 1]
-    ```
-
-    Assume-se que a primeira alternativa de cada opção tem a cotação indicada
-    em `correct`, enquanto a segunda alternativa tem a cotação contrária.
-
-    Tal como nas perguntas do tipo `radio`, podem ser usadas as configurações
-    `shuffle` e `discount` com valor `false` para as desactivar.
-    Se `discount` é `false` então as respostas erradas têm cotação 0 em vez do
-    simétrico.
-  options:
-    - ['Opção 0 (sim)', 'Opção 0 (não)']
-    - ['Opção 1 (não)', 'Opção 1 (sim)']
-    - Opção 2 (não)
-    - Opção 3 (sim)
-  correct: [1, 0, 0, 1]
-  shuffle: false
-
-# ----------------------------------------------------------------------------
-- type: text
-  ref: tut-text
-  title: Resposta de texto em linha
-  text: |
-    Este tipo de perguntas permite uma resposta numa linha de texto. A resposta
-    está correcta se coincidir exactamente com alguma das respostas admissíveis.
-
-    ```yaml
-    - type: text
-      ref: tut-text
-      title: Resposta de texto em linha
-      text: |
-        De que cor é o céu?
-
-        Escreva a resposta em português.
-      correct: ['azul', 'Azul', 'AZUL']
-    ```
-
-    Neste caso, as respostas aceites são `azul`, `Azul` ou `AZUL`.
-
-    Em alguns casos pode ser conveniente transformar a resposta antes de a
-    comparar, por exemplo para remover espaços ou converter para minúsculas.
-    A opção `transform` permite dar uma sequência de transformações a aplicar à
-    resposta do aluno, por exemplo:
-
-    ```yaml
-      transform: ['trim', 'lower']
-      correct: ['azul']
-    ```
-
-    Estão disponíveis as seguintes transformações:
-
-    * `trim` remove os espaços do início e fim da resposta, os espaços do meio
-      mantêm-se inalterados.
-    * `remove_space` remove todos os espaços (início, meio e fim).
-    * `normalize_space` remove espaços do início e fim (trim), e substitui
-      múltiplos espaços por um único espaço (no meio).
-    * `lower` e `upper` convertem respectivamente para minúsculas e maiúsculas.
-  transform: ['trim', 'lower']
-  correct: ['azul']
-  solution: |
-    O céu é `azul`, pelo menos durante o dia e se estiver bom tempo...
-
-# ---------------------------------------------------------------------------
-- type: text-regex
-  ref: tut-text-regex
-  title: Resposta de texto em linha, expressão regular
-  text: |
-    Este tipo de pergunta é semelhante à linha de texto da pergunta anterior.
-    A única diferença é que esta é validada por uma expressão regular.
-
-    ```yaml
-    - type: text-regex
-      ref: tut-text-regex
-      title: Resposta de texto em linha
-      text: |
-        Bla bla bla
-      correct: '(VERDE|[Vv]erde)'
-    ```
-
-    Neste exemplo a expressão regular é `(VERDE|[Vv]erde)`.
-
-    Também se pode dar uma lista de expressões regulares. Nesse caso a resposta
-    é considerada correcta se fizer match com alguma delas.
-    No exemplo acima, poder-se-ia ter usado uma lista
-
-    ```yaml
-      correct:
-        - 'VERDE'
-        - '[Vv]erde'
-    ```
-
-    ---
-
-    **Atenção:** A expressão regular deve seguir as convenções da suportadas em
-    python (ver
-    [Regular expression operations](https://docs.python.org/3/library/re.html)).
-    Em particular, a expressão regular acima também aceita a resposta
-    `verde, azul`.
-    Deve marcar-se o início e final `^(VERDE|[Vv]erde)$` para evitar estas
-    situações.
-
-  correct: '(VERDE|[Vv]erde)'
-  solution: |
-    Deveria ter escrito `VERDE` ou `Verde` ou `verde`.
-
-# ---------------------------------------------------------------------------
-- type: numeric-interval
-  ref: tut-numeric-interval
-  title: Resposta numérica em linha de texto
-  text: |
-    Este tipo de perguntas esperam uma resposta numérica (vírgula flutuante).
-    O resultado é considerado correcto se estiver dentro do intervalo fechado
-    indicado.
-
-    ```yaml
-    - type: numeric-interval
-      ref: tut-numeric-interval
-      title: Resposta numérica em linha de texto
-      text: |
-        Escreva o número $\pi$ com pelo menos duas casa decimais.
-      correct: [3.14, 3.15]
-    ```
-
-    Neste exemplo o intervalo de respostas correctas é o intervalo fechado
-    [3.14, 3.15].
-
-    Se em vez de dar um intervalo, apenas for indicado um valor numérico $n$,
-    este é automaticamente convertido para para um intervalo $[n,n]$.
-
-    **Atenção:** as respostas têm de usar o ponto como separador decimal.
-    Em geral são aceites números inteiros, como `123`,
-    ou em vírgula flutuante, como em `0.23`, `1e-3`.
-  correct: [3.14, 3.15]
-  solution: |
-    Sabemos que $\pi\approx 3.14159265359$.
-    Portanto, um exemplo de uma resposta correcta é `3.1416`.
-
-# ---------------------------------------------------------------------------
-- type: textarea
-  ref: tut-textarea
-  title: Resposta em múltiplas linhas de texto
-  text: |
-    Este tipo de perguntas permitem respostas em múltiplas linhas de texto e
-    são as mais flexíveis.
-
-    A resposta é enviada para um programa externo para ser avaliada.
-    O programa externo é um programa que tem de ser executável pelo pelo
-    sistema operativo (pode ser um binário ou script desde que o respectivo
-    interpretador instalado).
-    Este programa externo recebe a resposta submetida pelo aluno via `stdin` e
-    devolve a classificação via `stdout`.
-    Exemplo:
-
-    ```yaml
-    - type: textarea
-      ref: tut-textarea
-      title: Resposta em múltiplas linhas de texto
-      text: |
-        Bla bla bla
-      correct: correct/correct-question.py   # programa a executar
-      timeout: 5
-    ```
-
-    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 é executado num processo separado e a interacção faz-se
-    via stdin/stdout.
-
-    Se o programa externo exceder o `timeout` indicado (em segundos),
-    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.
-    Pode simplesmente fazer `print` da classificação como um número em vírgula
-    flutuante, por exemplo
-
-    ```yaml
-    0.75
-    ```
-
-    ou opcionalmente escrever em formato json ou yaml, eventualmente com um
-    comentário que será arquivado com o teste.
-    Exemplo:
-
-    ```yaml
-    grade: 0.5
-    comments: |
-      Esqueceu-se de algumas cores.
-      A resposta correcta era `red green blue`.
-    ```
-
-    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 como neste caso (use `answer: texto`).
-  correct: correct/correct-question.py
-  timeout: 5
-  tests_right:
-    - 'red green blue'
-  # tests_wrong:
-  #   - 'blue gray yellow'
-
-# ---------------------------------------------------------------------------
-- type: information
-  ref: tut-information
-  title: Texto informativo
-  text: |
-    As perguntas deste tipo não contam para avaliação. O objectivo é fornecer
-    instruções para os alunos, por exemplo tabelas para consulta, fórmulas, etc.
-    Nesta, tal como em todos os tipos de perguntas podem escrever-se fórmulas
-    em LaTeX. Exemplo:
-
-    A distribuição gaussiana $\mathcal{N}(x\mid\mu,\sigma^2)$ é
-    definida pela função densidade de probabilidade
-
-    $$
-    p(x) = \frac{1}{\sqrt{2\pi\sigma^2}}
-           \exp\Big({-\frac{(x-\mu)^2}{2\sigma^2}}\Big).
-    $$
-
-    ---
-
-    ```yaml
-    - type: information
-      ref: tut-information
-      title: Texto informativo
-      text: |
-        A distribuição gaussiana $\mathcal{N}(x\mid\mu,\sigma^2)$ é
-        definida pela função densidade de probabilidade
-
-        $$
-        p(x) = \frac{1}{\sqrt{2\pi\sigma^2}}
-               \exp\Big({-\frac{(x-\mu)^2}{2\sigma^2}}\Big).
-        $$
-    ```
-
-# ---------------------------------------------------------------------------
-- type: success
-  ref: tut-success
-  title: Texto informativo (sucesso)
-  text: |
-    Não conta para avaliação. É apenas o aspecto gráfico que muda.
-
-    Um pedaço de código em linha, por exemplo `x = sqrt(z)` é marcado com uma
-    fonte e cor diferente.
-    Também se podem escrever troços de código coloridos conforme a linguagem:
-
-    ```C
-    int main() {
-        printf("Hello world!");
-        return 0;   // comentario
-    }
-    ```
-
-    ---
-
-    ```yaml
-    - type: success
-      ref: tut-success
-      title: Texto informativo (sucesso)
-      text: |
-        Não conta para avaliação. É apenas o aspecto gráfico que muda.
-
-        Um pedaço de código em linha, por exemplo `x = sqrt(z)` é marcado com
-        uma fonte e cor diferente.
-        Também se podem escrever troços de código coloridos conforme a
-        linguagem:
-
-        ```C
-        int main() {
-            printf("Hello world!");
-            return 0;   // comentario
-        }
-        ```
-
-    ```
-
-# ---------------------------------------------------------------------------
-- type: warning
-  ref: tut-warning
-  title: Texto informativo (aviso)
-  text: |
-    Não conta para avaliação.
-
-    Neste exemplo mostramos como se pode construir uma tabela como a seguinte:
-
-    Left             | Center           | Right
-    -----------------|:----------------:|----------:
-    *hello*          | $\sin(x^2)$      | $1600.00
-    **world**        | $\frac{1}{2\pi}$ |   $12.50
-    `code`           | $\sqrt{\pi}$     |    $1.99
-
-    As tabelas podem conter Markdown e LaTeX e permitem alinhamento das colunas,
-    mas o markdown é muito simples e não permite mais funcionalidades.
-
-    ---
-
-    ```yaml
-    - type: warning
-      ref: tut-warning
-      title: Texto informativo (aviso)
-      text: |
-        Bla bla bla
-
-        Left             | Center           | Right
-        -----------------|:----------------:|----------:
-        *hello*          | $\sin(x^2)$      | $1600.00
-        **world**        | $\frac{1}{2\pi}$ |   $12.50
-        `code`           | $\sqrt{\pi}$     |    $1.99
-    ```
-
-# ----------------------------------------------------------------------------
-- type: alert
-  ref: tut-alert1
-  title: Texto informativo (perigo) - versão 1
-  text: |
-    Não conta para avaliação. Texto importante.
-
-    ![planetas](planets.png "Planetas do Sistema Solar")
-
-    As imagens podem ser adicionadas usando a notação standard em markdown. Há
-    duas possibilidads:
-
-    - Imagens inline: não têm título definido e podem ser incluídas no meio de
-    uma linha de texto usando`![alt text](image.jpg)`.
-    - Imagens centradas com título: `![alt text](image.jpg "Título da imagem")`.
-    O título é colocado por baixo da imagem. Pode ser uma string vazia.
-
-- type: alert
-  ref: tut-alert2
-  title: Texto informativo (perigo) - versão 2
-  text: |
-    Não conta para avaliação. Texto importante.
-
-    ![planetas](planets.png "Planetas do Sistema Solar")
-
-    As imagens podem ser adicionadas usando a notação standard em markdown. Há
-    duas possibilidads:
-
-    - Imagens inline: não têm título definido e podem ser incluídas no meio de
-    uma linha de texto usando `![alt text](image.jpg)`.
-    - Imagens centradas com título: `![alt text](image.jpg "Título da imagem")`.
-    O título é colocado por baixo da imagem. Pode ser uma string vazia.
-
-# ----------------------------------------------------------------------------
-- type: information
-  text: |
-    This question is not included in the test and will not show up.
-    It also lacks a "ref" and is automatically named
-    `questions/questions-tutorial.yaml:0013`.
-    A warning is shown on the console about this.
-    The number at the end is the index position of this question.
-    Indices start at 0.
-
-# ----------------------------------------------------------------------------
-- type: generator
-  ref: tut-generator
-  script: generate-question.py
-  args: [1, 100]
-
-# ----------------------------------------------------------------------------
-- type: information
-  ref: tut-yamllint
-  title: Sugestões para validar yaml
-  text: |
-    Como os testes e perguntas são ficheiros `yaml`, é conveniente validar se
-    estão correctamente definitos. Um *linter* recomendado é o `yamllint`. Pode
-    ser instalado com `pip install yamllint` e usado do seguinte modo:
-
-    ```sh
-        yamllint test.yaml
-        yamllint questions.yaml
-    ```
-
-    No caso de programas geradores de perguntas e programas de correcção de
-    respostas pode usar-se um *pipe*:
-
-    ```sh
-        generate-question | yamllint -
-        correct-answer | yamllint -
-    ```
-
-# ----------------------------------------------------------------------------
-# - type: code
-#   ref: tut-code
-#   title: Submissão de código (JOBE)
-#   text: |
-#     É possível enviar código para ser compilado e executado por um servidor
-#     JOBE instalado separadamente, ver [JOBE](https://github.com/trampgeek/jobe).
-
-#     ```yaml
-#     - type: code
-#       ref: tut-code
-#       title: Submissão de código (JOBE)
-#       text: |
-#         Escreva um programa em C que recebe uma string no standard input e
-#         mostra a mensagem `hello ` seguida da string.
-#         Por exemplo, se o input for `Maria`, o output deverá ser `hello Maria`.
-#       language: c
-#       correct:
-#         - stdin: 'Maria'
-#           stdout: 'hello Maria'
-#         - stdin: 'xyz'
-#           stdout: 'hello xyz'
-#     ```
-
-#     Existem várias linguagens suportadas pelo servidor JOBE (C, C++, Java,
-#     Python2, Python3, Octave, Pascal, PHP).
-#     O campo `correct` deverá ser uma lista de casos a testar.
-#     Se um caso incluir `stdin`, este será enviado para o programa e o `stdout`
-#     obtido será comparado com o declarado. A pergunta é considerada correcta se
-#     todos os outputs coincidirem.
-
-#     Por defeito é o usado o servidor JOBE declarado no teste. Para usar outro
-#     diferente nesta pergunta usa-se a opção `server: 127.0.0.1` com o endereço
-#     apropriado.
-#   answer: |
-#     #include 
-#     int main() {
-#       char name[20];
-#       scanf("%s", name);
-#       printf("hello %s", name);
-#     }
-#   # server: 192.168.1.85
-#   language: c
-#   correct:
-#     - stdin: 'Maria'
-#       stdout: 'hello Maria'
-#     - stdin: 'xyz'
-#       stdout: 'hello xyz'
diff --git a/demo/questions/tutorial.yaml b/demo/questions/tutorial.yaml
new file mode 100644
index 0000000..49e2473
--- /dev/null
+++ b/demo/questions/tutorial.yaml
@@ -0,0 +1,609 @@
+---
+# ----------------------------------------------------------------------------
+- type: information
+  ref: tut-test
+  title: Configuração do teste
+  text: |
+    O teste é configurado num ficheiro `yaml`
+    (ver especificação [aqui](https://yaml.org)).
+    A configuração contém a identificação do teste, base de dados dos alunos,
+    ficheiros de perguntas a importar e uma selecção de perguntas e respectivas
+    cotações.
+
+    Exemplo:
+
+    ```yaml
+    ---
+    # --------------------------------------------------------------------------
+    ref: tutorial              # referência, pode ser reusada em vários turnos
+    title: Demonstração        # título da prova
+    database: students.db      # base de dados previamente criada com initdb
+    answers_dir: ans           # directório onde ficam os testes dos alunos
+
+    # opcionais
+    duration: 60               # duração da prova em minutos (default: inf)
+    autosubmit: true           # submissão automática (default: false)
+    show_points: true          # mostra cotação das perguntas (default: true)
+    scale: [0, 20]             # normaliza cotações para o intervalo indicado.
+                               # não normaliza por defeito (default: None)
+
+    # --------------------------------------------------------------------------
+    # Ficheiros de perguntas a importar (relativamente a `questions_dir`)
+    files:
+      - tabelas.yaml
+      - topic1/questions.yaml
+      - topic2/part1/questions.yaml
+      - topic2/part2/questions.yaml
+
+    # --------------------------------------------------------------------------
+    # Especificação das perguntas do teste e respectivas cotações.
+    # O teste é uma lista de perguntas, onde cada pergunta é especificada num
+    # dicionário com a referência da pergunta e a respectiva cotação.
+    questions:
+      - ref: pergunta1
+        points: 3.5
+
+      - ref: pergunta2
+        points: 2.0
+
+      # por defeinto, a cotação da pergunta é 1.0 valor
+      - ref: pergunta3
+
+      # escolhe aleatoriamente uma das variantes da pergunta
+      - ref: [pergunta3a, pergunta3b]
+        points: 0.5
+
+    # --------------------------------------------------------------------------
+    ```
+
+    A ordem das perguntas é mantida quando apresentada no teste.
+
+    O mesmo teste pode ser realizado várias vezes em turnos diferentes, não é
+    necessário alterar nada.
+
+# ----------------------------------------------------------------------------
+- type: information
+  ref: tut-questions
+  title: Especificação das perguntas
+  text: |
+    As perguntas estão definidas num ou mais ficheiros `yaml` como uma lista de
+    perguntas, onde cada pergunta é um dicionário.
+
+    Por exemplo, um ficheiro com o conteúdo abaixo contém duas perguntas, uma
+    de escolha múltipla e outra apenas informativa:
+
+    ```yaml
+    ---
+    #---------------------------------------------------------------------------
+    - type: radio
+      ref: chave-unica-1
+      text: Quanto é $1+1$?
+      options:
+        - 1
+        - 2
+        - 3
+
+    #---------------------------------------------------------------------------
+    - type: information
+      ref: chave-unica-2
+      text: |
+        Quando o texto da pergunta tem várias linhas, dá jeito usar o símbolo
+        `|` de pipe, para indicar que tudo o que estiver indentado faz parte do
+        texto. É o caso desta pergunta.
+
+        O texto das perguntas é escrito em `markdown` e suporta fórmulas em
+        LaTeX.
+
+    #---------------------------------------------------------------------------
+    ```
+
+    As chaves são usadas para construir o teste e não se podem repetir, mesmo
+    em ficheiros diferentes.
+    De seguida mostram-se exemplos dos vários tipos de perguntas.
+
+# ----------------------------------------------------------------------------
+- type: radio
+  ref: tut-radio
+  title: Escolha simples, uma opção correcta.
+  text: |
+    As perguntas de escolha simples, permitem fazer uma pergunta e apresentar
+    várias opções de resposta em que apenas uma delas está certa.
+    A utilização mais simples é a seguinte:
+
+    ```yaml
+    - type: radio
+      ref: pergunta-1
+      title: Escolha simples, uma opção correcta.
+      text: |
+        Bla bla bla.
+      options:
+        - Opção 0
+        - Opção 1
+        - Opção 2
+        - Opção 3
+        - Opção 4
+    ```
+
+    Sem outras configurações, assume-se que a primeira opção é a resposta
+    correcta ("Opção 0" neste caso) e as 5 opções são apresentadas por ordem
+    aleatória.
+
+    Para evitar que os alunos memorizem os textos das opções, podem definir-se
+    várias opções correctas com escrita ligeiramente diferente, sendo escolhida
+    apenas uma delas para apresentação.
+    Por exemplo, se as 2 primeiras opções estiverem correctas e as restantes
+    erradas, e quisermos apresentar ao aluno 3 opções no total, acrescenta-se:
+
+    ```yaml
+      correct: [1, 1, 0, 0, 0]
+      choose: 3
+    ```
+
+    Neste caso, será escolhida uma opção certa e duas erradas.
+    Os valores em `correct` representam o grau de correcção no intervalo [0, 1]
+    onde 1 representa 100% certo e 0 representa 0%. Podem ser usados valores
+    entre 0 e 1, sendo atribuída a respectiva cotação, mas só o valor 1
+    representa uma opção certa.
+
+    Por defeito, as opções são apresentadas por ordem aleatória, mas é possível
+    usar a ordem predefinida. Por exemplo, para manter a ordem e indicar que a
+    resposta correcta é a do meio define-se:
+
+    ```yaml
+      correct: [0, 0, 1, 0, 0]
+      shuffle: false
+    ```
+
+    As respostas erradas descontam, tendo uma cotação de $-1/(n-1)$ do valor da
+    pergunta, onde $n$ é o número de opções apresentadas ao aluno (a ideia é o
+    valor esperado ser zero quando as respostas são aleatórias e uniformemente
+    distribuídas). Para não descontar acrescenta-se:
+
+    ```yaml
+      discount: false
+    ```
+  options:
+    - Opção 0 (certa)
+    - Opção 1 (certa)
+    - Opção 2
+    - Opção 3
+    - Opção 4
+  correct: [1, 1, 0, 0, 0]
+  choose: 3
+  solution: |
+    A solução correcta é a **Opção 0** ou a **Opção 1**.
+
+# ----------------------------------------------------------------------------
+- type: checkbox
+  ref: tut-checkbox
+  title: Escolha múltipla, várias opções correctas
+  text: |
+    As perguntas de escolha múltipla permitem apresentar um conjunto de opções
+    podendo ser seleccionadas várias em simultâneo.
+    Funcionam como múltiplas perguntas independentes de resposta sim/não.
+
+    As respostas que devem ou não ser seleccionadas são indicadas com `1` e `0`
+    ou com booleanos `true` e `false`.
+    Cada resposta errada desconta um valor que é o simétrico da resposta certa.
+    Se acertar uma opção ganha `+1`, se errar obtém `-1`.
+
+    ```yaml
+    - type: checkbox
+      ref: tut-checkbox
+      title: Escolha múltipla, várias opções correctas
+      text: |
+        Bla bla bla.
+      options:
+        - Opção 0 (certa)
+        - Opção 1
+        - Opção 2
+        - Opção 3 (certa)
+        - Opção 4
+      correct: [1, 0, 0, 1, 0]
+    ```
+
+    Neste exemplo, seleccionando as opções 0 e 3 obtém-se cotação `+1` em cada
+    uma, enquanto que seleccionando erradamente as opções 1, 2 e 4 obtém-se
+    cotação `-1`.
+    Do mesmo modo, não seleccionando as opções certas 0 e 3 obtém-se a cotação
+    `-1` em cada uma, e não seleccionando (correctamente) as 1, 2 e 4 obtém-se
+    `+1` em cada.
+
+    *(Neste tipo de perguntas não há forma de responder a apenas algumas delas,
+    são sempre todas corrigidas. Se um aluno só sabe a resposta a algumas das
+    opções, deve ter cuidado porque as restantes também serão classificadas e
+    arrisca-se a ter cotação negativa)*
+
+    Cada opção pode opcionalmente ser escrita como uma afirmação e o seu
+    contrário, de maneira a aumentar a variabilidade dos textos.
+    Por exemplo:
+
+    ```yaml
+      options:
+        - ['O céu é azul', 'O céu não é azul']
+        - ['Um triangulo tem 3 lados', 'Um triangulo tem 2 lados']
+        - O nosso planeta tem um satélite natural
+      correct: [1, 1, 1]
+    ```
+
+    Assume-se que a primeira alternativa de cada opção tem a cotação indicada
+    em `correct`, enquanto a segunda alternativa tem a cotação contrária.
+
+    Tal como nas perguntas do tipo `radio`, podem ser usadas as configurações
+    `shuffle` e `discount` com valor `false` para as desactivar.
+    Se `discount` é `false` então as respostas erradas têm cotação 0 em vez do
+    simétrico.
+  options:
+    - ['Opção 0 (sim)', 'Opção 0 (não)']
+    - ['Opção 1 (não)', 'Opção 1 (sim)']
+    - Opção 2 (não)
+    - Opção 3 (sim)
+  correct: [1, 0, 0, 1]
+  shuffle: false
+
+# ----------------------------------------------------------------------------
+- type: text
+  ref: tut-text
+  title: Resposta de texto em linha
+  text: |
+    Este tipo de perguntas permite uma resposta numa linha de texto. A resposta
+    está correcta se coincidir exactamente com alguma das respostas admissíveis.
+
+    ```yaml
+    - type: text
+      ref: tut-text
+      title: Resposta de texto em linha
+      text: |
+        De que cor é o céu?
+
+        Escreva a resposta em português.
+      correct: ['azul', 'Azul', 'AZUL']
+    ```
+
+    Neste caso, as respostas aceites são `azul`, `Azul` ou `AZUL`.
+
+    Em alguns casos pode ser conveniente transformar a resposta antes de a
+    comparar, por exemplo para remover espaços ou converter para minúsculas.
+    A opção `transform` permite dar uma sequência de transformações a aplicar à
+    resposta do aluno, por exemplo:
+
+    ```yaml
+      transform: ['trim', 'lower']
+      correct: ['azul']
+    ```
+
+    Estão disponíveis as seguintes transformações:
+
+    * `trim` remove os espaços do início e fim da resposta, os espaços do meio
+      mantêm-se inalterados.
+    * `remove_space` remove todos os espaços (início, meio e fim).
+    * `normalize_space` remove espaços do início e fim (trim), e substitui
+      múltiplos espaços por um único espaço (no meio).
+    * `lower` e `upper` convertem respectivamente para minúsculas e maiúsculas.
+  transform: ['trim', 'lower']
+  correct: ['azul']
+  solution: |
+    O céu é `azul`, pelo menos durante o dia e se estiver bom tempo...
+
+# ---------------------------------------------------------------------------
+- type: text-regex
+  ref: tut-text-regex
+  title: Resposta de texto em linha, expressão regular
+  text: |
+    Este tipo de pergunta é semelhante à linha de texto da pergunta anterior.
+    A única diferença é que esta é validada por uma expressão regular.
+
+    ```yaml
+    - type: text-regex
+      ref: tut-text-regex
+      title: Resposta de texto em linha
+      text: |
+        Bla bla bla
+      correct: '(VERDE|[Vv]erde)'
+    ```
+
+    Neste exemplo a expressão regular é `(VERDE|[Vv]erde)`.
+
+    Também se pode dar uma lista de expressões regulares. Nesse caso a resposta
+    é considerada correcta se fizer match com alguma delas.
+    No exemplo acima, poder-se-ia ter usado uma lista
+
+    ```yaml
+      correct:
+        - 'VERDE'
+        - '[Vv]erde'
+    ```
+
+    ---
+
+    **Atenção:** A expressão regular deve seguir as convenções da suportadas em
+    python (ver
+    [Regular expression operations](https://docs.python.org/3/library/re.html)).
+    Em particular, a expressão regular acima também aceita a resposta
+    `verde, azul`.
+    Deve marcar-se o início e final `^(VERDE|[Vv]erde)$` para evitar estas
+    situações.
+
+  correct: '(VERDE|[Vv]erde)'
+  solution: |
+    Deveria ter escrito `VERDE` ou `Verde` ou `verde`.
+
+# ---------------------------------------------------------------------------
+- type: numeric-interval
+  ref: tut-numeric-interval
+  title: Resposta numérica em linha de texto
+  text: |
+    Este tipo de perguntas esperam uma resposta numérica (vírgula flutuante).
+    O resultado é considerado correcto se estiver dentro do intervalo fechado
+    indicado.
+
+    ```yaml
+    - type: numeric-interval
+      ref: tut-numeric-interval
+      title: Resposta numérica em linha de texto
+      text: |
+        Escreva o número $\pi$ com pelo menos duas casa decimais.
+      correct: [3.14, 3.15]
+    ```
+
+    Neste exemplo o intervalo de respostas correctas é o intervalo fechado
+    [3.14, 3.15].
+
+    Se em vez de dar um intervalo, apenas for indicado um valor numérico $n$,
+    este é automaticamente convertido para para um intervalo $[n,n]$.
+
+    **Atenção:** as respostas têm de usar o ponto como separador decimal.
+    Em geral são aceites números inteiros, como `123`,
+    ou em vírgula flutuante, como em `0.23`, `1e-3`.
+  correct: [3.14, 3.15]
+  solution: |
+    Sabemos que $\pi\approx 3.14159265359$.
+    Portanto, um exemplo de uma resposta correcta é `3.1416`.
+
+# ---------------------------------------------------------------------------
+- type: textarea
+  ref: tut-textarea
+  title: Resposta em múltiplas linhas de texto
+  text: |
+    Este tipo de perguntas permitem respostas em múltiplas linhas de texto e
+    são as mais flexíveis.
+
+    A resposta é enviada para um programa externo para ser avaliada.
+    O programa externo é um programa que tem de ser executável pelo pelo
+    sistema operativo (pode ser um binário ou script desde que o respectivo
+    interpretador instalado).
+    Este programa externo recebe a resposta submetida pelo aluno via `stdin` e
+    devolve a classificação via `stdout`.
+    Exemplo:
+
+    ```yaml
+    - type: textarea
+      ref: tut-textarea
+      title: Resposta em múltiplas linhas de texto
+      text: |
+        Bla bla bla
+      correct: correct-question.py   # programa a executar
+      timeout: 5
+    ```
+
+    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 é executado num processo separado e a interacção faz-se
+    via stdin/stdout.
+
+    Se o programa externo exceder o `timeout` indicado (em segundos),
+    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.
+    Pode simplesmente fazer `print` da classificação como um número em vírgula
+    flutuante, por exemplo
+
+    ```yaml
+    0.75
+    ```
+
+    ou opcionalmente escrever em formato json ou yaml, eventualmente com um
+    comentário que será arquivado com o teste.
+    Exemplo:
+
+    ```yaml
+    grade: 0.5
+    comments: |
+      Esqueceu-se de algumas cores.
+      A resposta correcta era `red green blue`.
+    ```
+
+    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 como neste caso (use `answer: texto`).
+  correct: correct-question.py
+  timeout: 5
+  tests_right:
+    - 'red green blue'
+  # tests_wrong:
+  #   - 'blue gray yellow'
+
+# ---------------------------------------------------------------------------
+- type: information
+  ref: tut-information
+  title: Texto informativo
+  text: |
+    As perguntas deste tipo não contam para avaliação. O objectivo é fornecer
+    instruções para os alunos, por exemplo tabelas para consulta, fórmulas, etc.
+    Nesta, tal como em todos os tipos de perguntas podem escrever-se fórmulas
+    em LaTeX. Exemplo:
+
+    A distribuição gaussiana $\mathcal{N}(x\mid\mu,\sigma^2)$ é
+    definida pela função densidade de probabilidade
+
+    $$
+    p(x) = \frac{1}{\sqrt{2\pi\sigma^2}}
+           \exp\Big({-\frac{(x-\mu)^2}{2\sigma^2}}\Big).
+    $$
+
+    ---
+
+    ```yaml
+    - type: information
+      ref: tut-information
+      title: Texto informativo
+      text: |
+        A distribuição gaussiana $\mathcal{N}(x\mid\mu,\sigma^2)$ é
+        definida pela função densidade de probabilidade
+
+        $$
+        p(x) = \frac{1}{\sqrt{2\pi\sigma^2}}
+               \exp\Big({-\frac{(x-\mu)^2}{2\sigma^2}}\Big).
+        $$
+    ```
+
+# ---------------------------------------------------------------------------
+- type: success
+  ref: tut-success
+  title: Texto informativo (sucesso)
+  text: |
+    Não conta para avaliação. É apenas o aspecto gráfico que muda.
+
+    Um pedaço de código em linha, por exemplo `x = sqrt(z)` é marcado com uma
+    fonte e cor diferente.
+    Também se podem escrever troços de código coloridos conforme a linguagem:
+
+    ```C
+    int main() {
+        printf("Hello world!");
+        return 0;   // comentario
+    }
+    ```
+
+    ---
+
+    ```yaml
+    - type: success
+      ref: tut-success
+      title: Texto informativo (sucesso)
+      text: |
+        Não conta para avaliação. É apenas o aspecto gráfico que muda.
+
+        Um pedaço de código em linha, por exemplo `x = sqrt(z)` é marcado com
+        uma fonte e cor diferente.
+        Também se podem escrever troços de código coloridos conforme a
+        linguagem:
+
+        ```C
+        int main() {
+            printf("Hello world!");
+            return 0;   // comentario
+        }
+        ```
+
+    ```
+
+# ---------------------------------------------------------------------------
+- type: warning
+  ref: tut-warning
+  title: Texto informativo (aviso)
+  text: |
+    Não conta para avaliação.
+
+    Neste exemplo mostramos como se pode construir uma tabela como a seguinte:
+
+    Left             | Center           | Right
+    -----------------|:----------------:|----------:
+    *hello*          | $\sin(x^2)$      | $1600.00
+    **world**        | $\frac{1}{2\pi}$ |   $12.50
+    `code`           | $\sqrt{\pi}$     |    $1.99
+
+    As tabelas podem conter Markdown e LaTeX e permitem alinhamento das colunas,
+    mas o markdown é muito simples e não permite mais funcionalidades.
+
+    ---
+
+    ```yaml
+    - type: warning
+      ref: tut-warning
+      title: Texto informativo (aviso)
+      text: |
+        Bla bla bla
+
+        Left             | Center           | Right
+        -----------------|:----------------:|----------:
+        *hello*          | $\sin(x^2)$      | $1600.00
+        **world**        | $\frac{1}{2\pi}$ |   $12.50
+        `code`           | $\sqrt{\pi}$     |    $1.99
+    ```
+
+# ----------------------------------------------------------------------------
+- type: alert
+  ref: tut-alert1
+  title: Texto informativo (perigo) - versão 1
+  text: |
+    Não conta para avaliação. Texto importante.
+
+    ![planetas](planets.png "Planetas do Sistema Solar")
+
+    As imagens podem ser adicionadas usando a notação standard em markdown. Há
+    duas possibilidads:
+
+    - Imagens inline: não têm título definido e podem ser incluídas no meio de
+    uma linha de texto usando`![alt text](image.jpg)`.
+    - Imagens centradas com título: `![alt text](image.jpg "Título da imagem")`.
+    O título é colocado por baixo da imagem. Pode ser uma string vazia.
+
+- type: alert
+  ref: tut-alert2
+  title: Texto informativo (perigo) - versão 2
+  text: |
+    Não conta para avaliação. Texto importante.
+
+    ![planetas](planets.png "Planetas do Sistema Solar")
+
+    As imagens podem ser adicionadas usando a notação standard em markdown. Há
+    duas possibilidads:
+
+    - Imagens inline: não têm título definido e podem ser incluídas no meio de
+    uma linha de texto usando `![alt text](image.jpg)`.
+    - Imagens centradas com título: `![alt text](image.jpg "Título da imagem")`.
+    O título é colocado por baixo da imagem. Pode ser uma string vazia.
+
+# ----------------------------------------------------------------------------
+- type: information
+  text: |
+    This question is not included in the test and will not show up.
+    It also lacks a "ref" and is automatically named
+    `questions/questions-tutorial.yaml:0013`.
+    A warning is shown on the console about this.
+    The number at the end is the index position of this question.
+    Indices start at 0.
+
+# ----------------------------------------------------------------------------
+- type: generator
+  ref: tut-generator
+  script: generate-question.py
+  args: [1, 100]
+
+# ----------------------------------------------------------------------------
+- type: information
+  ref: tut-yamllint
+  title: Sugestões para validar yaml
+  text: |
+    Como os testes e perguntas são ficheiros `yaml`, é conveniente validar se
+    estão correctamente definitos. Um *linter* recomendado é o `yamllint`. Pode
+    ser instalado com `pip install yamllint` e usado do seguinte modo:
+
+    ```sh
+        yamllint test.yaml
+        yamllint questions.yaml
+    ```
+
+    No caso de programas geradores de perguntas e programas de correcção de
+    respostas pode usar-se um *pipe*:
+
+    ```sh
+        generate-question | yamllint -
+        correct-answer | yamllint -
+    ```
--
libgit2 0.21.2