Spaces:
Sleeping
Sleeping
| """ | |
| CLI entry point for the Multi-Source Candidate Data Transformer. | |
| Responsibilities: | |
| - Parse command-line arguments (input paths, config path, output path). | |
| - Orchestrate the pipeline: parse -> normalize -> merge -> project -> write output. | |
| - Handle top-level errors and exit codes. | |
| Business logic is delegated to parser, normalizer, merger, and projector modules. | |
| """ | |
| import argparse | |
| from dataclasses import asdict | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from typing import Any | |
| from .normalizer import normalize_candidate | |
| from .parser import read_ats_json, read_resume_pdf, read_github_profile, ParserError | |
| from .merger import merge_candidates | |
| from .projector import load_output_config, project_candidates | |
| from .utils import write_json_file | |
| # Project root is one level above src/ | |
| PROJECT_ROOT = Path(__file__).resolve().parent.parent | |
| DEFAULT_ATS_PATH = PROJECT_ROOT / "inputs" / "ats.json" | |
| DEFAULT_RESUME_PATH = PROJECT_ROOT / "inputs" / "resume.pdf" | |
| DEFAULT_CONFIG_PATH = PROJECT_ROOT / "config" / "output_config.json" | |
| DEFAULT_OUTPUT_PATH = PROJECT_ROOT / "output" / "final_candidate.json" | |
| def _print_section(title: str, data: Any) -> None: | |
| """Print a labeled JSON block.""" | |
| print("========================") | |
| print(title) | |
| print("========================") | |
| print() | |
| print(json.dumps(data, indent=2, ensure_ascii=False)) | |
| print() | |
| def main() -> int: | |
| """ | |
| Step 7 entry point: Parse CLI arguments and run the candidate transformation pipeline. | |
| Returns: | |
| Process exit code (0 = success, non-zero = failure). | |
| """ | |
| parser = argparse.ArgumentParser( | |
| description="Multi-Source Candidate Data Transformer CLI." | |
| ) | |
| parser.add_argument( | |
| "--ats", | |
| type=str, | |
| default=str(DEFAULT_ATS_PATH), | |
| help="Path to ATS JSON input file." | |
| ) | |
| parser.add_argument( | |
| "--resume", | |
| type=str, | |
| default=str(DEFAULT_RESUME_PATH), | |
| help="Path to Resume PDF input file." | |
| ) | |
| parser.add_argument( | |
| "--config", | |
| type=str, | |
| default=str(DEFAULT_CONFIG_PATH), | |
| help="Path to output projection configuration JSON file." | |
| ) | |
| parser.add_argument( | |
| "--output", | |
| type=str, | |
| default=str(DEFAULT_OUTPUT_PATH), | |
| help="Path to save the projected output JSON file." | |
| ) | |
| parser.add_argument( | |
| "--github", | |
| type=str, | |
| default=None, | |
| help="GitHub profile URL or username (optional third data source)." | |
| ) | |
| args = parser.parse_args() | |
| try: | |
| # 1. Parse & Normalize | |
| raw_ats = read_ats_json(args.ats) | |
| raw_resume = read_resume_pdf(args.resume) | |
| # 2. Normalize | |
| normalized_ats = normalize_candidate(raw_ats) | |
| normalized_resume = normalize_candidate(raw_resume) | |
| # Attach raw dictionaries for provenance retrieval | |
| normalized_ats["_raw"] = raw_ats | |
| normalized_resume["_raw"] = raw_resume | |
| candidates_to_merge = [normalized_ats, normalized_resume] | |
| # Optional GitHub profile integration | |
| if args.github: | |
| raw_github = read_github_profile(args.github) | |
| normalized_github = normalize_candidate(raw_github) | |
| normalized_github["_raw"] = raw_github | |
| candidates_to_merge.append(normalized_github) | |
| # 3. Merge | |
| merged_list = merge_candidates(candidates_to_merge) | |
| # 4. Project | |
| try: | |
| config = load_output_config(args.config) | |
| except FileNotFoundError: | |
| print(f"Error: Configuration file not found at '{args.config}'", file=sys.stderr) | |
| return 1 | |
| except json.JSONDecodeError as exc: | |
| print(f"Error: Configuration file at '{args.config}' contains invalid JSON: {exc}", file=sys.stderr) | |
| return 1 | |
| except Exception as exc: | |
| print(f"Error: Unable to load configuration file: {exc}", file=sys.stderr) | |
| return 1 | |
| projected_list = project_candidates(merged_list, config) | |
| # 5. Print Final Output JSON | |
| _print_section("FINAL OUTPUT JSON", projected_list) | |
| # 6. Save to disk if output path is specified | |
| if args.output: | |
| try: | |
| write_json_file(args.output, projected_list) | |
| print(f"Successfully saved projected candidates to '{args.output}'") | |
| except Exception as exc: | |
| print(f"Error: Failed to write output file to '{args.output}': {exc}", file=sys.stderr) | |
| return 1 | |
| return 0 | |
| except ParserError as exc: | |
| print(f"Parser Error: {exc}", file=sys.stderr) | |
| return 1 | |
| except Exception as exc: | |
| print(f"Unexpected Pipeline Error: {exc}", file=sys.stderr) | |
| return 1 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |