Corte de horas al 6-ago (53.83 h): sesión nocturna de la lectura Jira (1.25 h reales — sondeo customfields, bandeja+detalle con FAC en vivo, adjuntos, shell del prototipo); Excel de avance regenerado desde script. 'Cliente de la API de Jira' queda al 100%; ajuste Jira 5.50 de 7-10 h aprobadas

This commit is contained in:
Johann
2026-08-06 01:27:43 -06:00
parent 4ce5640e76
commit 547c300015
3 changed files with 195 additions and 0 deletions
Binary file not shown.
+3
View File
@@ -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,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,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-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)"
1 fecha semana etapa actividad_id actividad horas estado_hora fuente_evidencia facturable notas
43 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
44 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
45 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
46 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)
47 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)
48 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)
+192
View File
@@ -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%}")