bethania_porto_santos/.claude/skills/plano-de-ataque/SKILL.md

164 lines
8.7 KiB
Markdown
Raw Normal View History

---
name: plano-de-ataque
description: Gera ou atualiza o PPTX "Plano de Ataque" (progresso por quinzena de Geotecnia/Estaqueamento, Infraestrutura/Bloco e Superestrutura/Pilar) deste repositório. Use quando o usuário pedir para regenerar o plano de ataque, atualizar quinzenas com uma planilha nova, mudar as cores/legenda do PPTX, ajustar o mapeamento de pixels de uma planta nova, ou reempacotar os pacotes standalone (dist-linux/dist-windows). Escopo: repositório bethania_porto_santos (raiz do projeto).
---
# Plano de Ataque — PPTX de progresso por quinzena
Este skill resume o que foi aprendido construindo `gerar_plano.py`: um gerador
que pega um PPTX-modelo (1 slide de "uma quinzena") e uma planilha de
planejamento, e produz um PPTX com 1 slide por quinzena, pintando cada
quadrante da planta conforme o status de cada estrutura naquela data.
## Onde as coisas estão
- `gerar_plano.py` (raiz) — script-fonte, único arquivo, sem módulos próprios
além de `openpyxl`, `python-pptx`, `lxml`.
- `cores.json` (raiz) — cores/opacidades/parâmetros de quinzena, editável sem
tocar no código (ver seção "Cores" abaixo).
- `dist-linux/` — pacote pronto (executável standalone via PyInstaller) +
modelo `.pptx` + planilha `.xlsx` de exemplo + `LEIA-ME.txt`.
- `dist-windows/` — pacote pronto (Python "embeddable" do python.org com as
libs já instaladas em `python/Lib/site-packages`, sem precisar instalar
nada) + `PlanoDeAtaque.bat` + mesmos exemplos + `LEIA-ME.txt` + um `.zip`
com tudo empacotado.
## Fluxo normal de uso (regenerar o PPTX)
1. Reunir em uma pasta: 1 `.json` de cores, 1 `.pptx` modelo (slide 2 = layout
de uma quinzena), 1 `.xlsx` de planejamento.
2. `python3 gerar_plano.py` dentro dessa pasta (ou rodar o executável/`.bat`
em `dist-linux`/`dist-windows`).
3. O script AUTO-DETECTA os 3 arquivos pela extensão na própria pasta (não
usa nomes fixos) e gera `<nome-do-modelo>_ATUALIZADO.pptx`.
4. Se o usuário mandar uma planilha/modelo NOVO por upload, é só copiar para
`.uploads/` (ou onde estiver rodando), gerar, e comparar visualmente
(ver "Como validar" abaixo) antes de entregar.
## Modelo de dados da planilha
Aba **"Planejamento"**, dados a partir da linha 4:
- Coluna B: nome do quadrante, formato `"<col1>-<col2>/<linha1>-<linha2>"`,
ex. `"10-11/A1-B"` (colunas são números 1-48; linhas são rótulos entre
`D, C, B1, B, A1, A`). Regex usado: `^(\d+)-(\d+)/([A-Z0-9]+)-([A-Z0-9]+)$`.
- Colunas C/D: Geotecnia (Início/Término)
- Colunas E/F: Infraestrutura (Início/Término)
- Colunas G/H: Superestrutura (Início/Término)
Status por quadrante/estrutura numa data de corte `p_end`:
- `None` (não pintar) se Início vazio ou Início > p_end
- `"concluido"` se Término preenchido e Término <= p_end
- `"andamento"` caso contrário
## Quinzenas
Períodos de **14 dias** começando em `2026-12-14 = Quinzena 02` (a planilha
não tem atividade antes dessa data — por isso a numeração já começa em 02,
não em 01). Esses três parâmetros ficam em `cores.json → quinzena` e dão
para editar sem mexer no código: `duracao_dias`, `data_inicio`,
`numero_inicial`.
## Cores (`cores.json`)
Uma seção por estrutura (`geotecnia`, `superestrutura`, `infraestrutura`),
cada uma com `andamento` e `concluido`, cada um com `preenchimento` (hex sem
`#`), `borda` (hex sem `#`) e `alpha_preenchimento` (0-100). A legenda do
slide sempre usa 100% de opacidade (fiel à cor pedida); o desenho usa o
`alpha_preenchimento` configurado — mais baixo na geotecnia (cobre o
quadrante inteiro, não pode esconder a planta) e mais alto nos marcadores de
pilar/bloco (são pequenos, não atrapalham).
**Regra de ouro ao pedir "mude a cor X pra Y"**: sempre pedir/usar um print
em ALTA resolução da legenda e fazer amostragem de pixel (não confiar em
screenshot comprimido/pequeno — já erramos assim uma vez nesta conversa).
Amostrar tanto o preenchimento quanto a BORDA (são cores diferentes, e a
legenda de referência sempre tem as duas).
## Como as camadas são desenhadas (importante para não perder fidelidade)
- **Geotecnia/Estaqueamento**: retângulo arredondado cobrindo o quadrante
INTEIRO (não um recorte menor).
- **Superestrutura/Pilar e Infraestrutura/Bloco**: NÃO são um retângulo
proporcional ao quadrante — são marcadores pequenos (quadrado arredondado,
`marcador_fracao` do espaçamento entre colunas) plantados nos 4 VÉRTICES
(colunaxlinha) de cada quadrante ativo. Como vértices são compartilhados
entre quadrantes vizinhos, dedupe por ponto usando o status mais avançado
(`concluido` > `andamento`) para não desenhar marcadores duplicados/
sobrepostos no mesmo lugar.
## Mapeamento pixel → EMU (a parte mais delicada)
As posições de colunas/linhas da planta (`IM1_COLPX`, `IM2_COLPX`,
`IM1_ROWPX`, `IM2_ROWPX` no script) são constantes medidas a dedo nas imagens
do modelo, usando detecção de blobs por soma de pixels escuros por
coluna/linha (círculos dos eixos, ver histórico da conversa para o script de
medição). Se a planta do modelo mudar, essas constantes têm que ser
remedidas — **sempre medir direto na imagem que está DE FATO embutida no
`.pptx`** (`ppt/media/imageN.png` dentro do zip), não em uma cópia enviada
separadamente por upload: já tivemos um bug de desalinhamento porque as duas
não eram pixel-idênticas.
**Gotcha crítico**: se a imagem no slide tiver um corte aplicado no
PowerPoint (`<a:srcRect t="..." b="..." l="..." r=".../>` dentro do
`<p:blipFill>`), a conversão pixel→EMU tem que descontar esse corte, senão
tudo fica deslocado (geralmente para baixo/lado). Sempre checar o XML do
`<p:pic>` de cada imagem do slide-modelo antes de assumir que pixel 0 =
canto da imagem exibida. Foi exatamente isso que causou o "a imagem de baixo
está deslocada" nesta conversa.
## Duplicar slides em python-pptx
Não existe API pública para duplicar slide. A receita usada:
1. `dest = prs.slides.add_slide(source.slide_layout)`, depois remover os
placeholders que a layout injeta.
2. Para cada relationship de imagem do slide-fonte, `dest.part.relate_to(...)`
para criar a relação equivalente no slide-destino (isso gera um rId novo,
que pode não bater com o rId original).
3. Deep-copy de cada shape do slide-fonte e **reescrever os atributos
`r:embed`/`r:link` diretamente no elemento XML** (`el.set(qn('r:embed'), novo_rid)`),
nunca com substituição de texto/regex na string do XML — se o
remapeamento de rIds for uma permutação (ex. rId2↔rId3), um replace
sequencial de string corrompe os dois (bug real que já apareceu aqui).
## Reempacotar os executáveis
- **Linux** (`dist-linux/PlanoDeAtaque`): `pip install openpyxl python-pptx
lxml pyinstaller` e `pyinstaller --onefile --name PlanoDeAtaque
gerar_plano.py`.
- **Windows** (`dist-windows/`): este ambiente não tem Wine nem acesso a uma
máquina Windows, então NÃO dá para compilar um `.exe` de verdade aqui. A
solução usada foi montar um Python "portátil": baixar o
`python-3.11.9-embed-amd64.zip` oficial do python.org, habilitar
site-packages editando `python311._pth` (adicionar `Lib\site-packages` e
`import site`), e usar `pip download --platform win_amd64 --python-version
311 --implementation cp --abi cp311 --only-binary=:all: <pacotes>` para
baixar as wheels certas (mesmo rodando de um Linux — `pip download`
baixa o arquivo, não executa nada) e extrair (unzip) cada wheel dentro de
`python/Lib/site-packages`. Lançador é um `.bat` simples chamando
`python\python.exe gerar_plano.py`. Depois de montar, gerar de novo o
`PlanoDeAtaque_windows.zip` (cuidado: gerar o zip DENTRO da própria pasta
que está sendo zipada faz o zip se autoincluir — gerar em `/tmp` e mover
para dentro depois).
## Como validar antes de entregar
Como não há LibreOffice/PowerPoint disponível neste ambiente para renderizar
o `.pptx`, a validação usada foi:
1. Integridade: `zipfile.testzip()` + `lxml.etree.fromstring` em cada
`.xml`/`.rels` do pacote.
2. Reconstrução visual manual: ler `off/ext` (EMU) de cada `<p:pic>` e forma
colorida do slide via `python-pptx`, converter para pixels e compor com
Pillow (`Image.alpha_composite`) para gerar um PNG de conferência — é
assim que os desalinhamentos e cores erradas foram detectados nesta
conversa.
## Git neste repositório
Não existe identidade de git configurada globalmente por padrão aqui. Em vez
de rodar `git config --global` (proibido pelas regras de segurança do
ambiente), usar override só para o comando do commit:
`git -c user.name="..." -c user.email="..." commit -m "..."`.
`.uploads/` está no `.gitignore` (são anexos de conversa, não arquivos do
projeto) — os artefatos finais que importam ficam em `dist-linux/` e
`dist-windows/`.