Update GPT and add Profile
This commit is contained in:
@@ -14,12 +14,10 @@ from app.schemas import (
|
||||
ChallengesPreviewResponse,
|
||||
ChallengesSaveRequest,
|
||||
)
|
||||
from app.services.gpt import GPTService
|
||||
from app.services.gpt import gpt_service
|
||||
|
||||
router = APIRouter(tags=["challenges"])
|
||||
|
||||
gpt_service = GPTService()
|
||||
|
||||
|
||||
async def get_challenge_or_404(db, challenge_id: int) -> Challenge:
|
||||
result = await db.execute(
|
||||
@@ -215,22 +213,35 @@ async def preview_challenges(marathon_id: int, current_user: CurrentUser, db: Db
|
||||
if not games:
|
||||
raise HTTPException(status_code=400, detail="No approved games in marathon")
|
||||
|
||||
preview_challenges = []
|
||||
# Filter games that don't have challenges yet
|
||||
games_to_generate = []
|
||||
game_map = {}
|
||||
for game in games:
|
||||
# Check if game already has challenges
|
||||
existing = await db.scalar(
|
||||
select(Challenge.id).where(Challenge.game_id == game.id).limit(1)
|
||||
)
|
||||
if existing:
|
||||
continue # Skip if already has challenges
|
||||
if not existing:
|
||||
games_to_generate.append({
|
||||
"id": game.id,
|
||||
"title": game.title,
|
||||
"genre": game.genre
|
||||
})
|
||||
game_map[game.id] = game.title
|
||||
|
||||
try:
|
||||
challenges_data = await gpt_service.generate_challenges(game.title, game.genre)
|
||||
if not games_to_generate:
|
||||
return ChallengesPreviewResponse(challenges=[])
|
||||
|
||||
# Generate challenges for all games in one API call
|
||||
preview_challenges = []
|
||||
try:
|
||||
challenges_by_game = await gpt_service.generate_challenges(games_to_generate)
|
||||
|
||||
for game_id, challenges_data in challenges_by_game.items():
|
||||
game_title = game_map.get(game_id, "Unknown")
|
||||
for ch_data in challenges_data:
|
||||
preview_challenges.append(ChallengePreview(
|
||||
game_id=game.id,
|
||||
game_title=game.title,
|
||||
game_id=game_id,
|
||||
game_title=game_title,
|
||||
title=ch_data.title,
|
||||
description=ch_data.description,
|
||||
type=ch_data.type,
|
||||
@@ -241,9 +252,8 @@ async def preview_challenges(marathon_id: int, current_user: CurrentUser, db: Db
|
||||
proof_hint=ch_data.proof_hint,
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
# Log error but continue with other games
|
||||
print(f"Error generating challenges for {game.title}: {e}")
|
||||
except Exception as e:
|
||||
print(f"Error generating challenges: {e}")
|
||||
|
||||
return ChallengesPreviewResponse(challenges=preview_challenges)
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
from fastapi import APIRouter, HTTPException, status, UploadFile, File
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import select, func
|
||||
|
||||
from app.api.deps import DbSession, CurrentUser
|
||||
from app.core.config import settings
|
||||
from app.models import User
|
||||
from app.schemas import UserPublic, UserUpdate, TelegramLink, MessageResponse
|
||||
from app.core.security import verify_password, get_password_hash
|
||||
from app.models import User, Participant, Assignment, Marathon
|
||||
from app.models.assignment import AssignmentStatus
|
||||
from app.models.marathon import MarathonStatus
|
||||
from app.schemas import (
|
||||
UserPublic, UserUpdate, TelegramLink, MessageResponse,
|
||||
PasswordChange, UserStats, UserProfilePublic,
|
||||
)
|
||||
from app.services.storage import storage_service
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["users"])
|
||||
@@ -125,3 +131,142 @@ async def unlink_telegram(
|
||||
await db.commit()
|
||||
|
||||
return MessageResponse(message="Telegram account unlinked successfully")
|
||||
|
||||
|
||||
@router.post("/me/password", response_model=MessageResponse)
|
||||
async def change_password(
|
||||
data: PasswordChange,
|
||||
current_user: CurrentUser,
|
||||
db: DbSession,
|
||||
):
|
||||
"""Смена пароля текущего пользователя"""
|
||||
if not verify_password(data.current_password, current_user.password_hash):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Неверный текущий пароль",
|
||||
)
|
||||
|
||||
if data.current_password == data.new_password:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Новый пароль должен отличаться от текущего",
|
||||
)
|
||||
|
||||
current_user.password_hash = get_password_hash(data.new_password)
|
||||
await db.commit()
|
||||
|
||||
return MessageResponse(message="Пароль успешно изменен")
|
||||
|
||||
|
||||
@router.get("/me/stats", response_model=UserStats)
|
||||
async def get_my_stats(current_user: CurrentUser, db: DbSession):
|
||||
"""Получить свою статистику"""
|
||||
return await _get_user_stats(current_user.id, db)
|
||||
|
||||
|
||||
@router.get("/{user_id}/stats", response_model=UserStats)
|
||||
async def get_user_stats(user_id: int, db: DbSession):
|
||||
"""Получить статистику пользователя"""
|
||||
result = await db.execute(select(User).where(User.id == user_id))
|
||||
user = result.scalar_one_or_none()
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="User not found",
|
||||
)
|
||||
|
||||
return await _get_user_stats(user_id, db)
|
||||
|
||||
|
||||
@router.get("/{user_id}/profile", response_model=UserProfilePublic)
|
||||
async def get_user_profile(user_id: int, db: DbSession):
|
||||
"""Получить публичный профиль пользователя со статистикой"""
|
||||
result = await db.execute(select(User).where(User.id == user_id))
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="User not found",
|
||||
)
|
||||
|
||||
stats = await _get_user_stats(user_id, db)
|
||||
|
||||
return UserProfilePublic(
|
||||
id=user.id,
|
||||
nickname=user.nickname,
|
||||
avatar_url=user.avatar_url,
|
||||
created_at=user.created_at,
|
||||
stats=stats,
|
||||
)
|
||||
|
||||
|
||||
async def _get_user_stats(user_id: int, db) -> UserStats:
|
||||
"""Вспомогательная функция для подсчета статистики пользователя"""
|
||||
|
||||
# 1. Количество марафонов (участий)
|
||||
marathons_result = await db.execute(
|
||||
select(func.count(Participant.id))
|
||||
.where(Participant.user_id == user_id)
|
||||
)
|
||||
marathons_count = marathons_result.scalar() or 0
|
||||
|
||||
# 2. Количество побед (1 место в завершенных марафонах)
|
||||
wins_count = 0
|
||||
user_participations = await db.execute(
|
||||
select(Participant)
|
||||
.join(Marathon, Marathon.id == Participant.marathon_id)
|
||||
.where(
|
||||
Participant.user_id == user_id,
|
||||
Marathon.status == MarathonStatus.FINISHED.value
|
||||
)
|
||||
)
|
||||
|
||||
for participation in user_participations.scalars():
|
||||
# Для каждого марафона проверяем, был ли пользователь первым
|
||||
max_points_result = await db.execute(
|
||||
select(func.max(Participant.total_points))
|
||||
.where(Participant.marathon_id == participation.marathon_id)
|
||||
)
|
||||
max_points = max_points_result.scalar() or 0
|
||||
|
||||
if participation.total_points == max_points and max_points > 0:
|
||||
# Проверяем что он единственный с такими очками (не ничья)
|
||||
count_with_max = await db.execute(
|
||||
select(func.count(Participant.id))
|
||||
.where(
|
||||
Participant.marathon_id == participation.marathon_id,
|
||||
Participant.total_points == max_points
|
||||
)
|
||||
)
|
||||
if count_with_max.scalar() == 1:
|
||||
wins_count += 1
|
||||
|
||||
# 3. Выполненных заданий
|
||||
completed_result = await db.execute(
|
||||
select(func.count(Assignment.id))
|
||||
.join(Participant, Participant.id == Assignment.participant_id)
|
||||
.where(
|
||||
Participant.user_id == user_id,
|
||||
Assignment.status == AssignmentStatus.COMPLETED.value
|
||||
)
|
||||
)
|
||||
completed_assignments = completed_result.scalar() or 0
|
||||
|
||||
# 4. Всего очков заработано
|
||||
points_result = await db.execute(
|
||||
select(func.coalesce(func.sum(Assignment.points_earned), 0))
|
||||
.join(Participant, Participant.id == Assignment.participant_id)
|
||||
.where(
|
||||
Participant.user_id == user_id,
|
||||
Assignment.status == AssignmentStatus.COMPLETED.value
|
||||
)
|
||||
)
|
||||
total_points_earned = points_result.scalar() or 0
|
||||
|
||||
return UserStats(
|
||||
marathons_count=marathons_count,
|
||||
wins_count=wins_count,
|
||||
completed_assignments=completed_assignments,
|
||||
total_points_earned=total_points_earned,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user