Files
balam/planeacion/generar_avance_2026_07_27.py
T
Johann bee2d02f50 Documenta semana 24-27 jul: validación de prototipo (aprobada), API de Jira y avance de Etapa 1
- REGISTRO #55: validación del prototipo Etapa 0 (24-jul) APROBADA; único bloqueo = definir el flujo de facturación en Jira (sesión propuesta por Araceli para el martes 28)
- REGISTRO #56: sesión de API de Jira con Pedro (27-jul) — token PAF a 1 año, bandeja FAC; + seguimiento por correo del mismo día con los límites de velocidad (65k puntos/h, burst/s, por-issue) y entrega del token por Google Drive
- REGISTRO #57: hilo de WhatsApp con Erika (21-27 jul) — coordinación, envío del flujo por correo y reagenda 23→24 jul; 3 imágenes inferidas
- Transcripciones y correo en fuentes/; guion de validación en planeacion/
- Avance-Etapa1-2026-07-27 (nota + mensaje para Erika) y Excel del corte del 27-jul (núcleo verificado; capa de escritura en pausa por definición de Balam)
- Seguimiento-horas: sesiones del 24-jul (validación) y 27-jul (API Jira)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 08:18:08 -06:00

147 lines
6.9 KiB
Python

"""Genera Plan-actividades-avance-2026-07-27.xlsx a partir del del 21-jul.
Aplica las horas y estatus reales al 27-jul: sesión de reglas (22-jul) y
validación del prototipo (24-jul, APROBADO) completadas; sesión de API de Jira
(27-jul) en curso; capa de escritura EN PAUSA a la espera de la definición del
flujo de facturación (sesión 28-jul con Arturo/Araceli).
Reutiliza el estilo/semáforo del archivo del 21-jul."""
import re
from datetime import date
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
SRC = "/Users/johann/Desktop/Proyectos personales/BALAM/planeacion/Plan-actividades-avance-2026-07-21.xlsx"
DST = "/Users/johann/Desktop/Proyectos personales/BALAM/planeacion/Plan-actividades-avance-2026-07-27.xlsx"
HOY = date(2026, 7, 27)
CAFE, CREMA, GRIS_B, AMARILLO = "331F0E", "FDF3D8", "D9D9D9", "F7BD0C"
V_FILL, V_FONT = "C6EFCE", "006100" # verde
A_FILL, A_FONT = "FFEB9C", "9C6500" # amarillo
R_FILL, R_FONT = "FFC7CE", "9C0006" # rojo
G_FILL, G_FONT = "F2F2F2", "595959" # gris
C_PCT, C_BLQ, C_EST, C_TRAB, C_STAT = 7, 8, 9, 10, 11
# Prefijo de la actividad -> overrides de datos (pct=%, trab=horas, comienzo/fin=fechas texto)
OVERRIDES = {
"Integracion de plataformas Balam": dict(pct=0.67, trab=40),
"Etapa 0": dict(pct=1.0, trab=23),
"Documento de hallazgos": dict(pct=1.0),
"Sesión de validación del prototipo": dict(pct=1.0, trab=1.05,
comienzo="vie 24/07/26", fin="vie 24/07/26"),
"Etapa 1": dict(pct=0.82, trab=17),
"Demostración semanal": dict(trab=1.75),
"Sesión de reglas y dudas": dict(pct=1.0, trab=0.80),
"Definición de alcance Jira": dict(pct=0.40, trab=0.20),
}
# Estatus a mano (gana sobre el semáforo automático): texto, fill, font, negrita
K_ESPECIAL = {
"Documento de hallazgos": ("✅ Completada", V_FILL, V_FONT, False),
"Sesión de validación del prototipo": ("✅ Validado (aprobado)", V_FILL, V_FONT, True),
"Demostración semanal": ("🟡 En curso (semanal)", A_FILL, A_FONT, False),
"Modelo de facturas": ("🟡 En curso · falta seeder", A_FILL, A_FONT, False),
"Capa de escritura controlada": ("🔴 En pausa · def. Balam 28-jul", R_FILL, R_FONT, True),
"Sesión de reglas y dudas": ("✅ Completada", V_FILL, V_FONT, False),
"Definición de alcance Jira": ("🟡 En curso · API Jira 27-jul", A_FILL, A_FONT, False),
"Configuración de infraestructura en Azure": ("🟡 Pendiente (Balam)", A_FILL, A_FONT, False),
"CI/CD": ("🔴 Pendiente · dep. Azure", R_FILL, R_FONT, True),
}
def fecha(celda):
m = re.search(r"(\d{2})/(\d{2})/(\d{2})", str(celda or ""))
return date(2000 + int(m.group(3)), int(m.group(2)), int(m.group(1))) if m else None
def pinta(celda, fill, font_color, bold=False):
celda.fill = PatternFill("solid", fgColor=fill)
celda.font = Font(color=font_color, bold=bold, size=10)
celda.alignment = Alignment(horizontal="center", vertical="center")
wb = openpyxl.load_workbook(SRC)
ws = wb.active
# 1) Aplica overrides de datos por prefijo de nombre
for row in range(2, ws.max_row + 1):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre:
continue
for prefijo, ov in OVERRIDES.items():
if nombre.startswith(prefijo):
if "pct" in ov: ws.cell(row=row, column=C_PCT, value=ov["pct"])
if "trab" in ov: ws.cell(row=row, column=C_TRAB, value=ov["trab"])
if "comienzo" in ov: ws.cell(row=row, column=3, value=ov["comienzo"])
if "fin" in ov: ws.cell(row=row, column=4, value=ov["fin"])
break
# 2) Recalcula el semáforo con HOY=27-jul (mismas reglas que el del 21-jul)
for row in range(2, ws.max_row + 1):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
if not nombre or nombre.startswith("") or nombre.startswith(""):
continue
es_etapa = nombre.startswith("Etapa") or nombre.startswith("Integracion")
pct = ws.cell(row=row, column=C_PCT).value or 0
fin = fecha(ws.cell(row=row, column=4).value)
ini = fecha(ws.cell(row=row, column=3).value)
stat = ws.cell(row=row, column=C_STAT)
if es_etapa:
stat.value = ""
elif pct >= 1:
stat.value = "✅ Completada"; pinta(stat, V_FILL, V_FONT)
elif fin and fin < HOY:
stat.value = "🔴 Vencida"; pinta(stat, R_FILL, R_FONT, bold=True)
elif ini and ini <= HOY:
stat.value = "🟡 En curso"; pinta(stat, A_FILL, A_FONT)
elif pct > 0:
stat.value = "🟢 Adelantada"; pinta(stat, V_FILL, V_FONT)
else:
stat.value = "⚪ Próxima"; pinta(stat, G_FILL, G_FONT)
# semáforo en la celda de %
pcel = ws.cell(row=row, column=C_PCT)
if not es_etapa:
if pct >= 1: pinta(pcel, V_FILL, V_FONT, bold=True)
elif pct > 0: pinta(pcel, A_FILL, A_FONT, bold=True)
elif fin and fin < HOY: pinta(pcel, R_FILL, R_FONT, bold=True)
pcel.number_format = "0%"
ws.cell(row=row, column=C_TRAB).number_format = "0.0"
# 3) Overrides finos de estatus (bloqueos y dependencias)
for row in range(2, ws.max_row + 1):
nombre = str(ws.cell(row=row, column=1).value or "").strip()
for prefijo, (txt, fill, font, bold) in K_ESPECIAL.items():
if nombre.startswith(prefijo):
stat = ws.cell(row=row, column=C_STAT)
stat.value = txt
pinta(stat, fill, font, bold)
break
# 4) Encabezado de la columna de estatus y pie
ws.cell(row=1, column=C_STAT, value=f"Estatus (hoy {HOY.day}-jul)")
# 5) Reescribe leyenda/notas (borra las viejas de la línea de HOY del 21-jul)
for row in range(ws.max_row, 1, -1):
v = str(ws.cell(row=row, column=1).value or "")
if v.startswith("✅ Completada ·") or v.startswith("▬ La línea roja"):
for col in range(1, C_STAT + 1):
ws.cell(row=row, column=col).value = None
ley = ws.max_row + 2
notas = [
("✅ Completada · 🟡 En curso · 🔴 Vencida/Pendiente · ⚪ Próxima", G_FONT, True),
("Núcleo de la Etapa 1 construido y verificado contra producción: autenticación y roles, "
"bitácora, multi-tenant con RLS, cliente de BIND, sincronización de clientes/facturas/"
"cotizaciones, modelo de facturas con estados y cartera por moneda.", CAFE, False),
("🔴 La capa de escritura (cotización→factura) queda EN PAUSA a la espera de la definición "
"del flujo de facturación (sesión del martes 28-jul con Arturo y Araceli).", "9C0006", False),
("API de Jira: token recibido el 27-jul; la integración inicia una vez definido el flujo.", CAFE, False),
]
for i, (txt, color, bold) in enumerate(notas):
c = ws.cell(row=ley + i, column=1, value=txt)
c.font = Font(italic=True, size=9, color=color, bold=bold)
ws.freeze_panes = "A2"
ws.oddFooter.center.text = "Avance al 27-jul-2026 · Horas conforme al control local (pendiente conciliar con Jira)"
wb.save(DST)
print("OK ->", DST)