Jerlshin's picture
refined progress and defect fix
761d149
Raw
History Blame Contribute Delete
1.52 kB
from __future__ import annotations
from typing import final
from pydantic import BaseModel, ConfigDict, Field, model_validator
from redstack.domain.enums import EligibilityCode
from redstack.domain.ids import AnchorId, ArchetypeId
@final
class JobDescriptionSpec(BaseModel):
"""Frozen, parsed JD: experience band, hub set, anchors, gate codes."""
model_config = ConfigDict(
frozen=True, extra="forbid", str_strip_whitespace=True, validate_default=True
)
role_title: str = Field(min_length=1)
min_experience_years: float = Field(ge=0.0, le=50.0, allow_inf_nan=False)
max_experience_years: float = Field(ge=0.0, le=50.0, allow_inf_nan=False)
preferred_hubs: frozenset[str]
positive_anchors: frozenset[AnchorId]
negative_anchors: frozenset[AnchorId]
hard_disqualifiers: frozenset[EligibilityCode]
soft_penalties: frozenset[EligibilityCode]
target_archetypes: frozenset[ArchetypeId]
@model_validator(mode="after")
def _check(self) -> JobDescriptionSpec:
if self.min_experience_years > self.max_experience_years:
raise ValueError("min_experience_years exceeds max_experience_years")
if self.positive_anchors & self.negative_anchors:
raise ValueError("positive and negative anchors must be disjoint")
if self.hard_disqualifiers & self.soft_penalties:
raise ValueError("hard and soft eligibility codes must be disjoint")
return self
__all__: tuple[str, ...] = ("JobDescriptionSpec",)