From caf7f28789aed5592db5210774aa28f7e5c520ba Mon Sep 17 00:00:00 2001 From: welsberr Date: Mon, 27 Apr 2026 13:45:42 -0400 Subject: [PATCH] Add GroundRecall-to-doclift bridge workflow --- src/didactopus/groundrecall_pack_bridge.py | 46 ++++++++++++++++++++++ src/didactopus/main.py | 25 ++++++++++++ tests/test_groundrecall_pack_bridge.py | 39 ++++++++++++++++++ tests/test_main_cli.py | 31 +++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 src/didactopus/groundrecall_pack_bridge.py create mode 100644 tests/test_groundrecall_pack_bridge.py diff --git a/src/didactopus/groundrecall_pack_bridge.py b/src/didactopus/groundrecall_pack_bridge.py new file mode 100644 index 0000000..9610521 --- /dev/null +++ b/src/didactopus/groundrecall_pack_bridge.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from pathlib import Path +import sys + +from .doclift_bundle_demo import run_doclift_bundle_demo + + +def run_doclift_bundle_with_groundrecall( + *, + groundrecall_store_dir: str | Path, + groundrecall_concept_ref: str, + bundle_dir: str | Path, + course_title: str, + pack_dir: str | Path, + author: str = "doclift bundle import", + license_name: str = "See source bundle metadata", +) -> dict: + export_groundrecall_query_bundle = _load_groundrecall_export() + pack_dir = Path(pack_dir) + export_dir = pack_dir / "_groundrecall" + exported = export_groundrecall_query_bundle( + groundrecall_store_dir, + groundrecall_concept_ref, + export_dir, + ) + summary = run_doclift_bundle_demo( + bundle_dir=bundle_dir, + course_title=course_title, + pack_dir=pack_dir, + author=author, + license_name=license_name, + groundrecall_query_bundle_path=exported["bundle_path"], + ) + summary["groundrecall_concept_ref"] = groundrecall_concept_ref + summary["groundrecall_query_bundle_path"] = exported["bundle_path"] + return summary + + +def _load_groundrecall_export(): + groundrecall_src = Path("/home/netuser/bin/GroundRecall/src") + if groundrecall_src.exists(): + sys.path.insert(0, str(groundrecall_src)) + from groundrecall.export import export_groundrecall_query_bundle # type: ignore + + return export_groundrecall_query_bundle diff --git a/src/didactopus/main.py b/src/didactopus/main.py index 0de4d82..2b482b1 100644 --- a/src/didactopus/main.py +++ b/src/didactopus/main.py @@ -6,6 +6,7 @@ from pathlib import Path from .config import load_config from .doclift_bundle_demo import run_doclift_bundle_demo +from .groundrecall_pack_bridge import run_doclift_bundle_with_groundrecall from .review_loader import load_draft_pack from .review_schema import ReviewSession, ReviewAction from .review_actions import apply_action @@ -35,6 +36,18 @@ def build_parser() -> argparse.ArgumentParser: doclift_parser.add_argument("--course-title", required=True) doclift_parser.add_argument("--author", default="doclift bundle import") doclift_parser.add_argument("--license-name", default="See source bundle metadata") + + doclift_gr_parser = subparsers.add_parser( + "doclift-bundle-groundrecall", + help="Generate a draft pack from a doclift bundle using a GroundRecall concept query bundle", + ) + doclift_gr_parser.add_argument("groundrecall_store_dir") + doclift_gr_parser.add_argument("groundrecall_concept_ref") + doclift_gr_parser.add_argument("bundle_dir") + doclift_gr_parser.add_argument("pack_dir") + doclift_gr_parser.add_argument("--course-title", required=True) + doclift_gr_parser.add_argument("--author", default="doclift bundle import") + doclift_gr_parser.add_argument("--license-name", default="See source bundle metadata") return parser @@ -95,4 +108,16 @@ def main() -> None: ) print(summary) return + if args.command == "doclift-bundle-groundrecall": + summary = run_doclift_bundle_with_groundrecall( + groundrecall_store_dir=args.groundrecall_store_dir, + groundrecall_concept_ref=args.groundrecall_concept_ref, + bundle_dir=args.bundle_dir, + course_title=args.course_title, + pack_dir=args.pack_dir, + author=args.author, + license_name=args.license_name, + ) + print(summary) + return build_parser().print_help() diff --git a/tests/test_groundrecall_pack_bridge.py b/tests/test_groundrecall_pack_bridge.py new file mode 100644 index 0000000..74c9469 --- /dev/null +++ b/tests/test_groundrecall_pack_bridge.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from pathlib import Path + +from didactopus import groundrecall_pack_bridge as bridge_module + + +def test_run_doclift_bundle_with_groundrecall_bridges_export_and_demo(monkeypatch, tmp_path: Path) -> None: + captured: dict = {} + + def _fake_export(store_dir, concept_ref, out_dir): + out_dir = Path(out_dir) + out_dir.mkdir(parents=True, exist_ok=True) + bundle_path = out_dir / "groundrecall_query_bundle.json" + bundle_path.write_text("{}", encoding="utf-8") + captured["store_dir"] = str(store_dir) + captured["concept_ref"] = concept_ref + captured["export_dir"] = str(out_dir) + return {"bundle_path": str(bundle_path), "bundle": {"bundle_kind": "groundrecall_query_bundle"}} + + def _fake_run_doclift_bundle_demo(**kwargs): + captured["demo_kwargs"] = kwargs + return {"pack_dir": str(kwargs["pack_dir"]), "groundrecall_bundle_included": True} + + monkeypatch.setattr(bridge_module, "_load_groundrecall_export", lambda: _fake_export) + monkeypatch.setattr(bridge_module, "run_doclift_bundle_demo", _fake_run_doclift_bundle_demo) + + payload = bridge_module.run_doclift_bundle_with_groundrecall( + groundrecall_store_dir=tmp_path / "store", + groundrecall_concept_ref="channel-capacity", + bundle_dir=tmp_path / "bundle", + course_title="Example Course", + pack_dir=tmp_path / "pack", + ) + + assert captured["concept_ref"] == "channel-capacity" + assert captured["demo_kwargs"]["groundrecall_query_bundle_path"].endswith("groundrecall_query_bundle.json") + assert payload["groundrecall_concept_ref"] == "channel-capacity" + assert payload["groundrecall_query_bundle_path"].endswith("groundrecall_query_bundle.json") diff --git a/tests/test_main_cli.py b/tests/test_main_cli.py index 7b3cb7d..e631229 100755 --- a/tests/test_main_cli.py +++ b/tests/test_main_cli.py @@ -41,6 +41,37 @@ def test_main_doclift_bundle_subcommand(monkeypatch, capsys, tmp_path: Path) -> assert "Example Course" in out +def test_main_doclift_bundle_groundrecall_subcommand(monkeypatch, capsys, tmp_path: Path) -> None: + captured: dict = {} + + def _fake_run_doclift_bundle_with_groundrecall(**kwargs): + captured.update({key: str(value) for key, value in kwargs.items()}) + return {"pack_dir": str(kwargs["pack_dir"]), "course_title": kwargs["course_title"]} + + monkeypatch.setattr(main_module, "run_doclift_bundle_with_groundrecall", _fake_run_doclift_bundle_with_groundrecall) + monkeypatch.setattr( + main_module.sys, + "argv", + [ + "didactopus", + "doclift-bundle-groundrecall", + str(tmp_path / "store"), + "channel-capacity", + str(tmp_path / "bundle"), + str(tmp_path / "pack"), + "--course-title", + "Example Course", + ], + ) + + main_module.main() + out = capsys.readouterr().out + + assert captured["groundrecall_concept_ref"] == "channel-capacity" + assert captured["course_title"] == "Example Course" + assert "Example Course" in out + + def test_main_legacy_review_mode_uses_review_parser(monkeypatch, tmp_path: Path) -> None: called: dict = {}