# Guía de Configuración para Scripts Backend

## Introducción

Esta guía explica cómo usar la función `load_configuration()` para cargar parámetros desde un archivo `script_config.json` en los scripts del backend.

## 1. Configuración del Script

Para que tus scripts puedan encontrar los módulos del proyecto, necesitas añadir el directorio raíz al path de Python.

### Path Setup e Importación

Coloca este código al inicio de tu script:

```python
import os
import sys

# Añadir el directorio raíz al sys.path
# El número de `os.path.dirname()` depende de la profundidad del script.
# Para /backend/script_groups/grupo/script.py, son 4.
script_root = os.path.dirname(
    os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
sys.path.append(script_root)

# Importar la función
from backend.script_utils import load_configuration
```

## 2. Cargar la Configuración

La función `load_configuration()` busca y carga un archivo llamado `script_config.json` que debe estar en el **mismo directorio** que el script que la ejecuta. Devuelve un diccionario con todo el contenido del JSON.

### Ejemplo de Uso

```python
def main():
    # Cargar configuraciones del archivo script_config.json
    configs = load_configuration()
    
    # Es buena práctica verificar si la configuración se cargó
    if not configs:
        print("Error: No se pudo cargar la configuración. Saliendo.")
        return
    
    # Acceder a los parámetros usando .get() para evitar errores
    working_directory = configs.get("working_directory", "")
    level1_config = configs.get("level1", {})
    level2_config = configs.get("level2", {})
    level3_config = configs.get("level3", {})
     
    # Ejemplo de uso de un parámetro específico con valor por defecto
    scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
    xref_output_dir = level2_config.get("xref_output_dir", "xref_output")
    
    print(f"Directorio de salida/resultados: {working_directory}")
    print(f"Directorio de salida SCL: {scl_output_dir}")

if __name__ == "__main__":
    main()
```

## 3. Archivo `script_config.json`

Este archivo contiene los parámetros de tu script. La estructura interna del JSON, como el uso de `"level1"`, `"level2"`, etc., es una convención para organizar los parámetros. `load_configuration()` simplemente lee el archivo y devuelve su contenido.

### Tipos de Configuración

El sistema distingue entre dos tipos principales de configuración:

1. **`working_directory`**: Es el directorio donde el script guardará sus archivos de salida y resultados. En la interfaz web aparece como "Directorio de Resultados".

2. **`level3`**: Contiene los datos de entrada que el script necesita para procesar. En la interfaz web aparece como "Datos de Ingreso". Estos pueden incluir rutas de archivos de entrada, parámetros específicos de procesamiento, etc.

**Ejemplo de `script_config.json`:**
```json
{
    "working_directory": "/ruta/al/directorio/de/salida/resultados",
    "level1": {
        "parametro_global_1": "valor1"
    },
    "level2": {
        "scl_output_dir": "scl_output",
        "xref_output_dir": "xref_output"
    },
    "level3": {
        "archivo_entrada": "datos.xlsx",
        "parametro_especifico_1": true
    }
}
```

## 4. Manejo de Errores

`load_configuration()` está diseñada para ser robusta:
- Si `script_config.json` no se encuentra, retorna un diccionario vacío `{}`.
- Si el JSON es inválido, imprime un error y también retorna `{}`.

**Siempre** comprueba si el diccionario devuelto está vacío para manejar estos casos de forma segura en tu script.

## 5. Jerarquía de Archivos de Configuración

El sistema utiliza un modelo de configuración en cascada para gestionar los parámetros de los scripts. Esta jerarquía permite establecer configuraciones a nivel global, de grupo y de trabajo. Antes de la ejecución, el launcher lee estos archivos, los combina y genera un único `script_config.json` en la carpeta del script. La función `load_configuration()` es la que finalmente lee este archivo consolidado.

A continuación se describe la finalidad y ubicación de cada archivo clave.

### Archivos de Valores (Parámetros)

Contienen los datos y variables que utilizará el script. La configuración se superpone en el siguiente orden: `Nivel 1 < Nivel 2 < Nivel 3`.

- **`data.json` (Nivel 1 - Global)**
  - **Ubicación:** `data/data.json`
  - **Utilidad:** Almacena variables globales disponibles para todos los scripts. Ideal para parámetros generales.
  - **Acceso:** Sus datos se cargan en la clave `"level1"` del diccionario `configs`.

- **`script_config.json` (Nivel 2 - Grupo)**
  - **Ubicación:** En la raíz de cada directorio de grupo (ej: `backend/script_groups/MiGrupo/script_config.json`).
  - **Utilidad:** Define parámetros compartidos por todos los scripts de un grupo.
  - **Acceso:** Sus datos se cargan en la clave `"level2"`.

- **`work_dir.json` (Nivel 3 - Datos de Ingreso)**
  - **Ubicación:** Dentro del directorio de trabajo que el script va a procesar.
  - **Utilidad:** Contiene los datos de entrada que el script necesita para procesar. Es el nivel más específico y sobrescribe los anteriores.
  - **Acceso:** Sus datos se cargan en la clave `"level3"`.

### Archivos de Esquema (Definiciones de Estructura)

No contienen valores, sino que describen la estructura y tipos de datos que los archivos de valores deben tener. Son usados por el launcher para validación y para generar interfaces de configuración.

- **`esquema_group.json`**
  - **Ubicación:** Raíz del directorio del grupo.
  - **Utilidad:** Define la estructura del `script_config.json` del grupo.

- **`esquema_work.json`**
  - **Ubicación:** Raíz del directorio del grupo.
  - **Utilidad:** Define la estructura del `work_dir.json`.

## 6. Documentación de Scripts para el Launcher

El sistema de launcher utiliza archivos JSON para mostrar información sobre los grupos de scripts y scripts individuales en la interfaz web.

### Archivo description.json (Descripción del Grupo)

Ubicación: En el directorio raíz del grupo de scripts.

```json
{
  "name": "Nombre del Grupo",
  "description": "Descripción del propósito y funcionalidad del grupo",
  "version": "1.0",
  "author": "Nombre del Autor"
}
```

### Archivo scripts_description.json (Descripción de Scripts)

Ubicación: En el directorio raíz del grupo de scripts.

```json
{
    "nombre_script.py": {
        "display_name": "Nombre para mostrar en la UI",
        "short_description": "Descripción breve del script",
        "long_description": "Descripción detallada con explicación completa de funcionalidad, pasos que ejecuta, y contexto de uso",
        "hidden": false
    },
    "script_interno.py": {
        "display_name": "Script Interno",
        "short_description": "Script de uso interno",
        "long_description": "",
        "hidden": true
    }
}
```

### Propiedades Importantes

- **hidden**: `true` oculta el script del launcher (útil para scripts auxiliares)
- **display_name**: Nombre amigable que aparece en la interfaz
- **short_description**: Se muestra en la lista de scripts
- **long_description**: Se muestra al expandir detalles del script

### Ejemplo Práctico

Para un grupo "XML Parser to SCL":

**description.json:**
```json
{
  "name": "Siemens-Tia : 03 : Procesador de XML LAD-SCL-AWL",
  "description": "Scripts que procesan archivos XML exportados de TIA, convirtiendo LAD a SCL",
  "version": "1.0",
  "author": "Miguel"
}
```

**scripts_description.json:**
```json
{
    "x0_main.py": {
        "display_name": "1: Procesar Exportación XML completa",
        "short_description": "Conversor principal de LAD/FUP XML a SCL",
        "long_description": "Script orquestador que procesa todos los archivos XML...",
        "hidden": false
    },
    "x1_to_json.py": {
        "display_name": "x1_to_json",
        "short_description": "Converter XML interno",
        "long_description": "",
        "hidden": true
    }
}
```

## 7. Requisitos de Codificación de Salida (stdout)

Toda la salida estándar (`stdout`) generada por los scripts (por ejemplo, mediante la función `print()`) es capturada en tiempo real y mostrada en el panel de logs del frontend.

Para garantizar que el texto se muestre correctamente y evitar caracteres corruptos (mojibake), **la salida de los scripts debe tener codificación UTF-8**.

### Configuración Automática

El sistema está diseñado para facilitar esto. Al ejecutar un script, el entorno se configura automáticamente para que la salida de Python sea UTF-8. Específicamente, se establece la variable de entorno `PYTHONIOENCODING=utf-8`.

Gracias a esto, en la mayoría de los casos, **no necesitas hacer nada especial**. Simplemente usa `print()` y la codificación será la correcta.

### Casos Especiales y Solución de Problemas

Pueden surgir problemas si tu script lee datos de fuentes externas (como archivos) que tienen una codificación diferente. Si imprimes contenido directamente sin decodificarlo primero, podrías enviar bytes no válidos a la salida.

La regla es: **decodifica los datos de entrada a un string de Python lo antes posible, y deja que `print()` se encargue de la codificación de salida.**

**Ejemplo de cómo manejar un archivo con codificación `latin-1`:**

```python
# INCORRECTO: Si el archivo no es UTF-8, esto puede enviar bytes inválidos.
# El launcher intentará decodificarlos como UTF-8 y podría fallar o mostrar basura.
try:
    with open('mi_archivo_legacy.txt', 'rb') as f:
        print(f.read())
except Exception as e:
    print(f"Esto probablemente falle o muestre texto corrupto: {e}")


# CORRECTO: Leer el archivo especificando su codificación para decodificarlo a un string.
# Una vez que es un string de Python, `print` lo manejará correctamente.
try:
    with open('mi_archivo_legacy.txt', 'r', encoding='latin-1') as f:
        contenido = f.read()
        # Ahora `contenido` es un string de Python.
        # print() lo codificará a UTF-8 automáticamente gracias a la configuración del entorno.
        print(contenido)
except FileNotFoundError:
    print("El archivo 'mi_archivo_legacy.txt' no fue encontrado para el ejemplo.")
except Exception as e:
    print(f"Error al procesar el archivo: {e}")
```

## 8. Uso de Servicios Compartidos

El proyecto ofrece una serie de servicios reutilizables en el directorio `services/` para tareas comunes como la manipulación de archivos Excel, detección de idioma o traducción.

Para utilizar estos servicios en tu script, asegúrate de que el directorio raíz del proyecto esté en el `sys.path`, como se explica en la sección 1 de esta guía.

### 8.1 Servicio de Excel (`ExcelService`)

El `ExcelService` (`services/excel/excel_service.py`) facilita la lectura y escritura de archivos Excel, con manejo de reintentos (por si el archivo está abierto) y opciones de formato.

**Ejemplo de importación y uso:**

```python
# Asegúrate de tener el path raíz configurado
# ... (código de configuración de sys.path)

from services.excel.excel_service import ExcelService

def main():
    excel_service = ExcelService()

    # Leer un archivo Excel
    try:
        df = excel_service.read_excel("mi_archivo_de_entrada.xlsx")
        print("Datos cargados exitosamente.")
        
        # ... procesar el DataFrame ...
        
        # Guardar el DataFrame con formato personalizado
        format_options = {
            'freeze_row': 2,
            'header_color': 'E6E6E6'
        }
        
        excel_service.save_excel(
            df,
            "mi_archivo_de_salida.xlsx",
            sheet_name="Resultados",
            format_options=format_options
        )
        print("Archivo guardado con éxito.")
        
    except Exception as e:
        print(f"Ocurrió un error al manejar el archivo Excel: {e}")

if __name__ == "__main__":
    main()
```

### 8.2 Servicios de Lenguaje

Los servicios de lenguaje (`services/language/`) permiten detectar el idioma de un texto.

**Ejemplo de importación y uso:**

```python
# Asegúrate de tener el path raíz configurado
# ... (código de configuración de sys.path)

from services.language.language_factory import LanguageFactory
from services.language.language_utils import LanguageUtils

def main():
    # Crear el servicio de detección de idioma
    allowed_languages = LanguageUtils.get_available_languages()
    detector = LanguageFactory.create_service("langid", allowed_languages=allowed_languages)

    # Detectar idioma de un texto
    text = "Este es un texto de ejemplo en español."
    lang, confidence = detector.detect_language(text)
    
    print(f"Texto: '{text}'")
    print(f"Idioma detectado: {LanguageUtils.get_language_name(lang)} (código: {lang})")
    print(f"Confianza: {confidence:.2f}")

if __name__ == "__main__":
    main()
```

### 8.3 Servicios de LLM (Modelos de Lenguaje Grandes)

El proyecto integra una fábrica de servicios (`LLMFactory`) para interactuar con diferentes Modelos de Lenguaje Grandes (LLMs). Esto permite a los scripts aprovechar la IA generativa para tareas como el análisis de código, la generación de descripciones semánticas, etc.

#### 8.3.1 Configuración de API Keys

La mayoría de los servicios de LLM requieren una clave de API para funcionar. El sistema gestiona esto de forma centralizada a través de variables de entorno.

1.  **Crear el archivo `.env`**: En el **directorio raíz del proyecto**, crea un archivo llamado `.env` (si aún no existe).

2.  **Añadir las API Keys**: Abre el archivo `.env` y añade las claves para los servicios que planeas utilizar. El sistema cargará estas variables automáticamente al inicio.

    ```env
    # Ejemplo de contenido para el archivo .env
    # (Solo necesitas añadir las claves de los servicios que vayas a usar)

    OPENAI_API_KEY="sk-..."
    GROQ_API_KEY="gsk_..."
    CLAUDE_API_KEY="sk-ant-..."
    GEMINI_API_KEY="AIzaSy..."
    GROK_API_KEY="TU_API_KEY_DE_GROK"
    ```

    **Nota**: El servicio `ollama` se ejecuta localmente y no requiere una clave de API.

#### 8.3.2 Ejemplo de importación y uso

El siguiente ejemplo muestra cómo un script puede cargar su configuración, inicializar un servicio de LLM y usarlo para generar texto. Este patrón es similar al utilizado en `x3_generate_semantic_descriptions.py`.

```python
# Asegúrate de tener el path raíz configurado
# ... (código de configuración de sys.path)

from services.llm.llm_factory import LLMFactory
from backend.script_utils import load_configuration

def main():
    # Cargar la configuración del script, que puede incluir qué LLM usar
    configs = load_configuration()
    llm_configs = configs.get("llm", {})
    
    # Obtener el tipo de servicio y otros parámetros del config
    # Por defecto, usamos 'groq' si no se especifica
    service_type = llm_configs.get("service", "groq") 
    
    print(f"🤖 Inicializando servicio LLM: {service_type}")

    # Crear una instancia del servicio usando la fábrica
    # La fábrica se encarga de pasar las API keys desde las variables de entorno
    llm_service = LLMFactory.create_service(service_type, **llm_configs)

    if not llm_service:
        print(f"❌ Error: No se pudo crear el servicio LLM '{service_type}'. Abortando.")
        return

    # Usar el servicio para generar texto
    try:
        prompt = "Explica la computación cuántica en una sola frase."
        print(f"Enviando prompt: '{prompt}'")
        
        description = llm_service.generate_text(prompt)
        
        print("\nRespuesta del LLM:")
        print(description)
        
    except Exception as e:
        print(f"Ocurrió un error al contactar al servicio LLM: {e}")

if __name__ == "__main__":
    main()
```

#### 8.3.3 Servicios Disponibles

La `LLMFactory` soporta los siguientes tipos de servicio (`service_type`):
- `openai`
- `groq`
- `claude`
- `gemini`
- `grok`
- `ollama` (para ejecución local)