File size: 4,079 Bytes
c643b04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from __future__ import annotations

from datetime import datetime, timezone


def _float(row: dict, key: str, default: float = 0.0) -> float:
    try:
        return float(row.get(key, default) or default)
    except (TypeError, ValueError):
        return default


def composite_score(row: dict, spec: dict) -> float:
    weights = spec.get("scoring_weights") or {}
    score = (
        weights.get("retrieval_rrf", 0.14) * _float(row, "retrieval_rrf")
        + weights.get("career_evidence", 0.38) * _float(row, "career_evidence")
        + weights.get("title_tier", 0.13) * _float(row, "title_tier_score")
        + weights.get("yoe_location_fit", 0.09) * _float(row, "yoe_location_fit")
        + weights.get("skill_trust", 0.07) * _float(row, "skill_trust")
        + weights.get("assessment_score", 0.05) * _float(row, "assessment_score")
        + weights.get("product_company", 0.04) * _float(row, "product_company_score")
        + weights.get("education_score", 0.04) * _float(row, "education_score")
        + weights.get("company_scale_score", 0.03) * _float(row, "company_scale_score")
        + weights.get("work_mode_fit", 0.02) * _float(row, "work_mode_fit")
        + weights.get("platform_activity_score", 0.02) * _float(row, "platform_activity_score")
        - weights.get("anti_pattern_penalty", 0.12) * _float(row, "anti_pattern_penalty")
        + _float(row, "rank_time_bonus")
    )
    return max(0.0, min(1.0, score))


def _recency_multiplier(last_active: str | None) -> float:
    if not last_active:
        return 0.78
    try:
        dt = datetime.fromisoformat(str(last_active).replace("Z", "+00:00"))
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        days = (datetime.now(timezone.utc) - dt).days
    except (TypeError, ValueError):
        return 0.82
    if days <= 30:
        return 1.00
    if days <= 90:
        return 0.93
    if days <= 180:
        return 0.84
    if days <= 365:
        return 0.76
    return 0.68


def behavioral_multiplier(row: dict) -> float:
    mult = _recency_multiplier(row.get("last_active_date"))

    response = _float(row, "recruiter_response_rate")
    if response >= 0.60:
        mult *= 1.06
    elif response < 0.10:
        mult *= 0.76
    elif response < 0.25:
        mult *= 0.90

    avg_hours = _float(row, "avg_response_time_hours", 48)
    if avg_hours and avg_hours <= 12:
        mult *= 1.02
    elif avg_hours > 96:
        mult *= 0.95

    notice = int(_float(row, "notice_period_days", 90))
    if notice <= 30:
        mult *= 1.05
    elif notice > 90:
        mult *= 0.90

    if row.get("open_to_work_flag"):
        mult *= 1.04
    if row.get("willing_to_relocate"):
        mult *= 1.02

    completeness = _float(row, "profile_completeness_score")
    if completeness >= 80:
        mult *= 1.02
    elif completeness < 40:
        mult *= 0.95

    if _float(row, "github_activity_score", -1) >= 50:
        mult *= 1.02
    if _float(row, "saved_by_recruiters_30d") >= 3:
        mult *= 1.03
    if _float(row, "interview_completion_rate") >= 0.85:
        mult *= 1.02
    if 0 <= _float(row, "offer_acceptance_rate", -1) < 0.25:
        mult *= 0.96
    if _float(row, "verified_trust") >= 0.66:
        mult *= 1.01
    if _float(row, "work_mode_fit") >= 0.90:
        mult *= 1.02
    if _float(row, "platform_activity_score") >= 0.50:
        mult *= 1.02

    return max(0.70, min(1.15, mult))


def final_score(row: dict, spec: dict) -> float:
    base = composite_score(row, spec)
    return base * behavioral_multiplier(row)


def monotonic_submission_scores(raw_scores: list[float]) -> list[float]:
    if not raw_scores:
        return []
    hi, lo = max(raw_scores), min(raw_scores)
    if hi == lo:
        return [round(max(0.01, 0.99 - i * 0.001), 4) for i in range(len(raw_scores))]
    scaled = [0.20 + 0.79 * (s - lo) / (hi - lo) for s in raw_scores]
    out = [min(0.99, scaled[0])]
    for score in scaled[1:]:
        out.append(min(out[-1] - 0.0001, score))
    return [round(max(0.01, s), 4) for s in out]