feat(crm-ops): estrategia-marca 1.3.0 — SPEC v2.0, versão completa (36 capítulos), redação em paralelo, montagem e PDF A4; funde marketing:brand-strategy (marketing 2.1.0)
This commit is contained in:
+86
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Junta as partes de uma estratégia de marca num único markdown.
|
||||
|
||||
Uso:
|
||||
montar.py --partes DIR --intro intro.md --briefing briefing-llm.md --config marca.json --out estrategia.md
|
||||
[--glossario glossario.md] [--complementos complementos.md] [--historico historico.md]
|
||||
|
||||
- DIR contém parte-I.md ... parte-VIII.md (só os capítulos, `## N. Título`).
|
||||
- intro.md: resumo executivo + "Sobre este documento" (títulos `# ...`).
|
||||
- briefing-llm.md: a secção `## BRIEFING-LLM` completa (fica sempre em último).
|
||||
- marca.json: chaves `frontmatter` (dict) e, opcional, `partes` ([[romano, nome], ...]).
|
||||
Gera o anexo A (pontos a validar) a partir das marcas `[a validar: ...]`.
|
||||
"""
|
||||
import argparse, json, os, re, sys
|
||||
|
||||
PARTES = [["I", "Contexto"], ["II", "Fundamentos da marca"], ["III", "Públicos"],
|
||||
["IV", "Identidade e expressão"], ["V", "Oferta e venda"], ["VI", "Comunicação e canais"],
|
||||
["VII", "Crescimento"], ["VIII", "Medição e implementação"]]
|
||||
|
||||
|
||||
def yaml_value(v):
|
||||
if isinstance(v, list):
|
||||
return "[" + ", ".join(str(x) for x in v) + "]"
|
||||
s = str(v)
|
||||
return '"' + s.replace('"', '\\"') + '"' if re.search(r"[:#®·\-]", s) else s
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--partes", required=True)
|
||||
ap.add_argument("--intro", required=True)
|
||||
ap.add_argument("--briefing", required=True)
|
||||
ap.add_argument("--config", required=True)
|
||||
ap.add_argument("--out", required=True)
|
||||
ap.add_argument("--glossario")
|
||||
ap.add_argument("--complementos")
|
||||
ap.add_argument("--historico")
|
||||
a = ap.parse_args()
|
||||
|
||||
cfg = json.load(open(a.config, encoding="utf-8"))
|
||||
fm = "---\n" + "".join(f"{k}: {yaml_value(v)}\n" for k, v in cfg.get("frontmatter", {}).items()) + "---\n\n"
|
||||
doc = [fm, open(a.intro, encoding="utf-8").read().rstrip() + "\n\n"]
|
||||
|
||||
for rom, nome in cfg.get("partes", PARTES):
|
||||
p = os.path.join(a.partes, f"parte-{rom}.md")
|
||||
if not os.path.exists(p):
|
||||
sys.exit(f"Falta {p}")
|
||||
t = open(p, encoding="utf-8").read().strip()
|
||||
if re.search(r"^# ", t, re.M):
|
||||
sys.exit(f"{p} tem títulos de nível 1; só o montar.py escreve as Partes")
|
||||
doc.append(f"# Parte {rom}. {nome}\n\n{t}\n\n")
|
||||
full = "".join(doc)
|
||||
|
||||
pend, cap, seen, rows = [], None, set(), []
|
||||
for line in full.split("\n"):
|
||||
m = re.match(r"^## (\d+)\. ", line)
|
||||
if m:
|
||||
cap = int(m.group(1))
|
||||
for v in re.findall(r"\[a validar:\s*([^\]]+)\]", line):
|
||||
pend.append((cap or 0, v.strip().rstrip(".")))
|
||||
for c, v in pend:
|
||||
if (c, v.lower()) in seen:
|
||||
continue
|
||||
seen.add((c, v.lower()))
|
||||
rows.append(f"| {c or '—'} | {v[0].upper() + v[1:]} |")
|
||||
|
||||
anexos = ["# Anexos\n\n## A. Pontos a validar\n\n",
|
||||
f"{len(rows)} pontos, por capítulo. Cada ponto fechado corrige-se no capítulo e sai desta lista.\n\n",
|
||||
"| Cap. | O que falta confirmar |\n| --- | --- |\n", "\n".join(rows) or "| — | Nenhum |", "\n\n"]
|
||||
extras = [(t, f) for t, f in [("Glossário", a.glossario), ("Complementos do sector", a.complementos),
|
||||
("Histórico de alterações", a.historico)] if f]
|
||||
for letra, (titulo, f) in zip("BCD", extras):
|
||||
anexos.append(f"## {letra}. {titulo}\n\n" + open(f, encoding="utf-8").read().strip() + "\n\n")
|
||||
full += "".join(anexos)
|
||||
|
||||
br = open(a.briefing, encoding="utf-8").read().strip()
|
||||
if not br.startswith("## BRIEFING-LLM"):
|
||||
sys.exit("briefing-llm.md tem de começar por '## BRIEFING-LLM'")
|
||||
full += br + "\n"
|
||||
open(a.out, "w", encoding="utf-8").write(full)
|
||||
caps = len(re.findall(r"^## \d+\. ", full, re.M))
|
||||
print(f"{a.out}: {len(full.split())} palavras, {caps} capítulos, {len(rows)} pontos a validar")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user