EastSync-AI / UI /render_employee_card.py
StanSava's picture
switch to gemini model, update supabase_model managed agent query and update the flow (#10)
5df0d69
from __future__ import annotations
import html
from typing import Any, Iterable, Mapping, Sequence
# Placeholder for models.EmployeeTrainingPlan if not provided
class EmployeeTrainingPlan:
def __init__(self):
pass
def _as_mapping(member: EmployeeTrainingPlan | Mapping[str, Any]) -> Mapping[str, Any]:
if isinstance(member, Mapping):
return member
# Assuming the actual EmployeeTrainingPlan model has these attributes or model_dump
if hasattr(member, "model_dump"):
return member.model_dump()
return {
"employee_name": getattr(member, "employee_name", "Unknown employee"),
"role": getattr(member, "role", "Unknown role"),
"skills_gaps": getattr(member, "skills_gaps", []),
"training_plan": getattr(member, "training_plan", []),
}
def _render_skill_gaps(gaps: Sequence[Mapping[str, Any]] | None) -> str:
if not gaps:
return "<p>No notable skill gaps.</p>"
items = []
for gap in gaps:
skill = html.escape(str(gap.get("skill", "Unknown skill")))
raw_magnitude = gap.get("gap")
try:
magnitude_value = max(0.0, min(10.0, float(raw_magnitude)))
magnitude = f"{magnitude_value:.1f}"
except (TypeError, ValueError):
magnitude_value = None
magnitude = html.escape(str(raw_magnitude or "N/A"))
severity = "High"
if magnitude_value is not None:
if magnitude_value < 4:
severity = "Low"
elif magnitude_value < 7:
severity = "Medium"
# progress_pct is not used in the current HTML but kept for context if needed
# progress_pct = (
# f"{(magnitude_value or 0) / 10 * 100:.0f}"
# if magnitude_value is not None
# else "0"
# )
items.append(
"<li>"
f'<div class="skill-gap-item">'
f'<span class="skill-name">{skill}</span>'
f'<span class="gap-pill" data-severity="{severity.lower()}">Gap {magnitude}</span>'
"</div>"
"</li>"
)
return f'<ul class="skill-gap-list" role="list">{"".join(items)}</ul>'
def _render_training_plan(resources: Iterable[Mapping[str, Any]] | None) -> str:
if not resources:
return "<p>No training plan yet.</p>"
cards = []
for resource in resources:
title = html.escape(str(resource.get("title", "Training resource")))
url = resource.get("url")
provider = resource.get("provider") or resource.get("platform")
duration = resource.get("duration_hours") or resource.get("duration")
cost = resource.get("cost")
cost_text = "Free" if cost in (None, "", 0) else f"${cost}"
skills = resource.get("skills_covered") or resource.get("skills") or []
link = (
f'<a href="{html.escape(url)}" target="_blank" rel="noreferrer">{title}</a>'
if url
else title
)
rows = [f'<div class="resource-title">{link}</div>']
meta_bits = []
if provider:
meta_bits.append(html.escape(str(provider)))
if duration:
meta_bits.append(html.escape(str(duration)))
if cost_text:
meta_bits.append(html.escape(cost_text))
if meta_bits:
rows.append(f'<div class="resource-meta">{" · ".join(meta_bits)}</div>')
if skills:
chips = "".join(
f'<span class="skill-chip">{html.escape(str(skill))}</span>'
for skill in skills
)
rows.append(f'<div class="resource-skills">{chips}</div>')
cards.append(f'<li>{"".join(rows)}</li>')
return f'<ul class="training-plan-list" role="list">{"".join(cards)}</ul>'
def render_employee_card(member: EmployeeTrainingPlan | Mapping[str, Any]) -> str:
data = _as_mapping(member)
name = html.escape(str(data.get("employee_name", "Unknown employee")))
role = html.escape(str(data.get("role") or "Not provided"))
skill_gap_html = _render_skill_gaps(data.get("skills_gaps"))
training_plan_html = _render_training_plan(data.get("training_plan"))
return f"""
<div class="employee-card">
<div class="employee-title">{name}</div>
<div class="employee-meta">{role}</div>
<div class="employee-section">
<span class="section-label">Skill Gap</span>
{skill_gap_html}
</div>
<div class="employee-section">
<span class="section-label">Training Plan</span>
{training_plan_html}
</div>
</div>
"""