From 5cb45de448daa05f9458e789501b40977d094977 Mon Sep 17 00:00:00 2001 From: Miguel Barão Date: Tue, 23 Apr 2024 19:29:06 +0100 Subject: [PATCH] update demo --- demo/demo.yaml | 4 +--- demo/questions/generate-question.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------- demo/questions/tutorial.yaml | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------- 3 files changed, 128 insertions(+), 135 deletions(-) diff --git a/demo/demo.yaml b/demo/demo.yaml index 65ec5b6..87f76cd 100644 --- a/demo/demo.yaml +++ b/demo/demo.yaml @@ -75,9 +75,7 @@ questions: - ref: tut-information - ref: tut-success - ref: tut-warning - - # choose one from the list: - - ref: [tut-alert1, tut-alert2] + - ref: tut-alert - ref: tut-generator - ref: tut-yamllint diff --git a/demo/questions/generate-question.py b/demo/questions/generate-question.py index 92c3275..a92f4ef 100755 --- a/demo/questions/generate-question.py +++ b/demo/questions/generate-question.py @@ -1,77 +1,87 @@ #!/usr/bin/env python3 ''' -Example of a question generator. -Arguments are read from stdin. +Example of a parameterized question generator. +Parameters are read from stdin. +The generated question is written to stdout in JSON format. ''' -from random import randint +import json +import random import sys # read two arguments from the field `args` specified in the question yaml file a, b = (int(n) for n in sys.argv[1:]) -x = randint(a, b) -y = randint(a, b) +x = random.randint(a, b) +y = random.randint(a, b) r = x + y -print(f"""--- -type: text -title: Geradores de perguntas -text: | - - As perguntas podem ser estáticas (como as que vimos até aqui), ou serem - geradas dinâmicamente por um programa externo. Para gerar uma pergunta, o - programa deve escrever texto no `stdout` em formato `yaml` tal como os - exemplos das perguntas estáticas dos tipos apresentados anteriormente. Pode - também receber argumentos de linha de comando para parametrizar a pergunta. - Aqui está um exemplo de uma pergunta gerada por um script python: - - ```python - #!/usr/bin/env python3 - - from random import randint - import sys - - a, b = (int(n) for n in sys.argv[1:]) # argumentos da linha de comando - - x = randint(a, b) # número inteiro no intervalo a..b - y = randint(a, b) # número inteiro no intervalo a..b - r = x + y # calcula resultado correcto - - print(f'''--- - type: text - title: Contas de somar - text: | - Calcule o resultado de ${{x}} + {{y}}$. - correct: '{{r}}' - solution: | - A solução é {{r}}.''') - ``` - - Este script deve ter permissões para poder ser executado no terminal. - Podemos testar o programa no terminal `./gen-somar.py 1 100` e verificar que - o output é uma pergunta válida em formato `yaml`. Agora é necessário indicar - que este script deve ser usado para gerar uma pergunta. - - Uma pergunta gerada por um programa externo é declarada com - - ```yaml - - type: generator - ref: gen-somar - script: gen-somar.py - # argumentos opcionais - args: [1, 100] - ``` - - O programa pode receber uma lista de argumentos de linha de comando - declarados em `args`. - - --- - - Calcule o resultado de ${x} + {y}$. - - Os números foram gerados aleatoriamente no intervalo de {a} a {b}. -correct: '{r}' -solution: | - A solução é {r}.""") +# ---------------------------------------------------------------------------- +text = ''' +As perguntas podem ser estáticas (como as que vimos até aqui), ou serem +geradas dinâmicamente por um programa externo. Para gerar uma pergunta, o +programa deve escrever texto no `stdout` em formato `yaml` tal como os +exemplos das perguntas estáticas dos tipos apresentados anteriormente. Pode +também receber argumentos de linha de comando para parametrizar a pergunta. +Aqui está um exemplo de uma pergunta gerada por um script python: + +```python + #!/usr/bin/env python3 + + import json + import random + import sys + + a, b = (int(n) for n in sys.argv[1:]) # argumentos da linha de comando + + x = random.randint(a, b) # número inteiro no intervalo a..b + y = random.randint(a, b) # número inteiro no intervalo a..b + r = x + y # calcula o resultado correcto + + question = { + 'type': 'text', + 'title': 'Geradores de perguntas', + 'text': f'Quanto é {x} + {y}?', + 'transform': ['trim'], + 'correct': f'{r}', + 'solution': f'A resposta correcta é {r}.' + } + json.dump(question, sys.stdout) +``` + +Este script deve ter permissões para poder ser executado. +Podemos testar o programa no terminal `./gen-somar.py 1 100` e verificar que +o output é uma pergunta válida no formato JSON. Agora é necessário indicar +que este script deve ser usado para gerar uma pergunta. + +Uma pergunta gerada por um programa externo é declarada com + +```yaml +- type: generator + ref: gen-somar + script: gen-somar.py + # argumentos opcionais + args: [1, 100] +``` + +O script vai receber a lista de argumentos declarados em `args`. +A pergunta vai ser modificada, sendo acrescentados os campos gerados pelo +script. O tipo da pergunta vai também ser preenchido pelo tipo correcto. +Apenas a referência mantém-se. + +--- + +''' +# ---------------------------------------------------------------------------- + +question = { + 'type': 'text', + 'title': 'Geradores de perguntas', + 'text': text + f'Quanto é {x} + {y}?', + 'transform': ['trim'], + 'correct': f'{r}', + 'solution': f'A resposta correcta é {r}.' +} + +json.dump(question, sys.stdout) diff --git a/demo/questions/tutorial.yaml b/demo/questions/tutorial.yaml index 49e2473..f4f6ed7 100644 --- a/demo/questions/tutorial.yaml +++ b/demo/questions/tutorial.yaml @@ -10,7 +10,7 @@ ficheiros de perguntas a importar e uma selecção de perguntas e respectivas cotações. - Exemplo: + Exemplo de um ficheiro `test.yaml`: ```yaml --- @@ -18,7 +18,7 @@ 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 + answers_dir: ans # directório onde ficam as submissões dos alunos # opcionais duration: 60 # duração da prova em minutos (default: inf) @@ -28,7 +28,7 @@ # não normaliza por defeito (default: None) # -------------------------------------------------------------------------- - # Ficheiros de perguntas a importar (relativamente a `questions_dir`) + # Ficheiros de perguntas a importar files: - tabelas.yaml - topic1/questions.yaml @@ -46,19 +46,19 @@ - ref: pergunta2 points: 2.0 - # por defeinto, a cotação da pergunta é 1.0 valor + # por defeito, a pergunta tem a cotação de 1.0 valor - ref: pergunta3 # escolhe aleatoriamente uma das variantes da pergunta - - ref: [pergunta3a, pergunta3b] + - ref: [pergunta4a, pergunta4b] points: 0.5 # -------------------------------------------------------------------------- ``` - A ordem das perguntas é mantida quando apresentada no teste. + A ordem das perguntas é mantida. - O mesmo teste pode ser realizado várias vezes em turnos diferentes, não é + O mesmo teste pode ser repetido várias vezes em turnos diferentes, não é necessário alterar nada. # ---------------------------------------------------------------------------- @@ -278,8 +278,8 @@ 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. + múltiplos espaços por um único espaço no meio. + * `lower` e `upper` convertem para minúsculas e maiúsculas, respectivamente. transform: ['trim', 'lower'] correct: ['azul'] solution: | @@ -314,15 +314,12 @@ - '[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. + **Atenção:** + A expressão regular deve seguir as convenções 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 da string `^(VERDE|[Vv]erde)$` para + evitar estas situações. correct: '(VERDE|[Vv]erde)' solution: | @@ -333,7 +330,7 @@ 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). + Este tipo de perguntas esperam uma resposta numérica em vírgula flutuante. O resultado é considerado correcto se estiver dentro do intervalo fechado indicado. @@ -354,7 +351,7 @@ **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`. + ou em vírgula flutuante, como em `0.23`, `1e-3`, `1.2e4`. correct: [3.14, 3.15] solution: | Sabemos que $\pi\approx 3.14159265359$. @@ -366,14 +363,14 @@ 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. + são as mais flexíveis, sendo a resposta enviada para um programa externo + para ser avaliada. - 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). + O programa externo é um programa que tem de ser executável pelo sistema + operativo (pode ser um binário ou script desde que o respectivo + interpretador esteja instalado). Este programa externo recebe a resposta submetida pelo aluno via `stdin` e - devolve a classificação via `stdout`. + devolve a classificação em formato JSON via `stdout`. Exemplo: ```yaml @@ -390,7 +387,7 @@ 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. + 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 @@ -405,18 +402,29 @@ 0.75 ``` - ou opcionalmente escrever em formato json ou yaml, eventualmente com um - comentário que será arquivado com o teste. - Exemplo: + ou escrever em formato JSON, 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`. + ```json + { "grade": 0.5, "comments": "Esqueceu-se de algumas cores." } ``` O comentário é mostrado na revisão de prova. + + Como este tipo de perguntas é mais complexo, incluem-se *unit tests* para + testar os scripts de correcção das perguntas antes do início da prova. + As opções seguintes mostram casos em que a resposta está correcta e casos + em que a resposta está errada. O script de correcção é testado sobre todos + estes casos e para cada pergunta de cada aluno: + + ```yaml + tests_right: + - 'red green blue' + - 'green blue red' + tests_wrong: + - '' + - 'blue yellow black' + ``` answer: | Aqui o aluno escreve a resposta. Esta caixa aumenta de tamanho automaticamente e @@ -425,13 +433,14 @@ timeout: 5 tests_right: - 'red green blue' - # tests_wrong: - # - 'blue gray yellow' + tests_wrong: + - '' + - 'blue gray yellow' # --------------------------------------------------------------------------- - type: information ref: tut-information - title: Texto informativo + title: Texto informativo (genérico) 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. @@ -467,7 +476,7 @@ ref: tut-success title: Texto informativo (sucesso) text: | - Não conta para avaliação. É apenas o aspecto gráfico que muda. + Semelhante ao anterior, mas com cores diferentes. Um pedaço de código em linha, por exemplo `x = sqrt(z)` é marcado com uma fonte e cor diferente. @@ -508,7 +517,7 @@ ref: tut-warning title: Texto informativo (aviso) text: | - Não conta para avaliação. + Semelhante aos anteriores. Neste exemplo mostramos como se pode construir uma tabela como a seguinte: @@ -539,26 +548,10 @@ # ---------------------------------------------------------------------------- - type: alert - ref: tut-alert1 - title: Texto informativo (perigo) - versão 1 + ref: tut-alert + title: Texto informativo (perigo) 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. + Semelhante aos anteriores. ![planetas](planets.png "Planetas do Sistema Solar") @@ -599,11 +592,3 @@ 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