fix: hesláře osob se po aktualizaci tiše vyprázdní (#68)

Digiarchiv v4.1.0 (Solr 10, json.nl=arrarr) vrací položky facet jako
dvojice ["hodnota", počet] místo objektů {"name": ...}. fetch_set četl
r["name"], spadl na TypeError a hesláře vedoucích a nálezců se uložily
prázdné.

- _facet_name() přijímá oba formáty facet (starý i nový).
- Selhání setu vrací prázdný seznam i při přerušeném stránkování, ať
  se neuloží jen část hesláře.
- download_heslare() ponechá u selhaného nebo prázdného setu předchozí
  hodnoty z heslar.csv a vrátí seznam selhaných setů.
- Dialog při částečném selhání zobrazí varování místo „Hotovo“.
- Verze 2.1.3 + changelog.

Ověřeno proti produkčnímu API: vedoucí 2497, nálezci 426, ostatní
hesláře beze změny; simulované selhání ponechá předchozí hodnoty.

Refs #67, #66
Připraveno s pomocí AI (Claude).
This commit is contained in:
david-spacil authored and GitHub committed 2026-10-01 15:33:41 +02:00
1 parent 2b783cd13b
commit f8d938e353
3 files changed
+95 -11

No files matched your search

+66 -5
View File
@@ -104,6 +104,21 @@ def load_all_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):
dataset = []
params_amcr = {
@@ -206,7 +221,9 @@ def fetch_set(base_url, internal_name, api_set, task=None):
for r in records:
nazev = r["name"]
nazev = _facet_name(r)
if not nazev:
continue
dataset.append({
'Název': nazev,
@@ -217,17 +234,48 @@ def fetch_set(base_url, internal_name, api_set, task=None):
break
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(
f"Chyba u setu {api_set}: {e}",
"AMČR", Qgis.MessageLevel.Warning)
break
return []
return dataset
def download_heslare(task=None):
"""Fetches the codelists from the AMČR API and saves it to a CSV file."""
def _read_existing_rows():
"""
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()
existing = _read_existing_rows()
all_data = []
total_sets = len(slovnicek)
# index, (interni, api_nazev)
@@ -251,6 +299,18 @@ def download_heslare(task=None):
if data is None:
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)
# Report progress (0-100)
@@ -261,7 +321,8 @@ def download_heslare(task=None):
# Save to CSV
with open(OUTPUT_FILE, 'w', newline='', encoding='utf-8-sig') as f:
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.writerows(all_data)
+25 -5
View File
@@ -75,12 +75,15 @@ class UpdateCodelistsTask(QgsTask):
super().__init__(description, QgsTask.Flag.CanCancel)
self.success = False
self.exception = None
# Codelists that failed to download and kept their previous values
self.failed_sets = []
def run(self):
"""Runs in a background thread."""
try:
# Call the download function with the task reference
self.success = download_heslare(task=self)
self.success = download_heslare(
task=self, failed=self.failed_sets)
return self.success
except Exception as e:
self.exception = e
@@ -91,10 +94,17 @@ class UpdateCodelistsTask(QgsTask):
if result:
# Safely update the global variables in the main thread
refresh_globals()
QgsMessageLog.logMessage(
"Hesláře AMČR byly úspěšně aktualizovány.",
"AMČR", Qgis.MessageLevel.Info
)
if self.failed_sets:
QgsMessageLog.logMessage(
"Hesláře AMČR aktualizovány částečně, beze změny "
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:
if self.isCanceled():
QgsMessageLog.logMessage(
@@ -629,6 +639,16 @@ class AmcrFilterDialog(QDialog):
def on_completed():
_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(
parent_win,
"Hotovo",
+4 -1
View File
@@ -8,7 +8,7 @@ name=AMČR Viewer
qgisMinimumVersion=3.44.0
qgisMaximumVersion=4.99.0
description=Viewing and downloading the AMČR data.
version=2.1.2
version=2.1.3
author=David Spáčil
email=spacil@arub.cz
@@ -24,6 +24,9 @@ hasProcessingProvider=no
# Uncomment the following line and add your 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
v2.1.3 (2026-10-01)
* Fixed empty person codelists (excavation leaders, finders) after updating codelists against Digiarchive v4.1.0
* A codelist that fails to download keeps its previous values and the user is warned
v2.1.2 (2026-09-01)
* Qt6 compatibility
* Code clean-up