()
| 618 | |
| 619 | |
| 620 | def main() -> int: |
| 621 | args = parse_args() |
| 622 | run_dir = (args.outdir or (DEFAULT_RUN_ROOT / args.run_id)).expanduser().resolve() |
| 623 | if run_dir.exists(): |
| 624 | raise FileExistsError(f"run directory already exists: {run_dir}") |
| 625 | run_dir.mkdir(parents=True) |
| 626 | (run_dir / "logs").mkdir(parents=True, exist_ok=True) |
| 627 | |
| 628 | validation, method_decision = validate_inputs(args) |
| 629 | tool_status = tool_preflight(["Rscript"], optional=[]) |
| 630 | write_json( |
| 631 | run_dir / "config.json", |
| 632 | { |
| 633 | "method": method_decision, |
| 634 | "inputs": { |
| 635 | "count_matrix": str(args.count_matrix.expanduser().resolve()), |
| 636 | "sample_metadata": str(args.sample_metadata.expanduser().resolve()), |
| 637 | "contrasts": str(args.contrasts.expanduser().resolve()), |
| 638 | }, |
| 639 | }, |
| 640 | ) |
| 641 | write_json(run_dir / "validation" / "input_summary.json", {"inputs": validation}) |
| 642 | write_json(run_dir / "validation" / "validation_summary.json", validation) |
| 643 | write_json(run_dir / "validation" / "tool_preflight.json", tool_status) |
| 644 | write_json( |
| 645 | run_dir / "versions" / "software_versions.json", |
| 646 | software_versions({"Rscript": ["Rscript", "--version"]}), |
| 647 | ) |
| 648 | write_workflow(run_dir) |
| 649 | |
| 650 | cmd = r_command(args, run_dir, method_decision) |
| 651 | write_commands(run_dir, cmd) |
| 652 | dry_run = { |
| 653 | "ok": validation.get("ok") and tool_status.get("ok"), |
| 654 | "detail": "input/method validation completed", |
| 655 | } |
| 656 | write_json(run_dir / "logs" / "validation_dry_run.json", dry_run) |
| 657 | write_text(run_dir / "logs" / "validation_dry_run.log", dry_run["detail"] + "\n") |
| 658 | execution: dict[str, Any] | None = None |
| 659 | status = "blocked" if not dry_run["ok"] else "validated" |
| 660 | if args.execute and dry_run["ok"]: |
| 661 | execution = run_cmd(cmd, run_dir, timeout=86400) |
| 662 | write_json(run_dir / "logs" / "rscript_execute.json", execution) |
| 663 | write_text(run_dir / "logs" / "rscript_execute.log", execution.get("stdout_tail", "")) |
| 664 | status = "completed" if execution.get("ok") else "failed" |
| 665 | |
| 666 | outputs = build_outputs_map(str(method_decision.get("input_mode", "normalized_expression"))) |
| 667 | review_bundle = generate_visualizations(run_dir, validation) |
| 668 | review_notebook_path = run_dir / review_bundle["review_notebook"] |
| 669 | review_app_info = ( |
| 670 | maybe_launch_review_app(args, run_dir, review_notebook_path) |
| 671 | if args.execute and status == "completed" and review_notebook_path.exists() |
| 672 | else None |
| 673 | ) |
| 674 | review_bundle = generate_visualizations(run_dir, validation, review_app_info=review_app_info) |
| 675 | outputs["review_app_record"] = "notebooks/marimo_server.json" |
| 676 | outputs.update(review_bundle) |
| 677 | write_summary(run_dir, status, validation, method_decision, review_app_info=review_app_info) |
no test coverage detected