feat: linha do tempo processual dos autos vinculados Pet16704

Substitui o gitGraph do Mermaid (ilegível com 3.437 commits) por uma
timeline interativa com eixo temporal real, filtros por instituição,
tipo de documento e pessoas mencionadas (extraidas via NER local com
pierreguillou/ner-bert-base-cased-pt-lenerbr).
This commit is contained in:
2026-09-13 10:37:10 +02:00
commit 891d58ec55
10 changed files with 1559 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
import re, json, sys
root = "/Users/polux/tmp/Pet16704-pt2-md"
# 1. Parse gitgraph.md to get id -> branch (lane), preserving sequence order.
branch_of = {}
order_of = {}
current_branch = "STF · Relatoria"
idx = 0
with open(f"{root}/gitgraph.md", encoding="utf-8") as f:
for line in f:
line = line.strip()
m = re.match(r'checkout "([^"]*)"', line)
if m:
current_branch = m.group(1)
continue
m = re.match(r'commit id: "([^"]*)"', line)
if m:
cid = m.group(1)
branch_of[cid] = current_branch
order_of[cid] = idx
idx += 1
print(f"gitgraph commits: {len(branch_of)}", file=sys.stderr)
# 2. Parse TIMELINE.md: sections "## <Case>" then lines "- **NNNNN** (YYYY-MM-DD) — text — [`file`](<path>)"
records = []
current_case = None
line_re = re.compile(
r'^- \*\*(\d+)\*\*(?: \((\d{4}-\d{2}-\d{2})\))? — (.*?) — \[`[^`]*`\]\(<([^>]*)>\)\s*$'
)
with open(f"{root}/TIMELINE.md", encoding="utf-8") as f:
for line in f:
line = line.rstrip("\n")
m = re.match(r'^## (.+)$', line)
if m:
current_case = m.group(1).strip()
continue
m = line_re.match(line)
if m:
num, date, text, path = m.groups()
cid = f"{current_case}-{num}"
records.append({
"id": cid,
"date": date,
"text": text,
"file": path,
})
# disambiguate duplicate ids (multiple documents filed under the same number)
# to match gitgraph.md's "-a"/"-b"/"-c" suffix convention
from collections import Counter
id_counts = Counter(r["id"] for r in records)
seen = Counter()
suffixes = "abcdefgh"
for r in records:
base = r["id"]
if id_counts[base] > 1:
r["id"] = f"{base}-{suffixes[seen[base]]}"
seen[base] += 1
print(f"timeline records: {len(records)}", file=sys.stderr)
# 3a. Extract & normalize document type from the source filename.
TYPE_MAP = {
"Peticao": "Petição",
"Peticao inicial": "Petição inicial",
"Recibo de peticao eletronica": "Recibo de petição eletrônica",
"Documentos comprobatorios": "Documento comprobatório",
"Documento comprobatorio": "Documento comprobatório",
"Comunicacao assinada": "Comunicação assinada",
"Procuracao": "Procuração",
"Despacho": "Despacho",
"Certidao": "Certidão",
"Vista a PGR": "Vista à PGR",
"Intimacao": "Intimação",
"Decisao monocratica": "Decisão monocrática",
"Mandado de intimacao": "Mandado de intimação",
"Termo de disponibilizacao de autos": "Termo de disponibilização de autos",
"Documentos de identificacao": "Documentos de identificação",
"Aviso de recebimento": "Aviso de recebimento",
"Outras pecas": "Outras peças",
"Manifestacao": "Manifestação",
"Certidao de transito em julgado": "Certidão de trânsito em julgado",
"Certidao de julgamento": "Certidão de julgamento",
"Oficio": "Ofício",
"Documento Sigiloso": "Documento sigiloso",
"Manifestacao da PGR": "Manifestação da PGR",
"Pedido de ingresso como interessado": "Pedido de ingresso como interessado",
"Volume": "Volume",
"Inteiro teor do acordao": "Inteiro teor do acórdão",
"Inteiro teor do acordao (completo)": "Inteiro teor do acórdão",
"Mandado de prisao": "Mandado de prisão",
"Pedido de reconsideracao": "Pedido de reconsideração",
"Informacao": "Informação",
"Peticao de Interposicao de Agravo Regimental": "Petição de interposição de agravo regimental",
"Peticao de renuncia ao mandato": "Petição de renúncia ao mandato",
}
def extract_type(file_path):
base = re.sub(r'\.md$', '', file_path.split('/')[-1])
m = re.match(r'^\d+\s+(.*)$', base)
if not m:
return "Outro"
rest = re.sub(r'_[0-9a-fA-F]{6,}$', '', m.group(1))
if ' - ' in rest:
rest = rest.split(' - ')[0]
rest = rest.strip()
return TYPE_MAP.get(rest, rest)
# 3b. Merge
merged = []
missing_branch = 0
for r in records:
branch = branch_of.get(r["id"])
if branch is None:
missing_branch += 1
branch = "STF · Relatoria"
merged.append({
"id": r["id"],
"branch": branch,
"date": r["date"],
"text": r["text"],
"file": r["file"],
"type": extract_type(r["file"]),
"order": order_of.get(r["id"], -1),
})
print(f"missing branch matches: {missing_branch}", file=sys.stderr)
# also report ids in gitgraph not in timeline (should just be "raiz")
timeline_ids = {r["id"] for r in records}
only_in_gitgraph = [cid for cid in branch_of if cid not in timeline_ids]
print(f"only in gitgraph (not in timeline): {only_in_gitgraph[:10]} (total {len(only_in_gitgraph)})", file=sys.stderr)
merged.sort(key=lambda r: r["order"])
with open("/private/tmp/claude-501/-Users-polux-tmp-Pet16704-pt2-md/a294e67f-4337-4617-9903-54ea4711fb63/scratchpad/docs.json", "w", encoding="utf-8") as f:
json.dump(merged, f, ensure_ascii=False)
branches = sorted(set(branch_of.values()))
print("branches:", branches, file=sys.stderr)
dated = sum(1 for r in merged if r["date"])
print(f"dated: {dated}, undated: {len(merged)-dated}", file=sys.stderr)