From 8e616f6bc6851e45719f9d360999b283ff5d5422 Mon Sep 17 00:00:00 2001 From: welsberr Date: Thu, 23 Apr 2026 07:18:15 -0400 Subject: [PATCH] Wire doclift bundle import into Didactopus CLI --- pyproject.toml | 1 + src/didactopus/main.py | 49 ++++++++++++++++++++++++++++-- tests/test_main_cli.py | 67 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100755 tests/test_main_cli.py diff --git a/pyproject.toml b/pyproject.toml index 53c7bda..5e25618 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ ] [project.scripts] +didactopus = "didactopus.main:main" didactopus-api = "didactopus.api:main" [tool.setuptools.packages.find] diff --git a/src/didactopus/main.py b/src/didactopus/main.py index b530728..0de4d82 100644 --- a/src/didactopus/main.py +++ b/src/didactopus/main.py @@ -1,16 +1,18 @@ from __future__ import annotations import argparse +import sys from pathlib import Path from .config import load_config +from .doclift_bundle_demo import run_doclift_bundle_demo from .review_loader import load_draft_pack from .review_schema import ReviewSession, ReviewAction from .review_actions import apply_action from .review_export import export_review_state_json, export_promoted_pack, export_review_ui_data -def build_parser() -> argparse.ArgumentParser: +def build_review_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Didactopus interactive review workflow scaffold") parser.add_argument("--draft-pack", required=True, help="Path to draft pack directory") parser.add_argument("--output-dir", default="review-output") @@ -18,8 +20,25 @@ def build_parser() -> argparse.ArgumentParser: return parser -def main() -> None: - args = build_parser().parse_args() +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Didactopus command-line tools") + subparsers = parser.add_subparsers(dest="command") + + review_parser = subparsers.add_parser("review", help="Run the interactive review workflow scaffold") + review_parser.add_argument("--draft-pack", required=True, help="Path to draft pack directory") + review_parser.add_argument("--output-dir", default="review-output") + review_parser.add_argument("--config", default="configs/config.example.yaml") + + doclift_parser = subparsers.add_parser("doclift-bundle", help="Generate a draft pack from a doclift bundle") + doclift_parser.add_argument("bundle_dir") + doclift_parser.add_argument("pack_dir") + 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") + return parser + + +def run_review_workflow(args: argparse.Namespace) -> None: config = load_config(Path(args.config)) draft = load_draft_pack(args.draft_pack) session = ReviewSession(reviewer=config.review.default_reviewer, draft_pack=draft) @@ -53,3 +72,27 @@ def main() -> None: print(f"Concepts: {len(session.draft_pack.concepts)}") print(f"Ledger entries: {len(session.ledger)}") print(f"Output dir: {outdir}") + + +def main() -> None: + argv = sys.argv[1:] + if not argv or argv[0].startswith("-"): + args = build_review_parser().parse_args(argv) + run_review_workflow(args) + return + + args = build_parser().parse_args(argv) + if args.command == "review": + run_review_workflow(args) + return + if args.command == "doclift-bundle": + summary = run_doclift_bundle_demo( + 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_main_cli.py b/tests/test_main_cli.py new file mode 100755 index 0000000..7b3cb7d --- /dev/null +++ b/tests/test_main_cli.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from pathlib import Path + +import didactopus.main as main_module + + +def test_main_doclift_bundle_subcommand(monkeypatch, capsys, tmp_path: Path) -> None: + captured: dict = {} + + def _fake_run_doclift_bundle_demo(bundle_dir, course_title, pack_dir, author, license_name): + captured.update( + { + "bundle_dir": str(bundle_dir), + "course_title": course_title, + "pack_dir": str(pack_dir), + "author": author, + "license_name": license_name, + } + ) + return {"pack_dir": str(pack_dir), "course_title": course_title} + + monkeypatch.setattr(main_module, "run_doclift_bundle_demo", _fake_run_doclift_bundle_demo) + monkeypatch.setattr( + main_module.sys, + "argv", + [ + "didactopus", + "doclift-bundle", + str(tmp_path / "bundle"), + str(tmp_path / "pack"), + "--course-title", + "Example Course", + ], + ) + + main_module.main() + out = capsys.readouterr().out + + 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 = {} + + def _fake_run_review_workflow(args): + called["draft_pack"] = args.draft_pack + called["output_dir"] = args.output_dir + + monkeypatch.setattr(main_module, "run_review_workflow", _fake_run_review_workflow) + monkeypatch.setattr( + main_module.sys, + "argv", + [ + "didactopus", + "--draft-pack", + str(tmp_path / "draft"), + "--output-dir", + str(tmp_path / "out"), + ], + ) + + main_module.main() + + assert called["draft_pack"] == str(tmp_path / "draft") + assert called["output_dir"] == str(tmp_path / "out")