Jerlshin's picture
refined progress and defect fix
761d149
Raw
History Blame Contribute Delete
6.32 kB
from __future__ import annotations
from collections.abc import Mapping
from datetime import date
from types import MappingProxyType
from redstack.domain.enums import EvidenceKind, InstitutionTier
from redstack.domain.ids import UnitScore
from redstack.domain.source import RawCandidate, RawEducation
from redstack.features.normalize import normalize_text
from redstack.features.parsing import (
FeatureCell,
FeatureId,
clamp_unit,
feature_id,
make_cell,
mint_evidence,
)
__feature_version__ = "1.1.0"
_GROUP = "edu"
EDU_TIER_SCORE: FeatureId = feature_id(_GROUP, "tier_score")
EDU_FIELD_RELEVANCE: FeatureId = feature_id(_GROUP, "field_relevance")
EDU_TIMELINE_VALID: FeatureId = feature_id(_GROUP, "timeline_valid")
# Tier -> score. tier_1 strongest; UNKNOWN is a neutral-low prior, never 0
# (an unrecognized institution must not be penalised as if it were tier-4).
_TIER_SCORE: Mapping[InstitutionTier, float] = MappingProxyType(
{
InstitutionTier.TIER_1: 1.0,
InstitutionTier.TIER_2: 0.75,
InstitutionTier.TIER_3: 0.5,
InstitutionTier.TIER_4: 0.25,
InstitutionTier.UNKNOWN: 0.4,
}
)
# Field relevance lexicon: normalized substrings -> relevance weight. The JD's
# stack is retrieval / ranking / ML / IR; adjacent quantitative fields earn
# partial credit, unrelated fields stay low.
_FIELD_RELEVANCE: Mapping[str, float] = MappingProxyType(
{
"machine learning": 1.0,
"artificial intelligence": 1.0,
"information retrieval": 1.0,
"natural language processing": 1.0,
"computer science": 0.9,
"data science": 0.9,
"computational": 0.8,
"statistics": 0.7,
"applied mathematics": 0.65,
"mathematics": 0.6,
"information systems": 0.6,
"software engineering": 0.6,
"electrical engineering": 0.5,
"electronics": 0.45,
"physics": 0.45,
"engineering": 0.4,
}
)
# Earliest plausible matriculation year; guards against absurd start years while
# tolerating non-linear paths. Upper bound is the injected ``as_of`` year.
_MIN_PLAUSIBLE_START_YEAR = 1950
# A single degree spanning more than this many years is implausible.
_MAX_DEGREE_SPAN_YEARS = 15
def _field_relevance(field_of_study: str) -> float:
"""Best substring match of a normalized field against the relevance lexicon."""
normalized = normalize_text(field_of_study)
best = 0.0
for needle, weight in _FIELD_RELEVANCE.items():
if needle in normalized and weight > best:
best = weight
return best
def _timeline_ok(edu: RawEducation, as_of_year: int) -> bool:
"""True when one record's years are internally and externally plausible."""
if edu.end_year < edu.start_year:
return False
if edu.start_year < _MIN_PLAUSIBLE_START_YEAR:
return False
if edu.end_year > as_of_year:
return False
if (edu.end_year - edu.start_year) > _MAX_DEGREE_SPAN_YEARS:
return False
return True
def extract_education(
raw: RawCandidate,
as_of: date,
) -> Mapping[FeatureId, FeatureCell]:
"""Extract the ``edu.*`` cells for one candidate.
Deterministic and total: a candidate with no education records yields
neutral-low values at reduced confidence (never an error); an impossible
timeline drives ``edu.timeline_valid`` to ``0.0`` for the Integrity Engine.
"""
records = raw.education
cells: dict[FeatureId, FeatureCell] = {}
if not records:
# No structured education: neutral-low priors, low confidence. Evidence
# is the (empty) education arity, anchored on a field that always exists.
no_edu = mint_evidence(
raw, EvidenceKind.PROFILE_FIELD, "profile.years_of_experience"
)
cells[EDU_TIER_SCORE] = make_cell(0.0, 0.3, (no_edu,))
cells[EDU_FIELD_RELEVANCE] = make_cell(0.0, 0.3, (no_edu,))
cells[EDU_TIMELINE_VALID] = make_cell(1.0, 0.3, (no_edu,))
return MappingProxyType(cells)
as_of_year = as_of.year
# --- edu.tier_score: best tier across records --------------------------- #
best_tier_idx = 0
best_tier_value = -1.0
for idx, edu in enumerate(records):
score = _TIER_SCORE[edu.tier]
if score > best_tier_value:
best_tier_value = score
best_tier_idx = idx
tier_evidence = mint_evidence(
raw, EvidenceKind.EDUCATION, f"education[{best_tier_idx}].tier"
)
cells[EDU_TIER_SCORE] = make_cell(
best_tier_value, clamp_unit(0.95), (tier_evidence,)
)
# --- edu.field_relevance: best field across records --------------------- #
best_field_idx = 0
best_field_value = -1.0
for idx, edu in enumerate(records):
relevance = _field_relevance(edu.field_of_study)
if relevance > best_field_value:
best_field_value = relevance
best_field_idx = idx
field_evidence = mint_evidence(
raw, EvidenceKind.EDUCATION, f"education[{best_field_idx}].field_of_study"
)
# Confidence is high when we matched the lexicon, lower for an unrecognized
# field (structured but semantically opaque).
field_confidence = 0.9 if best_field_value > 0.0 else 0.5
cells[EDU_FIELD_RELEVANCE] = make_cell(
max(best_field_value, 0.0), field_confidence, (field_evidence,)
)
# --- edu.timeline_valid: every record must be plausible ----------------- #
timeline_valid = True
first_bad_idx = 0
for idx, edu in enumerate(records):
if not _timeline_ok(edu, as_of_year):
timeline_valid = False
first_bad_idx = idx
break
anchor_idx = first_bad_idx if not timeline_valid else 0
timeline_evidence = (
mint_evidence(raw, EvidenceKind.EDUCATION, f"education[{anchor_idx}].start_year"),
mint_evidence(
raw, EvidenceKind.EDUCATION, f"education[{anchor_idx}].end_year",
as_of=as_of,
),
)
cells[EDU_TIMELINE_VALID] = make_cell(
1.0 if timeline_valid else 0.0,
clamp_unit(0.98),
timeline_evidence,
)
return MappingProxyType(cells)
__all__: tuple[str, ...] = (
"EDU_FIELD_RELEVANCE",
"EDU_TIER_SCORE",
"EDU_TIMELINE_VALID",
"extract_education",
)