"""
def create_candidate_card(item: SearchResultItem) -> str:
s = item.scores if item.scores is not None else MatchScores()
bars = ""
bars += _score_bar("Overall", s.overall)
bars += _score_bar("Skill", s.skill_match)
bars += _score_bar("Experience", s.experience_match)
bars += _score_bar("Semantic", s.semantic_similarity)
bars += _score_bar("Keyword", s.keyword_match)
if s.education_match is not None:
bars += _score_bar("Education", s.education_match)
if s.cross_encoder_score is not None:
bars += _score_bar("AI Rerank", s.cross_encoder_score)
if s.behavioral_score is not None:
bars += _score_bar("Behavioral", s.behavioral_score)
if s.career_trajectory_score is not None:
bars += _score_bar("Career", s.career_trajectory_score)
if s.skill_proficiency_score is not None:
bars += _score_bar("Proficiency", s.skill_proficiency_score)
skills_html = "".join(
f'{skill}' for skill in item.matched_skills[:8]
)
missing_html = "".join(
f'{skill}' for skill in item.missing_skills[:5]
)
return f"""
#{item.rank}{item.name}{item.profile_id}
{item.current_title or 'N/A'}
{(
f' at '
f'{item.current_company}'
if item.current_company else ""
)}
{item.location or 'Location N/A'}
{_bullet_years(item.experience_years)}
{_score_badge(s.overall)}
SCORE BREAKDOWN
confidence: {s.confidence:.0%}
{bars}
{skills_html}
{missing_html}
"""
def create_score_radar_chart(scores: dict) -> str:
dims = ["Skill", "Experience", "Semantic", "Keyword", "Confidence"]
dim_keys = [
"skill_match", "experience_match",
"semantic_similarity", "keyword_match", "confidence",
]
values = [int(scores.get(k, 0) * 100) for k in dim_keys]
cx, cy, r = 100, 100, 80
angles = [math.radians(90 - i * 72) for i in range(5)]
outer_points = " ".join(
f"{cx + r * math.cos(a):.1f},{cy - r * math.sin(a):.1f}" for a in angles
)
data_points = " ".join(
f"{cx + v * r / 100 * math.cos(a):.1f},{cy - v * r / 100 * math.sin(a):.1f}"
for v, a in zip(values, angles)
)
labels = "".join(
f'{dim}'
for dim, a in zip(dims, angles)
)
grid_lines = ""
for frac in [0.25, 0.5, 0.75]:
pts = " ".join(
f"{cx + r * frac * math.cos(a):.1f},{cy - r * frac * math.sin(a):.1f}" for a in angles
)
grid_lines += f''
return f"""
"""
def create_skill_match_table(rationale: Rationale) -> str:
rows = ""
for sd in rationale.skill_details:
icon = "\u2705" if sd.found else "\u274c"
rows += f"
{icon}
{sd.skill}
{sd.evidence}
"
return f"""
Status
Skill
Evidence
{rows}
"""
def create_analytics_dashboard(results_json: str = "[]") -> str:
import json
from src.core.models import MatchResult, MatchScores
from src.fairness.metrics import (
compute_all_fairness_metrics,
)
if not results_json or results_json.strip() in ("[]", "", "{}"):
return create_empty_analytics()
try:
raw = json.loads(results_json) if results_json else []
except (json.JSONDecodeError, TypeError):
raw = []
total = len(raw)
listwise_ranked = any(r.get("listwise_ranked", False) for r in raw) if raw else False
listwise_badge = '🏆 Listwise Ranked' if listwise_ranked else ""
match_results = []
for r in raw:
if isinstance(r, dict):
scores_dict = r.get("scores", {})
if not isinstance(scores_dict, dict):
scores_dict = {}
match_scores = MatchScores(
overall=float(scores_dict.get("overall", r.get("_re_score", 0)) or 0),
semantic_similarity=float(scores_dict.get("semantic_similarity") or 0),
keyword_match=float(scores_dict.get("keyword_match") or 0),
skill_match=float(scores_dict.get("skill_match", 0) or 0),
experience_match=float(scores_dict.get("experience_match", 0) or 0),
location_match=float(scores_dict.get("location_match") or 0)
if scores_dict.get("location_match") is not None else None,
education_match=float(scores_dict.get("education_match") or 0)
if scores_dict.get("education_match") is not None else None,
confidence=float(scores_dict.get("confidence", 0) or 0),
)
match_results.append(
MatchResult(
query_id="",
profile_id=r.get("profile_id", ""),
rank=r.get("rank", 1),
name=r.get("name", ""),
scores=match_scores,
matched_skills=r.get("matched_skills", []),
missing_skills=r.get("missing_skills", []),
)
)
scores = [m.scores.overall for m in match_results if m.scores.overall > 0]
if scores:
avg_score = sum(scores) / len(scores)
max_score = max(scores)
min_score = min(scores)
bins = [0] * 10
for s in scores:
idx = min(9, int(s * 10))
bins[idx] += 1
max_bin = max(bins) or 1
pastel_colors = [
"#fbcfe8", "#fed7aa", "#fde68a", "#a7f3d0",
"#bfdbfe", "#c4b5fd", "#ddd6fe", "#fbcfe8",
"#fed7aa", "#a7f3d0"
]
bar_chart = "".join(
f''
for i, b in enumerate(bins)
)
else:
avg_score = max_score = min_score = 0
bar_chart = '
No scores to display
'
metric_cards = ""
if len(match_results) >= 3:
profiledict = _get_bias_profiles(match_results)
fairness = compute_all_fairness_metrics(match_results, profiledict)
dp = fairness.get("demographic_parity", {})
lang_bias = fairness.get("language_bias", {})
def _metric_card(label, value, threshold, format_str="{:.3f}"):
val = value if isinstance(value, (int, float)) else 0
if val < threshold:
cls = "pastel-green"
color = "#059669"
status = "✅ No bias detected"
elif val < threshold * 2:
cls = "pastel-amber"
color = "#d97706"
status = "👀 Monitor closely"
else:
cls = "pastel-rose"
color = "#e11d48"
status = "⚠️ Bias detected"
return f"""
"""
def _build_distribution_table(match_results: list[MatchResult]) -> str:
strong = sum(1 for m in match_results if m.scores.overall >= 0.8)
good = sum(1 for m in match_results if 0.6 <= m.scores.overall < 0.8)
potential = sum(1 for m in match_results if 0.4 <= m.scores.overall < 0.6)
weak = sum(1 for m in match_results if m.scores.overall < 0.4)
total = len(match_results) or 1
return f"""
{strong}
Strong ({strong*100//total}%)
{good}
Good ({good*100//total}%)
{potential}
Potential ({potential*100//total}%)
{weak}
Weak ({weak*100//total}%)
"""
def _get_bias_profiles(match_results: list[MatchResult]) -> dict[str, Profile]:
"""Build minimal Profile objects for bias detection from match results."""
from src.core.models import Location, PersonalInfo, Profile, ProfileMetadata
return {
m.profile_id: Profile(
profile_id=m.profile_id,
personal=PersonalInfo(
name=m.name or "",
location=Location(city=m.location),
languages_spoken=[],
),
metadata=ProfileMetadata(language_detected="en"),
)
for m in match_results
}
def create_rationale_panel(rationale: Rationale | None, profile_summary: str) -> str:
if rationale is None:
return ""
color = MATCH_COLORS.get(rationale.recommendation.value, "#a78bfa")
summary = rationale.summary or "No summary available."
strengths_html = "".join(
f"
{s}
" for s in rationale.strengths[:5]
)
gaps_html = "".join(
f"
{g}
" for g in rationale.gaps[:5]
)
return f"""
Rationale: {profile_summary}
{summary}
✓ Strengths
{strengths_html}
✗ Gaps
{gaps_html}
{rationale.recommendation.value}
"""
# ── Progressive Loading Steps ──────────────────────────────────────────
LOADING_STEPS = [
("🔍", "Parsing query", "Understanding job requirements, skills, and context"),
("📡", "Searching index", "Scanning 100K+ profiles with hybrid search"),
("⚡", "AI reranking", "Cross-encoder scoring for precision matching"),
("📊", "Computing scores", "Multi-signal evaluation across 6 dimensions"),
("🎯", "Building results", "Assembling ranked shortlist with rationales"),
]
LOADING_STEP_TIMING = [0.15, 0.40, 0.60, 0.80, 1.0]
def _step_class(i: int, current_step: int) -> str:
if current_step < 0:
return "completed"
if i < current_step:
return "completed"
if i == current_step:
return "active"
return ""
def _step_extra(i: int, current_step: int, desc: str) -> str:
if i == current_step and 0 <= current_step < len(LOADING_STEPS):
return f' {desc}'
return ""
def create_progress_html(current_step: int = 0) -> str:
if current_step < 0:
steps_html = "".join(
f'
'
f'{icon}{label}
'
for icon, label, _ in LOADING_STEPS
)
pct = 100
else:
steps_html = "".join(
f'
'
f'{icon}{label}'
f'{_step_extra(i, current_step, desc)}'
f'
'
for i, (icon, label, desc) in enumerate(LOADING_STEPS)
)
pct = int(sum(LOADING_STEP_TIMING[:current_step + 1]) / len(LOADING_STEPS) * 100)
step_label = LOADING_STEPS[current_step][1] if 0 <= current_step < len(LOADING_STEPS) else "Complete"
step_desc = LOADING_STEPS[current_step][2] if 0 <= current_step < len(LOADING_STEPS) else ""
icon = "✅" if current_step < 0 else "⏳"
return f"""