gendesign/backend/app/api/v1/admin_weight_profiles.py
lekss361 a6e4ff0407 feat(#114): seed 3 default weight presets + include_system API param
Add SQL migration 100_user_weight_profiles_default_seed.sql with system
presets Эконом/Комфорт/Бизнес (user_id='__system__'). Migration is
idempotent via ON CONFLICT DO UPDATE.

Backend:
- weight_profiles.py: add SYSTEM_USER_ID constant + list_profiles_with_system()
- admin_weight_profiles.py: add include_system query param to GET list endpoint

Tests: 3 new tests covering include_system flag and service sentinel behaviour.
2026-05-16 22:49:56 +03:00

113 lines
4.3 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,
list_profiles_with_system,
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,
include_system: Annotated[
bool,
Query(description="Включить системные preset-профили (Эконом/Комфорт/Бизнес)"),
] = False,
) -> Any:
"""Список всех weight profiles для заданного user_id. Default-профиль первым.
При include_system=true добавляются системные presets (user_id='__system__'):
Эконом, Комфорт, Бизнес — готовые шаблоны весов POI.
"""
if include_system:
return list_profiles_with_system(db, user_id)
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")