# IMPORT ESSENZIALI
# Per il parsing dell'XML -- questo pacchetto è incluso anche nel più generale lxml
import xml.etree.ElementTree as ET
# Utilities per leggere/scrivere files csv
import csv
# Utilities per gestire i character encodings
import unicodedata
# Dizionari ordinati
from collections import OrderedDict
# IMPORT OPZIONALI
# Per fare un stima della velocità delle varie istruzioni
from datetime import datetime
# Generatore di numeri casuali -- può sempre servire in fase di testing
from random import *
# Può servire per alcuni test
import sys
# Per le regex
import re
# La funzione BASE: traceElems
def traceElems(node: ET.Element, condition, parents: list = [], coords: list = []):
res = []
jj = 0
for child in node:
if condition(child):
res.append({'a_par': parents+[node],
'coords': coords+[jj], 'child': child})
else:
res = res + traceElems(child, condition, parents+[node], coords+[jj])
jj = jj+1
return res
# Funzione-base per stoppare traceElems
def isLeafOrC(aa: ET.Element):
if(aa.tag=='c' or len(aa)==0):
return True
else:
return False
# Funzioni-utilità che servono solo a visualizzare meglio i dati sul notebook.
def shownode(node: ET.Element):
return (node.tag, node.attrib, node.text.replace('\t','').replace('n','').strip() \
if type(node.text) is str else '')
def shownodelist(el: ET.Element):
return list(map(shownode, el))
# Utility copiata da INTERNEZZ -- versione 'multipla' del metodo str.index:
def indices(lst, element):
result = []
offset = -1
while True:
try:
offset = lst.index(element, offset+1)
except ValueError:
return result
result.append(offset)
import_dir = '/home/kora/Desktop/OVI_Data_Local/ASPO/XML/'
export_dir = '/home/kora/Desktop/OVI_Data_Local/ASPO/CSV/Gettatelli/'
#import_dir = '/Users/federicaspinelli/Google Drive/OVI:CNR/LAVORO 2020/SELEZIONE CONTENUTI/01_ASPO/XDAMS/'
#export_dir = '/Users/federicaspinelli/Google Drive/OVI:CNR/CSV/ASPO/gettatelli/'
ts1 = datetime.timestamp(datetime.now())
treeDatini = ET.parse(import_dir + 'export_aspoGT001--gettatelli.xml')
rootDatini = treeDatini.getroot()
ts2 = datetime.timestamp(datetime.now())
print(ts2 - ts1)
0.04100799560546875
cLevs = set(map(lambda a : a.attrib['level'], rootDatini.iter('c')))
print(cLevs)
{'item', 'fonds'}
ts1 = datetime.timestamp(datetime.now())
allCs = {}
for label in cLevs:
def tempFilt(aa: ET.Element):
if(aa.tag=='c' and aa.attrib['level']==label):
return True
else:
return False
allCs[label] = traceElems(rootDatini, tempFilt);
print('# di tag "c", livello ' + label + ', primo passaggio:', len(allCs[label]))
print()
print('Tempo trascorso:', datetime.timestamp(datetime.now()) - ts1)
# di tag "c", livello item, primo passaggio: 685 # di tag "c", livello fonds, primo passaggio: 1 Tempo trascorso: 0.0016021728515625
ts1 = datetime.timestamp(datetime.now())
allCs2 = {}
for label in cLevs:
partial = allCs[label]
print('# di tag "c", livello ' + label + ', primo passaggio:', len(partial))
allCs2[label] = partial
partialUpdate = []
while True:
def tempFilt(aa: ET.Element):
if(aa.tag=='c' and aa.attrib['level']==label):
return True
else:
return False
for node in partial:
partialUpdate = partialUpdate + traceElems(node['child'], tempFilt)
#print(len(partialUpdate))
partial = partialUpdate
if(len(partialUpdate)==0):
break
allCs2[label] = allCs2[label] + partial
partialUpdate = []
print('# di tag "c", livello ' + label + ', totali:', len(allCs2[label]))
print()
print('Tempo trascorso:', datetime.timestamp(datetime.now()) - ts1)
# di tag "c", livello item, primo passaggio: 685 # di tag "c", livello item, totali: 685 # di tag "c", livello fonds, primo passaggio: 1 # di tag "c", livello fonds, totali: 1 Tempo trascorso: 0.024380922317504883
level = 'item'
ii = 25
test = allCs2[level][ii]
print(level+':',ii)
toProc = traceElems(test['child'], isLeafOrC)
for node in toProc:
tags = list(map(lambda a: a.tag, node['a_par'])) + [node['child'].tag]
attributes = list(map(lambda a: a.attrib, node['a_par'])) + [node['child'].attrib]
contents = list(map(lambda a: a.text.replace('\n','').replace('\n','').strip(), node['a_par'])) + [str(node['child'].text).replace('\n','').replace('\n','').strip()]
print(tags)
print(attributes)
print(contents)
print()
item: 25 ['c', 'did', 'materialspec'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'label': 'tipologia'}] ['', '', 'scheda anagrafica'] ['c', 'did', 'repository'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {}] ['', '', 'Archivio di Stato di Prato'] ['c', 'did', 'unitid', 'extref'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'countrycode': 'IT', 'encodinganalog': 'ISAD 1 - 1 reference code', 'repositorycode': 'ASPO'}, {'role': 'id_galileo'}] ['', '', '', 'A46C062A-DC08-4F95-B54E-69E48DDE7AEB'] ['c', 'did', 'unitid', 'emph'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'countrycode': 'IT', 'encodinganalog': 'ISAD 1 - 1 reference code', 'repositorycode': 'ASPO'}, {'altrender': 'numero provvisorio'}] ['', '', '', '10'] ['c', 'did', 'container'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'type': 'registro'}] ['', '', 'VV'] ['c', 'did', 'unittitle', 'persname', 'emph'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'encodinganalog': 'ISAD 1 - 2 title'}, {'role': 'bambino'}, {'altrender': 'nome'}] ['', '', '', '', 'Perla Maria'] ['c', 'did', 'unittitle', 'num'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'encodinganalog': 'ISAD 1 - 2 title'}, {'type': 'matricola'}] ['', '', '', '287'] ['c', 'did', 'unittitle', 'date'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'encodinganalog': 'ISAD 1 - 2 title'}, {'normal': '17820401-17820401', 'type': 'ritrovamento'}] ['', '', '', '01/04/1782'] ['c', 'did', 'unittitle', 'unitdate', 'emph'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'encodinganalog': 'ISAD 1 - 2 title'}, {'normal': '17820401-17820401'}, {}] ['', '', '', '', 'ritrovamento: 01/04/1782'] ['c', 'did', 'physdesc', 'physfacet'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'encodinganalog': 'ISAD 1 - 5 extent and medium of the unit of description'}, {'type': 'descrizione contrassegno'}] ['', '', '', 'Nastro in seta gialla con mezzo\t\t\t\t\t\t\tsoldo in rame.'] ['c', 'note', 'p'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {'encodinganalog': 'ISAD 6 - 1 note'}, {}] ['', '', 'Nota dell\'ospedale: "Si battezzi la bambina di nascita [venuta] per la nostra ferrata alle ore sei incirca della scorsa sera per mano di Rosa vedova di Francesco Galantini di questa citt� quale asser� essere di genitori incerti e del distretto Pistoiese; detta bambina aveva pendente dal collo con un nastro giallo di seta un mezzo soldo di nostra moneta; fu [ricevuta] e mandata al sacro fonte col nome Perla Maria".\tSul medesimo biglietto, in calce: "Ad� 23 aprile 1782. La suddetta bambina consegnata alla Maria Anna di Francesco Toccafondi di S. Lionardo a Vernio".'] ['c', 'daogrp', 'daoloc'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'href': '/GETTATELLI/010_a.jpg', 'title': '010_a.JPG'}] ['', '', 'None'] ['c', 'daogrp', 'daoloc'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'href': '/GETTATELLI/010_a.JPG_1', 'title': '010_a.JPG_1'}] ['', '', 'None'] ['c', 'daogrp', 'daoloc'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'href': '/GETTATELLI/010_b.jpg', 'title': '010_b.JPG'}] ['', '', 'None'] ['c', 'daogrp', 'daoloc'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'href': '/GETTATELLI/010_c.JPG_1', 'title': '010_c.JPG_1'}] ['', '', 'None'] ['c', 'daogrp', 'daoloc'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {'href': '/GETTATELLI/010_c.jpg', 'title': '010_c.JPG'}] ['', '', 'None'] ['c', 'processinfo', 'list', 'item', 'date'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {}, {}, {}] ['', '', '', '', '04-2014'] ['c', 'processinfo', 'list', 'item', 'persname'] [{'audience': 'external', 'id': 'IT-ASPO-GT001-0000026', 'level': 'item'}, {}, {}, {}, {}] ['', '', '', '', 'regesta.exe']
def traduttoreItem(elem):
# Variabile che contiene l'output della traduzione:
csvProt = {}
# Processo i nodi-parent di 'elem'
par_tags = list(map(lambda a: a.tag, elem['a_par']))
par_attributes = list(map(lambda a: a.attrib, elem['a_par']))
# e0: Le varie id dei nodi parent
for ii in indices(par_tags, 'c'):
key = 'id_' + par_attributes[ii]['level']
csvProt[key] = par_attributes[ii]['id']
# Processo i nodi-child di 'elem'
toProc = traceElems(elem['child'], isLeafOrC)
first = True
for node in toProc:
tags = list(map(lambda a: a.tag, node['a_par'])) + [node['child'].tag]
attributes = list(map(lambda a: a.attrib, node['a_par'])) + [node['child'].attrib]
content = node['child'].text
# Da controllare solo per il primo nodo
# (informazioni a livello del nodo, uguali per tutti i figli)
if(first):
# e1 ID della item
csvProt['id'] = attributes[tags.index('c')]['id']
# e2 Audience: external o internal
try:
csvProt['audience'] = attributes[tags.index('c')]['audience']
except:
pass
# e19 Otherlevel
try:
csvProt['altro_livello'] = attributes[tags.index('c')]['otherlevel']
except:
pass
first = False
# La 'ciccia': si processa il contenuto vero e proprio
# e11: Il titolo da unittitle
try:
aa = csvProt['titolo_aspo']
except:
try:
ii = tags.index('unittitle')
try:
csvProt['titolo_aspo'] = str(node['a_par'][ii].text).replace('\t','').replace('\n','').strip()
except:
csvProt['titolo_aspo'] = str(content).replace('\t','').replace('\n','').strip()
except:
pass
# e10 Repository (qui dovrebbe essere sempre l'Archivio di Prato)
if('repository' in tags):
csvProt['repository'] = content
# e4 Tipologia
try:
ii = tags.index('materialspec')
if(attributes[ii]['label']=='tipologia'):
csvProt['tipologia'] = content
except:
pass
# e1 Persona + ruolo
try:
ii = tags.index('emph')
type1 = attributes[ii]['altrender']
if(type1=='cognome'):
csvProt['cognome_bambino'] = content
if(type1=='nome'):
csvProt['nome_bambino'] = content
except:
pass
# e2 Matricola
try:
ii = tags.index('num')
type1 = attributes[ii]['type']
if(type1=='matricola'):
csvProt['matricola'] = content
except:
pass
# e6 Registri Gettatelli
try:
ii = tags.index('container')
type1 = attributes[ii]['type']
if(type1=='registro'):
csvProt['riferimento_registro'] = content
except:
pass
#e7 Id oggetto nella scatola
try:
aa = csvProt['id_oggetto']
except:
try:
ii = tags.index('unitid')
try:
tails = ""
for chi in node['a_par'][ii]:
tails = "" + chi.tail
csvProt['id_oggetto'] = (node['a_par'][ii].text + tails).replace('\t','').replace('\n','').strip()
except:
pass
except:
pass
# e2 Date varie: tutte quelle con 'type' definito
try:
ii = tags.index('date')
key = 'data_' + attributes[ii]['type']
csvProt[key] = content
except:
pass
# e3 Data 1: periodo
if('unitdate' in tags):
csvProt['data_periodo'] = content
# e1 Scope-content head & body
if('scopecontent' in tags):
if('head' in tags):
csvProt['scope-content_head'] = content
else:
if('p' in tags):
csvProt['scope-content_body'] = content
# e8 Supporto fisico
try:
ii = tags.index('physfacet')
if(attributes[ii]['type']=='descrizione contrassegno'):
csvProt['descrizione_contrassegno'] = content
if(attributes[ii]['type']=='conservazione'):
csvProt['conservazione'] = content
except:
pass
# e11 Note
if('note' in tags):
csvProt['nota'] = content
# e13 Oggetto digitale allegato (nome)
# Questo è un campo multiplo; per il momento, salviamo tutto
# su una cella concatenando e separando i campi con una pipe
# '| '
try:
ii = tags.index('daoloc')
out = attributes[ii]['title']
try:
csvProt['oggetto_digitale'] = csvProt['oggetto_digitale'] + ' | ' + out
except:
csvProt['oggetto_digitale'] = out
except:
pass
return csvProt
# Di pari passo alla funzione, definisco un dict contenente tutti gli header;
# servirà per il CSV.
itemHeader = OrderedDict()
# e1 ID dell'entità
itemHeader.update({'id': '<c level="X" id=#>'})
# e2 Audience: external o internal
itemHeader.update({'audience': '<c level="item" audience=#>'})
# e15 Repository (qui dovrebbe essere sempre l'Archivio di Prato)
itemHeader.update({'repository': '<repository>#'})
# e10 Tipologia
itemHeader.update({'tipologia': '<materialspec label="tipologia">#'})
# e11 Persona + ruolo
itemHeader.update(
{'cognome_bambino': '<emph altrender="cognome">#', 'nome_bambino': '<emph altrender="nome">#',})
# e17 Segnatura codice
itemHeader.update({'matricola': '<num type="matricola">#'})
# e12 Registri Gettatelli
itemHeader.update(
{'riferimento_registro':'<container type="registro">#'})
# e7 ID oggetto
itemHeader.update({'id_oggetto': '<unitid>#'})
# e8 Date varie: tutte quelle con 'type' definito
itemHeader.update(
{'data_ritrovamento': '<date type="ritrovamento">#',
'data_nascita': '<date type="nascita">#',
'data_morte': '<date type="morte">#',
'data_ricongiungimento': '<date type="ricongiungimento">#',
'data_adozione': '<date type="adozione">#'})
# e4 Titolo ASPO
itemHeader.update({'titolo_aspo': '<unittitle>#'})
# e7 Data 1: periodo
itemHeader.update({'data_periodo': '<unitdate>#'})
# e14 Supporto fisico
itemHeader.update({'descrizione_contrassegno': '<physfacet type="descrizione contrassegno">#', 'conservazione': '<physfacet type="conservazione">#' })
# e3 Scope content, head & body
itemHeader.update(
{'scope-content_head': '<scopecontent><head>#',
'scope-content_body': '<scopecontent><p>#'})
# Note
itemHeader.update({'nota': '<note>#'})
# e18 Oggetto digitale allegato (nome)
itemHeader.update({'oggetto_digitale': '<daoloc title=#>'})
#0: Le varie id dei nodi parent
itemHeader.update(
{'id_subfonds': '<c level="subfonds" id=#>',
'id_fonds': '<c level="fonds" id=#>',
'id_series': '<c level="series" id=#>',
'id_subseries': '<c level="subseries" id=#>',
'id_file': '<c level="file" id=#>',
'id_otherlevel': '<c level="otherlevel" id=#>',
'id_collection': '<c level="collection" id=#>'})
# Do it! Export del CSV - items.
ts1 = datetime.timestamp(datetime.now())
# Apro il file per l'export
with open(export_dir + "data_item.csv", "w", newline="") as csv_file:
# Definisco la classe-motore per l'export
writer = csv.DictWriter(csv_file, fieldnames=list(itemHeader.keys()))
# Scrivo l'intestazione
writer.writeheader()
# Scrivo la seconda riga, esplicativa
writer.writerow(itemHeader)
# Scrivo gli item tradotti, uno a uno
for ii in range(len(allCs2['item'])):
test = allCs2['item'][ii]
writer.writerow(traduttoreItem(test))
print('Tempo trascorso:', datetime.timestamp(datetime.now()) - ts1)
Tempo trascorso: 0.10759091377258301
ts1 = datetime.timestamp(datetime.now())
fondsKeys = set()
for ii in range(len(allCs2['fonds'])):
test = allCs2['fonds'][ii]
fondsKeys = fondsKeys.union( traduttoreItem(test).keys() )
fondsHeader = OrderedDict()
for key in itemHeader:
if(key in fondsKeys):
fondsHeader[key] = itemHeader[key]
with open(export_dir + "data_fonds.csv", "w", newline="") as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=list(fondsHeader.keys()))
writer.writeheader()
writer.writerow(fondsHeader)
for ii in range(len(allCs2['fonds'])):
test = allCs2['fonds'][ii]
writer.writerow(traduttoreItem(test))
print('Tempo trascorso:', datetime.timestamp(datetime.now()) - ts1)
Tempo trascorso: 0.01978278160095215