Ë
    6ài¾-  ã                   ó|  — d Z ddlZddlZddlmZ ddlmZmZmZm	Z	  G d„ d«      Z
edk(  r‘ ee«      j                  j                  j                  Z e
 ee«      «      Zej#                  «       Zej'                  «       Zej+                  e«       ed   sed	   r! ed
«        eej/                  ee«      «       yyy)z^
Schema Analyzer - Analyzes SQLAlchemy models to detect missing tables/columns in init-db.sql
é    N)ÚPath)ÚDictÚSetÚListÚTuplec            	       ó2  — e Zd ZdZdefd„Zdeeeeef   f   fd„Zdede	e
eeeef   f      fd„Zdedefd	„Zdeeeeef   f   fd
„Zdeeef   fd„Zdeeef   deeeeef   f   defd„Zdeeef   deeeeef   f   defd„Zdefd„Zdeeef   fd„Zy)ÚSchemaAnalyzerz8Analyzes SQLAlchemy models and compares with init-db.sqlÚproject_dirc                 ó€   — t        |«      | _        | j                  dz  dz  dz  | _        | j                  dz  | _        y )NÚbackendÚappÚmodelszinit-db.sql)r   r
   Ú
models_dirÚinit_db_path)Úselfr
   s     ú:/home/factor_ks/STREAMA2026/tests/utils/schema_analyzer.pyÚ__init__zSchemaAnalyzer.__init__   s>   € Ü Ó,ˆÔØ×*Ñ*¨YÑ6¸Ñ>ÀÑIˆŒØ ×,Ñ,¨}Ñ<ˆÕó    Úreturnc                 óî   — i }| j                   j                  «       s|S | j                   j                  d«      D ]8  }|j                  dk(  rŒ| j	                  |«      }|D ]  \  }}|sŒ	|sŒ|||<   Œ Œ: |S )zGAnalyze SQLAlchemy models to extract table names and columns with typesz*.pyz__init__.py)r   ÚexistsÚglobÚnameÚ_extract_model_info)r   Úmodels_infoÚ
model_fileÚtables_infoÚ
table_nameÚcolumnss         r   Úanalyze_modelszSchemaAnalyzer.analyze_models   s…   € àˆà�‰×%Ñ%Ô'ØÐàŸ/™/×.Ñ.¨vÓ6ò 	6ˆJØ�‰ -Ò/Øà×2Ñ2°:Ó>ˆKØ'2ò 6Ñ#�
˜GÚ¢'Ø.5�K 
Ò+ñ6ð	6ð Ðr   r   c                 ó„  — 	 t        |dd¬«      5 }|j                  «       }ddd«       g }d}t        j                  |t        j                  «      D �]´  }|j                  d«      }|j                  d«      }|j                  «       }	||	d j                  d«      }
g }d}|
D ]¸  }|€5|j                  «       r%t        |«      t        |j                  «       «      z
  }|€Œ=|j                  «       r[t        |«      t        |j                  «       «      z
  |k  r3|j                  «       j                  d	«      s|j                  |«       Œ¦ n|j                  |«       Œº dj                  |«      }i }d
}t        j                  ||«      D ]i  }|j                  d«      }|j                  d«      }|dvsŒ*t        j                  d|«      }|sŒC|j                  d«      }| j                  |«      }|||<   Œk |j                  ||f«       �Œ· |S # 1 sw Y   �ŒíxY w# t         $ r}t#        d|› d|› �«       g cY d}~S d}~ww xY w)zVExtract table name and columns with types from a model file (may have multiple tables)Úrúutf-8©ÚencodingNzAclass\s+(\w+)\([^)]+\):.*?__tablename__\s*=\s*["\']([^"\']+)["\']é   é   ú
Úclassz(\w+)\s*=\s*Column\(([^)]+)\))Ú__tablename__Ú__table_args__Ú__mapper_args__z?(String|Integer|Text|Boolean|DateTime|UUID|ForeignKey)\(?\d*\)?zError analyzing z: )ÚopenÚreadÚreÚfinditerÚDOTALLÚgroupÚstartÚsplitÚstripÚlenÚlstripÚ
startswithÚappendÚjoinÚsearchÚ_map_sqlalchemy_type_to_sqlÚ	ExceptionÚprint)r   r   ÚfÚcontentr   Úclass_patternÚclass_matchÚ
class_namer   Úclass_startÚlinesÚclass_contentÚindent_levelÚlineÚclass_content_strr   Úcolumn_patternÚ	col_matchÚcolumn_nameÚcolumn_argsÚ
type_matchÚcolumn_typeÚsql_typeÚes                           r   r   z"SchemaAnalyzer._extract_model_info$   s2  € ð9	Ü�j #°Ô8ð #¸AØŸ&™&›(�÷#ð ˆKð aˆMä!Ÿ{™{¨=¸'Ä2Ç9Á9ÓMó *:�Ø(×.Ñ.¨qÓ1�
Ø(×.Ñ.¨qÓ1�
ð *×/Ñ/Ó1�à  Ð-×3Ñ3°DÓ9�Ø "�Ø#�à!ò 
7�DØ#Ð+°·
±
´Ü'*¨4£y´3°t·{±{³}Ó3EÑ'E˜Ø#Ñ/ØŸ:™:œ<¬S°«Y¼¸T¿[¹[»]Ó9KÑ-KÐP\Ò,\Ø#'§:¡:£<×#:Ñ#:¸7Ô#CØ -× 4Ñ 4°TÕ :á %à)×0Ñ0°Õ6ð
7ð %)§I¡I¨mÓ$<Ð!ð �Ø!A�Ü!#§¡¨^Ð=NÓ!Oò <�IØ"+§/¡/°!Ó"4�KØ"+§/¡/°!Ó"4�Kà"Ð*`Ò`ä%'§Y¡YÐ/qÐs~Ó%˜
Ú%Ø*4×*:Ñ*:¸1Ó*=˜Kà'+×'GÑ'GÈÓ'T˜HØ3;˜G KÒ0ð<ð ×"Ñ" J°Ð#8Ö9ðU*:ðX Ð÷i#ñ #ûôl ò 	ÜÐ$ Z L°°1°#Ð6Ô7Ø�Iûð	úsH   ‚H �H¡B=H ÃCH Æ2H ÇA H ÈHÈH È	H?È"H:È4H?È:H?Úsqlalchemy_typec                 ó:   — ddddddddœ}|j                  |d«      S )z+Map SQLAlchemy type names to SQL type nameszVARCHAR(255)ÚINTEGERÚTEXTÚBOOLEANzTIMESTAMP WITH TIME ZONEÚUUID)ÚStringÚIntegerÚTextÚBooleanÚDateTimerW   Ú
ForeignKey)Úget)r   rR   Útype_mappings      r   r<   z*SchemaAnalyzer._map_sqlalchemy_type_to_sqla   s5   € ð %Ø ØØ Ø2ØØ ñ
ˆð ×Ñ °Ó8Ð8r   c                 ób  — i }| j                   j                  «       s|S 	 t        | j                   dd¬«      5 }|j                  «       }ddd«       d}t	        j
                  |t        j                  «      D ]‚  }|j                  d«      }|j                  d«      }i }d}	t	        j
                  |	|«      D ]<  }
|
j                  d«      }|
j                  d«      }|j                  «       d	vsŒ8|||<   Œ> |||<   Œ„ |S # 1 sw Y   Œ·xY w# t        $ r}t        d
|› �«       |cY d}~S d}~ww xY w)zAAnalyze init-db.sql to extract table names and columns with typesr"   r#   r$   Nz/CREATE TABLE IF NOT EXISTS\s+(\w+)\s*\((.*?)\);r&   r'   z(\w+)\s+([A-Z]+)(?:\([^)]+\))?)ÚPRIMARYÚFOREIGNÚUNIQUEÚ
CONSTRAINTzError analyzing init-db.sql: )r   r   r-   r.   r/   r0   r1   r2   Úupperr=   r>   )r   Úinit_db_infor?   r@   Útable_patternÚmatchr   Úcolumns_strr   rJ   rK   rL   rO   rQ   s                 r   Úanalyze_init_dbzSchemaAnalyzer.analyze_init_dbn   s3  € àˆà× Ñ ×'Ñ'Ô)ØÐð	 Ü�d×'Ñ'¨°wÔ?ð #À1ØŸ&™&›(�÷#ð OˆMÜŸ™ ]°G¼R¿Y¹YÓGò 3�Ø"Ÿ[™[¨›^�
Ø#Ÿk™k¨!›n�ð �à!B�Ü!#§¡¨^¸[Ó!Iò ;�IØ"+§/¡/°!Ó"4�KØ"+§/¡/°!Ó"4�Kà"×(Ñ(Ó*Ð2`Ò`Ø/:˜ Ò,ð;ð ,3�˜ZÒ(ð3ð"  Ð÷-#ð #ûô0 ò 	 ÜÐ1°!°Ð5Ô6ØÕûð	 ús;    D ¸C?Á	B%D Ã/D Ã?DÄD Ä	D.ÄD)Ä#D.Ä)D.c                 ó0  — | j                  «       }| j                  «       }t        «       i dœ}|D ]c  }||vr|d   j                  |«       Œt        ||   j	                  «       «      }t        ||   j	                  «       «      }||z
  }|sŒ\||d   |<   Œe |S )z6Compare models with init-db.sql and return differences)Úmissing_tablesÚmissing_columnsrl   rm   )r    rj   ÚsetÚaddÚkeys)r   r   rf   Údifferencesr   Úmodel_columnsÚinit_db_columnsrm   s           r   Úcompare_schemaszSchemaAnalyzer.compare_schemas’   sµ   € à×)Ñ)Ó+ˆØ×+Ñ+Ó-ˆô "›eØ!ñ
ˆð &ò 
	QˆJØ Ñ-ØÐ,Ñ-×1Ñ1°*Õ=ô !$ K°
Ñ$;×$@Ñ$@Ó$BÓ C�Ü"% l°:Ñ&>×&CÑ&CÓ&EÓ"F�Ø"/°/Ñ"A�â"ØAP�KÐ 1Ñ2°:Ò>ð
	Qð Ðr   rq   r   c                 ó:  — g }|j                  d«       |j                  d| j                  «       › �«       |j                  d«       |j                  di «      j                  «       D ]I  \  }}|D ]?  }|j                  |i «      j                  |d«      }|j                  d|› d|› d|› d	�«       ŒA ŒK |j                  d
t	        «       «      D ]<  }|j                  d|› d�«       |j                  d|› �«       |j                  d«       Œ> dj                  |«      S )z<Generate SQL migration statements for missing tables/columnsz7-- Auto-generated migration for missing schema elementsz-- Generated at: Ú rm   rU   zALTER TABLE z ADD COLUMN IF NOT EXISTS ú ú;rl   z-- CREATE TABLE z2 (id UUID PRIMARY KEY DEFAULT uuid_generate_v4());z'-- TODO: Add full table definition for r(   )r9   Ú_get_timestampr^   Úitemsrn   r:   )r   rq   r   Úmigration_sqlr   r   ÚcolumnrO   s           r   Úgenerate_migration_sqlz%SchemaAnalyzer.generate_migration_sql«   s6  € àˆØ×ÑÐVÔWØ×ÑÐ0°×1DÑ1DÓ1FÐ0GÐHÔIØ×Ñ˜RÔ ð $/§?¡?Ð3DÀbÓ#I×#OÑ#OÓ#Qò 	sÑˆJ˜Ø!ò s�à)Ÿo™o¨j¸"Ó=×AÑAÀ&È&ÓQ�Ø×$Ñ$ |°J°<Ð?YÐZ`ÐYaÐabÐcnÐboÐopÐ%qÕrñsð	sð &Ÿ/™/Ð*:¼C»EÓBò 	%ˆJØ× Ñ Ð#3°J°<Ð?qÐ!rÔsØ× Ñ Ð#JÈ:È,Ð!WÔXØ× Ñ  Õ$ð	%ð
 �y‰y˜Ó'Ð'r   c                 óX  ‡— |d   s|d   sy	 t        | j                  dd¬«      5 }|j                  «       }ddd«       |j                  di «      j	                  «       D ][  \  }}d|› d	�}|D ]K  }|j                  |i «      j                  |d
«      }	d|› d|	› d�Šˆfd„}
t        j                  ||
d¬«      }ŒM Œ] t        | j                  dd¬«      5 }|j                  «       ddd«       y# 1 sw Y   ŒºxY w# 1 sw Y   yxY w# t        $ r}t        d|› �«       Y d}~yd}~ww xY w)zQAutomatically update init-db.sql with missing columns in CREATE TABLE definitionsrm   rl   Tr"   r#   r$   Nz(CREATE TABLE IF NOT EXISTS z\s*\([^)]*?)\);rU   z    rw   ú,c                 óT   •— | j                  d«      }|j                  «       d‰› d�z   S )Nr&   r(   z
);)r2   Úrstrip)rh   Ú	table_defÚ
column_defs     €r   Ú
add_columnz3SchemaAnalyzer.auto_fix_init_db.<locals>.add_columnÔ   s-   ø€ Ø$)§K¡K°£N˜	à(×/Ñ/Ó1°b¸¸ÀDÐ4IÑIÐIr   r&   )ÚcountÚwzError auto-fixing init-db.sql: F)
r-   r   r.   r^   rz   r/   ÚsubÚwriter=   r>   )r   rq   r   r?   r@   r   r   rg   r|   rO   r„   rQ   rƒ   s               @r   Úauto_fix_init_dbzSchemaAnalyzer.auto_fix_init_dbÁ   sG  ø€ àÐ,Ò-°kÐBRÒ6SØð	Ü�d×'Ñ'¨°wÔ?ð #À1ØŸ&™&›(�÷#ð (3§¡Ð7HÈ"Ó'M×'SÑ'SÓ'Uò RÑ#�
˜Gà#?À
¸|È?Ð [�à%ò 
R�FØ"-§/¡/°*¸bÓ"A×"EÑ"EÀfÈfÓ"U�KØ#'¨ x¨q°°¸QÐ!?�JôJô
 !Ÿf™f ]°JÀÈqÔQ‘Gñ
Rð	Rô" �d×'Ñ'¨°wÔ?ð !À1Ø—‘˜Ô ÷!ð ÷1#ð #ú÷*!ð ûäò 	ÜÐ3°A°3Ð7Ô8Üûð	úsL   ŽD ¦C0·BD ÃC<Ã'D Ã0C9Ã5D Ã<DÄD ÄD Ä	D)ÄD$Ä$D)c                 óL   — ddl m } |j                  «       j                  d«      S )zGet current timestampr   )Údatetimez%Y-%m-%d %H:%M:%S)r‹   ÚnowÚstrftime)r   r‹   s     r   ry   zSchemaAnalyzer._get_timestampå   s   € å%Ø�|‰|‹~×&Ñ&Ð':Ó;Ð;r   c                 ó¨  — t        d«       t        d«       t        d«       |d   r,t        d«       t        |d   «      D ]  }t        d|› �«       Œ |d   r[t        d«       t        |d   j                  «       «      D ]1  \  }}t        d|› �«       t        |«      D ]  }t        d	|› �«       Œ Œ3 |d   s|d   st        d
«       t        d«       y)z3Print a human-readable report of schema differencesz<============================================================zSchema Analysis Reportrl   z
Missing Tables:z  - rm   z
Missing Columns:z	  Table: z    - u   
âœ“ Schema is up to date!N)r>   Úsortedrz   )r   rq   Útabler   r   r|   s         r   Úprint_reportzSchemaAnalyzer.print_reportê   sã   € äˆhŒÜÐ&Ô'ÜˆhŒàÐ'Ò(ÜÐ%Ô&Ü Ð,<Ñ =Ó>ò &�Ü˜˜U˜G�nÕ%ð&ð Ð(Ò)ÜÐ&Ô'Ü'-¨kÐ:KÑ.L×.RÑ.RÓ.TÓ'Uò -Ñ#�
˜GÜ˜	 * Ð.Ô/Ü$ W›oò -�FÜ˜F 6 (Ð+Õ,ñ-ð-ð
 Ð+Ò,°[ÐARÒ5SÜÐ/Ô0äˆh�r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ústrr   r   r    r   r   r   r   r<   rj   rt   r}   Úboolr‰   ry   r‘   © r   r   r	   r	   
   s8  „ ÙBð= Có =ð
  S¨$¨s°C¨x©.Ð%8Ñ 9ó ð$;¨dð ;°t¸EÀ#ÀtÈCÐQTÈHÁ~ÐBUÑ<VÑ7Wó ;ðz9¸3ð 9À3ó 9ð"   c¨4°°S°©>Ð&9Ñ!:ó " ðH  c¨4 i¡ó ð2(°$°s¸D°y±/ð (ÐPTÐUXÐZ^Ð_bÐdgÐ_gÑZhÐUhÑPið (Ðnqó (ð,"¨D°°d°©Oð "È$ÈsÐTXÐY\Ð^aÐYaÑTbÐObÑJcð "Ðhló "ðH< ó <ð
¨¨S°$¨Y©ô r   r	   Ú__main__rl   rm   z
Generated Migration SQL:)r•   Úosr/   Úpathlibr   Útypingr   r   r   r   r	   r’   Ú__file__Úparentr
   r–   Úanalyzerrt   rq   r    r   r‘   r>   r}   r˜   r   r   ú<module>r       sÀ   ðñó 
Û 	Ý ß )Ó )÷uñ uðp ˆzÒá�x“.×'Ñ'×.Ñ.×5Ñ5€KÙ™c +Ó.Ó/€Hà×*Ñ*Ó,€KØ×)Ñ)Ó+€KØ×Ñ˜+Ô&àÐ#Ò$¨Ð4EÒ(FÙÐ*Ô+Ùˆh×-Ñ-¨k¸;ÓGÕHð )Gð r   