Ë
    rûië  ã                   óî   — d Z ddlZddlZddlZddlmZ ddlmZ ddlm	Z	 ddl
mZ  ej                  e«      ZdZded	edz  fd
„Zded	edz  fd„Zdedededededed	efd„Zded	edz  fd„Zde	d	efd„Zy)z0Nightly job: sync local uploads to Google Drive.é    N)ÚPath)Úselect)ÚAsyncSession)Úsettingsz	/uploads/ÚurlÚreturnc                 ó*  — | r| j                  t        «      sy| t        t        «      d j                  d«      }t        |«      dk(  r
|\  }}}}d}n;t        |«      dk\  r,|d   |d   |d   |d	   f\  }}}}dj	                  |dd «      }ny|||||d
œS )u/  
    Parse local URL â†’ dict with keys: env, spbu_code, yyyymmdd, tipe, filename.
    Handles both formats:
      Old: /uploads/{env}/{spbu_code}/{tipe}/{filename}           (4 parts)
      New: /uploads/{env}/{spbu_code}/{yyyymmdd}/{tipe}/{filename} (5 parts)
    Returns None if not a local URL.
    NÚ/é   Ú é   r   é   é   é   )ÚenvÚ	spbu_codeÚyyyymmddÚtipeÚfilename)Ú
startswithÚLOCAL_PREFIXÚlenÚsplitÚjoin)r   Úpartsr   r   r   r   r   s          ú7/var/www/html/spbu.com/backend/app/jobs/sync_storage.pyÚ_parse_local_urlr      s«   € ñ �c—n‘n¤\Ô2ØØ””LÓ!Ð"Ð#×)Ñ)¨#Ó.€EÜ
ˆ5ƒz�Q‚à).Ñ&ˆˆY˜˜hØ‰Ü	ˆU‹�qŠà).¨q©°5¸±8¸UÀ1¹XÀuÈQÁxÐ)OÑ&ˆˆY˜ $Ø—8‘8˜E ! "˜IÓ&‰àØ Y¸HÈdÐ`hÑiÐió    c                 ó¶   — t        | «      }|sy t        t        j                  «      |d   z  |d   z  }|d   r||d   z  |d   z  |d   z  S ||d   z  |d   z  S )Nr   r   r   r   r   )r   r   r   ÚSTORAGE_PATH)r   ÚpÚbases      r   Ú_local_pathr#   )   st   € Ü˜Ó€AÙØÜ”×%Ñ%Ó&¨¨5©Ñ1°A°k±NÑB€DØˆ‚}Ø�a˜
‘mÑ# a¨¡iÑ/°!°J±-Ñ?Ð?Ø�!�F‘)Ñ˜a 
™mÑ+Ð+r   Úpathr   r   r   r   r   c                 óH   — ddl m} | j                  «       } |||||||«      S )u[   Read file bytes and upload to GDrive â€” runs in a thread to avoid blocking the event loop.r   )Úgdrive_upload)Úapp.utils.gdriver&   Ú
read_bytes)r$   r   r   r   r   r   r&   Ú
file_bytess           r   Ú
_do_uploadr*   3   s&   € å.à—‘Ó"€JÙ˜ X¨y¸$ÀÀXÓNÐNr   c           
   ƒ   ó^  K  — t        | «      }|syt        | «      }|r|j                  «       st        j	                  d|«       y	 t        j                  t        ||d   |d   |d   |d   |d   «      ƒ d{  –—† }|S 7 Œ# t        $ r!}t        j                  d| |«       Y d}~yd}~ww xY w­w)	zGUpload local file to GDrive. Returns new GDrive URL or None on failure.NzLocal file not found: %sr   r   r   r   r   zGDrive upload failed for %s: %s)
r   r#   ÚexistsÚloggerÚwarningÚasyncioÚ	to_threadr*   Ú	ExceptionÚerror)r   r!   Ú
local_pathÚ
gdrive_urlÚes        r   Ú	_sync_urlr6   ;   s³   è ø€ ä˜Ó€AÙØÜ˜SÓ!€JÙ˜Z×.Ñ.Ô0Ü�‰Ð1°:Ô>Øðä '× 1Ñ 1Ü˜
 A j¡M°1°[±>À1ÀVÁ9ÈaÐPUÉhÐXYÐZdÑXeó!
÷ 
ˆ
ð Ðð
ùô ò Ü�‰Ð6¸¸QÔ?ÜûðüsB   ‚AB-Á1B  Á7A>Á8B  Á=B-Á>B  Â 	B*Â	B%Â B-Â%B*Â*B-Údbc              ƒ   ó>  K  — ddl m} ddlm}m}m} ddlm}m}m	}m
} ddlm}	 ddlm}
 ddlm} ddlm} |d	gf|d
gf|dgf|dgf|dgf|dgf|
dgf|d
gf|	ddgf|dgf|dgf|ddgfg}d}d}g }|D �]   \  }}|D �]  }t'        ||d«      }|€"t(        j+                  d|j,                  |«       Œ5| j/                  t1        |«      j3                  |j5                  d«      «      «      ƒ d{  –—† }|j7                  «       j9                  «       }|D ]}  }t'        ||«      }|sŒt;        |«      ƒ d{  –—† }|rRt=        |||«       | j?                  |«       tA        |«      }|r!|jC                  «       r|jE                  |«       |dz  }Œy|dz  }Œ �Œ �Œ# | jG                  «       ƒ d{  –—†  |D ]  }	 tI        jJ                  |«       Œ ||dœ}t(        jO                  d||«       |S 7 Œý7 Œº7 ŒC# tL        $ r!}t(        j+                  d||«       Y d}~Œdd}~ww xY w­w)z‡
    Scan all photo/file columns, upload local files to GDrive, update DB URLs.
    Returns stats dict: {synced, failed, skipped}.
    r   )ÚExpense)ÚAbsensiÚHousekeepingFotoÚ
SaprasItem)ÚLaporanShiftÚLaporanShiftDokumenÚLaporanShiftIslandFotoÚStockAdjustmentItemFoto)Ú	Penebusan)ÚPenerimaanFoto)Ú
Penyetoran)ÚKontrakSewaÚdokumen_urlÚ	bukti_urlÚsource_foto_urlÚfoto_urlÚfile_urlr   Ú
pdf_do_urlÚpdf_bukti_bayar_urlÚfoto_sebelum_urlÚfoto_sesudah_urlNz Column %s.%s not found, skippingz
/uploads/%r   z"Failed to delete local file %s: %s)ÚsyncedÚfailedz#Sync complete: %d synced, %d failed)(Úapp.models.expensesr9   Úapp.models.general_affairsr:   r;   r<   Úapp.models.operationalr=   r>   r?   r@   Úapp.models.penebusanrA   Úapp.models.penerimaanrB   Úapp.models.penyetoranrC   Úapp.models.spburD   Úgetattrr-   r.   Ú__name__Úexecuter   ÚwhereÚlikeÚscalarsÚallr6   ÚsetattrÚaddr#   r,   ÚappendÚcommitÚosÚremover1   Úinfo)r7   r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   rC   rD   ÚtargetsÚtotal_syncedÚtotal_failedÚfiles_to_deleteÚModelÚcolumnsÚcol_nameÚcolÚresultÚrowsÚrowÚold_urlr4   r3   r$   r5   Ústatss                                 r   Úsync_local_to_gdriverr   O   sW  è ø€ õ
 ,ßPÑP÷ó õ /Ý4Ý0Ý+ð 
�}�oÐ&Ø	�;�-Ð Ø	Ð)Ð*Ð+Ø	 * Ð.Ø	˜z˜lÐ+Ø	  5 'Ð*Ø	˜%˜Ð!Ø	�k�]Ð#Ø	�\Ð#8Ð9Ð:Ø	�:�,ÐØ	˜J˜<Ð(Ø	Ð(Ð*<Ð=Ð>ð-€Gð €LØ€LØ"$€Oä!‰ˆˆwÜˆHÜ˜% ¨4Ó0ˆCØˆ{Ü—‘ÐAÀ5Ç>Á>ÐS[Ô\ØàŸ:™:Ü�u“×#Ñ# C§H¡H¨\Ó$:Ó;ó÷ ˆFð —>‘>Ó#×'Ñ'Ó)ˆDã�Ü&-¨c°8Ó&<�ÙØä#,¨WÓ#5×5�
ÙÜ˜C ¨:Ô6Ø—F‘F˜3”Kô "-¨WÓ!5�JÙ! j×&7Ñ&7Ô&9Ø'×.Ñ.¨zÔ:à  AÑ%‘Là  AÑ%‘Lò# ò  ð "ð> �)‰)‹+×Ðó  ˆð	JÜ�I‰I�d�Oð  ð $¨|Ñ<€EÜ
‡K�KÐ5°|À\ÔRØ€LðGøð 6øð ùô ò 	JÜ�N‰NÐ?ÀÀq×IÑIûð	Jüsb   ‚C*HÃ,G*Ã-AHÄ1G,
Ä2A8HÆ*G.Æ+	HÆ5G0Ç
!HÇ,HÇ.HÇ0	HÇ9HÈHÈHÈH)Ú__doc__r/   Úloggingrb   Úpathlibr   Ú
sqlalchemyr   Úsqlalchemy.ext.asyncior   Úapp.core.configr   Ú	getLoggerrX   r-   r   ÚstrÚdictr   r#   r*   r6   rr   © r   r   Ú<module>r}      sÒ   ðÙ 6Û Û Û 	Ý å Ý /å $à	ˆ×	Ñ	˜8Ó	$€à€ðj˜#ð j $¨¡+ó jð0,�Sð ,˜T D™[ó ,ðO�Tð O Sð O°Sð OÀð OÈ#ð OÐY\ð OÐadó Oð˜ð   t¡ó ð(P <ð P°Dô Pr   