File size: 1,881 Bytes
7498f2c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations
from typing import List, Tuple, Set
from .text import extract_keywords_from_text


def allowed_keywords_from_profile(skills: List[str], experiences: List) -> Set[str]:
    allowed = set(s.lower() for s in skills)
    for e in experiences:
        for t in getattr(e, "technologies", []) or []:
            allowed.add(str(t).lower())
        for a in getattr(e, "achievements", []) or []:
            for k in extract_keywords_from_text(a, top_k=5):
                allowed.add(k.lower())
    return allowed


def clamp_to_allowed_keywords(text: str, allowed: Set[str]) -> Tuple[str, List[str]]:
    used = []
    # Retain only keywords that are allowed
    kws = extract_keywords_from_text(text, top_k=80)
    for k in kws:
        if k.lower() in allowed:
            used.append(k)
    return text, used


def detect_contradictions(resume_text: str, letter_text: str, allowed: Set[str]) -> List[str]:
    # Simple heuristic: keywords in letter not in resume nor allowed -> potential contradiction
    resume_k = set(k.lower() for k in extract_keywords_from_text(resume_text, top_k=100))
    letter_k = set(k.lower() for k in extract_keywords_from_text(letter_text, top_k=100))
    issues = []
    for k in letter_k:
        if k not in resume_k and k not in allowed:
            issues.append(k)
    return issues


def coverage_score(text: str, target_keywords: List[str]) -> float:
    if not target_keywords:
        return 1.0
    lower = text.lower()
    hits = sum(1 for k in target_keywords if k.lower() in lower)
    return hits / max(1, len(target_keywords))


def conciseness_score(text: str, max_chars: int) -> float:
    # 1.0 if within limit; decay if exceeded
    if len(text) <= max_chars:
        return 1.0
    return max(0.0, 1.0 - (len(text) - max_chars) / (max_chars * 0.5))