textmachine/eval/tenant_panel/verify22.py

128 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""ВЕРИФИКАТОР ЧИСЕЛ ОТЧЁТА ЭКСП-22. $0. Ненулевой код возврата = отчёт не сдаётся.
`itog22.py` пере-считывает числа из сырья; этот файл сверяет их с ТЕКСТОМ отчёта. Разделение
нужно потому, что «число посчиталось» и «в отчёте напечатано именно оно» — разные утверждения, и
эксп-21 разошёлся с сырьём ровно на этом стыке (§18: ИТОГ печатал расход фазы без одного тега,
а гейт это сертифицировал).
Правило: гейт сторожит НЕСУЩИЕ числа — те, на которых стоит решение владельца. Число, которого
гейт не проверяет, в ИТОГе стоять не должно.
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
ZONE = Path(__file__).resolve().parent
sys.path.insert(0, str(ZONE))
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
import bank as BANK # noqa: E402
import material as M # noqa: E402
import money as MONEY # noqa: E402
import panel as P2 # noqa: E402
REPORT = REPO / "docs" / "experiments" / "22-tenant-panel.md"
BAD = 0
def ck(name: str, ok: bool, detail: str = "") -> None:
global BAD # noqa: PLW0603
BAD += not ok
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" {detail}" if detail else ""))
def has(text: str, s: str) -> bool:
"""Число ищется по НОРМАЛИЗОВАННОМУ тексту: пробелы и переносы строк не значимы."""
return re.sub(r"\s+", "", s) in re.sub(r"\s+", "", text)
def main() -> int:
if not REPORT.exists():
print("отчёта нет")
return 1
t = REPORT.read_text(encoding="utf-8")
print("=== МАТЕРИАЛ ===")
zs = M.units_zh()
ck("книга и дата первой публикации названы",
has(t, M.BOOK["title"]) and has(t, M.BOOK["first_pub"]))
ck(f"число zh-единиц {len(zs)}", has(t, f"{len(zs)} единиц") or has(t, f"{len(zs)} zh"))
ck("гейт прав объявлен числом снятых глав",
has(t, "гейт прав") or has(t, "ГЕЙТ ПРАВ"))
b = BANK.PR.bank()
tot = sum(sum(u["source"].count(x) for u in zs) for x in b)
ck(f"банк {len(b)} терминов", has(t, f"{len(b)} термин"))
ck(f"знаменатель покрытия {tot}", has(t, str(tot)))
print("\n=== КОНТАМИНАЦИЯ ===")
f = MONEY.OUT / "contamination.json"
if f.exists():
for r in json.loads(f.read_text(encoding="utf-8")):
ck(f"{r['book']}/{r['model'].split('-')[0]} узнано {r['recognized']}/{r['n']}",
has(t, f"{r['recognized']}/{r['n']}"))
print("\n=== СКРИН Ф1 ===")
res = P2.screen_result()
for m, r in res.items():
if r.get("price_translator") is None:
continue
ck(f"{m}: цена переводчика {r['price_translator']:.5f}",
has(t, f"{r['price_translator']:.5f}"))
ck(f"{m}: вердикт {r['verdict']}", has(t, m))
print("\n=== ПАНЕЛЬ Ф2 ===")
for m in P2.tenants():
ck(f"жилец {m} назван в отчёте", has(t, m))
print("\n=== ДЕНЬГИ ===")
for ph in ("Ф0", "Ф1", "Ф2", "Ф3"):
sp = MONEY.Guarded(ph).spent()
if sp <= 0:
continue
ck(f"{ph}: расход ${sp:.6f} напечатан", has(t, f"{sp:.6f}"))
if sp > MONEY.CEILINGS[ph] + 1e-9:
ck(f"{ph}: ПРОБОЙ потолка объявлен",
has(t, "ПРОБОЙ") or has(t, "пробит") or has(t, "пробой"),
f"${sp:.6f} против ${MONEY.CEILINGS[ph]:.2f}")
pack = MONEY.Pack().spent()
ck(f"расход пака ${pack:.6f} напечатан", has(t, f"{pack:.6f}"))
print("\n=== СУДЕЙСТВО ===")
import judge as JU # noqa: PLC0415
rs = JU.runs_used()
if rs:
ck(f"агент-запусков {len(rs)} из {JU.CAP} напечатано",
has(t, f"{len(rs)} из {JU.CAP}") or has(t, f"{len(rs)}/{JU.CAP}"))
for pass_ in ("p2zh", "p2en", "p3", "dva"):
votes = MONEY.OUT / f"aj-{pass_}-votes"
if not votes.exists() or not list(votes.glob("*.json")):
continue
JU.setup(pass_)
import absjudge as AJ # noqa: PLC0415
data = AJ._by_unit() # noqa: SLF001
for a, bb, _w in JU.family(pass_):
m = AJ._margins(a, bb) # noqa: SLF001
if len(m) < 3:
continue
import statistics as sst # noqa: PLC0415
ck(f"{pass_}: контраст {a} vs {bb} = {sst.mean(m):+.2f} напечатан",
has(t, f"{sst.mean(m):+.2f}") or has(t, f"{sst.mean(m):.2f}"))
# контроли обязаны быть напечатаны ДО боевых чисел
dec = [d for d in data.values() if "CTRLdecoy" in d]
if dec:
ck(f"{pass_}: декой упомянут в отчёте", has(t, "декой") or has(t, "ДЕКОЙ"))
flo = [d for d in data.values() if "CTRLfloorA" in d and "CTRLfloorB" in d]
if flo:
ck(f"{pass_}: шумовой пол упомянут", has(t, "пол") or has(t, "ПОЛ"))
print(f"\n{'ВСЕ ЧИСЛА ОТЧЁТА СХОДЯТСЯ' if not BAD else f'РАСХОЖДЕНИЙ: {BAD} — ОТЧЁТ НЕ СДАЁТСЯ'}")
return BAD
if __name__ == "__main__":
sys.exit(1 if main() else 0)