HR-ATS-Portal/backend/g_sheet/serializers.py

189 lines
6.6 KiB
Python

"""Google Sheets response shapes. Plain dicts only — no DB, no Depends."""
from __future__ import annotations
def serialize_metadata(payload: dict) -> dict:
"""spreadsheets.get response -> the spreadsheet header the UI renders."""
properties = payload.get("properties") or {}
return {
"spreadsheet_id": payload.get("spreadsheetId"),
"title": properties.get("title"),
"locale": properties.get("locale"),
"time_zone": properties.get("timeZone"),
"url": payload.get("spreadsheetUrl"),
"tabs": [serialize_tab(sheet) for sheet in payload.get("sheets") or []],
}
def serialize_tab(sheet: dict) -> dict:
"""One entry of spreadsheets.get -> tab name plus its grid size."""
properties = sheet.get("properties") or {}
grid = properties.get("gridProperties") or {}
return {
"title": properties.get("title"),
"sheet_id": properties.get("sheetId"),
"index": properties.get("index"),
"row_count": grid.get("rowCount"),
"column_count": grid.get("columnCount"),
}
def serialize_values(tab: str, cell_range: str | None, rows: list[list[str]]) -> dict:
"""Raw rows -> the read_range envelope."""
return {
"tab": tab,
"range": cell_range,
"rows": rows,
"row_count": len(rows),
}
def serialize_records(tab: str, records: list[dict]) -> dict:
"""Header-mapped rows -> the read_records envelope."""
return {
"tab": tab,
"records": records,
"total": len(records),
"headers": list(records[0].keys()) if records else [],
}
def serialize_append(tab: str, payload: dict) -> dict:
"""values.append response -> what was written and where."""
updates = payload.get("updates") or {}
return {
"tab": tab,
"spreadsheet_id": payload.get("spreadsheetId"),
"updated_range": updates.get("updatedRange"),
"updated_rows": updates.get("updatedRows", 0),
"updated_columns": updates.get("updatedColumns", 0),
"updated_cells": updates.get("updatedCells", 0),
}
def serialize_update(tab: str, payload: dict) -> dict:
"""values.update response -> the same shape as an append result."""
return {
"tab": tab,
"spreadsheet_id": payload.get("spreadsheetId"),
"updated_range": payload.get("updatedRange"),
"updated_rows": payload.get("updatedRows", 0),
"updated_columns": payload.get("updatedColumns", 0),
"updated_cells": payload.get("updatedCells", 0),
}
def serialize_clear(tab: str, payload: dict) -> dict:
"""values.clear response -> the cleared range."""
return {
"tab": tab,
"spreadsheet_id": payload.get("spreadsheetId"),
"cleared_range": payload.get("clearedRange"),
}
def serialize_health(ok: bool, detail: str, tabs: list[str] | None = None) -> dict:
"""health_check result. Returned on failure too — this one never raises."""
return {
"status": "ok" if ok else "error",
"detail": detail,
"tabs": tabs or [],
"tab_count": len(tabs or []),
}
def _iso(value):
return value.isoformat() if value is not None else None
def serialize_form_data(row) -> dict:
"""FormData ORM row → API dict, including raw_record."""
return {
"id": row.id,
"sheet": row.sheet,
"name": row.name,
"degree": row.degree,
"experience": row.experience,
"age": row.age,
"age_raw": row.age_raw,
"family_details": row.family_details,
"interview_date": _iso(row.interview_date),
"interview_by": row.interview_by,
"interview_time": row.interview_time,
"interview_status": row.interview_status,
"interview_notes": row.interview_notes,
"interview_result": row.interview_result,
"second_interview_date": _iso(row.second_interview_date),
"second_interview_by": row.second_interview_by,
"second_interview_time": row.second_interview_time,
"second_interview_status": row.second_interview_status,
"second_interview_notes": row.second_interview_notes,
"second_interview_result": row.second_interview_result,
"third_interview_date": _iso(row.third_interview_date),
"third_interview_by": row.third_interview_by,
"third_interview_time": row.third_interview_time,
"third_interview_status": row.third_interview_status,
"third_interview_notes": row.third_interview_notes,
"third_interview_result": row.third_interview_result,
"fourth_interview_date": _iso(row.fourth_interview_date),
"fourth_interview_by": row.fourth_interview_by,
"fourth_interview_time": row.fourth_interview_time,
"fourth_interview_status": row.fourth_interview_status,
"fourth_interview_notes": row.fourth_interview_notes,
"fourth_interview_result": row.fourth_interview_result,
"raw_record": row.raw_record,
"row_number": row.row_number,
"imported_at": _iso(row.imported_at),
"created_at": _iso(row.created_at),
"updated_at": _iso(row.updated_at),
}
def serialize_import(report: dict) -> dict:
"""Per-tab import report."""
return {
"tab": report.get("tab"),
"rows_read": report.get("rows_read", 0),
"inserted": report.get("inserted", 0),
"deleted": report.get("deleted", 0),
"dates_parsed": report.get("dates_parsed", 0),
"dates_unparsed": report.get("dates_unparsed", 0),
"ages_parsed": report.get("ages_parsed", 0),
"unmapped_headers": report.get("unmapped_headers") or [],
"error": report.get("error"),
}
def serialize_import_all(reports: list[dict]) -> dict:
"""Aggregate of per-tab reports from import_all."""
ok=[r for r in reports if not r.get("error")]
failed=[r for r in reports if r.get("error")]
return {
"tabs": len(reports),
"succeeded": len(ok),
"failed": len(failed),
"inserted": sum(r.get("inserted", 0) for r in ok),
"deleted": sum(r.get("deleted", 0) for r in ok),
"reports": [serialize_import(r) for r in reports],
}
def serialize_sheet_summary(sheets: list[str]) -> dict:
return {"sheets": sheets, "total": len(sheets)}
def serialize_import_run(row) -> dict:
return {
"id": str(row.id),
"status": row.status,
"task_id": row.task_id,
"created_by": str(row.created_by) if row.created_by else None,
"tab": row.tab,
"report": row.report,
"error": row.error,
"created_at": _iso(row.created_at),
"started_at": _iso(row.started_at),
"finished_at": _iso(row.finished_at),
}