2025-10-26 20:23:26 +03:00
"""
Lichess Statistics API - Модели данных
Этот модуль содержит все Pydantic модели для валидации и сериализации данных .
Модели используются для :
- Валидации входных параметров API
- Сериализации ответов API
- Документации в Swagger UI
- Типизации данных в коде
Автор : Lichess Web Services Team
Версия : 1.0 .0
"""
from pydantic import BaseModel , Field
2026-07-03 09:38:51 +00:00
from typing import Dict , List , Optional , Any
2025-10-26 20:23:26 +03:00
# =============================================================================
# МОДЕЛИ СТАТИСТИКИ ЗАДАЧ (ПАЗЛОВ)
# =============================================================================
class TaskStats ( BaseModel ) :
"""
Статистика решения задач ( пазлов ) пользователя .
Содержит информацию о том , сколько задач пользователь решил ,
сколько решил правильно и сколько не решил или решил неправильно .
"""
total : int = Field ( . . . , description = " Общее количество решенных задач " , example = 15 )
solved : int = Field ( . . . , description = " Количество правильно решенных задач " , example = 12 )
unsolved : int = Field ( . . . , description = " Количество нерешенных или неправильно решенных задач " , example = 3 )
# =============================================================================
# МОДЕЛИ СТАТИСТИКИ ИГР
# =============================================================================
class GameModeStats ( BaseModel ) :
"""
Статистика игр для конкретного режима ( Bullet , Blitz , Rapid и т . д . ) .
Содержит полную статистику по играм в определенном временном формате :
- Количество сыгранных игр
- Изменение рейтинга за период
- Текущий рейтинг
- Результаты игр ( победы , поражения , ничьи )
"""
games_played : int = Field ( . . . , description = " Общее количество сыгранных игр " , example = 8 )
rating_change : int = Field ( . . . , description = " Изменение рейтинга (может быть отрицательным) " , example = 15 )
final_rating : int = Field ( . . . , description = " Текущий рейтинг игрока " , example = 2850 )
wins : int = Field ( . . . , description = " Количество побед " , example = 5 )
losses : int = Field ( . . . , description = " Количество поражений " , example = 2 )
draws : int = Field ( . . . , description = " Количество ничьих " , example = 1 )
class GamesStats ( BaseModel ) :
"""
Статистика игр по всем режимам .
Агрегирует статистику игр по всем временным форматам :
- Bullet : быстрые игры ( 1 - 3 минуты )
- Blitz : блиц игры ( 3 - 10 минут )
- Rapid : рапид игры ( 10 + минут )
2025-10-31 19:24:27 +03:00
- Classical : классические игры ( 30 + минут )
2025-10-26 20:23:26 +03:00
"""
bullet : GameModeStats = Field ( . . . , description = " Статистика Bullet игр (1-3 минуты) " )
blitz : GameModeStats = Field ( . . . , description = " Статистика Blitz игр (3-10 минут) " )
rapid : GameModeStats = Field ( . . . , description = " Статистика Rapid игр (10+ минут) " )
2025-10-31 19:24:27 +03:00
classical : GameModeStats = Field ( . . . , description = " Статистика Classical игр (30+ минут) " )
2025-10-26 20:23:26 +03:00
class UserStats ( BaseModel ) :
"""
Полная статистика пользователя .
Содержит всю доступную статистику по пользователю :
- Статистику решения задач ( пазлов )
- Статистику игр по всем режимам
"""
username : str = Field ( . . . , description = " Имя пользователя на Lichess " , example = " magnus " )
tasks : TaskStats = Field ( . . . , description = " Статистика решения задач " )
games : GamesStats = Field ( . . . , description = " Статистика игр по всем режимам " )
# =============================================================================
# МОДЕЛИ О Т В Е Т О В API
# =============================================================================
class ActivityResponse ( BaseModel ) :
"""
Стандартный ответ API с результатами запроса статистики .
Используется для всех эндпоинтов статистики ( сегодня , вчера , неделя ) .
Содержит сообщение о результате и данные статистики пользователя .
"""
message : str = Field ( . . . , description = " Сообщение о результате запроса " , example = " Статистика за сегодняшний день " )
data : Optional [ UserStats ] = Field ( None , description = " Данные статистики пользователя (null если пользователь не найден или неактивен) " )
class ErrorResponse ( BaseModel ) :
"""
Модель для стандартизированных ошибок API .
Используется для возврата структурированных ошибок с дополнительной информацией .
"""
detail : str = Field ( . . . , description = " Описание ошибки " , example = " Пользователь не найден " )
error_code : Optional [ str ] = Field ( None , description = " Код ошибки " , example = " USER_NOT_FOUND " )
timestamp : Optional [ str ] = Field ( None , description = " Время возникновения ошибки " , example = " 2024-01-15T10:30:00Z " )
class HealthResponse ( BaseModel ) :
"""
Ответ для health check эндпоинта .
Используется для мониторинга состояния сервиса и проверки е г о работоспособности .
"""
status : str = Field ( . . . , description = " Статус сервиса " , example = " healthy " )
timestamp : str = Field ( . . . , description = " Время проверки " , example = " 2024-01-15T10:30:00Z " )
service : str = Field ( . . . , description = " Название сервиса " , example = " Lichess Statistics API " )
# =============================================================================
# МОДЕЛИ ДЛЯ ЭНДПОИНТА СТАТИСТИКИ ИГР З А ПЕРИОД
# =============================================================================
class GamePlayer ( BaseModel ) :
"""
Информация о б игроке в партии .
Содержит данные о пользователе , е г о рейтинге и изменении рейтинга в конкретной игре .
"""
user : Optional [ Dict [ str , Any ] ] = Field ( None , description = " Информация о пользователе " )
rating : Optional [ int ] = Field ( None , description = " Рейтинг игрока " )
ratingDiff : Optional [ int ] = Field ( None , description = " Изменение рейтинга " )
2026-07-03 09:38:51 +00:00
analysis : Optional [ Dict [ str , Any ] ] = Field ( None , description = " Пост-анализ партии (только если игрок запускал анализ на Lichess), содержит поле ' accuracy ' " )
2025-10-26 20:23:26 +03:00
class Game ( BaseModel ) :
"""
Модель игры из Lichess API .
Содержит полную информацию о б игре , включая :
- Метаданные игры ( ID , время создания , статус )
- Информацию о б игроках и их рейтингах
- Результат игры и ходы в PGN формате
"""
id : str = Field ( . . . , description = " ID игры " )
rated : bool = Field ( . . . , description = " Рейтинговая ли игра " )
variant : str = Field ( . . . , description = " Вариант игры " )
speed : str = Field ( . . . , description = " Скорость игры (bullet, blitz, rapid, classical, correspondence) " )
perf : str = Field ( . . . , description = " Тип производительности " )
createdAt : int = Field ( . . . , description = " Время создания игры (timestamp) " )
lastMoveAt : int = Field ( . . . , description = " Время последнего хода (timestamp) " )
status : str = Field ( . . . , description = " Статус игры " )
players : Dict [ str , GamePlayer ] = Field ( . . . , description = " Игроки (white, black) " )
winner : Optional [ str ] = Field ( None , description = " Победитель (white, black или null) " )
moves : str = Field ( . . . , description = " Ходы игры в PGN формате " )
2026-07-03 09:38:51 +00:00
class GameRow ( BaseModel ) :
"""
Одна отдельная партия для построчного разбора ( today / yesterday / периодические уведомления ) .
Порядок игроков — как в партии ( белые / чёрные ) , а не " отслеживаемый пользователь/соперник " .
"""
white_name : str = Field ( . . . , description = " Имя игрока белыми " )
black_name : str = Field ( . . . , description = " Имя игрока чёрными " )
white_rating : Optional [ int ] = Field ( None , description = " Рейтинг белых " )
black_rating : Optional [ int ] = Field ( None , description = " Рейтинг чёрных " )
white_accuracy : Optional [ float ] = Field ( None , description = " Точность белых, если партия анализировалась " )
black_accuracy : Optional [ float ] = Field ( None , description = " Точность чёрных, если партия анализировалась " )
result : str = Field ( . . . , description = " Результат партии: ' 1-0 ' , ' 0-1 ' или ' 1/2-1/2 ' " )
created_at : int = Field ( . . . , description = " Время создания партии (Unix timestamp в миллисекундах) " )
tracked_is_white : Optional [ bool ] = Field ( None , description = " True, если отслеживаемый пользователь играл белыми; False — чёрными; None — не удалось определить сторону " )
2025-10-26 20:23:26 +03:00
class GameStats ( BaseModel ) :
"""
Статистика игр по конкретному типу ( Bullet , Blitz , Rapid и т . д . ) .
Содержит агрегированную статистику по играм определенного типа :
- Количество сыгранных игр
- Результаты игр ( победы , поражения , ничьи )
- Общее изменение рейтинга
- Итоговый рейтинг после последней игры
"""
games_played : int = Field ( . . . , description = " Общее количество сыгранных игр " , example = 10 )
wins : int = Field ( . . . , description = " Количество побед " , example = 6 )
losses : int = Field ( . . . , description = " Количество поражений " , example = 3 )
draws : int = Field ( . . . , description = " Количество ничьих " , example = 1 )
rating_change : int = Field ( . . . , description = " Общее изменение рейтинга " , example = 15 )
rating : Optional [ int ] = Field ( None , description = " Итоговый рейтинг после последней игры (только если games_played > 0) " , example = 2850 )
2026-07-03 09:38:51 +00:00
accuracy : Optional [ float ] = Field ( None , description = " Средняя точность отслеживаемого пользователя по проанализированным партиям этого режима; None если нет проанализированных партий " , example = 82.5 )
games : Optional [ List [ GameRow ] ] = Field ( None , description = " Построчный список отдельных партий (только blitz/rapid/classical), заполняется только если запрошено include_games=True " )
2025-10-26 20:23:26 +03:00
class GamesOfPeriodStats ( BaseModel ) :
"""
Статистика игр за период по всем типам .
Агрегирует статистику игр по всем временным форматам :
- Bullet , Blitz , Rapid , Classical , Correspondence
- Общая статистика по всем типам
"""
bullet : GameStats = Field ( . . . , description = " Статистика Bullet игр " )
blitz : GameStats = Field ( . . . , description = " Статистика Blitz игр " )
rapid : GameStats = Field ( . . . , description = " Статистика Rapid игр " )
classical : GameStats = Field ( . . . , description = " Статистика Classical игр " )
correspondence : GameStats = Field ( . . . , description = " Статистика Correspondence игр " )
total : GameStats = Field ( . . . , description = " Общая статистика по всем типам " )
class GamesOfPeriodResponse ( BaseModel ) :
"""
Ответ API с результатами запроса статистики игр за период .
Содержит метаинформацию о запросе и агрегированную статистику игр .
"""
message : str = Field ( . . . , description = " Сообщение о результате запроса " , example = " Статистика игр за период " )
2026-07-02 19:29:06 +00:00
success : bool = Field ( True , description = " False, если запрос к Lichess завершился ошибкой (а не легитимным нулевым результатом) " , example = True )
2025-10-26 20:23:26 +03:00
username : str = Field ( . . . , description = " Имя пользователя " , example = " magnus " )
period_start : int = Field ( . . . , description = " Начало периода (Unix timestamp) " , example = 1640995200 )
period_end : int = Field ( . . . , description = " Конец периода (Unix timestamp) " , example = 1641081600 )
games_count : int = Field ( . . . , description = " Общее количество игр " , example = 25 )
2025-11-16 13:24:39 +03:00
earliest_game_ts : Optional [ int ] = Field ( None , description = " Время самой старой найденной партии (Unix timestamp, секунды) " , example = 1638316800 )
2025-10-26 20:23:26 +03:00
data : Optional [ GamesOfPeriodStats ] = Field ( None , description = " Данные статистики игр " )
# =============================================================================
# МОДЕЛИ ДЛЯ ЭНДПОИНТА СТАТИСТИКИ ЗАДАЧ З А ПЕРИОД
# =============================================================================
class PuzzleActivity ( BaseModel ) :
"""
Активность по решению задачи ( пазла ) из Lichess API .
Содержит информацию о попытке решения задачи пользователем :
- ID задачи и время решения
- Результат решения ( решена / не решена )
- Дополнительная информация о задаче
"""
id : str = Field ( . . . , description = " ID задачи " , example = " abc123 " )
createdAt : int = Field ( . . . , description = " Время создания активности (timestamp в миллисекундах) " , example = 1640995200000 )
win : bool = Field ( . . . , description = " Решена ли задача правильно " , example = True )
puzzle : Dict [ str , Any ] = Field ( . . . , description = " Информация о задаче " )
class PuzzleStats ( BaseModel ) :
"""
Агрегированная статистика решения задач за период .
Содержит сводную информацию о решении задач :
- Общее количество попыток
- Количество успешных и неуспешных решений
- Процент успешности
"""
total_attempts : int = Field ( . . . , description = " Общее количество попыток решения " , example = 25 )
solved : int = Field ( . . . , description = " Количество решенных задач " , example = 18 )
failed : int = Field ( . . . , description = " Количество нерешенных задач " , example = 7 )
success_rate : float = Field ( . . . , description = " Процент успешных решений " , example = 72.0 )
class PuzzleOfPeriodResponse ( BaseModel ) :
"""
Ответ API с результатами запроса статистики решения задач за период .
Содержит метаинформацию о запросе и агрегированную статистику решения задач .
"""
message : str = Field ( . . . , description = " Сообщение о результате запроса " , example = " Статистика решения задач за период " )
2026-07-02 19:29:06 +00:00
success : bool = Field ( True , description = " False, если запрос к Lichess завершился ошибкой (а не легитимным нулевым результатом) " , example = True )
2026-07-05 07:37:14 +00:00
auth_failed : bool = Field ( False , description = " True, если Lichess отклонил токен (401/403) — permanent, не стоит ретраить; отличается от транзитных ошибок " )
2025-10-26 20:23:26 +03:00
period_start : int = Field ( . . . , description = " Начало периода (Unix timestamp в миллисекундах) " , example = 1640995200000 )
period_end : int = Field ( . . . , description = " Конец периода (Unix timestamp в миллисекундах) " , example = 1641081600000 )
max_puzzles : int = Field ( . . . , description = " Максимальное количество задач для получения " , example = 50 )
puzzles_in_period : int = Field ( . . . , description = " Количество задач в указанном периоде " , example = 15 )
data : Optional [ PuzzleStats ] = Field ( None , description = " Данные статистики решения задач " )