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?
admetlab30-DRUGBank-approved.xlsm.ALT + F11 para abrir el editor de VBA.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()
