158 lines
6.7 KiB
Python
158 lines
6.7 KiB
Python
from job.job_post.enums import RequisitionStatus
|
|
from job.job_post.models import JobPosts
|
|
|
|
|
|
def _status_label(value):
|
|
if value in (None, ""):
|
|
return None
|
|
parsed = RequisitionStatus.parse(value)
|
|
return parsed.label if parsed else value
|
|
|
|
|
|
def serialize_job_post_title(row) -> dict:
|
|
"""Inbox suggestion rail — title only until the recruiter expands the card."""
|
|
return {
|
|
"id": str(row.id),
|
|
"title": row.title,
|
|
"status": row.status,
|
|
"is_active": row.is_active,
|
|
"is_deleted": row.is_deleted,
|
|
}
|
|
|
|
|
|
def _recruiter_payload(row, names=None):
|
|
"""List of recruiter ids plus mapped names; first id stays the legacy pointer."""
|
|
names = names or {}
|
|
ids = JobPosts.recruiter_ids_of(row)
|
|
mapped = [names.get(i) for i in ids]
|
|
first = ids[0] if ids else None
|
|
return {
|
|
"current_recruiter_id": first,
|
|
"current_recruiter_ids": ids,
|
|
"recruiter_name": next((n for n in mapped if n), None),
|
|
"recruiter_names": [n for n in mapped if n],
|
|
"recruiters": [{"id": i, "name": names.get(i)} for i in ids],
|
|
}
|
|
|
|
|
|
def serialize_job_post(row, *, names=None) -> dict:
|
|
return {
|
|
"id": str(row.id),
|
|
"title": row.title,
|
|
# Talent Pool / candidate filters key off attached job_posts.department.
|
|
"department": row.department or None,
|
|
"employment_type": row.employment_type,
|
|
"location": row.location,
|
|
"experience_min": row.experience_min,
|
|
"experience_max": row.experience_max,
|
|
"requirements": list(row.requirements or []),
|
|
"optional_skills": list(row.optional_skills or []),
|
|
"salary": row.salary,
|
|
"description": row.description,
|
|
"post_text": row.post_text,
|
|
"channel_id": row.channel_id,
|
|
"platform": row.platform,
|
|
"is_active": row.is_active,
|
|
"is_deleted": row.is_deleted,
|
|
"buffer_post_id": row.buffer_post_id,
|
|
"buffer_external_link": row.buffer_external_link,
|
|
"buffer_sent_at": row.buffer_sent_at.isoformat() if row.buffer_sent_at else None,
|
|
"status": row.status,
|
|
"buffer_error": row.buffer_error,
|
|
"created_by": str(row.created_by) if row.created_by else None,
|
|
"created_by_name": row.user.name if getattr(row, "user", None) else None,
|
|
"created_at": row.created_at.isoformat() if row.created_at else None,
|
|
"updated_at": row.updated_at.isoformat() if row.updated_at else None,
|
|
"requisition_id": str(row.requisition_id) if getattr(row, "requisition_id", None) else None,
|
|
**_recruiter_payload(row, names),
|
|
}
|
|
|
|
|
|
def serialize_job_row(row, *, names=None, recruiter_name=None, hiring_manager_name=None, applicant_count=0) -> dict:
|
|
"""Requisition view of a job post, for the Jobs screen.
|
|
|
|
Deliberately separate from serialize_job_post: that payload is shared by the
|
|
inbox, candidate and matching paths. department is the one shared field —
|
|
talent-pool filters key off it on attached job_posts.
|
|
"""
|
|
req = getattr(row, "requisition", None)
|
|
payload = _recruiter_payload(row, names)
|
|
if recruiter_name and not payload["recruiter_name"]:
|
|
payload["recruiter_name"] = recruiter_name
|
|
return {
|
|
"id": str(row.id),
|
|
"title": row.title,
|
|
"department": row.department or None,
|
|
"location": row.location,
|
|
"employment_type": row.employment_type,
|
|
"vacancies": row.vacancies,
|
|
"platform": row.platform or None,
|
|
# Two different lifecycles, never conflate: requisition_status is hiring
|
|
# (open/closed/on_hold), status is Buffer publishing (draft/scheduled/...).
|
|
"requisition_status": row.requisition_status,
|
|
"status": row.status,
|
|
"experience_min": row.experience_min,
|
|
"experience_max": row.experience_max,
|
|
"salary": row.salary,
|
|
"requirements": list(row.requirements or []),
|
|
"optional_skills": list(row.optional_skills or []),
|
|
"description": row.description,
|
|
"is_active": row.is_active,
|
|
"closed_at": row.closed_at.isoformat() if row.closed_at else None,
|
|
**payload,
|
|
"hiring_manager_id": str(row.hiring_manager_id) if row.hiring_manager_id else None,
|
|
"hiring_manager_name": hiring_manager_name,
|
|
"requisition_id": str(row.requisition_id) if getattr(row, "requisition_id", None) else None,
|
|
"requisition_title": req.position_title if req else None,
|
|
"requisition_department": req.department.name if req and req.department else None,
|
|
"applicant_count": applicant_count,
|
|
"created_by": str(row.created_by) if row.created_by else None,
|
|
"created_by_name": row.user.name if getattr(row, "user", None) else None,
|
|
"created_at": row.created_at.isoformat() if row.created_at else None,
|
|
"updated_at": row.updated_at.isoformat() if row.updated_at else None,
|
|
}
|
|
|
|
|
|
def serialize_job_stats(row, *, names=None) -> dict:
|
|
"""One job post's live pipeline-stage counts. `row` is a mapping from fetch_job_stats."""
|
|
created_at = row.get("created_at")
|
|
payload = _recruiter_payload(row, names)
|
|
if not payload["recruiter_name"] and row.get("recruiter_name"):
|
|
payload["recruiter_name"] = row.get("recruiter_name")
|
|
return {
|
|
"job_post_id": str(row["job_post_id"]),
|
|
"title": row["title"],
|
|
"department": row["department"] or None,
|
|
"location": row["location"],
|
|
"requisition_status": row["requisition_status"],
|
|
**payload,
|
|
# Frontend computes days-open vs client clock; no server days_open field.
|
|
"created_at": created_at.isoformat() if created_at else None,
|
|
"total_applicants": int(row["total_applicants"] or 0),
|
|
"shortlisting": int(row["shortlisting"] or 0),
|
|
"screened": int(row["screened"] or 0),
|
|
"assessment": int(row["assessment"] or 0),
|
|
"interviewed": int(row["interviewed"] or 0),
|
|
"offered": int(row["offered"] or 0),
|
|
"on_hold": int(row["on_hold"] or 0),
|
|
"rejected": int(row["rejected"] or 0),
|
|
"approved": int(row["approved"] or 0),
|
|
"hired": int(row["hired"] or 0),
|
|
"reapplied": int(row.get("reapplied") or 0),
|
|
}
|
|
|
|
|
|
def serialize_status_history(row, *, changed_by_name=None) -> dict:
|
|
return {
|
|
"id": str(row.id),
|
|
"job_post_id": str(row.job_post_id) if row.job_post_id else None,
|
|
"from_status": row.from_status,
|
|
"from_label": _status_label(row.from_status),
|
|
"to_status": row.to_status,
|
|
"to_label": _status_label(row.to_status),
|
|
"changed_by": str(row.changed_by) if row.changed_by else None,
|
|
"changed_by_name": changed_by_name,
|
|
"actor_kind": row.actor_kind,
|
|
"created_at": row.created_at.isoformat() if row.created_at else None,
|
|
}
|