feat: add canonical profile merger with warnings
This commit is contained in:
parent
042feab4fd
commit
3c331ef687
56
src/job_research/profile/merge.py
Normal file
56
src/job_research/profile/merge.py
Normal file
@ -0,0 +1,56 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from job_research.models import (
|
||||
CandidateProfileOutput,
|
||||
EducationEntry,
|
||||
ExperienceEntry,
|
||||
WarningItem,
|
||||
)
|
||||
from job_research.profile.profile_parser import AuthoredProfile
|
||||
|
||||
|
||||
def build_candidate_profile_output(
|
||||
cv_signals: dict[str, object], authored: AuthoredProfile
|
||||
) -> CandidateProfileOutput:
|
||||
warnings: list[WarningItem] = []
|
||||
|
||||
if "years_of_experience" in cv_signals and any(
|
||||
"experience" in note.lower() for note in authored.notes
|
||||
):
|
||||
warnings.append(
|
||||
WarningItem(
|
||||
field="years_of_experience",
|
||||
message=(
|
||||
"CV-derived experience may differ from markdown interpretation. "
|
||||
"Review manually."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
merged_skills: list[str] = []
|
||||
for skill in [
|
||||
*(cv_signals.get("skills") or []),
|
||||
*authored.strengths,
|
||||
*authored.skills_to_emphasize,
|
||||
]:
|
||||
if skill not in merged_skills:
|
||||
merged_skills.append(skill)
|
||||
|
||||
experience_entries = cv_signals.get("experience_entries") or []
|
||||
education_entries = cv_signals.get("education_entries") or []
|
||||
|
||||
return CandidateProfileOutput(
|
||||
name=cv_signals.get("name"),
|
||||
summary=authored.summary,
|
||||
target_roles=authored.target_roles,
|
||||
strengths=authored.strengths,
|
||||
skills_to_emphasize=authored.skills_to_emphasize,
|
||||
constraints=authored.constraints,
|
||||
notes=authored.notes,
|
||||
location=cv_signals.get("location"),
|
||||
languages=cv_signals.get("languages") or [],
|
||||
skills=merged_skills,
|
||||
experience_entries=[ExperienceEntry.model_validate(item) for item in experience_entries],
|
||||
education_entries=[EducationEntry.model_validate(item) for item in education_entries],
|
||||
warnings=warnings,
|
||||
)
|
||||
28
tests/profile/test_merge.py
Normal file
28
tests/profile/test_merge.py
Normal file
@ -0,0 +1,28 @@
|
||||
from job_research.profile.merge import build_candidate_profile_output
|
||||
from job_research.profile.profile_parser import AuthoredProfile
|
||||
|
||||
|
||||
def test_build_candidate_profile_output_writes_warning_when_facts_conflict() -> None:
|
||||
cv_signals = {
|
||||
"name": "Tonio",
|
||||
"location": "France",
|
||||
"languages": ["French", "English"],
|
||||
"skills": ["Python", "SQL"],
|
||||
"experience_entries": [{"title": "Data Engineer", "company": "A"}],
|
||||
"education_entries": [],
|
||||
"years_of_experience": 2,
|
||||
}
|
||||
authored = AuthoredProfile(
|
||||
summary="Junior data engineer focused on GCP.",
|
||||
target_roles=["Data Engineer"],
|
||||
strengths=["Python"],
|
||||
skills_to_emphasize=["BigQuery", "GCP"],
|
||||
constraints=["CDI only"],
|
||||
notes=["Years of experience feels closer to 3."],
|
||||
)
|
||||
|
||||
output = build_candidate_profile_output(cv_signals, authored)
|
||||
|
||||
assert output.summary == "Junior data engineer focused on GCP."
|
||||
assert output.constraints == ["CDI only"]
|
||||
assert output.warnings[0].field == "years_of_experience"
|
||||
Loading…
x
Reference in New Issue
Block a user