from talent.plugins import extract_education, extract_experience def serialize_talent_run(row) -> dict: return { "id": str(row.id) if row.id else None, "job_post_id": str(row.job_post_id) if row.job_post_id else None, "status": row.status, "actor_id": row.actor_id, "search_input": row.search_input or {}, "max_results": row.max_results, "profiles_found": row.profiles_found, "cost_usd": row.cost_usd or 0, "apify_run_id": row.apify_run_id, "apify_error": row.apify_error, "started_at": row.started_at.isoformat() if row.started_at else None, "finished_at": row.finished_at.isoformat() if row.finished_at else None, "created_at": row.created_at.isoformat() if row.created_at else None, } def serialize_talent_profile(row, *, user_names=None) -> dict: # `raw` stays server-side: it is an actor-shaped blob that can be large and # is only needed for debugging/re-mapping, not for the profile cards. # `user_names` maps str(user_id) -> name for the shortlisted_by/contacted_by # stamps (resolved by the caller in one query, Users.names_by_ids). names = user_names or {} return { "id": str(row.id) if row.id else None, "job_post_id": str(row.job_post_id) if row.job_post_id else None, "linkedin_url": row.linkedin_url, "public_id": row.public_id, "full_name": row.full_name, "headline": row.headline, "location": row.location, "current_title": row.current_title, "current_company": row.current_company, "avatar_url": row.avatar_url, "summary": row.summary, "skills": row.skills or [], "match_score": row.match_score, "outreach_status": row.outreach_status, "shortlisted_at": row.shortlisted_at.isoformat() if row.shortlisted_at else None, "shortlisted_by": str(row.shortlisted_by) if row.shortlisted_by else None, "shortlisted_by_name": names.get(str(row.shortlisted_by)) if row.shortlisted_by else None, "contacted_at": row.contacted_at.isoformat() if row.contacted_at else None, "contacted_by": str(row.contacted_by) if row.contacted_by else None, "contacted_by_name": names.get(str(row.contacted_by)) if row.contacted_by else None, "first_seen_at": row.first_seen_at.isoformat() if row.first_seen_at else None, "last_seen_at": row.last_seen_at.isoformat() if row.last_seen_at else None, } def serialize_talent_profile_detail(row, *, user_names=None) -> dict: # The card payload plus employment/education history unpacked from the raw # actor item. Detail is fetched one profile at a time, so the extra weight # never rides along with the list endpoint. data = serialize_talent_profile(row, user_names=user_names) data["experience"] = extract_experience(row.raw or {}) data["education"] = extract_education(row.raw or {}) return data