fix: relax Apec input and write flow

This commit is contained in:
Antoine 2026-06-03 20:53:47 +02:00
parent 352dfcd6ce
commit 207d5c51c3
4 changed files with 37 additions and 40 deletions

View File

@ -5,14 +5,6 @@ def _normalize_term(raw_term: str) -> str:
return " ".join(raw_term.split())
def _normalize_constraint(raw_term: str) -> str:
term = _normalize_term(raw_term)
if term.lower().endswith(" only"):
return term[:-5].rstrip()
return term
def derive_apec_queries(profile: CandidateProfileOutput) -> list[str]:
queries: list[str] = []
seen: set[str] = set()
@ -35,28 +27,22 @@ def derive_apec_queries(profile: CandidateProfileOutput) -> list[str]:
support_terms = [_normalize_term(term) for term in profile.strengths]
support_terms.extend(_normalize_term(term) for term in profile.skills_to_emphasize)
support_terms.extend(_normalize_constraint(term) for term in profile.constraints)
support_terms = [term for term in support_terms if term]
if support_terms:
for target_role in unique_roles[:2]:
add_query(target_role)
for target_role in unique_roles:
add_query(target_role)
if len(queries) == 5:
return queries
if unique_roles:
primary_role = unique_roles[0]
for term in support_terms:
add_query(f"{primary_role} {term}")
if len(queries) == 5:
break
else:
for term in support_terms:
add_query(term)
if len(queries) == 5:
break
if unique_roles:
primary_role = unique_roles[0]
for term in support_terms:
add_query(f"{primary_role} {term}")
if len(queries) == 5:
break
else:
for target_role in unique_roles[:5]:
add_query(target_role)
for term in support_terms:
add_query(term)
if len(queries) == 5:
break

View File

@ -108,10 +108,8 @@ def fetch_apec(
data_root: Path = typer.Option(
Path("data"),
"--data-root",
exists=True,
file_okay=False,
dir_okay=True,
readable=True,
help="Directory containing candidate-profile.yaml and Apec run artifacts.",
),
) -> None:
@ -211,11 +209,20 @@ def fetch_apec(
listing_errors=listing_errors,
)
artifact_write_errors: list[str] = []
try:
_write_yaml(paths["listings"], [listing.model_dump(mode="json") for listing in deduplicated_listings])
except OSError as exc: # pragma: no cover - defensive boundary
artifact_write_errors.append(f"listings.yaml: {exc}")
try:
_write_yaml(paths["run_meta"], run_meta.model_dump(mode="json"))
except OSError as exc: # pragma: no cover - defensive boundary
typer.echo(f"Unable to write Apec run artifacts: {exc}", err=True)
artifact_write_errors.append(f"run-meta.yaml: {exc}")
if artifact_write_errors:
typer.echo(f"Unable to write Apec run artifacts: {'; '.join(artifact_write_errors)}", err=True)
raise typer.Exit(code=1)
typer.echo(

View File

@ -22,9 +22,9 @@ def test_derive_apec_queries_preserves_order_dedupes_and_caps_at_five() -> None:
assert queries == [
"Data Engineer",
"Analytics Engineer",
"Data Engineer Python",
"Data Engineer SQL",
"Data Engineer BigQuery",
"BI Engineer",
"Junior Data Platform Engineer",
"ML Engineer",
]
@ -51,7 +51,7 @@ def test_derive_apec_queries_uses_up_to_five_target_roles_when_no_support_terms_
]
def test_derive_apec_queries_includes_constraints_in_support_term_queries() -> None:
def test_derive_apec_queries_uses_support_terms_without_constraints() -> None:
profile = CandidateProfileOutput(
target_roles=["Data Engineer"],
strengths=["Python"],
@ -65,6 +65,4 @@ def test_derive_apec_queries_includes_constraints_in_support_term_queries() -> N
"Data Engineer",
"Data Engineer Python",
"Data Engineer BigQuery",
"Data Engineer CDI",
"Data Engineer France",
]

View File

@ -95,21 +95,19 @@ def _snapshot_meta(
def test_fetch_apec_fails_cleanly_when_candidate_profile_is_missing(tmp_path, monkeypatch) -> None:
data_root = tmp_path / "data"
data_root.mkdir()
class SpyApecAdapter:
def __init__(self, max_listings: int = 50) -> None:
raise AssertionError("adapter should not be created when the profile is missing")
monkeypatch.chdir(tmp_path)
monkeypatch.setattr("job_research.cli.ApecAdapter", SpyApecAdapter)
result = CliRunner().invoke(app, ["fetch-apec", "--data-root", str(data_root)])
result = CliRunner().invoke(app, ["fetch-apec"])
assert result.exit_code == 1
assert "candidate-profile.yaml" in result.stderr
assert "Traceback" not in result.stderr
assert not (data_root / "apec").exists()
assert not (tmp_path / "data").exists()
def test_fetch_apec_fails_cleanly_when_candidate_profile_yaml_is_malformed(
@ -341,6 +339,14 @@ def test_fetch_apec_reports_final_artifact_write_failures_cleanly(
assert adapter.search_calls == [["Role From YAML"]]
assert adapter.fetch_calls == ["https://example.test/job/123"]
run_dir = data_root / "apec" / "runs" / "2026-06-01T10-00-00Z"
if failing_filename == "listings.yaml":
assert not (run_dir / "listings.yaml").exists()
assert (run_dir / "run-meta.yaml").exists()
else:
assert (run_dir / "listings.yaml").exists()
assert not (run_dir / "run-meta.yaml").exists()
def test_fetch_apec_reads_profile_and_writes_run_artifacts(tmp_path, monkeypatch) -> None:
data_root = tmp_path / "data"