Skip to content

Commit 36b5402

Browse files
aborrusoclaude
andcommitted
docs(skill): audit live rndt-explorer — fix sort rotti, nuovo workflow data journalist
Verifiche contro l'API reale (2026-07-17): - workflows #3/#4: dateDescending (non ordina) → apiso_Modified_dt:desc - rimosso apiso_PublicationDate_dt (campo inesistente) - sort su title ora funziona (API cambiata): aggiornati search-syntax, codelists.py (commento), knowledge/known-issues - output-formats: documentato compact (+ resources vuoto → get) - SKILL: v1.0, installazione PyPI, --timeout - nuovo workflow 7 (download, licenza, citazione fonte) verificato end-to-end su record ISPRA; sanity numbers aggiornati - README: esempio conversazionale data journalist + nota resources Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f3a8382 commit 36b5402

10 files changed

Lines changed: 142 additions & 23 deletions

File tree

LOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# LOG
22

3+
## 2026-07-17 (skill audit)
4+
5+
- **Audit live della skill `rndt-explorer`** (comandi eseguiti contro l'API reale). Esito: struttura a 4 fasi solida, 4 punti stale corretti:
6+
- `workflows.md` #3/#4 usavano `--sort dateDescending` (che NON ordina, riconfermato live) → sostituito con `apiso_Modified_dt:desc` + warning.
7+
- `workflows.md` citava `apiso_PublicationDate_dt` → campo inesistente (verificato su record reale), rimosso; lista campi data corretta.
8+
- **Scoperta: il sort su `title` ora funziona** (`title:asc` ordina alfabeticamente) — l'API è cambiata rispetto a maggio-giugno, quando dava "Fielddata is disabled". Aggiornati `search-syntax.md`, `codelists.py` (commento), `knowledge/api/known-issues.md`. I campi garantiti sortable restano `_s`/`_dt`/`_i`.
9+
- `output-formats.md` non menzionava `compact` → aggiunta sezione dedicata (incl. `resources: []` → serve `get`).
10+
- SKILL.md: frontmatter v0.1→1.0, installazione da PyPI in compatibility, opzioni globali `--timeout`/`--base-url`.
11+
- **Nuovo workflow 7 per data journalist** (verificato end-to-end live su record ISPRA "Popolazione rischio alluvioni"): compact+isOpendata → `search --id` per link con dctype → `get` per licenza/ente/data (citazione fonte) → `ogr2ogr` dal WFS. Note oneste: enclosure raro, `isOpendata` a volte generico, download spesso dietro portali regionali.
12+
- Sanity numbers aggiornati al 2026-07-17 (catalogo 23.580→23.632).
13+
- Aggiunti `apiso_CRS`, `apiso_Format_s`, `isOpendata` ai campi utili di `search-syntax.md` (per operatori GIS).
14+
- 55/55 test, ruff e mypy verdi (unica modifica codice: commento in `codelists.py`).
15+
316
## 2026-07-17
417

518
- **Valutazione readiness produzione v0.1.0** in `docs/evaluation-v0.1.0.md`: codice production-grade, gap tutti infrastrutturali (no CI, no mypy, metadata PyPI incompleti, manca `py.typed` e CHANGELOG). Roadmap verso v1.0 inclusa.

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ openrndt --format compact search --q "catasto" --num 3
8585
# {"id":"…","title":"…","org":"Comune di Torino","type":"service","category":null,"updated":"…","resources":["WMS"]}
8686
```
8787

88+
Se `resources` è `[]` il record non linka servizi fruibili: recupera il
89+
dettaglio con `get <id>` e guarda `_source.links_s` (spesso il download è
90+
dietro un portale dell'ente, non un link diretto).
91+
8892
## Esempi di conversazione con un'AI
8993

9094
openrndt è pensato per essere **pilotato da un assistente AI**. Non devi imparare la
@@ -202,6 +206,26 @@ openrndt search --q "frane AND isOpendata:*" --num 10
202206
> Regione Toscana ai rilievi con drone dell'emergenza alluvione 2023 in
203207
> Emilia-Romagna. Te li elenco con la licenza di ciascuno?
204208
209+
### «Devo scaricare i dati e citare la fonte in un articolo»
210+
211+
> **Tu:** Sto scrivendo un articolo sul rischio alluvioni. Mi servono i dati,
212+
> la licenza d'uso e come citare la fonte.
213+
214+
L'AI cerca fra gli open data e recupera licenza ed ente dal dettaglio:
215+
216+
```bash
217+
openrndt --format compact search --q "alluvioni AND isOpendata:*" --num 30
218+
openrndt search --id "ispra_rm:01IdroHazard_DT" \
219+
| jq -r '.results[0].links[] | select(.dctype != null) | "\(.dctype)\t\(.href)"'
220+
openrndt get "ispra_rm:01IdroHazard_DT" \
221+
| jq '{licenza: ._source.isOpendata, ente: ._source.EnteResponsabile_s}'
222+
```
223+
224+
> **AI:** **ISPRA** pubblica *Popolazione a rischio alluvioni* con licenza
225+
> **CC-BY-4.0**: puoi riusarlo citando la fonte (es. "Fonte: ISPRA — Popolazione
226+
> a rischio alluvioni, CC-BY 4.0"). I dati sono esposti come WFS: te li scarico
227+
> in GeoPackage con `ogr2ogr`, pronti per QGIS o per un'analisi tabellare.
228+
205229
## Uso come libreria Python
206230

207231
```python

knowledge/api/known-issues.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ Tutti i punti seguenti sono stati verificati empiricamente contro l'API di produ
1313
| Comportamento documentato | Comportamento reale | Compensazione in openrndt |
1414
|---------------------------|---------------------|---------------------------|
1515
| Il parametro `dataCategory` filtra per categoria. | Non filtra: ritorna sempre il catalogo intero. | `--data-category` è tradotto nella clausola Lucene `q=keywords_s:VAL` (`OR` per valori multipli). |
16-
| `sort=dateDescending` / `dateAscending` ordinano per data. | Ignorati: ordine identico fra loro. | `discover --what sort_values` marca i valori rotti; la sintassi funzionante è `campo:asc\|desc` su campo sortable (es. `apiso_Modified_dt:desc`). |
17-
|| Ordinare su un campo `text` analizzato (es. `title` nudo) errore Elasticsearch "Fielddata is disabled". | Documentato nelle codelist; usare i campi `_s`/`_dt`/`_i`. |
16+
| `sort=dateDescending` / `dateAscending` ordinano per data. | Ignorati: ordine identico fra loro (riconfermato 2026-07-17). | `discover --what sort_values` marca i valori rotti; la sintassi funzionante è `campo:asc\|desc` su campo sortable (es. `apiso_Modified_dt:desc`). |
17+
|| Ordinare su un campo `text` analizzato (es. `title` nudo) dava errore Elasticsearch "Fielddata is disabled"; **dal 2026-07-17 risulta funzionare** (`title:asc` ordina alfabeticamente — comportamento API cambiato). | Documentato nelle codelist; i campi garantiti sortable restano `_s`/`_dt`/`_i`. |
1818
| Esiste una data di pubblicazione ordinabile. | Non esiste; `apiso_CreationDate_dt` è spesso null o fittizia (es. 2012-01-01). | Proxy consigliato per "più recenti": `apiso_Modified_dt:desc` (issue #4 del repository). |
1919
| L'endpoint CSW supporta `SortBy` (INSPIRE Discovery Services v3.1). | `SortBy` ignorato: non conforme. | Nessuna: documentato (issue #5 del repository). |
2020
| Item inesistente → errore HTTP. | Risponde `200` con body `{"found": false}`. | `get_item` controlla `found` e solleva `ItemNotFoundError`. |

knowledge/log.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 2026-07-17
44

5+
* **Update**: [api/known-issues.md](/api/known-issues.md), [skill.md](/skill.md) — audit live della skill rndt-explorer: il sort su `title` ora funziona (API cambiata), `dateDescending` riconfermato rotto; skill aggiornata (workflow corretti, campo `apiso_PublicationDate_dt` inesistente rimosso, formato compact documentato, nuovo workflow data journalist).
56
* **Update**: [project.md](/project.md) — versione 1.0.0, roadmap produzione completata (`CHANGELOG.md` nel repository).
67
* **Update**: [conventions/testing.md](/conventions/testing.md), [conventions/error-handling.md](/conventions/error-handling.md) — coverage 89%→99% (17 test nuovi, `tests/test_output.py`); bug reale trovato e corretto: `--format` invalido produceva un traceback, ora messaggio leggibile + exit 2.
78
* **Update**: [cli/index.md](/cli/index.md), [conventions/error-handling.md](/conventions/error-handling.md), [architecture.md](/architecture.md) — documentata la nuova opzione globale `--timeout` (stesso pattern di `--base-url`, override in `config.py`).

knowledge/skill.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ La skill `rndt-explorer` (in `skills/rndt-explorer/` nel repository) insegna a u
2424
| `references/search-syntax.md` | Sintassi Lucene con esempi verificati. |
2525
| `references/result-structure.md` | Struttura del JSON di risposta. |
2626
| `references/output-formats.md` | Guida ai formati (vedi anche [la convenzione](/conventions/output-formats.md)). |
27-
| `references/workflows.md` | Flussi tipici end-to-end. |
27+
| `references/workflows.md` | Flussi tipici end-to-end, incluso il workflow per data journalist (dati scaricabili, licenza, citazione fonte). |
2828
| `references/ogc-services.md` | Esplorazione WMS/WFS/WCS/WMTS con GDAL/OGR. |

skills/rndt-explorer/SKILL.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ description: >
1515
license: MIT
1616
compatibility: >
1717
Richiede la CLI openrndt (comandi: search, get, discover).
18+
Installazione: `uv tool install openrndt` (da PyPI) oppure `uvx openrndt`.
1819
metadata:
1920
author: ondata
20-
version: "0.1"
21+
version: "1.0"
2122
---
2223

2324
# RNDT Explorer — esplorazione guidata del catalogo
@@ -38,6 +39,12 @@ openrndt --format compact search … # NDJSON: 1 riga/record, per scremare a bas
3839
Il formato `compact` (solo per `search`) emette una riga JSON per record con i
3940
campi ad alto segnale — `id`, `title`, `org`, `type`, `category`, `updated`, `resources`
4041
ideale per individuare il record giusto prima di chiedere il dettaglio con `get`.
42+
Se `resources` è `[]` il record non linka servizi fruibili: fai `get <id>` e
43+
guarda `_source.links_s`.
44+
45+
Altre opzioni globali (sempre PRIMA del comando): `--timeout <secondi>` per il
46+
timeout HTTP per singolo tentativo (default 30s; con i retry il caso peggiore è
47+
~3x — utile abbassarlo se il portale è lento), `--base-url` per un mirror.
4148

4249
Tutti i comandi hanno `--help`. La skill segue 4 fasi.
4350

@@ -184,7 +191,8 @@ interrogabili con GetFeatureInfo, download vettoriale con `ogr2ogr`. Guida in
184191

185192
[`references/workflows.md`](./references/workflows.md) raccoglie sequenze
186193
testate live (catasto per provincia, WMS di un tema INSPIRE, dataset di un
187-
ente, aggiornamenti recenti per categoria, export CSV, sanity check con i
194+
ente, aggiornamenti recenti per categoria, export CSV, dati scaricabili con
195+
licenza e citazione della fonte per data journalist, sanity check con i
188196
totali attesi).
189197

190198
## Output e parsing

skills/rndt-explorer/references/output-formats.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
`openrndt` ha due livelli di formato:
44

5-
1. **Formato del comando CLI** (`--format` globale): `json` (default), `table`, `csv`.
5+
1. **Formato del comando CLI** (`--format` globale): `json` (default), `table`,
6+
`csv`, `compact` (NDJSON, solo per `search`).
67
Controlla come la CLI stampa il risultato.
78
2. **Formato della risposta API** (`-f` interno, gestito automaticamente):
89
`json`, `atom`, `csv`, `kml`, ecc. Non esposto direttamente nella CLI MVP —
@@ -13,11 +14,26 @@
1314
| Scenario | Formato consigliato |
1415
|---------------------------------------------------|---------------------|
1516
| Pipeline `\| jq`, scripting Python/Bash | `json` (default) |
17+
| Scremare molti risultati a basso costo di token (agenti) | `compact` |
1618
| Mostrare risultati in chat all'utente | `table` |
1719
| Esportare in foglio di calcolo | `csv` |
1820
| Recuperare XML ISO 19139 per validatori INSPIRE | `openrndt get <id> --xml` |
1921
| Generare pagina HTML del metadato | `openrndt get <id> --html` |
2022

23+
## `compact` — NDJSON per agenti (solo `search`)
24+
25+
Una riga JSON per record con i soli campi ad alto segnale: `id`, `title`,
26+
`org`, `type`, `category`, `updated`, `resources`.
27+
28+
```bash
29+
openrndt --format compact search --q "frane AND isOpendata:*" --num 30
30+
```
31+
32+
- `resources` elenca i tipi di servizio/download fruibili (`WMS`, `WFS`,
33+
`download`, …). Se è `[]` il record non linka servizi: per i dettagli fai
34+
`get <id>` e guarda `_source.links_s`.
35+
- `get --format compact` (come `csv`) è rifiutato: il dettaglio non è tabellare.
36+
2137
## Esempi
2238

2339
```bash

skills/rndt-explorer/references/search-syntax.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,15 @@ Lista completa in [`result-structure.md`](./result-structure.md). I più ricorre
3232
- `apiso_Type_s` (tipo risorsa: `dataset`, `service`, ecc.)
3333
- `PuntoDiContattoEmail_s` (email contatto)
3434
- `AmbitoTerritoriale_s` (Regionale/Nazionale/Locale)
35+
- `isOpendata` (licenza open data: `isOpendata:*` per tutti gli open data)
36+
- `apiso_CRS` (sistema di riferimento, es. `apiso_CRS:"EPSG:4326"` — utile per operatori GIS)
37+
- `apiso_Format_s` (formati disponibili, es. `Shapefile`, `GeoTIFF`, `GML`)
3538

3639
## Esempi verificati live
3740

3841
```bash
3942
# Tema INSPIRE
40-
openrndt search --q 'INSPIRETheme_s:Idrografia' --num 5 #845 totali
43+
openrndt search --q 'INSPIRETheme_s:Idrografia' --num 5 #863 totali (2026-07-17)
4144

4245
# Ente (forma breve)
4346
openrndt search --q 'contact_organizations_s:"Agenzia delle Entrate"' --num 5
@@ -92,8 +95,11 @@ Limiti noti:
9295
nell'XML ISO (`gmd:CI_Date dateType=publication`) ma non è un campo indicizzato
9396
ordinabile. Il proxy disponibile è `apiso_Modified_dt` (dateStamp del metadato).
9497
`apiso_CreationDate_dt` è spesso `null` o fittizio (`2012-01-01`): inaffidabile.
95-
- Ordinare per un campo `text`/analizzato (es. `title` nudo) dà errore
96-
Elasticsearch *"Fielddata is disabled"*: usa un campo keyword.
98+
- Il sort su `title` (campo text) in passato dava errore Elasticsearch
99+
*"Fielddata is disabled"*; **riverificato il 2026-07-17: ora funziona**
100+
(`title:asc` ordina alfabeticamente — il comportamento dell'API è cambiato).
101+
I campi *garantiti* sortable restano comunque quelli `_s`/`_dt`/`_i`:
102+
su altri campi text non c'è garanzia.
97103
- Il servizio **CSW** (`/csw`) ignora del tutto `<ogc:SortBy>`: non ordina per
98104
nessuna proprietà. Dettagli e implicazioni INSPIRE in `ref/rest-api-rndt.md`.
99105

skills/rndt-explorer/references/workflows.md

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,21 @@ openrndt --format json search \
5050
```bash
5151
openrndt --format table search \
5252
--q 'contact_organizations_s:"Agenzia delle Entrate"' \
53-
--num 20 --sort dateDescending
53+
--num 20 --sort 'apiso_Modified_dt:desc'
5454
```
5555

56+
> ⚠️ Non usare `--sort dateDescending`: documentato sul RNDT ma **ignorato**
57+
> dall'API (verificato live, riconfermato 2026-07-17). Il sort reale è
58+
> `campo:asc|desc` — vedi [`search-syntax.md`](./search-syntax.md).
59+
5660
## 4. Aggiornamenti recenti
5761

5862
> "Dataset modificati nel 2024, dal più recente."
5963
6064
```bash
6165
openrndt --format json search \
6266
--time "2024-01-01/2024-12-31" \
63-
--sort dateDescending \
67+
--sort 'apiso_Modified_dt:desc' \
6468
--num 30
6569
```
6670

@@ -70,12 +74,15 @@ openrndt --format json search \
7074
> 2. Il campo top-level `updated` di ogni risultato riflette la data di
7175
> reindicizzazione del catalogo (uguale per tutti), non la data del dataset.
7276
> Per ordinare/filtrare per "data del dataset" usare i campi `_source`:
73-
> - `apiso_RevisionDate_dt`data di revisione del metadato
74-
> - `apiso_CreationDate_dt` — data di creazione del metadato
75-
> - `apiso_PublicationDate_dt` — data di pubblicazione
77+
> - `apiso_Modified_dt`dateStamp del metadato (il proxy più affidabile)
78+
> - `apiso_RevisionDate_dt` — data di revisione della risorsa (spesso null)
79+
> - `apiso_CreationDate_dt` — data di creazione (spesso null o fittizia)
7680
> - `timeperiod_nst[].begin_dt`/`end_dt` — copertura temporale dei dati
7781
> (è il campo su cui agisce il parametro `--time`)
7882
>
83+
> Non esiste un campo `apiso_PublicationDate_dt` (verificato live 2026-07-17):
84+
> la data `publication` sta solo nell'XML ISO, non è indicizzata.
85+
>
7986
> Workaround per "inlandWaters revisionati nel 2024":
8087
>
8188
> ```bash
@@ -98,15 +105,57 @@ xmllint --noout meta.xml && echo "XML valido"
98105
openrndt --format csv search --q "ortofoto" --num 100 > ortofoto.csv
99106
```
100107

108+
## 7. Dati scaricabili, licenza e citazione della fonte (data journalist)
109+
110+
> "Mi servono i dati sulla popolazione a rischio alluvioni, con licenza che
111+
> ne permetta il riuso, e devo citare la fonte."
112+
113+
```bash
114+
# 1. cerca solo open data, scrematura veloce con compact:
115+
# la colonna `resources` dice subito cosa offre ogni record
116+
openrndt --format compact search --q "alluvioni AND isOpendata:*" --num 30
117+
# {"id":"ispra_rm:01IdroHazard_DT","title":"Popolazione rischio alluvioni - Dataset",...,"resources":["WFS","WMS"]}
118+
119+
# 2. URL dei servizi con il tipo (usa `search --id`, che espone rel/dctype)
120+
openrndt --format json search --id "ispra_rm:01IdroHazard_DT" \
121+
| jq -r '.results[0].links[] | select(.dctype != null) | "\(.dctype)\t\(.href)"'
122+
# WMS https://sdi.isprambiente.it/geoserver/nz1/wms?...
123+
# WFS https://sdi.isprambiente.it/geoserver/nz1/wfs?...
124+
125+
# 3. licenza, ente e data per la citazione della fonte
126+
openrndt --format json get "ispra_rm:01IdroHazard_DT" \
127+
| jq '{licenza: ._source.isOpendata, ente: ._source.EnteResponsabile_s,
128+
aggiornato: ._source.apiso_Modified_dt}'
129+
# → CC-BY-4.0, ISPRA, 2015-02-13
130+
131+
# 4. dal WFS ai dati tabellari (GeoPackage, apribile anche in QGIS)
132+
ogr2ogr -f GPKG alluvioni.gpkg "WFS:https://sdi.isprambiente.it/geoserver/nz1/wfs" <feature_type>
133+
```
134+
135+
Note verificate live (2026-07-17):
136+
137+
- `resources: []` nel compact è frequente: il record non linka servizi
138+
fruibili. In quel caso fai `get` e guarda `_source.links_s` — spesso il
139+
download è dietro un portale regionale (es. Geoscopio Toscana), non un
140+
link diretto.
141+
- Il download diretto (`rel=enclosure` / `dctype=download`) è raro: la
142+
maggior parte dei dataset si prende via WFS (vettoriale) con `ogr2ogr`
143+
vedi [`ogc-services.md`](./ogc-services.md).
144+
- `isOpendata` può valere una licenza precisa (`"CC BY 4.0"`) o un generico
145+
`"opendata"`: per la licenza esatta guarda anche
146+
`_source.apiso_AccessConstraints_s`.
147+
101148
## Risultati di riferimento (sanity check)
102149

103-
Numeri ottenuti live al 2026-05-27 — utili per accorgersi di regressioni:
150+
Numeri ottenuti live al 2026-07-17 — utili per accorgersi di regressioni
151+
(cambiano nel tempo: il catalogo cresce):
104152

105153
| Query | `total` atteso |
106154
|--------------------------------------------------------------------|---------------:|
107-
| `--q "catasto"` | 8.814 |
108-
| `--data-category planningCadastre` | 11.351 |
109-
| `--data-category "planningCadastre,boundaries"` | 11.720 |
110-
| `--q 'INSPIRETheme_s:Idrografia'` | 845 |
155+
| `--q "catasto"` | 8.827 |
156+
| `--data-category planningCadastre` | 11.659 |
157+
| `--data-category "planningCadastre,boundaries"` | 12.046 |
158+
| `--q 'INSPIRETheme_s:Idrografia'` | 863 |
111159
| `--q 'contact_organizations_s:"Agenzia delle Entrate"'` | 7.699 |
112-
| catalogo completo (`--q "*"` o nessun filtro) | 23.580 |
160+
| `--q 'title:"carta geologica"'` | 154 |
161+
| catalogo completo (nessun filtro) | 23.632 |

src/openrndt/codelists.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
# IMPORTANTE (verificato live): il meccanismo reale è `campo:asc|desc` su un campo
3636
# Elasticsearch *sortable* (keyword `_s`, data `_dt`, intero `_i`). I valori
3737
# "amichevoli" `dateDescending`/`dateAscending` documentati sulla pagina ufficiale
38-
# NON ordinano (su RNDT restituiscono ordine identico fra loro → ignorati).
39-
# Ordinare per un campo `text`/analizzato (es. `title` nudo) dà errore Elasticsearch
40-
# "Fielddata is disabled". Non esiste un campo data-di-pubblicazione ordinabile.
38+
# NON ordinano (su RNDT restituiscono ordine identico fra loro → ignorati;
39+
# riconfermato 2026-07-17). Il sort su `title` (campo text) in passato dava errore
40+
# "Fielddata is disabled", ma dal 2026-07-17 risulta funzionare: i campi garantiti
41+
# sortable restano `_s`/`_dt`/`_i`. Non esiste un campo data-di-pubblicazione
42+
# ordinabile.
4143
SORT_VALUES: dict[str, str] = {
4244
"apiso_Modified_dt:desc": "Per data: ultimi metadati modificati per primi (proxy migliore per 'più recenti').",
4345
"apiso_Modified_dt:asc": "Per data: metadati modificati meno di recente per primi.",

0 commit comments

Comments
 (0)