172 lines
5.6 KiB
Python
172 lines
5.6 KiB
Python
"""Google Sheets response shapes. Plain dicts only — no DB, no Depends."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from g_sheet.enums import FORM_DATA_FIELDS
|
|
|
|
|
|
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."""
|
|
out = {}
|
|
for key in FORM_DATA_FIELDS:
|
|
value = getattr(row, key)
|
|
if isinstance(value, datetime):
|
|
out[key] = _iso(value)
|
|
elif isinstance(value, uuid.UUID):
|
|
out[key] = str(value)
|
|
elif key == "suggested_job_post_ids":
|
|
out[key] = [str(v) for v in (value or []) if v not in (None, "")]
|
|
else:
|
|
out[key] = value
|
|
return out
|
|
|
|
|
|
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),
|
|
"salaries_parsed": report.get("salaries_parsed", 0),
|
|
"unmapped_headers": report.get("unmapped_headers") or [],
|
|
"extracted": report.get("extracted", 0),
|
|
"extract_failed": report.get("extract_failed", 0),
|
|
"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),
|
|
"extracted": sum(r.get("extracted", 0) for r in ok),
|
|
"extract_failed": sum(r.get("extract_failed", 0) for r in reports),
|
|
"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),
|
|
}
|