410 lines
15 KiB
Markdown
410 lines
15 KiB
Markdown
# 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 trabajo: {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.
|
|
|
|
**Ejemplo de `script_config.json`:**
|
|
```json
|
|
{
|
|
"working_directory": "/ruta/al/directorio/de/trabajo",
|
|
"level1": {
|
|
"parametro_global_1": "valor1"
|
|
},
|
|
"level2": {
|
|
"scl_output_dir": "scl_output",
|
|
"xref_output_dir": "xref_output"
|
|
},
|
|
"level3": {
|
|
"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 - Directorio de Trabajo)**
|
|
- **Ubicación:** Dentro del directorio de trabajo que el script va a procesar.
|
|
- **Utilidad:** Contiene parámetros para una ejecución específica. 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) |