diff --git a/tools/check_graphrag_rescan.py b/tools/check_graphrag_rescan.py new file mode 100644 index 0000000..c65038b --- /dev/null +++ b/tools/check_graphrag_rescan.py @@ -0,0 +1,112 @@ +"""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) + + +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 + fails = [] + + # first visit builds the view and scans once + win._goto(win._ROW_WORKSPACE, w._graphrag_tab_idx) + 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 lan dau khong dung duoc do thi") + sys.stdout.flush() + os._exit(1) + + 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] + + # 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()) diff --git a/ui/structure_graph_view.py b/ui/structure_graph_view.py index 5d3e6a9..eb7a4c6 100644 --- a/ui/structure_graph_view.py +++ b/ui/structure_graph_view.py @@ -404,10 +404,13 @@ class StructureGraphView(QWidget): if project is not None: self.path_edit.setText(str(project.workspace_dir())) if project_changed: + # Mark it and scan on the next visit rather than now. The rail's + # project picker made switching a one-click thing from any screen, + # and each switch rebuilt this graph — a folder walk plus a force + # layout plus a full setHtml of the D3 page — for a tab that was + # usually not even on screen. auto_scan_and_fit() picks the flag up + # when GraphRAG is actually opened. self._needs_scan = True - if self.web is not None: - self._needs_scan = False - self._scan() # ---- helpers ----------------------------------------------------- def _pick(self) -> None: