ó
    lÌj€*  ã            
       óP  • % S r SSKrSSKrSSKrSSKJr  SSKJrJrJ	r	J
r
  SSKJr  SSKJrJr  SSKJr  \R$                  " S5      rSq\	\   \S	'   \R,                  " 5       rS
rS\4S jrS\4S jrS-S jr\R8                  " S5      r\R8                  " S\R<                  5      r\R8                  " S\R<                  5      r \R8                  " S\R<                  5      r!\R8                  " S\R<                  5      r"\R8                  " S\R<                  5      r#0 r$\%\&\&4   \S'   S\&S\&4S jr' " S S\%5      r( " S S5      r) " S S5      r*S\*4S jr+\S.S\&S \,4S! jj5       r-S"\*S#\&S\.\&   4S$ jr/S"\*S#\&S\04S% jr1S"\*S#\&S&\&S'\&SS4
S( jr2S"\*S#\&S\&S)\&SS4
S* jr3S-S+ jr4S-S, jr5g)/uÉ  
AccÃ¨s MySQL partagÃ© par tous les modules (mÃªme pile que hec-photo-app : SQLAlchemy 2.0 + PyMySQL,
utf8mb4 / InnoDB, variables MYSQL_*).

Pourquoi un moteur SYNCHRONE derriÃ¨re `get_db()` : le nettoyage RGPD, l'organizer, l'Ã©lection du leader et
le rate limiting tournent dans des threads (`asyncio.to_thread`) et appelaient dÃ©jÃ  `get_db()`. Le pool de
SQLAlchemy fournit les connexions rÃ©utilisables ; l'interface (`conn.execute(sql, params)`, lignes accessibles
par nom, `with get_db() as conn`) ne change pas pour les ~220 requÃªtes existantes.

Dialecte : le code garde ses placeholders `?` ; ils sont traduits ici (avec `%` Ã©chappÃ©). `INSERT OR REPLACE`
â†’ `REPLACE`, `INSERT OR IGNORE` â†’ `INSERT IGNORE`, `BEGIN IMMEDIATE` â†’ `START TRANSACTION`, la colonne `key`
(mot rÃ©servÃ© MySQL) est citÃ©e automatiquement et `COLLATE NOCASE` est ignorÃ© (la collation par dÃ©faut
utf8mb4_unicode_ci est dÃ©jÃ  insensible Ã  la casse, comme LIKE sous SQLite).
é    N)Úcontextmanager)ÚAnyÚIteratorÚOptionalÚSequence)Úcreate_engine)ÚURLÚEngine)Úconfigzmeeconic.dbÚ_enginez@ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ciÚreturnc            
      ó,  • [         R                  " S[        R                  [        R                  =(       d    S [        R
                  [        R                  [        R                  SS0S9n [        U [        R                  [        R                  SSSS9$ )	Nzmysql+pymysqlÚcharsetÚutf8mb4)ÚusernameÚpasswordÚhostÚportÚdatabaseÚqueryi  TzREAD COMMITTED)Ú	pool_sizeÚmax_overflowÚpool_recycleÚpool_pre_pingÚisolation_level)
r	   Úcreater   Ú
MYSQL_USERÚMYSQL_PASSWORDÚ
MYSQL_HOSTÚ
MYSQL_PORTÚMYSQL_DBr   ÚMYSQL_POOL_SIZE)Úurls    ÚJD:\Ingenieria\TRABAJOS ONLINE\Projects\11- Photo School\backend\core\db.pyÚ_build_enginer%   "   ss   € Ü
�*Š*Ø¤&×"3Ñ"3¼f×>SÑ>S×>[ÐW[Ü×Ñ¤V×%6Ñ%6ÄÇÁØ˜)Ð$ñ&€Cô Ø”v×-Ñ-¼F×<RÑ<RÐaeØØ(ñ*ð *ó    c                  ó”   • [         c&  [           [         c
  [        5       q S S S 5        [         $ [         $ ! , (       d  f       [         $ = f©N)r   Ú_engine_lockr%   © r&   r$   Úenginer+   -   s5   € ä�ÞÜ‰Ü'›/�÷ ô €NŒ7€N÷ Œ\ô €Nús	   Ž4´
Ac                  ó~   • [            [        b  [        R                  5         SqSSS5        g! , (       d  f       g= f)zICloses the pool (tests switch database; the service does it on shutdown).N)r)   r   Údisposer*   r&   r$   Údispose_enginer.   6   s&   € ö 
ÜÑÜ�O‰OÔØˆG÷ 
�Žús   ‡.®
<z('(?:[^']|'')*')z(?<![\w`.])key(?![\w`])z \bINSERT\s+OR\s+REPLACE\s+INTO\bz\bINSERT\s+OR\s+IGNORE\s+INTO\bz\s+COLLATE\s+NOCASE\bz+^\s*BEGIN(\s+IMMEDIATE|\s+TRANSACTION)?\s*$Ú_cacheÚsqlc                 ó´  • [         R                  U 5      nUb  U$ [        R                  U 5      (       a  SnGOU R	                  5       SS R                  5       S;   n[        R                  U 5      n[        U5       Hª  u  pVUS-  (       a  UR                  SS5      XE'   M%  [        R                  SU5      n[        R                  S	U5      n[        R                  S
U5      nU(       d  [        R                  SU5      nUR                  SS5      R                  SS5      nXdU'   M¬     S
R                  U5      n[!        [         5      S:  a	  U[         U '   U$ )uM   SQL Ã©crit pour sqlite3 (placeholders `?`) â†’ SQL MySQL pour PyMySQL (`%s`).NzSTART TRANSACTIONé   )ÚCREATEzALTER zDROP Té   Ú%z%%zREPLACE INTOzINSERT IGNORE INTOÚ z`key`Ú?z%siÐ  )r/   ÚgetÚ	_BEGIN_REÚmatchÚlstripÚupperÚ
_STRING_REÚsplitÚ	enumerateÚreplaceÚ_OR_REPLACE_REÚsubÚ_OR_IGNORE_REÚ
_NOCASE_REÚ_KEY_REÚjoinÚlen)r0   ÚhitÚoutÚis_ddlÚpartsÚiÚparts          r$   Ú	translaterN   J   s"  € ä
�*‰*�S‹/€CØ
�Øˆ
Ü‡��s×ÑØ!Šð —‘“˜b˜qÐ!×'Ñ'Ó)Ð-KÑKˆä× Ñ  Ó%ˆÜ  Ö'‰GˆAØ�1�uØŸ<™<¨¨TÓ2�‘ÙÜ!×%Ñ% n°dÓ;ˆDÜ ×$Ñ$Ð%9¸4Ó@ˆDÜ—>‘> " dÓ+ˆDÞÜ—{‘{ 7¨DÓ1�Ø—<‘<  TÓ*×2Ñ2°3¸Ó=ˆDØ�!‹Hñ (ð �g‰g�e‹nˆÜ
Œ6ƒ{�TÓØŒˆs‰Ø€Jr&   c                   ó0   ^ • \ rS rSrSrSrU 4S jrSrU =r$ )ÚRowéi   z<Ligne accessible par nom ET par position, comme sqlite3.Row.r*   c                 ó„   >• [        U[        5      (       a  [        U R                  5       5      U   $ [        TU ]  U5      $ r(   )Ú
isinstanceÚintÚlistÚvaluesÚsuperÚ__getitem__)ÚselfÚkÚ	__class__s     €r$   rX   ÚRow.__getitem__m   s6   ø€ Ü�aœ×ÑÜ˜Ÿ™›Ó& qÑ)Ð)Ü‰wÑ" 1Ó%Ð%r&   )	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú	__slots__rX   Ú__static_attributes__Ú__classcell__)r[   s   @r$   rP   rP   i   s   ø† ÙFØ€I÷&ó &r&   rP   c                   óŒ   • \ rS rSrS r\S\4S j5       r\S\\   4S j5       r	S\\
   4S jrS\\
   4S jrS\\
   4S jrS	rg
)ÚCursorés   c                 ó   • Xl         g r(   ©Ú_cur)rY   Úcurs     r$   Ú__init__ÚCursor.__init__t   s   € Ø�	r&   r   c                 ó.   • U R                   R                  $ r(   )rj   Úrowcount©rY   s    r$   ro   ÚCursor.rowcountw   s   € à�y‰y×!Ñ!Ð!r&   c                 ó.   • U R                   R                  $ r(   )rj   Ú	lastrowidrp   s    r$   rs   ÚCursor.lastrowid{   s   € à�y‰y×"Ñ"Ð"r&   c                 óV   • U R                   R                  5       nUb  [        U5      $ S $ r(   )rj   ÚfetchonerP   ©rY   Úrs     r$   rv   ÚCursor.fetchone   s'   € Ø�I‰I×ÑÓ ˆØ™Œs�1‹vÐ0¨DÐ0r&   c                 ót   • U R                   R                  5        Vs/ s H  n[        U5      PM     sn$ s  snf r(   ©rj   ÚfetchallrP   rw   s     r$   r|   ÚCursor.fetchallƒ   s-   € Ø $§	¡	× 2Ñ 2Ô 4Ó5Ò 4˜1”�A–Ñ 4Ñ5Ð5ùÒ5s   �5c              #   óh   #   • U R                   R                  5        H  n[        U5      v •  M     g 7fr(   r{   rw   s     r$   Ú__iter__ÚCursor.__iter__†   s%   é € Ø—‘×#Ñ#Ö%ˆAÜ�a“&ŒLò &ùs   ‚02ri   N)r]   r^   r_   r`   rl   ÚpropertyrT   ro   r   rs   rP   rv   rU   r|   r   r   rc   r*   r&   r$   rf   rf   s   sq   † òð ð"˜#ó "ó ð"ð ð#˜8 C™=ó #ó ð#ð1˜( 3™-ô 1ð6˜$˜s™)ô 6ð˜( 3™-÷ r&   rf   c                   ó¨   • \ rS rSrSrS rSS\S\\   S\	4S jjr
S\S\\\      SS	4S
 jrS\SS	4S jrSS jrSS jrSS jrSS jrS\4S jrSrg	)Ú
Connectioné‹   zOConnexion du pool avec l'interface de sqlite3.Connection que le code utilisait.c                 ó\   • SS K nXl        UR                  R                  U l        SU l        g )Nr   r6   )Úpymysql.cursorsÚ_rawÚcursorsÚ
DictCursorÚ_dict_cursorr   )rY   ÚrawÚpymysqls      r$   rl   ÚConnection.__init__Ž   s$   € ÛØŒ	Ø#ŸO™O×6Ñ6ˆÔØ!ˆÕr&   r0   Úparamsr   c                 óª   • U R                   R                  U R                  5      nUR                  [	        U5      [        U5      5        [        U5      $ r(   )r‡   ÚcursorrŠ   ÚexecuterN   Útuplerf   )rY   r0   rŽ   rk   s       r$   r‘   ÚConnection.execute”   s<   € Ø�i‰i×Ñ˜t×0Ñ0Ó1ˆØ�‰”I˜c“N¤E¨&£MÔ2Ü�c‹{Ðr&   ÚseqNc           	      óÂ   • U R                   R                  U R                  5      nUR                  [	        U5      U Vs/ s H  n[        U5      PM     sn5        g s  snf r(   )r‡   r�   rŠ   ÚexecutemanyrN   r’   )rY   r0   r”   rk   Úps        r$   r–   ÚConnection.executemany™   sD   € Ø�i‰i×Ñ˜t×0Ñ0Ó1ˆØ�‰œ	 #›¹3Ó(?º3°a¬¨q®¹3Ñ(?Õ@ùÒ(?s   ¿A
Úscriptc                 óv   • S UR                  S5       5        H  nU(       d  M  U R                  U5        M     g )Nc              3   ó@   #   • U  H  oR                  5       v •  M     g 7fr(   )Ústrip)Ú.0Úss     r$   Ú	<genexpr>Ú+Connection.executescript.<locals>.<genexpr>ž   s   é € Ð:Ò(9 1—W‘W—Y�YÒ(9ùs   ‚Ú;)r>   r‘   )rY   r™   Ústmts      r$   ÚexecutescriptÚConnection.executescript�   s+   € Ù:¨¯©°SÔ(9Ö:ˆDßˆtØ—‘˜TÖ"ò ;r&   c                 ó8   • U R                   R                  5         g r(   )r‡   Úcommitrp   s    r$   r¦   ÚConnection.commit¢   s   € Ø�	‰	×ÑÕr&   c                 ó8   • U R                   R                  5         g r(   )r‡   Úrollbackrp   s    r$   r©   ÚConnection.rollback¥   s   € Ø�	‰	×ÑÕr&   c                 óZ   •  U R                   R                  5         g ! [         a     g f = fr(   )r‡   ÚcloseÚ	Exceptionrp   s    r$   r¬   ÚConnection.close¨   s'   € ð	Ø�I‰I�O‰OÕøÜó 	Ùð	ús   ‚ �
*©*c                 ó   • U $ r(   r*   rp   s    r$   Ú	__enter__ÚConnection.__enter__®   s   € Øˆr&   c                 óÀ   •  Uc  U R                   R                  5         OU R                   R                  5         U R                  5         g! U R                  5         f = f)NF)r‡   r¦   r©   r¬   )rY   Úexc_typeÚexcÚtbs       r$   Ú__exit__ÚConnection.__exit__±   sF   € ð	ØÑØ—	‘	× Ñ Õ"à—	‘	×"Ñ"Ô$à�J‰JŒLØøð �J‰J�Lús   ‚8A ÁA)rŠ   r‡   r   )r*   ©r   N)r   rƒ   )r]   r^   r_   r`   ra   rl   Ústrr   r   rf   r‘   r–   r£   r¦   r©   r¬   r°   Úboolr¶   rc   r*   r&   r$   rƒ   rƒ   ‹   s‚   † ÙYò"ñ˜3ð ¨°©ð ¸võ ð
A˜sð A¨°(¸3±-Ñ)@ð AÀTô Að# Cð #¨Dô #ô
ôôôð¨T÷ r&   rƒ   c                  óD   • [        [        5       R                  5       5      $ r(   )rƒ   r+   Úraw_connectionr*   r&   r$   Úget_dbr½   ¼   s   € Ü”f“h×-Ñ-Ó/Ó0Ð0r&   ÚnameÚtimeoutc              #   óØ  #   • SU  3n[        U5      S:”  a4  SSKnSUR                  U R                  5       5      R	                  5       -   n[        5       n UR                  SX!45      R                  5       S   nUS:w  a  [        SU < S	U S
35      e Sv •  UR                  SU45         UR                  5         g! UR                  SU45        f = f! UR                  5         f = f7f)u³   Verrou applicatif inter-processus (GET_LOCK). Remplace `BEGIN IMMEDIATE` de SQLite pour les
lecture-puis-Ã©criture qui doivent Ãªtre atomiques (rate limiting, cooldown d'alertes).z	meeconic:é@   r   NzSELECT GET_LOCK(?, ?) AS okÚoké   zlock z not acquired in rž   zSELECT RELEASE_LOCK(?))
rG   ÚhashlibÚsha1ÚencodeÚ	hexdigestr½   r‘   rv   ÚTimeoutErrorr¬   )r¾   r¿   ÚlockrÄ   ÚconnÚgots         r$   Ú
named_lockrÌ   À   sÓ   é € ð �t�fÐ€DÜ
ˆ4ƒy�2ƒ~ÛØ˜WŸ\™\¨$¯+©+«-Ó8×BÑBÓDÑDˆÜ‹8€Dð	Ø�l‰lÐ8¸4¸/ÓJ×SÑSÓUÐVZÑ[ˆØ�!‹8Ü  t¡hÐ.?À¸yÈÐJÓKÐKð	<Ûà�L‰LÐ1°D°7Õ;à�
‰
�øð �L‰LÐ1°D°7Õ;ûà�
‰
�üs6   ‚AC*Á=C ÂB= ÂC Â,C*Â=CÃC ÃC'Ã'C*rÊ   Útablec                 óx   • U R                  SU45      R                  5       nU Vs/ s H  o3S   PM	     sn$ s  snf )NziSELECT COLUMN_NAME AS name FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?r¾   )r‘   r|   )rÊ   rÍ   Úrowsrx   s       r$   Útable_columnsrÐ   ×   sA   € Ø�<‰<ð	9Ø;@¸(óDçDLÁHÃJð 	ñ  $Ó$št˜!ˆfŒI™tÑ$Ð$ùÒ$s   ¦7c                 óV   • [        U R                  SU45      R                  5       5      $ )NzVSELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?)rº   r‘   rv   )rÊ   rÍ   s     r$   Útable_existsrÒ   Þ   s)   € Ü�—‘Ø`Ø	ˆóç‘(“*óð r&   ÚcolumnÚddlc                 óX   • U[        X5      ;  a  U R                  SU SU SU 35        g g )NzALTER TABLE z ADD COLUMN Ú )rÐ   r‘   )rÊ   rÍ   rÓ   rÔ   s       r$   Úensure_columnr×   ä   s3   € Ø”] 4Ó/Ó/Ø�‰�| E 7¨,°v°h¸aÀ¸uÐEÕFð 0r&   Úcolumnsc           	      óŒ   • U R                  SX45      R                  5       nU(       d  U R                  SU SU SU S35        gg)z1CREATE INDEX IF NOT EXISTS n'existe pas en MySQL.zkSELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? AND INDEX_NAME=?zCREATE INDEX z ON z (Ú)N)r‘   rv   )rÊ   rÍ   r¾   rØ   Úexistss        r$   Úensure_indexrÜ   é   sM   € à�\‰\ØuØ	ˆóç‘x“zð ö Ø�‰�} T F¨$¨u¨g°R¸°yÀÐBÕCð r&   c                  ó"  • SSK n  U R                  [        R                  [        R                  [        R
                  [        R                  SS9n UR                  5        nUR                  S[        R                   S35        SSS5        UR                  5         UR                  5         g! , (       d  f       N/= f! UR                  5         f = f! [         a/  n[        R                  S[        R                  U5         SnAgSnAff = f)uY   CrÃ©e la base (utf8mb4) si l'utilisateur MySQL en a le droit â€” pratique en dev (XAMPP).r   Nr   )r   r   Úuserr   r   zCREATE DATABASE IF NOT EXISTS `z:` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciuO   Could not ensure database %s exists (%s) â€” assuming it was created beforehand)rŒ   Úconnectr   r   r    r   r   r�   r‘   r!   r¦   r¬   r­   ÚlogÚwarning)rŒ   rÊ   rk   Úes       r$   Úcreate_database_if_missingrã   ò   sÕ   € ãð{Ø�‰¤F×$5Ñ$5¼F×<MÑ<MÔTZ×TeÑTeÜ(.×(=Ñ(=Àyð ð Rˆð	Ø—‘” #Ø—‘Ð=¼f¿o¹oÐ=Nð OWð Xô Y÷ ð �K‰KŒMà�J‰J�L÷ •ûð
 �J‰J�LûÜó {ô 	�‰ÐeÔgm×gvÑgvÐxy×zÑzûð{úsH   †AC ÁC  Á"$B/ÂC  ÂC Â/
B=Â9C  Ã CÃC Ã
DÃ%D	Ä	Dc            
      óÎ   • [        5         [        5        n U R                  S[         S[         S[         S35        U R	                  5         S S S 5        g ! , (       d  f       g = f)Nz�
        CREATE TABLE IF NOT EXISTS app_kv (
            `key`   VARCHAR(191) NOT NULL PRIMARY KEY,
            value   MEDIUMTEXT
        ) zµ;
        CREATE TABLE IF NOT EXISTS rate_hits (
            `key` VARCHAR(191) NOT NULL,
            ts    DOUBLE NOT NULL,
            KEY idx_rate_hits_key (`key`, ts)
        ) zË;
        CREATE TABLE IF NOT EXISTS leader_locks (
            name       VARCHAR(64) NOT NULL PRIMARY KEY,
            owner      VARCHAR(64) NOT NULL,
            expires_at DOUBLE NOT NULL
        ) z	
        )rã   r½   r£   ÚTABLE_OPTIONSr¦   )rÊ   s    r$   Úinit_core_tablesræ     s`   € ÜÔ Ü	Œ�TØ×Ñð  ô ˆ/ð ô
 ˆ/ð ô
 ˆ/ð 	ðô 	ð  	�‰Œ÷# 
�Žús   •8AÁ
A$r¸   )é   )6ra   ÚloggingÚreÚ	threadingÚ
contextlibr   Útypingr   r   r   r   Ú
sqlalchemyr   Úsqlalchemy.enginer	   r
   Úcorer   Ú	getLoggerrà   r   Ú__annotations__ÚLockr)   rå   r%   r+   r.   Úcompiler=   Ú
IGNORECASErE   rA   rC   rD   r9   r/   Údictr¹   rN   rP   rf   rƒ   r½   rT   rÌ   rU   rÐ   rº   rÒ   r×   rÜ   rã   ræ   r*   r&   r$   Ú<module>rö      s  ðòó Û 	Û Ý %ß 4Ó 4å $ß )å à×Ò˜Ó&€à €ˆ�&Ñ	Ó  Ø�~Š~Ó€àR€ð*�vô *ð�ô ôð �ZŠZÐ+Ó,€
Ø
�*Š*Ð/°·±Ó
?€Ø—’Ð?ÀÇÁÓO€Ø—
’
Ð=¸r¿}¹}ÓM€Ø�ZŠZÐ0°"·-±-Ó@€
Ø�JŠJÐEÀrÇ}Á}ÓU€	Ø€ˆˆS�#ˆX‰Ó ð�3ð ˜3ô ô>&ˆ$ô &÷ñ ÷0.ñ .ðb1�
ô 1ð ñ�Sð  3ô ó ðð,%˜
ð %¨3ð %°4¸±9ô %ð�zð ¨#ð °$ô ðG˜
ð G¨3ð G¸ð GÀ#ð GÈ$ô Gð
D�zð D¨#ð D°Sð DÀ3ð DÈ4ô Dô{õ&r&   