164 lines
8.7 KiB
Markdown
164 lines
8.7 KiB
Markdown
|
|
---
|
||
|
|
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/`.
|