Files
smart-city-digital-twin-mar…/smart-app-city/backend/app/schemas/schemas.py

148 lines
5.4 KiB
Python

"""Pydantic schemas for request/response validation.
All models are plain Pydantic BaseModel (not SQLModel) so they can be
used purely for API I/O without DB-session side-effects.
"""
from __future__ import annotations
from datetime import datetime
from typing import Any, Generic, List, Optional, TypeVar
from uuid import UUID
from pydantic import BaseModel, Field
# ──────────────────────────────────────────────────────────────────────
# User
# ──────────────────────────────────────────────────────────────────────
class UserCreate(BaseModel):
email: str
password: str = Field(min_length=8)
username: str = Field(min_length=3, max_length=50)
first_name: str
last_name: str
phone: Optional[str] = None
class UserLogin(BaseModel):
email: str
password: str
class UserResponse(BaseModel):
id: UUID
email: str
username: str
first_name: str
last_name: str
phone: Optional[str] = None
role: str
avatar_url: Optional[str] = None
is_active: bool
created_at: datetime
model_config = {"from_attributes": True}
# ──────────────────────────────────────────────────────────────────────
# Auth / Token
# ──────────────────────────────────────────────────────────────────────
class TokenResponse(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
# ──────────────────────────────────────────────────────────────────────
# Sensor
# ──────────────────────────────────────────────────────────────────────
class SensorResponse(BaseModel):
id: UUID
name: str
type: str
status: str
latitude: float
longitude: float
zone_id: Optional[UUID] = None
last_value: Optional[float] = None
last_reading_at: Optional[datetime] = None
battery_level: Optional[int] = None
model_config = {"from_attributes": True}
class SensorReadingResponse(BaseModel):
id: UUID
sensor_id: UUID
value: float
unit: str
quality: float
recorded_at: datetime
model_config = {"from_attributes": True}
# ──────────────────────────────────────────────────────────────────────
# Zone
# ──────────────────────────────────────────────────────────────────────
class ZoneResponse(BaseModel):
id: UUID
name: str
description: Optional[str] = None
color: str
geojson: Optional[str] = None
model_config = {"from_attributes": True}
# ──────────────────────────────────────────────────────────────────────
# Alert
# ──────────────────────────────────────────────────────────────────────
class AlertResponse(BaseModel):
id: UUID
sensor_id: UUID
type: str
severity: str
message: str
value: Optional[float] = None
threshold: Optional[float] = None
status: str
created_at: datetime
model_config = {"from_attributes": True}
# ──────────────────────────────────────────────────────────────────────
# Dashboard
# ──────────────────────────────────────────────────────────────────────
class DashboardStats(BaseModel):
total_sensors: int
active_sensors: int
total_readings_24h: int
active_alerts: int
avg_air_quality: Optional[float] = None
avg_temperature: Optional[float] = None
avg_humidity: Optional[float] = None
# ──────────────────────────────────────────────────────────────────────
# Pagination (generic)
# ──────────────────────────────────────────────────────────────────────
T = TypeVar("T")
class PaginatedResponse(BaseModel, Generic[T]):
items: List[T]
total: int
page: int
page_size: int
pages: int