Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, Depends, HTTPException, status | |
| from sqlalchemy.orm import Session | |
| from sqlalchemy import func, and_, desc | |
| from typing import List | |
| from datetime import datetime, timedelta | |
| from database import get_db | |
| from models import User, UserStats, GameSession, GameAction, SystemStats | |
| from schemas import AdminStats, UserListItem, UserDetail, UserStatsResponse | |
| from auth import get_current_admin_user | |
| router = APIRouter(prefix="/api/admin", tags=["Admin"]) | |
| async def get_admin_stats( | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Общая статистика для админ-панели""" | |
| # Общее количество пользователей | |
| total_users = db.query(func.count(User.id)).scalar() | |
| # Активные пользователи сегодня (имели сессию за последние 24 часа) | |
| today = datetime.utcnow() - timedelta(days=1) | |
| active_users_today = db.query(func.count(func.distinct(GameSession.user_id))).filter( | |
| GameSession.started_at >= today | |
| ).scalar() | |
| # Новые пользователи сегодня | |
| new_users_today = db.query(func.count(User.id)).filter( | |
| User.created_at >= today | |
| ).scalar() | |
| # Всего сессий | |
| total_sessions = db.query(func.count(GameSession.id)).scalar() | |
| # Средняя длительность сессии | |
| avg_duration = db.query(func.avg(GameSession.duration)).filter( | |
| GameSession.ended_at != None | |
| ).scalar() or 0 | |
| # Общее время игры | |
| total_playtime = db.query(func.sum(UserStats.total_playtime)).scalar() or 0 | |
| # Общая статистика по комнатам | |
| total_rooms = db.query(func.sum(UserStats.rooms_visited)).scalar() or 0 | |
| # Общая статистика по предметам | |
| total_items = db.query(func.sum(UserStats.items_collected)).scalar() or 0 | |
| # Общая статистика по врагам | |
| total_enemies = db.query(func.sum(UserStats.enemies_defeated)).scalar() or 0 | |
| return AdminStats( | |
| total_users=total_users, | |
| active_users_today=active_users_today or 0, | |
| new_users_today=new_users_today or 0, | |
| total_sessions=total_sessions or 0, | |
| avg_session_duration=float(avg_duration), | |
| total_playtime=total_playtime, | |
| total_rooms_visited=total_rooms, | |
| total_items_collected=total_items, | |
| total_enemies_defeated=total_enemies | |
| ) | |
| async def get_all_users( | |
| skip: int = 0, | |
| limit: int = 100, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Список всех пользователей""" | |
| users = db.query(User).offset(skip).limit(limit).all() | |
| return users | |
| async def get_user_detail( | |
| user_id: int, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Детальная информация о пользователе""" | |
| user = db.query(User).filter(User.id == user_id).first() | |
| if not user: | |
| raise HTTPException(status_code=404, detail="User not found") | |
| return user | |
| async def toggle_user_active( | |
| user_id: int, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Активация/деактивация пользователя""" | |
| user = db.query(User).filter(User.id == user_id).first() | |
| if not user: | |
| raise HTTPException(status_code=404, detail="User not found") | |
| user.is_active = not user.is_active | |
| db.commit() | |
| return {"message": f"User {'activated' if user.is_active else 'deactivated'}", "is_active": user.is_active} | |
| async def toggle_user_admin( | |
| user_id: int, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Назначение/снятие прав администратора""" | |
| user = db.query(User).filter(User.id == user_id).first() | |
| if not user: | |
| raise HTTPException(status_code=404, detail="User not found") | |
| if user.id == current_user.id: | |
| raise HTTPException(status_code=400, detail="Cannot modify your own admin status") | |
| user.is_admin = not user.is_admin | |
| db.commit() | |
| return {"message": f"Admin rights {'granted' if user.is_admin else 'revoked'}", "is_admin": user.is_admin} | |
| async def delete_user( | |
| user_id: int, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Удаление пользователя""" | |
| user = db.query(User).filter(User.id == user_id).first() | |
| if not user: | |
| raise HTTPException(status_code=404, detail="User not found") | |
| if user.id == current_user.id: | |
| raise HTTPException(status_code=400, detail="Cannot delete yourself") | |
| db.delete(user) | |
| db.commit() | |
| return {"message": "User deleted successfully"} | |
| async def get_top_players( | |
| metric: str = "level", | |
| limit: int = 10, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Топ игроков по различным метрикам""" | |
| valid_metrics = ["level", "experience", "total_playtime", "enemies_defeated", "items_collected", "coins"] | |
| if metric not in valid_metrics: | |
| raise HTTPException(status_code=400, detail=f"Invalid metric. Choose from: {valid_metrics}") | |
| # Получаем топ игроков | |
| query = db.query(User, UserStats).join(UserStats).order_by(desc(getattr(UserStats, metric))).limit(limit) | |
| results = [] | |
| for user, stats in query: | |
| results.append({ | |
| "user_id": user.id, | |
| "username": user.username, | |
| "metric_value": getattr(stats, metric), | |
| "level": stats.level, | |
| "experience": stats.experience | |
| }) | |
| return {"metric": metric, "top_players": results} | |
| async def get_sessions_timeline( | |
| days: int = 7, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Временная шкала сессий за последние N дней""" | |
| start_date = datetime.utcnow() - timedelta(days=days) | |
| # Группировка по дням | |
| sessions_by_day = db.query( | |
| func.date(GameSession.started_at).label('date'), | |
| func.count(GameSession.id).label('count'), | |
| func.avg(GameSession.duration).label('avg_duration') | |
| ).filter( | |
| GameSession.started_at >= start_date | |
| ).group_by( | |
| func.date(GameSession.started_at) | |
| ).all() | |
| timeline = [] | |
| for day in sessions_by_day: | |
| timeline.append({ | |
| "date": str(day.date), | |
| "sessions_count": day.count, | |
| "avg_duration": float(day.avg_duration) if day.avg_duration else 0 | |
| }) | |
| return {"days": days, "timeline": timeline} | |
| async def get_activity_heatmap( | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Тепловая карта активности игроков по часам""" | |
| # Группировка по часам дня | |
| activity = db.query( | |
| func.extract('hour', GameSession.started_at).label('hour'), | |
| func.count(GameSession.id).label('count') | |
| ).group_by( | |
| func.extract('hour', GameSession.started_at) | |
| ).all() | |
| heatmap = {int(hour): count for hour, count in activity} | |
| # Заполняем отсутствующие часы нулями | |
| full_heatmap = {hour: heatmap.get(hour, 0) for hour in range(24)} | |
| return {"heatmap": full_heatmap} | |
| async def get_user_retention( | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Статистика удержания пользователей""" | |
| # Пользователи, зарегистрированные в последние 30 дней | |
| thirty_days_ago = datetime.utcnow() - timedelta(days=30) | |
| new_users = db.query(User).filter(User.created_at >= thirty_days_ago).all() | |
| # Из них активных в последние 7 дней | |
| seven_days_ago = datetime.utcnow() - timedelta(days=7) | |
| active_new_users = 0 | |
| for user in new_users: | |
| has_recent_session = db.query(GameSession).filter( | |
| GameSession.user_id == user.id, | |
| GameSession.started_at >= seven_days_ago | |
| ).first() | |
| if has_recent_session: | |
| active_new_users += 1 | |
| retention_rate = (active_new_users / len(new_users) * 100) if new_users else 0 | |
| return { | |
| "new_users_30d": len(new_users), | |
| "active_users_7d": active_new_users, | |
| "retention_rate": round(retention_rate, 2) | |
| } | |
| async def get_game_metrics( | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Метрики игрового процесса""" | |
| # Средние показатели | |
| avg_stats = db.query( | |
| func.avg(UserStats.level).label('avg_level'), | |
| func.avg(UserStats.energy).label('avg_energy'), | |
| func.avg(UserStats.hunger).label('avg_hunger'), | |
| func.avg(UserStats.coins).label('avg_coins'), | |
| func.avg(UserStats.deaths).label('avg_deaths') | |
| ).first() | |
| # Общие показатели | |
| total_stats = db.query( | |
| func.sum(UserStats.rooms_visited).label('total_rooms'), | |
| func.sum(UserStats.items_collected).label('total_items'), | |
| func.sum(UserStats.enemies_defeated).label('total_enemies'), | |
| func.sum(UserStats.deaths).label('total_deaths') | |
| ).first() | |
| return { | |
| "averages": { | |
| "level": float(avg_stats.avg_level) if avg_stats.avg_level else 0, | |
| "energy": float(avg_stats.avg_energy) if avg_stats.avg_energy else 0, | |
| "hunger": float(avg_stats.avg_hunger) if avg_stats.avg_hunger else 0, | |
| "coins": float(avg_stats.avg_coins) if avg_stats.avg_coins else 0, | |
| "deaths": float(avg_stats.avg_deaths) if avg_stats.avg_deaths else 0 | |
| }, | |
| "totals": { | |
| "rooms_visited": total_stats.total_rooms or 0, | |
| "items_collected": total_stats.total_items or 0, | |
| "enemies_defeated": total_stats.total_enemies or 0, | |
| "deaths": total_stats.total_deaths or 0 | |
| } | |
| } | |
| async def get_recent_actions( | |
| limit: int = 50, | |
| current_user: User = Depends(get_current_admin_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Последние действия игроков""" | |
| actions = db.query(GameAction, User).join(User).order_by( | |
| desc(GameAction.timestamp) | |
| ).limit(limit).all() | |
| results = [] | |
| for action, user in actions: | |
| results.append({ | |
| "id": action.id, | |
| "username": user.username, | |
| "action_type": action.action_type, | |
| "action_data": action.action_data, | |
| "timestamp": action.timestamp | |
| }) | |
| return {"recent_actions": results} | |