03-oct: superguardado automático

This commit is contained in:
minguezsanzjuanjose
2026-10-03 17:13:39 +02:00
parent f10ce5bef4
commit fdd4ea877a
31 changed files with 2229 additions and 4 deletions

View File

@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
"""jira.py — Fuente Jira del monitor (RF-3, RF-4, RF-16).
Recoge del proyecto DS (Service Desk):
- incidencias NUEVAS creadas desde el ciclo anterior,
- tickets SIN ASIGNAR no cerrados ni resueltos.
SOLO LECTURA (Regla 15): reutiliza el `JiraClient` de gestion_jira, que únicamente
expone consultas GET/POST de busqueda; nunca crea, modifica ni comenta.
"""
from __future__ import annotations
import sys
from datetime import datetime
from .. import config # noqa: F401 (asegura sys.path con scripts/)
import infra_paths
# Estados que NO cuentan como "sin asignar pendiente de atencion".
_ESTADOS_EXCLUIDOS = (
"'En Curso', 'In Progress', 'En desarrollo', 'Pasado a Programación Área', "
"'Cerrado', 'Resuelto', 'Cancelado', 'Closed', 'Resolved', 'Canceled'"
)
def _gestion_jira_dir(cfg: dict | None = None):
"""Directorio del repo gestion_jira (donde vive jira_api.py)."""
cfg = cfg or config.cargar()
override = cfg.get("jira", {}).get("gestion_jira_dir")
if override:
return override
return str(infra_paths.INFRA_ROOT.parent / "gestion_jira")
def construir_cliente(cfg: dict | None = None):
"""Devuelve un JiraClient (solo lectura)."""
directorio = _gestion_jira_dir(cfg)
if directorio not in sys.path:
sys.path.insert(0, directorio)
from jira_api import JiraClient
return JiraClient()
def _iso_para_jql(desde) -> str:
"""Normaliza una fecha a 'YYYY-MM-DD HH:MM' para el JQL."""
if isinstance(desde, datetime):
return desde.strftime("%Y-%m-%d %H:%M")
return str(desde)
def _normalizar(issue: dict) -> dict:
"""Convierte una issue de Jira en un registro legible."""
key = issue.get("key", "")
f = issue.get("fields", {}) or {}
return {
"id": key,
"key": key,
"resumen": f.get("summary", ""),
"estado": (f.get("status") or {}).get("name", ""),
"creado": (f.get("created", "") or "")[:10],
"prioridad": (f.get("priority") or {}).get("name", ""),
"url": f"https://prolongo.atlassian.net/browse/{key}",
}
def ds_nuevos(cliente, desde) -> list[dict]:
"""Incidencias DS creadas desde `desde` (inclusive)."""
jql = f'project = DS AND created >= "{_iso_para_jql(desde)}" ORDER BY created DESC'
res = cliente.search(jql, max_results=100)
return [_normalizar(i) for i in res.get("issues", [])]
def ds_sin_asignar(cliente) -> list[dict]:
"""Tickets DS sin asignar, no cerrados ni resueltos."""
jql = (
"project = DS AND assignee = EMPTY "
f"AND status not in ({_ESTADOS_EXCLUIDOS}) "
"AND statusCategory != Done "
"ORDER BY created DESC"
)
res = cliente.search(jql, max_results=100)
return [_normalizar(i) for i in res.get("issues", [])]
def render_jira(m: dict) -> str:
"""Render Markdown de un ticket para el fichero del dia."""
key = m.get("key", "?")
url = m.get("url", "")
estado = m.get("estado", "")
resumen = m.get("resumen", "")
return f"- **[{key}]({url})** ({estado}) — {resumen}"