diff --git a/backend/devices/service.py b/backend/devices/service.py index f8de946..5b12203 100644 --- a/backend/devices/service.py +++ b/backend/devices/service.py @@ -1,6 +1,8 @@ import secrets import string +from google.cloud.firestore_v1 import GeoPoint + from shared.firebase import get_db from shared.exceptions import NotFoundError from devices.models import DeviceCreate, DeviceUpdate, DeviceInDB @@ -36,9 +38,38 @@ def _ensure_unique_serial(db) -> str: raise RuntimeError("Could not generate a unique serial number after 100 attempts") +def _convert_firestore_value(val): + """Convert Firestore-specific types (Timestamp, GeoPoint) to strings.""" + if hasattr(val, "seconds") and hasattr(val, "nanos"): + # Firestore Timestamp → ISO string + from datetime import datetime, timezone + dt = datetime.fromtimestamp(val.seconds + val.nanos / 1e9, tz=timezone.utc) + return dt.strftime("%d %B %Y at %H:%M:%S UTC") + if isinstance(val, GeoPoint): + return f"{val.latitude}° N, {val.longitude}° E" + return val + + +def _sanitize_dict(d: dict) -> dict: + """Recursively convert Firestore-native types in a dict to plain strings.""" + result = {} + for k, v in d.items(): + if isinstance(v, dict): + result[k] = _sanitize_dict(v) + elif isinstance(v, list): + result[k] = [ + _sanitize_dict(item) if isinstance(item, dict) + else _convert_firestore_value(item) + for item in v + ] + else: + result[k] = _convert_firestore_value(v) + return result + + def _doc_to_device(doc) -> DeviceInDB: """Convert a Firestore document snapshot to a DeviceInDB model.""" - data = doc.to_dict() + data = _sanitize_dict(doc.to_dict()) return DeviceInDB(id=doc.id, **data)