
    %5jCY                    |   S r SSKJr  SSKrSSKrSSKJr  SSKJr  S S jr	S!S jr
SS.     S"S	 jjrSS.     S#S
 jjrS$S jrS%S jrS%S jrS&S'S j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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 g)0uF   Detección y reglas de plantilla para informe a la familia del agente.    )annotationsN)Path)Anyc                    [         R                  " SU =(       d    S5      nSR                  S U 5       5      n[        R                  " SSUR                  5       5      R                  5       $ )NNFKD c              3  `   #    U  H$  n[         R                  " U5      (       a  M   Uv   M&     g 7fN)unicodedata	combining).0chs     ZC:\Users\jesus\Desktop\proyecto_pie360\backend\app\backend\utils\agent_familia_template.py	<genexpr>_normalize.<locals>.<genexpr>   s     LFb+2G2G2KrrFs   .	.z
[^a-z0-9]+ )r   	normalizejoinresublowerstrip)textfoldedasciiishs      r   
_normalizer      sO    ""64:26FwwLFLLH66-hnn&67==??    c                f   [        U R                  SS5      R                  SS5      5      nU(       d  gSU;   d  SUR                  SS5      ;   a  gSU;   a  gSU;   a  S	U;   a  gS
U;   d  SUR                  SS5      ;   a  gSU;   d  SU;   a  gSU;   d  SUR                  SS5      ;   a  SU;  a  gg)z=Plantilla PIE360 con campos de formulario (content controls)._r   -Fzfamily reportfamily_reportT
formulariofamiliazinforme familiarinforme_familiarzcon formularioztipo formulariozinforme familiainforme_familiaformator   replacedisplay_namer   s     r   is_familia_form_templater+      s    |++C5==c3GHE%?emmC6M#M%ue!3U"&8EMM#s<S&S5 $5$>E!%6%--S:Q%QE!r   
file_pathsc               |    U =(       d    /  H-  n[        X!=(       d    0 R                  U5      5      (       d  M-    g   g)NTF)is_familia_form_fileget)
file_namesr-   names      r   agent_has_familia_form_templater3   '   s9    
  b '7R&<&<T&BCC ! r   c          	         U =(       d    / n[        X!S9(       d  U$ U Vs/ s H/  n[        X1=(       d    0 R                  U5      5      (       a  M-  UPM1     sn$ s  snf )zISi hay plantilla formulario, excluye FORMATO INFORME DE FAMILIA (tablas).r,   )r3   is_familia_tabla_filer0   )r1   r-   namesns       r   "filter_familia_template_file_namesr8   2   sZ     "E*5H A$Q)9r(>(>q(AB 	
  s   ,AAc                V    U(       a  SR                  S U 5       5      OSnSU  SU S3$ )Nz, c              3  .   #    U  H  nS U S3v   M     g7f)   «   »N )r   r7   s     r   r   6build_form_template_supremacy_block.<locals>.<genexpr>C   s     :Ar!BZs   z	(ninguno)u   === PLANTILLA FORMULARIO — PRIORIDAD SOBRE FORMATO MINISTERIAL ===
OBLIGATORIO: genera el informe copiando y rellenando SOLO «uD   ».
PROHIBIDO abrir o completar plantillas de tablas ministeriales: z.
Pasos: (1) lee bloque DATOS EN BASE DE DATOS; (2) shutil.copy plantilla formulario; (3) rellena content controls / placeholders; (4) completa narrativa desde archivos del caso.
)r   )base_filenameexcludedexcluded_txts      r   #build_form_template_supremacy_blockrB   B   sC    >F499:::KL	GGTo VKKW. Yi	ir   c                     SSK Jn  SSKJn  U" [	        U 5      5      n[        S UR                  R                  R                  U" S5      5       5       5      nUS:  $ ! [         a     gf = f)z0True si el .docx tiene content controls (w:sdt).r   )Document)qnc              3  &   #    U  H  nS v   M	     g7f)   Nr=   )r   r   s     r   r   ,docx_has_content_controls.<locals>.<genexpr>T   s     BA!AAs   zw:sdt   F)
docxrD   docx.oxml.nsrE   strsumelementbodyiter	Exception)pathrD   rE   doccounts        r   docx_has_content_controlsrU   M   s`    !#s4y!Bs{{//44R[ABBz s   AA 
A,+A,c                    [        U 5      (       a  g SSKJn  SSKJn  U" U 5      =(       d    U" U 5      $ ! [
         a     gf = f)zLTrue si el .docx es rellenable: SDT, FORMTEXT o tablas ministeriales planas.Tr   )docx_has_legacy_formtext)!docx_is_familia_ministerial_tablaF)rU   (app.backend.utils.agent_familia_formtextrW   *app.backend.utils.agent_familia_tabla_fillrX   rQ   )rR   rW   rX   s      r   docx_has_form_controlsr[   Z   sD     &&U`'-X1RSW1XX s   "6 
AAc                    [        U 5      (       a  gU(       a>  UR                  R                  5       S:X  a   UR                  5       (       a  [	        U5      $ g)NTz.docxF)r+   suffixr   is_filer[   r*   	disk_paths     r   r/   r/   g   sE    --Y%%++-8Y=N=N=P=P%i00r   c                :    [        X5      (       a  g[        U 5      $ )NF)r/   is_familia_tabla_templater_   s     r   r5   r5   o   s    L44$\22r   c                    [        U R                  SS5      R                  SS5      5      nSU;   =(       a    SU;   =(       d    SU;   $ )z8FORMATO INFORME DE FAMILIA.docx (tablas etiqueta/valor).r   r   r    r&   r#   zinforme de familiar'   r)   s     r   rb   rb   u   sG    |++C5==c3GHE59#5W:NRW:WWr   c                    [        U 5      (       d  [        U 5      (       a  g[        U R                  SS5      R                  SS5      5      nSUR                  SS5      ;   a  gSU;   a  SU;   a  gg)	u   
Plantilla del informe familia (por nombre), NO expedientes del estudiante.
No inspecciona el .docx: un informe psicopedagógico con form controls no es plantilla.
Tr   r   r    r!   r&   r#   F)r+   rb   r   r(   r)   s     r   #is_familia_report_template_filenamere   {   sl    
  --1J<1X1X|++C5==c3GHE%--S11Ei50r   c                z    [        U R                  SS5      R                  SS5      5      nSU;   =(       d    SU;   $ )uI   Informe / evaluación psicopedagógica del estudiante (archivo del caso).r   r   r    psicoped	psychopedr'   r)   s     r   is_psychoped_case_filenameri      s;    |++C5==c3GHE6+"66r   c                    [        U R                  SS5      R                  SS5      5      nSU;   a  g[        U 5      (       a  gSU;   a  g[        U 5      (       a  g	g
)Nr   r   r    r!   r   rG   cartilla   
      )r   r(   r+   rb   r)   s     r   familia_form_template_priorityro      s[    |++C5==c3GHE%--U ..r   )agent_idr-   c               R   U =(       d    / n/ n/ nU Hc  nU=(       d    0 R                  U5      n[        Xg5      (       a  UR                  U5        M@  [        Xg5      (       d  MR  UR                  U5        Me     U(       a  UR	                  [
        S9  US   S4$ U(       a  US   S4$ g)z=Devuelve (nombre_archivo, tipo) con tipo form | tabla | none.)keyr   formtabla)Nnone)r0   r/   appendr5   sortro   )r1   rp   r-   r6   rs   rt   r2   disks           r   pick_familia_base_templatery      s     "EDE b%%d+++KK"4..LL  		4	5AwQx  r   c                    SSK Jn  / n0 nU HV  n[        USS5      =(       d    SnU(       d  M"  UR                  U5        [        USS5      nU(       d  MI  U" U 5      U-  XF'   MX     [	        X4S9$ )z=Clasifica plantilla familia inspeccionando archivos en disco.r   	agent_dirr*   Nr   idr,   )app.backend.utils.agent_filesr|   getattrrv   ry   )rp   rowsr|   r6   pathsrowr2   fids           r   "resolve_familia_template_from_rowsr      sq    
 8EEsND17RTc4&3#H-3EK  &e>>r   c                   SSK Jn  [        X5      u  p4US:w  d  U(       d  gU HZ  n[        USS5      =(       d    Sn[        USS5      nXc:X  d  M-  U(       d  M6  U" U 5      U-  nUR	                  5       (       d  MX  Us  $    g)z<Ruta en disco de la plantilla formulario familia, si existe.r   r{   rs   Nr*   r   r}   r~   r|   r   r   r^   	rp   r   r|   basekindr   r2   r   rR   s	            r   resolve_form_template_pathr      u    73HCJDv~TsND17Rc4&<CCX&,D||~~  r   c                   SSK Jn  [        X5      u  p4US:X  d  U(       d  gU HZ  n[        USS5      =(       d    Sn[        USS5      nXc:X  d  M-  U(       d  M6  U" U 5      U-  nUR	                  5       (       d  MX  Us  $    g)zMRuta en disco de la plantilla familia prioritaria (formulario o ministerial).r   r{   ru   Nr*   r   r}   r   r   s	            r   resolve_familia_template_pathr      r   r   c                    SSK Jn  U R                  U5      R                  UR                  U:H  5      R                  UR                  R                  5       5      R                  5       n[        X5      $ )zQBusca plantilla familia en todos los archivos del agente (no solo seleccionados).r   )AgentFileModel)
app.backend.db.modelsr   queryfilterrp   order_byuploaded_atascallr   )dbrp   r   r   s       r   'resolve_familia_template_path_for_agentr      sZ    4 	 	''83	4	.,,002	3		 	 )88r   c                      g)u=   Prohibición de inventar datos no presentes en BD o archivos.u  - NO INVENTAR DATOS (OBLIGATORIO): usa únicamente información que esté en «DATOS DEL ESTUDIANTE EN BASE DE DATOS», en los archivos del caso o en el rol. Prohibido fabricar fechas, calendarizaciones, nombres, RUT, diagnósticos o frases genéricas del tipo «Según calendarización del establecimiento», «antecedente no informado» como párrafo, o textos explicativos en celdas de fecha.
- Si falta un dato narrativo en doc.tables[3]: indícalo según el rol o deja la celda vacía si es una fecha. Nunca sustituyas una fecha faltante por un párrafo.
- NO rellenes ni modifiques identificación (doc.tables[1] y doc.tables[2]) si el .docx base ya viene con esos datos desde PIE360.
- Fechas de seguimiento → doc.tables[3] fila 17: solo fechas DD/MM/YYYY en las celdas de valor (cols 4-9), una por celda asignada. Usa evaluation_date_1, evaluation_date_2, evaluation_date_3 de la BD si existen; si no existen, deja esas celdas vacías.
r=   r=   r   r   build_no_fabrication_rulesr      s    	5r   c                     S[        5       -   $ )uC   Conservar tipografía; narrativa justificada con un w:p por bloque.u@  - TIPOGRAFÍA RESPUESTAS (OBLIGATORIO): todo el texto que escribes en campos del formulario (identificación arriba y narrativa abajo) debe ser Arial 10 pt, sin negrita. Copia rPr de la plantilla pero fuerza w:rFonts ascii/hAnsi=Arial y w:sz val=20.
- ALINEACIÓN NARRATIVA (OBLIGATORIO): texto JUSTIFICADO (w:jc both). PROHIBIDO w:br dentro del mismo párrafo para separar bloques: Word estira cada línea corta y aparecen espacios enormes entre palabras (ej. «la          respuesta          correcta»). Usa un w:p independiente por bloque; la última línea de cada párrafo queda a la izquierda.
- PÁRRAFOS NARRATIVOS: un w:p por bloque. PROHIBIDO unir bloques con w:br en el mismo w:p.
- TIPOGRAFÍA DE PLANTILLA (OBLIGATORIO): NO cambies fuente, tamaño, negrita, color ni alineación del párrafo/campo. Copia el formato del primer run existente en la plantilla (rPr: w:rFonts, w:sz, w:color, w:b). PROHIBIDO usar p.text = ... o cell.text = ... (destruye runs y cambia la letra).
- Al rellenar content controls (w:sdt), escribe solo en w:t dentro del w:sdtContent preservando w:rPr del run; si no hay run, clónalo del párrafo vecino de la plantilla.
- ESPACIADO NARRATIVO (OBLIGATORIO): cada bloque narrativo va en su propio w:p (add_paragraph o párrafo existente), con space_before=Pt(0) y space_after=Pt(0). PROHIBIDO w:br para simular párrafos (causa espacios gigantes si hay justificado). PROHIBIDO dejar párrafos vacíos entre bloques (causan huecos grandes en Word).
- Texto arriba en celda: space_before/space_after = 0; elimina párrafos vacíos sobrantes; no centres verticalmente.
- PAGINACIÓN (OBLIGATORIO): PROHIBIDO keep_with_next, keep_together, page_break_before en párrafos narrativos. No uses cantSplit ni alturas fijas de fila (trHeight exact). Las filas deben crecer con el texto y dividirse naturalmente entre páginas.
)r   r=   r   r   build_typography_preserve_rulesr     s    	Y. %
&/	'r   c                     S[        5       -   $ )u@   Reglas de extensión mínima para textos narrativos del informe.u  - REDACCIÓN EXTENSA (OBLIGATORIO): cada apartado narrativo debe tener exactamente 2 párrafos LARGOS y desarrollados (no oraciones sueltas ni párrafos de 2-3 líneas). Aplica a motivo de evaluación, instrumentos, diagnóstico, fortalezas, necesidades de apoyo (pedagógico, social/afectivo y salud), trabajo colaborativo, apoyos en el hogar, acuerdos y secciones del rol.
- Extensión mínima por párrafo: 6 a 10 oraciones completas (aprox. 120-200 palabras). Desarrolla contexto escolar, observaciones del expediente, implicancias pedagógicas y orientaciones concretas. Un párrafo breve de 1-4 oraciones es INACEPTABLE.
- Párrafo 1: antecedentes, contexto e instrumentos/fuentes. Párrafo 2: hallazgos, interpretación e implicancias para apoyos y seguimiento.
- Extrae contenido de la cartilla, evaluación psicopedagógica y archivos del caso; no resumas en frases genéricas.
- Separa los dos párrafos con UN solo salto de línea (w:br), sin párrafo vacío intermedio ni espacio extra. Revisa que no queden huecos grandes entre párrafos antes de exportar.
- Antes de exportar, revisa cada campo narrativo: si algún párrafo tiene menos de 6 oraciones, amplíalo antes de guardar.
r   r=   r   r   $build_redaction_min_paragraphs_rulesr   )  s    	8  *
+!	,r   c                    SU  S3$ )uX   Delimita qué tablas/filas puede editar GPT cuando PIE360 ya pre-llenó identificación.uP   === ÁMBITO DE EDICIÓN (OBLIGATORIO — NO SALIR DE AQUÍ) ===
Trabaja sobre «uA  » tal como viene de PIE360.
PROHIBIDO modificar, borrar o reescribir:
  • doc.tables[0] — texto introductorio Decreto 170
  • doc.tables[1] — IDENTIFICACIÓN DEL ESTUDIANTE (nombre, RUT, curso, establecimiento…)
  • doc.tables[2] — IDENTIFICACIÓN del profesional y del apoderado/receptor
  • doc.tables[4] — firmas
  • Fila «MOTIVO DE LA EVALUACIÓN» (checkboxes ingreso/reevaluación — ya vienen de PIE360)
TU ÚNICO ÁMBITO DE TRABAJO es doc.tables[3] «RESULTADOS DE LA EVALUACIÓN», desde INSTRUMENTOS APLICADOS hacia abajo:
  • fila 3 col 0: instrumentos aplicados (applied_instruments)
  • fila 5 col 0: diagnóstico NEE (diagnostic / diagnosis)
  • fila 8: fortalezas pedagógicas (col 0) y necesidades de apoyo (col 3)
  • fila 10: fortalezas y necesidades ámbito social/afectivo
  • fila 12 col 0: trabajo colaborativo / apoyos en el establecimiento
  • fila 14 col 0: apoyos en el hogar
  • fila 16 col 0: acuerdos escuela-familia
  • fila 17 cols 4-9: fechas de seguimiento (solo DD/MM/YYYY o vacío)
No recorras doc.tables[1] ni doc.tables[2] con bucles genéricos. No uses cell.text = ... sobre celdas de identificación.
Si ves placeholders {{student_full_name}}, {{student_identification_number}}, etc., NO los toques: PIE360 los reemplaza desde la base de datos al guardar el archivo.
r=   base_doc_names    r    build_familia_hybrid_scope_rulesr   @  s    	(/ *^	^r   c                J    SU  S3[        U 5      -   SU  S3-   [        5       -   $ )uI   Instrucciones cuando PIE360 ya entregó base con identificación rellena.uT   === INFORME FAMILIA — BASE PIE360 + REDACCIÓN GPT (OBLIGATORIO) ===
El archivo «u   » ya tiene la IDENTIFICACIÓN superior rellena desde la base de datos (estudiante, profesional, apoderado, fechas, checkboxes de evaluación). NO modifiques esos campos.
u!  Tu trabajo exclusivo: redactar los apartados narrativos de doc.tables[3] (desde instrumentos aplicados hacia abajo) según el ROL DEL AGENTE, usando la cartilla técnica, la evaluación psicopedagógica y los demás archivos del caso en el contenedor.
El ROL DEL AGENTE define qué decir, cómo estructurar cada sección, extensión, tono técnico-pedagógico y criterios de completitud. Cada campo narrativo debe cumplir literalmente lo que el rol exige para ese apartado; no resumas ni omitas secciones que el rol mencione.
Pasos:
  1) Abre «u>  » (NO copies otra plantilla ni uses FORMATO INFORME DE FAMILIA.docx).
  2) Lee cartilla, informe psicopedagógico y documentos del estudiante.
  3) Completa SOLO las celdas narrativas vacías de doc.tables[3] según el rol.
  4) Guarda UN solo .docx final y expórtalo.
Mapeo rol → campos (solo parte inferior, doc.tables[3]):
  - Instrumentos aplicados → applied_instruments (fila 3)
  - Diagnóstico / diagnóstico NEE → diagnostic, diagnosis (fila 5)
  - Fortalezas pedagógicas → pedagogical_strengths, strengths_1 (fila 8 col 0)
  - Necesidades de apoyo pedagógico → pedagogical_support_needs, support_needs_1 (fila 8 col 3)
  - Fortalezas social/afectivo → social_affective_strengths, strengths_2 (fila 10 col 0)
  - Necesidades social/afectivo → social_affective_support_needs, support_needs_2 (fila 10 col 3)
  - Trabajo colaborativo / apoyos en el establecimiento → collaborative_work (fila 12)
  - Apoyos en el hogar → home_based_description, home_support (fila 14)
  - Acuerdos escuela-familia → school_family_agreements, agreements_commitments (fila 16)
OBLIGATORIO: ningún campo narrativo puede quedar vacío, con «Haz clic o pulse aquí…» ni con texto genérico. Extrae contenido concreto del expediente; si el rol exige citar cartilla técnica, Decreto 170 o criterios PIE, incorpóralos en la redacción.
PROHIBIDO: borrar o reescribir identificación ya completada; tocar doc.tables[0], [1], [2] o [4]; usar formato ministerial de tablas distinto al base; entregar el documento sin redactar doc.tables[3]; modificar la fila «MOTIVO DE LA EVALUACIÓN».
)r   r   r   s    r   (build_familia_narrative_enrichment_rulesr   \  sT    	% 'p	p +=
9	: %o &<<		<> /
0?	1!r   c                $    SU  S3[        5       -   $ )Nu,   - PLANTILLA FORMULARIO (OBLIGATORIO): usa «u  » como base del informe. Copia ese archivo (shutil.copy) y trabaja sobre la copia. NO uses FORMATO INFORME DE FAMILIA.docx ni reconstruyas el documento.
- La plantilla tiene campos de formulario Word (content controls; a veces muestran «Haz clic o pulse aquí para escribir texto»). Rellénalos sin mover tablas ni cambiar estilos.
- Lee primero el expediente del estudiante (docx del caso, cartilla, etc.) y extrae los datos. Si el formulario ya trae identificación rellena, consérvala.
- Identificación (tags w:tag exactos — NO modificar si ya vienen rellenos desde PIE360):
    student_full_name, student_identification_number, student_birth_date, student_age, student_course, student_school, professional_full_name, professional_identification_number, professional_job_position, professional_phone_email, professional_delivered_date_inform, person_full_name, person_identification_number, person_relation_student, person_presence.
    Contenido narrativo: evaluation_reason, diagnostic, strengths_1, support_needs_1, pedagogical_strengths / strengths_1; pedagogical_support_needs / support_needs_1; social_affective_strengths / strengths_2; social_affective_support_needs / support_needs_2; health_strengths / strengths_3; health_support_needs / support_needs_3; collaborative_work; home_based_description / home_support; school_family_agreements / agreements_commitments.
- Nombre y Rut van en sus campos respectivos; el Rol/cargo NUNCA recibe el RUT.
- Rellena con python-docx recorriendo w:sdt (w:tag) y asignando texto al w:sdtContent, o reemplazando {clave}, [clave] y <<clave>> en párrafos y celdas.
- Patrón mínimo para content controls:
    from docx import Document
    from docx.oxml.ns import qn
  def rellenar_formulario(ruta, reemplazos):
      doc = Document(ruta)
      def norm(s): return re.sub(r'[^a-z0-9]+','', (s or '').lower())
      tags = {norm(k): str(v or '') for k,v in reemplazos.items()}
      for sdt in doc.element.body.iter(qn('w:sdt')):
          tag = sdt.find('.//'+qn('w:tag'))
          if tag is None: continue
          key = norm(tag.get(qn('w:val')))
          if key not in tags: continue
          content = sdt.find(qn('w:sdtContent'))
          if content is None: continue
          for t in content.iter(qn('w:t')): t.text = ''
          ts = list(content.iter(qn('w:t')))
          if ts: ts[0].text = tags[key]
      for p in doc.paragraphs:
          t = p.text
          for k,v in reemplazos.items():
              for fmt in ('{%s}','[%s]','<<%s>>'):
                  t = t.replace(fmt % k, str(v or ''))
          if t != p.text: p.text = t
      for tbl in doc.tables:
          for row in tbl.rows:
              for cell in row.cells:
                  for p in cell.paragraphs:
                      t = p.text
                      for k,v in reemplazos.items():
                          for fmt in ('{%s}','[%s]','<<%s>>'):
                              t = t.replace(fmt % k, str(v or ''))
                      if t != p.text: p.text = t
      doc.save(ruta)
- Conserva tipografía de la plantilla; no uses cell.text sobre filas de rótulo.
- Para narrativa: 2 párrafos largos (6-10 oraciones c/u) por campo; texto justificado (w:jc both), un w:p por bloque (sin w:br), Arial 10 pt.
r   )r?   s    r   build_familia_form_rulesr     s*    
6}o 8FV 8	Vr *
+s9	,;r   )r   rL   returnrL   )r*   rL   r   bool)r1   	list[str]r-   dict[str, Path] | Noner   r   )r1   r   r-   r   r   r   )r?   rL   r@   r   r   rL   )rR   r   r   r   r
   )r*   rL   r`   Path | Noner   r   )r*   rL   r   int)r1   zlist[str] | Nonerp   z
str | Noner-   r   r   tuple[str | None, str])rp   rL   r   	list[Any]r   r   )rp   rL   r   r   r   r   )r   Sessionrp   rL   r   r   )r   rL   )r   rL   r   rL   )r?   rL   r   rL   )!__doc__
__future__r   r   r   pathlibr   typingr   r   r+   r3   r8   rB   rU   r[   r/   r5   rb   re   ri   ro   ry   r   r   r   r   r   r   r   r   r   r   r=   r   r   <module>r      s   L " 	   @2 *. ' 
	 *. ' 	 

3X7
   )-	   '	
 0??
? ?(""
9&<.8#L<r   