"""Genera el Excel de avance que se compartirá con Erika el 16-jul-2026.""" from copy import copy from pathlib import Path from shutil import copy2 import openpyxl DIRECTORY = Path(__file__).parent SOURCE = DIRECTORY / "Plan-actividades-avance-2026-07-10-ajustado.xlsx" TARGET = DIRECTORY / "Plan-actividades-avance-2026-07-16.xlsx" def find_row(sheet, task): for row in range(2, sheet.max_row + 1): if sheet.cell(row, 1).value == task: return row raise ValueError(f"No se encontró la actividad: {task}") copy2(SOURCE, TARGET) workbook = openpyxl.load_workbook(TARGET) sheet = workbook["Plan"] sheet.cell(1, 8, "Horas al 16/07") updates = { "Integracion de plataformas Balam": ( 0.20, "Corte de avance al 16-jul; Etapa 0 en validación y Etapa 1 en curso", ), "Etapa 0": ( 0.91, "22 h registradas; prototipo entregado y validación agendada para el 23-jul", ), "Etapa 1": ( 0.20, "8 h registradas; 1 de 8 entregables terminado y plataforma base parcial", ), "Backend base: autenticación, roles y bitácora de auditoría": ( 0.30, "API, health check y DbContext base listos; autenticación, roles y auditoría pendientes", ), "Repositorio privado GitHub": ( 0.90, "Invitación recibida y acceso confirmado el 16-jul; falta validar permiso de escritura", ), "Arquitectura multi-tenant (tenant_id + RLS)": ( 0.25, "Diseño técnico inicial; entidades tenant y políticas RLS pendientes", ), "Cliente de la API de BIND (reintentos, OData, errores)": ( 1.00, "Terminado: cliente C# solo lectura, paginación, reintentos y cuota; 13/13 pruebas aprobadas", ), "Modelo de facturas + migraciones + datos de prueba": ( 0.10, "Base operativa con SyncCheckpoint y AuditLog; modelo de facturas y migración pendientes", ), "Demostración semanal (viernes) + reporte de horas": ( 0.50, "Corte del 16-jul preparado; control local mientras se habilita Jira", ), } for task, (progress, note) in updates.items(): row = find_row(sheet, task) sheet.cell(row, 7, progress) sheet.cell(row, 10, note) stage1_row = find_row(sheet, "Etapa 1") sheet.cell(stage1_row, 8, "=SUM(H12:H24)") for row in range(2, sheet.max_row + 1): sheet.cell(row, 7).number_format = "0%" sheet.oddFooter.center.text = ( "Avance al 16-jul-2026 · Horas reconstruidas pendientes de conciliación con Jira" ) # Conserva los estilos y usa el nombre de archivo como título impreso. sheet.oddHeader.center.text = "Plan de actividades y avance · Proyecto Balam" for cell in sheet[1]: cell._style = copy(cell._style) temporary = TARGET.with_suffix(".tmp.xlsx") workbook.save(temporary) check = openpyxl.load_workbook(temporary, data_only=False)["Plan"] assert check.cell(find_row(check, "Etapa 1"), 7).value == 0.20 assert check.cell( find_row(check, "Cliente de la API de BIND (reintentos, OData, errores)"), 7 ).value == 1.00 assert check.cell(find_row(check, "Etapa 1"), 8).value == "=SUM(H12:H24)" temporary.replace(TARGET) print(f"Generado: {TARGET}")