# %% import csv import json import openpyxl as op import re # BASIC CONFIGURATION DATA_FOLDER = './data/' OUTPUT_FOLDER = './output/' ONTO_FILENAME = 'manoscritti_dariah' # No extension! ent_filename = ONTO_FILENAME + '_entities.csv' rel_filename = ONTO_FILENAME + '_relations.csv' # PART I: parse xlsx to (multiple) csv # Excel configuration XLSX_FILENAME = 'Struttura_NEW.xlsx' ENTITIES_SHEETNAME = 'Entità' RELATIONS_SHEETNAME = 'Relazioni' # %% # Import the defining xlsx through openpyxl input_data = op.load_workbook(DATA_FOLDER + XLSX_FILENAME) # Read relevant sheets entities_sheet = input_data[ENTITIES_SHEETNAME] relations_sheet = input_data[RELATIONS_SHEETNAME] # Parse sheet data into a dict (assuming the xlsx has headers) entities_keys = [cell for cell in next(entities_sheet.values) if cell] raw_entities = [{key: row[ind] for ind, key in enumerate(entities_keys)} for row in entities_sheet.values][1:] # relations_keys = [cell for cell in next(relations_sheet.values) if cell] raw_relations = [{key: row[ind] for ind, key in enumerate(relations_keys)} for row in relations_sheet.values][1:] # %% # NOTE: # a. Non ci sono, al momento, constraint di unicità per le relazioni imposti tramite il "foglio master" # b. Non ci sono neanche constraint di esistenza, TRANNE l'id univoca, intesa NON come quella del sistema ma quella della comunità di riferimento, e tipica del settore di dominio considerato # c. Per identificare le informazioni 'atomiche' non si usa un campo dedicato, ma una logica. AL MOMENTO la logica è che si considera atomica una entità che non è mai 'prima' in una relazione. L'ORDINE DELLE RELAZIONI E' IMPORTANTE a differenza di quanto assumevo inizialmente. # d. Si effettua un controllo di unicità sulle entità, basato sul nome normalizzato (parole con iniziale maiuscola e il resto minuscolo, spazi ridotti a spazio singolo, strip()). Nessuna entità può avere nome vuoto. # e. Si effettua un controllo di unicità sulle relazioni, che però riguarda tutta la terna SOGGETTO-RELAZIONE-OGGETTO (normalizzata in modo simile ai nomi di entità, ma nella RELAZIONE gli spazi sono underscores e si usa lower() invece che title()). Nessuno dei membri della terna può essere vuoto; il nome della relazione inversa è opzionale. # f. Si effettuano controlli di consistenza sulle relazioni: # .f1. Nessuna relazione con entità non definite # .f2. Nessuna entità "orfana", ovvero non presente in alcuna relazione # # TODO: completare secondo le specifiche sopra # TODO: effettuare il merge con i "miei" CSV, che hanno informazioni in più! # TODO: ottimizzare un po' la scrittura del codice # Process entities: # 1. Filter out unnamed entities, normalize entity names, collect aliases, discover duplicates clean_entities = {} for ent in raw_entities: entity_names = ent['Concetto'] if not isinstance(entity_names, str): continue aliases = [re.sub(r'\s+', ' ', al.strip().title()) for al in entity_names.split('\n') if al.strip()] if not aliases: continue entity_name = aliases[0] entity_same_as = aliases[1:] if clean_entities.get(entity_name): # DUPLICATE! clean_entities[entity_name].append({'Alias': aliases, 'Raw': ent}) else: clean_entities[entity_name] = [{'Alias': aliases, 'Raw': ent}] all_entities = clean_entities.keys() duplicated_entities = [ent_name for ent_name, ent_val in clean_entities.items() if len(ent_val)>1] # %% # Process relations: # 1. Filter ill-formed relations and normalize entity names clean_relations = [] for rel in raw_relations: subj = rel['Soggetto'] obj = rel['Oggetto'] if not isinstance(subj, str) or not isinstance(obj, str): continue subj = re.sub(r'\s+', ' ', subj.strip().title()) obj = re.sub(r'\s+', ' ', obj.strip().title()) if subj==obj: continue rel_name = rel['Relazione'] if isinstance(rel_name, str): rel_name = re.sub(r'\s+', '_', rel_name.strip().lower()).replace('__', '_') clean_rel = {'Soggetto': subj, 'Relazione': rel_name, 'Oggetto': obj} clean_relations.append(clean_rel) all_rels = set((rel['Soggetto'], rel['Relazione'], rel['Oggetto']) for rel in clean_relations) all_subjects = set(rel['Soggetto'] for rel in clean_relations) all_cited_entities = set(sum([[rel['Soggetto'], rel['Oggetto']] for rel in clean_relations], [])) undefined_entities = all_cited_entities - all_entities unused_entities = all_entities - all_cited_entities atomic_entities = all_cited_entities - all_subjects problematic_entities = undefined_entities - atomic_entities # %% #### # MANUS ONLINE (MOL) API: https://api.iccu.sbn.it/devportal/apis #### # %% for ent in sorted(list(problematic_entities)): print(ent) # %% for ent in sorted(list(atomic_entities)): print(ent) # %% for ent in sorted(list(unused_entities)): print(ent) # %% for ent in sorted(list(undefined_entities)): print(ent) # %%