71 lines
2.9 KiB
Python
71 lines
2.9 KiB
Python
"""Профиль пользователя: сборка ответа, правка данных, аватар.
|
||
|
||
Переиспользуется `api/users.py` (свой профиль) и `api/admin.py` (карточка
|
||
профиля любого пользователя администратором) — правила одни и те же (задача
|
||
4c: «те же данные, редактирование по тем же правилам»).
|
||
"""
|
||
|
||
import uuid
|
||
from pathlib import Path
|
||
|
||
from fastapi import UploadFile
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from models.team import Team
|
||
from models.user import User
|
||
from repositories.admin import TeamRepository
|
||
from services.avatars import delete_avatar, read_and_validate_avatar, save_avatar
|
||
|
||
|
||
class TeamNotFoundError(Exception):
|
||
"""Команда с указанным `team_id` не найдена."""
|
||
|
||
|
||
async def resolve_team_name(session: AsyncSession, team_id: uuid.UUID | None) -> str | None:
|
||
"""Имя команды пользователя (`None`, если команда не привязана или была удалена)."""
|
||
if team_id is None:
|
||
return None
|
||
team = await session.get(Team, team_id)
|
||
return team.name if team is not None else None
|
||
|
||
|
||
async def update_profile_fields(
|
||
session: AsyncSession,
|
||
user: User,
|
||
*,
|
||
name_user: str | None,
|
||
team_id: uuid.UUID | None,
|
||
team_id_is_set: bool,
|
||
) -> User:
|
||
"""Изменить ФИО и/или команду пользователя.
|
||
|
||
`team_id_is_set` — поле присутствовало в теле запроса (в т.ч. явный
|
||
`null`, различаем через `model_fields_set` на стороне роутера) — тот же
|
||
приём, что и `summary_recipients`/`team_id` в других PATCH-эндпоинтах
|
||
(`services/conferences.py`, `api/admin.py`). Email НЕ принимается —
|
||
read-only поле профиля.
|
||
"""
|
||
if name_user is not None:
|
||
user.name_user = name_user
|
||
if team_id_is_set:
|
||
if team_id is not None and await TeamRepository(session).get(team_id) is None:
|
||
raise TeamNotFoundError
|
||
user.team_id = team_id
|
||
return user
|
||
|
||
|
||
async def set_avatar(media_root: Path, user: User, file: UploadFile) -> User:
|
||
"""Загрузить, провалидировать и сохранить новый аватар пользователя.
|
||
|
||
Бросает `AvatarTooLargeError`/`AvatarInvalidTypeError` (см. `services/avatars.py`).
|
||
"""
|
||
content, ext = await read_and_validate_avatar(file)
|
||
user.avatar_path = save_avatar(media_root, user.id, content, ext, old_path=user.avatar_path)
|
||
return user
|
||
|
||
|
||
def clear_avatar(media_root: Path, user: User) -> None:
|
||
"""Удалить файл аватара пользователя с диска и очистить `avatar_path`."""
|
||
delete_avatar(media_root, user.avatar_path)
|
||
user.avatar_path = None
|