From 547c300015f491d5eeaa2894852dceefc8125d7b Mon Sep 17 00:00:00 2001 From: Johann Date: Thu, 6 Aug 2026 01:27:43 -0600 Subject: [PATCH] =?UTF-8?q?Corte=20de=20horas=20al=206-ago=20(53.83=20h):?= =?UTF-8?q?=20sesi=C3=B3n=20nocturna=20de=20la=20lectura=20Jira=20(1.25=20?= =?UTF-8?q?h=20reales=20=E2=80=94=20sondeo=20customfields,=20bandeja+detal?= =?UTF-8?q?le=20con=20FAC=20en=20vivo,=20adjuntos,=20shell=20del=20prototi?= =?UTF-8?q?po);=20Excel=20de=20avance=20regenerado=20desde=20script.=20'Cl?= =?UTF-8?q?iente=20de=20la=20API=20de=20Jira'=20queda=20al=20100%;=20ajust?= =?UTF-8?q?e=20Jira=205.50=20de=207-10=20h=20aprobadas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planeacion/Avance-Balam-2026-08-06.xlsx | Bin 0 -> 10292 bytes planeacion/Seguimiento-horas.csv | 3 + planeacion/generar_avance_2026_08_06.py | 192 ++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 planeacion/Avance-Balam-2026-08-06.xlsx create mode 100644 planeacion/generar_avance_2026_08_06.py diff --git a/planeacion/Avance-Balam-2026-08-06.xlsx b/planeacion/Avance-Balam-2026-08-06.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..18e184c4c5fc57c08d49cb07afd25048c6c22b71 GIT binary patch literal 10292 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 Ak^lez literal 0 HcmV?d00001 diff --git a/planeacion/Seguimiento-horas.csv b/planeacion/Seguimiento-horas.csv index 9a6e32d..6148c77 100644 --- a/planeacion/Seguimiento-horas.csv +++ b/planeacion/Seguimiento-horas.csv @@ -43,3 +43,6 @@ fecha,semana,etapa,actividad_id,actividad,horas,estado_hora,fuente_evidencia,fac 2026-08-01,2026-W31,1,E1-01,"B5 scheduler + B6 verificación RLS (rol balam_app) + B8 suite de integración (25 pruebas) + B9 seeder demo",1.50,confirmada,bitacora/REGISTRO.md#61,si,"Sesión asistida por IA; tiempo real de sesión" 2026-08-01,2026-W31,2,E2-01,"Frontend de lectura E2: login JWT, shell de marca, cartera por moneda, listado con filtros, detalle+PDF, clientes+CSV",1.00,confirmada,bitacora/REGISTRO.md#61,si,"Sesión asistida por IA; tiempo real de sesión" 2026-08-01,2026-W31,2,AJ-01,Cliente de lectura de la API de Jira (Balam.Integrations.Jira) con 10 pruebas y script de sondeo,0.50,confirmada,bitacora/REGISTRO.md#61,si,"Sesión asistida por IA; incluye prueba local nocturna" +2026-08-06,2026-W32,2,AJ-01,"Sondeo de customfields en FAC (IDs capturados y documentados), token a user-secrets y endpoints de lectura /api/jira/solicitudes (+converter de fechas de Jira)",0.50,confirmada,planeacion/Investigacion-API-Jira.md,si,"Sesión asistida por IA; tiempo real de sesión; commits 4ce5640 (privado) y e2c2d18 (cliente)" +2026-08-06,2026-W32,2,AJ-01,"Bandeja de solicitudes y detalle de ticket fieles al prototipo leyendo FAC en vivo (estatus reales, solo lectura) + adjuntos descargables vía proxy",0.50,confirmada,bitacora/PENDIENTES.md,si,"Sesión asistida por IA; tiempo real de sesión; commits e2c2d18 y 76ad7b1 (cliente)" +2026-08-06,2026-W32,2,E2-01,"Shell con el chrome del prototipo aplicado a toda la app (sidebar con grupos y logo oficial, breadcrumb, chips de sync y módulos próximamente)",0.25,confirmada,prototipo/Prototipo-Etapa0.html,si,"Sesión asistida por IA; tiempo real de sesión; commit e2c2d18 (cliente)" diff --git a/planeacion/generar_avance_2026_08_06.py b/planeacion/generar_avance_2026_08_06.py new file mode 100644 index 0000000..fda1c6a --- /dev/null +++ b/planeacion/generar_avance_2026_08_06.py @@ -0,0 +1,192 @@ +"""Genera Avance-Balam-2026-08-06.xlsx a partir del corte del 1-ago. + +Corte del jueves 6-ago (sesión nocturna, previa a la demo del vie 7-ago): la +LECTURA de la bandeja FAC quedó completa y en la plataforma — sondeo de +customfields (IDs capturados), endpoints /api/jira/solicitudes, bandeja de +solicitudes y detalle fieles al prototipo con datos reales (solo lectura), +adjuntos descargables vía proxy, y el shell con el chrome del prototipo +aplicado a toda la app. Disparador y trazabilidad siguen bloqueados por las +definiciones de negocio de Balam. + +Las horas se leen de `Seguimiento-horas.csv` (fuente de la verdad, corte +53.83 h); el script aborta si el reparto por fila no cuadra con el CSV. +""" +import csv +import os +from collections import defaultdict + +import openpyxl +from openpyxl.styles import Alignment, Font, PatternFill + +BASE = os.path.dirname(os.path.abspath(__file__)) +SRC = os.path.join(BASE, "Avance-Balam-2026-08-01.xlsx") +DST = os.path.join(BASE, "Avance-Balam-2026-08-06.xlsx") +CSV_HORAS = os.path.join(BASE, "Seguimiento-horas.csv") + +CAFE, GRIS = "331F0E", "595959" +V_FILL, V_FONT = "C6EFCE", "006100" +A_FILL, A_FONT = "FFEB9C", "9C6500" +R_FILL, R_FONT = "FFC7CE", "9C0006" +G_FILL, G_FONT = "F2F2F2", "595959" + +C_PCT, C_EST, C_TRAB, C_STAT = 4, 5, 6, 7 + +# --- CSV: totales por etapa y por actividad ----------------------------------- +por_act, por_etapa = defaultdict(float), defaultdict(float) +with open(CSV_HORAS, encoding="utf-8", newline="") as fh: + for r in csv.DictReader(fh): + if not (r.get("fecha") or "").strip(): + continue + por_act[r["actividad_id"]] += float(r["horas"]) + por_etapa[r["etapa"]] += float(r["horas"]) + +E0, E1 = por_etapa["0"], por_etapa["1"] +AJ = por_act["AJ-01"] # ajuste Jira→BIND (vive dentro de la etapa 2 del CSV) +E2 = por_etapa["2"] - AJ # Etapa 2 propiamente (frontend) +TOTAL = E0 + E1 + E2 + AJ +assert abs(TOTAL - sum(por_etapa.values())) < 0.01 +PCT_GLOBAL = round(TOTAL / ((119 + 146) / 2), 2) + +# --- Reparto por fila del delta 1-ago→6-ago (1.25 h nuevas) -------------------- +# Trazabilidad: +# AJ-01 +1.00 (sondeo de customfields + endpoints + bandeja/detalle + adjuntos) +# → fila "Cliente de la API de Jira" (su entregable es la lectura de FAC) +# E2-01 +0.25 (shell con el chrome del prototipo) → fila "Layout base" +DELTAS_TRAB = { + "Cliente de la API de Jira": 1.00, + "Layout base": 0.25, +} +assert abs(sum(DELTAS_TRAB.values()) - 1.25) < 0.01, "El reparto no suma las 1.25 h nuevas" + +ENCABEZADOS = { + "Integracion de plataformas Balam": (TOTAL, PCT_GLOBAL), + "Etapa 0": (E0, 1.0), + "Etapa 1": (E1, 0.875), + "Etapa 2": (E2, 0.50), + "» Ajuste dentro de la Etapa 2": (AJ, 0.55), + "Etapa 3": (None, 0), +} + +# (pct, estatus, fill, font, bold) — solo lo que cambió respecto al 1-ago. +FILAS = { + "Cliente de la API de Jira": (1.0, "✅ Completada (6-ago): bandeja de solicitudes y detalle en la plataforma " + "leyendo FAC en vivo (solo lectura) · customfields mapeados · adjuntos vía proxy", + V_FILL, V_FONT, False), + "Layout base": (1.0, "✅ Completada · chrome del prototipo aplicado a toda la app (6-ago)", V_FILL, V_FONT, False), +} + + +def pinta(celda, fill, font_color, bold=False, size=9, align="left"): + celda.fill = PatternFill("solid", fgColor=fill) + celda.font = Font(color=font_color, bold=bold, size=size) + celda.alignment = Alignment(horizontal=align, vertical="center", wrap_text=True) + + +wb = openpyxl.load_workbook(SRC) +ws = wb.active + +ws["A2"] = ("Plan y avance al 6-ago-2026 · horas trabajadas al corte del jueves 6-ago · " + "lectura de la bandeja Jira (FAC) completa y en pantalla; disparador y escritura en espera de Balam") +ws["A2"].font = Font(size=10, italic=True, color=GRIS) + +# Las notas del corte anterior van fusionadas A:G y algunas empiezan con +# "Etapa 1: …", que colisiona con los prefijos de encabezado: se localizan y +# limpian ANTES del bucle principal, y este se detiene donde empezaban. +primera_nota = None +for row in range(5, ws.max_row + 1): + v = str(ws.cell(row=row, column=1).value or "") + if v.startswith("✅ Completada ·"): + primera_nota = row + if primera_nota and row >= primera_nota: + for rango in list(ws.merged_cells.ranges): + if rango.min_row == row: + ws.unmerge_cells(str(rango)) + ws.cell(row=row, column=1).value = None +tope = primera_nota if primera_nota else ws.max_row + 1 + +aplicado = set() +for row in range(5, tope): + nombre = str(ws.cell(row=row, column=1).value or "").strip() + if not nombre: + continue + + for prefijo, (trab, pct) in ENCABEZADOS.items(): + if nombre.startswith(prefijo): + if trab is not None: + c = ws.cell(row=row, column=C_TRAB, value=round(trab, 2)) + c.number_format = "0.00" + p = ws.cell(row=row, column=C_PCT, value=pct) + p.number_format = "0%" + if pct >= 1: + pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center") + elif pct > 0: + pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center") + break + + for prefijo, delta in DELTAS_TRAB.items(): + if nombre.startswith(prefijo): + actual = ws.cell(row=row, column=C_TRAB).value + actual = float(actual) if isinstance(actual, (int, float)) else 0.0 + c = ws.cell(row=row, column=C_TRAB, value=round(actual + delta, 2)) + c.number_format = "0.00" + aplicado.add(prefijo) + break + + for prefijo, (pct, txt, fill, fcolor, bold) in FILAS.items(): + if nombre.startswith(prefijo): + if pct is not None: + p = ws.cell(row=row, column=C_PCT, value=pct) + p.number_format = "0%" + if pct >= 1: + pinta(p, V_FILL, V_FONT, bold=True, size=10, align="center") + elif pct > 0: + pinta(p, A_FILL, A_FONT, bold=True, size=10, align="center") + else: + p.font = Font(size=10, color=GRIS) + s = ws.cell(row=row, column=C_STAT, value=txt) + pinta(s, fill, fcolor, bold) + break + +faltan = set(DELTAS_TRAB) - aplicado +assert not faltan, f"Filas no encontradas en el Excel: {faltan}" + +# --- Notas: se escribe el bloque del 6-ago donde estaba el del 1-ago ---------- +notas = [ + ("✅ Completada · 🟡 En curso · 🔴 Pendiente/bloqueada · ⚪ Próxima · " + "(adelantada) = ejecutada antes de su fecha plan", True), + (f"Horas trabajadas al corte del jueves 6-ago: Etapa 0 {E0:.2f} h (cerrada y validada) · " + f"Etapa 1 {E1:.2f} h (rango 32–39) · Etapa 2 {E2:.2f} h · ajuste Jira {AJ:.2f} h " + f"(de 7–10 aprobadas) · total {TOTAL:.2f} h. Base de facturación a $600/h + IVA.", True), + ("Etapa 1: 7 de 8 entregables contractuales terminados. Solo queda la capa de " + "escritura, en pausa por la definición del flujo de facturación.", False), + ("Integración Jira → BIND (7–10 h, v1.3 aprobada por el Ing. Noé el 30-jul): la " + "LECTURA quedó completa el 6-ago — la plataforma muestra la bandeja de solicitudes " + "y el detalle de cada ticket de FAC en vivo (solo lectura), con los campos del " + "formulario mapeados y los adjuntos descargables. El disparador de cotización y la " + "trazabilidad esperan las definiciones de negocio (request type, estatus disparador " + "y origen de monto/conceptos) y la prueba de escritura acompañada.", False), + ("Etapa 2: además del frontend de consulta (1-ago), toda la aplicación estrenó la " + "interfaz del prototipo aprobado (navegación por módulos, marca y estado del " + "sistema). Las filas de emisión siguen sujetas al flujo definido.", False), + ("En espera de Balam: acceso a Azure (Contributor — con el Ing. Noé desde el " + "28-jul), fecha de la sesión del flujo de facturación, y las tres definiciones de " + "la integración Jira.", False), +] +r = primera_nota if primera_nota else ws.max_row + 2 +for txt, bold in notas: + c = ws.cell(row=r, column=1, value=txt) + c.font = Font(italic=True, size=9, color=CAFE, bold=bold) + c.alignment = Alignment(wrap_text=True, vertical="top") + try: + ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=7) + except ValueError: + pass # ya estaba fusionada desde el archivo origen + ws.row_dimensions[r].height = 28 if len(txt) > 120 else 14 + r += 1 + +ws.print_area = f"A1:G{r}" +wb.save(DST) + +print("OK ->", DST) +print(f" E0 {E0:.2f} · E1 {E1:.2f} · E2 {E2:.2f} · Jira {AJ:.2f} · TOTAL {TOTAL:.2f} h " + f"· avance global {PCT_GLOBAL:.0%}")