From 9b253c8c331a33ce12e4ec63555cf0735d7f23e0 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 30 Aug 2026 05:48:59 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20a=C3=B1adir=20rutina=20desatendida=20y?= =?UTF-8?q?=20traducir=20scripts=20de=20promoci=C3=B3n=20al=20espa=C3=B1ol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: LVT-ENG <214667862+LVT-ENG@users.noreply.github.com> --- .gitignore | 1 + automatizacion_promociones.md | 72 +++++++++++++++++++++++ scripts/auto_deploy_promotions.sh | 20 +++---- scripts/deploy_promotions.py | 32 +++++----- scripts/rutina_desatendida_promociones.sh | 15 +++++ 5 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 automatizacion_promociones.md create mode 100755 scripts/rutina_desatendida_promociones.sh diff --git a/.gitignore b/.gitignore index 3a7a575390..7b2cfc3fa2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ Brewfile.lock.json .DS_Store examples/*.mp3 +promotions.log diff --git a/automatizacion_promociones.md b/automatizacion_promociones.md new file mode 100644 index 0000000000..507d48c9bb --- /dev/null +++ b/automatizacion_promociones.md @@ -0,0 +1,72 @@ +# Automatización de Publicación de Promociones para tryonyou.pro + +Esta guía explica el funcionamiento de los scripts diseñados para validar, monitorizar y desplegar automáticamente las promociones en el entorno de producción. + +## Componentes del sistema + +El sistema se compone de varios scripts, todos ellos preparados para operar en español: + +1. **`deploy_promotions.py`**: + - Toma el archivo JSON con el contenido de la promoción. + - Valida su estructura asegurando que los campos obligatorios existen. + - Ejecuta la petición HTTP para enviar los datos a la API en producción. + - Soporta un modo `--dry-run` para validar sin realizar la petición de red. + +2. **`automate_promotions.sh`**: + - Itera a través del directorio `promotions/`. + - Llama a `deploy_promotions.py` para cada archivo JSON. + - Mueve los archivos procesados exitosamente a `promotions_processed/` o a `promotions_failed/` en caso de error. + +3. **`watch_promotions.sh`**: + - Utiliza `inotifywait` para monitorizar continuamente el directorio `promotions/`. + - Cuando se añade o modifica un archivo JSON de promoción, dispara automáticamente `automate_promotions.sh`. + +4. **`rutina_desatendida_promociones.sh`**: + - Es el punto de entrada principal para la automatización en background. + - Lanza `watch_promotions.sh` en modo desacoplado (utilizando `nohup`). + - Redirige todas las salidas (logs y errores) al archivo central `promotions.log`. + +## Cómo usar el sistema + +### Despliegue manual e interactivo + +Si deseas probar o desplegar un único archivo promocional: + +```bash +./scripts/auto_deploy_promotions.sh +``` + +Para simularlo sin afectar a la API real, asegúrate de que no esté definida la variable `TRYONYOU_API_KEY` o utiliza explícitamente `--dry-run` donde se soporte. + +### Configuración del Despliegue Desatendido (Background) + +Para que el servidor esté permanentemente escuchando y desplegando nuevos archivos que se depositen en la carpeta `promotions/`, ejecuta: + +```bash +./scripts/rutina_desatendida_promociones.sh +``` + +Esto iniciará el proceso en background. El sistema te mostrará un número de proceso (PID). +Puedes monitorizar la actividad revisando el archivo de log: + +```bash +tail -f promotions.log +``` + +### Configuración con Cron (Alternativa) + +Si prefieres ejecutar el despliegue de forma periódica en lugar de reaccionar a eventos (inotify), puedes añadir una tarea cron para ejecutar el procesamiento en bloque: + +1. Abre la configuración de cron: + ```bash + crontab -e + ``` +2. Añade la siguiente línea para que se ejecute cada hora (por ejemplo): + ```cron + 0 * * * * /ruta/absoluta/al/proyecto/scripts/automate_promotions.sh >> /ruta/absoluta/al/proyecto/promotions.log 2>&1 + ``` +*(Nota: Si usas cron, no es necesario ejecutar la rutina desatendida con `watch_promotions.sh`, ya que ambos cumplirían propósitos similares.)* + +## Variables de Entorno + +- **`TRYONYOU_API_KEY`**: Es requerida para realizar peticiones reales a la API de producción. Si no está configurada, los scripts de despliegue operarán o advertirán en modo *dry-run* o simulación. diff --git a/scripts/auto_deploy_promotions.sh b/scripts/auto_deploy_promotions.sh index f990cbc09f..6d06237e64 100755 --- a/scripts/auto_deploy_promotions.sh +++ b/scripts/auto_deploy_promotions.sh @@ -1,32 +1,32 @@ #!/bin/bash -# Default to promotion_example.json if no argument is provided +# Por defecto a promotion_example.json si no se proporciona argumento PROMO_FILE="${1:-promotion_example.json}" if [ ! -f "$PROMO_FILE" ]; then - echo "Error: Promotional content file '$PROMO_FILE' not found." + echo "Error: No se encontró el archivo de contenido promocional '$PROMO_FILE'." exit 1 fi -# Check if API key is provided for unattended deployment +# Comprobar si se proporciona clave de API para despliegue desatendido if [ -z "$TRYONYOU_API_KEY" ]; then - echo "Warning: TRYONYOU_API_KEY environment variable is not set." - echo "Running deployment in dry-run mode for validation..." + echo "Advertencia: La variable de entorno TRYONYOU_API_KEY no está configurada." + echo "Ejecutando despliegue en modo dry-run para validación..." DRY_RUN="--dry-run" else - echo "TRYONYOU_API_KEY is set. Proceeding with production deployment..." + echo "TRYONYOU_API_KEY está configurada. Procediendo con el despliegue en producción..." DRY_RUN="" fi -# Execute the deployment script -echo "Executing deployment..." +# Ejecutar el script de despliegue +echo "Ejecutando despliegue..." python3 scripts/deploy_promotions.py "$PROMO_FILE" $DRY_RUN EXIT_CODE=$? if [ $EXIT_CODE -eq 0 ]; then - echo "Routine completed successfully." + echo "Rutina completada exitosamente." else - echo "Routine failed with exit code $EXIT_CODE." + echo "La rutina falló con código de salida $EXIT_CODE." exit $EXIT_CODE fi diff --git a/scripts/deploy_promotions.py b/scripts/deploy_promotions.py index 796f67651c..a73f1b2250 100755 --- a/scripts/deploy_promotions.py +++ b/scripts/deploy_promotions.py @@ -12,7 +12,7 @@ def validate_promotion(data: Dict[str, Any]) -> bool: required_fields = ["title", "description", "discount_code", "valid_until"] for field in required_fields: if field not in data: - raise ValueError(f"Missing required field: {field}") + raise ValueError(f"Falta un campo requerido: {field}") return True def deploy_promotion(data: Dict[str, Any], api_url: str, api_key: str) -> bool: @@ -26,52 +26,52 @@ def deploy_promotion(data: Dict[str, Any], api_url: str, api_key: str) -> bool: title = data.get("title") if not isinstance(title, str): title = str(title) - print(f"Deploying promotion '{title}' to {api_url}...") + print(f"Desplegando promoción '{title}' en {api_url}...") response = urllib.request.urlopen(req) response_data = response.read() - print(f"Deployment successful: {response_data}") + print(f"Despliegue exitoso: {response_data}") return True except urllib.error.URLError as e: - print(f"Failed to deploy: {e}") + print(f"Error al desplegar: {e}") return False def main() -> None: - parser = argparse.ArgumentParser(description="Deploy promotional content for tryonyou.pro") - parser.add_argument("file", help="Path to the JSON file containing promotional content") - parser.add_argument("--api-url", default="https://api.tryonyou.pro/v1/promotions", help="API Endpoint URL") - parser.add_argument("--dry-run", action="store_true", help="Simulate deployment without making the HTTP request") + parser = argparse.ArgumentParser(description="Desplegar contenido promocional para tryonyou.pro") + parser.add_argument("file", help="Ruta al archivo JSON que contiene el contenido promocional") + parser.add_argument("--api-url", default="https://api.tryonyou.pro/v1/promotions", help="URL del endpoint de la API") + parser.add_argument("--dry-run", action="store_true", help="Simular despliegue sin realizar la petición HTTP") args = parser.parse_args() if not os.path.exists(args.file): - print(f"Error: File {args.file} not found.") + print(f"Error: El archivo {args.file} no existe.") sys.exit(1) try: with open(args.file, 'r', encoding='utf-8') as f: loaded_data = json.load(f) if not isinstance(loaded_data, dict): - raise ValueError("JSON file must contain a dictionary") + raise ValueError("El archivo JSON debe contener un diccionario") data = cast(Dict[str, Any], loaded_data) except (json.JSONDecodeError, ValueError) as e: - print(f"Error parsing JSON: {e}") + print(f"Error al procesar JSON: {e}") sys.exit(1) try: validate_promotion(data) - print("Validation passed.") + print("Validación exitosa.") except ValueError as e: - print(f"Validation failed: {e}") + print(f"Fallo en la validación: {e}") sys.exit(1) api_key = os.environ.get("TRYONYOU_API_KEY") if not api_key and not args.dry_run: - print("Error: TRYONYOU_API_KEY environment variable is missing.") + print("Error: La variable de entorno TRYONYOU_API_KEY no está configurada.") sys.exit(1) if args.dry_run: - print("Simulating deployment (dry-run mode)...") - print("Deployment successful.") + print("Simulando despliegue (modo dry-run)...") + print("Despliegue simulado exitoso.") else: if api_key is None: api_key = "" diff --git a/scripts/rutina_desatendida_promociones.sh b/scripts/rutina_desatendida_promociones.sh new file mode 100755 index 0000000000..943a29635e --- /dev/null +++ b/scripts/rutina_desatendida_promociones.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# scripts/rutina_desatendida_promociones.sh +# Inicia la monitorización y despliegue automático de promociones en background. + +BASE_DIR="$(pwd)" +LOG_FILE="${BASE_DIR}/promotions.log" + +echo "Iniciando rutina desatendida de promociones..." +echo "Los registros se guardarán en: $LOG_FILE" + +# Ejecutar el script watch_promotions.sh en background y redirigir la salida al log +nohup "${BASE_DIR}/scripts/watch_promotions.sh" "$@" > "$LOG_FILE" 2>&1 & +PID=$! + +echo "Rutina ejecutándose en background con PID: $PID"