i \ddlZddlZeejdrejddeejdrejddddlZddlmZddlZddl Z ddl m Z ddl Z ddl Z ddlZddlmZmZmZddlZddlZddlZddlZd Ze Gd d Zd ed efdZd ed edeefdZd ed ededeefdZd ed ededeefdZ e!fdede"ej#eeffdZ$dej%dej%fdZ&dZ'd,dZ(dZ)dZ*d-dZ+dZ,dZ-dde!fd ed!ed"ed#e.fd$Z/d%Z0d&Z1Gd'd(ej2Z3d)Z4e5d*kr@e6ej7d+kr e4dSe3Z8e89dSdS).N reconfigurezutf-8replace)encodingerrors)datetime) dataclass)ttk filedialog scrolledtextzdbcomparer_config.jsonceZdZUdZeed<eed<eed<eeezed<edeefdZedefdZ d S) DbConfigz*Configurazione per una singola fonte dati.db_typepathtable index_colreturncTt|jtr|jn|jgSN) isinstancerlistselfs c:\PYTHON/DbComparer\main.py index_colszDbConfig.index_cols$s$!+DND!A!AWt~~GWWc6d|jS)N, )joinrrs r index_labelzDbConfig.index_label(syy)))rN) __name__ __module__ __qualname____doc__str__annotations__rpropertyrrrrr r s44 LLL III JJJT#Y XDIXXXXX*S***X***rr rdb_pathctj|std||dkrt j|S|dkrd|zdz}t j|Std|)z?Crea e restituisce una connessione al database in base al tipo.zFile database non trovato: sqliteaccessz6DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=;z!Tipo di database non supportato: )osrexistsFileNotFoundErrorsqlite3connectpyodbc ValueError)rr(conn_strs rget_db_connectionr5-s 7>>' " "I Gg G GHHH(w'''(   # $ ~h''' BBB C CCrrc|rtj|sgS t||5}|}|dkr4|dd|D}n&|dkr d|dD}|cdddS#1swxYwYdS#t$r!}td |d |gcYd}~Sd}~wwxYw) z0Restituisce un elenco di tabelle da un database.r*z2SELECT name FROM sqlite_master WHERE type='table';cg|] }|d Srr'.0rows r z&get_tables_from_db..Gs>>>S#a&>>>rr+cg|] }|j Sr') table_namer9s rr<z&get_tables_from_db..IsUUUS#.UUUrTABLE) tableTypeNz!ERRORE nel leggere le tabelle da : ) r-rr.r5cursorexecutefetchalltables Exceptionprint)rr(connrBrEes rget_tables_from_dbrJ>sb "'..11   w 0 0 D[[]]F(""STTT>>FOO,=,=>>>H$$UUFMMGM4T4TUUU                    @'@@Q@@AAA sACA6B8+ C8B<<C?B<C C0C+%C0+C0r>c`|r!tj|r|sgS t||5}|}|dkr\|d|dd|D}dt|DcdddS|dkrt| |d }d |D}d |D}|r|n|} i} | D]:} | j | vr g| | j <| | j  | j ;| r3t| d cdddSdddn #1swxYwYn,#t$r} td |d| Yd} ~ nd} ~ wwxYwgS)znRileva automaticamente la chiave primaria di una tabella. Restituisce lista di colonne (vuota se non trovata).r*zPRAGMA table_info([z]);cFg|]}|ddk|d|dfS)rr'r9s rr<z#get_primary_key..Ys0WWWCPQFUVJJCFCF+JJJrcg|]\}}|Sr'r')r:_cols rr<z#get_primary_key..Zs:::3:::rNr+T)uniquecDg|]}|j |jdk|j|Sr8) index_name non_unique column_namer:rs rr<z#get_primary_key..^s4cccalcq|q?P?PUVUb?P1?P?P?PrcLg|]!}|jdk|"S) primarykey)rTlowerrWs rr<z#get_primary_key.._s0VVV1C1C1E1E1U1U11U1U1Urrz#Avviso: impossibile rilevare PK da rA)r-rr.r5rBrCrDsortedr statisticsrTappendrVvaluesrFrG) rr(r>rHrBpk_colsrowspk_rowsprimary candidatesseenrXrIs rget_primary_keyrfPst "'..11 G w 0 0 2D[[]]F(""DZDDDEEEWWv7H7HWWW::&//:::  2 2 2 2 2 2 2 2 H$$F--j-FFGGccdcccVVgVVV(/F F+ F&&F+c |r!tj|r|sgS t||5}|}|dkr8|d|dd|D}n~|dkrx |d|dd|jD}nL#t$r?}td |d |d | | D}Yd }~nd }~wwxYw|cd d d S#1swxYwYd S#t$r$}td|d|d|gcYd }~Sd }~wwxYw)z:Restituisce un elenco di colonne da una tabella specifica.r*zPRAGMA table_info(z);cg|] }|d S)rNr'r9s rr<z*get_columns_from_table..vs???c3q6???rr+zSELECT TOP 1 * FROM []cg|] }|d Sr8r')r:columns rr<z*get_columns_from_table..|sJJJVvayJJJrzAvviso: query fallita per z, uso cursor.columns(): cg|] }|j Sr')rVr9s rr<z*get_columns_from_table..s[[[3s[[[r)rNz!ERRORE nel leggere le colonne da z in rA) r-rr.r5rBrCrD descriptionrFrGcolumns)rr(r>rHrBrne_queryrIs rget_columns_from_tablerpms "'..11  w 0 0 D[[]]F(""BJBBBCCC??V__->->???H$$\NN#H:#H#H#HIIIJJv7IJJJGG \\\dzdd[bddeee[[&..z.:Z:Z[[[GGGGGG\                    P*PP'PPQPPQQQ skD#AD*B<;D< D5D;DDD D#DD#DD## E-E E Econfigc |js*td|jd|js*td|jd|js*td|jd|d|jd|jd|jd  t |j|j5}tj5tj d ttj d |jd |d d d n #1swxYwYd d d n #1swxYwYnl#tj jjt jt$jf$r9}t'd|jd|jd|d }~wwxYw|dt)d|jdfd|jD}|rItd|d|jd|jdt-jt)|jdkr|jn |jddt3jtjr@tjdjDjj_n;jt>j _t-jfS)zV Legge i dati da una fonte (SQLite o Access) e li restituisce come DataFrame. zPercorso database z non specificatoz Nome tabella zColonna indice z non specificatazLettura dati da rAz (Tabella: )ignorezSELECT * FROM [riNz Impossibile leggere la tabella 'z' da zTrovati z record in .c&g|] }|jv |Sr')rn)r:cdfs rr<zget_data..s%CCCQq /B/Bq/B/B/BrzColonne indice z non trovate nella tabella 'z' del database z. Colonne disponibili: rNrT)inplacec@g|]}td|DS)c3XK|]%}t|V&dSr)r$stripr:vs r z&get_data...s0//a3q66<<>>//////r)tuple)r:idxs rr<zget_data..s/ D D DCU//3/// / / D D Dr)names)!rr3rupperrrr5warningscatch_warnings simplefilter UserWarningpdread_sql_queryiosql DatabaseErrorr2Errorr0ConnectionErrorlenrrrn set_indexrindex MultiIndex from_tuplesrastyper$r|)rqloggerrHrImissingrxs @rget_datars ;XVfn.B.B.D.DVVVWWW <SQ)=)=)?)?QQQRRR  US6>+?+?+A+ASSSTTT F _fn2244 _ _ _ _PVP\ _ _ _```s v~v{ ; ; Pt(** P P%h <<<&'H'H'H'H$OO P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P EI #V\7= AsssqqqTZTbThThTjTjqqnoqqrrrs F 9c"gg 9 9&, 9 9 9:::CCCC&+CCCG  7g 7 76< 7 7`f`n`t`t`v`v 7 7$($4$4 7 7   LLc&*;&<&   0u... [TRONCATO])  )risnar$rr)value str_valueproblematic_charschars r_sanitize_for_reportrs }}xE ""4--55dC@@I 9~~fuf%(88 ))) "11%%dC00 rc|tj|rdS t|}n#t$rYdSwxYw|dkrdS|O|t|kr*|d|d}t |dkr |ddd zS|St |d kr |dd d zS|S) zNRappresenta un valore nel report mostrando il valore originale in modo chiaro.Nrzrzz (norm: 'z')iarr)rrr$r|rFr)original_valuenormalized_valueorig_strrs r_represent_value_for_reportrs!8!8x&~&&,,.. &&&%%%&2~~y#C8H4I4I4O4O4Q4Q(Q(Q;;'7;;; v;;  FUF#&66 6  8}}u"222 Os!< A  A c hd}d}||}||}||dS|||vrdS|||vrdS||vr||vrdSdS#t$rYdSwxYw)zHVerifica se due valori rappresentano entrambi l'assenza di informazione.>N-----n/a#n/a#null not availablenot applicableto be determinedrnaniltbdnonenullvoidunknown undefinedctj|s|dSt|Sr)rrr$r[r|vals rnormalize_for_null_checkz7_are_null_equivalents..normalize_for_null_checks=ws|| s{ts88>>##))++ +rNTF)rF)val1val2null_equivalentsr norm_val1 norm_val2s r_are_null_equivalentsrs     , , , -,T22 ,,T22  !24  .>!>!>4  .>!>!>4 ( ( (Y:J-J-J4u uus !AAAA AAc tj|stj|s||dSt| t| krdSgd}t fd|D}t fd|D}|r|sdSgd}d}d}|D])} t j |}n#t$rY&wxYw|D])} t j |}n#t$rY&wxYw|r,|r*||kSdS#t$rYdSwxYw)zGVerifica se due valori rappresentano la stessa data in formati diversi.NFT)z^\d{1,2}/\d{1,2}/\d{4}$z^\d{4}-\d{1,2}-\d{1,2}z^\d{1,2}-\d{1,2}-\d{4}c3BK|]}tj|VdSrrematch)r:patternstr1s rrz(_are_dates_equivalent../LL7rx..LLLLLLrc3BK|]}tj|VdSrr)r:rstr2s rrz(_are_dates_equivalent.. rr)z%d/%m/%Yz%m/%d/%Yz%Y-%m-%dz%Y-%m-%d %H:%M:%Sz%d-%m-%Yz%m-%d-%Y) rrr$r|anyrstrptimer3daterF) rr date_patternsis_date1is_date2 date_formatsdate1date2fmtrrs @@r_are_dates_equivalentr s3 74== BGDMM T\T\54yy  4yy   4<<4   LLLLmLLLLLLLLLmLLLLL X 5     C  )$44       C  )$44      0U 0::<<5::<</ /u uusk,E AE <>E < E C E C-*E ,C--E 5D  E DE D0E EE& .>c tj|d}tj|d}t||z |kS#ttf$rYdSwxYw)zLVerifica se due valori numerici sono equivalenti entro una certa tolleranza.raise)rF)r to_numericabsr3 TypeError)rr tolerancenum1num2s r_are_numbers_equivalentrEsm}T'222}T'2224$;)++  "uusAAAAc: tj|stj|s||dSt|}t|}||krdSt |t |kr(t |dkr||St |t |kr(t |dkr||SdS#t $rYdSwxYw)zQVerifica se due testi sono sostanzialmente uguali (uno potrebbe essere troncato).NFT2)rrr$r|r startswithrF)rrrrs r_are_texts_substantially_equalr Os 74== BGDMM T\T\54yy  4yy   4<<4 t99s4yy SYY^^??4(( ( YYT " "s4yy2~~??4(( (u uus%,D AD :AD AD DDc|duptj|}|duptj|}|r|rdS|r|sdS|s|rdSt|}t|}||krdSdS)z2Classifica il tipo di differenza per il reporting.N BOTH_NULL NULL_vs_VALUE VALUE_vs_NULLWHITESPACE_DIFF VALUE_DIFF)rrr$r|)rr val1_norm val2_norm val1_is_null val2_is_nullval1_strval2_strs r_classify_differenceres4<0274==L4<0274==L   {   l     l  t99??$$t99??$$ x  $$zd?|-d.d@|-d/t9|-dA|jdBdd6}.t9|-dA|jdBdd6}/|dC|.t |.d6krdDndB|dE|/t |/d6krdDndB t#j$|}0|dF|0j%|dGt |0j&|0d/'(}1d/dHg|1_&t |0}2|1dH|2z d6z)dI|1dJ<|1dJ*t8dKz|1dJ<tWj,-dL}3dM}4t |0}5|5|4kre|dN|5dOdP|4dOdQ|dRdS|jdT|j.dU|jdT|j.dT|3dV }6|0/|6d!dWX|dY|6dZ|jdT|j.dU|jdT|j.dT|3d[ }7 t#j0|7d\]5}8d^|jd1d_|jd1d`da|jd1db|jd1dcddde|jd1df|jd1g t | t | t | t | t | t |t |t |t |g dg}9t#j$|91|8dhd!i|1j2s|11|8djd!i|s|rg}:Ffdk|D};|;D]Q}<|:!dl|jd1||>1|8drd!idddn #1swxYwY|ds|7n#tD$r}?|dt|?Yd}?~?nd}?~?wwxYwdS|jdT|j.dU|jdT|j.dT|3d[ }@ t#j0|@d\]5}8|01|8dud!id^|jd1d_|jd1d`da|jd1db|jd1dcddde|jd1df|jd1g t | t | t | t | t | t |t |t |t |g dg}9t#j$|91|8dhd!i|1j2s|11|8djd!i|s|rg}:Ffdv|D};|;D]Q}<|:!dl|jd1||>1|8drd!idddn #1swxYwY|dx|@n#tD$r}A|dy|A|dzdS|jdT|j.dU|jdT|j.dT|3dV }6 |0/|6d!dWX|d{|6n0#tD$r#}B|d||B|d}Yd}B~Bnd}B~BwwxYwYd}A~And}A~AwwxYwdSdSdSdS#tD$r$}C|d~|C|dYd}C~CdSd}C~CwwxYw|ddS#tfthtjtDf$r}D|d|DdYd}D~DdSd}D~DwwxYw)z;Esegue la logica di confronto completa e logga i risultati.z# --- RIEPILOGO CONTEGGIO RECORD ---zRecord Totali in SQLite: zRecord Totali in Access: zRecord in Comune: zRecord SOLO in SQLite: z Esempi: NrMzRecord SOLO in Access: z2 --- COSTRUZIONE MAPPA DI CONFRONTO AUTOMATICA ---c8i|]}||Sr'r[r:rws r z"run_comparison..">>>QAGGIIq>>>rc8i|]}||Sr'r r!s rr"z"run_comparison..r#rc.i|]}||Sr'r')r:c_normaccess_cols_normsqlite_cols_norms rr"z"run_comparison..s(lllQW*624DV4LlllrTrovate z) colonne comuni con mappatura automatica.z$Applicazione di mappature manuali...z - Mappato manualmente: 'z' -> ''c6h|]}|Sr'r r!s r z!run_comparison..s ?i?i?ia ?i?i?irc6h|]}|Sr'r r!s rr,z!run_comparison..s>mZmZmZ{|mnmtmtmvmvmZmZmZrc6h|]}|Sr'r r!s rr,z!run_comparison..s ?k?k?ka ?k?k?krc6h|]}|Sr'r r!s rr,z!run_comparison..s>o\o\o\}~opovovoxoxo\o\o\rc g|] }| Sr'r'r:rwr(s rr<z"run_comparison..!U!U!U!"21"5!U!U!Urz - Colonne SOLO in SQLite: c g|] }| Sr'r'r:rwr's rr<z"run_comparison..r2rz - Colonne SOLO in Access: z@ --- CONFRONTO CAMPO PER CAMPO (SUI RECORD E COLONNE COMUNI) ---z=Nessun record in comune. Salto del confronto campo per campo.z Confronto: 'z' (SQLite) vs 'z ' (Access)...F)rignore_null_equivalentsTignore_date_formatsignore_number_precisionignore_text_truncationz Ignorate zA differenze di formato (date/numeri/troncamenti/null equivalenti)z -> TROVATE z DISCREPANZE!z | c34K|]}t|VdSr)r$r}s rrz!run_comparison..s(/J/J1A/J/J/J/J/J/JrIDColonnaz Valore_DB1 (rsz Valore_DB2 (z [!!!] ERRORE INTERNO per ID rAz z -> OK. Nessuna discrepanza.z --- RILEVATE z DISCREPANZE! ---zPrime 3 discrepanze:z rNz. ID=z , Colonna=Valore_rz SQLite=...z Access=zDataFrame creato con shape: zColonne nel DataFrame: Numero_Discrepanze Percentuale%z %Y%m%d_%H%M%Siz ATTENZIONE: ,z) discrepanze superano il limite Excel di z righe.z%Salvataggio diretto in formato CSV... confronto_rP_vs_z.csvz utf-8-sig)rrz%Report completo salvato come CSV in: riepilogo_z.xlsxopenpyxl)enginezRecord Totali DB1 (zRecord Totali DB2 (zRecord in ComunezRecord Solo DB1 (zRecord Solo DB2 (zDiscrepanze TrovatezColonne MappatezColonne Solo DB1 (zColonne Solo DB2 ()MetricaValore Riepilogo) sheet_namerDiscrepanze_per_Colonnac g|] }| Sr'r'r1s rr<z"run_comparison..K3g3g3gA4DQ4G3g3g3grzDB1 (zNon presente in DB2 ()Databaser;Statoc g|] }| Sr'r'r4s rr<z"run_comparison..RrQrzDB2 (zNon presente in DB1 (Colonne_Non_Mappatez$File Excel di riepilogo salvato in: z'ERRORE nel salvare il riepilogo Excel: Discrepanzec g|] }| Sr'r'r1s rr<z"run_comparison..rQrc g|] }| Sr'r'r4s rr<z"run_comparison..rQrz%Report delle discrepanze salvato in: z"ERRORE nel salvare il file Excel: z*Tentativo di salvataggio in formato CSV...zReport salvato come CSV in: z!ERRORE anche nel salvare il CSV: zSalvataggio report fallito.z&ERRORE nella creazione del DataFrame: zImpossibile creare il report.z6 --- NESSUNA DISCREPANZA TROVATA! CONGRATULAZIONI! ---z" !!! ERRORE DURANTE L'ESECUZIONE: z !!!)6rsetr intersection differencerrkeysrdiscardr[r_loccopyitemsrrrSeriesgetrrrr sumrrrr$rrrrr^rF enumerate DataFrameshapern value_counts reset_indexroundrrnowstrftimerto_csv ExcelWriterto_excelemptyr/r3r)Grrrrr df_sqlite sqlite_cols df_access access_cols sqlite_ids access_ids common_idsonly_in_sqliteonly_in_accesscommon_cols_normrwcolumn_mapping sqlite_col access_colunmatched_sqlite_normunmatched_access_normoriginal_case_cols discrepanciesdf_common_sqlitedf_common_accesss1s2s1_norms2_norm diff_masksmart_exceptionsdifferent_indices smart_optsrrr record_id val1_orig val2_origrrid_strdiscrepancy_recorde_inneridisc sqlite_val access_val df_report column_countstotal_discrepancies timestampEXCEL_MAX_ROWSnum_rowscsv_path summary_pathwriter summary_data unmapped_datasqlite_only_colsrQaccess_only_cols df_unmapped summary_error report_path excel_error csv_errordf_errorrIr'r(sG @@rrun_comparisonrzs;y>!)-!@!@ ;!)-!@!@ ; 5666)) )) ,,Z88 #..z::#..z::<3z??<<===<3z??<<===5C OO55666>^)<)<>>??? J66"ItN/C/CBQB/G"I"IJJJ>^)<)<>>??? J66"ItN/C/CBQB/G"I"IJJJ DEEE>>+>>>>>+>>>/446677DDSIYI^I^I`I`EaEabb) 0 0A  $ $QWWYY / / / /) 0 0A  $ $QWWYY / / / /lllll[klllX#n--XXXYYY  Y F9 : : :*9 Y Y& JY*Y1;N:.FW WW*WWWXXX #$4$9$9$;$; < CCEE (}T*-=-=>CCEE *8*>*>*@*@F <F <& JZjZZZZZ[[[%j1%j1+B//+B//#w. ==??J')yio'N'N'N$(1)(<(B%!.!4"J0%%%'VC["&+d%>>*CTJJ%OdeikoOpOp%8<,05$%>>*?FF%K`aegkKlKl%8<,05$%>>*CTJJ%OfgkmqOrOr%8<,05$%>>*BDII%NlmqswNxNx%8<,05$ )-=,= =I'++--J I/?/C/C/E/E I I IJJJ==??<FI9==??IIIJJJ%.y%9%? (*y(9I(*y(9I(/ I(>I(/ I(>IOYYbdiNjNj%~UZZ/J/J /J/J/J%J%J%Jpst}p~p~F $&:6&B&B )+? +K+K O}/D/J/J/L/L O O OQegBCLNWgXgXRYRY O}/D/J/J/L/L O O OQegBCLNWgXgXRYRY 2.*001CDDDD("F#\i#\#\SZ#\#\]]])00 $c)nn )3z?? O}/D/J/J/L/L O O OQr]`ah]i]ijnknjn]oQrQrQr O}/D/J/J/L/L O O OQr]`ah]i]ijnknjn]oQrQrQr 2%6F:;;;; r N FJS%7%7JJJ K K K F) * * *$]2A2%677 ^ ^4UAaCUUdhhtnnUU@S@SUUVVV *SM4I4O4O4Q4Q*S*SUW!X!XYYZ^[^Z^_  *SM4I4O4O4Q4Q*S*SUW!X!XYYZ^[^Z^_ \z\C OOsR0SVi0ilo0o/v/vwx/y/y m,/<]/K/R/RSV/W/WZ]/] m,$LNN33ODD ")y>>~--FzHzzzaozzzz{{{FBCCC [M,A [ [MDW [ []j]r [ [vCvI [ [LU [ [ [H$$XU[$QQQFM8MMNNN$` 0E$`$` H[$`$`anav$`$`zGzM$`$`PY$`$`$`L8Z^LLLL3pPV%[-:O:U:U:W:W$Z$Z$Z$Z-:O:U:U:W:W$Z$Z$Z$6$X 8M8S8S8U8U$X$X$X$X 8M8S8S8U8U$X$X$X$9$5$Y9N9T9T9V9V$Y$Y$Y$Y9N9T9T9V9V$Y$Y$Y ,"%( OO$' OO$' OO$'$7$7$'$7$7$' $6$6$'$7$7$'(=$>$>$'(=$>$> +",,L0L66??S^fk?lll$1#6r - 6 6vJckp 6 q q q 5p8Mp02 3g3g3g3gQf3g3g3g 0+;!'!'C$1$8$84\M$>$'(=$>$> +",,L0L66??S^fk?lll$1#6r - 6 6vJckp 6 q q q 5p8Mp02 4h3g3g3gQf3g3g3g 0+;!'!'C$1$8$84\M>>spW5AF!:D\AF! _"B0_AF!_E,AF! F$AE#/vI0v5 vvvv vAE# v?&v:4AE#:v??)AE#)AB-?JABB AB-BABBAB-BABBAB-B,AE#B- AEB7?AEC7&ADDAED AE D(AEEAEEAE E AEEAE#EAEEAE#E# AFE-AF FAF!F AFFAF!F!AGF>AGGAGc ttd5}tj||dddddS#1swxYwYdS#t$r}t d|Yd}~dSd}~wwxYw)z1Salva la configurazione dell'app su un file JSON.w)indentNz4Errore durante il salvataggio della configurazione: )open CONFIG_FILEjsondumprFrG) config_datafrIs rsave_app_configrsJ +s # # 0q Ik1Q / / / / 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 JJJ HQHHIIIIIIIIIJs7A < A AA AA A0A++A0c,tjtsiS t td5}t j|cdddS#1swxYwYdS#t$r}td|icYd}~Sd}~wwxYw)z2Carica la configurazione dell'app da un file JSON.rXNz4Errore durante il caricamento della configurazione: ) r-rr.rrrloadrFrG)rrIs rload_app_configrs 7>>+ & &  +s # # q9Q<<                   HQHHIII s@A+A A+A""A+%A"&A++ B5BBBcbeZdZfdZdZdZdZddZdZdZ d Z d Z d Z d Z d ZxZS)Appc t|d|dt j|d}|tjdt j |dd|_ |j tj d tj d |_ tj |_tj |_tj |_||j d |j |j|j|jt j |dd|_|jtj d tj d |_tj |_tj |_tj |_||jd|j|j|j|jt j |dd|_|jtj d tjd |_tjd |_tjd |_tjd |_t j|jd|jtjt j|jd|jtjt j|jd|jtjt j|jd|jtjt j |dd|_|jtj d t j|j|_ |j tj dt j!|jd|j"d g|_#t j!|d|j$|_%|j%dtMj'|tj(d|_)|j)tjd|*|+d |j,dS)!Nz7DbComparer - Versione Finale con Confronto Intelligente900x70010)paddingT)fillexpandz Database 1)textrrMrpadyr*)rdb1z Database 2r+db2zOpzioni Confronto IntelligenteuCIgnora differenze di formato delle date (30/05/2021 ≈ 2021-05-30))rvariable)anchoru8Ignora differenze di precisione numerica (1.0 ≈ 1.000)z"Ignora troncamenti di testo lunghiu5Ignora valori null equivalenti (NULL ≈ N/A ≈ TBD)zMappatura Manuale ColonnezAggiungi Mappaturarcommand)rzConfronta Database )wrapheightWM_DELETE_WINDOW)-super__init__titlegeometryr FramepacktkBOTH LabelFrame db1_frameX StringVardb1_typedb1_path db1_table db1_indexcreate_db_widgets db2_framedb2_typedb2_path db2_table db2_indexsmart_comparison_frame BooleanVarr6r7r8r5 CheckbuttonW mapping_framemanual_mappings_containerButtonadd_mapping_rowrstart_comparisoncompare_buttonr ScrolledTextWORD output_textload_initial_configprotocol on_closing)r main_frame __class__s rrz App.__init__sY  LMMM i   YtT222 RWT222 tTTT A... 8444    t~udmT]TXTbdhdrsss tTTT A... 8444    t~udmT]TXTbdhdrsss'*nZFfpt&u&u&u# #((bd(;;;#%=t#<#<#< ')}4'@'@'@$&(m$&?&?&?#')}4'@'@'@$ 3a $ 8 : : ::>$bd$:K:K:K 3V $ < > > >>Bd"$d>O>O>O 3@ $ ; = = ==ATT=N=N=N 3S $ < > > >>Bd"$d>O>O>O!^J=Xbfggg RT222),43E)F)F& &++d+CCC 4%,@$J^___ddjkdlll!"j:NX\Xmnnn   b )))'4ZbgVXYYY 274888   """ ($/:::::rc tjdddtjddtjddgd d }|dd tjd |_tjdd dtjddtjdd d tjtj dfdd ddtjdddtjddtjd }|dd tj|_ | dfdtjdddtjddtj} | dd tj| _  d d dS)NzTipo:rrrMrB)r;rkstickypadxrr*r+readonlyr) textvariabler_statewidthrN)r;rkrrzFile DB:<)rr)r;rkrz Sfoglia...c8Sr) browse_file)db_label index_varparentpath_varr table_vartype_varsrz'App.create_db_widgets..s.d>N>NvW_aiksu~AJ?K?Krr)r;rkrzTabella:)rrz<>c8Sr)update_columns_for_table)eventrrrrrrrs rrz'App.create_db_widgets..$s9t?\?\]cemowzBDMOX@Y@YrzColonna Indice:r>)r)weight)r LabelgridrrCombobox type_comboEntryEWr table_combobind index_combocolumnconfigure) rrrrrrrrr index_entrys ``````` rrzApp.create_db_widgetss &w''',,1RTPQXY,ZZZ\&xS[H\dnvxyyy Aa1===& &z***//AaST[\/]]] &xr:::??AaXZX]?^^^ 6 7K7K7K7K7K7K7K7K7K7K L L L Q QVW`ahi Q j j j &z***//AaST[\/]]]l6 TTT Qq777(/2Y2Y2Y2Y2Y2Y2Y2Y2Y2Y Z Z Z &0111661QrtZ[bc6dddiY??? Qq777(q+++++rc |}|dkrddg}n|dkrddg}ndg}tj|} | r?|| |d|d| t || } | sN|d | g|jd <|d |d dS| |jd <|d t| d d | ddt| dkrdnd | r9|| d| ||||||dSdSdS)Nr*)z SQLite DBz*.db;*.sqlite;*.sqlite3)z All filesz*.*r+)z Access DBz *.accdb;*.mdb) filetypeszCaricamento tabelle da rAz'ATTENZIONE: Nessuna tabella trovata in r_rr)z tabelle: rrMr@r) rbr askopenfilenamerY log_messagerrJrrrr) r parent_framerrrrrrrfilepathrEs rrzApp.browse_file.s,,.. h  ACWXII  79MNII-.I- BBB  p LL " " "   Tw}}TT(TT U U U'::F   !U8!U!UVVV57 (2 b!!! b!!!17L $X .   xF xxtyyPRQRPR?T?Tx_bci_j_jmn_n_nV[V[tvxx y y y p fQi(((--lHhPXZcenooooo# p p p prcN|}|}|} t||| |} | rd| dDng} | rtfd| Drnt ||| } | rtfd| Drs|d| t| dkr|d| nR|d| d n3r|d n|d |j d d D]} | | d d S) zKAggiorna il combobox delle colonne indice in base alla tabella selezionata.c6g|]}|Sr'r|r!s rr<z0App.update_columns_for_table..Ws HHHa HHHrrEc3 K|]}|vV dSrr'r:rwrns rrz/App.update_columns_for_table..Xs'$H$HaQ'\$H$H$H$H$H$Hrc3 K|]}|vV dSrr'r s rrz/App.update_columns_for_table..\s'331!w,333333rrrNz3Chiave primaria composta rilevata automaticamente: z*Chiave primaria rilevata automaticamente: rrNframe) rbrpsplitallrfrYrrrrremove_mapping_row)rrrrrrindex_var_tk_varrr(r>current_index_val current_colspk mapping_inforns @rrzApp.update_columns_for_tableNs,,..,,..]]__ (':FF,0022L]eHH+<+B+B3+G+GHHHHce  )$H$H$H$H<$H$H$H!H!H )  ':>>B )c333333333 ) $$TYYr]]333r77Q;;$$%_[]%_%_````$$%YRTUVRW%Y%YZZZZ ) $$WQZ0000 $$R((( 03 ; ;L  # #L$9 : : : : ; ;rNc tjj tjdt j}|r||t j}|r||\}}tj ||d}|tj tjddtj d tj tj ||d}|tj tjddtj d d fd } | tj  ||d} j| dS)z4Aggiunge una riga per la mappatura manuale alla GUI.rBrr)rr_rTrM)siderrrz->r)r,rr>c.Sr)r%) row_framersrrz%App.add_mapping_row..~sQUQhQhirQsQsr)rrr)r" sqlite_var access_varN)r rrrrrrrY"_get_available_columns_for_mappingr LEFTr rRIGHTrr^) rsqlite_col_valaccess_col_valsqlite_map_varaccess_map_varrqrs sqlite_combo access_combo remove_buttonr*r.s ` @rrzApp.add_mapping_rowjsId<== BDq))) =>--n=== =>--n===#'#J#J#L#L [|INS^fpqqq rwRT$QGGG )$''',,"',:::|INS^fpqqq rwRT$QGGG 93aIsIsIsIsIsttt )))!*.Xfgg  ##L11111rc`fd|jD|_dS)z>Rimuove una riga di mappatura dalla GUI e dalla lista interna.c,g|]}|dk|S)r"r')r:mr.s rr<z*App.remove_mapping_row..s([[[a1W:QZCZCZCZCZCZrN)rdestroy)rr.s `rr%zApp.remove_mapping_rows:[[[[4+?[[[rc t|j|j|j}t|j|j|j}|r|sggfS|j|j  fd|D fd|D  fdD fd Dd|j D d|j D t fd|D}t fd|D}||fS) zNRestituisce le colonne disponibili per la mappatura, escludendo quelle indice.cFi|]}|k||Sr'r )r:rw db1_index_cols rr"z:App._get_available_columns_for_mapping..-MMMQ!}:L:LAGGIIq:L:L:LrcFi|]}|k||Sr'r )r:rw db2_index_cols rr"z:App._get_available_columns_for_mapping..rBrc(h|]}|v|Sr'r'r:rwdb1_normdb2_norms rr,z9App._get_available_columns_for_mapping.."JJJ1AMM8A;MMMrc(h|]}|v|Sr'r'rFs rr,z9App._get_available_columns_for_mapping..rIrcvh|]6}|d|d7S)r/rbr:r=s rr,z9App._get_available_columns_for_mapping..@jjjqTUVbTcTgTgTiTijQ|_0022jjjrcvh|]6}|d|d7S)r0rLrMs rr,z9App._get_available_columns_for_mapping..rNrc0g|]}|kr |vr|v|Sr'r')r:rwauto_mapped_db1rAmanual_mapped_db1s rr<z:App._get_available_columns_for_mapping..J A A Aa$%$6$6%&o%=%=%&.?%?%?!"&@%?%?rc0g|]}|kr |vr|v|Sr'r')r:rwauto_mapped_db2rDmanual_mapped_db2s rr<z:App._get_available_columns_for_mapping..rSr) rprrbrrrrrrrrr\) r all_db1_cols all_db2_cols available_db1 available_db2rQrUrArGrDrHrRrVs @@@@@@@@rr1z&App._get_available_columns_for_mappings-dm.?.?.A.A4=CTCTCVCVX\XfXjXjXlXlmm -dm.?.?.A.A4=CTCTCVCVX\XfXjXjXlXlmm  < r6M**,, **,, NMMM,MMMMMMM,MMMJJJJJJJJJJJJJJJJkjD}||d|d?d|vr|d}|j|dd|j|dd|j|dd|j|dddSdS)z=Carica la configurazione iniziale e popola i campi della GUI.Nrc|t|trd|S|rt|ndS)Nrr)rrrr$rs r index_to_strz-App.load_initial_config..index_to_strs9#t$$ &yy~~%"*3s888 *rrr*typerrr_rrrr+rr{r|rr6Tr7r8r5)rr$rrYrbrrJrrrrrrrrrrrr6r7r8r5) rrqr]db1_cfgrrEdb2_cfgmappingrs rrzApp.load_initial_configsN ""  F + + + + + F??UmGG   X&G&GFOOG  O M  gkk&(;; < < < M  gkk&"55 6 6 6}  "" O-++--+GT]5F5F5H5HII7=*84;;w''611N&&w{{7';';<<<11$.%X\Xegkguw{xFGGGN&&||GKK4L4L'M'MNNN F??UmGG   X&G&GFOOG  O M  gkk&(;; < < < M  gkk&"55 6 6 6}  "" O-++--+GT]5F5F5H5HII7=*84;;w''611N&&w{{7';';<<<11$.%X\Xegkguw{xFGGGN&&||GKK4L4L'M'MNNN  & &!"34 [ [$$W[[%>%> L@Y@YZZZZ f $ $0J  $ ( (8Mt)T)T U U U  ( , ,Z^^}|d||jdYd}~dSd}~wwxYw)Ndisabledrg?zAvvio del confronto...zSeleziona il file database 1z#Seleziona la tabella per database 1z*Seleziona la colonna indice per database 1zSeleziona il file database 2z#Seleziona la tabella per database 2z*Seleziona la colonna indice per database 2rawrczd|dD}t|dkr|n|dS)Nc^g|]*}||+Sr'rr!s rr<z=App.start_comparison..parse_index..s-HHHqaggiiHHHHrrErNr)r#r)rkpartss r parse_indexz)App.start_comparison..parse_indexs;HHCIIcNNHHH #E QuuE!H. sJ$$$CD<$$&&,(;(;(=(=>$$$r)r6r7r8r5)r^rrrc0g|]}|d|ddS)rrN)r{r|r'rMs rr<z(App.start_comparison..s)#k#k#kQR1Q4qt$L$L#k#k#kr)rrrrz-Configurazione salvata per il prossimo avvio.T)targetargsdaemonzERRORE DI CONFIGURAZIONE: normal)$rrqrdeleterrdrrrbr3rrrrrr$rr rrrr6r7r8r5rrrrr threadingThreadrstartmonitor_threadrF) rro db1_config db2_configmanual_mappings_datasmart_options_dataconfig_to_savecomparison_threadrIs rrzApp.start_comparisons& """444 RV,,, 12228 7=$$&& A !?@@@>%%'' H !FGGG>%%'' O !MNNN=$$&& A !?@@@>%%'' H !FGGG>%%'' O !MNNN = =tCy = = = ="$-"3"3"5"5t}7H7H7J7JDNL^L^L`L`bmbmnrn|oAoAoCoCcDcDEEJ!$-"3"3"5"5t}7H7H7J7JDNL^L^L`L`bmbmnrn|oAoAoCoCcDcDEEJ$$HLH\$$$ (,'?'C'C'E'E+/+G+K+K+M+M*.*E*I*I*K*K+/+G+K+K+M+M "" '. R\RbmwnB'. R\RbmwnB$l#kVj#k#k#k!3  N N + + +   L M M M ) 0% *.BDVX\Xhi!!!   # # % % %    1 2 2 2 2 2 7 7 7   =!== > > >   & &X & 6 6 6 6 6 6 6 6 6 7sL-N O3O  OcrdfddSdjddS)Nr<c.Sr)rzrthreadsrrz$App.monitor_thread..1sD$7$7$?$?rz Confronto completato.rurj)is_aliveafterrrrqrs``rrzzApp.monitor_thread/sr ??   7 JJs????? @ @ @ @ @   6 7 7 7   & &X & 6 6 6 6 6rc.|dS)z.Gestisce la chiusura pulita dell'applicazione.N)r>rs rrzApp.on_closing6s r)NN)r r!r"rrrrrr%r1rrrrzr __classcell__)rs@rrrsB;B;B;B;B;H,,,6ppp@;;;822224 , , ,D>^>^>^@   =7=7=7~777rrc tjd}|dd|dddgd |d d |d d|dd|dddgd |dd|dd|dd|dd|dd|dd|dd |d!d"|d#d$|}|jr|j}t j|s|d%| t|d&5}tj |}d'd'd'n #1swxYwYn/#t$r"}|d(|Yd'}~nd'}~wwxYw| d)p| di}| d*p| di}| d+d}| d,d-} | d.d-} | d/d-} | s\t|| | } | r0t| d0kr| n| d1} t!d2| n|d3| d4| d+d} | d,d-}| d.d-}| d/d-}|s\t| ||} | r0t| d0kr| n| d1}t!d5|n|d6|d4t#|| | | 7}t#| |||7}d8| d9gD}| d:i}t%||||d'S|js|js|jr't!d;d}|j} |j} |j} nXt-|j|j|j|jgs|d<|j}|j} |j} |j} |js|js|jr't!d=d} |j}|j}|j}nXt-|j|j|j |j!gs|d>|j} |j}|j }|j!}t#|| | | }t#| |||}t%||d'S)?z-Funzione per l'esecuzione da riga di comando.z.Confronta due database (SQLite e/o MS Access).)rmz--configzEPercorso al file di configurazione JSON (es. dbcomparer_config.json).)helpz --db1-typer*r+z&Tipo del database 1 (sqlite o access).)choicesrz --db1-pathzPercorso del database 1.z --db1-tablez"Nome della tabella del database 1.z --db1-indexz-Colonna indice per la tabella del database 1.z --db2-typez&Tipo del database 2 (sqlite o access).z --db2-pathzPercorso del database 2.z --db2-tablez"Nome della tabella del database 2.z --db2-indexz-Colonna indice per la tabella del database 2.z --sqlite-dbz4[DEPRECATO] Usa --db1-type sqlite --db1-path invece.z--sqlite-tablez#[DEPRECATO] Usa --db1-table invece.z--sqlite-indexz#[DEPRECATO] Usa --db1-index invece.z --access-dbz4[DEPRECATO] Usa --db2-type access --db2-path invece.z--access-tablez#[DEPRECATO] Usa --db2-table invece.z--access-indexz#[DEPRECATO] Usa --db2-index invece.z$File di configurazione non trovato: rXNz1Errore nella lettura del file di configurazione: rrr^rrrrrNrz%PK rilevata automaticamente per DB1: z(Nessuna chiave primaria trovata per DB1:z!. Specificare 'index' nel config.z%PK rilevata automaticamente per DB2: z(Nessuna chiave primaria trovata per DB2:)rrrrcfg|].}|dd|ddf/S)r{rr|rLrMs rr<zmain_cli..sI   UU< $ $aeeL"&=&= >   rrrzoATTENZIONE: I parametri --sqlite-* sono deprecati. Usa --db1-type, --db1-path, --db1-table, --db1-index invece.zLSpecificare --config oppure --db1-type, --db1-path, --db1-table, --db1-indexzoATTENZIONE: I parametri --access-* sono deprecati. Usa --db2-type, --db2-path, --db2-table, --db2-index invece.zLSpecificare --config oppure --db2-type, --db2-path, --db2-table, --db2-index)"argparseArgumentParser add_argument parse_argsrqr-rr.errorrrrrFrbrfrrGr r sqlite_db sqlite_table sqlite_indexr$rrrr access_db access_table access_indexrrrr)parserrs config_pathrcfgrIdb1_rawdb2_rawrrrrr)rrrrr_r`rrs rmain_clir;s  $1a b b bF  )pqqq  x.BIqrrr  +EFFF  ,PQQQ  ,[\\\  x.BIqrrr  +EFFF  ,PQQQ  ,[\\\  ,bccc (/TUUU (/TUUU  ,bccc (/TUUU (/TUUU     D {/k w~~k** O LLM MM N N N Rk3'' #1ill # # # # # # # # # # # # # # # R R R LLPQPP Q Q Q Q Q Q Q Q R''%..9CGGHb$9$9''%..9CGGHb$9$9KK11KK++KK,, KK,,  v 8Y??B v"%b''A++BB2a5 IiIIJJJJ t tttuuuKK11KK++KK,, KK,,  v 8Y??B v"%b''A++BB2a5 IiIIJJJJ t tttuuu8()W`aaa8()W`aaa  WW.33   44 w-HHH ~ #* #d.? #  A A A>% % DM4=$.$.QRR i LLg h h h==N N  ~ #* #d.? #  A A A>% % DM4=$.$.QRR i LLg h h h==N N x9i@@Gx9i@@G7G$$$$$s<H H5 H HH H H H9H44H9__main__rNr)r):r-syshasattrstdoutrstderrrrrr dataclassesrrwrtkinterrr r r r0r2pandasrnumpynprr r$r5rrJrfrprGrrerrarrrrrrr rdictrrrTkrrr rargvappmainloopr'rrrs  73:}%%?JGI>>> 73:}%%?JGI>>> !!!!!! 1111111111 &   * * * * * * *  * DsDSDDDD"cd3i$S3CDI:C#34PS94',' ' X' blDI6M0N' ' ' ' TbiBI.2D555p,   *^b(,U|>|>(|>8|>VZ|>!%|>|>|>|>~ JJJ   jjjjj"%jjjZ m%m%m%` z s38}}q cee  r