from collections.abc import Mapping from typing import Any, List from sqlalchemy import text from banban.dao import BaseDAO class DeviceDAO(BaseDAO): async def ensure_device_access(self, *, device_id: str, user_id: int) -> None: from fastapi import HTTPException, status result = await self.execute( text( """ SELECT 1 FROM device_bindings WHERE device_id = :device_id AND owner_user_id = :user_id AND status = 1 LIMIT 1 """ ), {"device_id": device_id, "user_id": user_id}, ) row = result.mappings().first() if row is None: raise HTTPException(status_code=404, detail="device not found") async def list_device_messages( self, *, device_id: str, cursor: int | None, limit: int, ) -> List[Mapping[str, Any]]: params = {"device_id": device_id, "limit": limit + 1} where = "ch.device_id = :device_id" if cursor is not None: where += " AND cm.id < :cursor" params["cursor"] = cursor result = await self.execute( text( f""" SELECT cm.id, ch.id AS conversation_id, ch.role_key, cm.is_user, cm.content, cm.timestamp, cm.created_at FROM conversation_messages AS cm JOIN conversation_histories AS ch ON ch.id = cm.conversation_id WHERE {where} ORDER BY cm.id DESC LIMIT :limit """ ), params, ) return result.mappings().all() async def get_device_status( self, *, device_id: str, user_id: int, ) -> Mapping[str, Any]: from fastapi import HTTPException result = await self.execute( text( """ SELECT db.device_id, db.child_id, c.child_name, ds.power, ds.volume, ds.`signal` AS signal_strength, ds.`version` AS version, ds.updated_at AS settings_updated_at, cl.coord_type, cl.lat, cl.lng, cl.accuracy_m, cl.altitude_m, cl.speed_mps, cl.heading_deg, cl.source, cl.battery_pct, cl.device_time, cl.server_time, cl.updated_at AS location_updated_at FROM device_bindings AS db LEFT JOIN children AS c ON c.child_id = db.child_id AND c.status = 1 LEFT JOIN device_settings AS ds ON ds.device_id = db.device_id LEFT JOIN child_location_current AS cl ON cl.child_id = db.child_id AND cl.device_id = db.device_id WHERE db.device_id = :device_id AND db.owner_user_id = :user_id AND db.status = 1 LIMIT 1 """ ), {"device_id": device_id, "user_id": user_id}, ) row = result.mappings().first() if row is None: raise HTTPException(status_code=404, detail="device not found") return row