feat: add canonical candidate profile output model
This commit is contained in:
parent
a0522c316e
commit
1b4e901afe
37
src/job_research/models.py
Normal file
37
src/job_research/models.py
Normal file
@ -0,0 +1,37 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ExperienceEntry(BaseModel):
|
||||
company: str
|
||||
title: str
|
||||
start: str | None = None
|
||||
end: str | None = None
|
||||
highlights: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class EducationEntry(BaseModel):
|
||||
institution: str
|
||||
credential: str
|
||||
start: str | None = None
|
||||
end: str | None = None
|
||||
|
||||
|
||||
class WarningItem(BaseModel):
|
||||
field: str
|
||||
message: str
|
||||
|
||||
|
||||
class CandidateProfileOutput(BaseModel):
|
||||
name: str | None = None
|
||||
summary: str | None = None
|
||||
target_roles: list[str] = Field(default_factory=list)
|
||||
strengths: list[str] = Field(default_factory=list)
|
||||
skills_to_emphasize: list[str] = Field(default_factory=list)
|
||||
constraints: list[str] = Field(default_factory=list)
|
||||
notes: list[str] = Field(default_factory=list)
|
||||
location: str | None = None
|
||||
languages: list[str] = Field(default_factory=list)
|
||||
skills: list[str] = Field(default_factory=list)
|
||||
experience_entries: list[ExperienceEntry] = Field(default_factory=list)
|
||||
education_entries: list[EducationEntry] = Field(default_factory=list)
|
||||
warnings: list[WarningItem] = Field(default_factory=list)
|
||||
18
src/job_research/storage.py
Normal file
18
src/job_research/storage.py
Normal file
@ -0,0 +1,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
from job_research.models import CandidateProfileOutput
|
||||
|
||||
|
||||
def save_candidate_profile_yaml(path: Path, profile: CandidateProfileOutput) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
payload = profile.model_dump(mode="json")
|
||||
path.write_text(yaml.safe_dump(payload, sort_keys=False, allow_unicode=True), encoding="utf-8")
|
||||
|
||||
|
||||
def load_yaml(path: Path) -> dict[str, Any]:
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
29
tests/test_storage.py
Normal file
29
tests/test_storage.py
Normal file
@ -0,0 +1,29 @@
|
||||
from job_research.models import CandidateProfileOutput, ExperienceEntry, WarningItem
|
||||
from job_research.storage import save_candidate_profile_yaml, load_yaml
|
||||
|
||||
|
||||
def test_save_candidate_profile_yaml_round_trips_readable_output(tmp_path) -> None:
|
||||
profile = CandidateProfileOutput(
|
||||
name="Tonio",
|
||||
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."],
|
||||
location="France",
|
||||
languages=["French", "English"],
|
||||
skills=["Python", "SQL", "Terraform", "GCP", "BigQuery"],
|
||||
experience_entries=[ExperienceEntry(company="A", title="Data Engineer")],
|
||||
education_entries=[],
|
||||
warnings=[WarningItem(field="years_of_experience", message="CV and profile disagree.")],
|
||||
)
|
||||
|
||||
out = tmp_path / "candidate-profile.yaml"
|
||||
save_candidate_profile_yaml(out, profile)
|
||||
|
||||
payload = load_yaml(out)
|
||||
|
||||
assert payload["name"] == "Tonio"
|
||||
assert payload["constraints"] == ["CDI only", "France only"]
|
||||
assert payload["warnings"][0]["field"] == "years_of_experience"
|
||||
Loading…
x
Reference in New Issue
Block a user