Перечень модалок (modals.json), measureOpen с main_thread_ms, профиль перерисовок React (reactProfile), сценарий modals.spec.ts, подкоманда stand.sh web-profiling (:8081), генератор modals-inventory.md. Refs #202 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
4.0 KiB
Python
95 lines
4.0 KiB
Python
"""Перечень модалок: python -m perfkit.modals_inventory <run_dir> -> perf/report-data/<run_id>/modals-inventory.md.
|
||
|
||
Источник - perf/e2e/modals.json (строится по коду web/src); если в <run_dir> есть modal.jsonl, в таблицу
|
||
добавляются n и медиана click_to_visible_ms по каждому target (включая варианты `<id>@big`).
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import statistics
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
from . import PERF_DIR, REPORT_DATA_DIR
|
||
from .results import read_all
|
||
|
||
MODALS_JSON = PERF_DIR / "e2e" / "modals.json"
|
||
|
||
|
||
def load_modals(path: Path = MODALS_JSON) -> list[dict]:
|
||
return json.loads(Path(path).read_text(encoding="utf-8"))
|
||
|
||
|
||
def _measured(run_dir) -> dict[str, list[float]]:
|
||
out: dict[str, list[float]] = {}
|
||
if run_dir is None:
|
||
return out
|
||
for r in read_all(run_dir):
|
||
if r.get("scenario") != "modal" or r.get("incomplete"):
|
||
continue
|
||
v = r.get("metrics", {}).get("click_to_visible_ms")
|
||
if isinstance(v, (int, float)):
|
||
out.setdefault(r["target"], []).append(v)
|
||
return out
|
||
|
||
|
||
def build(modals: list[dict], run_dir=None) -> list[dict]:
|
||
meas = _measured(run_dir)
|
||
rows = []
|
||
for m in modals:
|
||
row = {k: m.get(k) for k in ("id", "title", "page", "source", "priority", "role", "skip_reason", "note")}
|
||
row["open"] = m["open"]["selector"]
|
||
row["pre"] = len(m["open"].get("pre") or [])
|
||
vals = meas.get(m["id"], [])
|
||
if m.get("skip_reason"):
|
||
row.update(status="skip", n=0, median_ms=None)
|
||
elif vals:
|
||
row.update(status="measured", n=len(vals), median_ms=statistics.median(vals))
|
||
else:
|
||
row.update(status="not_run", n=0, median_ms=None)
|
||
rows.append(row)
|
||
if m.get("big_sheet"):
|
||
bv = meas.get(f"{m['id']}@big", [])
|
||
rows.append({**row, "id": f"{m['id']}@big", "title": f"{m['title']} (самый большой лист {m['big_sheet']})",
|
||
"status": "measured" if bv else "not_run", "n": len(bv),
|
||
"median_ms": statistics.median(bv) if bv else None})
|
||
return rows
|
||
|
||
|
||
def render_md(rows: list[dict]) -> str:
|
||
lines = [
|
||
"# Перечень модалок",
|
||
"",
|
||
"Источник: `perf/e2e/modals.json` (поиск по `web/src`: `common/Modal`, emotion-модалки `ModalBackdrop`, MUI `Dialog`/`Modal`/`Popover`).",
|
||
"priority 1 - листы форм, задача, проект, проекты, задачи; priority 2 - админка и справочник ВСП.",
|
||
"",
|
||
"| id | страница | source | priority | роль | открытие | статус | n | медиана, мс | skip_reason |",
|
||
"|---|---|---|---|---|---|---|---|---|---|",
|
||
]
|
||
for r in rows:
|
||
med = "" if r["median_ms"] is None else f"{r['median_ms']:.0f}"
|
||
opn = f"`{r['open']}`" + (f" (+{r['pre']} шаг.)" if r["pre"] else "") if r["open"] else ""
|
||
lines.append(
|
||
f"| `{r['id']}` | {r['page']} | `{r['source']}` | {r['priority']} | {r['role']} | {opn} | {r['status']} | {r['n']} | {med} | {r.get('skip_reason') or ''} |"
|
||
)
|
||
skipped = sum(1 for r in rows if r["status"] == "skip")
|
||
lines += ["", f"Всего: {len(rows)}, без skip_reason: {len(rows) - skipped}, skip_reason: {skipped}."]
|
||
return "\n".join(lines) + "\n"
|
||
|
||
|
||
def main(argv=None) -> int:
|
||
argv = sys.argv[1:] if argv is None else argv
|
||
if len(argv) != 1:
|
||
print("usage: python -m perfkit.modals_inventory <run_dir>", file=sys.stderr)
|
||
return 2
|
||
run_dir = Path(argv[0])
|
||
out = REPORT_DATA_DIR / run_dir.resolve().name
|
||
out.mkdir(parents=True, exist_ok=True)
|
||
(out / "modals-inventory.md").write_text(render_md(build(load_modals(), run_dir)), encoding="utf-8")
|
||
print(out / "modals-inventory.md")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|