# -*- 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}"