from __future__ import annotations from collections.abc import Mapping from typing import Any from sqlalchemy import select from sqlalchemy.orm import Session from services.shared.core import utc_now_iso from services.shared.sql_models import ReportingInteractionFactRow _STRING_FIELDS = ( "interaction_id", "channel", "queue_id", "agent_id", "status", "created_at", "closed_at", "source", ) _BOOL_FIELDS = ("answered", "abandoned", "resolved_first_contact") _INT_FIELDS = ("wait_seconds", "handle_seconds") def _clean_string(value: Any) -> str | None: raw = str(value or "").strip() return raw or None def _clean_bool(value: Any) -> bool | None: if value is None: return None return bool(value) def _clean_int(value: Any) -> int | None: if value is None or value == "": return None parsed = int(value) return max(parsed, 0) def normalize_reporting_fact_payload(payload: Mapping[str, Any]) -> dict[str, Any]: normalized: dict[str, Any] = {} for field in _STRING_FIELDS: value = _clean_string(payload.get(field)) if value is not None: normalized[field] = value for field in _BOOL_FIELDS: value = _clean_bool(payload.get(field)) if value is not None: normalized[field] = value for field in _INT_FIELDS: value = _clean_int(payload.get(field)) if value is not None: normalized[field] = value interaction_id = normalized.get("interaction_id") if not interaction_id: raise ValueError("interaction_id is required") source = normalized.get("source") if not source: raise ValueError("source is required") return normalized def upsert_reporting_interaction_fact(session: Session, payload: Mapping[str, Any]) -> ReportingInteractionFactRow: normalized = normalize_reporting_fact_payload(payload) row = session.execute( select(ReportingInteractionFactRow).where( ReportingInteractionFactRow.interaction_id == normalized["interaction_id"] ) ).scalar_one_or_none() if row is None: row = ReportingInteractionFactRow( interaction_id=normalized["interaction_id"], source=normalized["source"], updated_at=utc_now_iso(), ) session.add(row) for field, value in normalized.items(): setattr(row, field, value) row.updated_at = utc_now_iso() return row