ADMETlab 3.0 para muchas moléculas.


Vamos a pedir a la aplicación web ADMETlab 3.0 que nos calcule todos los parámetros ADMET y drug-likeness. Usaremos el siguiente 'bot' (bot -acortamiento de "robot"- es una aplicación de software automatizada que se ejecuta en una red, diseñada para realizar tareas repetitivas a gran velocidad, a menudo simulando el comportamiento humano.) para calcular, descargar y unir las propiedades ADMET generadas por ADMETlab 3.0. El script de Python divide en bloques de 250 moléculas la lista completa de D:\admetlab3\list_32500.txt. De los resultados eliminamos la columna MOLSTR que contiene las coordenadas para deibujar cada molécula en 2D. El fichero final de resultados, D:\admetlab3\admetlab30-results-de-list_32500.txt, une todos los trozos según el orden en el que han sido creados.


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.webdriver.common.action_chains import ActionChains

# --- CONFIGURACIÓN DE RUTAS ---
INPUT_FILE = r'D:\admetlab3\list_32500.txt'
DOWNLOAD_DIR = r'D:\admetlab3\descargas_temporales'
FINAL_OUTPUT = r'D:\admetlab3\admetlab30-results-de-list_32500.txt'
TAMANO_BLOQUE = 250 

URL_WEB = "https://admetlab3.scbdd.com/server/screening"
PESTANA_SMILES_XPATH = '//*[@id="profile-tab"]'
CAJA_TEXTO_XPATH = "//textarea"
BOTON_DESCARGAR_XPATH = '/html/body/div[1]/div/div/div[1]/div[3]/button'

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

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

def configurar_navegador():
    if not os.path.exists(DOWNLOAD_DIR):
        os.makedirs(DOWNLOAD_DIR)
    else:
        print("\nLimpiando carpeta temporal para evitar duplicados...")
        archivos_viejos = glob.glob(os.path.join(DOWNLOAD_DIR, '*.csv'))
        for f in archivos_viejos:
            try: os.remove(f)
            except: pass

    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)
    
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=opciones)
    driver.maximize_window()
    return driver

def unir_resultados():
    # 1. Buscamos los archivos
    archivos_csv = glob.glob(os.path.join(DOWNLOAD_DIR, '*.csv'))
    if not archivos_csv:
        print("No hay archivos para unir.")
        return
    
    # 2. ORDENAMOS POR FECHA DE CREACIÓN (Crucial para el orden de los SMILES)
    archivos_csv.sort(key=os.path.getctime)
    
    print(f"Uniendo {len(archivos_csv)} archivos en orden cronológico...")
    lista_df = []
    
    for f in archivos_csv:
        try:
            df = pd.read_csv(f)
            # 3. ELIMINAMOS LA COLUMNA MOLSTR
            if 'molstr' in df.columns:
                df = df.drop(columns=['molstr'])
            lista_df.append(df)
        except Exception as e:
            print(f"Error al leer {os.path.basename(f)}: {e}")
    
    if lista_df:
        df_final = pd.concat(lista_df, ignore_index=True)
        df_final.to_csv(FINAL_OUTPUT, index=False, sep='\t')
        print(f"¡EXITO TOTAL! Archivo final ordenado y limpio: {FINAL_OUTPUT}")
        print(f"Total de moléculas: {len(df_final)}")

if __name__ == '__main__':
    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 existe 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: {len(smiles_list)} moléculas / {total_bloques} bloques.")
    
    driver = configurar_navegador()
    wait = WebDriverWait(driver, 300)

    try:
        for i, bloque in enumerate(divide_list(smiles_list, TAMANO_BLOQUE)):
            print(f"\n[{i+1}/{total_bloques}] Procesando bloque...")
            driver.get(URL_WEB)
            
            # Pestaña
            time.sleep(3)
            pestana = wait.until(EC.presence_of_element_located((By.XPATH, PESTANA_SMILES_XPATH)))
            ActionChains(driver).move_to_element(pestana).click().perform()
            time.sleep(2)

            # Pegar
            caja_texto = wait.until(EC.presence_of_element_located((By.XPATH, CAJA_TEXTO_XPATH)))
            caja_texto.send_keys("\n".join(bloque))
            
            # Submit Inteligente
            time.sleep(1)
            botones_submit = driver.find_elements(By.XPATH, "//*[contains(text(), 'Submit')]")
            for btn in botones_submit:
                if btn.is_displayed():
                    btn.click()
                    break
            
            # Esperar Cálculo y Descargar
            boton_descargar = wait.until(EC.element_to_be_clickable((By.XPATH, BOTON_DESCARGAR_XPATH)))
            archivos_antes = len(glob.glob(os.path.join(DOWNLOAD_DIR, '*.csv')))
            driver.execute_script("arguments[0].click();", boton_descargar)
            
            esperar_descarga(DOWNLOAD_DIR)
            time.sleep(2) # Pausa de seguridad para el disco

    except Exception as e:
        print(f"\nError en ejecución: {e}")
    finally:
        driver.quit()
        unir_resultados() # Llamada corregida