Compare commits

..
4 Commits
Author SHA1 Message Date
david-spacilandClaude Sonnet 5 222fe03bb8 ci: kontrola bumpu verze a changelogu při PR z version/* do main
Nová kontrola v release_pr_checks.yml ověří na PR směřujícím do main,
že verze v metadata.txt šla nahoru, první položka changelogu patří
nové verzi, CITATION.cff má stejnou verzi a posunuté date-released,
a že název release větve (version/vX.Y.Z) odpovídá verzi v metadata.txt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jT6Xv6FP2vWgBwLM57sc6
2026-09-02 17:16:54 +02:00
david-spacil 236907b9df metadata update 2026-09-02 17:16:54 +02:00
david-spacil 49fbd91cea feat: přidáno pole s váhou prvku
v případě možnosti stahování komponent bylo přidáno do tabulek s geometrií pole
`Váha prvku`, vypočtené jako 1/*n*, kde *n* představuje počet komponent na jednu
dokumentační jednotku. Změna by měla být užitečná v případě počítání heatmap.
2026-09-02 17:16:54 +02:00
david-spacilandClaude Opus 5 62d9ebd6f0 feat: verze 2.2.0 — sjednocení popisků filtrů (#64)
* fix: sjednocení popisků filtrů ve filtračním dialogu

Dvě nekonzistence, které vyplavalo psaní dokumentace:

- "Lokalita - stav dochování" mělo jako jediné ze čtyř polí lokality
  obyčejný spojovník místo pomlčky.
- Filtr "Specifikace nálezu" plní sloupec, jehož alias je "Materiál",
  takže uživatel filtroval podle jednoho názvu a výsledek dostal pod
  jiným. Sjednoceno na "Materiál". Klíč v cache (specifikace)
  i parametr API (f_specifikace) zůstávají beze změny, mění se jen
  popisek v UI.

Matice filtrů v README je srovnaná ve stejném commitu, aby se
dokumentace a kód nikdy nerozešly.

Ověřeno výpisem group boxů z dialogu v headless QGIS 4.2.2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GptqiE8kzeUUsx6h9apwiP

* chore: verze 2.2.0 a changelog

Odkaz "Plný seznam změn" mířil na tag v2.1.1, přestože changelog
pokračuje dál; přesměrován na přehled releasů. Vratná změna.

CITATION.cff zůstává na v2.1.2 — popisuje poslední vydanou verzi
a povyšuje se až při vydání, kdy z něj Zenodo generuje záznam.

Datum 2026-09-02 je datum přípravy; při vydání ho může být potřeba
posunout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GptqiE8kzeUUsx6h9apwiP

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:16:54 +02:00
15 changed files with 424 additions and 144 deletions

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@ Nepotřebné body můžeš smazat. Komentáře (<!-- ... -->) se v PR nezobrazuj
## Kontrolní seznam ## Kontrolní seznam
- [ ] Změny jsou v souladu se stylem projektu (viz `AGENTS.md`) - [ ] Změny jsou v souladu se stylem projektu (viz `AGENTS.md`)
- [ ] Při změně funkcí povýšena verze v `amcr_viewer/metadata.txt` (+ `changelog`) a v `CITATION.cff` (`version`, `date-released`) - [ ] Při změně funkcí povýšena verze v `amcr_viewer/metadata.txt` a doplněn `changelog`
- [ ] Otestováno v QGIS (min. podporovaná verze 3.44) - [ ] Otestováno v QGIS (min. podporovaná verze 3.44)
- [ ] PR míří do správné cílové větve - [ ] PR míří do správné cílové větve
- [ ] Větev odpovídá konvenci (`feat/ fix/ docs/ chore/<téma>`, AI `agents/<jméno>/<téma>`) - [ ] Větev odpovídá konvenci (`feat/ fix/ docs/ chore/<téma>`, AI `agents/<jméno>/<téma>`)
+7 -20
View File
@@ -74,10 +74,10 @@ jobs:
print('detect-secrets: bez nálezů') print('detect-secrets: bez nálezů')
" "
# Na plugins.qgis.org je informativní, tady blokuje. Bez konfigurace, # Na plugins.qgis.org je informativní, tady blokuje – konfigurace
# tj. se stejnými výchozími pravidly jako scanner. # v amcr_viewer/.flake8 je stejná pro obě místa.
- name: Flake8 - name: Flake8
run: flake8 --isolated amcr_viewer/ run: flake8 --config amcr_viewer/.flake8 amcr_viewer/
# Nad rámec plugins.qgis.org; konfigurace v pyproject.toml # Nad rámec plugins.qgis.org; konfigurace v pyproject.toml
- name: Ruff - name: Ruff
@@ -147,27 +147,14 @@ jobs:
- name: Zip plugin - name: Zip plugin
run: zip -r amcr_viewer.zip amcr_viewer -x "*.git*" run: zip -r amcr_viewer.zip amcr_viewer -x "*.git*"
# Verze v CITATION.cff se povyšuje ručně spolu s metadata.txt a snadno # Kontrola obsahu ZIPu. Config soubory pro scanner musí být uvnitř
# se zapomene – tag by pak nesl v citaci jinou verzi než plugin # vedle metadata.txt, jinak je plugins.qgis.org nenajde – a některé
- name: Verify CITATION.cff version # nástroje skryté soubory tiše vynechávají.
run: |
plugin=$(sed -n 's/^version=//p' amcr_viewer/metadata.txt \
| tr -d "\r\"' ")
citace=$(sed -n 's/^version://p' CITATION.cff \
| tr -d "\r\"' ")
echo "metadata.txt: '$plugin', CITATION.cff: '$citace'"
if [ -z "$plugin" ] || [ "$plugin" != "$citace" ]; then
echo "::error file=CITATION.cff::verze '$citace' neodpovídá" \
"metadata.txt ('$plugin')"
exit 1
fi
# Kontrola obsahu ZIPu: povinné soubory jsou uvnitř, git soubory ne.
- name: Verify archive contents - name: Verify archive contents
run: | run: |
unzip -l amcr_viewer.zip unzip -l amcr_viewer.zip
for soubor in amcr_viewer/metadata.txt amcr_viewer/__init__.py \ for soubor in amcr_viewer/metadata.txt amcr_viewer/__init__.py \
amcr_viewer/LICENSE; do amcr_viewer/.flake8; do
unzip -l amcr_viewer.zip | grep -qF " $soubor" \ unzip -l amcr_viewer.zip | grep -qF " $soubor" \
|| { echo "::error::v ZIPu chybí $soubor"; exit 1; } || { echo "::error::v ZIPu chybí $soubor"; exit 1; }
done done
+41
View File
@@ -0,0 +1,41 @@
name: Release PR Checks
# Hlídá bump verze a changelog v okamžiku, kdy se release větev
# version/vX.Y.Z slévá do main – tady poprvé jde skutečně o to, co se
# vydá, a zapomenutý bump by se jinak dostal rovnou k tagu a releasu.
#
# Kontroly viz tests/check_version_bump.py: verze v metadata.txt musí jít
# nahoru, první položka changelogu musí patřit nové verzi, CITATION.cff
# musí mít stejnou verzi (a posunuté date-released) a název větve musí
# odpovídat verzi, kterou nese.
on:
pull_request:
branches:
- main
permissions:
contents: read
jobs:
version-bump:
name: Kontrola verze a changelogu
runs-on: ubuntu-latest
if: startsWith(github.head_ref, 'version/')
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
- name: Verze a changelog
run: |
python3 tests/check_version_bump.py \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.head_ref }}"
+7 -11
View File
@@ -140,9 +140,6 @@ flatpak run --command=sh org.qgis.qgis -c \
- Verze pluginu žije v **`amcr_viewer/metadata.txt`** (`version=`). - 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 - **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). `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ř. - Datum v changelogu ber z **deterministického zdroje**, ne z paměti, např.
`python -c "import datetime; print(datetime.date.today().isoformat())"`. `python -c "import datetime; print(datetime.date.today().isoformat())"`.
- Release se spouští **pushnutím tagu `vX.Y.Z`**, ne publikací releasu - Release se spouští **pushnutím tagu `vX.Y.Z`**, ne publikací releasu
@@ -192,7 +189,7 @@ tohle:
| **Lint a bezpečnost** | `check_sources.py`, bandit, detect-secrets, flake8, ruff | | **Lint a bezpečnost** | `check_sources.py`, bandit, detect-secrets, flake8, ruff |
| **Kompatibilita s Qt6** | `pyqgis4-checker` v dockeru | | **Kompatibilita s Qt6** | `pyqgis4-checker` v dockeru |
| **Smoke test** | `smoke_test.py` v `qgis/qgis:ltr` i `qgis/qgis:stable` | | **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 | | **Balíček pluginu** | sestaví ZIP, ověří obsah, přiloží jako artefakt |
Smoke test běží v obou podporovaných řadách: `ltr` je QGIS 3.44 na Qt5, Smoke test běží v obou podporovaných řadách: `ltr` je QGIS 3.44 na Qt5,
`stable` je QGIS 4.x na Qt6. `stable` je QGIS 4.x na Qt6.
@@ -208,7 +205,7 @@ pip install bandit detect-secrets flake8 ruff
python3 tests/check_sources.py python3 tests/check_sources.py
bandit -r amcr_viewer/ bandit -r amcr_viewer/
detect-secrets scan --all-files amcr_viewer/ detect-secrets scan --all-files amcr_viewer/
flake8 --isolated amcr_viewer/ flake8 --config amcr_viewer/.flake8 amcr_viewer/
ruff check . ruff check .
# smoke test v obou verzích QGIS (docker, bez instalace čehokoli) # smoke test v obou verzích QGIS (docker, bez instalace čehokoli)
@@ -225,12 +222,11 @@ Na co si dát pozor:
v logu. Workflow proto kontroluje, že log obsahuje jen hlavičku. v logu. Workflow proto kontroluje, že log obsahuje jen hlavičku.
- **`detect-secrets` bez `--all-files` prohledá jen soubory sledované - **`detect-secrets` bez `--all-files` prohledá jen soubory sledované
gitem** a o nesledovaném souboru mlčí. Vypadá to jako čistý výsledek. gitem** a o nesledovaném souboru mlčí. Vypadá to jako čistý výsledek.
- **Flake8 běží bez konfigurace** (`--isolated`), tedy se stejnými - **Konfigurace lintů je rozdělená schválně.** `amcr_viewer/.flake8` leží
výchozími pravidly jako scanner na plugins.qgis.org. Do balíčku nepatří vedle `metadata.txt`, protože scanner na plugins.qgis.org hledá config
`.flake8`, `.bandit` ani `.secrets.baseline`: scanner by plugin označil soubory jen v kořeni balíčku uvnitř ZIPu; díky tomu platí stejná pravidla
jako „Validated (configured)“ a nález je lepší opravit v kódu. v CI, lokálně i při uploadu. Konfigurace ruffu je naopak v kořenovém
Konfigurace ruffu je v kořenovém `pyproject.toml` – ruff se do balíčku `pyproject.toml` – ruff se do balíčku pluginu nedistribuuje.
pluginu nedistribuuje.
Viz https://plugins.qgis.org/docs/security-scanning/config-files Viz https://plugins.qgis.org/docs/security-scanning/config-files
- **Verze nástrojů jsou v workflow napevno.** Výchozí sada pravidel ruffu se - **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. mezi verzemi mění, takže bez pinu by CI začalo padat samo od sebe.
+2 -2
View File
@@ -25,5 +25,5 @@ abstract: >-
the Digital archive of the Archaeological Map of the the Digital archive of the Archaeological Map of the
Czech Republic (https://digiarchiv.aiscr.cz/). Czech Republic (https://digiarchiv.aiscr.cz/).
license: GPL-3.0 license: GPL-3.0
version: '2.1.4' version: '2.1.2'
date-released: '2026-10-01' date-released: '2026-09-01'
+5 -3
View File
@@ -138,11 +138,11 @@ restriction". Click *Vybrat…* to open a searchable, checkable list.
| Lokalita – typ | — | ✓ | — | `f_typ_lokality` | | Lokalita – typ | — | ✓ | — | `f_typ_lokality` |
| Lokalita – druh | — | ✓ | — | `f_druh_lokality` | | Lokalita – druh | — | ✓ | — | `f_druh_lokality` |
| Lokalita – jistota určení | — | ✓ | — | `f_jistota` | | Lokalita – jistota určení | — | ✓ | — | `f_jistota` |
| Lokalita - stav dochování | — | ✓ | — | `f_lokalita_zachovalost` | | Lokalita – stav dochování | — | ✓ | — | `f_lokalita_zachovalost` |
| Období | ✓ | ✓ | ✓ | `f_obdobi` | | Období | ✓ | ✓ | ✓ | `f_obdobi` |
| Kategorie nálezu | — | — | ✓ | `f_kategorie` | | Kategorie nálezu | — | — | ✓ | `f_kategorie` |
| Druh nálezu | — | — | ✓ | `f_druh_nalezu` | | Druh nálezu | — | — | ✓ | `f_druh_nalezu` |
| Specifikace nálezu | — | — | ✓ | `f_specifikace` | | Materiál | — | — | ✓ | `f_specifikace` |
| Okolnosti nálezu | — | — | ✓ | `f_nalezove_okolnosti` | | Okolnosti nálezu | — | — | ✓ | `f_nalezove_okolnosti` |
| Nálezce | — | — | ✓ | `f_nalezce` | | Nálezce | — | — | ✓ | `f_nalezce` |
| Datum nálezu | — | — | ✓ | `samostatny_nalez_datum_nalezu` | | Datum nálezu | — | — | ✓ | `samostatny_nalez_datum_nalezu` |
@@ -333,7 +333,9 @@ amcr_viewer/ the plugin package (this is what gets zipped)
codelists/heslar.csv cached controlled vocabularies codelists/heslar.csv cached controlled vocabularies
i18n/ Qt translation files i18n/ Qt translation files
*.png toolbar and menu icons *.png toolbar and menu icons
resources.py generated by pyrcc, currently unused
metadata.txt plugin metadata and changelog metadata.txt plugin metadata and changelog
.flake8 lint config, read by the plugins.qgis.org scanner
tests/ tests/
check_sources.py source hygiene checks (no QGIS needed) check_sources.py source hygiene checks (no QGIS needed)
smoke_test.py loads the plugin in a real, headless QGIS smoke_test.py loads the plugin in a real, headless QGIS
@@ -402,7 +404,7 @@ Reproducing them locally:
```bash ```bash
python3 tests/check_sources.py python3 tests/check_sources.py
ruff check . ruff check .
flake8 --isolated amcr_viewer/ flake8 --config amcr_viewer/.flake8 amcr_viewer/
bandit -r amcr_viewer/ bandit -r amcr_viewer/
docker run --rm -v "$PWD:/work:ro" -w /work --user "$(id -u):$(id -g)" \ docker run --rm -v "$PWD:/work:ro" -w /work --user "$(id -u):$(id -g)" \
-e HOME=/tmp qgis/qgis:stable python3 tests/smoke_test.py -e HOME=/tmp qgis/qgis:stable python3 tests/smoke_test.py
+12
View File
@@ -0,0 +1,12 @@
# Konfigurace flake8 pro plugin AMČR Viewer.
#
# Soubor leží vedle metadata.txt schválně: scanner na plugins.qgis.org
# hledá .flake8 pouze v kořeni balíčku uvnitř ZIPu, takže stejná pravidla
# platí v CI, lokálně i při uploadu.
# https://plugins.qgis.org/docs/security-scanning/config-files
[flake8]
# resources.py je vygenerovaný výstup pyrcc ("All changes made in this
# file will be lost"), není nikde importovaný a zdrojový .qrc v repu není.
# Ručně se neformátuje.
per-file-ignores =
*resources.py: E302,E305,E501
+5 -66
View File
@@ -104,21 +104,6 @@ def load_all_data():
return categorized_data return categorized_data
def _facet_name(item):
"""
Returns the value of one facet item from the Digiarchive API.
Digiarchive v4.1.0 (Solr 10, json.nl=arrarr) returns facet items as
["value", count] pairs; older versions returned {"name": "value", ...}
objects. Both shapes are accepted so the plugin works against either.
"""
if isinstance(item, dict):
return item.get("name")
if isinstance(item, (list, tuple)) and item:
return item[0]
return None
def fetch_set(base_url, internal_name, api_set, task=None): def fetch_set(base_url, internal_name, api_set, task=None):
dataset = [] dataset = []
params_amcr = { params_amcr = {
@@ -221,9 +206,7 @@ def fetch_set(base_url, internal_name, api_set, task=None):
for r in records: for r in records:
nazev = _facet_name(r) nazev = r["name"]
if not nazev:
continue
dataset.append({ dataset.append({
'Název': nazev, 'Název': nazev,
@@ -234,48 +217,17 @@ def fetch_set(base_url, internal_name, api_set, task=None):
break break
except Exception as e: except Exception as e:
# A partial set (e.g. pagination interrupted halfway) would
# silently drop codes – report the whole set as failed instead
# and let the caller keep the previous values
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
f"Chyba u setu {api_set}: {e}", f"Chyba u setu {api_set}: {e}",
"AMČR", Qgis.MessageLevel.Warning) "AMČR", Qgis.MessageLevel.Warning)
return [] break
return dataset return dataset
def _read_existing_rows(): def download_heslare(task=None):
""" """Fetches the codelists from the AMČR API and saves it to a CSV file."""
Returns the rows of the current heslar.csv grouped by category, so a set
that fails to download can keep its previous values.
"""
rows = {}
if not os.path.exists(OUTPUT_FILE):
return rows
try:
with open(OUTPUT_FILE, encoding='utf-8-sig', newline='') as f:
for row in csv.DictReader(f, delimiter=';'):
cat = (row.get('Kategorie') or '').strip()
if cat:
rows.setdefault(cat, []).append(row)
except Exception as e:
QgsMessageLog.logMessage(
f"Nelze načíst stávající hesláře: {e}",
"AMČR", Qgis.MessageLevel.Warning)
return rows
def download_heslare(task=None, failed=None):
"""
Fetches the codelists from the AMČR API and saves it to a CSV file.
A set that fails or comes back empty keeps its rows from the current
heslar.csv instead of being wiped; its name is appended to ``failed``
(if given) so the caller can warn the user.
"""
ensure_codelists_dir() ensure_codelists_dir()
existing = _read_existing_rows()
all_data = [] all_data = []
total_sets = len(slovnicek) total_sets = len(slovnicek)
# index, (interni, api_nazev) # index, (interni, api_nazev)
@@ -299,18 +251,6 @@ def download_heslare(task=None, failed=None):
if data is None: if data is None:
return False # Cancelled mid-download return False # Cancelled mid-download
if not data:
# Never replace a working codelist with nothing – an API change
# would otherwise silently empty the filter in the dialog
old = existing.get(interni, [])
QgsMessageLog.logMessage(
f"Heslář '{interni}' se nepodařilo stáhnout, "
f"ponechávám předchozí hodnoty ({len(old)} položek).",
"AMČR", Qgis.MessageLevel.Warning)
if failed is not None:
failed.append(interni)
data = old
all_data.extend(data) all_data.extend(data)
# Report progress (0-100) # Report progress (0-100)
@@ -321,8 +261,7 @@ def download_heslare(task=None, failed=None):
# Save to CSV # Save to CSV
with open(OUTPUT_FILE, 'w', newline='', encoding='utf-8-sig') as f: with open(OUTPUT_FILE, 'w', newline='', encoding='utf-8-sig') as f:
fieldnames = ['Název', 'Kód', 'Kategorie'] fieldnames = ['Název', 'Kód', 'Kategorie']
writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=';', writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=';')
extrasaction='ignore')
writer.writeheader() writer.writeheader()
writer.writerows(all_data) writer.writerows(all_data)
+7 -27
View File
@@ -75,15 +75,12 @@ class UpdateCodelistsTask(QgsTask):
super().__init__(description, QgsTask.Flag.CanCancel) super().__init__(description, QgsTask.Flag.CanCancel)
self.success = False self.success = False
self.exception = None self.exception = None
# Codelists that failed to download and kept their previous values
self.failed_sets = []
def run(self): def run(self):
"""Runs in a background thread.""" """Runs in a background thread."""
try: try:
# Call the download function with the task reference # Call the download function with the task reference
self.success = download_heslare( self.success = download_heslare(task=self)
task=self, failed=self.failed_sets)
return self.success return self.success
except Exception as e: except Exception as e:
self.exception = e self.exception = e
@@ -94,17 +91,10 @@ class UpdateCodelistsTask(QgsTask):
if result: if result:
# Safely update the global variables in the main thread # Safely update the global variables in the main thread
refresh_globals() refresh_globals()
if self.failed_sets: QgsMessageLog.logMessage(
QgsMessageLog.logMessage( "Hesláře AMČR byly úspěšně aktualizovány.",
"Hesláře AMČR aktualizovány částečně, beze změny " "AMČR", Qgis.MessageLevel.Info
f"zůstaly: {', '.join(self.failed_sets)}", )
"AMČR", Qgis.MessageLevel.Warning
)
else:
QgsMessageLog.logMessage(
"Hesláře AMČR byly úspěšně aktualizovány.",
"AMČR", Qgis.MessageLevel.Info
)
else: else:
if self.isCanceled(): if self.isCanceled():
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
@@ -343,7 +333,7 @@ class AmcrFilterDialog(QDialog):
layout.addWidget(self.picker_jistota) layout.addWidget(self.picker_jistota)
self.picker_lokalita_zachovalost = self.setup_picker( self.picker_lokalita_zachovalost = self.setup_picker(
"Lokalita - stav dochování", "Lokalita – stav dochování",
'lokalita_zachovalost', 'lokalita_zachovalost',
LOKALITA_ZACHOVALOST LOKALITA_ZACHOVALOST
) )
@@ -370,7 +360,7 @@ class AmcrFilterDialog(QDialog):
layout.addWidget(self.picker_druh_nalezu) layout.addWidget(self.picker_druh_nalezu)
self.picker_specifikace = self.setup_picker( self.picker_specifikace = self.setup_picker(
"Specifikace nálezu", "Materiál",
'specifikace', 'specifikace',
SPECIFIKACE SPECIFIKACE
) )
@@ -639,16 +629,6 @@ class AmcrFilterDialog(QDialog):
def on_completed(): def on_completed():
_cleanup() _cleanup()
if task.failed_sets:
QMessageBox.warning(
parent_win,
"Hesláře aktualizovány částečně",
"Některé hesláře se nepodařilo stáhnout, "
"ponechány byly jejich předchozí hodnoty:\n"
+ "\n".join(f"• {name}" for name in task.failed_sets)
+ "\n\nPodrobnosti jsou v panelu Zprávy, záložka AMČR."
)
return
QMessageBox.information( QMessageBox.information(
parent_win, parent_win,
"Hotovo", "Hotovo",
+7 -1
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import json import json
import requests import requests
@@ -680,6 +680,8 @@ def load_amcr_data(canvas, bb, filters=None,
# One feature per component – # One feature per component –
# all data on a single row, no relations needed # all data on a single row, no relations needed
if komps: if komps:
komps_count = len(komps)
for komp in komps: for komp in komps:
if not komp_projde_filtrem( if not komp_projde_filtrem(
komp, filter_areal, komp, filter_areal,
@@ -701,6 +703,7 @@ def load_amcr_data(canvas, bb, filters=None,
komp.get('komponenta_obdobi') komp.get('komponenta_obdobi')
or {} or {}
).get('value', ""), ).get('value', ""),
'vaha': 1/komps_count,
} }
pian_lookup[dj_pian_value].append( pian_lookup[dj_pian_value].append(
komp_meta) komp_meta)
@@ -1056,6 +1059,7 @@ def load_amcr_data(canvas, bb, filters=None,
"poznamka": "Poznámka/bližší popis", "poznamka": "Poznámka/bližší popis",
"pred_org": "Předáno organizaci", "pred_org": "Předáno organizaci",
"evidencni": "Evidenční číslo", "evidencni": "Evidenční číslo",
"prvek_vaha": "Váha prvku",
} }
if komponenty == "true": if komponenty == "true":
@@ -1063,6 +1067,7 @@ def load_amcr_data(canvas, bb, filters=None,
QgsField("komponenta", QMetaType.Type.QString), QgsField("komponenta", QMetaType.Type.QString),
QgsField("komponenta_areal", QMetaType.Type.QString), QgsField("komponenta_areal", QMetaType.Type.QString),
QgsField("komponenta_obdobi", QMetaType.Type.QString), QgsField("komponenta_obdobi", QMetaType.Type.QString),
QgsField("prvek_vaha", QMetaType.Type.Double),
] ]
for vl in layers: for vl in layers:
@@ -1209,6 +1214,7 @@ def load_amcr_data(canvas, bb, filters=None,
meta.get('komponenta_id', ""), meta.get('komponenta_id', ""),
meta.get('komponenta_areal', ""), meta.get('komponenta_areal', ""),
meta.get('komponenta_obdobi', ""), meta.get('komponenta_obdobi', ""),
meta.get('vaha', 1),
]) ])
feat.setAttributes(atributy) feat.setAttributes(atributy)
+6 -7
View File
@@ -8,7 +8,7 @@ name=AMČR Viewer
qgisMinimumVersion=3.44.0 qgisMinimumVersion=3.44.0
qgisMaximumVersion=4.99.0 qgisMaximumVersion=4.99.0
description=Viewing and downloading the AMČR data. description=Viewing and downloading the AMČR data.
version=2.1.4 version=2.2.0
author=David Spáčil author=David Spáčil
email=spacil@arub.cz email=spacil@arub.cz
@@ -23,12 +23,11 @@ repository=https://github.com/ARUP-CAS/aiscr-qgis-amcr-viewer
hasProcessingProvider=no hasProcessingProvider=no
# Uncomment the following line and add your changelog: # Uncomment the following line and add your changelog:
changelog= changelog=
Plný seznam změn v češtině je dostupný zde: https://github.com/ARUP-CAS/aiscr-qgis-amcr-viewer/releases/tag/v2.1.1 Plný seznam změn v češtině je dostupný zde: https://github.com/ARUP-CAS/aiscr-qgis-amcr-viewer/releases/tag/v2.2.0
v2.1.4 (2026-10-01) v2.2.0 (2026-09-02)
* Removed unused generated resources.py and the bundled flake8 config, so the plugin passes the plugins.qgis.org scan without custom configuration * Filter labels unified: "Specifikace nálezu" renamed to "Materiál" to match the attribute alias
v2.1.3 (2026-10-01) * README rewritten to match the current state of the code
* Fixed empty person codelists (excavation leaders, finders) after updating codelists against Digiarchive v4.1.0 * Tables for Akce and Lokality contain a field with feature weight, when Komponenty rendering is enabled
* A codelist that fails to download keeps its previous values and the user is warned
v2.1.2 (2026-09-01) v2.1.2 (2026-09-01)
* Qt6 compatibility * Qt6 compatibility
* Code clean-up * Code clean-up
+128
View File
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
# Resource object code
#
# Created by: The Resource Compiler for PyQt5 (Qt v5.15.13)
#
# WARNING! All changes made in this file will be lost!
from qgis.PyQt import QtCore
qt_resource_data = b"\
\x00\x00\x04\x0a\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x17\x00\x00\x00\x18\x08\x06\x00\x00\x00\x11\x7c\x66\x75\
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd9\x02\x15\
\x16\x11\x2c\x9d\x48\x83\xbb\x00\x00\x03\x8a\x49\x44\x41\x54\x48\
\xc7\xad\x95\x4b\x68\x5c\x55\x18\xc7\x7f\xe7\xdc\x7b\x67\xe6\xce\
\x4c\x66\x26\x49\xd3\x24\x26\xa6\xc6\xf8\x40\x21\xa5\x04\xb3\x28\
\xda\x98\x20\xa5\x0b\xad\x55\xa8\x2b\xc5\x50\x1f\xa0\x6e\x34\x2b\
\x45\x30\x14\x02\xba\x52\x69\x15\x17\x66\x63\x45\x97\x95\xa0\xad\
\x0b\xfb\xc0\x06\x25\xb6\x71\x61\x12\x41\x50\xdb\x2a\x21\xd1\xe2\
\x24\xf3\x9e\xc9\xcc\xbd\xe7\x1c\x17\x35\x43\x1e\x33\x21\xb6\xfd\
\x56\x87\xf3\x9d\xfb\xfb\x1e\xf7\xff\x9d\x23\x8c\x31\x43\x95\xf4\
\x85\x1e\x3f\x3b\x35\xac\xfd\xcc\x43\xdc\xa4\x49\x3b\xfe\x9d\x1d\
\xdb\x7b\x22\x90\x78\xf8\xb2\x28\xa7\xbe\x7d\xc1\x4b\x9d\x79\xdf\
\x18\x15\xe5\x16\x99\x10\x56\xde\x69\xdc\x3f\x22\xfd\xec\xd4\xf0\
\xad\x04\x03\x18\xa3\xa2\x7e\x76\x6a\x58\xde\x68\x2b\xb4\x36\xf8\
\xbe\xc6\x18\x53\xdb\xef\xe7\xfa\xec\xed\x67\x63\x10\x42\x00\xf0\
\xfb\xd5\x65\x2a\x15\x45\xc7\x6d\x0d\x00\xc4\xa2\xc1\xaa\x6f\x0d\
\x3e\x6c\xab\xc2\x1c\x56\xa4\x77\x4b\xb0\xf2\x35\x15\x5f\x21\x85\
\xe0\xc8\x6b\x5f\x92\x2d\x37\x33\x39\xf9\x03\x27\x8e\x1f\xa2\xf7\
\xbe\x9d\x04\x1c\x0b\x37\xe4\xac\xff\xa6\x30\x87\xbd\xba\x00\x6a\
\x06\x79\xe5\xf5\xaf\x89\xd9\x92\xc5\xcc\x0a\xd9\x7c\x19\xcf\xe9\
\xe2\xe4\xa9\x2f\x78\x7c\xff\x01\x72\x85\x0a\x2b\x65\x1f\xa5\x4c\
\xb5\xb2\x55\x16\x80\xbd\x31\xda\xda\x20\x1f\x7d\x3e\xcd\xc2\xfd\
\x59\xa6\x93\x39\x92\xd1\x22\xea\x9b\x16\xce\x9d\x3f\xce\xe0\x83\
\x03\x24\x82\x59\x3a\xdb\x7b\x88\xc7\x82\x68\x63\x58\xc9\xcc\x62\
\x8c\x21\x18\xb0\x6a\xc3\x37\x06\x49\x16\xff\x24\x6b\xa5\x49\xbb\
\x25\xbc\xa2\xa6\x21\xbb\x40\x7f\xdf\x00\x83\xbd\x01\x8e\x3c\xd5\
\x45\xd7\x8e\x6b\x9c\x9c\x98\x25\x1a\xb6\xe8\xbe\x3d\xc2\xdd\x77\
\x44\x48\xc4\x1c\x22\xe1\xeb\x58\x59\xaf\xcf\xd3\x33\x29\x2e\x34\
\x2d\x91\x93\x3e\xbe\x34\x78\x01\xc5\xe2\x61\xc5\xae\x72\x8e\x70\
\xc8\xc2\x0d\x5a\xbc\xf5\xee\x2f\x9c\xfa\x3e\x86\x69\x7a\x8e\xcf\
\x26\xe6\xf9\x63\xa1\x44\xa1\xa4\xd0\xda\x6c\x0d\x2f\x15\x7c\xb4\
\x67\x28\x59\x0a\xcf\xd6\x54\xe2\x06\x13\x87\x2b\x6f\x68\xa6\x27\
\xaf\x31\x32\x36\xc7\xb2\x7f\x17\xef\x7d\x7c\x8c\x33\x67\xcf\x12\
\x70\x24\x4a\x69\xd6\x6a\x46\xd6\xd3\x70\x72\xa9\x82\x67\x34\x45\
\xad\x28\xdb\x1a\x15\x34\x98\xff\x46\xed\xef\x37\x0d\x99\xbf\x4a\
\x3c\x30\x38\xc0\xc8\x4b\xaf\x92\x5a\x9c\xe2\xe0\x23\x6d\x74\xb4\
\xba\x84\x5d\x0b\x29\x45\x7d\xb8\x94\x82\x96\xb6\x10\xf3\xc5\x12\
\x2a\xef\x53\x11\x1a\x63\xad\x3f\x93\x19\x85\xf1\xb1\x77\x58\x5a\
\xf8\x99\x97\x9f\xe9\xa6\x75\x47\x90\xc6\xb8\x43\xd8\xb5\xb6\xce\
\xfc\xfa\xfd\x00\xfb\x3e\xf4\xc8\x05\x35\xba\x5e\xeb\x46\x21\xf9\
\xcf\x0a\xa9\x8c\x87\xe3\x48\xdc\x90\xb5\x6e\x98\x6a\xaa\x65\xf2\
\x52\x92\x43\x2f\x5e\xc2\x8c\x02\x1a\x10\xf5\x07\xac\xc3\x75\x70\
\x83\x92\x80\xb3\xf9\xd0\x26\xf8\x8f\xb3\x29\xc6\x3e\xb8\x8c\x19\
\x35\x75\x6b\x7b\x7e\x3c\xca\x45\x0c\x7e\x49\x31\xf4\x58\x3b\xf7\
\xf6\x34\x90\x88\x39\x04\x1c\x59\x1f\xfe\xdb\xd5\x3c\x5f\x9d\x4b\
\x32\xfd\x44\xb2\xba\xd7\xfa\xb6\x60\xcf\xde\x16\xdc\x90\x45\x4c\
\x4a\x2a\x9e\x62\xfe\x4e\xc5\xc8\xc1\x4e\xda\x76\x86\xe8\xe9\x0a\
\xe3\xd8\x92\x58\xd4\xc6\xb2\x44\x6d\x78\x2a\x53\xe1\xca\x7c\x99\
\x63\x5d\xbf\x56\x9d\xbd\x9f\x44\x18\x7a\xba\x95\x27\x0f\xb4\xd3\
\xdc\x18\xc0\xf3\x0d\x52\x40\xd8\xb5\xb0\xa4\x20\x14\xb2\x70\x6c\
\x81\x63\xcb\xaa\x42\xd6\xfd\xb7\xf4\xec\xa3\x06\xa0\x50\x52\xd8\
\x4e\x1b\x7e\x4a\xd3\x31\xf9\x29\xcf\xfe\xd4\x49\x7f\x5f\x13\xfb\
\xfa\x9b\x71\x43\x92\x58\xd4\x21\x18\x90\xac\xde\xb0\x42\x50\x13\
\x58\x33\xf3\x88\x6b\xa1\xfd\x65\x96\xf2\x79\xc6\x43\x7b\xd8\x75\
\x38\xcc\x3d\xdd\xd1\xaa\xcf\x71\xe4\xff\x7f\x91\x56\x33\xaf\xea\
\x37\xe7\xa1\x94\x21\x16\xb5\xd1\x06\x2c\x29\x36\xf5\x72\x9b\x96\
\x95\xc0\xc4\xda\x9d\x78\x83\x43\x53\x22\x80\x65\x09\x1c\xfb\x86\
\xc1\x00\xe7\x25\x70\x14\x48\x6f\x1e\x22\x51\xe3\x75\xd9\xb6\xa5\
\x81\xa3\x32\xb1\xfb\xf4\x0c\x30\xb8\xb1\x82\x9b\xb0\x09\x60\x30\
\xb1\xfb\xf4\xcc\xbf\xa0\xe9\x6e\xae\x5a\xdf\x4b\x81\x00\x00\x00\
\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
"
qt_resource_name = b"\
\x00\x07\
\x07\x3b\xe0\xb3\
\x00\x70\
\x00\x6c\x00\x75\x00\x67\x00\x69\x00\x6e\x00\x73\
\x00\x0b\
\x06\x1f\xb8\xc2\
\x00\x61\
\x00\x6d\x00\x63\x00\x72\x00\x5f\x00\x76\x00\x69\x00\x65\x00\x77\x00\x65\x00\x72\
\x00\x08\
\x0a\x61\x5a\xa7\
\x00\x69\
\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
"
qt_resource_struct_v1 = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
"
qt_resource_struct_v2 = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x9c\x23\xfd\x16\x70\
"
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
if qt_version < [5, 8, 0]:
rcc_version = 1
qt_resource_struct = qt_resource_struct_v1
else:
rcc_version = 2
qt_resource_struct = qt_resource_struct_v2
def qInitResources():
QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()
+4 -3
View File
@@ -5,14 +5,15 @@
# jen k tomu, aby ruff choval stejně v CI, lokálně i za rok. Bez explicitní # jen k tomu, aby ruff choval stejně v CI, lokálně i za rok. Bez explicitní
# konfigurace se výchozí sada pravidel mezi verzemi ruffu mění. # konfigurace se výchozí sada pravidel mezi verzemi ruffu mění.
# #
# Flake8 záměrně žádnou konfiguraci nemá a běží s výchozími pravidly – stejně # Konfigurace flake8 je záměrně jinde: v amcr_viewer/.flake8, protože ji
# jako scanner na plugins.qgis.org. Config soubor v balíčku by plugin # musí najít i scanner na plugins.qgis.org.
# označil jako „Validated (configured)“.
[tool.ruff] [tool.ruff]
line-length = 79 line-length = 79
# QGIS 3.44 běží na Pythonu 3.9 a novějším # QGIS 3.44 běží na Pythonu 3.9 a novějším
target-version = "py39" target-version = "py39"
# Generovaný výstup pyrcc, "All changes made in this file will be lost"
extend-exclude = ["amcr_viewer/resources.py"]
[tool.ruff.lint] [tool.ruff.lint]
select = [ select = [
+5 -3
View File
@@ -23,6 +23,10 @@ import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BALICEK = os.path.join(ROOT, "amcr_viewer") BALICEK = os.path.join(ROOT, "amcr_viewer")
# Files that belong in the plugin package even though the upload scanner
# would otherwise call them hidden
POVOLENE_SKRYTE = {".flake8", ".bandit", ".secrets.baseline"}
# Extensions that have no business inside a plugin package # Extensions that have no business inside a plugin package
PODEZRELE = {".exe", ".dll", ".so", ".dylib", ".sh", ".bat", ".cmd", PODEZRELE = {".exe", ".dll", ".so", ".dylib", ".sh", ".bat", ".cmd",
".pyc", ".pyd", ".jar", ".bin"} ".pyc", ".pyd", ".jar", ".bin"}
@@ -68,9 +72,7 @@ for cesta in vsechny_soubory():
if rezim & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH): if rezim & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
nalezy.append(f"{zkratka(cesta)}: spustitelná práva " nalezy.append(f"{zkratka(cesta)}: spustitelná práva "
f"({stat.filemode(rezim)})") f"({stat.filemode(rezim)})")
# No exceptions: scanner config files (.flake8, .bandit, if jmeno.startswith(".") and jmeno not in POVOLENE_SKRYTE:
# .secrets.baseline) would mark the upload "Validated (configured)"
if jmeno.startswith("."):
nalezy.append(f"{zkratka(cesta)}: skrytý soubor v balíčku pluginu") nalezy.append(f"{zkratka(cesta)}: skrytý soubor v balíčku pluginu")
if os.path.splitext(jmeno)[1].lower() in PODEZRELE: if os.path.splitext(jmeno)[1].lower() in PODEZRELE:
nalezy.append(f"{zkratka(cesta)}: podezřelý typ souboru") nalezy.append(f"{zkratka(cesta)}: podezřelý typ souboru")
+187
View File
@@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
"""
Guards the release PR (version/vX.Y.Z -> main).
Checks that the version actually moved forward and that the three places
that name it agree, because plugins.qgis.org and Zenodo both read from
files a human has to remember to touch by hand:
* amcr_viewer/metadata.txt: version= must be higher than on main, and the
first changelog entry must be for that exact version
* CITATION.cff: version must match metadata.txt, and date-released must
not be left pointing at the old release
* the branch name (version/vX.Y.Z) must match metadata.txt, so a stray
push to the wrong release branch is caught before merge
Run from the repository root:
python3 tests/check_version_bump.py <base_sha> <head_ref>
"""
import datetime
import re
import subprocess
import sys
METADATA = "amcr_viewer/metadata.txt"
CITATION = "CITATION.cff"
VERZE_METADATA = re.compile(r"^version=(.+)$", re.MULTILINE)
VERZE_CITATION = re.compile(r"^version:\s*'?([^'\n]+)'?\s*$", re.MULTILINE)
DATUM_CITATION = re.compile(r"^date-released:\s*'?([^'\n]+)'?\s*$",
re.MULTILINE)
# Přeskočí úvodní řádek "Plný seznam změn ... /vX.Y.Z" a najde první
# skutečnou položku changelogu.
POLOZKA_CHANGELOGU = re.compile(r"^\s+v([0-9][^\s(]*)\s*\(", re.MULTILINE)
BRANCH_VERZE = re.compile(r"^version/v(.+)$")
nalezy = []
def nacti_na_base(base_sha, cesta):
vysledek = subprocess.run(
["git", "show", f"{base_sha}:{cesta}"],
capture_output=True, text=True,
)
if vysledek.returncode != 0:
nalezy.append(f"{cesta}: na cílové větvi nejde přečíst "
f"(git show {base_sha}:{cesta} selhalo)")
return None
return vysledek.stdout
def nacti(cesta):
with open(cesta, encoding="utf-8") as f:
return f.read()
def hledej(vzor, text, cesta, popis):
shoda = vzor.search(text)
if not shoda:
nalezy.append(f"{cesta}: {popis} nenalezeno")
return None
return shoda.group(1).strip()
def verze_tuple(verze):
jadro = verze.split("-", 1)[0]
try:
return tuple(int(c) for c in jadro.split("."))
except ValueError:
return None
def je_bump(stara, nova):
if stara == nova:
return False
stara_t, nova_t = verze_tuple(stara), verze_tuple(nova)
if stara_t is None or nova_t is None:
# Nestandardní formát verze – nejde spolehlivě porovnat čísly,
# stačí tedy, že se řetězec liší.
return True
if nova_t != stara_t:
return nova_t > stara_t
# Stejné jádro (např. "-alpha" přípona): bump platí, pokud se text
# liší a nejde o couvnutí z release na prerelease.
return "-" in stara or "-" not in nova
def datum(text):
try:
return datetime.date.fromisoformat(text)
except ValueError:
return None
def main():
if len(sys.argv) != 3:
print("použití: check_version_bump.py <base_sha> <head_ref>")
return 2
base_sha, head_ref = sys.argv[1], sys.argv[2]
base_metadata = nacti_na_base(base_sha, METADATA)
base_citation = nacti_na_base(base_sha, CITATION)
head_metadata = nacti(METADATA)
head_citation = nacti(CITATION)
if base_metadata is None or base_citation is None:
# Bez base souborů nejde nic dalšího smysluplně ověřit.
print("Nálezy:")
for nalez in nalezy:
print(f" {nalez}")
return 1
stara_verze = hledej(VERZE_METADATA, base_metadata, METADATA,
"verze na cílové větvi (version=)")
nova_verze = hledej(VERZE_METADATA, head_metadata, METADATA,
"verze (version=)")
stara_citation_verze = hledej(VERZE_CITATION, base_citation, CITATION,
"verze na cílové větvi (version:)")
nova_citation_verze = hledej(VERZE_CITATION, head_citation, CITATION,
"verze (version:)")
stare_datum = hledej(DATUM_CITATION, base_citation, CITATION,
"date-released na cílové větvi")
nove_datum = hledej(DATUM_CITATION, head_citation, CITATION,
"date-released")
if None in (stara_verze, nova_verze, stara_citation_verze,
nova_citation_verze, stare_datum, nove_datum):
print("Nálezy:")
for nalez in nalezy:
print(f" {nalez}")
return 1
# 1. metadata.txt: verze musí jít dopředu.
if not je_bump(stara_verze, nova_verze):
nalezy.append(
f"{METADATA}: verze nepovýšena ({stara_verze} -> {nova_verze})")
# 2. metadata.txt: první položka changelogu musí patřit nové verzi.
prvni_polozka = POLOZKA_CHANGELOGU.search(head_metadata)
if not prvni_polozka:
nalezy.append(f"{METADATA}: v changelog= nenalezena žádná položka "
f"'vX.Y.Z (...)'")
elif prvni_polozka.group(1) != nova_verze:
nalezy.append(
f"{METADATA}: první položka changelogu je pro "
f"v{prvni_polozka.group(1)}, ale version={nova_verze}")
# 3. CITATION.cff: verze musí souhlasit s metadata.txt.
if nova_citation_verze != nova_verze:
nalezy.append(
f"{CITATION}: version: {nova_citation_verze} neodpovídá "
f"{METADATA} version={nova_verze}")
# 4. CITATION.cff: date-released se musí posunout, a ne dozadu.
stary_datum_obj, novy_datum_obj = datum(stare_datum), datum(nove_datum)
if stary_datum_obj is None or novy_datum_obj is None:
nalezy.append(f"{CITATION}: date-released není platné datum "
f"ISO 8601 ({stare_datum!r} -> {nove_datum!r})")
elif novy_datum_obj < stary_datum_obj:
nalezy.append(
f"{CITATION}: date-released couvlo ({stare_datum} -> "
f"{nove_datum})")
elif nove_datum == stare_datum and stara_citation_verze != nova_citation_verze:
nalezy.append(
f"{CITATION}: version se změnila, ale date-released zůstalo "
f"na {stare_datum}")
# 5. Název release větve musí odpovídat verzi, kterou nese.
shoda_branch = BRANCH_VERZE.match(head_ref)
if shoda_branch and shoda_branch.group(1) != nova_verze:
nalezy.append(
f"větev {head_ref!r} neodpovídá {METADATA} "
f"version={nova_verze}")
if nalezy:
print("Nálezy:")
for nalez in nalezy:
print(f" {nalez}")
return 1
print(f"Kontrola verze a changelogu: v{stara_verze} -> v{nova_verze}, "
f"bez nálezů")
return 0
if __name__ == "__main__":
sys.exit(main())