
    iJ                        d Z ddlmZ ddlZddlZddlmZmZmZ ddlm	Z	m
Z
mZ ddlZdddZ G d d      Zdd	Zdd
Zy)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                    t         j                  j                  |       }|t        |      j	                         dk7  r|S |S )N )osenvirongetstrstrip)namedefaultvs      R/var/www/pie360backend.cl/public_html/app/backend/classes/inspection_api_client.py_envr      s4    


tA#a&,,.B"61CGC    c                      e Zd ZU dZ ej
                         ZdZded<   dZ	ded<   d Z
ddZdd	Zdd
ZddZddZd d!dZd"dZd#dZd d$dZd%dZddZddZddZddZddZddZd&dZd&dZddZd'dZy)(InspectionApiClientzIToken en memoria por worker (gunicorn); se renueva al expirar o ante 401.NOptional[str]_tokenOptional[datetime]_expires_atc                    t        d      xs dj                  d      | _        t        d      | _        t        d      | _        	 t        t        d      xs d      | _        y # t        $ r
 d| _        Y y w xY w)	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__zInspectionApiClient.__init__+   sm    78b<bjjkno6767	  &>!?!G4HDL 	 DL	 s   A# #A65A6c                H    t        | j                  xr | j                        S N)boolr&   r'   r+   s    r   is_configuredz!InspectionApiClient.is_configured4   s    DMM3dmm44r   c                    | j                   sy| j                  syt        j                  t        j
                        }| j                  }|j                   |j                  t        j
                        }||k  S )NFTtzinfo)r   r   r   nowr   utcr4   replace)r,   r5   exps      r   _token_validz InspectionApiClient._token_valid7   s]    {{ll8<<(::++X\\+2CSyr   c                   |y t        |      j                         }|sy 	 d|v r&t        j                  |j	                  dd            }nt        j
                  |d d d      }|j                   |j	                  t        j                        }|S # t        $ r3 t        j                  t        j                        t        d      z   cY S w xY w)	NTZz+00:00   z%Y-%m-%d %H:%M:%Sr3      hours)r   r   r   fromisoformatr7   strptimer4   r   r6   	Exceptionr5   r   )r,   rawsdts       r   _parse_expiresz"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   A0B 9CCc                   | j                         sddd dS | j                   d}d t        | j                        fd t        | j                        fd}	 t        j                  ||| j                        }	 |j                         }|j                  d	k\  s|j                  d
      s5d|j                  d      xs d|j                   |j                  d      dS |j                  d      xs i }|j                  d      }|sddd dS | j                  5  || j                  _        | j!                  |j                  d            }|xs0 t#        j$                  t&        j(                        t+        d      z   | j                  _        d d d        d|j                  d      xs d|dS # t        $ r dd|j                  d d  d dcY S w xY w# 1 sw Y   GxY w# t
        j.                  $ r}dt        |      d dcY d }~S d }~ww xY w)NFz9INSPECTION_API_USERNAME / INSPECTION_API_PASSWORD not setokmessagedataz/login)r&   r'   )filesr)   Non-JSON response:      rJ   rK   HTTP rL   tokenz!Login response missing data.token
expires_atr>   r?   TOK)r1   r%   r   r&   r'   requestspostr)   jsonrC   textstatus_coder   _lock	__class__r   rG   r   r5   r   r6   r   r   RequestException)	r,   urlrM   rbodyrL   rR   r8   es	            r   loginz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 f(-%))$((<*@A-0-eX\\(,,5OR[bcRd5d*f
 488I+>+F$PTUU!  d#2EaffTcl^0T^bccdf f (( 	BCFDAA	Bsh   "G 9F. 	AG -G G A5G G .GG GG GG H2H HHc                R   | j                   5  | j                         r| j                  cd d d        S 	 d d d        | j                         sy | j	                         }|j                  d      sy | j                   5  | j                  cd d d        S # 1 sw Y   ^xY w# 1 sw Y   y xY w)NrJ   )rZ   r9   r   r1   ra   r   )r,   ress     r   get_bearer_tokenz$InspectionApiClient.get_bearer_tokenv   s    ZZ 	#  "{{	# 	#"	# !!#jjlwwt}ZZ 	;;	 		# 	#	 	s   B;BBB&c                   |xs dj                         }|sddddS | j                         }|sddddS |j                  d      }| j                   d| }dd	| i}d
d|fi|ddt	        |      fini }	 t        j                  |||| j                        }	 |j                         }	|j                  dk(  r| j                  5  d| j                  _        d| j                  _        ddd       | j                         }
|
r9t        j                  |dd	|
 i|| j                        }	 |j                         }	|j                  dk\  r5d|	j!                  d      xs d|j                   |	j!                  d      dS t#        |	t$              r|	S ddddS # t        $ r dd|j                  dd  ddcY S w xY w# 1 sw Y   xY w# t        $ r	 ddddcY S w xY w# t
        j&                  $ r}dt	        |      ddcY d}~S d}~ww xY w)z
        POST {base}/{remote_path} with form field rut (multipart). Used for getDatosAlumno, getDatosFuncionario, etc.
        r   FzRUT is requiredNrI   8Inspection API authentication failed (check credentials)r   AuthorizationBearer rutanioheadersrM   r)   rN   rO     Retry after 401 failedrP   rK   rQ   rL   Invalid response body)r   rd   lstripr%   r   rU   rV   r)   rW   rC   rX   rY   rZ   r[   r   r   r   
isinstancedictr\   )r,   remote_pathri   rj   rR   r]   rl   rM   r^   r_   token2r`   s               r   _post_multipart_rutz'InspectionApiClient._post_multipart_rut   sH    yb!,=tLL%%',fptuu!((-q."geW$56D#;
.2.>s4y)*B

!	Bc7%VAdvvx }}#ZZ 6,0DNN)15DNN.6 ..0 !0GF82D E# $	A` vvx }}##xx	2Mamm_6M HHV,  &dD14vePgqu7vv7  d#2EaffTcl^0T^bccd6 6 % `&+8PZ^__` (( 	BCFDAA	Bs   8#G F ,G #F>*AG -G
 =AG G G F;8G :F;;G >GG 
GG GG H2H HHc           	         | j                         }|sddddS |j                  d      }| j                   d| }dd| i}|j                         D ci c]  \  }}|	|dt	        |      f }}}	 t        j                  |||| j                        }		 |	j                         }
|	j                  dk(  r| j                  5  d| j                  _        d| j                  _        ddd       | j                         }|r9t        j                  |dd| i|| j                        }		 |	j                         }
|	j                  dk\  r5d|
j!                  d      xs d|	j                   |
j!                  d      dS t#        |
t$              r|
S ddddS c c}}w # t        $ r dd	|	j                  dd
  ddcY S w xY w# 1 sw Y   xY w# t        $ r	 ddddcY S w xY w# t
        j&                  $ r}dt	        |      ddcY d}~S d}~ww xY w)zJPOST multipart/form-data con campos arbitrarios (solo texto), Bearer auth.Frf   NrI   r   rg   rh   rk   rN   rO   rm   rn   rP   rK   rQ   rL   ro   )rd   rp   r%   itemsr   rU   rV   r)   rW   rC   rX   rY   rZ   r[   r   r   r   rq   rr   r\   )r,   rs   fieldsrR   r]   rl   kr   rM   r^   r_   rt   r`   s                r   _post_multipart_formz(InspectionApiClient._post_multipart_form   s+   %%',fptuu!((-q."geW$56/5||~Otq!T3q6N"OO!	Bc7%VAdvvx }}#ZZ 6,0DNN)15DNN.6 ..0 !0GF82D E# $	A` vvx }}##xx	2Mamm_6M HHV,  &dD14vePgqu7vvC P  d#2EaffTcl^0T^bccd6 6 % `&+8PZ^__` (( 	BCFDAA	Bs   
F F4#G! F (G! #G &AG! )G 9AG! =G! G! F=:G! <F==G!  G	G! GG! GG! !H4HHHc                   | j                         }|sddd dS |j                  d      }| j                   d| }dd| i}	 t        j                  ||| j
                        }	 |j                         }|j                  d
k(  r| j                  5  d | j                  _        d | j                  _        d d d        | j                         }|r8t        j                  |dd| i| j
                        }	 |j                         }|j                  dk\  r5d|j	                  d      xs d|j                   |j	                  d      dS t        |t              r|S ddd dS # t        $ r dd|j                  d d	  d dcY S w xY w# 1 sw Y   xY w# t        $ r	 ddd dcY S w xY w# t        j                   $ r}dt#        |      d dcY d }~S d }~ww xY w)NFrf   rI   r   rg   rh   )rl   r)   rN   rO   rm   rn   rP   rK   rQ   rL   ro   )rd   rp   r%   rU   r   r)   rW   rC   rX   rY   rZ   r[   r   r   rq   rr   r\   r   )	r,   rs   rR   r]   rl   r^   r_   rt   r`   s	            r   _get_with_bearerz$InspectionApiClient._get_with_bearer   s   %%',fptuu!((-q."geW$56 	BS'4<<HAdvvx }}#ZZ 6,0DNN)15DNN.6 ..0 !0GF82D E $A
` vvx }}##xx	2Mamm_6M HHV,  &dD14vePgqu7vv5  d#2EaffTcl^0T^bccd6 6 % `&+8PZ^__` (( 	BCFDAA	Bs   "F( &E" 6F( #F4AF( 6F AF( 
F( F( "FF( FF( FF( F%"F( $F%%F( (G;G	GGc                (    | j                  d||      S )u9   POST /getDatosAlumno — remote path fixed by Inspection.getDatosAlumnoru   )r,   ri   rj   s      r   fetch_student_dataz&InspectionApiClient.fetch_student_data  s    ''(8#tDDr   c                &    | j                  d|      S )uZ   POST /getDatosFuncionario — staff/professional by RUT (remote path fixed by Inspection).getDatosFuncionarior   )r,   ri   s     r   fetch_professional_dataz+InspectionApiClient.fetch_professional_data  s    ''(=sCCr   c                `    t        d      xs dj                  d      }| j                  |      S )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   rp   r|   r,   paths     r   fetch_communes_listz'InspectionApiClient.fetch_communes_list  s0    34I8IQQRUV$$T**r   c                "    | j                         S )uZ   Alias: mismo catálogo que provincias (compatibilidad con rutas `/regions/endpoint/list`).)fetch_provinces_listr+   s    r   fetch_regions_listz&InspectionApiClient.fetch_regions_list  s    ((**r   c                `    t        d      xs dj                  d      }| j                  |      S )u:   GET listado/provincias — catálogo remoto de provincias.INSPECTION_API_PROVINCES_PATHzlistado/provinciasr   r   r   s     r   r   z(InspectionApiClient.fetch_provinces_list#  s0    45M9MUUVYZ$$T**r   c                `    t        d      xs dj                  d      }| j                  |      S )uM   GET listado/regiones — catálogo remoto de regiones (configurable por env).INSPECTION_API_REGIONS_PATHzlistado/regionesr   r   r   s     r   fetch_regiones_listz'InspectionApiClient.fetch_regiones_list(  0    23I7IQQRUV$$T**r   c                `    t        d      xs dj                  d      }| j                  |      S )uT   GET listado/nacionalidades — `{ ok, data: [{ id, gentilicio, pais, iso }, ...] }`.!INSPECTION_API_NATIONALITIES_PATHzlistado/nacionalidadesr   r   r   s     r   fetch_nationalities_listz,InspectionApiClient.fetch_nationalities_list-  s0    89U=U]]^ab$$T**r   c                `    t        d      xs dj                  d      }| j                  |      S )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_listz(InspectionApiClient.fetch_teachings_list2  s0    45S9S[[\_`$$T**r   c                    t        d      xs dj                  d      }| j                  |t        |      t        |      d      S )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   colegiorj   r   rp   rz   intr,   
colegio_idrj   r   s       r   fetch_courses_listz&InspectionApiClient.fetch_courses_list7  sD     23G7GOOPST((3z?TWX\T]/^__r   c                    t        d      xs dj                  d      }| j                  |t        |      t        |      d      S )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_listz'InspectionApiClient.fetch_students_list@  sD    
 34I8IQQRUV((3z?TWX\T]/^__r   c                `    t        d      xs dj                  d      }| j                  |      S )z<GET listado de colegios (Inspection: /api/listado/colegios).INSPECTION_API_SCHOOLS_PATHzlistado/colegiosr   r   r   s     r   fetch_schools_listz&InspectionApiClient.fetch_schools_listH  r   r   c                   	 t        |      }| j                         }|j	                  d      s|S |j	                  d      }t        |t              sddddS d}|D ]5  }t        |t              s	 t        |j	                  d            |k(  r|} n7 |
dd	| d
ddS |j	                  d      xs7 |j	                  d      xs$ |j	                  d      xs |j	                  d      }|g }t        |t              sddddS d|j	                  d      xs d|dS # t        t        f$ r	 ddddcY S w xY w# t        t        f$ r Y w xY w)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álidoNrI   rJ   rL   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 encontradoTrK   rT   )r   	TypeErrorr*   r   r   rq   listrr   )r,   	school_idsidremoteschoolsmatchrE   tiposs           r   !fetch_teachings_for_active_schoolz5InspectionApiClient.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#   D 0D7D43D47E	E	)returnr0   )rD   r   r   r   )r   Dict[str, Any])r   r   r/   )rs   r   ri   r   rj   
int | Noner   r   )rs   r   rx   r   r   r   )rs   r   r   r   )ri   r   rj   r   r   r   )ri   r   r   r   )r   r   rj   r   r   r   )r   r   r   r   )__name__
__module____qualname____doc__	threadingLockrZ   r   __annotations__r   r-   r1   r9   rG   ra   rd   ru   rz   r|   r   r   r   r   r   r   r   r   r   r   r   r    r   r   r   r   $   s    SINNE FM &*K#* 5	C$ BD
5Bn,B\)BVED+
++
+
+
+
``+
@
r   r   c                    |D ]J  }| j                  |      }|t        |      j                         s1t        |      j                         c S  y r/   )r   r   r   )rL   keysry   r   s       r   _first_valuer     sD     "HHQK=SV\\^q6<<>!" r   c                   t        |       }|j                  d      s|S |j                  d      }||S t        |t              r)t	        |      dkD  rt        |d   t               r|d   ni }nt        |t               r|}n|S t        |d      }|r||d<   t        |d      }|r||d<   t        |d      }|r||d	<   t        |d
      }|r||d<   t        |d      }	|	r|	|d<   t        |d      }
|
r|
|d<   t        |d      }|r||d<   |S )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 }.
    rJ   rL   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   )ri   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   )rr   r   rq   r   lenr   )student_inputsapi_payloadoutrD   rL   nvapamri   r   r   borns               r   $merge_inspection_into_student_inputsr     sU   
 ~
C??4 

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