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.

Option Explicit

' =========================================================================
' MACRO PRINCIPAL PARA COLOREAR LA TABLA ADMET
' =========================================================================
Sub ColorearSemaforosADMET()
    Dim ws As Worksheet
    Set ws = ActiveSheet
    Dim lastRow As Long, lastCol As Long
    
    ' Encontrar los límites de los datos
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
    lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column

    Dim col As Long, row As Long
    Dim rawHeader As String, cleanHeader As String
    Dim cellValue As Variant
    Dim bgColor As Long, fontColor As Long

    ' Apagamos la actualización de pantalla para que la macro vuele
    Application.ScreenUpdating = False

    ' Recorremos todas las columnas
    For col = 1 To lastCol
        rawHeader = CStr(ws.Cells(1, col).Value)
        cleanHeader = LimpiarCabecera(rawHeader)
        
        ' Recorremos todas las filas de esa columna
        For row = 2 To lastRow
            cellValue = ws.Cells(row, col).Value
            
            ' Solo procesamos si la celda no está vacía
            If Not IsEmpty(cellValue) Then
                bgColor = -1
                fontColor = RGB(0, 0, 0) ' Texto negro por defecto
                
                ' Obtenemos el color según las reglas
                bgColor = ObtenerColorADMET(cleanHeader, cellValue, fontColor)
                
                ' Si hay una regla para esta celda, la coloreamos
                If bgColor <> -1 Then
                    ws.Cells(row, col).Interior.Color = bgColor
                    ws.Cells(row, col).Font.Color = fontColor
                    ' Si el fondo es oscuro y el texto blanco, lo ponemos en negrita
                    If fontColor = RGB(255, 255, 255) Then ws.Cells(row, col).Font.Bold = True
                End If
            End If
        Next row
    Next col

    Application.ScreenUpdating = True
    MsgBox "¡La coloración del semáforo ADMET se ha completado con éxito!", vbInformation, "Proceso Terminado"
End Sub

' =========================================================================
' FUNCIÓN PARA LIMPIAR LOS NOMBRES DE LAS COLUMNAS (VERSIÓN MEJORADA)
' =========================================================================
Function LimpiarCabecera(ByVal header As String) As String
    header = LCase(Trim(header))
    
    ' 1. Quitar sufijos comunes
    header = Replace(header, " (prob)", "")
    header = Replace(header, " (da)", "")
    header = Replace(header, " (å²)", "")
    header = Replace(header, " (papp)", "")
    header = Replace(header, " (l/kg)", "")
    header = Replace(header, " (l/h/kg)", "")
    header = Replace(header, " (-log h)", "")
    header = Replace(header, " (log)", "")
    header = Replace(header, " (-log (td50 [mg/kg/day]))", "")
    header = Replace(header, " (-log ld50, mg/kg)", "")
    header = Replace(header, " (-log nmol/kg/day)", "")
    header = Replace(header, " (-log [igc50])", "")
    header = Replace(header, " (logbcf)", "")
    
    ' 2. Reemplazar símbolos problemáticos
    header = Replace(header, "µ", "u") ' Signo micro real
    header = Replace(header, "μ", "u") ' Letra griega mu
    
    ' 3. Aspiradora de prefijos: Quita cualquier número, punto, guion o espacio inicial
    Dim c As String
    Do While Len(header) > 0
        c = Left(header, 1)
        If IsNumeric(c) Or c = "." Or c = "-" Or c = " " Then
            header = Mid(header, 2)
        Else
            Exit Do
        End If
    Loop
    
    LimpiarCabecera = Trim(header)
End Function

' =========================================================================
' MOTOR DE REGLAS ADMET
' =========================================================================
Function ObtenerColorADMET(header As String, v As Variant, ByRef fontCol As Long) As Long
    ' Colores base
    Dim R As Long, Y As Long, G As Long
    R = HexToRGB("#C14E4E") ' Rojo
    Y = HexToRGB("#FFDF33") ' Amarillo
    G = HexToRGB("#63C28D") ' Verde
    
    Dim bg As Long
    bg = -1
    
    ' 1. REGLAS DE TEXTO
    Select Case header
        Case "gsk_rule", "lipinski_rule", "pfizer_rule"
            If LCase(CStr(v)) = "accept" Then bg = G
            If LCase(CStr(v)) = "not accept" Then bg = R
            GoTo FinishColor
            
        Case "bbb+/bbb-"
            If LCase(CStr(v)) = "bbb+" Then bg = G
            If LCase(CStr(v)) = "bbb-" Then bg = R
            GoTo FinishColor
    End Select
    
    ' Si el valor no es un número para el resto, salimos
    If Not IsNumeric(v) Then GoTo FinishColor
    
    ' 2. REGLAS NUMÉRICAS
    Select Case header
        Case "molecular weight", "mw"
            If v < 100 Or v > 600 Then bg = R
            If (v >= 100 And v < 200) Or (v > 500 And v <= 600) Then bg = Y
            If v >= 200 And v <= 500 Then bg = G
            
        Case "natom"
            If v < 5 Or v > 50 Then bg = R
            If (v >= 5 And v <= 14) Or (v >= 36 And v <= 50) Then bg = Y
            If v >= 15 And v <= 35 Then bg = G
            
        Case "nhet"
            If v = 0 Or v >= 16 Then bg = R
            If v = 1 Or (v >= 10 And v <= 15) Then bg = Y
            If v >= 2 And v <= 9 Then bg = G
            
        Case "nring"
            If v >= 7 Then bg = R
            If v = 0 Or (v >= 5 And v <= 6) Then bg = Y
            If v >= 1 And v <= 4 Then bg = G
            
        Case "nrot"
            If v >= 16 Then bg = R
            If v >= 10 And v <= 15 Then bg = Y
            If v >= 0 And v <= 9 Then bg = G
            
        Case "hba"
            If v >= 13 Then bg = R
            If v = 0 Or (v >= 11 And v <= 12) Then bg = Y
            If v >= 1 And v <= 10 Then bg = G
            
        Case "hbd"
            If v >= 6 Then bg = R
            If v >= 4 And v <= 5 Then bg = Y
            If v >= 0 And v <= 3 Then bg = G
            
        Case "tpsa"
            If v > 160 Then bg = R
            If v < 20 Or (v > 140 And v <= 160) Then bg = Y
            If v >= 20 And v <= 140 Then bg = G
            
        Case "slogp", "logp"
            If v < 0 Or v > 5 Then bg = R
            If (v >= 0 And v <= 1) Or (v > 3 And v <= 5) Then bg = Y
            If v > 1 And v <= 3 Then bg = G
            
        Case "logs"
            If v < -6 Or v > 1 Then bg = R
            If (v >= -6 And v <= -4) Or (v > 0.5 And v <= 1) Then bg = Y
            If v > -4 And v <= 0.5 Then bg = G
            
        Case "qed"
            bg = GetRangeCol(v, 0.35, 0.6, R, Y, G)
            
        Case "pka"
            If v < 0 Or v > 11 Then bg = R
            If (v >= 0 And v <= 3) Or (v > 8 And v <= 11) Then bg = Y
            If v > 3 And v <= 8 Then bg = G
            
        Case "caco_2"
            bg = GetRangeCol(v, -6, -5.15, R, Y, G)
        Case "caco_2_c", "hia", "ugt_substrate", "ppb"
            bg = GetRangeCol(v, 0.3, 0.7, R, Y, G)
        Case "mdck"
            bg = GetRangeCol(v, 0.4, 0.6, R, Y, G)
        Case "f50", "f30", "f20"
            bg = GetRangeCol(v, 0.3, 0.5, R, Y, G)
        Case "bbb", "eye_irritation", "skin_corrosion", "skin_sensitisation"
            bg = GetRangeCol(v, 0.3, 0.8, R, Y, G)
            
        Case "oatp1b1_inhibitor"
            bg = GetRangeCol(v, 0.5, 0.8, HexToRGB("#DBDBDB"), HexToRGB("#FFB733"), R)
        Case "oatp1b3_inhibitor"
            bg = GetRangeCol(v, 0.6, 0.85, HexToRGB("#DBDBDB"), HexToRGB("#FF926A"), R)
        Case "oatp2b1_inhibitor"
            bg = GetRangeCol(v, 0.25, 0.45, G, Y, R)
        Case "oct1_inhibitor"
            bg = GetRangeCol(v, 0.25, 0.55, G, Y, R)
        Case "oct2_inhibitor"
            bg = GetRangeCol(v, 0.15, 0.45, G, Y, R)
        Case "bcrp_inhibitor", "cyp2b6_inhibitor"
            bg = GetRangeCol(v, 0.25, 0.6, G, Y, R)
        Case "bsep_inhibitor"
            bg = GetRangeCol(v, 0.25, 0.7, G, Y, R)
        Case "mate1_inhibitor"
            If v < 0.55 Then bg = G Else bg = R
            
        Case "pgp_inhibitor", "cyp1a2_inhibitor"
            bg = GetRangeCol(v, 0.2, 0.6, G, Y, R)
        Case "pgp_substrate"
            bg = GetRangeCol(v, 0.3, 0.7, HexToRGB("#3CB371"), HexToRGB("#FFD700"), HexToRGB("#FF8C00"))
        Case "vdss"
            bg = GetRangeCol(v, -0.6, 0.5, HexToRGB("#C96464"), HexToRGB("#FFE24C"), HexToRGB("#7995E9"))
            
        Case "cyp3a4_inhibitor", "cyp2c9_inhibitor", "cyp2c19_inhibitor"
            bg = GetRangeCol(v, 0.15, 0.5, G, Y, R)
        Case "cyp2d6_inhibitor"
            bg = GetRangeCol(v, 0.15, 0.8, G, Y, R)
            
        Case "cyp1a2_substrate"
            bg = GetRangeCol(v, 0.2, 0.6, HexToRGB("#9FD7EF"), HexToRGB("#DBDBDB"), HexToRGB("#6787E7"))
        Case "cyp3a4_substrate"
            bg = GetRangeCol(v, 0.3, 0.65, HexToRGB("#9FD7EF"), HexToRGB("#DBDBDB"), HexToRGB("#FFA333"))
        Case "cyp2b6_substrate"
            bg = GetRangeCol(v, 0.2, 0.8, HexToRGB("#9FD7EF"), HexToRGB("#DFCBDF"), HexToRGB("#845CAD"))
        Case "cyp2c9_substrate"
            bg = GetRangeCol(v, 0.2, 0.6, HexToRGB("#9FD7EF"), HexToRGB("#7FB1B3"), HexToRGB("#F39999"))
        Case "cyp2c19_substrate"
            bg = GetRangeCol(v, 0.25, 0.75, HexToRGB("#9FD7EF"), HexToRGB("#DBDBDB"), HexToRGB("#EDAB94"))
        Case "cyp2d6_substrate"
            bg = GetRangeCol(v, 0.2, 0.75, HexToRGB("#9FD7EF"), HexToRGB("#DBDBDB"), HexToRGB("#C777DB"))
            
        Case "hlm", "rlm", "clp_c", "dili", "nephrotoxicity", "ames", "skin_irritation", "hse"
            bg = GetRangeCol(v, 0.3, 0.7, G, Y, R)
        Case "clr"
            If v < 0.5 Then bg = HexToRGB("#FFE24C") Else bg = HexToRGB("#7DA7CA")
            
        Case "t50"
            bg = GetRangeCol(v, -1, 0, HexToRGB("#C96464"), HexToRGB("#76C99B"), HexToRGB("#7DA7CA"))
        Case "mrt"
            bg = GetRangeCol(v, -1.2, 0, HexToRGB("#C96464"), HexToRGB("#76C99B"), HexToRGB("#7DA7CA"))
            
        Case "neurotoxicity"
            bg = GetRangeCol(v, -2, -1.5, G, Y, R)
            
        ' ====== AÑADIDAS TODAS LAS VARIANTES POSIBLES DE hERG ======
        Case "herg_1um", "herg 1um"
            bg = GetRangeCol(v, 0.15, 0.4, G, Y, R)
        Case "herg_10um", "herg 10um"
            bg = GetRangeCol(v, 0.25, 0.75, G, Y, R)
        Case "herg_30um", "herg 30um", "eye_corrosion"
            bg = GetRangeCol(v, 0.25, 0.8, G, Y, R)
        Case "herg_1-10um", "herg_10-30um", "herg 1-10um", "herg 10-30um", "herg_1_10um", "herg_10_30um"
            bg = GetRangeCol(v, 0.3, 0.7, G, Y, R)
            
        Case "respiratory_toxicity"
            bg = GetRangeCol(v, 0.4, 0.8, HexToRGB("#00FF7F"), HexToRGB("#F0E68C"), HexToRGB("#CD853F"))
            
        Case "adt", "mouse_carcinogenicity_c", "rodents_carcinogenicity", "micronucleus", _
             "reproductive_toxicity", "mitochondrial_toxicity", "hemolytic_toxicity", "repeated_dose_toxicity", _
             "aot_c", "fdamdd_c", "ar", "er", "ar_lbd", "er_lbd", "aromatase", "ahr", "are", "atad5", _
             "p53", "ppar", "mmp", "tr", "gr", "subcapitata_toxicity", "crustaceans_toxicity", _
             "magna_toxicity", "fish_toxicity", "fathead_minnow_toxicity", "bluegill_sunfish_toxicity", _
             "rainbow_trout_toxicity", "sheepshead_minnow_toxicity", "pyriformis_toxicity_c", _
             "honey_bee_toxicity", "colinus_virginanus_toxicity", "anas_platyrhynchos_toxicity", _
             "bcf_c", "biodegradability", "photoinduced_toxicity", "phototoxicity_photoirritation", "photoallergy"
            bg = GetRangeCol(v, 0.4, 0.7, G, Y, R)
            
        Case "rat_carcinogenicity_c"
            bg = GetRangeCol(v, 0.5, 0.8, G, Y, R)
        Case "mouse_carcinogenicity", "rat_carcinogenicity", "fdamdd"
            bg = GetRangeCol(v, 1, 3, G, Y, R)
        Case "aot"
            bg = GetRangeCol(v, -2.7, -1.7, G, Y, R)
        Case "pyriformis_toxicity"
            bg = GetRangeCol(v, -0.5, 1, G, Y, R)
        Case "bcf"
            bg = GetRangeCol(v, 2, 3, G, Y, R)
            
    End Select

FinishColor:
    If bg <> -1 Then
        If bg = R Or bg = HexToRGB("#C14E4E") Or bg = HexToRGB("#C96464") _
           Or bg = HexToRGB("#845CAD") Or bg = HexToRGB("#6787E7") _
           Or bg = HexToRGB("#CD853F") Then
            fontCol = RGB(255, 255, 255) ' Blanco
        End If
    End If
    
    ObtenerColorADMET = bg
End Function

' =========================================================================
' FUNCIONES AUXILIARES
' =========================================================================
Function GetRangeCol(ByVal v As Double, ByVal t1 As Double, ByVal t2 As Double, ByVal cLow As Long, ByVal cMid As Long, ByVal cHigh As Long) As Long
    If v < t1 Then
        GetRangeCol = cLow
    ElseIf v <= t2 Then
        GetRangeCol = cMid
    Else
        GetRangeCol = cHigh
    End If
End Function

Function HexToRGB(hexColor As String) As Long
    Dim r As String, g As String, b As String
    hexColor = Replace(hexColor, "#", "")
    r = Mid(hexColor, 1, 2)
    g = Mid(hexColor, 3, 2)
    b = Mid(hexColor, 5, 2)
    HexToRGB = RGB(Val("&H" & r), Val("&H" & g), Val("&H" & b))
End Function

'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()