
    ڻfi&                       S SK Jr  S SKJrJr  S SKJr  S SKrS SKrS SK	r
S SKrS SKrS SKJr  S SKJr  S SKJrJr  S SKJr  S S	KJr  S
/rS SKJr  S S jrS!S jrS"S jrS rS rS#S$S jjr \ " S5      r!\!(       d  \"" S5      eS%S jr#S%S jr$S&S jr%S'S jr&S'S jr'S SKJr  S(S jr(\ " S S5      5       r)SSS.         S)S jjr*g)*    )annotations)	dataclassasdict)PathN)
get_engine)QUERY_FOLIOS_AGG)
FOLIOS_DIRensure_dirs)service_account)buildz%https://www.googleapis.com/auth/drive)MediaFileUploadc                    [        U 5      nUR                  5       (       d<  [        [        5      R                  5       R                  S   nX!-  R                  5       n[        U5      $ )N   )r   is_absolute__file__resolveparentsstr)path_strpROOTs      dC:\Users\julio\OneDrive\Documentos\Trabajo\Ideas Frescas\Proyectos\cabanna-api\app\dwh\etl_folios.py_resolve_sa_pathr      sN    XA==?? H~%%'//2X q6M    c                    U =(       d    SR                  5       n U R                  S5      =(       a    U R                  S5      $ )N {})strip
startswithendswith)ss    r   _looks_like_jsonr#      s.    	
bA<<0C0r   c                    [        U 5      n U R                  S5      n[        U[        5      (       a7  SU;   a  UR	                  SS5      nUR                  S5      (       d  US-  nXS'   U $ )ut   
Asegura que private_key tenga saltos de línea reales.
Sirve tanto si viene con \n escapados como si viene normal.
private_keyz\n
)dictget
isinstancer   replacer!   )infopks     r   _normalize_private_keyr-   !   sd    
 :D	-	 B"cB;E4(B{{4  $JB ]Kr   c                 P   [         R                  " S5      n U (       d  [        S5      eU R                  5       R	                  SS5      n [        U 5      (       aD   [        R                  " U 5      n[        U5      n[        R                  R                  U[        S9$ [        U 5      n[         R                   R#                  U5      (       d  [        SU S	35      e[        R                  R%                  U[        S9$ ! [        R                   a  n[        SU 35      UeSnAff = f)
u   
ÚNICA función válida para local + producción.

Prioridad:
1) Streamlit Cloud secrets: [gcp_service_account] (dict)
2) ENV GDRIVE_SA_JSON como JSON (texto)
3) ENV GDRIVE_SA_JSON como ruta a archivo .json (local)
GDRIVE_SA_JSONz|Falta credencial. Define:
- Streamlit Secrets: [gcp_service_account]
o
- ENV: GDRIVE_SA_JSON (JSON completo o ruta al .json)z
r&   u   GDRIVE_SA_JSON parece JSON pero no es válido.
Si lo pegaste en secrets como string, asegúrate de que el private_key use \n escapados.
Recomendado: usa [gcp_service_account] en Streamlit secrets.
Detalle: N)scopesz-No encuentro el JSON del Service Account en: um   
En local: define GDRIVE_SA_JSON como ruta válida.
En Streamlit Cloud: usa [gcp_service_account] en Secrets.)osgetenvFileNotFoundErrorr   r*   r#   jsonloadsJSONDecodeErrorRuntimeErrorr-   r   Credentialsfrom_service_account_infoSCOPESr   pathexistsfrom_service_account_file)rawr+   e	json_paths       r   _load_sa_credentials_from_envrA   0   s%    ))$
%CD
 	
 ))+

fd
+C 	::c?D &d+**DDTRXDYY !%I77>>)$$;I; GH H
 	
 &&@@SY@ZZ) ## 	 3 
 	s   C= =D%D  D%c                 ,    [        5       n [        SSU S9$ )Ndrivev3)credentials)rA   r   )credss    r   get_drive_servicerG   `   s    )+E$E22r   c                    [         R                  " U 5      nUb6  [        U5      R                  5       S:w  a  [        U5      R                  5       $ U$ )Nr   )r1   r2   r   r   )keydefaultvs      r   get_settingrL   d   s<     			#A}Q2-1v||~Nr   FOLDER_ID_FOLIOSz6Falta FOLDER_ID_FOLIOS en .env o variables de entorno.c                <    U R                  5       R                  SS9$ )N   )day)	normalizer*   dts    r   month_startrT   q   s    <<>!!a!((r   c                b    U [         R                  R                  S5      -   R                  5       $ )NrO   )pdoffsets
MonthBeginrQ   rR   s    r   
next_monthrY   t   s%    &&q))4466r   c              #     #    [        U 5      n[        R                  " U5      nX!:  a'  [        U5      nUn[	        X15      nXE4v   UnX!:  a  M&  g g 7fN)rT   rV   to_datetimerY   min)start_dtend_dtcurnxtinifins         r   month_rangesrd   w   sM     
h
C^^F#F
,o#h ,s   AAAc                    U R                  5       (       d  gSnU R                  S5       H6  nUR                  5       (       d  M  XR                  5       R                  -  nM8     U$ )Nr   *)r<   rglobis_filestatst_size)r;   totalr   s      r   _dir_size_bytesrl      sM    ;;==EZZ_99;;VVX%%%E  Lr   c                X    [         R                  " [        U 5      5      nUR                  $ r[   )shutil
disk_usager   free)r;   usages     r   _disk_free_bytesrr      s     c$i(E::r   c                   [        5       nSU  SU S3nUR                  5       R                  USS9R                  5       nUR	                  S/ 5      n[        USS9nU(       a3  UR                  5       R                  US	   S
   US9R                  5         g UR                  5       R                  X/S.US9R                  5         g )N'z' in parents and name='z' and trashed=falsez	files(id))qfieldsfilesT)	resumabler   id)fileId
media_body)namer   )bodyr{   )rG   rw   listexecuter(   r   updatecreate)	folder_idfilename
local_pathserviceru   resrw   medias           r   upload_or_replace_in_driver      s    !G
I;-hZ7JKA
--/

;

7
?
?
ACGGGR EJ$7E8D> 	 	
 ') 	"{; 	 	
 ')r   c                      \ rS rSr% S\S'   S\S'   S\S'   S\S'   S\S'   S\S	'   S\S
'   S\S'   S\S'   S\S'   S\S'   S\S'   S\S'   Srg)EtlFoliosResult   r   statusstartendintfiles_writtenrows_writtenmonths_totalmonths_emptyz	list[str]empty_months
str | Nonelast_nonempty_end	last_filefolios_dir_bytesdisk_free_byteswarnings N)__name__
__module____qualname____firstlineno____annotations____static_attributes__r   r   r   r   r      sM    KJ	H!!r   r      rO   )stop_on_consecutive_emptymin_rows_to_writec                  [        5         [        5       n[        R                  " U 5      n[        R                  " U5      nSnSnSn	Sn
/ n/ nSnSnSn[	        XV5       GH  u  nnU	S-  n	UR                  S5      n[        SUR                  S5       S3-  nUR                  5        n[        R                  " [        UUU4S9nSSS5        WR                  (       d+  S	UR                  ;   a  [        R                  " US	   S
S9US	'   Ub  [        U5      U:  af  U
S-  n
UR                  U5        US-  nU(       aA  X:  a<  UR                  SU SU SUR                  5        SUR                  5        S3	5          OGM"  Sn[        R                   " SSS9nUR#                  UR$                  SS9  UR'                  5         [)        [*        UR$                  UR$                  S9  [,        R.                  " UR$                  5        US-  nU[        U5      -  nUnUnGM     [1        [        5      n[3        [        5      nUS:  a  UR                  S5        US:  a  UR                  S5        [5        SU UUUU	U
USS Ub  UR7                  5       OSUb  [9        U5      OSUUUS9nUS:X  a"  SUl        UR<                  R                  S5        [?        U5      $ ! , (       d  f       GN= f)u   
Construye parquets mensuales folios_YYYY_MM.parquet.
- NO guarda archivos vacíos.
- Opcional: se detiene tras N meses consecutivos vacíos.
- Regresa un resumen (para que /etl/folios lo reporte).
r   NrO   z%Y-%mfolios_z%Y_%mz.parquet)paramsFechacoerce)errorszSe detuvo por u6    meses consecutivos sin datos. Último mes procesado: z (rango u   →z).F)deletesuffix)index)r   r   r   i /hYziCarpeta folios supera ~1.5GB; considera mover parquets a almacenamiento persistente (S3/GCS/Render Disk).i ʚ;u`   Espacio libre en disco bajo (<~1GB). Podrías quedarte sin espacio al seguir generando parquets.oki)r   r   r   r   r   r   r   r   r   r   r   r   r   no_datauZ   No se escribió ningún parquet con datos. Revisa QUERY_FOLIOS_AGG y/o el rango de fechas.) r
   r   rV   r\   rd   strftimer	   connectread_sqlr   emptycolumnslenappenddatetempfileNamedTemporaryFile
to_parquetr|   closer   rM   r1   unlinkrl   rr   r   	isoformatr   r   r   r   )r   r   r   r   enginer^   r_   r   r   r   r   r   r   r   r   consecutive_emptyrb   rc   ymoutconndftmpr   r   r   s                             r   build_folios_parquetr      s    M\F~~e$H^^C FMLLL LH-1!I 2S\\'"WS\\'%:$;8DD^^-tS#JGB 
 xxGrzz1..GXFBwK :R#44AL#" )->-[$%6$7 8..0T#((*STVX    ))zJ
chhe,		"&XXxx	
 			#(( 	B	e 3j 'z2&z2O -'  D  	E&z{
#!!!!#$';L;X+557^b$-$9#i.t)'C" 
xy#;_ s   'K
K'	)r   r   returnr   )r"   r   r   bool)r+   r'   r   r'   r[   )rI   r   rJ   r   r   r   )rS   pd.Timestampr   r   )r^   r   r_   r   )r;   r   r   r   )r   r   r   r   r   r   )
r   r   r   r   r   r   r   r   r   r'   )+
__future__r   dataclassesr   r   pathlibr   r1   rn   pandasrV   r4   r   app.dbr   app.queriesr   app.dwh.pathsr	   r
   google.oauth2r   googleapiclient.discoveryr   r:   googleapiclient.httpr   r   r#   r-   rA   rG   rL   rM   r7   rT   rY   rd   rl   rr   r   r   r   r   r   r   <module>r      s    " )  	      ( 1 ) +
1	2 0	1.[`3 12 
O
PP)7
 1.   ( &'tt	t  #	t
 t 
tr   