Add shared card schemas layouts and renderer
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
name: Check card layouts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check schemas and render example
|
||||
run: |
|
||||
python3 -m pip install -r requirements-dev.txt
|
||||
CARD_LAYOUTS_REQUIRE_JSONSCHEMA=1 python3 scripts/check_repository.py
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.pytest_cache/
|
||||
|
||||
examples/card/build/
|
||||
examples/card/web/
|
||||
examples/card/doc/*.aux
|
||||
examples/card/doc/*.fdb_latexmk
|
||||
examples/card/doc/*.fls
|
||||
examples/card/doc/*.log
|
||||
examples/card/doc/*.out
|
||||
examples/card/doc/*.pdf
|
||||
@@ -0,0 +1,14 @@
|
||||
.PHONY: check render-example pdf-example
|
||||
|
||||
PYTHON ?= python3
|
||||
EXAMPLE_CARD := examples/card
|
||||
EXAMPLE_TEX_DIR := $(EXAMPLE_CARD)/build/tex
|
||||
|
||||
render-example:
|
||||
$(PYTHON) tools/render_card.py $(EXAMPLE_CARD)
|
||||
|
||||
pdf-example: render-example
|
||||
cd $(EXAMPLE_TEX_DIR) && latexmk -pdf -interaction=nonstopmode -halt-on-error main.tex
|
||||
|
||||
check:
|
||||
$(PYTHON) scripts/check_repository.py
|
||||
@@ -0,0 +1,129 @@
|
||||
# card-layouts
|
||||
|
||||
Wspólne źródło schematów, layoutów i generatora kart pracy używanych przez
|
||||
serie edukacyjne. Jeden plik `card_source.json` opisuje treść oraz mapowanie
|
||||
edukacyjne, a `tools/render_card.py` generuje z niego równolegle LaTeX i HTML.
|
||||
|
||||
Repozytorium rozdziela trzy warstwy:
|
||||
|
||||
1. **Treść karty** — `card_source.json`: cele, efekty nauczania, kryteria,
|
||||
drzewka marginesowe, kroki oraz zadania.
|
||||
2. **Layout** — `templates/*.json`: geometria strony, role marginesów,
|
||||
komponenty i wybór emitera TeX/HTML.
|
||||
3. **Seria** — `series.json`: kolejność kart, repozytoria, wersje i kontrakt
|
||||
uruchamiania zadań.
|
||||
|
||||
## Zawartość
|
||||
|
||||
```text
|
||||
card-layouts/
|
||||
├── schemas/
|
||||
│ ├── card-source.schema.json
|
||||
│ ├── card-template.schema.json
|
||||
│ └── series.schema.json
|
||||
├── templates/
|
||||
│ ├── karta-klasyczna.json
|
||||
│ ├── karta-5a.json
|
||||
│ └── karta-5b.json
|
||||
├── tools/render_card.py
|
||||
├── docs/
|
||||
│ ├── CARD.md
|
||||
│ └── SERIES.md
|
||||
└── examples/
|
||||
├── card/
|
||||
└── series/
|
||||
```
|
||||
|
||||
## Layouty
|
||||
|
||||
| Layout | Przeznaczenie | Marginesy | Emitery |
|
||||
|---|---|---|---|
|
||||
| `karta-klasyczna` | elastyczna karta sekcyjna | dwa panele drzewek | TeX + HTML |
|
||||
| `karta-5a` | wielostronicowa karta A4 landscape | TECH po lewej, OG po prawej | TeX + HTML |
|
||||
| `karta-5b` | referencyjna karta A4 portrait | gołe drzewka TECH/OG | TeX + HTML |
|
||||
|
||||
`karta-5b` jest obecnym layoutem referencyjnym. Używa geometrii
|
||||
`0,55 / 2,05 / 0,55 / 14,70 / 0,55 / 2,05 / 0,55 cm`; lewy margines
|
||||
zawiera efekty zawodowe `TECH`, a prawy efekty ogólne `OG`.
|
||||
|
||||
## Szybki start
|
||||
|
||||
Wygenerowanie przykładowej karty:
|
||||
|
||||
```bash
|
||||
make render-example
|
||||
```
|
||||
|
||||
Powstaną:
|
||||
|
||||
```text
|
||||
examples/card/build/tex/main.tex
|
||||
examples/card/build/html/index.html
|
||||
examples/card/build/html/style.css
|
||||
```
|
||||
|
||||
Złożenie kontrolnego PDF-u wymaga `latexmk` i pakietów LaTeX używanych przez
|
||||
generator:
|
||||
|
||||
```bash
|
||||
make pdf-example
|
||||
```
|
||||
|
||||
Pełna kontrola repozytorium:
|
||||
|
||||
```bash
|
||||
make check
|
||||
```
|
||||
|
||||
Walidacja względem JSON Schema jest wykonywana, gdy dostępny jest pakiet
|
||||
`jsonschema`; CI instaluje go z `requirements-dev.txt`.
|
||||
|
||||
Generator można też wywołać bezpośrednio dla dowolnego katalogu karty:
|
||||
|
||||
```bash
|
||||
python3 tools/render_card.py /sciezka/do/karty
|
||||
python3 tools/render_card.py /sciezka/do/karty --template templates/karta-5b.json
|
||||
```
|
||||
|
||||
Katalog karty musi zawierać `json/card_source.json`. Ścieżki wyjściowe
|
||||
określa obiekt `generated` w tym pliku.
|
||||
|
||||
## Model edukacyjny
|
||||
|
||||
Źródłem marginesów nie jest luźny tekst. Relacje są jawne:
|
||||
|
||||
```text
|
||||
WE (wymaganie edukacyjne)
|
||||
└── EN/EK (efekt nauczania lub efekt kształcenia)
|
||||
└── KW (kryterium weryfikacji)
|
||||
```
|
||||
|
||||
Każda sekcja i każdy krok mogą wskazywać te same drzewka przez `tree_refs`.
|
||||
Dzięki temu TeX i HTML pokazują identyczne powiązanie treści z efektami.
|
||||
|
||||
Szczegóły modelu karty opisuje [docs/CARD.md](docs/CARD.md), a manifestu
|
||||
serii [docs/SERIES.md](docs/SERIES.md).
|
||||
|
||||
## Słowniki podstaw programowych
|
||||
|
||||
Generator może rozszerzać skróty z zewnętrznych słowników JSON. Karta podaje
|
||||
ich katalog przez:
|
||||
|
||||
```json
|
||||
{
|
||||
"dictionary_refs": {
|
||||
"pp_json_root": "../../pp/json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Brak słowników nie blokuje generowania, jeśli karta zawiera kompletne pola
|
||||
`display` i `text` we własnych drzewkach.
|
||||
|
||||
## Zasada zmian
|
||||
|
||||
- zmiana treści należy do `card_source.json`;
|
||||
- zmiana geometrii i stylu należy do `templates/*.json` oraz emitera;
|
||||
- wygenerowanych plików nie edytujemy ręcznie;
|
||||
- nowa wersja layoutu wymaga aktualizacji numeru `version`, przykładu i testu
|
||||
obu wyjść.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Opis repozytorium pojedynczej karty
|
||||
|
||||
Każda karta jest osobnym repozytorium lub samodzielnym katalogiem. Jej źródłem
|
||||
prawdy jest `json/card_source.json`; pliki TeX i HTML są artefaktami
|
||||
generowanymi.
|
||||
|
||||
## Minimalne drzewo
|
||||
|
||||
```text
|
||||
moja-karta/
|
||||
├── README.md
|
||||
├── json/card_source.json
|
||||
├── doc/
|
||||
├── web/
|
||||
└── src/
|
||||
```
|
||||
|
||||
## Obowiązkowe warstwy danych
|
||||
|
||||
### Metadane
|
||||
|
||||
Obiekt `card` identyfikuje serię, numer, wersję, UUID, autora i status.
|
||||
Obiekt `generated` wskazuje ścieżki wyjściowe TeX/HTML/CSS. Pole `template`
|
||||
wybiera layout.
|
||||
|
||||
### Efekty i kryteria
|
||||
|
||||
- `educational_requirements` — wymagania `WE`;
|
||||
- `learning_effects` — efekty `EN` lub `EK`;
|
||||
- `assessment_criteria` — mierzalne kryteria `KW`;
|
||||
- `educational_requirements.*.learning_tree` — połączenia renderowane na
|
||||
marginesach.
|
||||
|
||||
Każdy wpis `learning_tree.ogolne[]` lub `learning_tree.zawodowe[]` ma stabilne
|
||||
`tree_id`, odwołanie `effect_ref`, etykietę `display` oraz co najmniej jedno
|
||||
powiązane `KW`.
|
||||
|
||||
### Sekcje i kroki
|
||||
|
||||
Pole `sections[]` porządkuje treść. Kroki `sections[].steps[]` mają:
|
||||
|
||||
- `id` i `title`;
|
||||
- `tree_refs` wskazujące drzewka z marginesów;
|
||||
- opcjonalne odwołania do wymagań, efektów, kryteriów, równań i figur.
|
||||
|
||||
Layouty 5a/5b obsługują również:
|
||||
|
||||
- `model_block.steps` — kroki modelowania;
|
||||
- `code_block.blocks[].web_steps` — interaktywne kroki HTML;
|
||||
- `hardware_procedure[]` — tabela `step/action/condition` w TeX i HTML.
|
||||
|
||||
### Zadania
|
||||
|
||||
`tasks` jest słownikiem zadań, a `tasks_order` określa ich kolejność. Każde
|
||||
zadanie posiada polecenie `prompt_tex`, kryterium zaliczenia i odwołania do
|
||||
modelu edukacyjnego.
|
||||
|
||||
## Zalecany README karty
|
||||
|
||||
README konkretnej karty powinien zawierać:
|
||||
|
||||
1. nazwę, numer i miejsce w serii;
|
||||
2. cel oraz rezultat obserwowalny;
|
||||
3. mapowanie do źródeł i efektów nauczania;
|
||||
4. listę kroków/zadań;
|
||||
5. polecenia generowania i testowania;
|
||||
6. warunek `PASS`;
|
||||
7. informację, które pliki są źródłowe, a które generowane.
|
||||
|
||||
Gotowy przykład znajduje się w `examples/card/README.md`.
|
||||
|
||||
## Generowanie
|
||||
|
||||
Z katalogu repozytorium `card-layouts`:
|
||||
|
||||
```bash
|
||||
python3 tools/render_card.py /sciezka/do/mojej-karty
|
||||
```
|
||||
|
||||
Generator najpierw sprawdza spójność identyfikatorów WE/EN/EK/KW, kroków,
|
||||
zadań i odwołań, a dopiero potem zapisuje oba formaty.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Opis repozytorium i README serii
|
||||
|
||||
Seria jest uporządkowaną ścieżką kart. Jej manifest nie kopiuje treści kart;
|
||||
przechowuje kolejność, adres repozytorium, gałąź, wersję dokumentu i kontrakt
|
||||
uruchamiania zadań.
|
||||
|
||||
## Manifest `series.json`
|
||||
|
||||
Najważniejsze pola:
|
||||
|
||||
- `version` — wersja formatu manifestu;
|
||||
- `series`, `org`, `title` — identyfikacja serii;
|
||||
- `default_branch` — domyślna gałąź kart;
|
||||
- `cards[]` — uporządkowane wpisy kart;
|
||||
- `cards[].build` — cele budowania i czyszczenia;
|
||||
- `cards[].tasks[]` — cele uruchamiane przez narzędzia ucznia.
|
||||
|
||||
Schemat znajduje się w `schemas/series.schema.json`, a minimalny manifest w
|
||||
`examples/series/series.json`.
|
||||
|
||||
## Zalecany README serii
|
||||
|
||||
README serii powinien odpowiadać na pięć pytań:
|
||||
|
||||
1. Jaki jest rezultat całej ścieżki?
|
||||
2. Jakie są wymagania wejściowe?
|
||||
3. W jakiej kolejności realizujemy karty i dlaczego?
|
||||
4. Z jakich książek, dokumentacji i przykładów korzystamy?
|
||||
5. Jaki wspólny kontrakt `PASS` obowiązuje wszystkie karty?
|
||||
|
||||
Zalecana tabela mapuje:
|
||||
|
||||
```text
|
||||
karta → lekcja → źródło → eksperyment → rezultat → stan
|
||||
```
|
||||
|
||||
README nie zastępuje `series.json`: Markdown opisuje narrację i decyzje,
|
||||
natomiast JSON jest kontraktem maszynowym dla narzędzi.
|
||||
|
||||
Przykład znajduje się w `examples/series/README.md`.
|
||||
@@ -0,0 +1,36 @@
|
||||
# demo-01 / pierwszy krok
|
||||
|
||||
Karta `01/02` demonstracyjnej serii `demo`. Pokazuje minimalny kontrakt
|
||||
generatora: jedno wymaganie edukacyjne, efekty ogólne i zawodowe na
|
||||
marginesach, kryterium weryfikacji oraz kroki obecne w TeX i HTML.
|
||||
|
||||
## Cel
|
||||
|
||||
Uczeń uruchamia generator, rozpoznaje pliki źródłowe oraz potrafi wskazać,
|
||||
który krok realizuje dane wymaganie i kryterium.
|
||||
|
||||
## Kroki
|
||||
|
||||
1. Sprawdź `json/card_source.json`.
|
||||
2. Wygeneruj TeX i HTML.
|
||||
3. Potwierdź obecność marginesów `TECH` i `OG`.
|
||||
4. Zapisz dowód wykonania polecenia kontrolnego.
|
||||
|
||||
## Generowanie
|
||||
|
||||
Z katalogu głównego repozytorium `card-layouts`:
|
||||
|
||||
```bash
|
||||
make render-example
|
||||
make pdf-example
|
||||
```
|
||||
|
||||
## Warunek PASS
|
||||
|
||||
- generator kończy pracę kodem `0`;
|
||||
- istnieją `build/tex/main.tex`, `build/html/index.html` i `style.css`;
|
||||
- oba wyjścia zawierają tytuł karty i identyfikatory drzewek;
|
||||
- PDF składa się bez błędu.
|
||||
|
||||
Źródłem prawdy jest `json/card_source.json`. Plików w `build/` nie edytujemy
|
||||
ręcznie.
|
||||
@@ -0,0 +1,586 @@
|
||||
{
|
||||
"$schema": "../../../schemas/card-source.schema.json",
|
||||
"schema": "esc-card-source.v1",
|
||||
"card": {
|
||||
"id": "demo-01",
|
||||
"series": "demo",
|
||||
"number": "01",
|
||||
"count": "02",
|
||||
"slug": "pierwszy-krok",
|
||||
"title": "DEMO-01: Jedno źródło, dwa formaty",
|
||||
"topic": "Generowanie TeX i HTML z mapą efektów nauczania",
|
||||
"project": "System kart pracy",
|
||||
"subject": "Zajęcia projektowe",
|
||||
"level": "przykład techniczny",
|
||||
"dominant": "informatyka i kompetencje techniczne",
|
||||
"revision_date": "15.07.2026",
|
||||
"status": "Przykład",
|
||||
"version": "v0.1",
|
||||
"uuid": "00000000-0000-4000-8000-000000000001",
|
||||
"author": "Zespół edukacyjny",
|
||||
"year": "2026"
|
||||
},
|
||||
"generated": {
|
||||
"tex": "build/tex/main.tex",
|
||||
"html": "build/html/index.html",
|
||||
"html_css": "build/html/style.css"
|
||||
},
|
||||
"template": "templates/karta-5b.json",
|
||||
"front_page_break": false,
|
||||
"metric_fill_fields": [
|
||||
{
|
||||
"label": "Uczeń",
|
||||
"kind": "fill_in_line"
|
||||
},
|
||||
{
|
||||
"label": "Klasa · data",
|
||||
"kind": "fill_in_line"
|
||||
}
|
||||
],
|
||||
"side_margin_tree_layout": {
|
||||
"columns": [
|
||||
{
|
||||
"id": "zawodowe",
|
||||
"label": "TECH",
|
||||
"side": "left",
|
||||
"tree": "WE -> EK -> KW",
|
||||
"description": "Efekty techniczne po lewej stronie."
|
||||
},
|
||||
{
|
||||
"id": "ogolne",
|
||||
"label": "OG",
|
||||
"side": "right",
|
||||
"tree": "WE -> EN -> KW",
|
||||
"description": "Efekty ogólne po prawej stronie."
|
||||
}
|
||||
]
|
||||
},
|
||||
"learning_effects": {
|
||||
"DEMO.EN01": {
|
||||
"bloom_level": "Zastosowanie",
|
||||
"label": "Generowanie",
|
||||
"text": "Uczeń generuje TeX i HTML z jednego pliku JSON.",
|
||||
"assessment_criteria": [
|
||||
"DEMO.KW01"
|
||||
]
|
||||
},
|
||||
"DEMO.EK01": {
|
||||
"bloom_level": "Analiza",
|
||||
"label": "Spójność",
|
||||
"text": "Uczeń rozpoznaje wspólny model danych i layoutu.",
|
||||
"assessment_criteria": [
|
||||
"DEMO.KW01"
|
||||
]
|
||||
}
|
||||
},
|
||||
"assessment_criteria": {
|
||||
"DEMO.KW01": {
|
||||
"text": "Generator kończy pracę kodem 0, a oba wyjścia zawierają tytuł, kroki i drzewka marginesowe.",
|
||||
"learning_effects": [
|
||||
"DEMO.EN01",
|
||||
"DEMO.EK01"
|
||||
]
|
||||
}
|
||||
},
|
||||
"educational_requirements": {
|
||||
"DEMO.WE01": {
|
||||
"text": "Wygenerowanie spójnej karty pracy z jednego źródła.",
|
||||
"label": "Jedno źródło",
|
||||
"learning_effects": [
|
||||
"DEMO.EN01",
|
||||
"DEMO.EK01"
|
||||
],
|
||||
"learning_tree": {
|
||||
"schema": "we-learning-tree.v1",
|
||||
"policy": "Każdy efekt ma co najmniej jedno mierzalne kryterium.",
|
||||
"ogolne": [
|
||||
{
|
||||
"effect_ref": "DEMO.EN01",
|
||||
"display": "EN IP WO 01.01.01",
|
||||
"source": "IP",
|
||||
"official": "01.01",
|
||||
"local": "01",
|
||||
"text": "Uczeń generuje TeX i HTML z jednego pliku JSON.",
|
||||
"kind": "WO",
|
||||
"tree_id": "DEMO.WE01.OG.IP.01.01.01",
|
||||
"kw": [
|
||||
{
|
||||
"criterion_ref": "DEMO.KW01",
|
||||
"display": "KW IP WS 01.01.01",
|
||||
"source": "IP",
|
||||
"kind": "WS",
|
||||
"official": "01.01",
|
||||
"local": "01",
|
||||
"text": "Oba artefakty zawierają te same metadane i kroki."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"zawodowe": [
|
||||
{
|
||||
"effect_ref": "DEMO.EK01",
|
||||
"display": "EK INF04 01.01.01",
|
||||
"source": "I4",
|
||||
"official": "01.01",
|
||||
"local": "01",
|
||||
"text": "Uczeń rozpoznaje wspólny model danych i layoutu.",
|
||||
"kind": "EK",
|
||||
"tree_id": "DEMO.WE01.TECH.I4.01.01.01",
|
||||
"kw": [
|
||||
{
|
||||
"criterion_ref": "DEMO.KW01",
|
||||
"display": "KW INF04 01.01.01.01",
|
||||
"source": "I4",
|
||||
"kind": "KW",
|
||||
"official": "01.01.01",
|
||||
"local": "01",
|
||||
"text": "Uczeń wskazuje źródło treści, layout i oba artefakty."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"title": "Uruchomienie generatora",
|
||||
"content_kind": "prose",
|
||||
"content_tex": "Jedno źródło JSON jest renderowane do dwóch formatów.",
|
||||
"educational_requirement_refs": [
|
||||
"DEMO.WE01"
|
||||
],
|
||||
"learning_effect_refs": [
|
||||
"DEMO.EN01",
|
||||
"DEMO.EK01"
|
||||
],
|
||||
"assessment_criterion_refs": [
|
||||
"DEMO.KW01"
|
||||
],
|
||||
"area_tree_refs": [
|
||||
"DEMO.WE01.OG.IP.01.01.01",
|
||||
"DEMO.WE01.TECH.I4.01.01.01"
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"id": "K1",
|
||||
"title": "Otwórz card_source.json i odczytaj metadane.",
|
||||
"tree_refs": [
|
||||
"DEMO.WE01.OG.IP.01.01.01",
|
||||
"DEMO.WE01.TECH.I4.01.01.01"
|
||||
],
|
||||
"educational_requirement_refs": [
|
||||
"DEMO.WE01"
|
||||
],
|
||||
"learning_effect_refs": [
|
||||
"DEMO.EN01",
|
||||
"DEMO.EK01"
|
||||
],
|
||||
"assessment_criterion_refs": [
|
||||
"DEMO.KW01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "K2",
|
||||
"title": "Uruchom generator i sprawdź oba wyjścia.",
|
||||
"tree_refs": [
|
||||
"DEMO.WE01.OG.IP.01.01.01",
|
||||
"DEMO.WE01.TECH.I4.01.01.01"
|
||||
],
|
||||
"educational_requirement_refs": [
|
||||
"DEMO.WE01"
|
||||
],
|
||||
"learning_effect_refs": [
|
||||
"DEMO.EN01",
|
||||
"DEMO.EK01"
|
||||
],
|
||||
"assessment_criterion_refs": [
|
||||
"DEMO.KW01"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": {
|
||||
"task01": {
|
||||
"prompt_tex": "Wygeneruj TeX i HTML oraz porównaj tytuł i identyfikatory efektów.",
|
||||
"criterion": "Oba pliki istnieją i pochodzą z tego samego źródła.",
|
||||
"educational_requirement_refs": [
|
||||
"DEMO.WE01"
|
||||
],
|
||||
"learning_effect_refs": [
|
||||
"DEMO.EN01",
|
||||
"DEMO.EK01"
|
||||
],
|
||||
"assessment_criterion_ref": "DEMO.KW01",
|
||||
"links": {
|
||||
"equations": [],
|
||||
"figures": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"tasks_order": [
|
||||
"task01"
|
||||
],
|
||||
"mission": "Wygeneruj kartę pracy do TeX i HTML z jednego źródła, a następnie potwierdź, że oba formaty pokazują tę samą mapę efektów i te same kroki.",
|
||||
"objectives": [
|
||||
"Rozpoznasz rozdzielenie treści, layoutu i manifestu serii.",
|
||||
"Wygenerujesz TeX i HTML jednym poleceniem.",
|
||||
"Powiążesz krok z wymaganiem, efektem i kryterium."
|
||||
],
|
||||
"objective_refs": [
|
||||
{
|
||||
"tech": "01",
|
||||
"og": "01"
|
||||
},
|
||||
{
|
||||
"tech": "01",
|
||||
"og": "01"
|
||||
},
|
||||
{
|
||||
"tech": "01",
|
||||
"og": "01"
|
||||
}
|
||||
],
|
||||
"agenda": [
|
||||
{
|
||||
"time": "0--5 min",
|
||||
"work": "Odczyt źródła i drzewek edukacyjnych."
|
||||
},
|
||||
{
|
||||
"time": "5--10 min",
|
||||
"work": "Generowanie TeX i HTML."
|
||||
},
|
||||
{
|
||||
"time": "10--15 min",
|
||||
"work": "Porównanie rezultatów i zapis PASS."
|
||||
}
|
||||
],
|
||||
"hw_gate": {
|
||||
"title": "Bramka wejścia",
|
||||
"criteria": [
|
||||
{
|
||||
"check": "Python 3 jest dostępny.",
|
||||
"evidence": "python3 --version"
|
||||
},
|
||||
{
|
||||
"check": "Źródło JSON jest poprawne składniowo.",
|
||||
"evidence": "python3 -m json.tool json/card_source.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tech_stack": {
|
||||
"software": [
|
||||
{
|
||||
"name": "Python",
|
||||
"use": "walidacja i generowanie"
|
||||
},
|
||||
{
|
||||
"name": "LaTeX",
|
||||
"use": "skład PDF"
|
||||
},
|
||||
{
|
||||
"name": "HTML/CSS",
|
||||
"use": "wersja ekranowa i druk z przeglądarki"
|
||||
}
|
||||
],
|
||||
"hardware": [
|
||||
{
|
||||
"name": "Komputer",
|
||||
"use": "uruchomienie przykładu"
|
||||
}
|
||||
]
|
||||
},
|
||||
"theory_refs": {
|
||||
"intro": "Treść i layout są osobnymi warstwami. Identyfikatory WE, EN/EK i KW tworzą graf powiązań, który renderer umieszcza na marginesach.",
|
||||
"known_refs": [],
|
||||
"new_concepts": [
|
||||
{
|
||||
"term": "źródło prawdy",
|
||||
"definition": "Plik, z którego odtwarzamy wszystkie artefakty bez ręcznych poprawek."
|
||||
},
|
||||
{
|
||||
"term": "layout",
|
||||
"definition": "Kontrakt geometrii, komponentów i emiterów niezależny od treści karty."
|
||||
}
|
||||
]
|
||||
},
|
||||
"math_block": {
|
||||
"title": "Kontrola liczby wyjść",
|
||||
"tasks": [
|
||||
{
|
||||
"prompt": "Policz formaty generowane z jednego źródła.",
|
||||
"equation_tex": "N_{wyjsc} = N_{TeX} + N_{HTML} = 1 + 1 = 2",
|
||||
"answer_hint": "Dwa formaty, jedno źródło."
|
||||
}
|
||||
]
|
||||
},
|
||||
"parts": {
|
||||
"a": {
|
||||
"title": "CZĘŚĆ A -- model",
|
||||
"body": "Sprawdź identyfikatory i zależności przed generowaniem."
|
||||
},
|
||||
"b": {
|
||||
"title": "CZĘŚĆ B -- weryfikacja",
|
||||
"body": "Porównaj TeX, HTML i wynik PDF."
|
||||
}
|
||||
},
|
||||
"model_block": {
|
||||
"title": "Model danych",
|
||||
"notebook": "nie dotyczy",
|
||||
"output_constants": "json/card_source.json",
|
||||
"steps": [
|
||||
"Odczytaj metadane karty.",
|
||||
"Przejdź po relacji WE -> EN/EK -> KW.",
|
||||
"Sprawdź tree_refs dla każdego kroku."
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"name": "SOURCE_COUNT",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "OUTPUT_COUNT",
|
||||
"value": "2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"code_block": {
|
||||
"title": "Do wykonania: render przykładu",
|
||||
"context": "Pracujemy wyłącznie na plikach przykładowych w tym repozytorium.",
|
||||
"blocks": [
|
||||
{
|
||||
"title": "Bloczek 1 -- GENERATOR",
|
||||
"slug": "generator",
|
||||
"goal": "Wytworzyć oba formaty i sprawdzić ich spójność.",
|
||||
"context": "Kroki są zapisywane w JSON i emitowane do interaktywnego HTML.",
|
||||
"web_steps": [
|
||||
"Uruchom make render-example.",
|
||||
"Otwórz build/html/index.html.",
|
||||
"Sprawdź build/tex/main.tex.",
|
||||
"Potwierdź obecność TECH, OG i kroku K2."
|
||||
],
|
||||
"web_commands": [
|
||||
{
|
||||
"label": "Render",
|
||||
"command": "make render-example"
|
||||
},
|
||||
{
|
||||
"label": "Kontrola",
|
||||
"command": "make check"
|
||||
}
|
||||
],
|
||||
"web_files": [
|
||||
"json/card_source.json",
|
||||
"build/tex/main.tex",
|
||||
"build/html/index.html"
|
||||
],
|
||||
"web_evidence": [
|
||||
"Polecenie kończy się kodem 0.",
|
||||
"Oba wyjścia zawierają tytuł karty."
|
||||
],
|
||||
"web_safety": [
|
||||
"Nie edytuj ręcznie plików w build/."
|
||||
],
|
||||
"columns": [
|
||||
{
|
||||
"title": "Artefakty",
|
||||
"items": [
|
||||
"build/tex/main.tex",
|
||||
"build/html/index.html",
|
||||
"build/html/style.css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Procedura",
|
||||
"items": [
|
||||
"render",
|
||||
"kontrola marginesów",
|
||||
"kontrola kroków"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Uczeń oddaje",
|
||||
"items": [
|
||||
"wynik PASS",
|
||||
"krótkie porównanie obu formatów"
|
||||
]
|
||||
}
|
||||
],
|
||||
"pass_condition": "TeX i HTML istnieją oraz zawierają te same metadane."
|
||||
}
|
||||
]
|
||||
},
|
||||
"container_test": {
|
||||
"title": "Test bez sprzętu",
|
||||
"commands": [
|
||||
"make render-example",
|
||||
"make check"
|
||||
],
|
||||
"pass_condition": "Wszystkie kontrole kończą się kodem 0."
|
||||
},
|
||||
"bom": [
|
||||
{
|
||||
"item": "Python 3",
|
||||
"check": "Interpreter dostępny."
|
||||
},
|
||||
{
|
||||
"item": "latexmk",
|
||||
"check": "Wymagany tylko dla PDF."
|
||||
}
|
||||
],
|
||||
"safety_rules": [
|
||||
{
|
||||
"title": "Jedno źródło",
|
||||
"body": "Nie poprawiaj osobno TeX i HTML; popraw card_source.json lub layout."
|
||||
},
|
||||
{
|
||||
"title": "Stabilne identyfikatory",
|
||||
"body": "Nie zmieniaj tree_id po opublikowaniu karty bez migracji odwołań."
|
||||
}
|
||||
],
|
||||
"reference_settings": [
|
||||
{
|
||||
"test": "layout",
|
||||
"dps": "karta-5b",
|
||||
"xy": "TeX + HTML"
|
||||
}
|
||||
],
|
||||
"figures": [],
|
||||
"connection_section": {
|
||||
"title": "Przepływ danych: JSON -> renderer -> TeX/HTML",
|
||||
"intro": "Źródło treści i layout są wczytywane osobno, a następnie przekazywane do dwóch emiterów.",
|
||||
"pin_table": [
|
||||
{
|
||||
"rp2350": "card_source.json",
|
||||
"isolator_a": "walidacja",
|
||||
"isolator_b": "emiter",
|
||||
"xy": "main.tex / index.html"
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
"Treść jest niezależna od geometrii.",
|
||||
"Oba emitery korzystają z przygotowanego modelu danych."
|
||||
],
|
||||
"warnings": [
|
||||
"Ręczna zmiana artefaktu znika przy kolejnym generowaniu."
|
||||
],
|
||||
"source_note": "Repozytorium card-layouts jest źródłem generatora, schematów i layoutów."
|
||||
},
|
||||
"hardware_procedure": [
|
||||
{
|
||||
"step": "1",
|
||||
"action": "Uruchom make render-example.",
|
||||
"condition": "Generator zwraca kod 0."
|
||||
},
|
||||
{
|
||||
"step": "2",
|
||||
"action": "Otwórz HTML i sprawdź marginesy TECH/OG oraz kroki.",
|
||||
"condition": "Widoczne są oba drzewka i lista kroków."
|
||||
},
|
||||
{
|
||||
"step": "3",
|
||||
"action": "Złóż PDF poleceniem make pdf-example.",
|
||||
"condition": "Powstaje main.pdf bez błędu LaTeX."
|
||||
}
|
||||
],
|
||||
"observation_table": {
|
||||
"title": "Karta kontroli artefaktów",
|
||||
"columns": [
|
||||
"Artefakt",
|
||||
"Oczekiwany element",
|
||||
"Wynik"
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"cells": [
|
||||
"HTML",
|
||||
"tytuł, TECH, OG, kroki",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"TeX/PDF",
|
||||
"tytuł, TECH, OG, procedura",
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"troubleshooting": [
|
||||
{
|
||||
"symptom": "Brak layoutu",
|
||||
"hypothesis": "Błędna ścieżka template.",
|
||||
"action": "Sprawdź pole template i katalog templates/.",
|
||||
"result": ""
|
||||
},
|
||||
{
|
||||
"symptom": "Błąd tree_ref",
|
||||
"hypothesis": "Krok wskazuje nieistniejące drzewko.",
|
||||
"action": "Porównaj tree_refs z educational_requirements.*.learning_tree.",
|
||||
"result": ""
|
||||
}
|
||||
],
|
||||
"analysis_tasks": [
|
||||
"Wyjaśnij, dlaczego treść i layout są osobnymi plikami.",
|
||||
"Wskaż relację między DEMO.WE01, DEMO.EN01 i DEMO.KW01.",
|
||||
"Porównaj elementy obecne w TeX i HTML."
|
||||
],
|
||||
"team_decision": {
|
||||
"title": "Decyzja zespołu",
|
||||
"questions": [
|
||||
"Który layout wybieramy?",
|
||||
"Jakie były alternatywy?",
|
||||
"Jak potwierdziliśmy oba formaty?",
|
||||
"Który commit dokumentuje decyzję?"
|
||||
]
|
||||
},
|
||||
"git_gate": {
|
||||
"title": "Brama wyjścia",
|
||||
"criteria": [
|
||||
{
|
||||
"criterion": "JSON przechodzi walidację generatora.",
|
||||
"evidence": "make check"
|
||||
},
|
||||
{
|
||||
"criterion": "TeX i HTML zostały wygenerowane.",
|
||||
"evidence": "examples/card/build/"
|
||||
},
|
||||
{
|
||||
"criterion": "PDF składa się bez błędu.",
|
||||
"evidence": "main.pdf"
|
||||
}
|
||||
]
|
||||
},
|
||||
"session_record": {
|
||||
"title": "Rekord pracy",
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"purpose": "opis wyniku i poleceń"
|
||||
}
|
||||
],
|
||||
"timeline_sources": [
|
||||
"git log --oneline",
|
||||
"wynik make check"
|
||||
]
|
||||
},
|
||||
"ai_rules": {
|
||||
"allowed": [
|
||||
"wyjaśnienie błędu walidacji",
|
||||
"review spójności identyfikatorów"
|
||||
],
|
||||
"forbidden": [
|
||||
"fabrykowanie wyniku PASS",
|
||||
"ręczna poprawa wygenerowanego pliku zamiast źródła"
|
||||
],
|
||||
"student_owns": [
|
||||
"treść kroków",
|
||||
"mapowanie efektów",
|
||||
"wynik kontroli"
|
||||
],
|
||||
"log_path": "ai-log.md"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Seria demo
|
||||
|
||||
Minimalna seria pokazująca, jak opisywać kolejność kart niezależnie od ich
|
||||
layoutu i generatora.
|
||||
|
||||
## Rezultat ścieżki
|
||||
|
||||
Po przejściu serii uczeń potrafi wygenerować kartę z JSON, odczytać marginesy
|
||||
efektów nauczania oraz wykonać kroki zakończone jednoznacznym `PASS`.
|
||||
|
||||
## Mapa
|
||||
|
||||
| Karta | Temat | Rezultat | Stan |
|
||||
|---:|---|---|---|
|
||||
| 01 | źródło JSON i layout 5b | TeX, HTML i PDF z jednego źródła | gotowa |
|
||||
| 02 | własna karta | poprawny model WE → EN/EK → KW | planowana |
|
||||
|
||||
## Kontrakt wspólny
|
||||
|
||||
Każda karta:
|
||||
|
||||
- posiada `json/card_source.json` zgodny ze schematem;
|
||||
- generuje TeX i HTML tym samym narzędziem;
|
||||
- ma jawne kroki oraz kryterium `PASS`;
|
||||
- nie wymaga ręcznej edycji artefaktów wygenerowanych.
|
||||
|
||||
Maszynowym opisem tej kolejności jest `series.json`.
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "../../schemas/series.schema.json",
|
||||
"version": 1,
|
||||
"series": "demo",
|
||||
"org": "edu-demo",
|
||||
"title": "Demonstracja generatora kart",
|
||||
"default_branch": "deploy",
|
||||
"cards": [
|
||||
{
|
||||
"id": "01",
|
||||
"repo": "lab-demo-card-layout",
|
||||
"branch": "deploy",
|
||||
"title": "demo / pierwszy krok",
|
||||
"summary": "Jedno źródło JSON generujące TeX i HTML z marginesami efektów nauczania.",
|
||||
"kind": "lab",
|
||||
"status": "example",
|
||||
"area": "demo",
|
||||
"card_series": "demo",
|
||||
"card_number": "01",
|
||||
"card_count": "02",
|
||||
"slug": "pierwszy-krok",
|
||||
"version": "v0.1",
|
||||
"document_uuid": "00000000-0000-4000-8000-000000000001",
|
||||
"pdf": "doc/pdf/demo-01-pierwszy-krok-v0.1.pdf",
|
||||
"build": {
|
||||
"driver": "make",
|
||||
"all_target": "check",
|
||||
"clean_target": "clean"
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"id": "task01",
|
||||
"title": "Wygeneruj oba formaty",
|
||||
"target": "render",
|
||||
"source": "json/card_source.json",
|
||||
"build_dir": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
jsonschema>=4,<5
|
||||
@@ -0,0 +1,524 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Źródło karty pracy",
|
||||
"description": "Schemat wspólnego źródła treści generowanego do TeX i HTML.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema",
|
||||
"card",
|
||||
"generated",
|
||||
"template",
|
||||
"learning_effects",
|
||||
"assessment_criteria",
|
||||
"educational_requirements",
|
||||
"sections",
|
||||
"tasks",
|
||||
"tasks_order"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"const": "esc-card-source.v1"
|
||||
},
|
||||
"card": {
|
||||
"$ref": "#/$defs/card"
|
||||
},
|
||||
"generated": {
|
||||
"$ref": "#/$defs/generated"
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"dictionary_refs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pp_json_root": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"side_margin_tree_layout": {
|
||||
"$ref": "#/$defs/sideMarginLayout"
|
||||
},
|
||||
"learning_effects": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/learningEffect"
|
||||
}
|
||||
},
|
||||
"assessment_criteria": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/assessmentCriterion"
|
||||
}
|
||||
},
|
||||
"educational_requirements": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/educationalRequirement"
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/section"
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/task"
|
||||
}
|
||||
},
|
||||
"tasks_order": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"model_block": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"steps": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"code_block": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"goal": {
|
||||
"type": "string"
|
||||
},
|
||||
"web_steps": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"web_commands": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["label", "command"],
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"hardware_procedure": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/procedureStep"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"stringList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"card": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"series",
|
||||
"number",
|
||||
"count",
|
||||
"slug",
|
||||
"title",
|
||||
"version",
|
||||
"uuid"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"series": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"number": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"count": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"generated": {
|
||||
"type": "object",
|
||||
"required": ["tex", "html"],
|
||||
"properties": {
|
||||
"tex": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"html": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"html_css": {
|
||||
"type": "string"
|
||||
},
|
||||
"bibliography": {
|
||||
"type": "string"
|
||||
},
|
||||
"report_tasks_json": {
|
||||
"type": "string"
|
||||
},
|
||||
"report_tasks_tex": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"sideMarginLayout": {
|
||||
"type": "object",
|
||||
"required": ["columns"],
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "array",
|
||||
"minItems": 2,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "label", "side", "tree"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"enum": ["ogolne", "zawodowe"]
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"side": {
|
||||
"enum": ["left", "right"]
|
||||
},
|
||||
"tree": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"learningEffect": {
|
||||
"type": "object",
|
||||
"required": ["text"],
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"bloom_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"assessment_criteria": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"assessmentCriterion": {
|
||||
"type": "object",
|
||||
"required": ["text", "learning_effects"],
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"learning_effects": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"educationalRequirement": {
|
||||
"type": "object",
|
||||
"required": ["text", "learning_effects", "learning_tree"],
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"learning_effects": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"learning_tree": {
|
||||
"type": "object",
|
||||
"required": ["ogolne", "zawodowe"],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"policy": {
|
||||
"type": "string"
|
||||
},
|
||||
"ogolne": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/treeItem"
|
||||
}
|
||||
},
|
||||
"zawodowe": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/treeItem"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"treeItem": {
|
||||
"type": "object",
|
||||
"required": ["tree_id", "effect_ref", "display", "text", "kw"],
|
||||
"properties": {
|
||||
"tree_id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"effect_ref": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"display": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"official": {
|
||||
"type": "string"
|
||||
},
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"kw": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/$defs/treeCriterion"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"treeCriterion": {
|
||||
"type": "object",
|
||||
"required": ["criterion_ref", "display", "text"],
|
||||
"properties": {
|
||||
"criterion_ref": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"display": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"official": {
|
||||
"type": "string"
|
||||
},
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"section": {
|
||||
"type": "object",
|
||||
"required": ["title"],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"content_kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_tex": {
|
||||
"type": "string"
|
||||
},
|
||||
"area_tree_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"educational_requirement_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"learning_effect_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"assessment_criterion_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"steps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/sectionStep"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"sectionStep": {
|
||||
"type": "object",
|
||||
"required": ["id", "title", "tree_refs"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"tree_refs": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"educational_requirement_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"learning_effect_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"assessment_criterion_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"equation_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"figure_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"task": {
|
||||
"type": "object",
|
||||
"required": ["prompt_tex", "criterion"],
|
||||
"properties": {
|
||||
"prompt_tex": {
|
||||
"type": "string"
|
||||
},
|
||||
"criterion": {
|
||||
"type": "string"
|
||||
},
|
||||
"assessment_criterion_ref": {
|
||||
"type": "string"
|
||||
},
|
||||
"educational_requirement_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
},
|
||||
"learning_effect_refs": {
|
||||
"$ref": "#/$defs/stringList"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"procedureStep": {
|
||||
"type": "object",
|
||||
"required": ["step", "action", "condition"],
|
||||
"properties": {
|
||||
"step": {
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"condition": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Layout karty pracy",
|
||||
"description": "Schemat pliku templates/karta-*.json używanego przez render_card.py.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"tokens",
|
||||
"emitters"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"const": "esc-card-template.v1"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"approved": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"tokens": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"page": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"type": "string"
|
||||
},
|
||||
"orientation": {
|
||||
"enum": ["portrait", "landscape"]
|
||||
},
|
||||
"width_px": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
},
|
||||
"height_px": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"grid": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "array",
|
||||
"minItems": 3,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"left_role": {
|
||||
"type": "string"
|
||||
},
|
||||
"right_role": {
|
||||
"type": "string"
|
||||
},
|
||||
"geometry_cm": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"typography": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"colors": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"components": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"block_map": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"emitters": {
|
||||
"type": "object",
|
||||
"required": ["html", "latex"],
|
||||
"properties": {
|
||||
"html": {
|
||||
"enum": ["classic", "5a", "5b"]
|
||||
},
|
||||
"latex": {
|
||||
"enum": ["classic", "5a", "5b"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"limitations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Manifest serii kart",
|
||||
"description": "Maszynowy opis kolejności repozytoriów kart i ich zadań.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version",
|
||||
"series",
|
||||
"org",
|
||||
"title",
|
||||
"default_branch",
|
||||
"cards"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"const": 1
|
||||
},
|
||||
"series": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
||||
},
|
||||
"org": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"default_branch": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"cards": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/cardEntry"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"cardEntry": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"repo",
|
||||
"branch",
|
||||
"title",
|
||||
"summary",
|
||||
"status",
|
||||
"card_series",
|
||||
"card_number",
|
||||
"card_count",
|
||||
"slug",
|
||||
"version",
|
||||
"build",
|
||||
"tasks"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
||||
},
|
||||
"branch": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"summary": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"area": {
|
||||
"type": "string"
|
||||
},
|
||||
"card_series": {
|
||||
"type": "string"
|
||||
},
|
||||
"card_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"card_count": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"document_uuid": {
|
||||
"type": "string"
|
||||
},
|
||||
"pdf": {
|
||||
"type": "string"
|
||||
},
|
||||
"build": {
|
||||
"type": "object",
|
||||
"required": ["driver", "all_target", "clean_target"],
|
||||
"properties": {
|
||||
"driver": {
|
||||
"const": "make"
|
||||
},
|
||||
"all_target": {
|
||||
"type": "string"
|
||||
},
|
||||
"clean_target": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "title", "target"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"build_dir": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCHEMAS = ROOT / "schemas"
|
||||
TEMPLATES = ROOT / "templates"
|
||||
EXAMPLE_CARD = ROOT / "examples" / "card"
|
||||
EXAMPLE_SERIES = ROOT / "examples" / "series" / "series.json"
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict:
|
||||
with path.open(encoding="utf-8") as stream:
|
||||
value = json.load(stream)
|
||||
if not isinstance(value, dict):
|
||||
raise AssertionError(f"{path}: korzeń JSON musi być obiektem")
|
||||
return value
|
||||
|
||||
|
||||
def check_json_files() -> None:
|
||||
for path in sorted(SCHEMAS.glob("*.json")):
|
||||
schema = load_json(path)
|
||||
assert schema.get("$schema", "").endswith("2020-12/schema"), path
|
||||
|
||||
for path in sorted(TEMPLATES.glob("*.json")):
|
||||
template = load_json(path)
|
||||
assert template.get("schema") == "esc-card-template.v1", path
|
||||
assert template.get("emitters", {}).get("latex"), path
|
||||
assert template.get("emitters", {}).get("html"), path
|
||||
|
||||
card = load_json(EXAMPLE_CARD / "json" / "card_source.json")
|
||||
assert card.get("schema") == "esc-card-source.v1"
|
||||
assert card.get("sections")
|
||||
assert card.get("educational_requirements")
|
||||
|
||||
series = load_json(EXAMPLE_SERIES)
|
||||
assert series.get("version") == 1
|
||||
assert series.get("cards")
|
||||
|
||||
|
||||
def check_optional_json_schema() -> None:
|
||||
try:
|
||||
import jsonschema
|
||||
except ModuleNotFoundError:
|
||||
if os.environ.get("CARD_LAYOUTS_REQUIRE_JSONSCHEMA") == "1":
|
||||
raise AssertionError("brak pakietu jsonschema wymaganego przez pełną kontrolę")
|
||||
print("INFO: jsonschema nie jest zainstalowany; wykonuję walidację generatora.")
|
||||
return
|
||||
|
||||
pairs = [
|
||||
(SCHEMAS / "card-source.schema.json", EXAMPLE_CARD / "json" / "card_source.json"),
|
||||
(SCHEMAS / "series.schema.json", EXAMPLE_SERIES),
|
||||
]
|
||||
pairs.extend(
|
||||
(SCHEMAS / "card-template.schema.json", path)
|
||||
for path in sorted(TEMPLATES.glob("*.json"))
|
||||
)
|
||||
for schema_path, document_path in pairs:
|
||||
jsonschema.Draft202012Validator(load_json(schema_path)).validate(load_json(document_path))
|
||||
|
||||
|
||||
def check_render() -> None:
|
||||
subprocess.run(
|
||||
[sys.executable, str(ROOT / "tools" / "render_card.py"), str(EXAMPLE_CARD)],
|
||||
cwd=ROOT,
|
||||
check=True,
|
||||
)
|
||||
|
||||
tex_path = EXAMPLE_CARD / "build" / "tex" / "main.tex"
|
||||
html_path = EXAMPLE_CARD / "build" / "html" / "index.html"
|
||||
css_path = EXAMPLE_CARD / "build" / "html" / "style.css"
|
||||
for path in (tex_path, html_path, css_path):
|
||||
if not path.is_file() or path.stat().st_size == 0:
|
||||
raise AssertionError(f"brak wygenerowanego pliku: {path}")
|
||||
|
||||
tex = tex_path.read_text(encoding="utf-8")
|
||||
html = html_path.read_text(encoding="utf-8")
|
||||
for marker in ("DEMO-01", "IP WO", "INF04"):
|
||||
if marker not in tex:
|
||||
raise AssertionError(f"TeX nie zawiera znacznika {marker!r}")
|
||||
if marker not in html:
|
||||
raise AssertionError(f"HTML nie zawiera znacznika {marker!r}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
check_json_files()
|
||||
check_optional_json_schema()
|
||||
check_render()
|
||||
print("PASS: schematy, layouty, przykład serii oraz render TeX/HTML są spójne.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,184 @@
|
||||
{
|
||||
"$schema": "../schemas/card-template.schema.json",
|
||||
"schema": "esc-card-template.v1",
|
||||
"id": "karta-5a",
|
||||
"name": "Karta 5a",
|
||||
"version": "1.0",
|
||||
"description": "Wielostronicowy layout A4 landscape wyekstrahowany z referencyjnego pliku series/esc/html/ESC-04 karta.html; pierwsze dwie strony zachowują konwencję wzorca, dalsze strony dokładają pełną treść karty.",
|
||||
"tokens": {
|
||||
"page": {
|
||||
"format": "a4",
|
||||
"orientation": "landscape",
|
||||
"width_px": 1123,
|
||||
"height_px": 794,
|
||||
"padding_px": {
|
||||
"top": 38,
|
||||
"right": 38,
|
||||
"bottom": 30,
|
||||
"left": 38
|
||||
},
|
||||
"paper": "#fefefc",
|
||||
"desk": "#eceae4",
|
||||
"ink": "#1c1c1a"
|
||||
},
|
||||
"grid": {
|
||||
"columns": [
|
||||
"112px",
|
||||
"1fr",
|
||||
"112px"
|
||||
],
|
||||
"gap_px": 19,
|
||||
"left_role": "TECH",
|
||||
"right_role": "OG"
|
||||
},
|
||||
"typography": {
|
||||
"text": "STIX Two Text",
|
||||
"mono": "JetBrains Mono",
|
||||
"body_px": 13,
|
||||
"line_height": 1.55,
|
||||
"margin_tree_px": 7.5,
|
||||
"label_px": 8.5,
|
||||
"footer_px": 9.5
|
||||
},
|
||||
"colors": {
|
||||
"paper": "#fefefc",
|
||||
"desk": "#eceae4",
|
||||
"ink": "#1c1c1a",
|
||||
"muted": "#8a887f",
|
||||
"subtle": "#4a4944",
|
||||
"header": "#f4f3ef",
|
||||
"line": "#d8d6d0",
|
||||
"line_soft": "#e4e2dc",
|
||||
"tree_line": "#c9c7bf",
|
||||
"hairline": "#f0efe9",
|
||||
"accent": "#3b5a8f"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"section_box": {
|
||||
"border": "1px solid #1c1c1a",
|
||||
"header": "§N + uppercase title, letter-spacing .12em, background #f4f3ef"
|
||||
},
|
||||
"metric_header": {
|
||||
"border": "1.5px solid #1c1c1a",
|
||||
"title_bar": true,
|
||||
"grid": "2x2",
|
||||
"fill_fields": [
|
||||
"Uczeń",
|
||||
"Klasa · data"
|
||||
]
|
||||
},
|
||||
"margin_tree_panel": {
|
||||
"left": "TECH — zawodowe; WE → EK → KW",
|
||||
"right": "OG — ogólne; WE → EN → KW",
|
||||
"footer": "KW wspólne + dominanta"
|
||||
},
|
||||
"checkbox_row": {
|
||||
"size_px": 11,
|
||||
"border": "1.2px solid #1c1c1a"
|
||||
},
|
||||
"agenda_grid": {
|
||||
"columns": [
|
||||
"56px",
|
||||
"1fr"
|
||||
],
|
||||
"time_color": "#3b5a8f"
|
||||
},
|
||||
"theory_table": {
|
||||
"border": "1px solid #d8d6d0",
|
||||
"id_font": "JetBrains Mono"
|
||||
},
|
||||
"tech_stack_table": {
|
||||
"columns": [
|
||||
"88px",
|
||||
"1fr"
|
||||
],
|
||||
"groups": [
|
||||
"Software",
|
||||
"Hardware"
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"rule": "1.5px solid #1c1c1a",
|
||||
"format": "commit · uuid · wersja · data | strona N / M | status"
|
||||
},
|
||||
"fill_in_line": {
|
||||
"style": "1px dotted #9a988f"
|
||||
}
|
||||
},
|
||||
"block_map": {
|
||||
"page_1": [
|
||||
"metric_header",
|
||||
"mission",
|
||||
"objectives",
|
||||
"agenda",
|
||||
"hw_gate"
|
||||
],
|
||||
"page_2": [
|
||||
"running_card_header",
|
||||
"theory_refs",
|
||||
"tech_stack",
|
||||
"math_block"
|
||||
],
|
||||
"page_3": [
|
||||
"part_a_banner",
|
||||
"model_block",
|
||||
"code_block",
|
||||
"container_test",
|
||||
"bom"
|
||||
],
|
||||
"page_4": [
|
||||
"part_b_banner",
|
||||
"safety_rules",
|
||||
"reference_settings",
|
||||
"figures",
|
||||
"hardware_procedure"
|
||||
],
|
||||
"page_5": [
|
||||
"observation_table"
|
||||
],
|
||||
"page_6": [
|
||||
"troubleshooting",
|
||||
"analysis_tasks"
|
||||
],
|
||||
"page_7": [
|
||||
"team_decision",
|
||||
"git_gate",
|
||||
"session_record",
|
||||
"ai_rules",
|
||||
"references"
|
||||
],
|
||||
"objectives": "checkbox_row + objective_refs",
|
||||
"agenda": "agenda_grid",
|
||||
"hw_gate": "checkbox_row + teacher_signature",
|
||||
"theory_refs": "theory_table",
|
||||
"tech_stack": "tech_stack_table",
|
||||
"math_block": "equations + answer_lines",
|
||||
"model_block": "notebook + constants_table + manual_inputs",
|
||||
"code_block": "skeleton_files + interfaces + responsibilities + pass_condition",
|
||||
"container_test": "command_list + evidence_line",
|
||||
"bom": "checkbox_table",
|
||||
"safety_rules": "golden_rules_grid",
|
||||
"reference_settings": "compact_reference_table",
|
||||
"figures": "kicad_includegraphics",
|
||||
"hardware_procedure": "checkbox_steps",
|
||||
"observation_table": "spacious_grid",
|
||||
"troubleshooting": "symptom_hypothesis_action_result_log",
|
||||
"analysis_tasks": "answer_lines",
|
||||
"team_decision": "mini_adr",
|
||||
"git_gate": "checkbox_criterion_evidence_signature",
|
||||
"ai_rules": "compact_policy_box",
|
||||
"references": "compact_mono_list"
|
||||
},
|
||||
"emitters": {
|
||||
"html": "5a",
|
||||
"latex": "5a"
|
||||
},
|
||||
"limitations": {
|
||||
"latex": [
|
||||
"pdfLaTeX nie używa fontów Google Fonts; renderer wybiera stix2, jeśli jest dostępny, oraz mono z pakietu inconsolata/beramono/lmodern jako fallback.",
|
||||
"HTML odwzorowuje pikselowe rozmiary 1123x794; LaTeX mapuje je na A4 landscape w centymetrach.",
|
||||
"Panele drzewek w LaTeX są statycznymi minipage, bez interaktywnych tooltipów HTML."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"$schema": "../schemas/card-template.schema.json",
|
||||
"schema": "esc-card-template.v1",
|
||||
"id": "karta-5b",
|
||||
"name": "Karta 5b",
|
||||
"version": "1.3",
|
||||
"approved": "04.07.2026 — layout referencyjny zatwierdzony przez prowadzącego (symetria 0,55/2,05/0,55/14,70; gołe drzewka); v1.1 — nagłówki punktowe zamiast §, 05.07.2026; v1.2 — linie nagłówka/stopki jako warstwa strony ograniczona do bloku treści, 06.07.2026; v1.3 — margines gorny/dolny 2,5 cm, 06.07.2026",
|
||||
"description": "Wielostronicowy layout A4 portrait: charakter 5a z ramkami sekcji, ale z golymi drzewkami WE->EN/EK jako marginalia bez paneli i bez luznych KW. Linie strony sa warstwa overlay i nie wychodza pod drzewka. Blok tresci zaczyna sie 2,5 cm od gornej krawedzi i konczy 2,5 cm od dolnej.",
|
||||
"tokens": {
|
||||
"page": {
|
||||
"format": "a4",
|
||||
"orientation": "portrait",
|
||||
"width_px": 794,
|
||||
"height_px": 1123,
|
||||
"padding_px": {
|
||||
"top": 95,
|
||||
"right": 21,
|
||||
"bottom": 95,
|
||||
"left": 21
|
||||
},
|
||||
"paper": "#fefefc",
|
||||
"desk": "#eceae4",
|
||||
"ink": "#1c1c1a"
|
||||
},
|
||||
"grid": {
|
||||
"columns": [
|
||||
"77px",
|
||||
"21px",
|
||||
"1fr",
|
||||
"21px",
|
||||
"77px"
|
||||
],
|
||||
"gap_px": 0,
|
||||
"left_gap_px": 21,
|
||||
"right_gap_px": 21,
|
||||
"geometry_cm": {
|
||||
"top": 2.5,
|
||||
"bottom": 2.5,
|
||||
"outer_margin_left": 0.55,
|
||||
"left_tree": 2.05,
|
||||
"left_gap": 0.55,
|
||||
"content": 14.7,
|
||||
"content_height": 24.7,
|
||||
"right_gap": 0.55,
|
||||
"right_tree": 2.05,
|
||||
"outer_margin_right": 0.55
|
||||
},
|
||||
"left_role": "TECH",
|
||||
"right_role": "OG"
|
||||
},
|
||||
"page_rules": {
|
||||
"layer": "overlay",
|
||||
"portrait_content_width_cm": 14.7,
|
||||
"portrait_content_height_cm": 24.7,
|
||||
"portrait_rule_scope": "kolumna tresci; linia nie wchodzi pod drzewka ani w przerwy marginesowe",
|
||||
"landscape_schematic_slot_mm": 242,
|
||||
"landscape_schematic_slot_height_mm": 160,
|
||||
"landscape_schematic_figure_height_mm": 135,
|
||||
"landscape_rule_scope": "slot schematu technicznego; pasek sekcji i stopka maja ta sama szerokosc co arkusz",
|
||||
"line_weight_pt": 0.5
|
||||
},
|
||||
"typography": {
|
||||
"text": "STIX Two Text",
|
||||
"mono": "JetBrains Mono",
|
||||
"body_px": 11,
|
||||
"line_height": 1.42,
|
||||
"margin_tree_px": 7.2,
|
||||
"label_px": 7.8,
|
||||
"footer_px": 8.2
|
||||
},
|
||||
"colors": {
|
||||
"paper": "#fefefc",
|
||||
"desk": "#eceae4",
|
||||
"ink": "#1c1c1a",
|
||||
"muted": "#8a887f",
|
||||
"subtle": "#4a4944",
|
||||
"header": "#f4f3ef",
|
||||
"line": "#d8d6d0",
|
||||
"line_soft": "#e4e2dc",
|
||||
"tree_line": "#c9c7bf",
|
||||
"hairline": "#f0efe9",
|
||||
"accent": "#3b5a8f"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"section_box": {
|
||||
"border": "1px solid #1c1c1a",
|
||||
"header": "N. + uppercase title, letter-spacing .12em, background #f4f3ef"
|
||||
},
|
||||
"metric_header": {
|
||||
"border": "1.5px solid #1c1c1a",
|
||||
"title_bar": true,
|
||||
"grid": "2x2",
|
||||
"fill_fields": [
|
||||
"Uczen",
|
||||
"Klasa · data"
|
||||
]
|
||||
},
|
||||
"margin_tree_panel": {
|
||||
"left": "TECH jako gole drzewko tekstowe WE->EK/EN bez KW, bez ramki, tla, naglowka i stopki",
|
||||
"right": "OG jako gole drzewko tekstowe WE->EK/EN bez KW, bez ramki, tla, naglowka i stopki",
|
||||
"footer": false
|
||||
},
|
||||
"checkbox_row": {
|
||||
"size_px": 10,
|
||||
"border": "1.1px solid #1c1c1a"
|
||||
},
|
||||
"agenda_grid": {
|
||||
"columns": [
|
||||
"46px",
|
||||
"1fr"
|
||||
],
|
||||
"time_color": "#3b5a8f"
|
||||
},
|
||||
"theory_table": {
|
||||
"border": "1px solid #d8d6d0",
|
||||
"id_font": "JetBrains Mono"
|
||||
},
|
||||
"tech_stack_table": {
|
||||
"columns": [
|
||||
"72px",
|
||||
"1fr"
|
||||
],
|
||||
"groups": [
|
||||
"Software",
|
||||
"Hardware"
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"rule": "1.5px solid #1c1c1a",
|
||||
"layer": "page overlay",
|
||||
"width": "content block only: 14.7cm portrait, 242mm technical landscape",
|
||||
"format": "commit · uuid · wersja · data | strona N / M | status"
|
||||
},
|
||||
"running_card_header": {
|
||||
"rule": "1.4px solid #1c1c1a",
|
||||
"layer": "page overlay",
|
||||
"width": "content block only: 14.7cm portrait, 242mm technical landscape"
|
||||
},
|
||||
"fill_in_line": {
|
||||
"style": "1px dotted #9a988f"
|
||||
}
|
||||
},
|
||||
"block_map": {
|
||||
"page_1": [
|
||||
"metric_header",
|
||||
"mission",
|
||||
"objectives",
|
||||
"agenda",
|
||||
"hw_gate",
|
||||
"theory_refs",
|
||||
"tech_stack"
|
||||
],
|
||||
"page_2": [
|
||||
"running_card_header",
|
||||
"math_block",
|
||||
"part_a_banner",
|
||||
"model_block",
|
||||
"code_block",
|
||||
"container_test",
|
||||
"bom"
|
||||
],
|
||||
"page_3": [
|
||||
"part_b_banner",
|
||||
"safety_rules",
|
||||
"reference_settings",
|
||||
"figures",
|
||||
"hardware_procedure"
|
||||
],
|
||||
"page_4": [
|
||||
"observation_table",
|
||||
"troubleshooting"
|
||||
],
|
||||
"page_5": [
|
||||
"analysis_tasks",
|
||||
"team_decision",
|
||||
"git_gate",
|
||||
"session_record",
|
||||
"ai_rules",
|
||||
"references"
|
||||
],
|
||||
"objectives": "checkbox_row + objective_refs",
|
||||
"agenda": "agenda_grid",
|
||||
"hw_gate": "checkbox_row + teacher_signature",
|
||||
"theory_refs": "theory_table",
|
||||
"tech_stack": "tech_stack_table",
|
||||
"math_block": "equations + answer_lines",
|
||||
"model_block": "notebook + constants_table + manual_inputs",
|
||||
"code_block": "skeleton_files + interfaces + responsibilities + pass_condition",
|
||||
"container_test": "command_list + evidence_line",
|
||||
"bom": "checkbox_table",
|
||||
"safety_rules": "golden_rules_grid",
|
||||
"reference_settings": "compact_reference_table",
|
||||
"figures": "kicad_includegraphics",
|
||||
"hardware_procedure": "checkbox_steps",
|
||||
"observation_table": "spacious_grid",
|
||||
"troubleshooting": "symptom_hypothesis_action_result_log",
|
||||
"analysis_tasks": "answer_lines",
|
||||
"team_decision": "mini_adr",
|
||||
"git_gate": "checkbox_criterion_evidence_signature",
|
||||
"ai_rules": "compact_policy_box",
|
||||
"references": "compact_mono_list"
|
||||
},
|
||||
"emitters": {
|
||||
"html": "5b",
|
||||
"latex": "5b"
|
||||
},
|
||||
"limitations": {
|
||||
"latex": [
|
||||
"pdfLaTeX nie uzywa fontow Google Fonts; renderer wybiera stix2, jesli jest dostepny, oraz mono z pakietu inconsolata/beramono/lmodern jako fallback.",
|
||||
"HTML odwzorowuje pikselowe rozmiary 794x1123; LaTeX mapuje je na A4 portrait z minipage dla marginesow.",
|
||||
"Drzewka marginesowe sa statycznym tekstem mono: lewy margines TECH, prawy margines OG."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"$schema": "../schemas/card-template.schema.json",
|
||||
"schema": "esc-card-template.v1",
|
||||
"id": "karta-klasyczna",
|
||||
"name": "Karta klasyczna",
|
||||
"version": "1.0",
|
||||
"description": "Dotychczasowy portretowy layout generatora: metryczka, marginesy z drzewkami po bokach, sekcje pełnej karty i słownik.",
|
||||
"tokens": {
|
||||
"page": {
|
||||
"format": "a4",
|
||||
"orientation": "portrait",
|
||||
"paper": "#ffffff",
|
||||
"margin_cm": {
|
||||
"left": 2.15,
|
||||
"right": 2.15,
|
||||
"top": 2.2,
|
||||
"bottom": 2.2
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"columns": [
|
||||
"116px",
|
||||
"minmax(0, 1fr)",
|
||||
"116px"
|
||||
],
|
||||
"gap_px": 18
|
||||
},
|
||||
"typography": {
|
||||
"text": "Georgia, Times New Roman, serif",
|
||||
"mono": "Latin Modern Mono, ui-monospace, SFMono-Regular, Consolas, monospace",
|
||||
"body_px": 17,
|
||||
"margin_px": 7
|
||||
},
|
||||
"colors": {
|
||||
"paper": "#ffffff",
|
||||
"desk": "#eeeeee",
|
||||
"ink": "#111111",
|
||||
"muted": "#666666",
|
||||
"line": "#b9b9b9",
|
||||
"line_soft": "#dddddd",
|
||||
"we": "#e00000",
|
||||
"og": "#008000",
|
||||
"tech": "#d36b00",
|
||||
"kw": "#0018c8"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"section_box": "classic_section",
|
||||
"metric_header": "classic_front_matter",
|
||||
"margin_tree_panel": "classic_margin_notes",
|
||||
"checkbox_row": "classic_empty_check",
|
||||
"agenda_grid": "classic_compact_agenda",
|
||||
"theory_table": "classic_tabularx",
|
||||
"tech_stack_table": "classic_tabularx",
|
||||
"footer": "classic_fancyhdr",
|
||||
"fill_in_line": "classic_dotfill"
|
||||
},
|
||||
"block_map": {
|
||||
"front_matter": "metric_header",
|
||||
"front_page_scope": "classic_scope",
|
||||
"objectives": "section_box",
|
||||
"agenda": "section_box",
|
||||
"hw_gate": "section_box",
|
||||
"theory_refs": "section_box",
|
||||
"tech_stack": "section_box",
|
||||
"sections": "section_box",
|
||||
"bibliography": "section_box",
|
||||
"dictionary": "dictionary_page"
|
||||
},
|
||||
"emitters": {
|
||||
"html": "classic",
|
||||
"latex": "classic"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user