
    niJ                        S r SSKJr  SSKrSSKrSSKJrJrJr  SSKJ	r	J
r
Jr  SSKrSSS jjr " S S5      rSS	 jrSS
 jrg)u  
HTTP client for Inspection API (configurable base URL).
Login: POST multipart form-data → Bearer token.
Remote endpoints (multipart field `rut`): /getDatosAlumno, /getDatosFuncionario.

Env:
  INSPECTION_API_BASE_URL  (default: https://liceomixto.inspection.cl/api)
  INSPECTION_API_USERNAME
  INSPECTION_API_PASSWORD
  INSPECTION_API_TIMEOUT   (seconds, default 30)
  INSPECTION_API_TEACHINGS_PATH  (default: listado/tipos-ensenanzas) — GET tipos global (legacy)
  INSPECTION_API_COURSES_PATH   (default: listado/cursos) — POST multipart colegio + anio
  INSPECTION_API_STUDENTS_PATH  (default: listado/alumnos) — POST multipart colegio + anio
  INSPECTION_API_SCHOOLS_PATH  (default: listado/colegios) — GET colegios con tiposEnsenanzas (import enseñanzas por colegio activo)
  INSPECTION_API_NATIONALITIES_PATH (default: listado/nacionalidades) — GET catálogo nacionalidades (import)
  INSPECTION_API_REGIONS_PATH (default: listado/regiones) — GET catálogo regiones (import)
  INSPECTION_API_PROVINCES_PATH (default: listado/provincias) — GET catálogo provincias (import)
  INSPECTION_API_COMMUNES_PATH (default: listado/comunas) — GET catálogo comunas (import)
    )annotationsN)datetime	timedeltatimezone)AnyDictOptionalc                    [         R                  R                  U 5      nUb  [        U5      R	                  5       S:w  a  U$ U$ )N )osenvirongetstrstrip)namedefaultvs      [C:\Users\jesus\Desktop\proyecto_pie360\backend\app\backend\classes\inspection_api_client.py_envr      s4    


tA#a&,,.B"61CGC    c                  .   \ rS rSr% Sr\R                  " 5       rSrS\	S'   Sr
S\	S'   S rSS	 jrSS
 jrSS 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 jjrS'S jrS S jrS S jrS S jrS S jrS S jrS S jrS(S jrS(S jrS S jrS)S jrSr g)*InspectionApiClient$   zIToken en memoria por worker (gunicorn); se renueva al expirar o ante 401.NOptional[str]_tokenOptional[datetime]_expires_atc                   [        S5      =(       d    SR                  S5      U l        [        S5      U l        [        S5      U l         [        [        S5      =(       d    S5      U l        g ! [         a
    SU l         g f = f)	NINSPECTION_API_BASE_URLz$https://liceomixto.inspection.cl/api/INSPECTION_API_USERNAMEINSPECTION_API_PASSWORDINSPECTION_API_TIMEOUT30g      >@)r   rstripbase_urlusernamepasswordfloattimeout
ValueErrorselfs    r   __init__InspectionApiClient.__init__+   sm    78b<bjjkno6767	  &>!?!G4HDL 	 DL	 s   
"A- -B Bc                R    [        U R                  =(       a    U R                  5      $ N)boolr'   r(   r,   s    r   is_configured!InspectionApiClient.is_configured4   s    DMM3dmm44r   c                   U R                   (       d  gU R                  (       d  g[        R                  " [        R
                  5      nU R                  nUR                  c  UR                  [        R
                  S9nX:  $ )NFTtzinfo)r   r   r   nowr   utcr7   replace)r-   r8   exps      r   _token_valid InspectionApiClient._token_valid7   s[    {{ll8<<(::++X\\+2Cyr   c                   Uc  g [        U5      R                  5       nU(       d  g  SU;   a'  [        R                  " UR	                  SS5      5      nO[        R
                  " US S S5      nUR                  c  UR	                  [        R                  S9nU$ ! [         a1    [        R                  " [        R                  5      [        SS9-   s $ f = f)	NTZz+00:00   z%Y-%m-%d %H:%M:%Sr6      hours)r   r   r   fromisoformatr:   strptimer7   r   r9   	Exceptionr8   r   )r-   rawsdts       r   _parse_expires"InspectionApiClient._parse_expiresB   s    ;HNN
	Cax++AIIc8,DE&&q"v/BCyy ZZx||Z4I 	C<<-	0BBB	Cs   A2B 8CCc                f   U R                  5       (       d  SSS S.$ U R                   S3nS [        U R                  5      4S [        U R                  5      4S.n [
        R                  " XU R                  S9n UR                  5       nUR                  S	:  d  UR                  S
5      (       d:  SUR                  S5      =(       d    SUR                   3UR                  S5      S.$ UR                  S5      =(       d    0 nUR                  S5      nU(       d  SSS S.$ U R                     X`R                  l        U R!                  UR                  S5      5      nU=(       d.    ["        R$                  " [&        R(                  5      [+        SS9-   U R                  l        S S S 5        SUR                  S5      =(       d    SUS.$ ! [         a    SSUR                  S S  3S S.s $ f = f! , (       d  f       NR= f! [
        R.                   a  nS[        U5      S S.s S nA$ S nAff = f)NFz9INSPECTION_API_USERNAME / INSPECTION_API_PASSWORD not setokmessagedataz/login)r'   r(   )filesr*   Non-JSON response:      rO   rP   HTTP rQ   tokenz!Login response missing data.token
expires_atrB   rC   TOK)r3   r&   r   r'   r(   requestspostr*   jsonrG   textstatus_coder   _lock	__class__r   rK   r   r8   r   r9   r   r   RequestException)	r-   urlrR   rbodyrQ   rW   r;   es	            r   loginInspectionApiClient.loginT   s   !!##,gquvvv& s4==12s4==12
	BcEAdvvx }}#488D>>#0C0^q}}oG^hlhphpqwhxyy88F#)rDHHW%E#0S]abb(-%))$((<*@A-0-eX\\(,,5OR[bcRd5d* 
 488I+>+F$PTUU!  d#2EaffTcl^0T^bccd  (( 	BCFDAA	Bsh   H ;G AH +7H #H /A7G2&%H  G/,H .G//H 2
H <H H0H+%H0+H0c                   U R                      U R                  5       (       a  U R                  sS S S 5        $  S S S 5        U R                  5       (       d  g U R	                  5       nUR                  S5      (       d  g U R                      U R                  sS S S 5        $ ! , (       d  f       Nm= f! , (       d  f       g = f)NrO   )r_   r<   r   r3   rf   r   )r-   ress     r   get_bearer_token$InspectionApiClient.get_bearer_tokenv   s    ZZ  ""{{ Z"  !!##jjlwwt}}ZZ;; Z Z Zs   !B 
B1 
B.1
B?c                b   U=(       d    SR                  5       nU(       d  SSSS.$ U R                  5       nU(       d  SSSS.$ UR                  S5      nU R                   SU 3nSS	U 30nS
SU40Ub  SS[	        U5      40O0 En [
        R                  " XVXpR                  S9n UR                  5       n	UR                  S:X  a  U R                     SU R                  l        SU R                  l        SSS5        U R                  5       n
U
(       a7  [
        R                  " USS	U
 30UU R                  S9n UR                  5       n	UR                  S:  a:  SU	R!                  S5      =(       d    SUR                   3U	R!                  S5      S.$ [#        U	[$        5      (       a  U	$ SSSS.$ ! [         a    SSUR                  SS  3SS.s $ f = f! , (       d  f       N= f! [         a	    SSSS.s $ f = f! [
        R&                   a  nS[	        U5      SS.s SnA$ SnAff = f)zo
POST {base}/{remote_path} with form field rut (multipart). Used for getDatosAlumno, getDatosFuncionario, etc.
r   FzRUT is requiredNrN   8Inspection API authentication failed (check credentials)r    AuthorizationBearer rutanioheadersrR   r*   rS   rT     Retry after 401 failedrU   rP   rV   rQ   Invalid response body)r   rj   lstripr&   r   rZ   r[   r*   r\   rG   r]   r^   r_   r`   r   r   r   
isinstancedictra   )r-   remote_pathrp   rq   rW   rb   rs   rR   rc   rd   token2re   s               r   _post_multipart_rut'InspectionApiClient._post_multipart_rut   s?    yb!,=tLL%%',fptuu!((-q."geW$56D#;
.2.>s4y)*B

!	Bc%VAdvvx }}#ZZ,0DNN)15DNN.   ..0 !0GF82D E# $	A` vvx }}##xx	2Mamm_6M HHV,  &dD114vePgqu7vv7  d#2EaffTcl^0T^bccd  Z % `&+8PZ^__` (( 	BCFDAA	Bs   H (F4 8H #G7AH =G+ A	H H .H 4 GH GH 
G($H +G>;H =G>>H H.H)#H.)H.c           	     X   U R                  5       nU(       d  SSSS.$ UR                  S5      nU R                   SU 3nSSU 30nUR                  5        VVs0 s H  u  pgUc  M
  US[	        U5      4_M     nnn [
        R                  " XEXR                  S9n	 U	R                  5       n
U	R                  S:X  a  U R                     SU R                  l        SU R                  l        SSS5        U R                  5       nU(       a7  [
        R                  " USSU 30UU R                  S9n	 U	R                  5       n
U	R                  S:  a:  SU
R!                  S5      =(       d    SU	R                   3U
R!                  S5      S.$ [#        U
[$        5      (       a  U
$ SSSS.$ s  snnf ! [         a    SS	U	R                  SS
  3SS.s $ f = f! , (       d  f       N= f! [         a	    SSSS.s $ f = f! [
        R&                   a  nS[	        U5      SS.s SnA$ SnAff = f)zJPOST multipart/form-data con campos arbitrarios (solo texto), Bearer auth.Frm   NrN   r    rn   ro   rr   rS   rT   rt   ru   rU   rP   rV   rQ   rv   )rj   rw   r&   itemsr   rZ   r[   r*   r\   rG   r]   r^   r_   r`   r   r   r   rx   ry   ra   )r-   rz   fieldsrW   rb   rs   kr   rR   rc   rd   r{   re   s                r   _post_multipart_form(InspectionApiClient._post_multipart_form   s"   %%',fptuu!((-q."geW$56/5||~O~tq"T3q6N"~O!	Bc%VAdvvx }}#ZZ,0DNN)15DNN.   ..0 !0GF82D E# $	A` vvx }}##xx	2Mamm_6M HHV,  &dD114vePgqu7vvC P  d#2EaffTcl^0T^bccd  Z % `&+8PZ^__` (( 	BCFDAA	Bs   	F)'F)=G< F/ -G< 	#G,AG< 2G& A	G< G< #G< / GG< GG< 
G#G< &G96G< 8G99G< <H)H$H)$H)c                   U R                  5       nU(       d  SSS S.$ UR                  S5      nU R                   SU 3nSSU 30n [        R                  " X4U R
                  S9n UR                  5       nUR                  S
:X  a  U R                     S U R                  l        S U R                  l        S S S 5        U R                  5       nU(       a6  [        R                  " USSU 30U R
                  S9n UR                  5       nUR                  S:  a:  SUR	                  S5      =(       d    SUR                   3UR	                  S5      S.$ [        U[        5      (       a  U$ SSS S.$ ! [         a    SSUR                  S S	  3S S.s $ f = f! , (       d  f       N= f! [         a	    SSS S.s $ f = f! [        R                    a  nS[#        U5      S S.s S nA$ S nAff = f)NFrm   rN   r    rn   ro   )rs   r*   rS   rT   rt   ru   rU   rP   rV   rQ   rv   )rj   rw   r&   rZ   r   r*   r\   rG   r]   r^   r_   r`   r   r   rx   ry   ra   r   )	r-   rz   rW   rb   rs   rc   rd   r{   re   s	            r   _get_with_bearer$InspectionApiClient._get_with_bearer   s   %%',fptuu!((-q."geW$56 	BS4<<HAdvvx }}#ZZ,0DNN)15DNN.   ..0 !0GF82D E $A
` vvx }}##xx	2Mamm_6M HHV,  &dD114vePgqu7vv5  d#2EaffTcl^0T^bccd  Z % `&+8PZ^__` (( 	BCFDAA	Bs   G  (E3 8G  #F7AG  <F* A	G  G  -G  3 FG  FG  
F'#G  *F=:G  <F==G   G-G("G-(G-c                &    U R                  SX5      $ )u9   POST /getDatosAlumno — remote path fixed by Inspection.getDatosAlumnor|   )r-   rp   rq   s      r   fetch_student_data&InspectionApiClient.fetch_student_data  s    ''(8#DDr   c                &    U R                  SU5      $ )uZ   POST /getDatosFuncionario — staff/professional by RUT (remote path fixed by Inspection).getDatosFuncionarior   )r-   rp   s     r   fetch_professional_data+InspectionApiClient.fetch_professional_data  s    ''(=sCCr   c                j    [        S5      =(       d    SR                  S5      nU R                  U5      $ )u{   GET listado/comunas — catálogo remoto de comunas (`id`, `nombre`, `provincia_id` → resolver región vía `provinces`).INSPECTION_API_COMMUNES_PATHzlistado/comunasr    r   rw   r   r-   paths     r   fetch_communes_list'InspectionApiClient.fetch_communes_list  s0    34I8IQQRUV$$T**r   c                "    U R                  5       $ )uZ   Alias: mismo catálogo que provincias (compatibilidad con rutas `/regions/endpoint/list`).)fetch_provinces_listr,   s    r   fetch_regions_list&InspectionApiClient.fetch_regions_list  s    ((**r   c                j    [        S5      =(       d    SR                  S5      nU R                  U5      $ )u:   GET listado/provincias — catálogo remoto de provincias.INSPECTION_API_PROVINCES_PATHzlistado/provinciasr    r   r   s     r   r   (InspectionApiClient.fetch_provinces_list#  s0    45M9MUUVYZ$$T**r   c                j    [        S5      =(       d    SR                  S5      nU R                  U5      $ )uM   GET listado/regiones — catálogo remoto de regiones (configurable por env).INSPECTION_API_REGIONS_PATHzlistado/regionesr    r   r   s     r   fetch_regiones_list'InspectionApiClient.fetch_regiones_list(  0    23I7IQQRUV$$T**r   c                j    [        S5      =(       d    SR                  S5      nU R                  U5      $ )uT   GET listado/nacionalidades — `{ ok, data: [{ id, gentilicio, pais, iso }, ...] }`.!INSPECTION_API_NATIONALITIES_PATHzlistado/nacionalidadesr    r   r   s     r   fetch_nationalities_list,InspectionApiClient.fetch_nationalities_list-  s0    89U=U]]^ab$$T**r   c                j    [        S5      =(       d    SR                  S5      nU R                  U5      $ )uP   GET listado de tipos de enseñanzas (Inspection: /api/listado/tipos-ensenanzas).INSPECTION_API_TEACHINGS_PATHzlistado/tipos-ensenanzasr    r   r   s     r   fetch_teachings_list(InspectionApiClient.fetch_teachings_list2  s0    45S9S[[\_`$$T**r   c                    [        S5      =(       d    SR                  S5      nU R                  U[        U5      [        U5      S.5      $ )u   
POST `{base}/listado/cursos` — multipart/form-data, Bearer.
Campos: `colegio` (id establecimiento), `anio` (año matrícula).
Respuesta típica: `{ ok, message, data: [{ id, nombre, nivel, letra, tipo_ensenanza_id, colegio_id, anio, ... }] }`.
INSPECTION_API_COURSES_PATHzlistado/cursosr    colegiorq   r   rw   r   intr-   
colegio_idrq   r   s       r   fetch_courses_list&InspectionApiClient.fetch_courses_list7  sD     23G7GOOPST((3z?TWX\T]/^__r   c                    [        S5      =(       d    SR                  S5      nU R                  U[        U5      [        U5      S.5      $ )u   
POST `{base}/listado/alumnos` — multipart/form-data, Bearer.
Campos: `colegio` (id establecimiento), `anio` (año matrícula).
INSPECTION_API_STUDENTS_PATHzlistado/alumnosr    r   r   r   s       r   fetch_students_list'InspectionApiClient.fetch_students_list@  sD    
 34I8IQQRUV((3z?TWX\T]/^__r   c                j    [        S5      =(       d    SR                  S5      nU R                  U5      $ )z<GET listado de colegios (Inspection: /api/listado/colegios).INSPECTION_API_SCHOOLS_PATHzlistado/colegiosr    r   r   s     r   fetch_schools_list&InspectionApiClient.fetch_schools_listH  r   r   c                    [        U5      nU R                  5       nUR	                  S5      (       d  U$ UR	                  S5      n[        U[        5      (       d  SSSS.$ SnU H=  n[        U[        5      (       d  M   [        UR	                  S5      5      U:X  a  Un  OM?     Uc
  SS	U S
3SS.$ UR	                  S5      =(       dA    UR	                  S5      =(       d)    UR	                  S5      =(       d    UR	                  S5      nUc  / n[        U[        5      (       d  SSSS.$ SUR	                  S5      =(       d    SUS.$ ! [        [        4 a	    SSSS.s $ f = f! [        [        4 a     GM  f = f)u   
GET listado/colegios y devuelve solo los tipos de enseñanza del colegio cuyo id remoto
coincide con school_id (mismo id que en BD / sesión).

Formato esperado por colegio: tiposEnsenanzas | tipos_ensenanzas → [{ id, codigo, nombre }, ...]
Fu   school_id inválidoNrN   rO   rQ   z9Respuesta de Inspection: data no es una lista de colegiosidu"   No se encontró el colegio con id=zW en el listado de Inspection (el id del colegio en BD debe coincidir con el id remoto).tiposEnsenanzastipos_ensenanzastiposEnsenanzatipos_ensenanzaz8tiposEnsenanzas no es una lista en el colegio encontradoTrP   rY   )r   	TypeErrorr+   r   r   rx   listry   )r-   	school_idsidremoteschoolsmatchrI   tiposs           r   !fetch_teachings_for_active_school5InspectionApiClient.fetch_teachings_for_active_schoolM  s   	Qi.C ((*zz$M**V$'4((V  +/Aa&&quuT{#s*E +	  =8 >Q Q   II'( ,yy+,,yy)*, yy*+	 	 =E%&&U  zz),4
 	
g :& 	Q,A4PP	Q. z* s#   E
   E&
E#"E#&E;:E;)r&   r(   r*   r'   )returnr2   )rH   r   r   r   )r   Dict[str, Any])r   r   r1   )rz   r   rp   r   rq   
int | Noner   r   )rz   r   r   r   r   r   )rz   r   r   r   )rp   r   rq   r   r   r   )rp   r   r   r   )r   r   rq   r   r   r   )r   r   r   r   )!__name__
__module____qualname____firstlineno____doc__	threadingLockr_   r   __annotations__r   r.   r3   r<   rK   rf   rj   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   __static_attributes__ r   r   r   r   $   s    SNNE FM &*K#* 5	C$ BD
5Bn,B\)BVED+
++
+
+
+
``+
@
r   r   c                    U HR  nU R                  U5      nUc  M  [        U5      R                  5       (       d  M9  [        U5      R                  5       s  $    g r1   )r   r   r   )rQ   keysr   r   s       r   _first_valuer     sA    HHQK=SV\\^^q6<<>!  r   c                v   [        U 5      nUR                  S5      (       d  U$ UR                  S5      nUc  U$ [        U[        5      (       a/  [	        U5      S:  a   [        US   [         5      (       a  US   O0 nO[        U[         5      (       a  UnOU$ [        US5      nU(       a  XRS'   [        US5      nU(       a  XbS'   [        US5      nU(       a  XrS	'   [        US
5      nU(       a  XS'   [        US5      n	U	(       a  XS'   [        US5      n
U
(       a  XS'   [        US5      nU(       a  XS'   U$ )z
Merge fields returned by the remote student endpoint onto student_inputs (keeps existing keys if no match).
api_payload: full remote JSON { ok, message, data }.
rO   rQ   r   )nombresnombrenombre_alumnonombres_alumnonamesr   )apellido_paternoapellidoPaterno
apellido_pfather_lastnameprimer_apellidor   )apellido_maternoapellidoMaterno
apellido_mmother_lastnamesegundo_apellidor   )rp   identification_numberidentificacionr   )emailcorreocorreo_electronicomailr   )telefonou	   teléfonophonecelularmovilu   móvilr   )fecha_nacimientofechaNacimiento	born_date
birth_date	fecha_nacr   )ry   r   rx   r   lenr   )student_inputsapi_payloadoutrH   rQ   nvapamrp   r   r   borns               r   $merge_inspection_into_student_inputsr    sS   
 ~
C??4  

//&
!C
{
#tSA#CFD11s1vr	C		
 
	
	
B 
G		
	
B 
!#		
	
B 
!#
tO
PC
'*#$PQEGHE G	
	D KJr   r1   )r   r   r   r   r   r   )rQ   r   r   tupler   r   )r   r   r   r   r   r   )r   
__future__r   r   r   r   r   r   typingr   r   r	   rZ   r   r   r   r  r   r   r   <module>r     sB   ( # 	  2 2 & & D
i
 i
XWr   