
    I{ji                       S r SSKJr  SSKrSSKrSSKJrJr  SSKJr  SSK	J
r
  SSKJrJr  SSKJr  \rS	rS
rSrSr\S   r\\\\S.rS.S jrS/S jrS0S jr\" SS9S1S j5       r\" SS9S2S j5       rS3S jrS4S jrS5S jr        S6S jrS7S jr S8S jr!        S9S jr"          S:S jr#S;S jr$S<S=S jjr%              S>S  jr&S?S! jr'S"S#.S@S$ jjr(SAS% jr)S4S& jr*SS'.                     SBS( jjr+SS'.                   SCS) jjr,SS'.             SDS* jjr-S+S,.               SES- jjr.g)Fz>Subida de archivos a Google Drive (credenciales por customer).    )annotationsN)datetimetimezone)	lru_cache)Path)AnyLiteral)DriveCustomerConfig)z%https://www.googleapis.com/auth/drivez"application/vnd.google-apps.folderentryexit)r   r   )entradasalidainputoutputc                    [         R                  " SSU =(       d    SR                  5       5      nU(       d  [        S5      eU$ )Nz[^a-zA-Z0-9._-]_ u   Segmento de ruta inválido.)resubstrip
ValueError)valuecleaneds     XC:\Users\jesus\Desktop\proyecto_pie360\backend\app\backend\utils\google_drive_storage.py_safe_segmentr      s6    ff'u{.A.A.CDG677N    c                    U =(       d    [         R                  5       R                  5       n[        R	                  X5      nU[         [
        4;  a  [        S5      eU$ )Nzflow debe ser 'entry' o 'exit'.)
FLOW_ENTRYr   lower_LEGACY_FLOWget	FLOW_EXITr   )flowkeys     r   _normalize_flowr%   !   sL    :
$
$
&
,
,
.C


3
$C
:y)):;;Jr   c                    U b  U O-[         R                  " [        R                  5      R                  nUS:  d  US:  a  [        S5      e[        U5      $ )N  4  u   year inválido.)r   nowr   utcyearr   int)r+   r   s     r   _normalize_yearr-   )   sD    $D(,,x||*D*I*IEt|ut|*++u:r       )maxsizec                     SSK Jn  SSKJn  SS KnUR                  U5      nUR                  R                  U[        [        5      S9nU" SSUSS	9$ ! [         a  n[        S5      UeS nAff = f)
Nr   )service_accountbuildOFaltan dependencias Google Drive. Instala: google-api-python-client google-auth)scopesdrivev3Fcredentialscache_discovery)google.oauth2r1   googleapiclient.discoveryr3   ImportErrorr   jsonloadsCredentialsfrom_service_account_infolistDRIVE_SCOPES)	cache_keyservice_account_jsonr1   r3   excr>   infocredss           r   _drive_servicerI   0   s    13 ::*+D''AAL! B E $E5II  3
 	s   A 
A.A))A.c           
     B    SSK Jn  SSKJn  SSKJn  SS KnUR                  U5      nU" UR                  S5      UR                  S5      UR                  S5      =(       d    S	UR                  S
5      UR                  S5      [        [        5      S9nUR                  (       dT  UR                  (       d  UR                  (       d2  UR                   (       d  [        S5      eUR#                  U" 5       5        U" SSUSS9$ ! [         a  n[        S5      UeS nAff = f)Nr   )Request)r@   r2   r4   tokenrefresh_token	token_uriz#https://oauth2.googleapis.com/token	client_idclient_secret)rL   rM   rN   rO   rP   r5   uf   El access_token expiró y no hay refresh_token. Genera un nuevo refresh_token OAuth para este cliente.r6   r7   Fr8   )google.auth.transport.requestsrK   google.oauth2.credentialsr@   r<   r3   r=   r   r>   r?   r!   rB   rC   validexpiredrL   rM   refresh)	rD   
oauth_jsonrK   r@   r3   rF   r>   rG   rH   s	            r   _drive_service_oauthrW   E   s    :93 ::j!Dhhwhh/((;'P+P((;'hh/L!E ;;==&& M  MM')$$E5II3  3
 	s   D 
DDDc                   SS K n[        U SS 5      (       a.  [        U R                  UR	                  U R
                  SS95      $ U R                  (       d  [        S5      e[        U R                  UR	                  U R                  SS95      $ )Nr   
oauth_infoT)	sort_keysz1Drive sin credenciales (OAuth o service account).)	r>   getattrrW   rD   dumpsrY   service_account_infor   rI   )configr>   s     r   _service_for_configr_   g   s    v|T**#JJv((DJ9
 	
 &&LMM

6..$
? r   c           	        [        U 5      nUR                  SS5      nSU SU S[         S3nUR                  5       R	                  USSSS	S	S
9R                  5       nUR                  S5      =(       d    / nU(       a  US   S   $ S $ )N'\'' in parents and name = 'z' and mimeType = '' and trashed = falser6   files(id,name)   TqspacesfieldspageSizesupportsAllDrivesincludeItemsFromAllDrivesfilesr   idr_   replace_FOLDER_MIMErn   rB   executer!   r^   	parent_idnameservice	safe_namequeryresultrn   s           r   _find_child_folderr{   w   s    !&)GS%(I
I; + #n$9	; 
 		#"&* 
 

 
  JJw%2E"58D>,,r   c                    [        XU5      nU(       a  U$ [        U 5      nU[        U/S.nUR                  5       R	                  USSS9R                  5       nUS   $ )N)rv   mimeTypeparentsro   T)bodyrj   rl   )r{   r_   rr   rn   creaters   )r^   ru   rv   existingrw   metacreateds          r   _ensure_folderr      se    !&T:H!&)G ;D 		T$$	?	 
 4=r   c                L    [        XU5      nU(       a  US4$ [        XU5      S4$ )u<   Devuelve (folder_id, created). created=False si ya existía.FT)r{   r   )r^   ru   rv   r   s       r   _ensure_folder_with_statusr      s/     "&T:H&T2D88r   c           
        [        U 5      n/ nS nSU S[         S3n UR                  5       R                  USSSUSSS9R	                  5       nUR                  S	5      =(       d    /  Hn  n[        UR                  S
5      =(       d    S5      n[        UR                  S5      =(       d    S5      n	U(       d  MR  U	(       d  M[  UR                  XS.5        Mp     UR                  S5      nU(       d   U$ M  )Nra   z' in parents and mimeType = 'rd   Tr6   znextPageToken, files(id,name)d   )rh   ri   rj   rk   	pageTokenrl   rm   rn   ro   r   rv   )ro   rv   nextPageToken)r_   rr   rn   rB   rs   r!   strappend)
r^   ru   rw   folders
page_tokenry   rz   ffidrv   s
             r   _list_child_foldersr      s    !&)G$&G!J
I; #n$9	; 
 MMOT6$"&*.   WY 	 G$**AaeeDk'R(CquuV}*+Dsttc89	 +
 ZZ0
N- r   c                x    [        U 5      nUR                  5       R                  USS0SS9R                  5         g )NtrashedT)fileIdr   rl   )r_   rn   updaters   )r^   	folder_idrw   s      r   _trash_folderr      s<    !&)GMMO   gir   c           	     Z   [        U 5      nUR                  SS5      nSU SU S3nUR                  5       R                  USSSSSS	9R	                  5       nUR                  S
5      =(       d    / nU(       d  gU HU  nUR                  S5      [        :X  d  M  [        US   5      [        UR                  S5      =(       d    U5      [        S.s  $    US   n[        US   5      [        UR                  S5      =(       d    U5      [        UR                  S5      =(       d    S5      S.$ )z9Busca archivo o carpeta por nombre exacto bajo parent_id.ra   rb   rc   rd   r6   zfiles(id,name,mimeType)   Trg   rn   Nr}   ro   rv   )ro   rv   r}   r   r   )r_   rq   rn   rB   rs   r!   rr   r   )	r^   ru   rv   rw   rx   ry   rz   rn   r   s	            r   _find_child_by_namer      s$    "&)GS%(I
I; +2	4 

 		,"&* 
 

 
  JJw%2E55,!D'lAEE&M1T2(   	aA!D'lAEE&M)T*j)/R0 r   c           
     B   SSK Jn  [        U5      S:  a  [        S5      e[	        U5      R                  5       R                  S5      nUR                  S5       Vs/ s H  of(       d  M  US;  d  M  UPM     nnU(       d  [        S5      eU" U [        U5      5      nUR                  R                  5       n	U=(       d    SR                  5       =(       d    S	U 3n
[        XU
5      nU(       d  S
S
SS.$ UnUSS  H#  n[        XU5      nU(       d  S
S
SU 3S.s  $ UnM%     [        XUS   5      nU(       d	  S
S
SU 3S.$  [        XS   5        S
SUS   US   US   [        U5       SU
 SU 3S.$ s  snf ! [         a  n[        [        U5      5      UeSnAff = f)zWElimina (papelera) archivo o carpeta en Drive del customer: {root}/{agent_name}/{path}.r   load_customer_drive_configrf      customer_id inválido./.z..u:   No se puede eliminar la carpeta raíz del agente en Drive.r   agent-Tz&Carpeta del agente no existe en Drive.)okskippedmessageNzRuta no encontrada en Drive: z!Elemento no encontrado en Drive: ro   Frv   r}   )r   r   file_idrv   	mime_type
drive_path)'app.backend.utils.customer_drive_configr   r,   r   r   as_posixr   splitroot_folder_idr{   r   r   	Exception_drive_api_error_message)dbcustomer_id
agent_namerelative_pathr   posixppartsr^   root_idagent_labelagent_folder_idru   segmentfoundtargetrF   s                    r   delete_from_agent_folderr      s    S
;!122((*005EC(G(1AQ!;2FQ(EGUVV'C,<=F##))+G#**,F&0FK(+FO?
 	
  I":"6g>:5'B 
 	  !E"I>F:5'B
 	
AfTl+
 $<vJ'[)*!K=%A Q HJ  A1#67S@As*   
E5*E52E5E: :
FFFc                X    [        U 5      nUR                  5       nSU;   d  SU;   a   gU$ )Nstoragequotaexceededzstorage quotau  La cuenta de servicio no tiene cuota en My Drive. Usa una Shared Drive (unidad compartida), agrega la cuenta de servicio como miembro con permiso de administrador de contenido, y pon el ID de una carpeta dentro de esa Shared Drive como carpeta raíz de Agentes.)r   r   )rF   textr   s      r   r   r   =  s7    s8DJJLE&/U*BW	
 Kr   c           	        SSK Jn  SSKJnJn  U R                  U5      R                  UR                  R                  S5      5      nUb6  [        U5      S:  a'  UR                  UR                  [        U5      :H  5      nUR                  UR                  R                  5       UR                  R                  5       5      R                  5       n0 nU H9  n[        UR                  5      n	UR                  U	/ 5      R                  U5        M;     Ub,  [        U5      S:  a  [        U5      U;  a  / U[        U5      '   / n
SSSSSSSS.n[!        UR#                  5       5       GH  u  pU" X	5      (       d  U
R                  U	SS/ S.5        M+   U" X	5      nUS	==   S
-  ss'   UR(                  nU Vs1 s HP  nUR                  =(       d    SR+                  5       (       d  M-  UR                  =(       d    SR+                  5       iMR     nn[-        X5       HK  nUR/                  S5      =(       d    SnUU;  d  M%  U(       d  M.   [1        UUS   5        US==   S
-  ss'   MM     / nU H  nUR                  =(       d    SR+                  5       nU(       d  M/  [5        XU5      u  nnU(       a  US==   S
-  ss'   OUS==   S
-  ss'   UR                  UR6                  UUUS.5        M     U
R                  U	SUSUS.5        GM     SSUS    SUS    SUS    S3SUU
S.$ ! [$         a+  nU
R                  U	S['        U5      / S.5         SnAGM  SnAff = fs  snf ! [2         a     GMY  f = f)z~
En el Drive del customer: {root}/{agent_name}/
Si customer_id es None, sincroniza cada customer que tenga Drive configurado.
r   )
AgentModelcustomer_drive_configuredr   N)customers_touchedcustomer_folders_createdcustomer_folders_existingcustomer_folders_deletedagent_folders_createdagent_folders_existingagent_folders_deletedFz(Drive no configurado para este customer.)r   r   r   agentsr   rf   r   rv   ro   r   r   r   )agent_idrv   r   r   T)r   r   r   r   r   zSincronizado por customer: +z
 creadas, z existentes, -z eliminadas.)r   r   r   summary	customers)app.backend.db.models.agentr   r   r   r   ry   filterr   isnotr,   order_byascrv   all
setdefaultr   sorteditemsr   r   r   r   r   r!   r   r   r   ro   )r   r   r   r   r   agents_qr   by_customeragentcidcustomers_outr   
agent_listr^   rF   r   awanted_nameschildrv   
agent_rowsr   r   s                          r   sync_customer_agent_foldersr   I  s{   
 7
 xx
#**:+A+A+G+G+MNH3{#3a#7??:#9#9S=M#MNz5599;Z__=P=P=RSWWYF(*K%##$sB'..u5  3{#3a#7C<LT_<_(*C$%*,M$%%&$%!""#!"G "+"3"3"56(11  #&I 	 	/8F 	#$)$''8B]
1qvv|QSFZFZF\.2,,.
](9E99V$*D<'DD!&%+63494 : ,.
EJJ$"++-D!;FT!RIw/0A5001Q61 % !*&	  $ 	"$ $	
g 7| *73J+K*LJ/01 2/01? "
 
a  	   #5SXQST 		 ^ ! s6   L)7,M!''M!
M&)
M3MM&
M54M5c           	        US:  a  [        S5      eUS:  a  [        S5      eU R                  R                  5       n[        U5      n[	        U5      n[        [        U5      5      n	[        [        U5      5      n
[        [        U5      5      n[        [        U5      5      nUnXXU4 H  n[        XU5      nM     U	 SU
 SU SU SU 3	nX4$ )u   Crea la jerarquía si no existe y devuelve (folder_id, ruta lógica).

Ruta: {school_id}/{year}/{entry|exit}/{document_id}/{student_id}/
rf   zdocument_id debe ser >= 1.zstudent_id debe ser >= 1.r   )r   r   r   r-   r%   r   r   r   )r^   	school_idr+   r#   document_id
student_idr   yflow_key
school_segyear_segdoc_segstudent_segparentr   logicals                   r   resolve_target_folderr     s     Q566A~455##))+GAt$Hs9~.JSV$HC,-GJ0KF([I8 J AhZq
!G9Ak]KG?r   c                    [        U 5      R                  R                  5       nU(       a  US;   a  [        S5      eUR	                  SS5      R	                  SS5      $ )Nr   u   Nombre de archivo inválido.r   r   \)r   rv   r   r   rq   )filenamerv   s     r   _safe_filenamer     sN    >$$&D4;&788<<S!))$44r   z
Sin nombrefallbackc                   U =(       d    SR                  5       nUR                  SS5      R                  SS5      R                  SS5      n[        R                  " SSU5      R                  5       nU(       a  USS	 $ U$ )
zONombre de carpeta legible (conserva espacios/acentos; sin separadores de ruta).r   r   -r    z\s+ N   )r   rq   r   r   )r   r   r   s      r   _drive_folder_labelr     sl    KR D<<S!))$4<<T2FD66&#t$**,DD#J-H-r   c                    [         R                  " SSU =(       d    SR                  5       5      nU(       d  [        S5      eUR	                  5       $ )u@   RUT solo alfanumérico (sin puntos ni guión), p. ej. 274309032.z[^0-9kK]r   u)   RUT del alumno no disponible o inválido.)r   r   r   r   upper)rutr   s     r   _numeric_rutr     s<    ff["syb&7&7&9:GDEE==?r   c           	        [        U 5      nUR                  SS5      nSU SU S[         S3nUR                  5       R	                  USSSS	S	S
9R                  5       nUR                  S5      =(       d    / nU(       a  US   S   $ S $ )Nra   rb   rc   z' and mimeType != 'rd   r6   re   rf   Trg   rn   r   ro   rp   rt   s           r   _find_child_filer    s    !&)GS%(I
I; + $~%:	< 
 		#"&* 
 

 
  JJw%2E"58D>,,r   )r   c        
        `   SSK Jn
Jn  U(       d  [        S5      e[	        U5      S:  a  [        S5      eU
" U [	        U5      5      (       d  [        S5      eU" U [	        U5      5      nUR
                  R                  5       n[        U5      n[        [	        U5      5      n[	        U5      S:  d  [	        U5      S:  a  [        S	5      e[        US
S9n[        USS9n[        USS9nU=(       d    SR                  5       R                  S5      nUS;  a  Sn[        U SU SU 35      n [        XU5      n[        UUU5      n[        UUU5      n[        UUU5      n[        U5      n SSKJn  U	=(       d    US:X  a  SO	US:X  a  SOSn[%        U5      S:  nU" [&        R(                  " U5      UUS9n[+        UUU5      nU(       a3  UR-                  5       R/                  UUSSS9R1                  5       n U n!Sn"O7UU/S.n#UR-                  5       R3                  U#USSS9R1                  5       n!S n" U S!U S!U S!U S!U 3	n$SU!R9                  S"5      U!R9                  S#5      =(       d    UU!R9                  S$5      =(       d    U[	        U!R9                  S%5      =(       d    [%        U5      5      U!R9                  S&5      U$U"[	        U5      U[	        U5      UUUUR:                  S'.$ ! ["         a  n[        S5      UeSnAff = f! [4         a  n[        [7        U5      5      UeSnAff = f)(u   
Sube a Drive del customer con árbol:
  {root}/{Liceo}/{Año}/{Curso}/{RUT numérico}/{RUT_Tipo de documento}.{ext}
Crea carpetas si no existen. Si el archivo ya existe, lo reemplaza.
r   r      El archivo está vacío.rf   r   uc   Google Drive no está configurado para este cliente. Conéctalo en Configuración → Google Drive.r'   r(   u   Año inválido.Liceor   Curso	Documentodocxr   >   docpdfr  r   MediaIoBaseUpload#   googleapiclient no está instalado.NzGapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentr
  zapplication/pdfapplication/octet-stream  P mimetype	resumable!id,name,mimeType,size,webViewLinkT)r   
media_bodyrj   rl   rv   r~   r   r  rj   rl   Fr   ro   rv   r}   sizewebViewLink)r   r   r   r   
size_bytesweb_view_linkr   replacedr   school_namer+   course_namestudent_rut_numericdocument_type_namedrive_config_source)r   r   r   r   r,   r   r   r   r   r   r   lstripr   r   r_   googleapiclient.httpr  r=   lenioBytesIOr  rn   r   rs   r   r   r   r!   source)%r   r   r  r+   r  student_rutr  datafile_extensionr   r   r   r^   r   rut_num
year_labelliceocursodoc_typeextr   liceo_idyear_id	course_idstudent_folder_idrw   r  rF   mimer  mediaexisting_idupdated	file_metar  r   r   s%                                        r   upload_student_document_treer9    sT   $
 344
;!122$R[)9::=
 	

 (C,<=F##))+G;'GSYJ
4y43t9t+*++g>Eg>E"#5LH#V
*
*
,
3
3C
8C
((	8*AcU;<H2A!&59 :>"67E:	*69gF%f-	M>  
f} V e| #+ 	 I0	!"**T"2TYW&v/@(K&$>&*	      IH$2C1DED$>&*	     H qAeWAgYazBG==&MM&)5X]]:.6$)--/<3t9="}5;'D	&&%}} Y  	MBCL	MP  A1#67S@AsC   ->L	 ,K+ 2BL	 >6L	 +
L5LLL	 	
L-L((L-c        	           U(       d  [        S5      e[        U5      n	[        U UUUUUS9u  p[        U 5      n SSKJn  U" [        R                  " U5      U=(       d    SSS9nX/S	.nUR                  5       R                  UUS
SS9R                  5       n[        U5      n[        U5      nSUR                  S5      UR                  S5      =(       d    U	UR                  S5      [        UR                  S5      =(       d    [!        U5      5      UR                  S5      U SU	 3UUUUUU R"                  S.$ ! [         a  n[        S5      UeS nAff = f)Nr  r^   r   r+   r#   r   r   r   r  r  Fr  r  r  r  Tr  ro   rv   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%  r=   rn   r   rs   r-   r%   r!   r,   r#  r&  )r^   r   r+   r#   r   r   r   r(  r   rv   r   logical_pathrw   r  r5  rF   r   r   r   r   s                       r   upload_bytesr=    si    344(#D3I "&)G	I:!JJt<"<
 [1D	6"	 
 

 
  	At$H;;t$KK'/4[[,'++f-:T; ]3%av." %}} !  I>?SHIs   +D< <
EEEc                F   SSK Jn  U(       d  [        S5      e[        U5      S:  a  [        S5      eU" U [        U5      5      nUR                  R                  5       nU=(       d    SR                  5       =(       d    SU 3n	 [        XxU	5      n
[        U5      R                  5       R                  S5      nUR                  S5       Vs/ s H  o(       d  M  US	;  d  M  UPM     nnU(       d  [        S
5      eU
nUSS  H  n[        X~U5      nM     [        US   5      n[        U5      n SSKJn  [        U5      S:  nU" [         R"                  " U5      U=(       d    SUS9nUU/S.nUR%                  5       R'                  UUSSS9R)                  5       nU	 SSR/                  USS U/-   5       3R1                  SS5      nSUR3                  S5      UR3                  S5      =(       d    UUR3                  S5      [        UR3                  S5      =(       d    [        U5      5      UR3                  S5      U[        U5      U	UR4                  S.
$ s  snf ! [         a  n[        S5      UeSnAff = f! [*         a  n[        [-        U5      5      UeSnAff = f)uK   Sube un archivo a Drive del customer: {root}/{agent_name}/[parent…]/file.r   r   r  rf   r   r   r   r   r   u   Ruta de archivo inválida.Nr   r  r  r  r  r  r  r  Tr  z//ro   rv   r}   r  r  )
r   r   r   r   r  r  r   r   r   r   )%app.backend.utils.school_drive_configr   r   r,   r   r   r   r   r   r   r   r_   r"  r  r=   r#  r$  r%  rn   r   rs   r   r   joinrq   r!   r&  )r   r   r   r   r(  r   r   r^   r   r   r   r   r   r   ru   r   rv   rw   r  rF   r  r5  r   r   r   s                            r   upload_to_agent_folderrA    s    Q344
;!122'C,<=F##))+G#**,F&0FK(A(+F]#,,.44S9!KK,K,qa{6J,K9::#	SbzG&v'BI "eBi(%f-	M>
 I0	!JJt<"<
 )5MMOV :"&	   WY 	 Qsxxcr
dV(;<=>FFtSQG;;t$KK'/4[[,'++f-:T; ]3;'!%}} M L  	MBCL	M,  A1#67S@As]   AI< 
II"I(AI< 0I 6A'I< I< 
I9(I44I99I< <
J JJ 2   )	page_sizec                V   [        U UUUUUS9u  px[        U 5      n	U	R                  5       R                  SU S3SS[	        [        US5      S5      SS	S	S
9R                  5       n
U
R                  S5      =(       d    / n[        U5      n[        U5      nS	UUUUUUU R                  US.	$ )Nr;  ra   z ' in parents and trashed = falser6   zAfiles(id,name,mimeType,size,webViewLink,createdTime,modifiedTime)rf   r   zmodifiedTime descT)rh   ri   rj   rk   orderByrl   rm   rn   )	r   r   r+   r#   r   r   r   r   rn   )r   r_   rn   rB   minmaxrs   r!   r-   r%   r&  )r^   r   r+   r#   r   r   rC  r   r<  rw   rz   rn   r   r   s                 r   list_folder_filesrH    s     4I "&)G	)<=VY*C0'"&* 
 

 
  JJw%2EAt$H" "%}}
 
r   )r   r   returnr   )r#   r   rI  	DriveFlow)r+   
int | NonerI  r,   )rD   r   rE   r   )rD   r   rV   r   )r^   DriveSchoolConfig)r^   rL  ru   r   rv   r   rI  
str | None)r^   rL  ru   r   rv   r   rI  r   )r^   rL  ru   r   rv   r   rI  ztuple[str, bool])r^   rL  ru   r   rI  zlist[dict[str, str]])r^   rL  r   r   rI  None)r^   rL  ru   r   rv   r   rI  zdict[str, str] | None)
r   r   r   r,   r   r   r   r   rI  dict[str, Any])rF   BaseExceptionrI  r   )N)r   rK  rI  rO  )r^   rL  r   r,   r+   rK  r#   r   r   r,   r   r,   rI  ztuple[str, str])r   r   rI  r   )r   r   r   r   rI  r   )r   r   rI  r   )r   r   r   r,   r  r   r+   r,   r  r   r'  r   r  r   r(  bytesr)  r   r   rM  rI  rO  )r^   rL  r   r,   r+   rK  r#   r   r   r,   r   r,   r   r   r(  rQ  r   rM  rI  rO  )r   r   r   r,   r   r   r   r   r(  rQ  r   rM  rI  rO  )r^   rL  r   r,   r+   rK  r#   r   r   r,   r   r,   rC  r,   rI  rO  )/__doc__
__future__r   r$  r   r   r   	functoolsr   pathlibr   typingr   r	   r   r
   rL  rC   rr   r   r"   rJ  r    r   r%   r-   rI   rW   r_   r{   r   r   r   r   r   r   r   r   r   r   r   r   r  r9  r=  rA  rH   r   r   <module>rX     sF   D " 	 	 '    G ( 93
	O$	%Z[de 2J J( 2J JB -0$99*-95899B&&*-&58&&R<< < 	<
 < <~
jZ     	 
        F5 8D .-F !uu u 	u
 u u u u u u u uD !?? ? 	?
 ? ? ? ? ? ? ?R !KK K 	K
 K K K Kl -- - 	-
 - - - - -r   