
    {h                        d dl Z d dlZd dlZd dlZd dlmZ d dlmZ d dlmZm	Z	m
Z
mZmZ d dlZd dlZd dlZd dlmZmZ d dlmZmZ d dlmZ d dlmZ  e        d d	lmZmZmZ d
e
e   fdZd
e	eef   fdZ de	eef   d
e	eef   fdZ!de"de	eef   d
e	eef   fdZ#de	eef   d
e	eef   fdZ$de	eef   d
e	eef   fdZ%de	eef   d
e	eef   fdZ&de	eef   d
e	eef   fdZ'de	eef   d
e	eef   fdZ(d9dede)d
ejT                  fdZ+ded
e	eef   fdZ,ded
efdZ-ded
e	eef   fdZ.ded
e	eef   fdZ/d Z0d  Z1defd!Z2ded"e	eef   d
e	eef   fd#Z3d$ejT                  d
e)fd%Z4d& Z5d' Z6d( Z7d$ejT                  d
e
e   fd)Z8d$ejT                  d
e
e	eef      fd*Z9	 	 	 d:d$ejT                  d+ee   d,ed-e"d
e	eef   f
d.Z:d$ejT                  d/e	eef   d
ejT                  fd0Z;d$ejT                  d
ejT                  fd1Z<d$ejT                  d
ejT                  fd2Z=d$ejT                  d
ejT                  fd3Z>d$ejT                  d
ejT                  fd4Z?	 d;d$ejT                  d5ed6e"d
e"fd7Z@d$ejT                  d
e	eef   fd8ZAy)<    N)BytesIO)datetime)AnyDictListOptionalTuple)
UploadFileHTTPException)r
   Headers)load_dotenv)tabulate)get_connectionexecutequeriesreturnc                     K   	 t        d       d{   } | r| D cg c]  }|d   	 c}S g S 7 c c}w # t        $ r g cY S w xY ww)zQ
    Devuelve la lista de tablas permitidas desde listado_tablas_prospectos.
    z'SELECT * FROM listado_tablas_prospectosNnombre_tabla)r   	Exception)filasfs     [/var/www/dev.api.imparables.com.co/api_imparables/crm_asincrono/procesos/contactabilidad.pytraer_tablas_permitidasr      sR     GHH5:51a.!1BB I1 	sF   A5 .
5 05 A5 A5 5 A AAAc                     K   	 t        d       d {   } dd| xs g dS 7 # t        $ r}ddt        |       dcY d }~S d }~ww xY ww)NzLSELECT * FROM listado_tablas_prospectos WHERE estado_contacto = 'Prospectos'   ztablas obtenidas correctamentesuccessmessagedatar   Error: r   r   )r   r   str)r   es     r   getListaTablasr$   -   sb     
=Z
 
 7KR
 	

  =73q6();<<=s:   A
# !# A
# 	AAAA
AA
r   c                   K   d}d}	 | j                  d      }t        | j                  dd            }t        | j                  dd            }t        d|dz
  |z        }t                d{   }|s:dd	d
|r|j	                          d{    |r|j	                          d{    S S ||vr:ddd
|r|j	                          d{    |r|j	                          d{    S S d| d}t        |||f       d{   }	d| d}
t        |
       d{   }|rt        |d   d         nd}||z  ||z  rdndz   }|	D ]  }|j                  dd      |d<    dd|	|||d|r|j	                          d{    |r|j	                          d{    S S 7 27 7 7 7 7 7 7 07 # t        $ rR}ddt        |       d
cY d}~|r|j	                          d{  7   |r|j	                          d{  7   S S d}~ww xY w# |r|j	                          d{  7   |r|j	                          d{  7   w w xY ww)u   
    Paginado + join con estados_etapas. Valida tabla por whitelist.
    Filtra estado_etapa IN (1,5) para los datos y cuenta solo estado_etapa=1, tal como tu lógica original.
    NtablaSeleccionadapager   pageSize   r   Fz#No se encontraron tablas permitidasr!   zTabla no permitidazp
            SELECT 
                t.*, e.estado_etapa AS estado_etapa_desc
            FROM 
                z t
            LEFT JOIN 
                estados_etapas e ON t.estado_etapa = e.id
            WHERE 
                t.estado_etapa IN (1, 5)
            ORDER BY 
                t.id DESC
            LIMIT %s OFFSET %s
        zSELECT COUNT(*) AS total FROM z t WHERE t.estado_etapa = 1totalestado_etapa_descestado_etapaTzDatos obtenidos correctamente)r   r   r   totalRecords
totalPagescurrentPager    )	getintmaxr   closer   popr   r"   )r   cnxcurtablar'   	page_sizeoffsettablas_validasquery	registroscount_query
total_rowsr*   total_pagesrr#   s                   r   datos_tabla_seleccionadarA   ;   s    
 C
C5,-488FA&'Q/0	QY./688$1VWT ))+))+ W &$1EFP ))+))+ O  		 "%)V)<==	 7ug=XY";//
/9JqM'*+q	)593Da!L  	AA !&94 @An	A 6!%
 ))+))+ ] 9Z  7 > 0*   A ws1vh-?@@))+))+ A ))+))+ s2  IA&F4 .F /
F4 9IF#I(F&)I/F4 7IF(I&F*'I-F4 F,F4 F.AF4 *I?F0 IF2I F4 #I&I(I*I,F4 .F4 0I2I4	H=H
HH I'G*(IHI
HH I(H+)IIIIid_voluntarioc                 	  K   t                d{   }d}|dddS 	 |j                  dd      }|dvr:ddd|r|j                          d{    |r|j                          d{    S S |j                  d	      }|j                  d
      }|j                  d      }|j                  d      }|j                  d      }	|j                  d      }
|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      xs d}|j                  d      xs d}|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d       }|j                  d!      }|j                  d"      }|j                  d#      }|j                  d$%       d{   }d}g }|d&k(  rd'}|||||	|
|||||||||||| g}n|d(k(  rd)}||||	|
||||| g
}nw|d*k(  rd+}|||	|
||||||||||| g}n^|d,k(  rY|j	                  d-| f       d{    |j                          d{   } | r| d#   nd}!d.}|||||	|
||||||||||||||||||!| g}|s:ddd|r|j                          d{    |r|j                          d{    S S |j	                  ||       d{    |j                          d{    |j	                  d/| d0| f       d{    |j                          d{   }"d$d1|"d2|r|j                          d{    |r|j                          d{    S S 7 7 z7 c7 7 /7 7 7 7 7 7 q7 [7 >7 &# t        $ rR}#dd3t        |#       dcY d}#~#|r|j                          d{  7   |r|j                          d{  7   S S d}#~#ww xY w# |r|j                          d{  7   |r|j                          d{  7   w w xY ww)4z^
    UPDATE sobre varias tablas soportadas. Preserva fecha_registro en prospectos_manual.
    NFu%   Error de conexión a la base de datosr!   r    )inscripciones_voluntariosunion_voluntarios_antiguosbase_datos_10kregistros_crm_antiguoprospectos_manualu-   Tabla no válida especificada en la solicitudtipo_documento	documentonombres	apellidoscorreostelefonopaisdepartamentociudad	direcciongenerorecomendadoNonombre_recomendadorzNo recomendadogrupo_wp	grupo_wp1como_ayudarpasionnombres_completos	interesesautorizacionbarriofecha_cumpleanos	localidadtipo_sangrefecha_registroT
dictionaryrE   a  
                UPDATE inscripciones_voluntarios
                   SET tipo_documento=%s, documento=%s, nombres=%s, apellidos=%s, correos=%s,
                       telefono=%s, pais=%s, departamento=%s, ciudad=%s, direccion=%s,
                       genero=%s, recomendado=%s, nombre_recomendador=%s, grupo_wp=%s,
                       grupo_wp1=%s, como_ayudar=%s, pasion=%s
                 WHERE id=%s
            rG   a  
                UPDATE base_datos_10k
                   SET fecha_registro=%s, nombres_completos=%s, documento=%s, correos=%s,
                       telefono=%s, departamento=%s, ciudad=%s, intereses=%s, autorizacion=%s
                 WHERE id=%s
            rH   ab  
                UPDATE registros_crm_antiguo
                   SET nombres=%s, apellidos=%s, correos=%s, telefono=%s,
                       tipo_documento=%s, documento=%s, departamento=%s, ciudad=%s, localidad=%s,
                       barrio=%s, direccion=%s, fecha_cumpleanos=%s, genero=%s, tipo_sangre=%s
                 WHERE id=%s
            rI   z8SELECT fecha_registro FROM prospectos_manual WHERE id=%sa%  
                UPDATE prospectos_manual
                   SET nombres=%s, apellidos=%s, tipo_documento=%s, documento=%s, correos=%s,
                       telefono=%s, pais=%s, departamento=%s, ciudad=%s, localidad=%s,
                       barrio=%s, direccion=%s, fecha_cumpleanos=%s, genero=%s, tipo_sangre=%s,
                       recomendado=%s, nombre_recomendador=%s, grupo_wp=%s, grupo_wp1=%s,
                       como_ayudar=%s, pasion=%s, intereses=%s, autorizacion=%s, fecha_registro=%s
                 WHERE id=%s
            zSELECT * FROM z WHERE id=%szVoluntario actualizador   zError al actualizar los datos: )	r   r0   r3   cursorr   fetchonecommitr   r"   )$rB   r   r5   r6   r   rJ   rK   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   nombre_recomrX   rY   rZ   r[   nombres_compr]   r^   r_   fecha_cumplera   rb   rc   r;   paramsrowfecha_registro_actualvoluntario_actualizador#   s$                                       r   actualizar_voluntario_dbrp   z   s      
 C
C
{ -TUUqxx3  
 
  %1`aJ ))+))+ I "23+.	),+.	),*-&).1(++.	(+-08D"78L<L*-+.	-0(+"56+.	.1(+"45+.	-0"23JJ$J//66E %i)WhL&)V["Hifm]F --E %lIwfi}^F 44E y'8$ivyiv{M[F 00++X[hZjkkk&C=@C(8$9d!E y.)WhL&)VYP\k;hPY!69lDY[hjF
 $1`a ))+))+  kk%(((jjlkkN<.EGWXXX'*||~!5,DNde ))+))+ k !h W 0R l&>  	)X!5   Y /NsSTvh-WXX))+))+ Y ))+))+ s  RORO' ROR(O)R/GO' 5O6A,O' "O#O' :O;0O' +R OROR!O' 6O7O' OO' -O.O' O!	O' R$O#%R>O%?RRRO' O' O' RRO' O' O' !O' #R%R'	Q0P= QQ RPR5P86R=QQ Q>QQ>6Q97Q>>Rc           	        K   	 | j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }|dv rd }t        d |||||fD              rd	d
dS t        d||||||f       d {   }|rdnd	|rddS ddS 7 # t        $ r}d	dt	        |       dcY d }~S d }~ww xY ww)Nid_empleadoid_contacto_origenid_contacto_unionorigen_datosid_estado_etapanota)NrD   	undefinednullc              3   $   K   | ]  }|d u  
 y wN ).0xs     r   	<genexpr>zcreateNotes.<locals>.<genexpr>  s     kQqDyks   r   zFaltan datos requeridosr!   z
            INSERT INTO notas_contacto
            (fecha_hora, id_empleado, id_contacto_origen, id_contacto_union, origen_datos, id_estado_etapa, nota)
            VALUES (NOW(), %s, %s, %s, %s, %s, %s)
            r   zNota creada correctamenteu   No se insertó la notar    )r0   anyr   r   r"   )	r   rr   rs   rt   ru   rv   rw   affectedr#   s	            r   createNotesr      s    =hh}-!XX&:; HH%89xx/((#45xx=="Ok;0BDUWcei"jkk -FGG 
 ,.?`de
 
 !)1a[c<W  C  	C  jB  C  	C
  =73q6();<<=s_   CBB7 CB7 !B5"B7 0C1B7 4C5B7 7	C CCCCCc                    K   	 | j                  d      }|sdddS t        d|f       d {   }dd|dS 7 
# t        $ r}dd	t        |       dcY d }~S d }~ww xY ww)
Nrs   r   zFalta el ID del contactor!   a  
            SELECT nc.*,
                   CONCAT(e.nombres,' ',e.apellidos) AS nombre_empleado,
                   ee.estado_etapa AS estado_etapa
              FROM notas_contacto nc
              JOIN empleados e ON nc.id_empleado = e.id
         LEFT JOIN estados_etapas ee ON nc.id_estado_etapa = ee.id
             WHERE nc.id_contacto_origen = %s
          ORDER BY nc.fecha_hora DESC
            r   zNotas obtenidas correctamenter   r    r0   r   r   r"   )r   rs   notasr#   s       r   getNotesContactr     s     =!XX&:;! -GHH	  !
 
 )HRWXX
  =73q6();<<=sF   A 9 A 9 7	9 A 9 	AAAA AA c                   K   	 | j                  d      }| j                  dd      }t        d||f       d {   }|rdd|d   d   dd	d
S ddd dd	d
S 7 # t        $ r}ddt        |       i d
cY d }~S d }~ww xY ww)NidOrigenDatonombreTablarD   z
            SELECT id
              FROM union_contactos_personas
             WHERE id_origen_datos = %s
               AND origen_datos     = %s
            Tu&   Registro encontrado en tabla de uniónr   id)campo_encontradoexistr   u)   Registro no encontrado en la tabla uniónFzError en la consulta: r   )r   r   r   r   r#   s        r   verificacion_persona_contactadar   +  s     \xx/hh}b1 ;'
 
 C-21Xd^dK  B)->
 	

(  \ /Ec!fX-NXZ[[\sP   B4A AA BA BA 	A>"A93A>4B9A>>Bc                 6  K   	 | j                  d      }| j                  d      }| j                  d      }g d}||vrddi dS t        d| d	||f       d {   }|rd
dd|idS ddi dS 7 # t        $ r}ddt        |       i dcY d }~S d }~ww xY ww)Nru   r,   r   rE   rH   rG   rI   Fu#   La tabla especificada no es válidar   zUPDATE z  SET estado_etapa=%s WHERE id=%sTz=Estado de la etapa del contacto actualizado a primer contactou3   No se encontró el registro con el id proporcionadou   Error en la actualización: )r0   r   r   r"   )r   ru   r,   id_registror:   r   r#   s          r   "actualizar_estado_etapa_verificador   H  s     bxx/xx/xx~
 ~-$1V`bcc l^#CD;'
 
 Z{+ 
 !-blnoo
  b /KCPQF8-T^`aabs_   BA A1 BA1 A/A1 (B)A1 .B/A1 1	B:BBBBBc                 d
  K   d}d}	 | j                  dd      }g d}||vr;ddi d|r|j                          d{    |r|j                          d{    S S | j                  dd      }| j                  d	d      }| j                  d
d      }| j                  dd      }| j                  dd      }	| j                  d      }
| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d       }|rVt        d!| d"|f       d{   }|r;dd#i d|r|j                          d{    |r|j                          d{    S S |rXsVt        d!| d$|f       d{   }|r;dd%i d|r|j                          d{    |r|j                          d{    S S |	rXsVt        d!| d&|	f       d{   }|r;dd'i d|r|j                          d{    |r|j                          d{    S S t                d{   }|j	                  d()       d{   }|d*k(  rd+}|
|||||	|||||||||||||f} n_|d,k(  r$| d-| j                         }!d.}||!|||	|||||f
} n6|d/k(  rd0}||||	|
||||||||||f} nd1}|||
|||	|||||||||||||||||||f} |j                  ||        d{    |j                          d{    |j                  }"d(d2d3|"id|r|j                          d{    |r|j                          d{    S S 7 7 7 7 7 7 7 7 7 }7 _7 H7 77  7 7 v7 K7 3# t        $ r}#	 t        |d4d      }$|$rt        d5|$       n# t        $ r Y nw xY wdd6t        |#       i dcY d}#~#|r|j                          d{  7   |r|j                          d{  7   S S d}#~#ww xY w# |r|j                          d{  7   |r|j                          d{  7   w w xY ww)7z
    Inserta en tabla validada por whitelist. Verifica duplicados por documento/correos/telefono.
    Devuelve el ID insertado.
    Nr   rD   r   Fu   Tabla no válidar   rL   rM   rK   rN   rO   rJ   rQ   rR   r,   rT   rc   r]   r^   rP   rS   rU   rW   rX   rY   rZ   r[   ra   r_   r`   rb   zSELECT id FROM z WHERE documento=%sz4Ya existe un registro con el documento proporcionadoz WHERE correos=%sz2Ya existe un registro con el correos proporcionadoz WHERE telefono=%sz3Ya existe un registro con el telefono proporcionadoTrd   rE   a  
                INSERT INTO inscripciones_voluntarios (
                    tipo_documento, documento, nombres, apellidos, correos, telefono,
                    pais, departamento, ciudad, direccion, genero, recomendado,
                    nombre_recomendador, grupo_wp, grupo_wp1, como_ayudar, pasion,
                    fecha_registro, estado_etapa, fecha_estado
                ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW())
            rG    a,  
                INSERT INTO base_datos_10k (
                    fecha_registro, nombres_completos, documento, correos, telefono,
                    departamento, ciudad, intereses, autorizacion, estado_etapa, fecha_estado
                ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW())
            rH   a  
                INSERT INTO registros_crm_antiguo (
                    nombres, apellidos, correos, telefono, tipo_documento, documento,
                    departamento, ciudad, localidad, barrio, direccion, fecha_cumpleanos,
                    genero, tipo_sangre, estado_etapa, fecha_estado
                ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW())
            a,  
                INSERT INTO prospectos_manual (
                    nombres, apellidos, tipo_documento, documento, correos, telefono,
                    pais, departamento, ciudad, localidad, barrio, direccion, fecha_cumpleanos,
                    genero, tipo_sangre, recomendado, nombre_recomendador, grupo_wp, grupo_wp1,
                    como_ayudar, pasion, intereses, autorizacion, fecha_registro, estado_etapa, fecha_estado
                ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW())
            zProspecto agregado exitosamenter   	statementu   Última consulta:u   Error en la inserción: )r0   r3   r   r   rf   stripr   rh   	lastrowidr   getattrprintr"   )%r   r5   r6   r   r:   rL   rM   rK   rN   rO   rJ   rQ   rR   r,   rT   rc   r]   r^   rP   rS   rU   ri   rX   rY   rZ   r[   ra   r_   rk   rb   dupsqlvalsr\   new_idr#   lasts%                                        r   agregar_prospecto_nuevor   f  s    
 C
C|hh}b1
 n,$1CRP` ))+))+ _ !HHY3 HH["5	 HH["5	 HHY3 HHZ4 HH%56 HH^4 HHX. HH^4 HHX. HH%56 HH[1	 HH^4 HHV, HH[1	 HH]3 HH%:; HHZ0 HH[1	 HH]3 HHX. HH[1	 HHX. HH%78 HH]3 /+>Q RU^T`aaC#(5kuwx^ ))+))+ a 3/+>O PSZR\]]C#(5isuvV ))+))+ Y C/+>P QT\S^__C#(5jtvwN ))+))+ O #$$JJ$J//55C #Iw	7H,	6; (I{F"L2D
 ,,#*)1YK 8 > > @C #$5y'8 &)\<QD 33C Y>9 &)VYK7D
C Y	7H,	69lKlHiL.R^`D
 kk#t$$$jjl,MX\^dWef ))+))+  i bd a ^\ Y `T Q %/j 	%   ^	3T2D)40 		 /GAx-PZ\]]))+))+ ^ ))+))+ s  T0Q) T0P:T0P=T0GQ) !Q "Q) -T0QT0QT0#Q) ;Q	<Q) T0QT06Q7T0=Q) QQ) !T06Q7T0QT0Q) $Q%Q) >Q?BQ) Q!Q) ,Q#-Q) T0Q%T03Q'4T0=T0 Q) T0T0	Q) T0T0Q) T0T0Q) Q) !Q) #Q) %T0'T0)	S13RS,	RS,RS,/S10S4 4T0	S
T0$S'%T0,S11S4 4T-
TT-%T(&T--T0archivoprocesamiento_ligeroc                   K   h d| j                   xs dj                         | j                  xs ddt        ffd}|r	 | j                  j                  d        |       r?t        j                  t        j                  | j                  dt        d       d{   }n?t        j                  t        j                  | j                  t        dd	d
       d{   }	 | j                  j                  d       |S | j                          d{   }	 | j                  j                  d        |       r<t        j                  t        j                  t        |      dt               d{   S t        j                  t        j                  t        |      t        dd	       d{   S # t        $ r Y cw xY w7 '7 # t        $ r Y |S w xY w7 # t        $ r Y w xY w7 w7 <w)z
    Convierte un UploadFile (Excel o CSV) en un DataFrame de pandas.
    Usa asyncio.to_thread para que pandas no bloquee el event loop.
    >   application/vnd.ms-excel.application/vnd.ms-excel.sheet.macroEnabled.12Aapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetrD   r   c                  2    j                  d      xs  v S )N).xlsx.xls.xlsm.xlsb)endswith)TIPOS_EXCELnombre_archivotipo_contenidos   r   es_excelz%archivo_a_dataframe.<locals>.es_excel  s     &&'JKl~alOll    r      )
sheet_namedtypenrowsNpython)r   sepenginer   )r   r   )r   r   r   )filenamelowercontent_typeboolfileseekr   asyncio	to_threadpd
read_excelr"   read_csvreadr   )r   r   r   dfrawr   r   r   s        @@@r   archivo_a_dataframer     s    
K
 &&,"335N))/RNmd m 	LLa  :((QRZ]eghhB((gll#SW`hprssB	LLa  	
C! z&&r}}gclqX[\\\""2;;CTZbccc)  		 is  			    ]cs   AG5	F; $A G5$G%?G5$G%G5*G G5G G5 G" ;>G59G1:<G56G37G5;	GG5GG5G5	GG5GG5"	G.+G5-G..G53G5c                   K   	 d}t        j                  |d       t         j                  j                  | j                  xs d      \  }}t        j                         j                  d      }| | }t         j                  j                  ||      }t        j                  |d      4 d{   }	 | j                  d       d{   }|sn|j                  |       d{    6ddd      d{    	 | j                  d	       d{    dd
| ||ddS 7 q7 Y7 ?7 0# 1 d{  7  sw Y   @xY w7 -# t        $ r Y 6w xY w# t        $ r}	ddt        |	       i dcY d}	~	S d}	~	ww xY ww)uL   
    Guarda el archivo en disco de forma asíncrona, leyendo en chunks.
    prospectos/archivoT)exist_okrD   z%Y%m%d_%H%M%SwbNi   r   z!Archivo guardado exitosamente en )rutanombrer   FzError al guardar el archivo: )osmakedirspathsplitextr   r   nowstrftimejoinaiofilesopenr   writer   r   r"   )
r   carpeta_destino_	extensioncurrent_timer   ruta_archivor   chunkr#   s
             r   subir_archivo_excelr     so    c.
Od3ww''(8(8(>B?9||~..?(>)5ww||O^D==t4 	% 	%%ll;77ggen$$	 	% 	%	,,q/!!
 :<.I)^D
 	
	%7 %	% 	% 	% 	% " 		  c /LSQRVH-U_abbcs   E8B,E 0D"1E 4D*
D$D*&D&'D*,E 7D(8E =E D?E E !E8"E $D*&D*(E *D<0D31D<8E ?E 	E
E EE 	E5E0*E5+E80E55E8nombre_excelc                 l   d}t         j                  j                  ||       }t         j                  j                  |      st	        d|       t         j                  j                  |       d   j                         }|dv rd}n
|dk(  rd}nd}t        d	|i      }t        | t        |d
      |      S )uJ   
    Crea un UploadFile desde un archivo en disco (sync es OK aquí).
    r   zNo existe el archivo: r   )r   r   r   r   r   z.csvztext/csvzapplication/octet-streamzcontent-typerb)r   r   headers)
r   r   r   existsFileNotFoundErrorr   r   r   StarletteUploadFiler   )r   carpetar   r   r   r   s         r   excel_a_uploadfiler   <  s     #G77<<.D77>>$"8 ?@@  .q1779I77Z	f	!1~|45G4d;KU\]]r   excelc                   K   t        |        d {   }|j                  d      sddi dS t        | d       d {   }|ddi dS t        |      sddd	S dd
|j                  di       j                  dd      dS 7 n7 Dw)Nr   FzError al subir el archivor   Tr   Error al leer el archivou$   Encabezados inválidos en el archivor!   u   Encabezados válidosr   r   rD   )r   r   r   )r   r0   r   validar_headers_en_primera_fila)r   subir_excelr   s      r   1procesamiento_principal_paso_1_validacion_formator   Q  s     +E22K??9% -HRTUU"5tD	DB	z -GQSTT*2. -STT(>kooV\^`FaFeFefnprFstt 3 
Es    BB +BBABBc                    K   t        |       }t        |d       d {   }|ddi dS t        |      }|sdddg idS dd||j                  d	dS 7 4w)
NTr   Fr   r   z(No se encontraron columnas en el archivocolumnaszColumnas obtenidas)r   r   )r   r   obtener_columnas_dfr   )r   upr   r   s       r   5procesamiento_principal_paso_2_obtener_columnas_excelr   _  sv     	E	"B"2DA	AB	z -GQSTT"2&H -Wblnpaqrr(<S[oqozozF{|| 
Bs   AA5Ac                  2   K   t                d {   S 7 wr{   )/obtener_columnas_obligatorias_prospectos_manualr|   r   r   Jprocesamiento_principal_paso_3_obtener_columnas_obligatorias_base_de_datosr   j  s     @BBBB   c                  2   K   t                d {   S 7 wr{   )-obtener_columnas_opcionales_prospectos_manualr|   r   r   Hprocesamiento_principal_paso_4_obtener_columnas_opcionales_base_de_datosr   n  s     >@@@@r   c                    K   t        |       }t        |d       d {   }t        |d       d {   }|S 7 7 w)NTr   <   )timeout_seconds)r   r   validar_datos_con_openai)r   r   r   respuesta_openais       r   0procesamiento_principal_paso_5_validacion_openair   r  s?     	L	)B"2DA	AB5b"MM 
BMs   >:><>>mapeo_columnasc                 n  K   t        d       t        d|        t        |       }t        |       d {   }|t        d       ddi dS 	 t        dt        |       dt        |j                         d	       t        d
t        |j                                |j                         D cg c]  }||j                  vs| }}|r-t        d|        dd| |t        |j                        ddS t        d       t        |j                         |      }t        dt        |j                                t        d       t        |j                                d {   }t        dt        |j                                t        d       t        |j                               }t        dt        |j                                t        d       t        |j                               }	t        dt        |	j                                t        d       t        |	j                               }
t        dt        |
j                                t        d       t        |
j                                d {   }t        d|        t        d       t        d       dt        |
      |j                  dd      |j                  dd      |j                  d d      d!d"S 7 c c}w 7 7 v# t        $ r/}dd l}|j#                          t%        d#t'        |      $      d }~ww xY ww)%Nz=== Paso 6: Inicio ===u   📌 Mapeo Columnas recibido: u#   ❌ Error: no se pudo leer el ExcelFr   r   u   ✅ DataFrame leído con z	 filas y z	 columnasu$   📋 Columnas originales del Excel: u   ⚠️ Columnas faltantes: z"Columnas no encontradas en Excel: )	faltantescolumns_excelu#   👉 Aplicando mapeo_de_columnas...u    ✅ df_mapeado listo. Columnas: u2   👉 Aplicando incluir_solo_columnas_necesarias...u0   ✅ df_con_columnas_necesarias listo. Columnas: u2   👉 Aplicando normalizar_columnas_obligatorias...u.   ✅ df_columnas_obligatorias listo. Columnas: u+   👉 Aplicando normalizar_todas_columnas...u+   ✅ df_sin_strings_vacios listo. Columnas: u+   👉 Aplicando categorizacion_prospectos...u%   ✅ df_categorizado listo. Columnas: z!Entrando a insertar_prospectos...u   Resultado de la inserción: u!   ✅ Inserción masiva finalizada.z(=== Paso 6: Finalizado correctamente ===Tprospectos_manual_totalr   prospectos_manual_parcialdescartados_contactabilidad)cantidad_total_de_filascantidad_con_validacion_totalcantidad_con_validacion_parcial#cantidad_con_validacion_descartados)r   r   i  )status_codedetail)r   r   r   lencolumnslistkeysmapeo_de_columnascopy incluir_solo_columnas_necesarias normalizar_columnas_obligatoriasnormalizar_todas_columnascategorizacion_prospectosinsertar_prospectosr0   r   	traceback	print_excr   r"   )r   r   r   r   colr  
df_mapeadodf_necesariasdf_obldf_cleandf_cat	insercionr#   r  s                 r   Hprocesamiento_principal_paso_6_procesar_excel_y_guardar_en_base_de_datosr   y  s     

"#	*>*:
;<	L	)B"2&	&B	z34 -GQSTT3<)#b')C

O;LIVW4T"**5E4FGH$2$7$7$9SSS

=RSS	S/	{;< ?	{K&/$rzzBRS  	34&rwwy.A
0j6H6H1I0JKLBC>z?PQQ@mF[F[A\@]^_BC1-2D2D2FG>tFNN?S>TUV;<,V[[];;DAQAQ<R;STU;<*8==?;5d6>>6J5KLM12-fkkm<<	,YK891289 +.v;1:?XZ[1\3<==A\^_3`7@}}Ebde7f	
 	
Y 
' T R  =  <CF;;<sy   3L5K-L5A!K: /K0K00K: 7L58A-K: %K5&DK: K8A*K: ,L50K: 8K: :	L2*L--L22L5r   c                     | j                   D cg c])  }t        |      j                         j                         + }}|D cg c]  }|s|j	                  d      r| }}t        |      dkD  S c c}w c c}w )Nunnamedr   )r  r"   r   r   
startswithr  )r   cr   headers_validoss       r   r   r     sd    02

;1A$$&;H;"*PQaY8OqPOP!## <Ps   .A1A6A6A6c                     K   t        d       d {   } | xs g D cg c]  }|d   dk7  s|d    }}dd|dS 7 )c c}w w)Nz^SELECT columna FROM columnas_obligatorias WHERE es_obligatoria=1 AND tabla='prospectos_manual'columnar   TzColumnas obligatorias obtenidasr   r   r   r$  r   s      r   r   r     sZ     h E $);BHQ1Y<43GAiLHDH(ISWXX	 I    A;A==	AAc                     K   t        d       d {   } | xs g D cg c]  }|d   dk7  s|d    }}dd|dS 7 )c c}w w)NzISELECT columna FROM columnas_obligatorias WHERE tabla='prospectos_manual'r'  r   TzTodas las columnas obtenidasr   r(  r)  s      r   ,obtener_todas_las_columnas_prospectos_manualr,    sZ     S E $);BHQ1Y<43GAiLHDH(FPTUU	 Ir*  c                     K   t        d       d {   } | xs g D cg c]  }|d   dk7  s|d    }}dd|dS 7 )c c}w w)Nz^SELECT columna FROM columnas_obligatorias WHERE es_obligatoria=0 AND tabla='prospectos_manual'r'  r   TzColumnas opcionales obtenidasr   r(  r)  s      r   r   r     sZ     h E $);BHQ1Y<43GAiLHDH(GQUVV	 Ir*  c                     | j                   j                         D cg c]  }t        |      j                          c}S c c}w r{   )r  tolistr"   r   )r   r$  s     r   r   r     s-    $&JJ$5$5$78qCFLLN888s    A c                     | j                  d      j                         j                  d      }|j                  d      S )N
   rD   records)orient)headr  fillnato_dict)r   
df_muestras     r   obtener_primeras_10_filas_dfr8    s7    !!#**2.JY//r   openai_api_keymodelr   c                 t  K   t                d {   }|j                  dg       }t        t        |             }d| d| d}|xs t	        j
                  d      xs dj                         j                  d      j                  d      }|sd	d
dS d| dd}	|dddd|dgdd}
d	}	 t        j                  |      4 d {   }|j                  d|	|
       d {   }|j                          |j                         }|j                  di g      d   j                  di       j                  dd      xs |j                         j                         }d d d       d {    ||dk(  dS 7 f7 7 7 # 1 d {  7  sw Y   #xY w# t        $ r}t        d|        Y d }~Bd }~ww xY ww)Nr   u   
Eres un validador de coherencia de datos tabulares. 
Tu tarea es revisar si las columnas especificadas tienen sentido lógico y son consistentes, 
basándote en la semántica de los valores y no únicamente en el formato.

COLUMNAS A VALIDAR:
z?

MUESTRA DE DATOS (lista de diccionarios que mapea la tabla):
u  

Criterios de validación:
- Documento: puede ser cédula, tarjeta de identidad, pasaporte, NIT u otro identificador. 
  Debe parecer razonable (solo dígitos o combinación alfanumérica típica de documentos). 
  Ejemplos válidos: "1032456789", "TI12345", "NIT 900123456". 
  Ejemplos inválidos: "ABCXYZ", "123hola", "!!!!!".
- Nombres/Apellidos: deben contener letras (mínimo 2 caracteres alfabéticos). 
  Se permite que tengan espacios, tildes o caracteres especiales comunes en nombres. 
  Son inválidos si contienen solo números, símbolos o letras aleatorias sin sentido.
- Teléfonos: deben ser cadenas numéricas razonables (7–15 dígitos aprox.), 
  se aceptan con prefijos internacionales (+57, etc.). 
- En general: un valor se considera coherente si corresponde con el tipo esperado para la columna. 
  No te fijes en mayúsculas/minúsculas o pequeños errores de formato.
- Los valores nulos o vacíos NO cuentan como incoherentes.

Regla de decisión:
- Evalúa fila por fila las columnas especificadas. 
- Si en una fila la mayoría de las columnas evaluadas son coherentes → la fila es aceptable. 
- Si alguna fila tiene la mayoría de sus columnas incoherentes → toda la validación se considera fallida.

Regla de salida:
- Si TODAS las filas son aceptables → responde ÚNICAMENTE: exitosamenteexitoso
- Si ALGUNA fila es fallida → responde ÚNICAMENTE: fracasadamentefracasado

No des explicaciones, no muestres porcentajes ni repitas datos. 
Responde SOLO con la palabra clave fracasadamentefracasado o exitosamenteexitoso.
OPENAI_API_KEYrD   "'fracasadamentefracasadoF)ai_validationr   zBearer zapplication/json)AuthorizationzContent-Typesystemz5Eres un verificador de coherencia de datos tabulares.)rolecontentuserr   )r:  messagestemperature)timeoutz*https://api.openai.com/v1/chat/completions)r   jsonchoicesr   rD  zError en la llamada a OpenAI: exitosamenteexitoso)r   r0   r"   r8  r   getenvr   httpxAsyncClientpostraise_for_statusrI  r   r   r   )r   r9  r:  r   columnas_respr   filas_textopromptapi_keyr   payloadai_textclientrespr   r#   s                   r   r   r     s     JKKM  ,H2267K 

   $FJ B+;!<BIIKQQRUV\\]`aG!:uMM")' 3EWXG*ab/
 G (G4$$_= 	v 	v%Q[bipqqD!!#99;Dxx	B40377	2FJJ9VXYd]dkkmssuG		v 	v %<Q1QRRA Lp	vq	v 	v 	v 	v
  4.qc2334s   F8E6BF8-F E9F E?#E;$A:E?F )E=*F .	F89F ;E?=F ?FFFF 	F5F0+F80F55F8mapeoc                 &    | j                  |      S )Nr  )rename)r   rY  s     r   r  r  4  s    99U9##r   c                    K   t                d{   j                  dg       }|D ]  }|| j                  vsd| |<    | |   j                         S 7 Bw)zK
    Trae columnas de la BD (async), agrega faltantes (None) y ordena.
    Nr   )r,  r0   r  r  )r   columnas_tablar  s      r   r  r  8  sc      IJJOOPVXZ[N bjj BsG n""$$	 Ks   AA'AAc                    dt         fdfd}fd}fd}fd}d| j                  v r| d   j                  |      | d<   d| j                  v r| d   j                  |      | d<   d	| j                  v r| d	   j                  |      | d	<   d
| j                  v r| d
   j                  |      | d
<   d| j                  v r| d   j                  |      | d<   | S )Nr   c                 h    t        j                  |       xs t        |       j                         dk(  S NrD   r   isnar"   r   vs    r   es_vacioz2normalizar_columnas_obligatorias.<locals>.es_vacioD  $    wwqz1SV\\^r11r   c                 f     |       rd S t        |       j                         j                         S r{   r"   r   r   re  rf  s    r   limpiar_textoz7normalizar_columnas_obligatorias.<locals>.limpiar_textoG  '    {t>A(<(<(>>r   c                 b     |       ry t        j                  ddt        |             }|r|S d S )Nz\DrD   )resubr"   )re  solorf  s     r   limpiar_documentoz;normalizar_columnas_obligatorias.<locals>.limpiar_documentoJ  s0    A;vveRQ(t%%r   c                 t     |       ry t        |       j                         j                         }d|v r|S d S )N@ri  )re  correorf  s     r   limpiar_correoz8normalizar_columnas_obligatorias.<locals>.limpiar_correoP  s6    A;Q%%'v0D0r   c                      |       ry t        j                  ddt        |             j                         j	                         }|r|S d S )Nz[^0-9 +]rD   )rn  ro  r"   r   r   )re  telrf  s     r   limpiar_telefonoz:normalizar_columnas_obligatorias.<locals>.limpiar_telefonoV  sB    A;ff["c!f-335;;=s#t#r   rL   rM   rK   rN   rO   r   r  apply)r   rk  rq  ru  rx  rf  s        @r   r  r  C  s    2t 2?&1$ bjj ByM4G4G4V"Y-bjj B{O4I4I-4X"[/bjj B{O4I4IJ[4\"[/bjj ByM4G4G4W"Y-bjj BzN4H4HIY4Z"Z.Ir   c                 v    dt         fdfd}| j                  D ]  }| |   j                  |      | |<    | S )Nr   c                 h    t        j                  |       xs t        |       j                         dk(  S ra  rb  rd  s    r   rf  z+normalizar_todas_columnas.<locals>.es_vacioe  rg  r   c                 f     |       rd S t        |       j                         j                         S r{   ri  rj  s    r   limpiarz*normalizar_todas_columnas.<locals>.limpiarh  rl  r   ry  )r   r~  r  rf  s      @r   r  r  d  sD    2t 2? zz )S'--(3)Ir   c                 h   dt         t           dt        fd}g }| j                         D ]p  \  }} ||j	                  d            } ||j	                  d            }|r|r|j                  d       J|s|r|j                  d       `|j                  d       r | j                         } || d	<   | S )
Nvalorr   c                     | rt        j                  |       ryt        |       j                         j	                         }|dvS )NF)zno ingresadozmal digitado)r   rc  r"   r   r   )r  re  s     r   	es_validoz,categorizacion_prospectos.<locals>.es_validoq  s9    J$$&888r   rN   rO   r*   parcial
descartadocategoria_prospecto)r   r"   r   iterrowsr0   appendr  )r   r  
categoriasr   fila	correo_oktelefono_oks          r   r  r  p  s    9# 94 9 J;;= ,4dhhy12	 45g&+i(l+, 
B *BIr   r   tamano_lotec                 |  K   | j                   ryt        | j                        }dj                  d |D              dz   }dj                  dgt	        |      z        dz   }d| d| d	| d
}| j                  dd      D cg c]  }t        d |D               }}t        |||       d{   }	|	S c c}w 7 w)z
    Inserta un DataFrame en MySQL por lotes (async) usando execute() con chunking interno.
    Agrega `estado_etapa`=1 y `fecha_estado`=NOW() en VALUES.
    Devuelve la cantidad de filas insertadas.
    r   z, c              3   (   K   | ]
  }d | d   yw)`Nr|   )r}   r$  s     r   r   z-insercion_masiva_dataframe.<locals>.<genexpr>  s     8!q1X8s   z , `estado_etapa`, `fecha_estado`z%sz
, 1, NOW()zINSERT INTO `z` (z
) VALUES ()FN)indexnamec              3   N   K   | ]  }t        j                  |      rd n|  y wr{   )r   rc  )r}   re  s     r   r   z-insercion_masiva_dataframe.<locals>.<genexpr>  s     6Abggajda'6s   #%)
chunk_size)emptyr  r  r   r  
itertuplestupler   )
r   r   r  r   columnas_sqlplaceholdersr   r  rl   
insertadoss
             r   insercion_masiva_dataframer    s      
xx BJJH998x88;]]L99dVc(m34|CL,s<.
<.PQ
RC
 MMDM9% 	666%F % sF{CCJ% Ds   B B<B5B<.B:/B<c                 X  K   ddddg d}	 | j                   rd|d<   |S d| j                  vrt        d      | j                         } | d   j	                  t
              j
                  j                         j
                  j                         | d<   g d}d	t        j                  d
t        t
           dt        t
           fd}	 | | d   dk(     j                         }|j                   s(d|d<    |||      }t        ||   d       d{   |d<   	 | | d   dk(     j                         }|j                   s(d|d<    |||      }t        ||   d       d{   |d<   	 | | d   dk(     j                         }|j                   s(d|d<    |||      }t        ||   d       d{   |d<   t        |d         dk(  |d<   |S 7 # t        $ r*}|d   j                  dt        |              Y d}~d}~ww xY w7 # t        $ r*}|d   j                  dt        |              Y d}~d}~ww xY w7 # t        $ r*}|d   j                  dt        |              Y d}~d}~ww xY w# t        $ r1}|d   j                  dt        |              d|d<   |cY d}~S d}~ww xY ww)u^   
    total/parcial → prospectos_manual
    descartado   → descartados_contactabilidad
    Fr   )r   r  r  r  errorsTr   r  z;El DataFrame debe incluir la columna 'categoria_prospecto'.)rL   rM   rJ   rK   rN   rO   rP   rQ   rR   ra   r_   rS   r`   rT   rb   rU   rW   rX   rY   rZ   r[   r]   r^   rc   validacion_contactabilidadcdfcolsr   c                 J    |D cg c]  }|| j                   v s| c}S c c}w r{   r[  )r  r  r$  s      r   cols_presentesz+insertar_prospectos.<locals>.cols_presentes  s!    #8!qCKK'7A888s     r*      r  rI   Nr  r  z#Error insertando prospectos TOTAL: r  r   r  z%Error insertando prospectos PARCIAL: r  r  z)Error insertando prospectos DESCARTADOS: zError general: )r  r  
ValueErrorr  astyper"   r   r   r   	DataFramer   r  r   r  r  )	r   resumencolumnas_comunesr  df_totalr  r#   
df_parcialdf_descs	            r   r  r    s     #$%&'(G?88!%GIN 

2Z[[WWY$&'<$=$D$DS$I$M$M$S$S$U$Y$Y$_$_$a !
	9 	9DI 	9$s) 	9		U"23w>?DDFH>>9:56%h0@A;UTN$7< 612		WB45BCHHJJ##;<
78%j2BC=Wt$&9> 834		[12lBCHHJG==8945%g/?@?YDM#@@ :56 !!23q8	?6  	UH$$'J3q6(%STT	U8  	WH$$'LSQRVH%UVV	W:  	[H$$'PQTUVQWPX%YZZ	[
    ?3q6(!;<"	s   	J*I- J*B&I- AG GG AG? G=G? "AH7 'H5(H7 /I- J*G 	G: G50I- 5G::I- =G? ?	H2 H-(I- -H22I- 5H7 7	I*  I% I- %I**I- -	J'6&J"J'J*"J''J*)F)Nzgpt-4o   )i  )Br   rn  mathr   ior   r   typingr   r   r   r   r	   pandasr   rM  r   fastapir
   r   starlette.datastructuresr   r   dotenvr   r   conexion_db.conexionr   r   r   r"   r   r$   rA   r1   rp   r   r   r   r   r   r   r  r   r   r   r   r   r   r   r   r   r   r   r,  r   r   r8  r   r  r  r  r  r  r  r  r|   r   r   <module>r     s   	 	     3 3    - O    B AtCy =d38n =<c3h <DcN <~z# zT#s(^ zPTUXZ]U]P^ zz=DcN =tCH~ =8=S#X =4S> =0\S#X \4S> \:b4S> bd3PS8n b<CS#X C4S> CT'dz 'd 'dZ\ZfZf 'dTcz cd38n cD^S ^-@ ^*u: uRVWZ\_W_R` u}s }tTWY\T\~ }CA @<@<cN@< 
#s(^@<N$ $ $YVW9BLL 9T#Y 90R\\ 0d4S>6J 0 %)	FS
FSSMFS FS 	FS
 
#s(^FSZ$",, $tCH~ $",, $%r|| % % ",, B	",, 	2<< 	",, 2<< : 
  		<K",, K4S> Kr   