mirror of
https://github.com/ARUP-CAS/aiscr-qgis-amcr-viewer.git
synced 2026-10-09 12:27:36 +02:00
Plánovaný workflow api_monitor.yml (denně 05:17 UTC + ruční spuštění) ověřuje kontrakt API, na kterém plugin závisí: - tests/api_contract.py – stejné dotazy jako plugin, kontrola klíčů, typů a tvarů odpovědí (OK / DRIFT / FAIL / UNAVAILABLE) - tests/api_plugin_live.py – vlastní funkce pluginu (fetch_set, load_amcr_data) proti živému API v qgis/qgis:ltr - tests/api_monitor_report.py – jedno sledovací issue se štítkem api-monitor; čistý běh ho zavře, výpadek ho nemění Neběží na PR, aby výpadek digiarchivu neshodil PR. Popis v AGENTS.md, OpenSpec změna add-daily-api-monitor. Připraveno s pomocí AI (Claude), ověřeno proti produkčnímu API.
323 lines
15 KiB
Markdown
323 lines
15 KiB
Markdown
# AGENTS.md
|
||
|
||
Pokyny pro AI agenty i lidské přispěvatele pracující v tomto repozitáři.
|
||
Tento soubor je jediný zdroj pravdy; `CLAUDE.md` na něj pouze odkazuje.
|
||
|
||
## O projektu
|
||
|
||
**AMČR Viewer** je plugin do QGIS pro stahování a vizualizaci dat z Digitálního
|
||
archivu Archeologické mapy ČR (AMČR / AIS CR) – akce (*Fieldwork events*),
|
||
lokality (*Sites*) a jejich komponenty. Podporuje anonymní i přihlášený přístup
|
||
přes AMČR účet.
|
||
|
||
Zdroj dat: https://digiarchiv.aiscr.cz/ · Nápověda: https://amcr-help.aiscr.cz/digiarchiv/qgis-viewer.html
|
||
|
||
## Zařazení v ekosystému AIS CR
|
||
|
||
Tento repozitář je jedním ze **sourozeneckých repozitářů** ekosystému AIS CR.
|
||
Centrální governance a AI konfigurace spravuje hub **`aiscr-management`**; konvence
|
||
v tomto souboru jsou s tímto vzorem sladěné a zjednodušené pro potřeby jednoho
|
||
QGIS pluginu. Z hubu přebírá **OpenSpec** ve stupni `change-tracked` (viz
|
||
níže). Ostatní mašinerii hubu (složka `.agents/`, sync skripty, vlastní
|
||
schémata OpenSpec, multi-assistant generování) tento repozitář **záměrně
|
||
nepřebírá**. Při širších otázkách governance má přednost vzor
|
||
z `aiscr-management`.
|
||
|
||
## OpenSpec
|
||
|
||
Repozitář používá OpenSpec ve stupni **`change-tracked`**: plánovací
|
||
artefakty změn (`proposal.md`, delta spec, `design.md`, `tasks.md`) žijí
|
||
v `openspec/changes/<slug>/`, trvalé specifikace v `openspec/specs/` se
|
||
**neudržují**. Stupeň a kontext pro agenty jsou v `openspec/config.yaml`;
|
||
změna stupně se dělá vědomě společně s hubem, ne v rámci rozpracované práce.
|
||
|
||
- **Kdy založit změnu:** práce, která mění chování (co uživatel vidí,
|
||
atributy vrstev, kontrakt s API digiarchivu, uložená nastavení), zasahuje
|
||
víc repozitářů nebo mění pravidla / AI konfiguraci / CI.
|
||
- **Kdy ne:** překlepy a formátování, bump závislostí či pinů nástrojů bez
|
||
změny chování, přegenerování odvozených souborů.
|
||
- **Postup:** `openspec new change <slug>` → artefakty → `openspec validate
|
||
<slug> --strict` → implementace (až na výslovný pokyn) → po merge
|
||
`openspec archive <slug> --skip-specs` (archiv
|
||
`openspec/changes/archive/RRRR-MM-DD-<slug>/`).
|
||
- Artefakty změny jdou **ve stejném PR** jako implementace; v popisu PR
|
||
odkaž na adresář změny.
|
||
- Používá se vestavěné schéma `spec-driven`; vlastní schémata hubu se sem
|
||
nepřenášejí. CLI: `npx @fission-ai/openspec@1.14.0` (nebo lokálně
|
||
nainstalované `openspec`); bez CLI lze artefakty psát i ručně.
|
||
- Asistentské povrchy (`.claude/`, `.github/prompts/` …) doručuje sync
|
||
z hubu; v tomto repozitáři se ručně nezakládají ani necommitují.
|
||
|
||
## Struktura repozitáře
|
||
|
||
```
|
||
amcr_viewer/ # vlastní kód pluginu (toto se balí do releasu)
|
||
amcr_viewer.py # vstupní bod pluginu, integrace do QGIS
|
||
amcr_dialog.py # dialogy a UI (filtry, přihlášení)
|
||
amcr_tools.py # stahování dat z API, sestavení vrstev a atributů
|
||
amcr_codelists.py # hesláře / číselníky (codelists)
|
||
codelists/heslar.csv # lokální kopie číselníků
|
||
metadata.txt # metadata pluginu + verze + changelog
|
||
i18n/ # překlady (.ts)
|
||
*.png # ikony
|
||
.github/workflows/ # CI – kontroly kvality a release pluginu
|
||
openspec/ # OpenSpec – konfigurace a plánovací artefakty změn
|
||
README.md # uživatelská dokumentace (anglicky)
|
||
```
|
||
|
||
## Konvence
|
||
|
||
### Jazyk
|
||
- **Kód a identifikátory:** anglicky. Atributová pole vrstev musí být ASCII
|
||
kompatibilní (bez diakritiky) – viz historie změn v `metadata.txt`.
|
||
- **README a uživatelská dokumentace:** anglicky.
|
||
- **Commity, PR a komentáře v issue:** česky.
|
||
|
||
### Commity
|
||
- Styl odpovídá historii: česky, věcně, popisně; jeden commit = jedna logická
|
||
změna.
|
||
- První řádek stručně a výstižně (ideálně v imperativu); podrobnosti do těla.
|
||
- Pokud je commit připraven s pomocí AI, uveď to v těle commitu nebo v popisu PR.
|
||
|
||
### Větve
|
||
Konvence názvů je sladěná s hubem `aiscr-management`:
|
||
|
||
- **lidé:** `feat/<téma>` (nová funkce), `fix/<téma>` (oprava), `docs/<téma>`
|
||
(dokumentace), `chore/<téma>` (údržba).
|
||
- **AI agenti:** `agents/<jméno-agenta>/<téma>` (např. `agents/claude/oprava-pian`).
|
||
|
||
Další pravidla:
|
||
|
||
- Cílová větev pro nový vývoj je aktuální `version/v2.x.y` (ne přímo do
|
||
výchozí větve bez PR).
|
||
- **Nikdy** nepushuj přímo do chráněných větví; vždy přes Pull Request.
|
||
- Standardizační / nefunkční změny drž v samostatné větvi, ať se nemíchají do
|
||
feature PR.
|
||
|
||
### Pravidla pro AI agenty (git)
|
||
- AI ve výchozím stavu zůstává u **lokální práce na aktuální větvi**.
|
||
- Bez **výslovného pokynu** uživatele AI nestageuje (`git add`), necommituje,
|
||
nepushuje ani samo nepřepíná/nezakládá větev pro vzdálené doručení.
|
||
- Při výslovném požadavku na push/PR použij větev `agents/<jméno-agenta>/<téma>`;
|
||
pokud aktuální větev tomuto vzoru neodpovídá, vyžádej si nejdřív potvrzení.
|
||
- Vytvoření větve, stage, commit, push ani draft PR AI běžně nenabízí; zmiňuj je
|
||
jen tehdy, když jsou pro splnění úkolu opravdu nutné.
|
||
|
||
### QGIS specifika
|
||
- Minimální podporovaná verze QGIS je **3.44** (`qgisMinimumVersion` v
|
||
`metadata.txt`); kód nesmí spoléhat na novější API.
|
||
- Vrstvy a atributy se sestavují přes `QgsField` / QGIS API v `amcr_tools.py`.
|
||
Při přidání atributu je potřeba doplnit ho konzistentně na všech místech:
|
||
definice pole (`QgsField`), naplnění hodnoty z dokumentu, překlad hlavičky
|
||
sloupce a export atributů.
|
||
|
||
### Kompatibilita s Qt6 / QGIS 4
|
||
|
||
Plugin cílí na QGIS 3.44 i na QGIS 4 (`qgisMaximumVersion=4.99.0`), tedy na
|
||
Qt5 i Qt6 zároveň. **Tohle se drží rigorózně** – ne až před releasem, ale při
|
||
každé změně kódu. Chování obou větví se liší tiše: pod Qt5 projde i to, co
|
||
QGIS 4 odmítne, takže lokální „funguje mi to“ nic nedokazuje.
|
||
|
||
Závazná pravidla:
|
||
|
||
- **Nikdy neimportuj přímo z `PyQt5` ani z `PyQt6`.** Vždy přes shim
|
||
`qgis.PyQt.*`. Ten mimo jiné pod Qt6 přetahuje `QAction`, `QActionGroup`
|
||
a `QShortcut` z `QtGui`, takže import z `qgis.PyQt.QtWidgets` je správně.
|
||
- **Enumy vždy plně kvalifikované (scoped).** `Qgis.MessageLevel.Info`, ne
|
||
`Qgis.Info`; `QgsTask.Flag.CanCancel`, ne `QgsTask.CanCancel`;
|
||
`QgsWkbTypes.GeometryType.PointGeometry`, ne `QgsWkbTypes.PointGeometry`.
|
||
Totéž pro Qt: `Qt.CheckState.Checked`, `QDialogButtonBox.StandardButton.Ok`.
|
||
Zkrácené tvary sice v QGIS 4.2 zatím fungují, ale oficiální kontrola je
|
||
hlásí a do budoucna mizí.
|
||
- **Zdrojové `.py` soubory ukládej bez BOM.** Kontrolní skript čte soubor
|
||
jako UTF-8 bez `utf-8-sig` a na BOM spadne s
|
||
`SyntaxError: invalid non-printable character U+FEFF`, takže se takový
|
||
soubor **vůbec nezkontroluje**. (`codelists/heslar.csv` BOM mít smí, tam je
|
||
kvůli Excelu.)
|
||
- Nepoužívej API zrušená v Qt6: `exec_()`, `QRegExp`, `QDesktopWidget`,
|
||
`QApplication.desktop()`, `Qt.MidButton`, `QFontMetrics.width()`,
|
||
`setResizeMode`, atributy `AA_EnableHighDpiScaling` / `AA_UseHighDpiPixmaps`.
|
||
- `supportsQt6=True` v `metadata.txt` **nepatří** – bylo zrušeno; o zařazení
|
||
mezi „QGIS 4 Ready“ rozhoduje rozsah `qgisMinimumVersion` až
|
||
`qgisMaximumVersion`.
|
||
|
||
Ověření před PR, který mění Python kód:
|
||
|
||
```sh
|
||
# oficiální kontrola, kterou pouští i plugins.qgis.org (pyqgis4-checker)
|
||
docker run --rm --pull always --user $(id -u):$(id -g) \
|
||
--workdir /workspace/ -v "$(pwd):/workspace/" \
|
||
ghcr.io/qgis/pyqgis4-checker:main-ubuntu \
|
||
pyqt5_to_pyqt6.py --dry_run --logfile /workspace/pyqt6_checker.log .
|
||
```
|
||
|
||
Prázdný log = čisté. Kontrola je na plugins.qgis.org informativní
|
||
(neblokuje schválení), ale nález znamená, že plugin v QGIS 4 dříve nebo
|
||
později přestane fungovat.
|
||
|
||
Když je po ruce QGIS 4 (např. flatpak `org.qgis.qgis`), ověř navíc, že se
|
||
plugin pod Qt6 opravdu načte:
|
||
|
||
```sh
|
||
flatpak run --command=sh org.qgis.qgis -c \
|
||
'PYTHONPATH=/app/share/qgis/python python3 -c "import qgis.core"'
|
||
```
|
||
|
||
## Verzování a release
|
||
|
||
- Verze pluginu žije v **`amcr_viewer/metadata.txt`** (`version=`).
|
||
- **Při každé změně chování / nové funkci** povyš verzi a doplň položku do
|
||
`changelog=` v `metadata.txt` (formát `vX.Y.Z (RRRR-MM-DD)` + odrážky).
|
||
- Současně povyš i **`CITATION.cff`** v kořeni repozitáře: `version:` na
|
||
stejnou verzi jako v `metadata.txt` a `date-released:` na datum releasu.
|
||
Oba soubory musí mít stejnou verzi, než se založí tag.
|
||
- Datum v changelogu ber z **deterministického zdroje**, ne z paměti, např.
|
||
`python -c "import datetime; print(datetime.date.today().isoformat())"`.
|
||
- Release se spouští **pushnutím tagu `vX.Y.Z`**, ne publikací releasu
|
||
v UI. Workflow `.github/workflows/release_plugin.yml` zabalí složku
|
||
`amcr_viewer/` do `amcr_viewer.zip` a založí **koncept** releasu i s touto
|
||
přílohou; text se dopíše a release zveřejní ručně. Do ZIPu se nesmí dostat
|
||
`.git*` soubory.
|
||
- Organizace má zapnuté **immutable releases** – k publikovanému releasu už
|
||
nelze nic přiložit. Proto příloha vzniká na konceptu, ještě před
|
||
zveřejněním; workflow spouštěný na `release: published` by vždy selhal.
|
||
- Workflow se čte z commitu, na který **tag ukazuje**. Tag proto zakládej až
|
||
na commitu, který obsahuje aktuální podobu workflow – jinak se nespustí nic.
|
||
|
||
## Pull requesty
|
||
|
||
- Používej PR šablonu (`.github/pull_request_template.md`): Souhrn / Změny /
|
||
Testování / Kontrolní seznam.
|
||
- PR musí mířit do správné `version/v2.x.y` větve.
|
||
- Před požádáním o review projdi kontrolní seznam v šabloně (zejména bump verze
|
||
v `metadata.txt`, pokud měníš chování).
|
||
- Mění-li PR chování, obsahuje i odpovídající změnu v `openspec/changes/`.
|
||
- V popisu PR uveď **podíl AI** (např. „text navržen AI, ručně zkontrolováno")
|
||
a odkaz na související issue, pokud existuje.
|
||
|
||
## Bezpečnost a soukromí
|
||
|
||
- Do promptů, příkladů ani commitů **nevkládej** ostrá produkční data, plné
|
||
log dumpy ani reálné osobní údaje (PII).
|
||
- **Rediguj** secrets, tokeny, API klíče a hesla z čehokoli, co posíláš AI;
|
||
nikdy je necommituj do repozitáře (ani přihlašovací údaje k AMČR účtu).
|
||
- Pro interní infrastrukturu (URL, hostname, prostředí) používej placeholdery,
|
||
pokud konkrétní hodnota není nutná a povolená.
|
||
|
||
## Lokální ověření
|
||
|
||
Plugin se testuje načtením do QGIS (Plugins → Manage and Install Plugins →
|
||
Install from ZIP, nebo nasazením složky `amcr_viewer/` do adresáře pluginů
|
||
QGIS). **Ruční test v QGIS nic nenahrazuje** – automatické kontroly ověřují,
|
||
že se plugin načte a že projde kontrolami kvality, ne že dělá správnou věc.
|
||
|
||
### Automatické kontroly
|
||
|
||
Workflow `.github/workflows/code_quality.yml` pouští při každém PR do `main`
|
||
tohle:
|
||
|
||
| job | co dělá |
|
||
|---|---|
|
||
| **Lint a bezpečnost** | `check_sources.py`, bandit, detect-secrets, flake8, ruff |
|
||
| **Kompatibilita s Qt6** | `pyqgis4-checker` v dockeru |
|
||
| **Smoke test** | `smoke_test.py` v `qgis/qgis:ltr` i `qgis/qgis:stable` |
|
||
| **Balíček pluginu** | ověří shodu verze v `CITATION.cff` a `metadata.txt`, sestaví ZIP, ověří obsah, přiloží jako artefakt |
|
||
|
||
Smoke test běží v obou podporovaných řadách: `ltr` je QGIS 3.44 na Qt5,
|
||
`stable` je QGIS 4.x na Qt6.
|
||
|
||
Artefakt z posledního jobu se dá stáhnout ze stránky běhu a rovnou
|
||
nainstalovat přes *Install from ZIP* – recenzent nemusí nic balit ručně.
|
||
|
||
Totéž lokálně:
|
||
|
||
```sh
|
||
pip install bandit detect-secrets flake8 ruff
|
||
|
||
python3 tests/check_sources.py
|
||
bandit -r amcr_viewer/
|
||
detect-secrets scan --all-files amcr_viewer/
|
||
flake8 --isolated amcr_viewer/
|
||
ruff check .
|
||
|
||
# smoke test v obou verzích QGIS (docker, bez instalace čehokoli)
|
||
for tag in ltr stable; do
|
||
docker run --rm -v "$PWD:/work:ro" -w /work \
|
||
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
||
"qgis/qgis:$tag" python3 tests/smoke_test.py
|
||
done
|
||
```
|
||
|
||
Na co si dát pozor:
|
||
|
||
- **`pyqgis4-checker` končí kódem 0, i když něco najde** – výsledek je jen
|
||
v logu. Workflow proto kontroluje, že log obsahuje jen hlavičku.
|
||
- **`detect-secrets` bez `--all-files` prohledá jen soubory sledované
|
||
gitem** a o nesledovaném souboru mlčí. Vypadá to jako čistý výsledek.
|
||
- **Flake8 běží bez konfigurace** (`--isolated`), tedy se stejnými
|
||
výchozími pravidly jako scanner na plugins.qgis.org. Do balíčku nepatří
|
||
`.flake8`, `.bandit` ani `.secrets.baseline`: scanner by plugin označil
|
||
jako „Validated (configured)“ a nález je lepší opravit v kódu.
|
||
Konfigurace ruffu je v kořenovém `pyproject.toml` – ruff se do balíčku
|
||
pluginu nedistribuuje.
|
||
Viz https://plugins.qgis.org/docs/security-scanning/config-files
|
||
- **Verze nástrojů jsou v workflow napevno.** Výchozí sada pravidel ruffu se
|
||
mezi verzemi mění, takže bez pinu by CI začalo padat samo od sebe.
|
||
|
||
### Denní kontrola API
|
||
|
||
Workflow `.github/workflows/api_monitor.yml` jednou denně (05:17 UTC)
|
||
a na ruční spuštění ověřuje, že API digiarchivu a AMČR OAI pořád vrací
|
||
to, co plugin čte. Změny typu #67 se tak odhalí do druhého dne, ne až
|
||
od uživatelů. Na pull requesty se schválně nespouští – PR nesmí
|
||
zčervenat kvůli výpadku digiarchivu.
|
||
|
||
| job | co dělá |
|
||
|---|---|
|
||
| **API kontrakt** | `tests/api_contract.py` – stejné dotazy jako plugin, kontrola klíčů, typů a tvarů odpovědí; jen `requests` |
|
||
| **Plugin proti živému API** | `tests/api_plugin_live.py` v `qgis/qgis:ltr` – volá přímo `fetch_set` a `load_amcr_data` |
|
||
| **Report** | jedno sledovací issue se štítkem `api-monitor` |
|
||
|
||
Každá kontrola skončí jedním ze stavů:
|
||
|
||
- **OK** – odpověď odpovídá očekávání,
|
||
- **DRIFT** – API se změnilo, ale plugin to ustojí,
|
||
- **FAIL** – změna, na které se plugin rozbije,
|
||
- **UNAVAILABLE** – server nedostupný ani po opakování; výpadek, ne
|
||
změna API. Po prvním neúspěchu se daný server už nezkouší, takže
|
||
běh při výpadku skončí za pár sekund.
|
||
|
||
Běh s FAIL nebo DRIFT na `main` založí issue `api-monitor`, nebo
|
||
doplní komentář do otevřeného, pokud se změnil seznam selhaných
|
||
kontrol. Další čistý běh issue zavře; samotné UNAVAILABLE ho
|
||
nemění. Job, který nevyrobí výsledky, se počítá jako FAIL.
|
||
|
||
Očekávání jsou zapsaná přímo v `tests/api_contract.py`. Jejich změna
|
||
je běžná změna kódu přes PR – automaticky obnovovaný baseline by
|
||
změnu API, kterou chceme vidět, tiše přijal.
|
||
|
||
Lokálně:
|
||
|
||
```sh
|
||
uv run -q --no-project --with requests==2.34.2 \
|
||
python tests/api_contract.py
|
||
|
||
docker run --rm -v "$PWD:/work:ro" -w /work \
|
||
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
||
-e AMCR_RESULTS_DIR=/tmp/results \
|
||
qgis/qgis:ltr python3 tests/api_plugin_live.py
|
||
```
|
||
|
||
Na co si dát pozor:
|
||
|
||
- **`schedule` běží jen na výchozí větvi.** Verzní větev se dá ověřit
|
||
ručně: `gh workflow run api_monitor.yml --ref <větev>`; issue se
|
||
přitom nezakládá ani nezavírá.
|
||
- **GitHub plánovaný workflow vypne po 60 dnech bez aktivity**
|
||
v repozitáři. Stačí jakýkoli push do `main`, i od dependabota.
|
||
- **Testy běží anonymně**, pokrývají tedy jen záznamy s přístupností A.
|
||
Z přihlášení se ověřuje jen chybová cesta.
|
||
- **Simulace výpadku:** `AMCR_DA_URL=http://127.0.0.1:9` a
|
||
`AMCR_OAI_URL=http://127.0.0.1:9/oai` – všechno má skončit
|
||
UNAVAILABLE s kódem 0.
|