Initial Commit
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, desc
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.security import get_current_user
|
||||
from app.models.user import User, NamedRoute, RouteSegment, Activity
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class SegmentCreate(BaseModel):
|
||||
name: str
|
||||
start_distance_m: float
|
||||
end_distance_m: float
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class RouteCreate(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
sport_type: Optional[str] = None
|
||||
activity_id: int # use this activity as the reference route
|
||||
|
||||
|
||||
class RouteOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
description: Optional[str]
|
||||
sport_type: Optional[str]
|
||||
reference_polyline: Optional[str]
|
||||
bounding_box: Optional[dict]
|
||||
distance_m: Optional[float]
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SegmentOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
start_distance_m: float
|
||||
end_distance_m: float
|
||||
description: Optional[str]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
@router.get("/", response_model=List[RouteOut])
|
||||
async def list_routes(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(NamedRoute)
|
||||
.where(NamedRoute.user_id == current_user.id)
|
||||
.order_by(desc(NamedRoute.created_at))
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
@router.post("/", response_model=RouteOut)
|
||||
async def create_route(
|
||||
body: RouteCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
# Load the reference activity
|
||||
act_result = await db.execute(
|
||||
select(Activity).where(
|
||||
Activity.id == body.activity_id,
|
||||
Activity.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
activity = act_result.scalar_one_or_none()
|
||||
if not activity:
|
||||
raise HTTPException(status_code=404, detail="Activity not found")
|
||||
|
||||
route = NamedRoute(
|
||||
user_id=current_user.id,
|
||||
name=body.name,
|
||||
description=body.description,
|
||||
sport_type=body.sport_type or activity.sport_type,
|
||||
reference_polyline=activity.polyline,
|
||||
bounding_box=activity.bounding_box,
|
||||
distance_m=activity.distance_m,
|
||||
)
|
||||
db.add(route)
|
||||
await db.flush()
|
||||
|
||||
# Link this activity to the route
|
||||
activity.named_route_id = route.id
|
||||
await db.commit()
|
||||
await db.refresh(route)
|
||||
return route
|
||||
|
||||
|
||||
@router.get("/{route_id}", response_model=RouteOut)
|
||||
async def get_route(
|
||||
route_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(NamedRoute).where(
|
||||
NamedRoute.id == route_id,
|
||||
NamedRoute.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
route = result.scalar_one_or_none()
|
||||
if not route:
|
||||
raise HTTPException(status_code=404, detail="Route not found")
|
||||
return route
|
||||
|
||||
|
||||
@router.get("/{route_id}/activities")
|
||||
async def route_activities(
|
||||
route_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""All activities on this named route, ordered fastest first."""
|
||||
result = await db.execute(
|
||||
select(Activity).where(
|
||||
Activity.named_route_id == route_id,
|
||||
Activity.user_id == current_user.id,
|
||||
).order_by(Activity.duration_s)
|
||||
)
|
||||
activities = result.scalars().all()
|
||||
return [
|
||||
{
|
||||
"id": a.id,
|
||||
"name": a.name,
|
||||
"start_time": a.start_time,
|
||||
"duration_s": a.duration_s,
|
||||
"distance_m": a.distance_m,
|
||||
"avg_heart_rate": a.avg_heart_rate,
|
||||
"avg_speed_ms": a.avg_speed_ms,
|
||||
}
|
||||
for a in activities
|
||||
]
|
||||
|
||||
|
||||
@router.post("/{route_id}/assign-activity")
|
||||
async def assign_activity_to_route(
|
||||
route_id: int,
|
||||
body: dict,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Manually assign an activity to a named route."""
|
||||
activity_id = body.get("activity_id")
|
||||
act_result = await db.execute(
|
||||
select(Activity).where(
|
||||
Activity.id == activity_id,
|
||||
Activity.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
activity = act_result.scalar_one_or_none()
|
||||
if not activity:
|
||||
raise HTTPException(status_code=404, detail="Activity not found")
|
||||
|
||||
activity.named_route_id = route_id
|
||||
await db.commit()
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@router.get("/{route_id}/segments", response_model=List[SegmentOut])
|
||||
async def list_segments(
|
||||
route_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(RouteSegment)
|
||||
.where(RouteSegment.route_id == route_id)
|
||||
.order_by(RouteSegment.start_distance_m)
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
@router.post("/{route_id}/segments", response_model=SegmentOut)
|
||||
async def create_segment(
|
||||
route_id: int,
|
||||
body: SegmentCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
segment = RouteSegment(
|
||||
route_id=route_id,
|
||||
name=body.name,
|
||||
start_distance_m=body.start_distance_m,
|
||||
end_distance_m=body.end_distance_m,
|
||||
description=body.description,
|
||||
)
|
||||
db.add(segment)
|
||||
await db.commit()
|
||||
await db.refresh(segment)
|
||||
return segment
|
||||
Reference in New Issue
Block a user