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:
@@ -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)
|
||||
Reference in New Issue
Block a user