Migra el servicio a arquitectura Docker persistente y mueve credenciales HANA a .env
- Reemplaza config_loader/config_model/hana_client/logger_utilities/outlook_utilities por core.py (pipeline funcional), hana_manager.py, logger_manager.py, scheduler.py y descargador_sovos.py (Playwright/Firefox) según la plantilla de servicios. - main.py pasa a loop continuo dirigido por scheduler.json, con manejo de SIGINT/SIGTERM. - Agrega Dockerfile, docker-compose.yml (restart unless-stopped) y certs/ para despliegue. - Agrega smb_transfer.py para la transferencia a carpeta compartida. - Saca las credenciales de HANA de config.json y las mueve a .env (HANA_HOST/PORT/USER/PASSWORD).
This commit is contained in:
1 parent
1503d6a2ec
commit
277d07dfeb
22 files changed
+1081
-750
No files matched your search
@@ -0,0 +1,116 @@
|
||||
import logging
|
||||
import contextvars
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
LOG_DIR = Path(__file__).parent / "logs"
|
||||
LOG_FILE = LOG_DIR / "descarga-cpe.log"
|
||||
_LOG_CONFIGURED = False
|
||||
|
||||
_stack: contextvars.ContextVar[list] = contextvars.ContextVar("log_stack", default=[])
|
||||
# ============================================================================
|
||||
|
||||
class TreeFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
return f"{self.formatTime(record)} ─── {record.getMessage()}"
|
||||
|
||||
def formatTime(self, record, datefmt=None):
|
||||
import datetime
|
||||
return datetime.datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# ============================================================================
|
||||
def _build_prefix(stack, is_last):
|
||||
if not stack:
|
||||
return ""
|
||||
parts = []
|
||||
for sib_is_last in stack[:-1]:
|
||||
parts.append(" " if sib_is_last else "│ ")
|
||||
parts.append("└── " if is_last else "├── ")
|
||||
return "".join(parts)
|
||||
|
||||
# ============================================================================
|
||||
class _TaskContext:
|
||||
def __init__(self, logger, msg, emoji, is_last):
|
||||
self._logger = logger
|
||||
self._msg = msg
|
||||
self._emoji = emoji
|
||||
self._is_last = is_last
|
||||
|
||||
def __enter__(self):
|
||||
stack = _stack.get()
|
||||
prefix = _build_prefix(stack, self._is_last)
|
||||
emoji_str = f"{self._emoji} " if self._emoji else ""
|
||||
self._logger._logger.info(f"{prefix}{emoji_str}{self._msg}")
|
||||
_stack.set(stack + [self._is_last])
|
||||
return TaskLogger(self._logger._logger)
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
_stack.set(_stack.get()[:-1])
|
||||
|
||||
# ============================================================================
|
||||
class TaskLogger:
|
||||
def __init__(self, logger):
|
||||
self._logger = logger
|
||||
|
||||
@contextmanager
|
||||
def task(self, msg, emoji=""):
|
||||
with _TaskContext(self, msg, emoji, is_last=False):
|
||||
yield
|
||||
|
||||
@contextmanager
|
||||
def last_task(self, msg, emoji=""):
|
||||
with _TaskContext(self, msg, emoji, is_last=True):
|
||||
yield
|
||||
|
||||
def info(self, msg, *args, _last=False, **kwargs):
|
||||
self._log(logging.INFO, msg, args, kwargs, _last)
|
||||
|
||||
def warning(self, msg, *args, _last=False, **kwargs):
|
||||
self._log(logging.WARNING, msg, args, kwargs, _last)
|
||||
|
||||
def error(self, msg, *args, _last=False, **kwargs):
|
||||
self._log(logging.ERROR, msg, args, kwargs, _last)
|
||||
|
||||
def _log(self, level, msg, args, kwargs, is_last):
|
||||
stack = _stack.get()
|
||||
prefix = _build_prefix(stack, is_last)
|
||||
self._logger.log(level, f"{prefix}{msg}", *args, **kwargs)
|
||||
|
||||
# ============================================================================
|
||||
def setup_logging(level=None, console=True):
|
||||
global _LOG_CONFIGURED
|
||||
if _LOG_CONFIGURED:
|
||||
return
|
||||
|
||||
if level is None:
|
||||
level_name = os.getenv("LOG_LEVEL", "INFO").upper()
|
||||
level = getattr(logging, level_name, logging.INFO)
|
||||
|
||||
LOG_DIR.mkdir(exist_ok=True)
|
||||
|
||||
formatter = TreeFormatter()
|
||||
|
||||
file_handler = TimedRotatingFileHandler(
|
||||
LOG_FILE,
|
||||
when="midnight",
|
||||
interval=1,
|
||||
backupCount=30,
|
||||
encoding="utf-8",
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
handlers = [file_handler]
|
||||
if console:
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
handlers.append(console_handler)
|
||||
|
||||
logging.basicConfig(level=level, handlers=handlers, force=True)
|
||||
_LOG_CONFIGURED = True
|
||||
|
||||
# ============================================================================
|
||||
def get_logger(name="descarga-cpe"):
|
||||
setup_logging()
|
||||
return TaskLogger(logging.getLogger(name))
|
||||
Reference in new issue
Block a user