2. esempio Milano (http://dati.comune.milano.it/openbilancio/spese2019)
3. analisi del codice html/js, con riferimento alla struttura dati
4. collegamento con Socrata
5. suggerimenti alle Amministrazioni
1. cos'è la treemap
2. esempio Milano (http://dati.comune.milano.it/openbilancio/spese2019)
il sorgente come al solito si guarda qui:
view-source:dati.comune.milano.it/openbilancio/spese2019
3. analisi del codice html/js, con riferimento alla struttura dati
...
var data = {
'DIRITTI SOCIALI, POLITICHE SOCIALI E FAMIGLIA' :{
'INTERVENTI PER L`INFANZIA E I MINORI E PER ASILI NIDO' :{
'CONTRATTI DI SERVIZIO PUBBLICO' :'108140720',
'RETRIBUZIONI IN DENARO' :'47197600',
'CONTRIBUTI SOCIALI EFFETTIVI A CARICO DELL`ENTE' :'13760700',
'UTENZE E CANONI' :'3819410',
'MANUTENZIONE ORDINARIA E RIPARAZIONI' :'3049760',
'ALTRO' :'4523820'},
'INTERVENTI PER GLI ANZIANI' :{
'CONTRATTI DI SERVIZIO PUBBLICO' :'66118870',
'INTERVENTI ASSISTENZIALI' :'4862260',
'ALTRI TRASFERIMENTI A FAMIGLIE' :'3400000',
'RETRIBUZIONI IN DENARO' :'1988990',
'ALTRO' :'2096600'},
'INTERVENTI PER LA DISABILITA`' :{
'CONTRATTI DI SERVIZIO PUBBLICO' :'47249980',
'TRASFERIMENTI CORRENTI A AMMINISTRAZIONI CENTRALI' :'7253480',
'RETRIBUZIONI IN DENARO' :'6053250',
'INTERVENTI ASSISTENZIALI' :'2543870',
'CONTRIBUTI SOCIALI EFFETTIVI A CARICO DELL`ENTE' :'1750230',
'TRASFERIMENTI CORRENTI A ISTITUZIONI SOCIALI PRIVATE' :'1250000',
'ALTRO' :'2007100'},
etc...
come si vede i dati devono essere in qualche modo 'annidati' con le parentesi graffe
i programmini python servono a creare la variabile data che deve rimpiazzare quella di Milano con i dati di Crema (magari un giorno anche tutto html...).
4. collegamento con Socrata
il json del bilancio è molto grezzo, non annida le voci in base alle missioni e programmi
occorre un programmino python per riorganizzarlo in base al codice ministeriale:
appunti su come accedere a Socrata, spezzare i campi e ricostruirli con python (esempio riga 101):
Nota : lo split in base al trattino "-" del campo descrizione darà liste di lunghezza 1 invece che 2 perché non sempre presente nelle righe; da gestire
per prima cosa occorre riorganizzare i campi (soprattutto quelli del codice MEF) come NON vuole fare l'Amministrazione, poi creare l'albero.
import requests
from string import split
url="https://www.dati.lombardia.it/api/views/gy48-yges/rows.csv"
sito=requests.get(url)
listarighe=split(sito.text,"\n");
riga=[split(s,",") for s in listarighe]
f=open("output.txt","w")
r=1
while r<len(riga):
codiceMEF=riga[r][1]
listacodiceMEF=split(riga[r][1],".")
#gestione trattino non sempre presente o troppo presente, maledizione
listadescrizione=split(riga[r][2],"-")
if len(listadescrizione)==1:
campodescr1=listadescrizione[0]
campodescr2=" "
else:
campodescr1=listadescrizione[0]
campodescr2=' '.join(listadescrizione[1:])
bilanciostrut = riga[r][0]+"\t"+listacodiceMEF[0]+"\t"+listacodiceMEF[1]+"\t"+listacodiceMEF[2][0:5]+"\t"+listacodiceMEF[2][5:8]+"\t"+campodescr1+"\t"+campodescr2+"\t"+riga[r][4]+"\n"
#solo uscite U, E per entrate
if riga[r][0]== "U":
#ignorare codici no ascii , per evitare casini
print(str(r)+"\t" + bilanciostrut+"\n")
f.write(bilanciostrut.encode('ascii', 'ignore'))
r +=1
f.close()
#chiude il file, salvando modifiche ecc
il file output.txt si presenta così, coi campi separati:
ACHTUNG: Crema usa gli apici per gli accenti (') questo è drammatico per javascript, highcharts; occorre sostituire gli apici con un altro tipo di apice come fanno a Milano (`)
Passiamo alla creazione dell'albero con le parentesi graffe.
Si percorre in modo ricorsivo ciascuna colonna di output.txt generato sopra da sinistra a destra pregando che i codici siano giusti...
import csv
with open('output.txt', 'rb') as csvfile:
reader = csv.reader(csvfile, delimiter='\t', quotechar='|')
varTreeMap="var data = { \n '"
numrig = 0
currentc1=""
currentc2=""
previousc1=""
previousc2=""
for row in reader:
colnum = 0
for col in row:
a=row[colnum]
a=a.replace("'","`")
row[colnum]=a
colnum += 1
if not row[7] == '0':
if numrig == 0:
#inizializzo var data
print row[1]
currentc1=row[1]
currentc2=row[2]
previousc1=row[1]
previousc2=row[2]
varTreeMap+=currentc1
varTreeMap+=" ':{ \n \t \t'"
varTreeMap+=currentc2
varTreeMap+=" ':{ \n \t \t \t '"
varTreeMap+=row[6]
varTreeMap+=" ': '"
varTreeMap+=row[7]
varTreeMap+=" ', \n "
else:
#tutte le altre righe
currentc1=row[1]
currentc2=row[2]
if currentc1==previousc1:
if currentc2==previousc2:
#è un'altra foglia
varTreeMap+="\t \t \t '"
varTreeMap+=row[6]
varTreeMap+=" ': '"
varTreeMap+=row[7]
varTreeMap+=" ', \n "
else:
#cambio rametto c2
varTreeMap+="\t \t \t 'altro':'0'},\n"
varTreeMap+="\t \t '"
varTreeMap+=currentc2
varTreeMap+=" ':{ \n \t \t \t '"
varTreeMap+=row[6]
varTreeMap+=" ': '"
varTreeMap+=row[7]
varTreeMap+=" ', \n "
else:
#cambio missione c1
varTreeMap+="\t \t \t 'altro':'0' }\n"
varTreeMap+="\t },\n '"
varTreeMap+=currentc1
varTreeMap+=" ':{ \n \t '"
varTreeMap+=currentc2
varTreeMap+=" ':{ \n \t \t '"
varTreeMap+=row[6]
varTreeMap+=" ': '"
varTreeMap+=row[7]
varTreeMap+=" ', \n "
previousc1=currentc1
previousc2=currentc2
numrig += 1
varTreeMap+="\t\t\t'altro':'0' }\n\t\t}\n},"
f=open('vartreemap.txt','w')
f.write(varTreeMap.encode('ascii', 'ignore'))
f.close()
filenames = ['primopezzohtml.txt', 'vartreemap.txt', 'secondopezzohtml.txt']
with open('treemapCrema2019.html', 'w') as outfile:
for fname in filenames:
with open(fname) as infile:
for line in infile:
outfile.write(line)
A questo punto nel file vartreemap.txt c'è la variabile da copiare tale e quale nel codice html di Milano al posto della loro
![]() |
| il file con la sola variabile da copiare |
e il gioco è fatto:
treemapCrema2019.html
Ovviamente i pezzi di python andranno messi insieme uno dopo l'altro , inclusa l'inclusione finale di vardata fra i due pezzi (il prima primopezzohtml.txt e il dopo secondopezzohtml.txt, preparati all'uopo; ultime 6 righe del programma) di html.
5. suggerimenti alle Amministrazioni
a. usare apici diversi da quelli di javascript (`) altrimenti uno è costretto a lavorarci
b. controllare formato numerico caricato su Socrata (al momento in centesimi di euro, per cui il valore in output.txt va diviso per 100 - per ora - per avere numeri coretti nel treemap:
bilanciostrut = riga[r][0]+"\t"+listacodiceMEF[0]+"\t"+listacodiceMEF[1]+"\t"+listacodiceMEF[2][0:5]+"\t"+listacodiceMEF[2][5:8]+"\t"+campodescr1+"\t"+campodescr2+"\t"+str(int(riga[r][5])/100)+"\n"
)
c. divulgare una tabella di corrispondenze fra codice di campo e significato, in modo che sul treemap appaiano parole invece che numeri
d. un codice di costo è sbagliato...., e non appare nel treemap
'50024 ':{
'01 ':{
' ': '17897586000 ',
'altro':'0'},
'03 ':{
' ': '1557266300 ',
' ': '61169600 ',
' ': '54138100 ',
' ': '1510858200 ',
'altro':'0' }
},

















