diff --git a/src/data/fts-eu-grants.json.py b/src/data/fts-eu-grants.json.py new file mode 100644 index 0000000..2acb5e6 --- /dev/null +++ b/src/data/fts-eu-grants.json.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""Data loader: FTS EU Grants — finanziamenti UE a beneficiari italiani.""" +import json +import sys + +sys.path.insert(0, "src/data") + +from lab_connectors.duckdb import safe_connect +from lab_connectors.gcs.paths import https_url + + +SLUG = "fts_eu_grants" +YEARS = [2020, 2021, 2022, 2023, 2024] + + +parquet_refs = " UNION ALL ".join( + f"SELECT * FROM read_parquet('{https_url('clean', 'clean_parquet', slug=SLUG, year=y)}')" + for y in YEARS +) + + +PROGRAM_CASE = """ + CASE + WHEN nome_programma ILIKE '%Recovery and Resilience%' THEN 'Recovery and resilience' + WHEN nome_programma ILIKE '%Horizon%' THEN 'Ricerca (Horizon)' + WHEN nome_programma ILIKE '%Erasmus%' THEN 'Istruzione (Erasmus+)' + WHEN nome_programma ILIKE '%Digital Europe%' THEN 'Digitale' + WHEN nome_programma ILIKE '%Creative Europe%' OR nome_programma ILIKE '%Culture%' THEN 'Cultura' + WHEN nome_programma ILIKE '%LIFE%' THEN 'Ambiente (LIFE)' + WHEN nome_programma ILIKE '%CERV%' OR nome_programma ILIKE '%Citizens%' THEN 'Cittadinanza' + WHEN nome_programma ILIKE '%Health%' OR nome_programma ILIKE '%EU4H%' THEN 'Salute' + WHEN nome_programma ILIKE '%Humanitarian%' THEN 'Aiuti umanitari' + WHEN nome_programma ILIKE '%Migration%' OR nome_programma ILIKE '%AMIF%' THEN 'Migrazione' + WHEN nome_programma ILIKE '%Connecting Europe Facility%' THEN 'Infrastrutture (CEF)' + WHEN nome_programma ILIKE '%Defence Fund%' THEN 'Difesa' + ELSE 'Altri programmi' + END +""" + + +ENTITY_CASE = """ + CASE + WHEN LOWER(flag_no_profit) IN ('true', 'yes') THEN 'Non-profit' + WHEN LOWER(flag_ong) IN ('true', 'yes') THEN 'ONG' + WHEN tipo_beneficiario ILIKE '%university%' + OR tipo_beneficiario ILIKE '%research%' + OR tipo_beneficiario ILIKE '%higher%' THEN 'Ricerca/Università' + WHEN tipo_beneficiario ILIKE '%SME%' + OR tipo_beneficiario ILIKE '%enterprise%' + OR tipo_beneficiario ILIKE '%company%' THEN 'Impresa' + WHEN tipo_beneficiario ILIKE '%public%' + OR tipo_beneficiario ILIKE '%government%' THEN 'Pubblica amministrazione' + ELSE 'Altro' + END +""" + + +def _query(con, sql): + rel = con.sql(sql) + cols = [desc[0] for desc in rel.description] + rows = [] + for row in rel.fetchall(): + rows.append( + { + col: int(val) if isinstance(val, float) and val == int(val) else val + for col, val in zip(cols, row) + } + ) + return rows + + +with safe_connect() as con: + base = f"({parquet_refs})" + + totali = _query( + con, + f""" + SELECT + COUNT(*) AS numero_grant, + COUNT(DISTINCT beneficiario_nome) AS beneficiari, + ROUND(SUM(COALESCE(importo_contrattato, 0)), 0) AS importo_totale + FROM {base} + """, + )[0] + + per_anno = _query( + con, + f""" + SELECT + anno, + COUNT(*) AS numero_grant, + COUNT(DISTINCT beneficiario_nome) AS beneficiari, + ROUND(SUM(COALESCE(importo_contrattato, 0)), 0) AS importo_totale + FROM {base} + GROUP BY anno + ORDER BY anno + """, + ) + + per_programma = _query( + con, + f""" + SELECT + anno, + {PROGRAM_CASE} AS categoria_programma, + COUNT(*) AS numero_grant, + COUNT(DISTINCT beneficiario_nome) AS beneficiari, + ROUND(SUM(COALESCE(importo_contrattato, 0)), 0) AS importo_totale + FROM {base} + GROUP BY anno, categoria_programma + ORDER BY anno, importo_totale DESC + """, + ) + + per_tipo_ente = _query( + con, + f""" + SELECT + anno, + {ENTITY_CASE} AS tipo_ente, + COUNT(*) AS numero_grant, + COUNT(DISTINCT beneficiario_nome) AS beneficiari, + ROUND(SUM(COALESCE(importo_contrattato, 0)), 0) AS importo_totale + FROM {base} + GROUP BY anno, tipo_ente + ORDER BY anno, importo_totale DESC + """, + ) + + per_citta = _query( + con, + f""" + SELECT + anno, + CASE + WHEN beneficiario_citta IS NULL OR beneficiario_citta IN ('', '-') THEN 'Non indicata' + ELSE beneficiario_citta + END AS citta, + COUNT(*) AS numero_grant, + COUNT(DISTINCT beneficiario_nome) AS beneficiari, + ROUND(SUM(COALESCE(importo_contrattato, 0)), 0) AS importo_totale + FROM {base} + GROUP BY anno, citta + QUALIFY ROW_NUMBER() OVER (PARTITION BY anno ORDER BY importo_totale DESC) <= 30 + ORDER BY anno, importo_totale DESC + """, + ) + + top_beneficiari = _query( + con, + f""" + SELECT + anno, + COALESCE(beneficiario_nome, 'Non indicato') AS beneficiario_nome, + CASE + WHEN beneficiario_citta IS NULL OR beneficiario_citta IN ('', '-') THEN 'Non indicata' + ELSE beneficiario_citta + END AS beneficiario_citta, + {PROGRAM_CASE} AS categoria_programma, + {ENTITY_CASE} AS tipo_ente, + COUNT(*) AS numero_grant, + ROUND(SUM(COALESCE(importo_contrattato, 0)), 0) AS importo_totale + FROM {base} + GROUP BY anno, beneficiario_nome, beneficiario_citta, categoria_programma, tipo_ente + QUALIFY ROW_NUMBER() OVER (PARTITION BY anno ORDER BY importo_totale DESC) <= 100 + ORDER BY anno, importo_totale DESC + """, + ) + + +output = { + "anni": YEARS, + "totali": totali, + "per_anno": per_anno, + "per_programma": per_programma, + "per_tipo_ente": per_tipo_ente, + "per_citta": per_citta, + "top_beneficiari": top_beneficiari, +} + +json.dump(output, sys.stdout, ensure_ascii=False) diff --git a/src/data/themes.json.py b/src/data/themes.json.py index 76e71da..8b67de3 100644 --- a/src/data/themes.json.py +++ b/src/data/themes.json.py @@ -13,7 +13,7 @@ "slug": "finanza-pubblica", "name": "Finanza pubblica", "description": "Entrate dello Stato, capacità fiscale, tributi locali, FSC, politiche di coesione, partecipazioni PA e disuguaglianza del reddito", - "datasets": ["irpef-comunale", "entrate-stato", "consip-consumi-convenzione", "istat-gini-regionale", "opencivitas-fsc-2025", "opencoesione-progetti", "mef-partecipazioni", "bdap-spese-stato"], + "datasets": ["irpef-comunale", "entrate-stato", "consip-consumi-convenzione", "istat-gini-regionale", "opencivitas-fsc-2025", "opencoesione-progetti", "fts-eu-grants", "mef-partecipazioni", "bdap-spese-stato"], }, { "slug": "sanita", diff --git a/src/dataset/fts-eu-grants.md b/src/dataset/fts-eu-grants.md new file mode 100644 index 0000000..2180d72 --- /dev/null +++ b/src/dataset/fts-eu-grants.md @@ -0,0 +1,208 @@ +--- +title: FTS EU Grants — Finanziamenti UE in Italia +description: Finanziamenti UE a beneficiari italiani dal Financial Transparency System, per anno, programma, territorio e beneficiario +source: Commissione europea — Financial Transparency System +source_url: https://commission.europa.eu/funding-tenders/financial-transparency-system_en +period: "2020–2024" +last_modified: 2026-07-01 +dataset_slug: fts_eu_grants +--- + +# FTS EU Grants — Finanziamenti UE in Italia + +Il Financial Transparency System della Commissione europea elenca i finanziamenti UE assegnati a beneficiari italiani. Il dataset copre il periodo 2020-2024 e permette di leggere importi, programmi, beneficiari e localizzazione dichiarata. + +**Fonte**: [Commissione europea — Financial Transparency System](https://commission.europa.eu/funding-tenders/financial-transparency-system_en) · **Periodo**: 2020–2024 + +```js +import { num, euroCompact, tableFormat } from "../import/format-utils.js"; +``` + +```js +const data = await FileAttachment("../data/fts-eu-grants.json").json(); +``` + +```js +const anni = [...data.anni].sort((a, b) => b - a); +const annoSel = view(Inputs.select(anni, {label: "Anno", value: anni[0]})); +``` + +```js +const annoData = data.per_anno.find(d => d.anno === annoSel); +const programmi = data.per_programma + .filter(d => d.anno === annoSel) + .sort((a, b) => d3.descending(a.importo_totale, b.importo_totale)); +const tipiEnte = data.per_tipo_ente + .filter(d => d.anno === annoSel) + .sort((a, b) => d3.descending(a.importo_totale, b.importo_totale)); +const citta = data.per_citta + .filter(d => d.anno === annoSel) + .sort((a, b) => d3.descending(a.importo_totale, b.importo_totale)); +const beneficiari = data.top_beneficiari + .filter(d => d.anno === annoSel) + .sort((a, b) => d3.descending(a.importo_totale, b.importo_totale)); +``` + +