ADMETlab 3.0 versus ADMETsar 3.0 frente a la base de datos Drugbank approved.


Ya hemos desarrollado un 'bot' para calculos con ADMETlab 3.0, a continuación se muestra un 'bot' para calculos con ADMETsar 3.0. Hemos aplicado ambos scripts sobre la base de datos Drugbank approved (puedes descargar una copia con estructuras 3D). Puedes descargar una copia de los cálculos de ADMET en este fichero Excel con macros.


Los colores de fondo en las celdas del fichero Excel de la figura anterior se consiguen con un script de Visual Basic para Aplicaciones (VBA) que puedes ver justo debajo.

¿Cómo se aplicar sobre un fichero Excel?

  1. Abre tu archivo admetlab30-DRUGBank-approved.xlsm.
  2. Pulsa ALT + F11 para abrir el editor de VBA.
  3. Ve a Insertar > Módulo.
  4. Pega el código de abajo.
  5. Cierra el editor de VBA y vuelve a Excel.
  6. Pulsa ALT + F8, selecciona ADMET_Final_Numeric_87_Corregido y pulsa Ejecutar.

Sub ADMET_Final_Numeric_87_Corregido()
    Dim ws As Worksheet: Set ws = ActiveSheet
    Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Dim lastCol As Long: lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
    Dim r As Long, c As Long
    Dim h As String, v As Variant
    
    ' Columnas de soporte para cálculos
    Dim colMW As Long, colLogP As Long, colLogD As Long
    Dim colNHA As Long, colNHD As Long, colTPSA As Long
    Dim vCount As Integer
    
    ' Definición de colores
    Dim cGreen As Long: cGreen = RGB(198, 239, 206)
    Dim cYellow As Long: cYellow = RGB(255, 235, 156)
    Dim cRed As Long: cRed = RGB(255, 199, 206)
    
    Application.ScreenUpdating = False

    ' 1. Mapeo de columnas de datos base
    For c = 1 To lastCol
        Select Case UCase(Trim(ws.Cells(1, c).Value))
            Case "MW": colMW = c
            Case "LOGP": colLogP = c
            Case "LOGD": colLogD = c
            Case "NHA": colNHA = c
            Case "NHD": colNHD = c
            Case "TPSA": colTPSA = c
        End Select
    Next c

    ' 2. Procesar toda la tabla
    For c = 1 To lastCol
        h = UCase(Trim(ws.Cells(1, c).Value))
        
        For r = 2 To lastRow
            v = ws.Cells(r, c).Value
            
            Select Case h
                ' --- REGLAS INDUSTRIALES CON RESULTADO NUMÉRICO ---
                
                Case "LIPINSKI"
                    vCount = 0
                    If ws.Cells(r, colMW).Value > 500 Then vCount = vCount + 1
                    If ws.Cells(r, colLogP).Value > 5 Then vCount = vCount + 1
                    If ws.Cells(r, colNHA).Value > 10 Then vCount = vCount + 1
                    If ws.Cells(r, colNHD).Value > 5 Then vCount = vCount + 1
                    
                    ws.Cells(r, c).Value = vCount ' Guardamos el nro de violaciones (0-4)
                    ' Verde si cumple (0-1 violaciones), Rojo si falla (>=2)
                    If vCount < 2 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed

                Case "PFIZER"
                    ' logP > 3 y TPSA < 75 = 0 (Pobre), de lo contrario 1 (Excelente)
                    If ws.Cells(r, colLogP).Value > 3 And ws.Cells(r, colTPSA).Value < 75 Then
                        ws.Cells(r, c).Value = 0: ws.Cells(r, c).Interior.Color = cRed
                    Else
                        ws.Cells(r, c).Value = 1: ws.Cells(r, c).Interior.Color = cGreen
                    End If

                Case "GSK"
                    ' MW <= 400 y logP <= 4 = 1 (Excelente)
                    If ws.Cells(r, colMW).Value <= 400 And ws.Cells(r, colLogP).Value <= 4 Then
                        ws.Cells(r, c).Value = 1: ws.Cells(r, c).Interior.Color = cGreen
                    Else
                        ws.Cells(r, c).Value = 0: ws.Cells(r, c).Interior.Color = cRed
                    End If

                Case "GOLDENTRIANGLE"
                    ' 200 <= MW <= 500 y -2 <= logD <= 5 = 1 (Excelente)
                    If (ws.Cells(r, colMW).Value >= 200 And ws.Cells(r, colMW).Value <= 500) And _
                       (ws.Cells(r, colLogD).Value >= -2 And ws.Cells(r, colLogD).Value <= 5) Then
                        ws.Cells(r, c).Value = 1: ws.Cells(r, c).Interior.Color = cGreen
                    Else
                        ws.Cells(r, c).Value = 0: ws.Cells(r, c).Interior.Color = cRed
                    End If
                
                ' --- COLOREADO DE LOS 83 PARÁMETROS RESTANTES ---
                Case Else
                    If IsNumeric(v) And v <> "" Then
                        Select Case h
                            ' Rangos Óptimos
                            Case "MW": If v >= 100 And v <= 600 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NHA": If v >= 0 And v <= 12 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NHD": If v >= 0 And v <= 7 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "TPSA": If v >= 0 And v <= 140 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NROT": If v >= 0 And v <= 11 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NRING": If v >= 0 And v <= 6 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "MAXRING": If v >= 0 And v <= 18 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NHET": If v >= 1 And v <= 15 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "FCHAR": If v >= -4 And v <= 4 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NRIG": If v >= 0 And v <= 30 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "NSTEREO": If v <= 2 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            
                            ' Índices y Logs
                            Case "QED", "FSP3": If v > 0.67 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "MCE-18": If v >= 45 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "LOGS": If v >= -4 And v <= 0.5 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "LOGD", "LOGP": If v >= 1 And v <= 3 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "LOGVDSS": If v >= 0.04 And v <= 20 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            
                            ' Permeabilidad y Transporte
                            Case "CACO2", "PAMPA": If v > -5.15 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "MDCK": If v > 0.000002 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "PPB": If v <= 90 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed
                            Case "FU": If v >= 5 Then ws.Cells(r, c).Interior.Color = cGreen Else ws.Cells(r, c).Interior.Color = cRed

                            ' Farmacocinética y Toxicidad (Probabilidades 0-1)
                            ' Verdes si < 0.3 (Inhibidores, Tox, etc)
                            Case "PGP_INH", "PGP_SUB", "HIA", "F20", "F30", "F50", "OATP1B1", "OATP1B3", "MRP1", _
                                 "CYP1A2-INH", "CYP2C19-INH", "CYP2C9-INH", "CYP2D6-INH", "CYP3A4-INH", "CYP2B6-INH", "CYP2C8-INH", _
                                 "HERG", "HERG-10UM", "DILI", "AMES", "ROA", "FDAMDD", "SKINSEN", "CARCINOGENICITY", "EC", "EI", _
                                 "RESPIRATORY", "NEUROTOXICITY-DI", "OTOTOXICITY", "HEMATOTOXICITY", "NEPHROTOXICITY-DI", "GENOTOXICITY", _
                                 "NR-AHR", "NR-AR", "NR-AR-LBD", "NR-AROMATASE", "NR-ER", "NR-ER-LBD", "NR-PPAR-GAMMA", "SR-ARE", _
                                 "SR-ATAD5", "SR-HSE", "SR-MMP", "SR-P53", "SKIN_SENSITIZATION":
                                
                                If v <= 0.3 Then ws.Cells(r, c).Interior.Color = cGreen Else _
                                If v <= 0.7 Then ws.Cells(r, c).Interior.Color = cYellow Else ws.Cells(r, c).Interior.Color = cRed

                            ' Invertidos (Verde si > 0.7, como los sustratos CYP que queremos que se metabolicen o BBB)
                            Case "BCRP", "BSEP", "BBB", "CYP1A2-SUB", "CYP2C19-SUB", "CYP2C9-SUB", "CYP2D6-SUB", "CYP3A4-SUB", "CYP2B6-SUB", "LM-HUMAN":
                                If v >= 0.7 Then ws.Cells(r, c).Interior.Color = cGreen Else _
                                If v >= 0.3 Then ws.Cells(r, c).Interior.Color = cYellow Else ws.Cells(r, c).Interior.Color = cRed
                            
                            ' Clearance y Vida Media
                            Case "CL-PLASMA":
                                If v <= 5 Then ws.Cells(r, c).Interior.Color = cGreen Else _
                                If v <= 15 Then ws.Cells(r, c).Interior.Color = cYellow Else ws.Cells(r, c).Interior.Color = cRed
                            Case "T0.5":
                                If v > 8 Then ws.Cells(r, c).Interior.Color = cGreen Else _
                                If v >= 1 Then ws.Cells(r, c).Interior.Color = cYellow Else ws.Cells(r, c).Interior.Color = cRed
                        End Select
                    End If
            End Select
        Next r
    Next c

    Application.ScreenUpdating = True
    MsgBox "Análisis de Frecuencias (1/0) y Violaciones de Lipinski (0-4) completado.", vbInformation
End Sub

'bot' de Python...


import os
import time
import glob
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.common.exceptions import TimeoutException

# --- CONFIGURACIÓN DE RUTAS ---
INPUT_FILE = r'D:\admetsar3\todos_mis_smiles.txt' 
DOWNLOAD_DIR = r'D:\admetsar3\descargas'
TEMP_FILE = r'D:\admetsar3\temp_50.txt' 
FINAL_OUTPUT = r'D:\admetsar3\resultados_combinados_admetsar.txt'
TAMANO_BLOQUE = 50 

# --- XPATHS ---
URL_WEB = "https://lmmd.ecust.edu.cn/admetsar3/predict.php"
PESTANA_ADMET_XPATH = '//*[@id="myTab"]/li[2]/a'
INPUT_FILE_XPATH = '//*[@id="smiles_file"]'
BOTON_SUBMIT_XPATH = '//*[@id="ios"]/div/div[2]/div[2]/a/button'
ENLACE_JOB_XPATH = '/html/body/div/div[1]/div[2]/ul/li[1]/a'
BOTON_TXT_XPATH = '/html/body/div/div/div[3]/div[2]/div[1]/a/button'

# --- NUEVO: Función para limpiar la carpeta antes de empezar ---
def limpiar_descargas_anteriores():
    if not os.path.exists(DOWNLOAD_DIR):
        os.makedirs(DOWNLOAD_DIR)
        return
        
    archivos_viejos = glob.glob(os.path.join(DOWNLOAD_DIR, '*.*'))
    if archivos_viejos:
        print(f"Limpiando {len(archivos_viejos)} archivos de ejecuciones anteriores...")
        for f in archivos_viejos:
            try:
                os.remove(f)
            except Exception as e:
                print(f"No se pudo borrar {os.path.basename(f)}: {e}")
        print("Carpeta de descargas limpia y lista.\n")

def divide_list(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

def esperar_descarga(directorio, timeout=120):
    segundos = 0
    while segundos < timeout:
        time.sleep(1)
        if not glob.glob(os.path.join(directorio, '*.crdownload')):
            return True 
        segundos += 1
    return False

def configurar_navegador():
    opciones = webdriver.ChromeOptions()
    opciones.add_argument('--ignore-certificate-errors')
    opciones.add_argument('--ignore-ssl-errors')
    prefs = {
        "download.default_directory": DOWNLOAD_DIR,
        "download.prompt_for_download": False,
        "directory_upgrade": True,
        "safebrowsing.enabled": True
    }
    opciones.add_experimental_option("prefs", prefs)
    return webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=opciones)

def unir_resultados_txt():
    archivos_txt = glob.glob(os.path.join(DOWNLOAD_DIR, '*.txt'))
    if not archivos_txt:
        print("No hay archivos para unir.")
        return
    
    archivos_txt.sort(key=os.path.getctime)
    print(f"\nUniendo {len(archivos_txt)} archivos...")
    
    lista_df = []
    for f in archivos_txt:
        try:
            df = pd.read_csv(f, sep='\t')
            lista_df.append(df)
        except Exception as e:
            try:
                df = pd.read_csv(f, sep=',')
                lista_df.append(df)
            except Exception as e2:
                print(f"Error al leer {os.path.basename(f)}: {e2}")
    
    if lista_df:
        df_final = pd.concat(lista_df, ignore_index=True)
        df_final.to_csv(FINAL_OUTPUT, index=False, sep='\t')
        print("="*50)
        print(f"¡ÉXITO! Archivo final generado: {FINAL_OUTPUT}")
        print(f"Total de moléculas procesadas: {len(df_final)}")
        print("="*50)

if __name__ == '__main__':
    # 0. Limpiamos la basura de ayer
    limpiar_descargas_anteriores()

    try:
        with open(INPUT_FILE, 'r') as f:
            smiles_list = [line.strip() for line in f if line.strip()]
    except FileNotFoundError:
        print(f"Error: No se encontró el archivo {INPUT_FILE}")
        exit(1)
        
    total_bloques = (len(smiles_list) // TAMANO_BLOQUE) + (1 if len(smiles_list) % TAMANO_BLOQUE != 0 else 0)
    print(f"Iniciando ADMETsar 3: {len(smiles_list)} moléculas en {total_bloques} bloques de {TAMANO_BLOQUE}.")
    
    driver = configurar_navegador()

    try:
        for i, bloque in enumerate(divide_list(smiles_list, TAMANO_BLOQUE)):
            print(f"\n--- Procesando bloque {i+1} de {total_bloques} ---")
            
            with open(TEMP_FILE, 'w') as f_temp:
                f_temp.write("\n".join(bloque))
            
            driver.get(URL_WEB)
            time.sleep(2) 
            
            pestana = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, PESTANA_ADMET_XPATH)))
            driver.execute_script("arguments[0].click();", pestana)
            time.sleep(1)
            
            input_file = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, INPUT_FILE_XPATH)))
            input_file.send_keys(TEMP_FILE)
            time.sleep(1)
            
            btn_submit = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, BOTON_SUBMIT_XPATH)))
            driver.execute_script("arguments[0].click();", btn_submit)
            
            enlace_elemento = WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.XPATH, ENLACE_JOB_XPATH)))
            url_resultado = enlace_elemento.get_attribute("href")
            print(f"Job generado! Navegando a: {url_resultado}")
            
            driver.get(url_resultado)
            
            print("Esperando cálculos...")
            archivos_antes = len(glob.glob(os.path.join(DOWNLOAD_DIR, '*.txt')))
            
            boton_txt = None
            intentos = 0
            max_intentos = 10 
            
            while intentos < max_intentos:
                try:
                    boton_txt = WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.XPATH, BOTON_TXT_XPATH)))
                    break 
                except TimeoutException:
                    intentos += 1
                    print(f"  -> El botón TXT no aparece. Recargando web (Intento {intentos}/{max_intentos})...")
                    driver.refresh()
                    time.sleep(3) 
            
            if boton_txt:
                print("¡Resultados listos! Descargando TXT...")
                driver.execute_script("arguments[0].click();", boton_txt)
                esperar_descarga(DOWNLOAD_DIR)
                
                if len(glob.glob(os.path.join(DOWNLOAD_DIR, '*.txt'))) > archivos_antes:
                    print(f"-> Bloque {i+1} completado con éxito.")
                else:
                    print("-> ADVERTENCIA: No se detectó la descarga del archivo.")
            else:
                print(f"-> ERROR GRAVE: El botón TXT no apareció tras {max_intentos} recargas. Saltando al siguiente bloque.")
            
            time.sleep(3) 
            
    except Exception as e:
        print(f"\nSe detuvo por un error: {e}")
    finally:
        driver.quit()
        if os.path.exists(TEMP_FILE):
            os.remove(TEMP_FILE)
        print("\nNavegador cerrado.")
        unir_resultados_txt()



Sub ColorearADMETsar()
    Dim ws As Worksheet
    Dim lastRow As Long, lastCol As Long
    Dim c As Long, r As Long
    Dim header As String
    Dim cell As Range
    Dim v As Variant
    Dim hexC As String
    
    ' Colores principales por defecto
    Const R_Col As String = "#C14E4E"
    Const Y_Col As String = "#FFDF33"
    Const G_Col As String = "#63C28D"

    On Error Resume Next
    Set ws = ThisWorkbook.Sheets("ADMETsar30")
    On Error GoTo 0

    If ws Is Nothing Then
        MsgBox "No se encontró la hoja 'ADMETsar30'. Comprueba el nombre.", vbExclamation
        Exit Sub
    End If

    ' Optimizamos el rendimiento apagando el refresco de pantalla
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    For c = 1 To lastCol
        header = Trim(ws.Cells(1, c).Value)
        
        For r = 2 To lastRow
            Set cell = ws.Cells(r, c)
            v = cell.Value
            hexC = ""

            ' Validar que la celda no esté vacía
            If Not IsEmpty(v) And Trim(CStr(v)) <> "" Then
                
                ' 1. Reglas Categóricas (Texto)
                If header = "Lipinski_rule" Or header = "Pfizer_rule" Or header = "GSK_rule" Then
                    If LCase(CStr(v)) = "accept" Then
                        hexC = G_Col
                    ElseIf LCase(CStr(v)) = "not accept" Then
                        hexC = R_Col
                    End If
                
                ' 2. Reglas Numéricas
                ElseIf IsNumeric(v) Then
                    v = CDbl(v)
                    Select Case header
                        
                        ' --- RANGOS PERSONALIZADOS DISCONTINUOS ---
                        Case "MW"
                            If v < 100 Or v > 600 Then
                                hexC = R_Col
                            ElseIf (v >= 100 And v <= 200) Or (v >= 500 And v <= 600) Then
                                hexC = Y_Col
                            ElseIf v > 200 And v < 500 Then
                                hexC = G_Col
                            End If
                        Case "nAtom"
                            If v < 5 Or v > 50 Then
                                hexC = R_Col
                            ElseIf (v >= 5 And v <= 15) Or (v >= 35 And v <= 50) Then
                                hexC = Y_Col
                            ElseIf v > 15 And v < 35 Then
                                hexC = G_Col
                            End If
                        Case "nHet"
                            If v = 0 Or v > 15 Then
                                hexC = R_Col
                            ElseIf v = 1 Or (v >= 10 And v <= 15) Then
                                hexC = Y_Col
                            ElseIf v >= 2 And v <= 9 Then
                                hexC = G_Col
                            End If
                        Case "nRing"
                            If v > 6 Then
                                hexC = R_Col
                            ElseIf v = 0 Or (v >= 5 And v <= 6) Then
                                hexC = Y_Col
                            ElseIf v >= 1 And v <= 4 Then
                                hexC = G_Col
                            End If
                        Case "nRot"
                            If v > 15 Then
                                hexC = R_Col
                            ElseIf v >= 10 And v <= 15 Then
                                hexC = Y_Col
                            ElseIf v >= 0 And v <= 9 Then
                                hexC = G_Col
                            End If
                        Case "HBA"
                            If v > 12 Then
                                hexC = R_Col
                            ElseIf v = 0 Or (v >= 11 And v <= 12) Then
                                hexC = Y_Col
                            ElseIf v >= 1 And v <= 10 Then
                                hexC = G_Col
                            End If
                        Case "HBD"
                            If v > 5 Then
                                hexC = R_Col
                            ElseIf v >= 4 And v <= 5 Then
                                hexC = Y_Col
                            ElseIf v >= 0 And v <= 3 Then
                                hexC = G_Col
                            End If
                        Case "TPSA"
                            If v > 160 Then
                                hexC = R_Col
                            ElseIf (v >= 0 And v <= 20) Or (v >= 140 And v <= 160) Then
                                hexC = Y_Col
                            ElseIf v > 20 And v < 140 Then
                                hexC = G_Col
                            End If
                        Case "SlogP"
                            If v < 0 Or v > 5# Then
                                hexC = R_Col
                            ElseIf (v >= 0 And v <= 1#) Or (v >= 3.5 And v <= 5#) Then
                                hexC = Y_Col
                            ElseIf v > 1# And v < 3.5 Then
                                hexC = G_Col
                            End If
                        Case "logS"
                            If v < -6 Or v > 1 Then
                                hexC = R_Col
                            ElseIf (v >= -6 And v <= -4) Or (v >= 0.5 And v <= 1) Then
                                hexC = Y_Col
                            ElseIf v > -4 And v < 0.5 Then
                                hexC = G_Col
                            End If
                        Case "QED"
                            If v < 0.35 Then
                                hexC = R_Col
                            ElseIf v >= 0.35 And v < 0.6 Then
                                hexC = Y_Col
                            ElseIf v >= 0.6 Then
                                hexC = G_Col
                            End If
                        Case "logP"
                            If v < 0 Or v > 5 Then
                                hexC = R_Col
                            ElseIf (v >= 0 And v < 2) Or (v >= 4 And v <= 5) Then
                                hexC = Y_Col
                            ElseIf v >= 2 And v <= 4 Then
                                hexC = G_Col
                            End If
                        Case "pKa"
                            If v < 0 Or v > 11 Then
                                hexC = R_Col
                            ElseIf (v >= 0 And v <= 3) Or (v >= 8 And v <= 11) Then
                                hexC = Y_Col
                            ElseIf v > 3 And v < 8 Then
                                hexC = G_Col
                            End If
                        Case "MATE1_inhibitor"
                            If v <= 0.5 Then
                                hexC = G_Col
                            Else
                                hexC = R_Col
                            End If
                        Case "CLr"
                            If v < 0.5 Then
                                hexC = "#FFE24C"
                            Else
                                hexC = "#7DA7CA"
                            End If
                            
                        ' --- BLOQUES AGRUPADOS POR RANGOS ESTÁNDAR ---
                        Case "Caco_2_c", "HIA", "F50", "F30", "F20": hexC = GetRangeCol(v, 0.3, 0.7, R_Col, Y_Col, G_Col)
                        Case "Caco_2": hexC = GetRangeCol(v, -6#, -5.15, R_Col, Y_Col, G_Col)
                        Case "MDCK": hexC = GetRangeCol(v, 0.4, 0.6, R_Col, Y_Col, G_Col)
                        Case "BBB", "Eye_irritation", "Skin_corrosion", "Skin_sensitisation": hexC = GetRangeCol(v, 0.3, 0.8, R_Col, Y_Col, G_Col)
                        Case "OATP1B1_inhibitor": hexC = GetRangeCol(v, 0.5, 0.8, "#DBDBDB", "#FFB733", R_Col)
                        Case "OATP1B3_inhibitor": hexC = GetRangeCol(v, 0.6, 0.85, "#DBDBDB", "#FF926A", R_Col, True)
                        Case "OATP2B1_inhibitor": hexC = GetRangeCol(v, 0.3, 0.5, G_Col, Y_Col, R_Col)
                        Case "OCT1_inhibitor": hexC = GetRangeCol(v, 0.3, 0.6, G_Col, Y_Col, R_Col)
                        Case "OCT2_inhibitor": hexC = GetRangeCol(v, 0.2, 0.5, G_Col, Y_Col, R_Col)
                        Case "BCRP_inhibitor", "CYP2B6_inhibitor": hexC = GetRangeCol(v, 0.25, 0.6, G_Col, Y_Col, R_Col)
                        Case "BSEP_inhibitor": hexC = GetRangeCol(v, 0.25, 0.7, G_Col, Y_Col, R_Col)
                        Case "Pgp_inhibitor", "CYP1A2_inhibitor": hexC = GetRangeCol(v, 0.2, 0.6, G_Col, Y_Col, R_Col)
                        Case "Pgp_substrate": hexC = GetRangeCol(v, 0.3, 0.7, G_Col, Y_Col, R_Col)
                        Case "PPB": hexC = GetRangeCol(v, 0.2, 0.7, R_Col, Y_Col, G_Col)
                        Case "VDss": hexC = GetRangeCol(v, -0.6, 0.5, "#C96464", "#FFE24C", "#7995E9", True)
                        Case "CYP3A4_inhibitor", "CYP2C9_inhibitor", "CYP2C19_inhibitor": hexC = GetRangeCol(v, 0.15, 0.5, G_Col, Y_Col, R_Col)
                        Case "CYP2D6_inhibitor": hexC = GetRangeCol(v, 0.15, 0.8, G_Col, Y_Col, R_Col)
                        Case "CYP1A2_substrate": hexC = GetRangeCol(v, 0.2, 0.6, "#9FD7EF", "#DBDBDB", "#6787E7")
                        Case "CYP3A4_substrate": hexC = GetRangeCol(v, 0.3, 0.65, "#9FD7EF", "#DBDBDB", "#FFA333")
                        Case "CYP2B6_substrate": hexC = GetRangeCol(v, 0.2, 0.8, "#9FD7EF", "#DFCBDF", "#845CAD")
                        Case "CYP2C9_substrate": hexC = GetRangeCol(v, 0.2, 0.6, "#9FD7EF", "#7FB1B3", "#F39999")
                        Case "CYP2C19_substrate": hexC = GetRangeCol(v, 0.25, 0.75, "#9FD7EF", "#DBDBDB", "#EDAB94")
                        Case "CYP2D6_substrate": hexC = GetRangeCol(v, 0.2, 0.75, "#9FD7EF", "#DBDBDB", "#C777DB")
                        Case "HLM", "RLM", "CLp_c", "DILI", "Nephrotoxicity", "Ames", "Skin_irritation": hexC = GetRangeCol(v, 0.3, 0.7, G_Col, Y_Col, R_Col)
                        Case "UGT_substrate": hexC = GetRangeCol(v, 0.3, 0.7, R_Col, Y_Col, G_Col, True)
                        Case "T50": hexC = GetRangeCol(v, -1#, 0, "#C96464", "#76C99B", "#7DA7CA")
                        Case "MRT": hexC = GetRangeCol(v, -1.2, 0, "#C96464", "#76C99B", "#7DA7CA")
                        Case "Neurotoxicity": hexC = GetRangeCol(v, -2, -1.5, G_Col, Y_Col, R_Col)
                        Case "hERG_1uM": hexC = GetRangeCol(v, 0.15, 0.4, G_Col, Y_Col, R_Col)
                        Case "hERG_10uM": hexC = GetRangeCol(v, 0.25, 0.75, G_Col, Y_Col, R_Col)
                        Case "hERG_30uM": hexC = GetRangeCol(v, 0.25, 0.8, G_Col, "#FFE9C3", "#F39999")
                        Case "Respiratory_toxicity": hexC = GetRangeCol(v, 0.4, 0.8, "#33FF98", "#F3EBA3", "#D79D65")
                        Case "Eye_corrosion": hexC = GetRangeCol(v, 0.2, 0.8, G_Col, Y_Col, R_Col)
                        Case "ADT", "Mouse_carcinogenicity_c": hexC = GetRangeCol(v, 0.4, 0.7, G_Col, Y_Col, R_Col)
                        Case "Mouse_carcinogenicity": hexC = GetRangeCol(v, 1, 3, G_Col, Y_Col, R_Col)
                    End Select
                End If
            End If

            ' Aplicar color (si coincide con alguna regla) o limpiar el fondo si no
            If hexC <> "" Then
                cell.Interior.Color = HexToRGB(hexC)
                
                ' === NUEVA REGLA DE FORMATO DE TEXTO ===
                ' Si el color asignado es exactamente el rojo principal (#C14E4E)
                If UCase(hexC) = UCase(R_Col) Then
                    cell.Font.Color = vbWhite  ' Letra blanca
                    cell.Font.Bold = True      ' Negrita
                Else
                    cell.Font.ColorIndex = xlAutomatic ' Letra por defecto (negra)
                    cell.Font.Bold = False             ' Quitar negrita
                End If
                ' =======================================
            Else
                cell.Interior.ColorIndex = xlNone
                cell.Font.ColorIndex = xlAutomatic ' Restaurar letra si no hay fondo
                cell.Font.Bold = False
            End If
            
        Next r
    Next c

    ' Restaurar ajustes de Excel
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    MsgBox "¡Análisis ADMET completado y celdas coloreadas!", vbInformation
End Sub

' Función auxiliar para evaluar rangos simples contínuos (CORREGIDA CON ByVal)
Function GetRangeCol(ByVal v As Double, ByVal t1 As Double, ByVal t2 As Double, ByVal cLow As String, ByVal cMid As String, ByVal cHigh As String, Optional ByVal incLow As Boolean = False) As String
    Dim inLow As Boolean
    If incLow Then inLow = (v <= t1) Else inLow = (v < t1)
    
    If inLow Then
        GetRangeCol = cLow
    ElseIf v <= t2 Then
        GetRangeCol = cMid
    Else
        GetRangeCol = cHigh
    End If
End Function

' Función auxiliar para convertir HEX (ej. #C14E4E) a color Long de VBA (CORREGIDA CON ByVal)
Function HexToRGB(ByVal hexStr As String) As Long
    hexStr = Replace(hexStr, "#", "")
    HexToRGB = RGB(Val("&H" & Mid(hexStr, 1, 2)), Val("&H" & Mid(hexStr, 3, 2)), Val("&H" & Mid(hexStr, 5, 2)))
End Function