from __future__ import annotations from datetime import datetime from app.database.crud.referral import get_referral_statistics from app.database.crud.subscription import get_subscriptions_statistics, get_trial_statistics from app.database.crud.transaction import get_transactions_statistics from app.database.crud.user import get_users_statistics from fastapi import APIRouter, Depends, Security from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.database.models import ( Subscription, SubscriptionStatus, Ticket, TicketStatus, Transaction, TransactionType, User, UserStatus, ) from ..dependencies import get_db_session, require_api_token router = APIRouter() def _kopeks_to_rubles(value: int | float | None) -> float: return round((value or 0) / 100, 2) async def _get_overview(db: AsyncSession) -> dict[str, object]: total_users = await db.scalar(select(func.count()).select_from(User)) or 0 active_users = await db.scalar( select(func.count()).select_from(User).where(User.status == UserStatus.ACTIVE.value) ) or 0 blocked_users = await db.scalar( select(func.count()).select_from(User).where(User.status == UserStatus.BLOCKED.value) ) or 0 total_balance_kopeks = await db.scalar( select(func.coalesce(func.sum(User.balance_kopeks), 0)) ) or 0 active_subscriptions = await db.scalar( select(func.count()).select_from(Subscription).where( Subscription.status == SubscriptionStatus.ACTIVE.value, ) ) or 0 expired_subscriptions = await db.scalar( select(func.count()).select_from(Subscription).where( Subscription.status == SubscriptionStatus.EXPIRED.value, ) ) or 0 pending_tickets = await db.scalar( select(func.count()).select_from(Ticket).where( Ticket.status.in_([TicketStatus.OPEN.value, TicketStatus.ANSWERED.value]) ) ) or 0 today = datetime.utcnow().date() today_transactions = await db.scalar( select(func.coalesce(func.sum(Transaction.amount_kopeks), 0)).where( func.date(Transaction.created_at) == today, Transaction.type == TransactionType.DEPOSIT.value, ) ) or 0 return { "users": { "total": total_users, "active": active_users, "blocked": blocked_users, "balance_kopeks": int(total_balance_kopeks), "balance_rubles": _kopeks_to_rubles(total_balance_kopeks), }, "subscriptions": { "active": active_subscriptions, "expired": expired_subscriptions, }, "support": { "open_tickets": pending_tickets, }, "payments": { "today_kopeks": int(today_transactions), "today_rubles": _kopeks_to_rubles(today_transactions), }, } @router.get( "/overview", summary="Общая статистика", response_description="Агрегированные показатели пользователей, подписок, саппорта и платежей", responses={ 200: { "content": { "application/json": { "example": { "users": { "total": 12345, "active": 9876, "blocked": 321, "balance_kopeks": 1234567, "balance_rubles": 12345.67, }, "subscriptions": { "active": 4321, "expired": 210, }, "support": { "open_tickets": 42, }, "payments": { "today_kopeks": 654321, "today_rubles": 6543.21, }, } } } } }, ) async def stats_overview( _: object = Security(require_api_token), db: AsyncSession = Depends(get_db_session), ) -> dict[str, object]: return await _get_overview(db) @router.get( "/full", summary="Полная статистика", response_description="Расширенные показатели пользователей, подписок, платежей и рефералов", responses={ 200: { "content": { "application/json": { "example": { "overview": { "users": { "total": 12345, "active": 9876, "blocked": 321, "balance_kopeks": 1234567, "balance_rubles": 12345.67, }, "subscriptions": { "active": 4321, "expired": 210, }, "support": { "open_tickets": 42, }, "payments": { "today_kopeks": 654321, "today_rubles": 6543.21, }, }, "users": { "total_users": 12345, "active_users": 9876, "blocked_users": 321, "new_today": 12, "new_week": 345, "new_month": 1234, }, "subscriptions": { "total_subscriptions": 9876, "active_subscriptions": 8765, "trial_subscriptions": 321, "paid_subscriptions": 8444, "purchased_today": 12, "purchased_week": 210, "purchased_month": 765, "trial_to_paid_conversion": 42.5, "renewals_count": 123, "trial_statistics": { "used_trials": 555, "active_trials": 210, "resettable_trials": 42, }, }, "transactions": { "period": { "start_date": "2024-06-01T00:00:00Z", "end_date": "2024-06-30T23:59:59Z", }, "totals": { "income_kopeks": 1234567, "income_rubles": 12345.67, "expenses_kopeks": 21000, "expenses_rubles": 210, "profit_kopeks": 1213567, "profit_rubles": 12135.67, "subscription_income_kopeks": 987654, "subscription_income_rubles": 9876.54, }, "today": { "transactions_count": 42, "income_kopeks": 654321, "income_rubles": 6543.21, }, "by_type": { "deposit": {"count": 123, "amount": 1234567}, "withdrawal": {"count": 10, "amount": 21000}, }, "by_payment_method": { "card": {"count": 100, "amount": 1000000} }, }, "referrals": { "users_with_referrals": 4321, "active_referrers": 123, "total_paid_kopeks": 765432, "total_paid_rubles": 7654.32, "today_earnings_kopeks": 12345, "today_earnings_rubles": 123.45, "week_earnings_kopeks": 23456, "week_earnings_rubles": 234.56, "month_earnings_kopeks": 34567, "month_earnings_rubles": 345.67, "top_referrers": [ { "user_id": 123456789, "display_name": "@testuser", "username": "testuser", "telegram_id": 123456789, "total_earned_kopeks": 54321, "referrals_count": 42, } ], }, } } } } }, ) async def stats_full( _: object = Security(require_api_token), db: AsyncSession = Depends(get_db_session), ) -> dict[str, object]: overview = await _get_overview(db) users_stats = await get_users_statistics(db) subscriptions_stats = await get_subscriptions_statistics(db) trial_stats = await get_trial_statistics(db) transactions_stats = await get_transactions_statistics(db) referral_stats = await get_referral_statistics(db) transactions_totals = transactions_stats.get("totals", {}) transactions_today = transactions_stats.get("today", {}) transactions_totals = { **transactions_totals, "income_rubles": _kopeks_to_rubles(transactions_totals.get("income_kopeks")), "expenses_rubles": _kopeks_to_rubles(transactions_totals.get("expenses_kopeks")), "profit_rubles": _kopeks_to_rubles(transactions_totals.get("profit_kopeks")), "subscription_income_rubles": _kopeks_to_rubles( transactions_totals.get("subscription_income_kopeks") ), } transactions_today = { **transactions_today, "income_rubles": _kopeks_to_rubles(transactions_today.get("income_kopeks")), } referral_stats = { **referral_stats, "total_paid_rubles": _kopeks_to_rubles(referral_stats.get("total_paid_kopeks")), "today_earnings_rubles": _kopeks_to_rubles( referral_stats.get("today_earnings_kopeks") ), "week_earnings_rubles": _kopeks_to_rubles(referral_stats.get("week_earnings_kopeks")), "month_earnings_rubles": _kopeks_to_rubles(referral_stats.get("month_earnings_kopeks")), } return { "overview": overview, "users": users_stats, "subscriptions": {**subscriptions_stats, "trial_statistics": trial_stats}, "transactions": { **transactions_stats, "totals": transactions_totals, "today": transactions_today, }, "referrals": referral_stats, }