140 lines
4.6 KiB
Python
140 lines
4.6 KiB
Python
|
|
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()
|