from __future__ import annotations import importlib import sys from collections.abc import Sequence from dataclasses import dataclass @dataclass(frozen=True, slots=True) class Command: module: str description: str COMMANDS: dict[str, Command] = { "statement": Command("gemini_for_enonce", "Analyse the exam statement with Gemini"), "statement-personal": Command("enonce_info", "Generate personal statement metadata"), "copies": Command("copies_tools", "Rotate or rename scanned copies"), "page-split": Command("page_splitter", "Split and reorder scanned PDF pages"), "crop-labels": Command("cutleft", "Crop the label margin from copies"), "labels": Command("gemini_for_labels", "Detect question labels with Gemini"), "review-labels": Command("plotting", "Review detected labels interactively"), "split-answers": Command("splitting_int", "Split copies into answers"), "group-answers": Command("grouping", "Group answers by question"), "verify-groups": Command("verify_groups", "Verify grouped answer metadata"), "correct": Command("correction", "Generate or integrate corrections"), "batch-submit": Command("submit_batches", "Submit Gemini batch jobs"), "batch-status": Command("batch_status", "Inspect Gemini batch jobs"), "batch-fetch": Command("fetch_batched_results", "Fetch Gemini batch results"), "post-correction": Command("post_correction", "Clean generated correction text"), "resolve-manual": Command("resolve_manual", "Resolve manual label conflicts"), "annotate-simple": Command("annotating", "Generate simple annotations"), "annotate-checks": Command("annotating_with_checks", "Generate checkable annotations"), "annotate-grouped": Command("annotating_by_label", "Generate grouped annotations"), "export": Command("export", "Export annotations"), "import": Command("import_annotations", "Import handwritten annotations"), "read-annotations": Command("reading_annotations", "Read checkable annotations"), "read-grouped": Command("reading_grouped_annotations", "Read grouped annotations"), "giving-names": Command("giving_names", "Name copies and prepare A Rendre"), "update-ods": Command("update_ods", "Update the configured score spreadsheet"), "add-final-score": Command("add_final_score", "Stamp final scores on copies"), "clean": Command("clean", "Delete intermediate files from a finished evaluation"), "gui": Command("@gui", "Launch the graphical workflow assistant"), } def _print_help() -> None: print("Usage: copienator COMMAND [ARGUMENTS]") print(" python -m copienator COMMAND [ARGUMENTS]") print("\nCommands:") width = max(len(name) for name in COMMANDS) for name, command in COMMANDS.items(): print(f" {name:<{width}} {command.description}") print("\nUse 'copienator COMMAND --help' for command-specific help.") def main(argv: Sequence[str] | None = None) -> int: arguments = list(sys.argv[1:] if argv is None else argv) if not arguments or arguments[0] in {"-h", "--help"}: _print_help() return 0 command_name = arguments.pop(0) command = COMMANDS.get(command_name) if command is None: print(f"Unknown command: {command_name}", file=sys.stderr) print("Use 'copienator --help' to list commands.", file=sys.stderr) return 2 module_name = ( "copienator_gui.__main__" if command.module == "@gui" else f"copienator.commands.{command.module}" ) module = importlib.import_module(module_name) result = module.main(arguments) return int(result or 0)