Files

149 lines
5.1 KiB
Python
Raw Permalink Normal View History

"""Pydantic schemas for vehicle-related request and response bodies."""
import uuid
from datetime import date, datetime
from decimal import Decimal
from typing import Literal, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
VEHICLE_TYPES = Literal["lkw", "pkw", "baumaschine", "stapler", "transporter"]
CONDITIONS = Literal["new", "used"]
AVAILABILITY = Literal["available", "reserved", "sold"]
HOURS_UNITS = Literal["h", "min"]
class VehicleBase(BaseModel):
"""Base vehicle fields shared across schemas."""
make: str = Field(..., min_length=1, max_length=100)
model: str = Field(..., min_length=1, max_length=100)
fin: str = Field(..., min_length=17, max_length=17)
year: Optional[int] = Field(None, ge=1900, le=2100)
first_registration: Optional[date] = None
power_kw: Optional[int] = Field(None, ge=0)
power_hp: Optional[int] = Field(None, ge=0)
fuel_type: Optional[str] = Field(None, max_length=50)
transmission: Optional[str] = Field(None, max_length=20)
color: Optional[str] = Field(None, max_length=50)
condition: CONDITIONS = "used"
location: Optional[str] = Field(None, max_length=255)
availability: AVAILABILITY = "available"
price: Decimal = Field(..., ge=0)
vehicle_type: VEHICLE_TYPES
lkw_type: Optional[str] = Field(None, max_length=50)
machine_type: Optional[str] = Field(None, max_length=50)
body_type: Optional[str] = Field(None, max_length=100)
operating_hours: Optional[Decimal] = Field(None, ge=0)
operating_hours_unit: Optional[HOURS_UNITS] = None
mileage_km: Optional[int] = Field(None, ge=0)
description: Optional[str] = None
@model_validator(mode="after")
def compute_power_hp(self) -> "VehicleBase":
"""Auto-compute power_hp from power_kw if not provided."""
if self.power_kw is not None and self.power_hp is None:
self.power_hp = round(self.power_kw * 1.35962)
return self
class VehicleCreate(VehicleBase):
"""POST /api/v1/vehicles request body."""
pass
class VehicleUpdate(BaseModel):
"""PUT /api/v1/vehicles/:id request body (all fields optional)."""
make: Optional[str] = Field(None, min_length=1, max_length=100)
model: Optional[str] = Field(None, min_length=1, max_length=100)
fin: Optional[str] = Field(None, min_length=17, max_length=17)
year: Optional[int] = Field(None, ge=1900, le=2100)
first_registration: Optional[date] = None
power_kw: Optional[int] = Field(None, ge=0)
power_hp: Optional[int] = Field(None, ge=0)
fuel_type: Optional[str] = Field(None, max_length=50)
transmission: Optional[str] = Field(None, max_length=20)
color: Optional[str] = Field(None, max_length=50)
condition: Optional[CONDITIONS] = None
location: Optional[str] = Field(None, max_length=255)
availability: Optional[AVAILABILITY] = None
price: Optional[Decimal] = Field(None, ge=0)
vehicle_type: Optional[VEHICLE_TYPES] = None
lkw_type: Optional[str] = Field(None, max_length=50)
machine_type: Optional[str] = Field(None, max_length=50)
body_type: Optional[str] = Field(None, max_length=100)
operating_hours: Optional[Decimal] = Field(None, ge=0)
operating_hours_unit: Optional[HOURS_UNITS] = None
mileage_km: Optional[int] = Field(None, ge=0)
description: Optional[str] = None
@model_validator(mode="after")
def compute_power_hp(self) -> "VehicleUpdate":
"""Auto-compute power_hp from power_kw if not provided."""
if self.power_kw is not None and self.power_hp is None:
self.power_hp = round(self.power_kw * 1.35962)
return self
class VehicleResponse(BaseModel):
"""Vehicle response schema."""
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
make: str
model: str
fin: str
year: Optional[int] = None
first_registration: Optional[date] = None
power_kw: Optional[int] = None
power_hp: Optional[int] = None
fuel_type: Optional[str] = None
transmission: Optional[str] = None
color: Optional[str] = None
condition: str
location: Optional[str] = None
availability: str
price: Decimal
vehicle_type: str
lkw_type: Optional[str] = None
machine_type: Optional[str] = None
body_type: Optional[str] = None
operating_hours: Optional[Decimal] = None
operating_hours_unit: Optional[str] = None
mileage_km: Optional[int] = None
description: Optional[str] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
deleted_at: Optional[datetime] = None
class VehicleListResponse(BaseModel):
"""Paginated vehicle list response."""
items: list[VehicleResponse]
total: int
page: int
page_size: int
class MobileDeStatusResponse(BaseModel):
"""mobile.de sync status for a vehicle."""
synced: bool
ad_id: Optional[str] = None
synced_at: Optional[datetime] = None
sync_status: str = "pending"
error_log: Optional[str] = None
class MobileDePushResponse(BaseModel):
"""Response for mobile.de push request."""
message: str = "Push queued"
vehicle_id: uuid.UUID
listing_id: Optional[uuid.UUID] = None