from datetime import datetime, timedelta from typing import Optional from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select, desc from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models import Vehicle, Position from app.schemas import VehicleCreate, VehicleUpdate, VehicleResponse, VehicleWithPosition from app.schemas.vehicle import LastPosition router = APIRouter() def get_vehicle_status(last_position: Optional[Position], now: datetime) -> str: if not last_position: return "offline" time_diff = now - last_position.timestamp if time_diff > timedelta(minutes=5): return "offline" elif last_position.speed < 2: return "stopped" else: return "moving" @router.get("", response_model=list[VehicleWithPosition]) async def get_vehicles(db: AsyncSession = Depends(get_db)): """Получить список всех транспортных средств с последними позициями""" result = await db.execute(select(Vehicle)) vehicles = result.scalars().all() response = [] now = datetime.utcnow() for vehicle in vehicles: # Get last position pos_result = await db.execute( select(Position) .where(Position.vehicle_id == vehicle.id) .order_by(desc(Position.timestamp)) .limit(1) ) last_pos = pos_result.scalar_one_or_none() vehicle_data = VehicleWithPosition( id=vehicle.id, name=vehicle.name, type=vehicle.type, created_at=vehicle.created_at, last_position=LastPosition( lat=last_pos.lat, lon=last_pos.lon, speed=last_pos.speed, heading=last_pos.heading, timestamp=last_pos.timestamp ) if last_pos else None, status=get_vehicle_status(last_pos, now) ) response.append(vehicle_data) return response @router.get("/{vehicle_id}", response_model=VehicleWithPosition) async def get_vehicle(vehicle_id: int, db: AsyncSession = Depends(get_db)): """Получить информацию о транспортном средстве""" result = await db.execute(select(Vehicle).where(Vehicle.id == vehicle_id)) vehicle = result.scalar_one_or_none() if not vehicle: raise HTTPException(status_code=404, detail="Vehicle not found") # Get last position pos_result = await db.execute( select(Position) .where(Position.vehicle_id == vehicle.id) .order_by(desc(Position.timestamp)) .limit(1) ) last_pos = pos_result.scalar_one_or_none() now = datetime.utcnow() return VehicleWithPosition( id=vehicle.id, name=vehicle.name, type=vehicle.type, created_at=vehicle.created_at, last_position=LastPosition( lat=last_pos.lat, lon=last_pos.lon, speed=last_pos.speed, heading=last_pos.heading, timestamp=last_pos.timestamp ) if last_pos else None, status=get_vehicle_status(last_pos, now) ) @router.post("", response_model=VehicleResponse, status_code=201) async def create_vehicle(vehicle: VehicleCreate, db: AsyncSession = Depends(get_db)): """Создать новое транспортное средство""" db_vehicle = Vehicle(**vehicle.model_dump()) db.add(db_vehicle) await db.commit() await db.refresh(db_vehicle) return db_vehicle @router.put("/{vehicle_id}", response_model=VehicleResponse) async def update_vehicle(vehicle_id: int, vehicle: VehicleUpdate, db: AsyncSession = Depends(get_db)): """Обновить транспортное средство""" result = await db.execute(select(Vehicle).where(Vehicle.id == vehicle_id)) db_vehicle = result.scalar_one_or_none() if not db_vehicle: raise HTTPException(status_code=404, detail="Vehicle not found") update_data = vehicle.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(db_vehicle, field, value) await db.commit() await db.refresh(db_vehicle) return db_vehicle @router.delete("/{vehicle_id}", status_code=204) async def delete_vehicle(vehicle_id: int, db: AsyncSession = Depends(get_db)): """Удалить транспортное средство""" result = await db.execute(select(Vehicle).where(Vehicle.id == vehicle_id)) db_vehicle = result.scalar_one_or_none() if not db_vehicle: raise HTTPException(status_code=404, detail="Vehicle not found") await db.delete(db_vehicle) await db.commit()