Q&A: aceleasi verificari aplicate si pe fisierele mele

audit_diagrams.py - verificare mecanica pe diagrame: text taiat de viewBox,
texte suprapuse, linii goale in <svg>, si raportul pret-vs-pixeli pe
diagramele cu trei niveluri.

Gasit si reparat in propriile sumarizari:
- Black Swan (25.08): stop-loss-ul de 24.00 desenat prea aproape de 27.00 —
  raport 0.21R in preturi, 0.31R desenat. Acum la scara, deci golul de sub
  stop arata cat e in realitate
- eticheta care descria panoul COT era pusa in panoul de pret
- 5 captions taiate de marginea dreapta

Plus moltbot: supervizor de repornire dupa ce workerul a murit fara urma in
loguri (probabil OOM nelogat), cpu_threads 5 -> 4. Documentat in PROGRESS.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
This commit is contained in:
Claude Agent
2026-09-12 21:31:56 +00:00
parent 9415fea0e7
commit 841a5f828c
12 changed files with 126 additions and 32 deletions

85
audit_diagrams.py Normal file
View File

@@ -0,0 +1,85 @@
"""
Verificări mecanice pe diagramele SVG dintr-un fișier de sumarizare.
Prinde exact clasele de bug găsite manual în 01_MODUL_1/05_patternuri_candele.md:
scară inconsistentă între niveluri, text tăiat de viewBox, text suprapus,
linii goale care rup blocul HTML. NU înlocuiește verificarea vizuală — nu
vede dacă un text stă peste o linie de preț.
Rulează: .venv/bin/python audit_diagrams.py summaries/<modul>/<fisier>.md
"""
import re
import sys
from pathlib import Path
CHAR_W = 5.7 # latime medie per caracter la 12px sans-serif (calibrat pe randare)
LINE_H = 13 # înălțimea unei linii de text
def texts(svg: str):
for m in re.finditer(r'<text\b([^>]*)>(.*?)</text>', svg, re.S):
attrs, inner = m.group(1), re.sub(r'<[^>]+>', '', m.group(2))
x = float(re.search(r'\bx="([-\d.]+)"', attrs).group(1)) if re.search(r'\bx="', attrs) else 0.0
y = float(re.search(r'\by="([-\d.]+)"', attrs).group(1)) if re.search(r'\by="', attrs) else 0.0
anchor = (re.search(r'text-anchor="(\w+)"', attrs) or [None, "start"])[1]
w = len(inner) * CHAR_W
x0 = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
yield x0, x0 + w, y, inner.strip()
def check(path: Path) -> list[str]:
md = path.read_text(encoding="utf-8")
problems = []
for i, svg in enumerate(re.findall(r'<svg .*?</svg>', md, re.S)):
vb = re.search(r'viewBox="0 0 ([\d.]+) ([\d.]+)"', svg)
if not vb:
problems.append(f"svg {i}: fără viewBox")
continue
vw, vh = float(vb.group(1)), float(vb.group(2))
if re.search(r"\n[ \t]*\n", svg):
problems.append(f"svg {i}: linie goală înăuntru — markdown rupe blocul")
tx = list(texts(svg))
for x0, x1, y, s in tx:
if x1 > vw + 2:
problems.append(f'svg {i}: text depășește marginea dreaptă ({x1:.0f} > {vw:.0f}): "{s[:45]}"')
if y > vh + 2 or y < 0:
problems.append(f'svg {i}: text în afara înălțimii (y={y:.0f}, viewBox {vh:.0f}): "{s[:45]}"')
for a in range(len(tx)):
for b in range(a + 1, len(tx)):
xa0, xa1, ya, sa = tx[a]
xb0, xb1, yb, sb = tx[b]
if abs(ya - yb) < LINE_H and xa0 < xb1 - 4 and xb0 < xa1 - 4:
problems.append(f'svg {i}: texte suprapuse — "{sa[:28]}" / "{sb[:28]}"')
# dacă diagrama are exact 3 niveluri de preț orizontale (SL/intrare/TP),
# raportul distanțelor în preț trebuie să fie cel din pixeli
lv = re.findall(r'<line x1="[\d.]+" y1="([\d.]+)"[^>]*?/>\s*<text[^>]*>([^<]*?(\d+\.\d\d)[^<]*)</text>', svg)
if len(lv) == 3:
pts = sorted((float(p), float(y)) for y, _, p in lv)
(plo, ylo), (pmid, ymid), (phi, yhi) = pts
if pmid - plo > 0 and ylo - ymid > 0:
rp = (phi - pmid) / (pmid - plo)
rx = (ymid - yhi) / (ylo - ymid)
if abs(rp - rx) > 0.05:
problems.append(f"svg {i}: SCARĂ GREȘITĂ — raport în prețuri {rp:.2f}R, desenat {rx:.2f}R")
return problems
def main():
files = [Path(a) for a in sys.argv[1:]] or sorted(Path("summaries").glob("*/*.md"))
total = 0
for f in files:
probs = check(f)
total += len(probs)
print(f"\n=== {f} — {len(probs)} probleme" if probs else f"\n=== {f} — curat")
for p in probs:
print(" ", p)
print(f"\nTOTAL: {total} probleme în {len(files)} fișiere")
return 0
if __name__ == "__main__":
sys.exit(main())