248 lines
7.4 KiB
Python
248 lines
7.4 KiB
Python
"""
|
|
Star System Management API routes
|
|
Handles CRUD operations for star systems (Solar System and exoplanet systems)
|
|
"""
|
|
import logging
|
|
from fastapi import APIRouter, HTTPException, Depends, Query, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from typing import Optional
|
|
|
|
from app.database import get_db
|
|
from app.models.star_system import (
|
|
StarSystemCreate,
|
|
StarSystemUpdate,
|
|
StarSystemResponse,
|
|
StarSystemWithBodies,
|
|
StarSystemListResponse,
|
|
StarSystemStatistics
|
|
)
|
|
from app.services.star_system_service import star_system_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/star-systems", tags=["star-systems"])
|
|
|
|
|
|
@router.get("", response_model=StarSystemListResponse)
|
|
async def get_star_systems(
|
|
skip: int = Query(0, ge=0, description="Number of records to skip"),
|
|
limit: int = Query(100, ge=1, le=1000, description="Maximum records to return"),
|
|
exclude_solar: bool = Query(False, description="Exclude Solar System from results"),
|
|
search: Optional[str] = Query(None, description="Search by name (English or Chinese)"),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Get all star systems with pagination and optional filtering
|
|
|
|
Args:
|
|
skip: Number of records to skip (for pagination)
|
|
limit: Maximum number of records to return
|
|
exclude_solar: If True, exclude Solar System (id=1) from results
|
|
search: Search keyword to filter by name or host star name
|
|
db: Database session
|
|
|
|
Returns:
|
|
List of star systems with total count
|
|
"""
|
|
systems = await star_system_service.get_all(
|
|
db=db,
|
|
skip=skip,
|
|
limit=limit,
|
|
exclude_solar=exclude_solar,
|
|
search=search
|
|
)
|
|
|
|
# Get total count for pagination
|
|
from sqlalchemy import select, func, or_
|
|
from app.models.db.star_system import StarSystem
|
|
|
|
count_query = select(func.count(StarSystem.id))
|
|
if exclude_solar:
|
|
count_query = count_query.where(StarSystem.id != 1)
|
|
if search:
|
|
search_pattern = f"%{search}%"
|
|
count_query = count_query.where(
|
|
or_(
|
|
StarSystem.name.ilike(search_pattern),
|
|
StarSystem.name_zh.ilike(search_pattern),
|
|
StarSystem.host_star_name.ilike(search_pattern)
|
|
)
|
|
)
|
|
|
|
result = await db.execute(count_query)
|
|
total = result.scalar()
|
|
|
|
return StarSystemListResponse(
|
|
total=total,
|
|
systems=[StarSystemResponse.from_orm(s) for s in systems]
|
|
)
|
|
|
|
|
|
@router.get("/statistics", response_model=StarSystemStatistics)
|
|
async def get_statistics(db: AsyncSession = Depends(get_db)):
|
|
"""
|
|
Get star system statistics
|
|
|
|
Returns:
|
|
- Total star systems count
|
|
- Exoplanet systems count
|
|
- Total planets count (Solar System + exoplanets)
|
|
- Nearest star systems (top 10)
|
|
"""
|
|
stats = await star_system_service.get_statistics(db)
|
|
return StarSystemStatistics(**stats)
|
|
|
|
|
|
@router.get("/{system_id}", response_model=StarSystemResponse)
|
|
async def get_star_system(
|
|
system_id: int,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Get a single star system by ID
|
|
|
|
Args:
|
|
system_id: Star system ID (1 = Solar System, 2+ = Exoplanet systems)
|
|
"""
|
|
system = await star_system_service.get_by_id(db, system_id)
|
|
if not system:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Star system with ID {system_id} not found"
|
|
)
|
|
return StarSystemResponse.from_orm(system)
|
|
|
|
|
|
@router.get("/{system_id}/bodies", response_model=StarSystemWithBodies)
|
|
async def get_star_system_with_bodies(
|
|
system_id: int,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Get a star system with all its celestial bodies
|
|
|
|
Args:
|
|
system_id: Star system ID
|
|
|
|
Returns:
|
|
Star system details along with list of all celestial bodies
|
|
(stars, planets, dwarf planets, satellites, probes, comets, etc.)
|
|
"""
|
|
result = await star_system_service.get_with_bodies(db, system_id)
|
|
if not result:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Star system with ID {system_id} not found"
|
|
)
|
|
|
|
# Convert ORM objects to dicts
|
|
system_dict = StarSystemResponse.from_orm(result["system"]).dict()
|
|
bodies_list = [
|
|
{
|
|
"id": body.id,
|
|
"name": body.name,
|
|
"name_zh": body.name_zh,
|
|
"type": body.type,
|
|
"description": body.description,
|
|
"details": body.details,
|
|
"is_active": body.is_active,
|
|
"extra_data": body.extra_data,
|
|
}
|
|
for body in result["bodies"]
|
|
]
|
|
|
|
return StarSystemWithBodies(
|
|
**system_dict,
|
|
bodies=bodies_list,
|
|
body_count=result["body_count"]
|
|
)
|
|
|
|
|
|
@router.post("", status_code=status.HTTP_201_CREATED, response_model=StarSystemResponse)
|
|
async def create_star_system(
|
|
system_data: StarSystemCreate,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Create a new star system
|
|
|
|
Note: This is an admin operation. Use with caution.
|
|
"""
|
|
# Check if name already exists
|
|
existing = await star_system_service.get_by_name(db, system_data.name)
|
|
if existing:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Star system with name '{system_data.name}' already exists"
|
|
)
|
|
|
|
new_system = await star_system_service.create(db, system_data.dict())
|
|
return StarSystemResponse.from_orm(new_system)
|
|
|
|
|
|
@router.put("/{system_id}", response_model=StarSystemResponse)
|
|
async def update_star_system(
|
|
system_id: int,
|
|
system_data: StarSystemUpdate,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Update a star system
|
|
|
|
Args:
|
|
system_id: Star system ID to update
|
|
system_data: Fields to update (only non-null fields will be updated)
|
|
"""
|
|
# Filter out None values
|
|
update_data = {k: v for k, v in system_data.dict().items() if v is not None}
|
|
|
|
if not update_data:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="No fields to update"
|
|
)
|
|
|
|
updated_system = await star_system_service.update(db, system_id, update_data)
|
|
if not updated_system:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Star system with ID {system_id} not found"
|
|
)
|
|
|
|
return StarSystemResponse.from_orm(updated_system)
|
|
|
|
|
|
@router.delete("/{system_id}")
|
|
async def delete_star_system(
|
|
system_id: int,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Delete a star system and all its celestial bodies
|
|
|
|
WARNING: This will cascade delete all celestial bodies in this system!
|
|
Cannot delete Solar System (id=1).
|
|
"""
|
|
if system_id == 1:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Cannot delete Solar System"
|
|
)
|
|
|
|
try:
|
|
deleted = await star_system_service.delete_system(db, system_id)
|
|
if not deleted:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Star system with ID {system_id} not found"
|
|
)
|
|
|
|
return {
|
|
"message": f"Star system {system_id} and all its bodies deleted successfully"
|
|
}
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail=str(e)
|
|
)
|