feat: add one-command candidate profile build flow

This commit is contained in:
Antoine 2026-05-28 18:26:09 +02:00
parent 1879de68a8
commit d27c58014d
2 changed files with 176 additions and 1 deletions

View File

@ -1,5 +1,12 @@
from pathlib import Path
import typer
from job_research.profile.cv_extractor import extract_cv_signals, extract_pdf_text
from job_research.profile.merge import build_candidate_profile_output
from job_research.profile.profile_parser import parse_profile_markdown
from job_research.storage import save_candidate_profile_yaml
app = typer.Typer(help="Build one canonical candidate profile YAML")
@ -8,9 +15,46 @@ def main_command() -> None:
pass
@app.command("build-profile")
def build_profile() -> None:
def build_profile(
cv: Path = typer.Option(
...,
"--cv",
exists=True,
dir_okay=False,
readable=True,
help="Path to the CV PDF or UTF-8 text file.",
),
profile: Path = typer.Option(
...,
"--profile",
exists=True,
dir_okay=False,
readable=True,
help="Path to the light-template markdown profile.",
),
out: Path = typer.Option(
Path("data/candidate-profile.yaml"),
"--out",
dir_okay=False,
help="Path to write the canonical YAML profile.",
),
) -> None:
"""Build candidate-profile.yaml from CV and markdown profile."""
cv_text = extract_pdf_text(cv) if cv.suffix.lower() == ".pdf" else cv.read_text(encoding="utf-8")
authored_profile = parse_profile_markdown(profile.read_text(encoding="utf-8"))
cv_signals = extract_cv_signals(cv_text)
candidate_profile = build_candidate_profile_output(cv_signals, authored_profile)
save_candidate_profile_yaml(out, candidate_profile)
typer.echo(f"candidate-profile.yaml written to {out}")
warning_count = len(candidate_profile.warnings)
if warning_count:
typer.echo(f"Warnings included: {warning_count}")
else:
typer.echo("No warnings included.")
def main() -> None:
app()

View File

@ -1,4 +1,7 @@
from subprocess import run
from textwrap import dedent
from job_research.storage import load_yaml
def test_installed_cli_help_exposes_build_profile_subcommand() -> None:
@ -14,3 +17,131 @@ def test_installed_cli_subcommand_help_works() -> None:
assert result.returncode == 0
assert "Usage: job-research build-profile" in result.stdout
def test_build_profile_writes_yaml_from_cv_and_profile(tmp_path) -> None:
cv = tmp_path / "cv.txt"
cv.write_text(
dedent(
"""
Tonio Example
Location: France
Languages: French, English
Skills: Python, SQL, Terraform
Years of experience: 3
Data Engineer at Acme
Education: Master of Science at Example University
"""
).strip(),
encoding="utf-8",
)
profile = tmp_path / "profile.md"
profile.write_text(
dedent(
"""
# Candidate Profile
## Summary
Junior data engineer focused on Python and GCP.
## Target Roles
- Data Engineer
## Strengths
- Python
- SQL
## Skills To Emphasize
- GCP
- BigQuery
## Constraints
- CDI only
- France only
## Notes
- Slight preference for French listings.
- Experience should stay visible.
"""
).strip(),
encoding="utf-8",
)
out = tmp_path / "candidate-profile.yaml"
result = run(
["uv", "run", "job-research", "build-profile", "--cv", str(cv), "--profile", str(profile), "--out", str(out)],
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0
assert f"candidate-profile.yaml written to {out}" in result.stdout
assert "Warnings included: 1" in result.stdout
payload = load_yaml(out)
assert payload["name"] == "Tonio Example"
assert payload["summary"] == "Junior data engineer focused on Python and GCP."
assert payload["target_roles"] == ["Data Engineer"]
assert payload["skills"] == ["Python", "SQL", "Terraform", "GCP", "BigQuery"]
assert payload["warnings"][0]["field"] == "years_of_experience"
def test_build_profile_reports_when_no_warnings_are_included(tmp_path) -> None:
cv = tmp_path / "cv.txt"
cv.write_text(
dedent(
"""
Tonio Example
Location: France
Languages: French, English
Skills: Python, SQL
Data Engineer at Acme
Education: Master of Science at Example University
"""
).strip(),
encoding="utf-8",
)
profile = tmp_path / "profile.md"
profile.write_text(
dedent(
"""
# Candidate Profile
## Summary
Junior data engineer focused on Python and GCP.
## Target Roles
- Data Engineer
## Strengths
- Python
- SQL
## Skills To Emphasize
- GCP
- BigQuery
## Constraints
- CDI only
- France only
## Notes
- Slight preference for French listings.
"""
).strip(),
encoding="utf-8",
)
out = tmp_path / "candidate-profile.yaml"
result = run(
["uv", "run", "job-research", "build-profile", "--cv", str(cv), "--profile", str(profile), "--out", str(out)],
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0
assert f"candidate-profile.yaml written to {out}" in result.stdout
assert "No warnings included." in result.stdout