gendesign/backend/app/api/v1/admin_weight_profiles.py
lekss361 7250aa6187
feat(site-finder): weight profiles endpoints + analyze integration (#114 sub-PR 3/4) (#138)
Admin CRUD router + non-breaking analyze_parcel integration. 46 tests pass.

Refs: #114

Co-authored-by: lekss361 <claudestars@proton.me>
2026-05-15 00:40:06 +03:00

102 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Admin endpoints для управления weight profiles.
Per #114 (Макс feedback: садики ≠ Мегамарт). Endpoints под X-Admin-Token.
GET /api/v1/admin/site-finder/weight-profiles?user_id=... → list
POST /api/v1/admin/site-finder/weight-profiles → create
GET /api/v1/admin/site-finder/weight-profiles/{id}?user_id= → get one
PUT /api/v1/admin/site-finder/weight-profiles/{id}?user_id= → update
DELETE /api/v1/admin/site-finder/weight-profiles/{id}?user_id= → delete
"""
from __future__ import annotations
from typing import Annotated, Any
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session
from app.core.db import get_db
from app.core.deps import AdminTokenAuth
from app.services.site_finder.weight_profiles import (
WeightProfile,
WeightProfileCreate,
WeightProfileUpdate,
create_profile,
delete_profile,
get_profile,
list_profiles,
update_profile,
)
router = APIRouter()
@router.get("", response_model=list[WeightProfile])
def list_user_profiles(
user_id: Annotated[str, Query(min_length=1, description="user_id для фильтра профилей")],
db: Annotated[Session, Depends(get_db)],
_: AdminTokenAuth,
) -> Any:
"""Список всех weight profiles для заданного user_id. Default-профиль первым."""
return list_profiles(db, user_id)
@router.post("", response_model=WeightProfile, status_code=status.HTTP_201_CREATED)
def create_user_profile(
payload: WeightProfileCreate,
db: Annotated[Session, Depends(get_db)],
_: AdminTokenAuth,
) -> Any:
"""Создать новый weight profile.
При is_default=True снимает is_default у всех существующих профилей
пользователя в одной транзакции.
"""
return create_profile(db, payload)
@router.get("/{profile_id}", response_model=WeightProfile)
def get_user_profile(
profile_id: int,
user_id: Annotated[str, Query(min_length=1, description="Владелец профиля")],
db: Annotated[Session, Depends(get_db)],
_: AdminTokenAuth,
) -> Any:
"""Получить один weight profile по id (scoped к user_id)."""
profile = get_profile(db, user_id, profile_id)
if profile is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found")
return profile
@router.put("/{profile_id}", response_model=WeightProfile)
def update_user_profile(
profile_id: int,
user_id: Annotated[str, Query(min_length=1, description="Владелец профиля")],
payload: WeightProfileUpdate,
db: Annotated[Session, Depends(get_db)],
_: AdminTokenAuth,
) -> Any:
"""PATCH-style обновление weight profile.
Передавай только изменяемые поля. Не переданные поля остаются прежними.
При is_default=True снимает is_default у остальных профилей пользователя.
"""
profile = update_profile(db, user_id, profile_id, payload)
if profile is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found")
return profile
@router.delete("/{profile_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user_profile(
profile_id: int,
user_id: Annotated[str, Query(min_length=1, description="Владелец профиля")],
db: Annotated[Session, Depends(get_db)],
_: AdminTokenAuth,
) -> None:
"""Удалить weight profile. 404 если не найден."""
deleted = delete_profile(db, user_id, profile_id)
if not deleted:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found")