Initial QAML question data
This commit is contained in:
17
.github/pull_request_template.md
vendored
Normal file
17
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## Co zmieniasz?
|
||||||
|
|
||||||
|
- [ ] poprawiam treść pytania
|
||||||
|
- [ ] poprawiam odpowiedź
|
||||||
|
- [ ] dodaję nowe pytanie
|
||||||
|
- [ ] dodaję/zmieniam obrazek w `img/`
|
||||||
|
- [ ] usuwam duplikat albo błąd
|
||||||
|
|
||||||
|
## Źródło / uzasadnienie
|
||||||
|
|
||||||
|
Opisz krótko skąd pochodzi poprawka albo dlaczego obecna wersja jest błędna.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] każde pytanie i każda odpowiedź mieści się w jednej linii
|
||||||
|
- [ ] każda odpowiedź zaczyna się od `-` albo `-|`
|
||||||
|
- [ ] obrazki użyte jako `img/...` istnieją w repozytorium
|
||||||
15
.github/workflows/deploy.yml
vendored
Normal file
15
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
name: Deploy Quiz Data
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: qaml-deploy
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Validate pytania.txt
|
||||||
|
run: php tools/validate_qaml.php pytania.txt
|
||||||
|
- name: Deploy data
|
||||||
|
run: deploy-quiz-data statystyka
|
||||||
14
.github/workflows/validate.yml
vendored
Normal file
14
.github/workflows/validate.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: Validate QAML
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: qaml-validate
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Validate pytania.txt
|
||||||
|
run: php tools/validate_qaml.php pytania.txt
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ip.txt
|
||||||
|
ip.txt.old
|
||||||
|
*.log
|
||||||
|
*.bak
|
||||||
|
*.old
|
||||||
|
.DS_Store
|
||||||
13
CONTRIBUTING.md
Normal file
13
CONTRIBUTING.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Jak zgłaszać poprawki
|
||||||
|
|
||||||
|
Poprawki zgłaszamy przez Pull Request.
|
||||||
|
|
||||||
|
Najczęstsze dobre zmiany:
|
||||||
|
|
||||||
|
- poprawienie literówki,
|
||||||
|
- oznaczenie prawidłowej odpowiedzi jako `-|`,
|
||||||
|
- usunięcie błędnej odpowiedzi,
|
||||||
|
- dopisanie źródła w komentarzu `//`,
|
||||||
|
- dodanie brakującego obrazka do `img/`.
|
||||||
|
|
||||||
|
Nie zmieniaj formatu pliku na pełny Markdown, JSON, CSV ani HTML. To repozytorium używa prostego formatu QAML opisanego w `README.md`.
|
||||||
203
README.md
Normal file
203
README.md
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# Baza pytań quizu
|
||||||
|
|
||||||
|
To repozytorium zawiera dane quizu: `pytania.txt` oraz opcjonalny katalog `img/` z obrazkami używanymi w pytaniach.
|
||||||
|
|
||||||
|
Kod aplikacji nie jest częścią tego repozytorium. Zmiany w pytaniach należy zgłaszać przez Pull Request.
|
||||||
|
|
||||||
|
## QAML — Question Answer Markdown Lines
|
||||||
|
|
||||||
|
QAML to prosty liniowy format zapisu pytań testowych wielokrotnego wyboru.
|
||||||
|
|
||||||
|
Format wygląda jak Markdown, ale jego składnia strukturalna jest znacznie prostsza. Parser nie analizuje pełnego Markdowna. Interpretuje wyłącznie początki linii:
|
||||||
|
|
||||||
|
- linia pytania,
|
||||||
|
- linia odpowiedzi błędnej,
|
||||||
|
- linia odpowiedzi poprawnej,
|
||||||
|
- komentarz,
|
||||||
|
- pusta linia.
|
||||||
|
|
||||||
|
Treść pytania i odpowiedzi może zawierać Markdown, HTML oraz inline LaTeX, ale parser traktuje je jako zwykły tekst.
|
||||||
|
|
||||||
|
## Minimalny przykład
|
||||||
|
|
||||||
|
```text
|
||||||
|
// Przykładowa sekcja
|
||||||
|
|
||||||
|
Zaznacz zdania prawdziwe
|
||||||
|
- To jest odpowiedź błędna.
|
||||||
|
-| To jest odpowiedź poprawna.
|
||||||
|
- To jest kolejna odpowiedź błędna.
|
||||||
|
|
||||||
|
Ile wynosi $2 + 2$?
|
||||||
|
- 3
|
||||||
|
-| 4
|
||||||
|
- 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reguły składni
|
||||||
|
|
||||||
|
### 1. Pytanie
|
||||||
|
|
||||||
|
Pytaniem jest każda niepusta linia, która:
|
||||||
|
|
||||||
|
- nie zaczyna się od znaku `-`,
|
||||||
|
- nie zaczyna się od `//`.
|
||||||
|
|
||||||
|
Pytanie musi mieścić się w jednej linii.
|
||||||
|
|
||||||
|
Poprawnie:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Zaznacz zdania prawdziwe dotyczące indukcji matematycznej.
|
||||||
|
```
|
||||||
|
|
||||||
|
Niepoprawnie:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Zaznacz zdania prawdziwe
|
||||||
|
dotyczące indukcji matematycznej.
|
||||||
|
```
|
||||||
|
|
||||||
|
Drugi zapis zostanie zinterpretowany jako dwa osobne pytania.
|
||||||
|
|
||||||
|
### 2. Odpowiedź błędna
|
||||||
|
|
||||||
|
Odpowiedź błędna zaczyna się od pojedynczego myślnika `-`.
|
||||||
|
|
||||||
|
Poprawne są oba style:
|
||||||
|
|
||||||
|
```text
|
||||||
|
- Odpowiedź błędna
|
||||||
|
-Odpowiedź błędna
|
||||||
|
```
|
||||||
|
|
||||||
|
Parser usuwa znak `-`, a następnie przycina białe znaki z początku i końca odpowiedzi.
|
||||||
|
|
||||||
|
### 3. Odpowiedź poprawna
|
||||||
|
|
||||||
|
Odpowiedź poprawna zaczyna się od `-|`.
|
||||||
|
|
||||||
|
Poprawne są oba style:
|
||||||
|
|
||||||
|
```text
|
||||||
|
-| Odpowiedź poprawna
|
||||||
|
-|Odpowiedź poprawna
|
||||||
|
```
|
||||||
|
|
||||||
|
Parser usuwa prefiks `-|`, a następnie przycina białe znaki z początku i końca odpowiedzi.
|
||||||
|
|
||||||
|
### 4. Pytania jednokrotnego i wielokrotnego wyboru
|
||||||
|
|
||||||
|
Format dopuszcza dowolną liczbę poprawnych odpowiedzi, w tym zero poprawnych odpowiedzi albo wszystkie odpowiedzi poprawne.
|
||||||
|
|
||||||
|
Pytanie jednokrotnego wyboru:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Ile wynosi $2 + 2$?
|
||||||
|
- 3
|
||||||
|
-| 4
|
||||||
|
- 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Pytanie wielokrotnego wyboru:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Wskaż liczby pierwsze
|
||||||
|
-| 2
|
||||||
|
-| 3
|
||||||
|
- 4
|
||||||
|
-| 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Parser nie narzuca liczby poprawnych odpowiedzi. Zero poprawnych odpowiedzi może oznaczać zadanie, w którym żadna odpowiedź nie jest prawdziwa, a oznaczenie wszystkich odpowiedzi jako `-|` może oznaczać zadanie, w którym wszystkie odpowiedzi są prawdziwe.
|
||||||
|
|
||||||
|
### 5. Komentarze
|
||||||
|
|
||||||
|
Komentarzem jest linia zaczynająca się od `//`.
|
||||||
|
|
||||||
|
Przykłady:
|
||||||
|
|
||||||
|
```text
|
||||||
|
// Sterna 2024/2025 B
|
||||||
|
// Formanowicz 2021-2022
|
||||||
|
```
|
||||||
|
|
||||||
|
Komentarze są ignorowane przez parser demonstracyjny. Można ich używać jako nagłówków sekcji, źródeł, dat albo notatek.
|
||||||
|
|
||||||
|
### 6. Puste linie
|
||||||
|
|
||||||
|
Puste linie są ignorowane. Można ich używać do oddzielania pytań, odpowiedzi lub sekcji.
|
||||||
|
|
||||||
|
## LaTeX
|
||||||
|
|
||||||
|
Dozwolony jest inline LaTeX między pojedynczymi znakami dolara:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Ile wynosi $\binom{n}{k}$?
|
||||||
|
```
|
||||||
|
|
||||||
|
Dozwolony przykład:
|
||||||
|
|
||||||
|
```text
|
||||||
|
-| Liczba kombinacji wynosi $\binom{n}{k}$.
|
||||||
|
```
|
||||||
|
|
||||||
|
Nie jest częścią formalnej składni:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$$
|
||||||
|
a^2 + b^2 = c^2
|
||||||
|
$$
|
||||||
|
```
|
||||||
|
|
||||||
|
oraz:
|
||||||
|
|
||||||
|
```text
|
||||||
|
\[ a^2 + b^2 = c^2 \]
|
||||||
|
```
|
||||||
|
|
||||||
|
Parser demonstracyjny nie waliduje poprawności LaTeX-a. Traktuje zapis `$...$` jako zwykły fragment tekstu.
|
||||||
|
|
||||||
|
## HTML i obrazki
|
||||||
|
|
||||||
|
HTML jest dopuszczony jako część treści pytania lub odpowiedzi.
|
||||||
|
|
||||||
|
Przykład:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Zaznacz funkcję odpowiadającą obrazkowi <img src="img/example.png" height="100" />
|
||||||
|
-| $f(x) = x^2$
|
||||||
|
- $f(x) = x$
|
||||||
|
```
|
||||||
|
|
||||||
|
Jeżeli `pytania.txt` odwołuje się do obrazka przez `img/...`, plik musi istnieć w katalogu `img/` w tym repozytorium.
|
||||||
|
|
||||||
|
## Jedna linia = jeden element
|
||||||
|
|
||||||
|
To najważniejsza zasada formatu.
|
||||||
|
|
||||||
|
Każde pytanie i każda odpowiedź muszą mieścić się w jednej fizycznej linii.
|
||||||
|
|
||||||
|
Poprawnie:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Zaznacz zdania prawdziwe dotyczące funkcji $f(x) = x^2$.
|
||||||
|
-| Funkcja jest parzysta.
|
||||||
|
- Funkcja jest nieparzysta.
|
||||||
|
```
|
||||||
|
|
||||||
|
Niepoprawnie:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Zaznacz zdania prawdziwe dotyczące funkcji
|
||||||
|
$f(x) = x^2$.
|
||||||
|
-| Funkcja jest parzysta.
|
||||||
|
```
|
||||||
|
|
||||||
|
Parser potraktuje drugą linię jako nowe pytanie.
|
||||||
|
|
||||||
|
## Walidacja lokalna
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php tools/validate_qaml.php pytania.txt
|
||||||
|
```
|
||||||
7
pytania.txt
Normal file
7
pytania.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Przykładowe pytanie startowe
|
||||||
|
|
||||||
|
Która miara statystyczna opisuje przeciętną wartość obserwacji w próbie?
|
||||||
|
- Mediana
|
||||||
|
- Wariancja
|
||||||
|
-| Średnia arytmetyczna
|
||||||
|
- Odchylenie standardowe
|
||||||
90
tools/validate_qaml.php
Normal file
90
tools/validate_qaml.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
$file = $argv[1] ?? 'pytania.txt';
|
||||||
|
$baseDir = dirname(__DIR__);
|
||||||
|
$path = $baseDir . '/' . $file;
|
||||||
|
|
||||||
|
if (!is_file($path)) {
|
||||||
|
fwrite(STDERR, "File not found: {$file}\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
||||||
|
$errors = [];
|
||||||
|
$question = null;
|
||||||
|
$questionLine = 0;
|
||||||
|
$answers = 0;
|
||||||
|
$questionCount = 0;
|
||||||
|
|
||||||
|
$finishQuestion = static function () use (&$errors, &$question, &$questionLine, &$answers, &$questionCount): void {
|
||||||
|
if ($question === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($answers === 0) {
|
||||||
|
$errors[] = "Line {$questionLine}: question has no answers.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$questionCount++;
|
||||||
|
$question = null;
|
||||||
|
$questionLine = 0;
|
||||||
|
$answers = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach ($lines as $i => $rawLine) {
|
||||||
|
$lineNo = $i + 1;
|
||||||
|
$line = trim($rawLine);
|
||||||
|
|
||||||
|
if ($line === '' || str_starts_with($line, '//')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!str_starts_with($line, '-')) {
|
||||||
|
$finishQuestion();
|
||||||
|
$question = $line;
|
||||||
|
$questionLine = $lineNo;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($question === null) {
|
||||||
|
$errors[] = "Line {$lineNo}: answer appears before any question.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($line, '-|')) {
|
||||||
|
$answer = trim(substr($line, 2));
|
||||||
|
} else {
|
||||||
|
$answer = trim(substr($line, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($answer === '') {
|
||||||
|
$errors[] = "Line {$lineNo}: answer is empty.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$answers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$finishQuestion();
|
||||||
|
|
||||||
|
$content = file_get_contents($path) ?: '';
|
||||||
|
preg_match_all('/<img\s+[^>]*src=["\']([^"\']+)["\'][^>]*>/i', $content, $matches);
|
||||||
|
foreach ($matches[1] ?? [] as $src) {
|
||||||
|
if (str_starts_with($src, 'img/') && !is_file($baseDir . '/' . $src)) {
|
||||||
|
$errors[] = "Missing image referenced from pytania.txt: {$src}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($questionCount === 0) {
|
||||||
|
$errors[] = 'No questions found.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors !== []) {
|
||||||
|
foreach ($errors as $error) {
|
||||||
|
fwrite(STDERR, $error . PHP_EOL);
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "OK: {$questionCount} questions validated.\n";
|
||||||
Reference in New Issue
Block a user