From d663884521adc897075422bfbfa55bee4322e7e9 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Sun, 26 Apr 2026 01:31:18 +0200 Subject: [PATCH] Aggiungi script per verificare duplicati di email e numeri di telefono in un file Excel --- .claude/settings.local.json | 9 ++ fix_gaia_excel_.py => fix_gaia_excel.py | 18 +-- verifica_duplicati.py | 151 ++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 .claude/settings.local.json rename fix_gaia_excel_.py => fix_gaia_excel.py (95%) create mode 100644 verifica_duplicati.py diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..42a7949 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(python -c ' *)", + "Bash(python verifica_duplicati.py)", + "Bash(python verifica_duplicati.py \"data/No_Estensioni_Elenco_Volontari_al_2026-04-16_14_31_05.947610.xlsx\")" + ] + } +} diff --git a/fix_gaia_excel_.py b/fix_gaia_excel.py similarity index 95% rename from fix_gaia_excel_.py rename to fix_gaia_excel.py index 7f25e79..c2eb7ec 100644 --- a/fix_gaia_excel_.py +++ b/fix_gaia_excel.py @@ -180,17 +180,17 @@ DESCRIZIONE: 5. Salva un nuovo file con suffisso "_modificato" UTILIZZO: - python fixgaiaexcel.py [opzioni] + python fix_gaia_excel.py [opzioni] ESEMPI: # Estrae la data più vecchia (comportamento predefinito) - python fixgaiaexcel.py "data/report.xlsx" + python fix_gaia_excel.py "data/report.xlsx" # Estrae la data più recente - python fixgaiaexcel.py "data/report.xlsx" --data-recente + python fix_gaia_excel.py "data/report.xlsx" --data-recente # Mostra questo aiuto - python fixgaiaexcel.py --help + python fix_gaia_excel.py --help OPZIONI: --data-recente, --recente @@ -227,11 +227,11 @@ def main(): # Controlla gli argomenti da riga di comando if len(sys.argv) < 2: print("Errore: File Excel non specificato\n") - print("Uso: python fixgaiaexcel.py [opzioni]") + print("Uso: python fix_gaia_excel.py [opzioni]") print("\nEsempi:") - print(" python fixgaiaexcel.py data/report.xlsx") - print(" python fixgaiaexcel.py data/report.xlsx --data-recente") - print("\nPer maggiori informazioni: python fixgaiaexcel.py --help") + print(" python fix_gaia_excel.py data/report.xlsx") + print(" python fix_gaia_excel.py data/report.xlsx --data-recente") + print("\nPer maggiori informazioni: python fix_gaia_excel.py --help") sys.exit(1) # Verifica se è stata richiesta la data più recente @@ -242,7 +242,7 @@ def main(): if not file_args: print("Errore: Nessun file specificato") - print("\nPer maggiori informazioni: python fixgaiaexcel.py --help") + print("\nPer maggiori informazioni: python fix_gaia_excel.py --help") sys.exit(1) file_path = Path(file_args[0]) diff --git a/verifica_duplicati.py b/verifica_duplicati.py new file mode 100644 index 0000000..2e84be2 --- /dev/null +++ b/verifica_duplicati.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +""" +Verifica duplicati di Email e Numeri di Telefono in un file Excel dei soci. +""" + +import sys +import re +import argparse +from collections import defaultdict + +import pandas as pd + + +SEPARATORI_TELEFONO = re.compile(r"[;,/|\n]+") + + +def normalizza_email(email: str) -> str: + return str(email).strip().lower() + + +def normalizza_telefono(n: str) -> str: + """Rimuove spazi interni dal numero (es. '344 503 2906' -> '3445032906').""" + return re.sub(r"\s+", "", n.strip()) + + +def estrai_telefoni(cella: str) -> list[str]: + if not cella or str(cella).strip() in ("", "nan"): + return [] + parti = SEPARATORI_TELEFONO.split(str(cella).strip()) + return [normalizza_telefono(p) for p in parti if p.strip()] + + +def analizza_duplicati(filepath: str, esempi: int = 5): + print(f"\nCaricamento file: {filepath}") + df = pd.read_excel(filepath, dtype=str) + totale_righe = len(df) + print(f"Righe totali: {totale_righe}") + + col_email = "Email" + col_tel = "Numeri di telefono" + + if col_email not in df.columns: + print(f"[ERRORE] Colonna '{col_email}' non trovata. Colonne disponibili: {df.columns.tolist()}") + sys.exit(1) + if col_tel not in df.columns: + print(f"[ERRORE] Colonna '{col_tel}' non trovata. Colonne disponibili: {df.columns.tolist()}") + sys.exit(1) + + # --- EMAIL --- + email_occorrenze: dict[str, list[int]] = defaultdict(list) + email_vuote = 0 + + for idx, val in df[col_email].items(): + if pd.isna(val) or str(val).strip() == "": + email_vuote += 1 + continue + norm = normalizza_email(val) + email_occorrenze[norm].append(idx + 2) # +2: header riga 1, indice 0-based + + email_duplicate = {e: righe for e, righe in email_occorrenze.items() if len(righe) > 1} + email_uniche = len(email_occorrenze) + totale_email_duplicate = sum(len(r) - 1 for r in email_duplicate.values()) + + # --- TELEFONI --- + tel_occorrenze: dict[str, list[int]] = defaultdict(list) + tel_vuote = 0 + totale_numeri_estratti = 0 + + for idx, val in df[col_tel].items(): + if pd.isna(val) or str(val).strip() == "": + tel_vuote += 1 + continue + numeri = estrai_telefoni(val) + totale_numeri_estratti += len(numeri) + for n in numeri: + tel_occorrenze[n].append(idx + 2) + + tel_duplicate = {t: righe for t, righe in tel_occorrenze.items() if len(righe) > 1} + tel_uniche = len(tel_occorrenze) + totale_tel_duplicate = sum(len(r) - 1 for r in tel_duplicate.values()) + + # --- STAMPA REPORT --- + sep = "=" * 60 + + print(f"\n{sep}") + print(" REPORT DUPLICATI - EMAIL") + print(sep) + print(f" Righe totali : {totale_righe}") + print(f" Righe senza email : {email_vuote}") + print(f" Email univoche : {email_uniche}") + print(f" Email con duplicati : {len(email_duplicate)}") + print(f" Occorrenze duplicate extra : {totale_email_duplicate}") + + top_email = sorted(email_duplicate.items(), key=lambda x: len(x[1]), reverse=True) if email_duplicate else [] + + if top_email: + n = min(esempi, len(top_email)) + print(f"\n Top {n} email piu' duplicate:") + print(f" {'#':<4} {'Email':<40} {'Volte':>6} Righe Excel (prime 6)") + print(" " + "-" * 72) + for i, (email, righe) in enumerate(top_email[:n], 1): + righe_str = str(righe[:6])[1:-1] + ("..." if len(righe) > 6 else "") + print(f" {i:<4} {email:<40} {len(righe):>6} {righe_str}") + else: + print("\n Nessun duplicato trovato.") + + print(f"\n{sep}") + print(" REPORT DUPLICATI - NUMERI DI TELEFONO") + print(sep) + print(f" Righe totali : {totale_righe}") + print(f" Righe senza telefono : {tel_vuote}") + print(f" Numeri totali estratti : {totale_numeri_estratti}") + print(f" Numeri univoci : {tel_uniche}") + print(f" Numeri con duplicati : {len(tel_duplicate)}") + print(f" Occorrenze duplicate extra : {totale_tel_duplicate}") + + top_tel = sorted(tel_duplicate.items(), key=lambda x: len(x[1]), reverse=True) if tel_duplicate else [] + + if top_tel: + n = min(esempi, len(top_tel)) + print(f"\n Top {n} numeri piu' duplicati:") + print(f" {'#':<4} {'Numero':<20} {'Volte':>6} Righe Excel (prime 6)") + print(" " + "-" * 60) + for i, (tel, righe) in enumerate(top_tel[:n], 1): + righe_str = str(righe[:6])[1:-1] + ("..." if len(righe) > 6 else "") + print(f" {i:<4} {tel:<20} {len(righe):>6} {righe_str}") + else: + print("\n Nessun duplicato trovato.") + + print(f"\n{sep}") + print(" RIEPILOGO GENERALE") + print(sep) + pct_email = len(email_duplicate) / email_uniche * 100 if email_uniche else 0 + pct_tel = len(tel_duplicate) / tel_uniche * 100 if tel_uniche else 0 + print(f" Email duplicate : {len(email_duplicate):>5} ({pct_email:.1f}% delle email univoche)") + print(f" Telefoni duplicati : {len(tel_duplicate):>5} ({pct_tel:.1f}% dei numeri univoci)") + print(sep) + + +def main(): + parser = argparse.ArgumentParser(description="Verifica duplicati email e telefoni in un file Excel.") + parser.add_argument("file", + help="Percorso del file Excel da analizzare") + parser.add_argument("--esempi", type=int, default=5, + help="Numero di esempi da mostrare per categoria (default: 5)") + args = parser.parse_args() + analizza_duplicati(args.file, args.esempi) + + +if __name__ == "__main__": + main()