---
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` só
  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/`.
