Files
cowork-local/tools/check_graphrag_rescan.py
T
anhtnm1andClaude Opus 5 81b9482011 fix(tools): 5 checker UI hỏng sau đợt tách widget R08
`tools/` không đổi một byte nào giữa hai bản, nhưng 5 checker vẫn chết vì
chúng tìm control bằng `getattr(root, "ten")` trên đúng widget cũ — mà R08 đã
dời control xuống widget con.

Thêm ba helper dùng chung vào `capture_screens.py`:
  * `_own_member` — tên do app khai trên widget, không phải thừa kế từ Qt
  * `owner_of`   — widget thật sự đang giữ tên đó, duyệt theo bề rộng
  * `control`    — lấy control dù nó nằm ở cấp nào

`check_controls_alive` từ "MẤT 24 control" về 0, kèm liệt kê 22 control đã
đổi chỗ và 2 cái đổi tên. `check_probes_bite` từ 1/4 lên 6/6 phép cấy lỗi đều
bị bắt — phép cấy thứ hai trỏ vào `ui/schedule_task_tab.py` đã bị xoá, nay
trỏ vào `presentation/scheduling/kanban_board_widget.py`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 10:37:06 +09:00

132 lines
4.9 KiB
Python

"""GraphRAG rebuilds when it is opened, not every time a project is picked.
Switching project used to rescan the folder, redo the force layout and setHtml
the whole D3 page immediately — for a tab usually not on screen. With the rail's
picker one click away from anywhere, that fired constantly, and the rebuild you
did see on opening GraphRAG read as the page reloading itself.
"""
from __future__ import annotations
import os
import sys
import time
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO.parent))
sys.path.insert(0, str(Path(__file__).resolve().parent))
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from capture_screens import ( # noqa: E402
_apply_theme, _freeze_schedulers, _isolate_home, _load_fonts, owner_of)
def main() -> int:
sandbox = _isolate_home()
from PySide6.QtWidgets import QApplication
app = QApplication([])
_load_fonts()
_freeze_schedulers()
_apply_theme(app)
from cowork_local.config import CONFIG_DIR
assert str(sandbox) in str(CONFIG_DIR), f"isolation failed: {CONFIG_DIR}"
from seed_demo_data import seed
seed()
from cowork_local.app import MainWindow
from cowork_local.config import AppConfig
from cowork_local.i18n import set_language
from cowork_local.state import AppContext
set_language("vi")
win = MainWindow(AppContext(AppConfig.load()), user_name="local")
win.resize(1600, 950)
win.show()
app.processEvents()
st, w = win.structure, win.workspace
# R08-T14 split StructureGraphView: the browser view, the cached graph
# and the scan/render steps all moved onto GraphRenderer, while the shell
# only forwards the public methods. Probe the widget that owns them.
st = owner_of(st, "web") or st
fails: list[str] = []
# startup itself must not build any of it
if st.web is not None or st._graph is not None:
fails.append("dung san do thi ngay luc khoi dong — startup phai nhe")
# the idle warm-up builds the browser view and the first graph off the
# click path (MainWindow fires this on a 3s timer; call it directly here)
win._prewarm_graph()
deadline = time.monotonic() + 30
while st._graph is None and time.monotonic() < deadline:
app.processEvents()
time.sleep(0.02)
if st._graph is None:
print("FAIL lam nong khong dung duoc do thi")
sys.stdout.flush()
os._exit(1)
print(f"sau khi lam nong: web={'co' if st.web else 'chua'} do thi={'co' if st._graph else 'chua'}")
calls = {"scan": 0, "html": 0}
real_scan, real_html = st._scan, st._render_d3
st._scan = lambda: (calls.__setitem__("scan", calls["scan"] + 1), real_scan())[1]
st._render_d3 = lambda: (calls.__setitem__("html", calls["html"] + 1), real_html())[1]
# ...so the click itself does nothing but show it — this is the flash
t0 = time.monotonic()
win._goto(win._ROW_WORKSPACE, w._graphrag_tab_idx)
app.processEvents()
print(f"bam vao GraphRAG: {(time.monotonic() - t0) * 1000:.0f}ms "
f"_scan={calls['scan']} setHtml={calls['html']}")
if calls["scan"] or calls["html"]:
fails.append("bam vao GraphRAG van phai quet/nap lai trang — con chop trang")
# re-entering an unchanged graph must not rebuild anything
for _ in range(3):
win._goto(win._ROW_WORKSPACE, w._project_tab_idx)
app.processEvents()
win._goto(win._ROW_WORKSPACE, w._graphrag_tab_idx)
app.processEvents()
print(f"vao lai 3 lan (khong doi gi): _scan={calls['scan']} setHtml={calls['html']}")
if calls["scan"] or calls["html"]:
fails.append("vao lai GraphRAG van quet/reload du khong co gi doi")
# switching project off-screen defers the work
win._goto(win._ROW_WORKSPACE, w._cowork_tab_idx)
app.processEvents()
ids = [pid for _n, pid in w.project_choices()]
for pid in ids[1:3]:
w.choose_project(pid)
app.processEvents()
print(f"doi {len(ids[1:3])} project khi dang o Cowork: _scan={calls['scan']} "
f"setHtml={calls['html']} | can quet lan toi={st._needs_scan}")
if calls["scan"]:
fails.append("doi project van quet ngay du GraphRAG khong tren man")
if len(ids) > 1 and not st._needs_scan:
fails.append("doi project ma khong danh dau can quet lai")
# ...and opening it does the work once
win._goto(win._ROW_WORKSPACE, w._graphrag_tab_idx)
app.processEvents()
print(f"mo GraphRAG: _scan={calls['scan']}")
if len(ids) > 1 and calls["scan"] != 1:
fails.append(f"mo GraphRAG phai quet dung 1 lan, dang {calls['scan']}")
print()
for f in fails:
print("FAIL " + f)
print("PASS GraphRAG chi dung lai do thi khi can" if not fails
else f"{len(fails)} problem(s)")
sys.stdout.flush()
os._exit(1 if fails else 0)
if __name__ == "__main__":
raise SystemExit(main())