a
    0i*.                     @   sR   d Z ddlZddlZddlZddlmZ ddlmZm	Z	 G dd dZ
dd ZdS )	u:   
Funciones auxiliares para validación de datos del Score
    N)datetime)DecimalInvalidOperationc                   @   sx   e Zd ZdZdgZedd Zedd Zedd Zed	d
 Z	edd Z
edd Zedd ZedddZdS )ValidadorTiposDatosuJ   
    Clase para validar tipos de datos según el catálogo SQL Server
    OrderNum&Linec                 C   s   |    } d}d}t|| }|rJ|ddt|dt|dfS t|| }|rv|dt|dddfS | dddfS )u  
        Extrae información del tipo de dato SQL Server
        
        Args:
            tipo_sql (str): Tipo SQL como 'NVARCHAR(50)', 'DECIMAL(18,2)', 'INT'
            
        Returns:
            tuple: (tipo_base, longitud, precision, escala)
        z(\w+)\((\d+)\)z(\w+)\((\d+),(\d+)\)   N      )upperstriprematchgroupint)tipo_sqlZpatron_con_longitudZpatron_decimalr    r   N/var/www/html/src/App/SupyCtrol_Module/IngenieroControl/ValidarScoreHelpers.pyextraer_info_tipo_sql   s    &z)ValidadorTiposDatos.extraer_info_tipo_sqlc              	   C   s~   t | s| dkrdS z>tt| }d|  kr8dkrBn nW dS dd|  fW S W n$ ttfyx   dd|  f Y S 0 dS )	u!   Valida si el valor es INT válido TNi   iFzValor fuera de rango INT: !   No es un número entero válido: Npdisnar   float
ValueError	TypeErrorvalorZval_intr   r   r   validar_int<   s    zValidadorTiposDatos.validar_intc              	   C   s~   t | s| dkrdS z>tt| }d|  kr8dkrBn nW dS dd|  fW S W n$ ttfyx   dd|  f Y S 0 dS )	u$   Valida si el valor es BIGINT válidor   r   l         l    FzValor fuera de rango BIGINT: r   Nr   r   r   r   r   validar_bigintK   s    z"ValidadorTiposDatos.validar_bigintc           
   
   C   s  t | s| dkrdS zt| }t|}|dddd}d|v rh|d}t|d }t|d }nt|}d}||| krdd	|  d
||  dfW S ||d krdd|  d| d| dfW S ||krW dS  ty
 }	 zddt|	 fW  Y d}	~	S d}	~	0 0 dS )u$  
        Valida si el valor cumple con DECIMAL(precision, escala)
        PERMITE valores con más decimales y los trunca mentalmente
        
        Args:
            valor: Valor a validar
            precision (int): Total de dígitos
            escala (int): Dígitos decimales
        r   r   -+.r   r   Fu    Parte entera excede precisión: z (max u    dígitos enteros)d   zDemasiados decimales: z (tiene z, se esperaban max )zError validando decimal: N)r   r   r   strreplacesplitlen	Exception)
r   	precisionescalaZvalor_float	valor_strZ	valor_absZpartesZdigitos_enterosZdigitos_decimaleser   r   r   validar_decimalZ   s(    
z#ValidadorTiposDatos.validar_decimalc              
   C   s   t | s| dkrdS zLt|  }t||kr6W dS dd| d|dd  dt| d	fW S W n6 ty } zdd
t| fW  Y d}~S d}~0 0 dS )z0Valida si el valor cumple con NVARCHAR(longitud)r   r   Fu   Excede longitud máxima de z: 'N2   z...' (longitud: r%   zError validando NVARCHAR: )r   r   r&   r   r)   r*   )r   longitudr-   r.   r   r   r   validar_nvarchar   s    .z$ValidadorTiposDatos.validar_nvarcharc                 C   s   t | s| dkrdS g d}t|  }||v r6dS |dsT|dsT|drXdS z.t j| dd}|jd	k rd
d|  fW S W dS    d
d|  f Y S 0 dS )uN   Valida si el valor es DATETIME válido o un valor especial que representa NULLr   r   )z
00/01/1900z
00-01-1900z
1900-01-00z00:00:00z
0000-00-00z
00/00/0000z
01/01/1900z00/z00:z00-raise)errorsil  Fz#Fecha muy antigua (antes de 1900): u   No es una fecha válida: N)r   r   r&   r   
startswithto_datetimeyear)r   Zvalores_null_fechar-   Zfecha_parseadar   r   r   validar_datetime   s    
z$ValidadorTiposDatos.validar_datetimec                 C   s4   t | s| dkrdS | dv r"dS dd|  fS dS )u5   Valida si el valor es BIT válido (0, 1, True, False)r   r   )
r   r   01TFTrueFalsetruefalseFu   No es un valor BIT válido: N)r   r   )r   r   r   r   validar_bit   s
    zValidadorTiposDatos.validar_bitNc           	      C   s   t |p&|dkp&t|to&| dk}|rJ|rJ|| jv rJdd| dfS |rRdS | |\}}}}|dkrv| |S |dkr| |S |dv r| 	|||S |d	v r| 
||S |d
v r| |S |dkr| |S dS dS )u  
        Valida un valor según su tipo SQL Server
        INCLUYE VALIDACIÓN DE COLUMNAS OBLIGATORIAS (NOT NULL)
        
        Args:
            valor: Valor a validar
            tipo_sql (str): Tipo SQL como 'INT', 'NVARCHAR(50)', etc.
            nombre_columna (str): Nombre de la columna (para validar si es obligatoria)
            
        Returns:
            tuple: (es_valido: bool, mensaje_error: str o None)
        r   Fu"   ⚠️ Campo obligatorio vacío: 'u   ' no puede estar vacíor   INTZBIGINT)ZDECIMALZNUMERIC)ZNVARCHARZVARCHARZCHARZNCHAR)DATETIMEZ	DATETIME2DATEZSMALLDATETIMEZBITN)r   r   
isinstancer&   r   COLUMNAS_OBLIGATORIASr   r   r    r/   r2   r8   r?   )	clsr   r   nombre_columnaZ
esta_vacioZ	tipo_baser1   r+   r,   r   r   r   validar_por_tipo_sql   s&    (



z(ValidadorTiposDatos.validar_por_tipo_sql)N)__name__
__module____qualname____doc__rD   staticmethodr   r   r    r/   r2   r8   r?   classmethodrG   r   r   r   r   r      s&   



4

'

r   c           
   
   C   s  g }| j D ](}| | jdkr
| | dd | |< q
| j D ](}| | jdv r:| | dd | |< q:d}|| j v rt| }| |  | | dkB }| | j }|D ]&}||d |d	d
d| dd q| |  } t| }|| }	td| d|	  | |fS )u   
    Limpia y prepara el DataFrame según las especificaciones
    
    Args:
        df (pd.DataFrame): DataFrame a limpiar
        
    Returns:
        tuple: (pd.DataFrame limpio, list de filas eliminadas con detalle)
    objectc                 S   s   t | tr|  S | S )N)rC   r&   r   xr   r   r   <lambda>      z#limpiar_dataframe.<locals>.<lambda>)float64int64c                 S   s   t | rt| dS | S )N   )r   notnaroundrO   r   r   r   rQ     rR   r   r   r   u   [VACÍO]zNOT NULLu    ⚠️ Eliminar Fila completa: 'u"   ' está vacío (campo obligatorio))filacolumnar   tipo_esperadoerrorzFilas eliminadas por u    nulo/vacío: )	columnsdtypeapplyr)   r   indextolistappendprint)
dffilas_eliminadas_detallecolZcolumna_obligatoriaZfilas_antesZmask_vaciasZindices_vaciasidxZfilas_despuesfilas_eliminadasr   r   r   limpiar_dataframe  s2    





	
rh   )rK   pandasr   numpynpr   r   decimalr   r   r   rh   r   r   r   r   <module>   s    u