"""Перечень модалок: python -m perfkit.modals_inventory -> perf/report-data//modals-inventory.md. Источник - perf/e2e/modals.json (строится по коду web/src); если в есть modal.jsonl, в таблицу добавляются n и медиана click_to_visible_ms по каждому target (включая варианты `@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 ", 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())