Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ El flujo principal es:

| Campo | Detalle |
|:-------------------------------:|:------------------:|
| **Versión Actual** | 1.3.0 |
| **Versión Actual** | 1.3.1 |
| **Última Actualización** | 23 de Enero, 2026 |
| **Lenguaje** | Java 17+ |
| **Framework de Automatización** | Selenium WebDriver |
Expand Down
33 changes: 26 additions & 7 deletions src/main/java/com/automatizacion/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,31 @@
import javax.swing.*;
import java.util.Scanner;

/**
* Clase principal (Entry Point) del sistema de automatización.
* Orquesta el arranque de la infraestructura, carga de configuraciones
* y gestiona el bucle principal de interacción con el usuario.
*
* @author SebasCodeDev
* @version 1.3.1
* @since 01/24/2026
*/
public class App {

public static void main(String[] args) {

// 🔧 Configuración
// --- Fase de Configuración ---
// Recupera parámetros externos desde el archivo de propiedades
String rutaExcel = ConfigManager.get("ruta.excel");
String url = ConfigManager.get("web.url"); // Aquí obtienes la URL de la config.properties
String url = ConfigManager.get("web.url");

// 🧱 Infraestructura
// --- Inicialización de Componentes (Infraestructura) ---
ExcelManager excelManager = new ExcelManager();
ConsolaView vista = new ConsolaView();

DoctorSimAutomation automation = new DoctorSimAutomationImpl();
RedService redService = new RedServiceImpl();

// 🎮 Controller
// --- Inyección de Dependencias en el Controller ---
BusquedaContactosController controller =
new BusquedaContactosController(
excelManager,
Expand All @@ -40,6 +49,7 @@ public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

// --- Ciclo de Vida de la Aplicación ---
while (true) {
vista.mostrarMensaje("""

Expand All @@ -54,11 +64,20 @@ public static void main(String[] args) {
""");

vista.mostrarMensaje("Ingrese opción:");

// Validación básica de entrada por consola
if (!scanner.hasNextInt()) {
vista.mostrarMensaje("❌ Por favor, ingresa un número válido.");
scanner.next(); // Limpiar buffer
continue;
}

int opcion = scanner.nextInt();

switch (opcion) {

case 1 -> {
// Captura interactiva de fila inicial mediante ventana de diálogo
String input = JOptionPane.showInputDialog(
null,
"¿Desde qué fila deseas iniciar la ejecución?",
Expand All @@ -72,7 +91,7 @@ public static void main(String[] args) {
}

try {
int filaInicio = Integer.parseInt(input) - 1; // base 0
int filaInicio = Integer.parseInt(input) - 1; // Ajuste a índice base 0
controller.ejecutar(filaInicio);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(
Expand All @@ -98,4 +117,4 @@ public static void main(String[] args) {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,35 @@

import com.automatizacion.model.ResultadoBusqueda;

/**
* Interfaz que define el contrato para la automatización de DoctorSim.
* * @author SebasCodeDev
* @version 1.3.1
*/
public interface DoctorSimAutomation {

/**
* Inicia el navegador y carga la URL del servicio.
*/
void iniciar(String url);

/**
* Ingresa el número en el formulario y ejecuta la búsqueda.
*/
void ingresarNumero(String numero) throws Exception;

/**
* Extrae el resultado obtenido tras la consulta.
*/
ResultadoBusqueda consultarResultado();

/**
* Refresca la página para limpiar la sesión actual.
*/
void refrescar();

/**
* Cierra el navegador y libera recursos.
*/
void cerrar();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,96 @@
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

/**
* Clase que implementa la lógica de interacción con el portal DoctorSim.
* Centraliza el manejo de elementos web y control de flujo de la búsqueda.
* @author SebasCodeDev
* @version 1.3.1
*/
public class DoctorSimAutomationImpl implements DoctorSimAutomation {

private WebDriver driver;

/**
* Inicializa el WebDriver usando la fábrica y abre la URL.
* Crea la instancia del navegador y navega a la página objetivo.
*/
@Override
public void iniciar(String url) {
// Se utiliza el Factory para obtener un driver configurado
driver = WebDriverFactory.crear(url);
}

/**
* Proceso de ingreso de datos en el buscador.
*/
@Override
public void ingresarNumero(String numero) throws InterruptedException {
// Espera máxima de 20 segundos para que el input sea interactuable
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
WebElement input = wait.until(
ExpectedConditions.elementToBeClickable(DoctorSimLocators.INPUT_NUMERO)
);

// Localiza el campo de texto del número
WebElement input = wait.until(ExpectedConditions.elementToBeClickable(DoctorSimLocators.INPUT_NUMERO));

// Limpia cualquier residuo de texto antes de escribir
input.clear();

// Pequeñas pausas para asegurar que la página procese los eventos de teclado
Thread.sleep(800);
input.sendKeys(numero);

Thread.sleep(600);
// Simula la tecla Enter para disparar la consulta
input.sendKeys(Keys.ENTER);
}

/**
* Evalúa si la consulta fue exitosa o si aparecieron obstáculos.
*/
@Override
public ResultadoBusqueda consultarResultado() {
try {
// Intenta localizar el texto del operador con una espera corta
WebDriverWait waitRes = new WebDriverWait(driver, Duration.ofSeconds(5));
WebElement operador = waitRes.until(
ExpectedConditions.visibilityOfElementLocated(DoctorSimLocators.OPERADOR)
);
WebElement operador = waitRes.until(ExpectedConditions.visibilityOfElementLocated(DoctorSimLocators.OPERADOR));

String resultado = operador.getText().trim().toUpperCase();
return ResultadoBusqueda.ok(resultado);
// Retorna el resultado formateado en mayúsculas
return ResultadoBusqueda.ok(operador.getText().trim().toUpperCase());

} catch (TimeoutException e) {

// Detectar modal
// Si no aparece el resultado, verifica si hay un modal de error visible
if (!driver.findElements(DoctorSimLocators.MODAL_OPERADOR).isEmpty()) {
try {
// Intenta cerrar el modal para no bloquear futuras ejecuciones
driver.findElement(DoctorSimLocators.BOTON_OK).click();
} catch (Exception ignored) {}

} catch (Exception ignored) {
// Si falla el clic, se ignora para continuar con el flujo
}
return ResultadoBusqueda.modal();
}

// Si no hay modal ni resultado, se marca como no detectado
return ResultadoBusqueda.noDetectado();
}
}

/**
* Recarga la página actual del navegador.
*/
@Override
public void refrescar() {
driver.navigate().refresh();
}

/**
* Finaliza la sesión del navegador de forma segura.
*/
@Override
public void cerrar() {
if (driver != null) {
driver.quit();
// Limpia la referencia para evitar fugas de memoria
driver = null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* NO contiene lógica de red
* NO contiene lógica de infraestructura
*
* @author Jhoan
* @version 3.1
* @author SebasCodeDev
* @version 1.3.1
*/
public class BusquedaContactosController {

Expand All @@ -28,20 +28,23 @@ public class BusquedaContactosController {
private final String rutaExcel;
String url;

/**
* Constructor que recibe las dependencias y la configuración necesaria.
*/
public BusquedaContactosController(
ExcelManager excelManager,
ConsolaView vista,
DoctorSimAutomation automation,
RedService redService,
String rutaExcel,
String url // ✅ nuevo parámetro
String url
) {
this.excelManager = excelManager;
this.vista = vista;
this.automation = automation;
this.redService = redService;
this.rutaExcel = rutaExcel;
this.url = url; // ✅ ahora sí tiene un valor
this.url = url;
}

/**
Expand All @@ -51,15 +54,16 @@ public void ejecutar(int filaInicio) {

List<String> contactos = excelManager.leerContactos(rutaExcel);

// Validación de rango para evitar errores de índice
if (filaInicio < 0 || filaInicio >= contactos.size()) {
vista.mostrarError("La fila de inicio no es válida.");
return;
}

vista.mostrarMensaje("🚀 Iniciando proceso desde fila " + (filaInicio + 1));

redService.alternarRed();
automation.iniciar(url); // ✅ AQUÍ SE INICIALIZA EL DRIVER
// AQUÍ SE INICIALIZA EL DRIVER
automation.iniciar(url);

for (int i = filaInicio; i < contactos.size(); i++) {
String numero = contactos.get(i);
Expand All @@ -69,9 +73,13 @@ public void ejecutar(int filaInicio) {
automation.ingresarNumero(numero);
ResultadoBusqueda resultado = automation.consultarResultado();

// Lógica de recuperación si se detecta un bloqueo (modal)
if (resultado.hayModal()) {
vista.mostrarMensaje("🚩 Modal detectado. Cambiando red...");
excelManager.escribirOperador(rutaExcel, i + 1, ResultadoBusqueda.noDetectado().getOperador()); // <--- esto se perdió
// Se guarda operador no detectado
excelManager.escribirOperador(rutaExcel, i + 1, ResultadoBusqueda.noDetectado().getOperador());

// Reinicio de sesión con cambio de IP
automation.cerrar();
redService.alternarRed();
automation.iniciar(url);
Expand All @@ -80,6 +88,7 @@ public void ejecutar(int filaInicio) {
continue;
}

// Registro del operador encontrado en el Excel
excelManager.escribirOperador(
rutaExcel,
i + 1,
Expand Down Expand Up @@ -109,8 +118,9 @@ public void reprocesarNoDetectados() {

vista.mostrarMensaje("🔁 Reprocesando " + filas.size() + " registros...");

// Preparación del entorno para el reintento
redService.alternarRed();
automation.iniciar(url); // ✅ AQUÍ TAMBIÉN
automation.iniciar(url);

List<String> contactos = excelManager.leerContactos(rutaExcel);

Expand All @@ -125,6 +135,7 @@ public void reprocesarNoDetectados() {
if (resultado.hayModal()) {
vista.mostrarMensaje("🚩 Modal detectado. Cambiando red...");
excelManager.escribirOperador(rutaExcel, fila, ResultadoBusqueda.noDetectado().getOperador());

automation.cerrar();
redService.alternarRed();
automation.iniciar(url);
Expand All @@ -147,4 +158,4 @@ public void reprocesarNoDetectados() {
automation.cerrar();
vista.mostrarMensaje("✅ Reprocesamiento completado");
}
}
}
21 changes: 20 additions & 1 deletion src/main/java/com/automatizacion/driver/WebDriverFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,33 @@
import org.openqa.selenium.chrome.*;
import io.github.bonigarcia.wdm.WebDriverManager;

/**
* Fábrica encargada de centralizar la creación y configuración
* de la instancia de WebDriver para Chrome.
* * @author SebasCodeDev
* @version 1.3.1
*/
public class WebDriverFactory {

/**
* Configura el driver, define opciones de inicio y navega a la URL base.
* @param url Dirección web que el navegador cargará al iniciar.
* @return Una instancia de WebDriver (ChromeDriver) lista para usar.
*/
public static WebDriver crear(String url) {
// Configura automáticamente el binario de ChromeDriver según la versión del navegador
WebDriverManager.chromedriver().setup();

// Configuración de parámetros de inicio para Chrome
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized", "--remote-allow-origins=*");

// Inicialización del driver con las opciones definidas
WebDriver driver = new ChromeDriver(options);

// Navegación inmediata a la URL proporcionada
driver.get(url);

return driver;
}
}
}
Loading