Skip to content

Commit af5df55

Browse files
committed
feat: pipeline merge+validate readiness_score 0-10
Merge engine: normalizzazione titoli, dataset_group logico, slug coerenti. Validate: HEAD probe + sniff CSV leggero. readiness_score 0-10: reachable, formato, colonne, status, delimiter, encoding, anni. Penalità: sniff fallito (-3), content-type non-CSV (-1). Validatori per protocollo: CKAN, HTML, SDMX (score 5), SPARQL (score 3). Rimossi: bulk_source_check, source_check_analyze, source_check_fetch, catalog_diff, build_catalog_signals, run_source (-6.400 righe nette). Unificate: FORMAT_PRIORITY, compute_dataset_group, _validate_utils.
1 parent 277c57f commit af5df55

62 files changed

Lines changed: 3411 additions & 9721 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/observatory.yml

Lines changed: 22 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ on:
99
permissions:
1010
contents: write
1111
id-token: write
12-
issues: write
1312

1413
concurrency:
1514
group: ${{ github.workflow }}-${{ github.ref }}
@@ -48,13 +47,6 @@ jobs:
4847
# ═══════════════════════════════════════════════════════════════════
4948
# STEP 1 — INVENTORY BUILD
5049
# ═══════════════════════════════════════════════════════════════════
51-
- name: Download previous report (for signals delta)
52-
if: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER != '' && env.GCP_SERVICE_ACCOUNT != '' && env.CATALOG_INVENTORY_GCS_PREFIX != '' }}
53-
run: |
54-
prefix='${{ env.CATALOG_INVENTORY_GCS_PREFIX }}'
55-
prefix="${prefix%/}"
56-
gcloud storage cp "$prefix/catalog_inventory_report.json" previous_report.json || echo "{}" > previous_report.json
57-
5850
- name: Download previous inventory (for source-check merge)
5951
if: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER != '' && env.GCP_SERVICE_ACCOUNT != '' && env.CATALOG_INVENTORY_GCS_PREFIX != '' }}
6052
run: |
@@ -71,97 +63,36 @@ jobs:
7163
--workers 16 \
7264
--skip-red-sources
7365
74-
- name: Build catalog signals + diff
75-
run: |
76-
python scripts/build_catalog_signals.py \
77-
--report data/catalog_inventory/generated/catalog_inventory_report.json \
78-
--previous previous_report.json \
79-
--radar data/radar/radar_summary.json \
80-
--out data/catalog/catalog_signals.json
81-
82-
if python -c "import json,sys; d=json.load(open('previous_report.json')); sys.exit(0 if d.get('sources') else 1)"; then
83-
python scripts/catalog_diff.py previous_report.json \
84-
data/catalog_inventory/generated/catalog_inventory_report.json --output diff.md
85-
else
86-
echo "NO_BASELINE" > diff.md
87-
fi
88-
89-
- name: Commit catalog signals
90-
env:
91-
GH_TOKEN: ${{ github.token }}
92-
run: |
93-
git config user.name "github-actions[bot]"
94-
git config user.email "github-actions[bot]@users.noreply.github.com"
95-
git add data/catalog/catalog_signals.json
96-
if git diff --cached --quiet; then
97-
echo "Nessuna variazione nei segnali."
98-
else
99-
git commit -m "chore(so): aggiorna catalog_signals e watch report [ci skip]"
100-
git push
101-
fi
102-
103-
- name: Create catalog alert issue
104-
env:
105-
GH_TOKEN: ${{ github.token }}
106-
GCS_PREFIX: ${{ secrets.CATALOG_INVENTORY_GCS_PREFIX }}
107-
run: |
108-
python scripts/gha/build_issue_body.py --gcs-prefix "$GCS_PREFIX"
109-
if [ ! -f issue_title.txt ]; then
110-
echo "Nessuna variazione o baseline assente."
111-
exit 0
112-
fi
113-
LABEL_ARGS=$(python3 -c "import json,sys; print(' '.join('--label '+l for l in json.load(open('issue_labels.json'))))")
114-
EXISTING=$(gh issue list --label "catalog-alert" --state open --json number,title --jq \
115-
".[] | select(.title | startswith(\"[Catalog]\")) | .number" | head -1)
116-
if [ -n "$EXISTING" ]; then
117-
echo "Aggiorno issue #$EXISTING."
118-
gh issue comment "$EXISTING" --body-file issue_body.md
119-
else
120-
eval "gh issue create --title \"$(cat issue_title.txt)\" --body-file issue_body.md $LABEL_ARGS"
121-
fi
122-
12366
# ═══════════════════════════════════════════════════════════════════
124-
# STEP 3SOURCE-CHECK (sempre)
67+
# STEP 2PIPELINE (merge + validate)
12568
# ═══════════════════════════════════════════════════════════════════
126-
- name: Download source-check results from GCS (for merge)
127-
if: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER != '' && env.GCP_SERVICE_ACCOUNT != '' && env.CATALOG_INVENTORY_GCS_PREFIX != '' }}
69+
- name: Clean old artifacts
12870
run: |
129-
prefix='${{ env.CATALOG_INVENTORY_GCS_PREFIX }}'
130-
prefix="${prefix%/}"
131-
mkdir -p data/catalog_inventory/generated
132-
gcloud storage cp "$prefix/source-check/source_check_results.parquet" \
133-
data/catalog_inventory/generated/source_check_results.parquet 2>/dev/null || \
134-
echo "No previous results — starting fresh"
71+
rm -f data/catalog/CATALOG_WATCH_REPORT.md
13572
136-
- name: Run bulk source-check
73+
- name: Run pipeline (merge + validate)
13774
run: |
138-
python scripts/bulk_source_check.py \
139-
--skip-red-sources \
140-
--no-sdmx-years \
141-
--circuit-fail-threshold 2 \
142-
--max-items 5000 \
75+
python scripts/pipeline/run_pipeline.py \
14376
--workers 16
14477
14578
# ═══════════════════════════════════════════════════════════════════
146-
# STEP 3b — BUILD SOURCE REPORTS
79+
# STEP 3 — SOURCE REPORTS
14780
# ═══════════════════════════════════════════════════════════════════
14881
- name: Build source reports
14982
run: |
15083
python scripts/build_source_reports.py
15184
152-
- name: Commit source reports + parquet
85+
- name: Commit results
15386
env:
15487
GH_TOKEN: ${{ github.token }}
15588
run: |
15689
git config user.name "github-actions[bot]"
15790
git config user.email "github-actions[bot]@users.noreply.github.com"
158-
git add data/reports/
159-
# Parquet: force-add perché data/catalog_inventory/generated/ è in .gitignore
91+
git add data/reports/ data/pipeline/validated.parquet data/pipeline/summary.json
16092
git add -f data/catalog_inventory/generated/catalog_inventory_latest.parquet \
161-
data/catalog_inventory/generated/source_check_results.parquet \
16293
data/catalog_inventory/generated/catalog_inventory_report.json
16394
if git diff --cached --quiet; then
164-
echo "Nessuna variazione nei report."
95+
echo "Nessuna variazione."
16596
else
16697
git commit -m "chore(so): aggiorna reports e parquet [ci skip]"
16798
git push
@@ -170,18 +101,21 @@ jobs:
170101
# ═══════════════════════════════════════════════════════════════════
171102
# STEP 4 — UPLOAD GCS
172103
# ═══════════════════════════════════════════════════════════════════
173-
- name: Upload source-check results to GCS
104+
- name: Upload validated results to GCS
174105
if: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER != '' && env.GCP_SERVICE_ACCOUNT != '' && env.CATALOG_INVENTORY_GCS_PREFIX != '' }}
175106
run: |
176107
stamp=$(python3 -c "from datetime import datetime, timezone; print(datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%S'))")
177108
prefix='${{ env.CATALOG_INVENTORY_GCS_PREFIX }}'
178109
prefix="${prefix%/}"
179110
python scripts/gha/gcs_upload.py \
180-
data/catalog_inventory/generated/source_check_results.parquet \
181-
"$prefix/source-check/source_check_results.parquet"
111+
data/pipeline/validated.parquet \
112+
"$prefix/pipeline/validated.parquet"
113+
python scripts/gha/gcs_upload.py \
114+
data/pipeline/validated.parquet \
115+
"$prefix/pipeline/snapshots/validated_${stamp}.parquet"
182116
python scripts/gha/gcs_upload.py \
183-
data/catalog_inventory/generated/source_check_results.parquet \
184-
"$prefix/source-check/snapshots/source_check_${stamp}.parquet"
117+
data/pipeline/summary.json \
118+
"$prefix/pipeline/summary_${stamp}.json"
185119
186120
- name: Upload inventory snapshot to GCS
187121
if: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER != '' && env.GCP_SERVICE_ACCOUNT != '' && env.CATALOG_INVENTORY_GCS_PREFIX != '' }}
@@ -207,20 +141,19 @@ jobs:
207141
"$prefix/snapshots/catalog_inventory_report_${stamp}.json"
208142
209143
# ═══════════════════════════════════════════════════════════════════
210-
# STEP 5 — ARTIFACTS + SUMMARIES
144+
# STEP 5 — ARTIFACTS
211145
# ═══════════════════════════════════════════════════════════════════
212146
- name: Upload workflow artifacts
213147
uses: actions/upload-artifact@v7
214148
with:
215149
name: observatory-results
216150
path: |
217-
data/catalog_inventory/generated/source_check_results.parquet
151+
data/pipeline/validated.parquet
152+
data/pipeline/summary.json
153+
data/catalog_inventory/generated/catalog_inventory_latest.parquet
218154
data/radar/radar_summary.json
219-
data/radar/radar_history.json
220155
221-
- name: Publish summaries
156+
- name: Publish radar summary
222157
run: |
223158
python scripts/gha/publish_radar_summary.py
224159
cat radar_summary.md >> "$GITHUB_STEP_SUMMARY"
225-
python scripts/gha/publish_source_check_summary.py
226-
cat source_check_summary.md >> "$GITHUB_STEP_SUMMARY"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ observatory-results/
3535
# Generated by CI — bridge verso data-advocacy
3636
data/health/
3737
data/compliance/
38+
data/pipeline/

CONTRIBUTING.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,18 @@ Risponde a una domanda: **questa fonte vale il tempo del Lab?**
1313
Il funnel del repo:
1414

1515
```
16-
radar ── gate ── catalog-watch ── catalog-inventory ── source-check
17-
└── radar-only
16+
radar ── gate ── catalog-watch ── catalog-inventory ── pipeline (merge → validate)
17+
└── radar-only
1818
```
1919

2020
Qui stanno:
2121

2222
- `sources_registry.yaml` — registro di tutte le fonti osservate
23-
- `scripts/` — radar check, inventory, source-check, catalog diff
23+
- `scripts/` — radar check, inventory, pipeline, report
24+
- `scripts/pipeline/` — merge + validate (produce `validated.parquet`)
2425
- `skills/` — guide operative per agenti (source-check, inventory-triage, portal-scout)
2526
- `so_mcp/` — layer MCP read-only sugli artifact
26-
- `data/` — artifact versionati: radar_summary, radar_history, catalog_signals
27+
- `data/` — artifact versionati: radar_summary, radar_history
2728
- workflow CI: `radar.yml` (daily), `observatory.yml` (weekly)
2829

2930
Qui non stanno:
@@ -42,7 +43,7 @@ il trattamento:
4243
| Modalità | Cosa succede | Frequenza |
4344
|---|---|---|
4445
| `radar-only` | Solo health check HTTP | Daily (radar.yml) |
45-
| `catalog-watch` | Radar + inventory + source-check | Daily radar + weekly observatory |
46+
| `catalog-watch` | Radar + inventory + pipeline (merge→validate) | Daily radar + weekly observatory |
4647

4748
### Radar (daily)
4849

@@ -54,9 +55,9 @@ Probe HTTP leggero su ogni fonte. Produce:
5455
### Observatory (weekly, lunedì)
5556

5657
1. Build inventory parquet per fonti `catalog-watch`
57-
2. Calcola segnali di drift
58-
3. Scoring item-level (source-check)
59-
4. Upload su GCS + issue alert in caso di variazioni
58+
2. Pipeline merge + validate → `validated.parquet`
59+
3. Report per fonte + dashboard
60+
4. Upload su GCS
6061

6162
## Setup locale
6263

@@ -75,13 +76,16 @@ pip install -e ../lab-connectors
7576

7677
```bash
7778
# Radar check manuale
78-
python scripts/radar_check.py
79+
so-radar-check
7980

8081
# Catalog inventory
8182
python scripts/build_catalog_inventory.py --out-dir data/catalog_inventory/generated --workers 4
8283

83-
# Source-check incrementale
84-
python scripts/bulk_source_check.py --skip-red-sources --max-items 200 --workers 8
84+
# Pipeline merge + validate
85+
so-run-pipeline --workers 4
86+
87+
# Build reports
88+
so-build-reports
8589

8690
# Test
8791
pytest tests/
@@ -121,9 +125,9 @@ Vedi [`.github`](https://github.com/dataciviclab/.github) per orientarti.
121125
## Riferimenti
122126

123127
- [README.md](README.md) — panoramica del repo
124-
- [docs/runbook.md](docs/runbook.md) — guida operativa radar, inventory, source-check
128+
- [docs/runbook.md](docs/runbook.md) — guida operativa radar, inventory, pipeline
125129
- [docs/architecture.md](docs/architecture.md) — architettura del sistema
126130
- [docs/catalog_watch_measurement_policy.md](docs/catalog_watch_measurement_policy.md) — policy di misura
127131
- [skills/](skills/) — guide operative per agenti
128132
- [`lab-connectors`](https://github.com/dataciviclab/lab-connectors) — dipendenza condivisa
129-
- [`dataset-incubator`](https://github.com/dataciviclab/dataset-incubator) — downstream: qui finiscono i source-check che diventano candidate
133+
- [`dataset-incubator`](https://github.com/dataciviclab/dataset-incubator) — downstream: qui finiscono i validated che diventano candidate

0 commit comments

Comments
 (0)