refactor: improve admin stats cache usage and etc
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy import Date, and_, cast, func
|
||||
from sqlalchemy import Date, and_, case, cast, func
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
from sqlalchemy.orm import joinedload, selectinload
|
||||
|
||||
from db.models import Payment, User
|
||||
|
||||
@@ -80,7 +80,7 @@ async def get_payment_by_db_id(session: AsyncSession, payment_db_id: int) -> Opt
|
||||
stmt = (
|
||||
select(Payment)
|
||||
.where(Payment.payment_id == payment_db_id)
|
||||
.options(selectinload(Payment.user), selectinload(Payment.promo_code_used))
|
||||
.options(joinedload(Payment.user), joinedload(Payment.promo_code_used))
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
@@ -108,7 +108,7 @@ async def get_recent_payment_logs_with_user(
|
||||
) -> List[Payment]:
|
||||
stmt = (
|
||||
select(Payment)
|
||||
.options(selectinload(Payment.user))
|
||||
.options(joinedload(Payment.user))
|
||||
.where(Payment.status == "succeeded")
|
||||
.order_by(Payment.created_at.desc())
|
||||
.limit(limit)
|
||||
@@ -211,45 +211,31 @@ async def get_financial_statistics(session: AsyncSession) -> Dict[str, Any]:
|
||||
"""Get comprehensive financial statistics."""
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from sqlalchemy import and_
|
||||
|
||||
now = datetime.utcnow()
|
||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
week_start = today_start - timedelta(days=7)
|
||||
month_start = today_start - timedelta(days=30)
|
||||
|
||||
# Today's revenue
|
||||
stmt_today = select(func.sum(Payment.amount)).where(
|
||||
and_(Payment.status == "succeeded", Payment.created_at >= today_start)
|
||||
)
|
||||
today_revenue = await session.execute(stmt_today)
|
||||
today_amount = today_revenue.scalar() or 0
|
||||
|
||||
# Week revenue
|
||||
stmt_week = select(func.sum(Payment.amount)).where(
|
||||
and_(Payment.status == "succeeded", Payment.created_at >= week_start)
|
||||
)
|
||||
week_revenue = await session.execute(stmt_week)
|
||||
week_amount = week_revenue.scalar() or 0
|
||||
|
||||
# Month revenue
|
||||
stmt_month = select(func.sum(Payment.amount)).where(
|
||||
and_(Payment.status == "succeeded", Payment.created_at >= month_start)
|
||||
)
|
||||
month_revenue = await session.execute(stmt_month)
|
||||
month_amount = month_revenue.scalar() or 0
|
||||
|
||||
# All time revenue
|
||||
stmt_all = select(func.sum(Payment.amount)).where(Payment.status == "succeeded")
|
||||
all_revenue = await session.execute(stmt_all)
|
||||
all_amount = all_revenue.scalar() or 0
|
||||
|
||||
# Count of successful payments today
|
||||
stmt_count_today = select(func.count(Payment.payment_id)).where(
|
||||
and_(Payment.status == "succeeded", Payment.created_at >= today_start)
|
||||
)
|
||||
today_count = await session.execute(stmt_count_today)
|
||||
today_payments_count = today_count.scalar() or 0
|
||||
revenue_stmt = select(
|
||||
func.coalesce(
|
||||
func.sum(case((Payment.created_at >= today_start, Payment.amount), else_=0)), 0
|
||||
),
|
||||
func.coalesce(
|
||||
func.sum(case((Payment.created_at >= week_start, Payment.amount), else_=0)), 0
|
||||
),
|
||||
func.coalesce(
|
||||
func.sum(case((Payment.created_at >= month_start, Payment.amount), else_=0)),
|
||||
0,
|
||||
),
|
||||
func.coalesce(func.sum(Payment.amount), 0),
|
||||
func.coalesce(func.sum(case((Payment.created_at >= today_start, 1), else_=0)), 0),
|
||||
).where(Payment.status == "succeeded")
|
||||
revenue_row = (await session.execute(revenue_stmt)).one()
|
||||
today_amount = revenue_row[0] or 0
|
||||
week_amount = revenue_row[1] or 0
|
||||
month_amount = revenue_row[2] or 0
|
||||
all_amount = revenue_row[3] or 0
|
||||
today_payments_count = int(revenue_row[4] or 0)
|
||||
|
||||
# Longer tail for admin dashboard charts (presets up to 1y + custom range on the client).
|
||||
daily_series = await _daily_revenue_series_utc(session, days=730)
|
||||
|
||||
+27
-36
@@ -4,7 +4,7 @@ import string
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from sqlalchemy import and_, delete, desc, func, or_, update
|
||||
from sqlalchemy import and_, case, delete, desc, func, or_, update
|
||||
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.future import select
|
||||
@@ -591,55 +591,46 @@ async def get_enhanced_user_statistics(session: AsyncSession) -> Dict[str, Any]:
|
||||
now = datetime.now(timezone.utc)
|
||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
# Total users
|
||||
total_users_stmt = select(func.count(User.user_id))
|
||||
total_users = (await session.execute(total_users_stmt)).scalar() or 0
|
||||
|
||||
# Banned users
|
||||
banned_users_stmt = select(func.count(User.user_id)).where(User.is_banned == True)
|
||||
banned_users = (await session.execute(banned_users_stmt)).scalar() or 0
|
||||
|
||||
# Active users today (proxy: registered today)
|
||||
active_today_stmt = select(func.count(User.user_id)).where(
|
||||
User.registration_date >= today_start
|
||||
user_counts_stmt = select(
|
||||
func.count(User.user_id),
|
||||
func.coalesce(func.sum(case((User.is_banned == True, 1), else_=0)), 0),
|
||||
func.coalesce(func.sum(case((User.registration_date >= today_start, 1), else_=0)), 0),
|
||||
func.coalesce(func.sum(case((User.referred_by_id.is_not(None), 1), else_=0)), 0),
|
||||
)
|
||||
active_today = (await session.execute(active_today_stmt)).scalar() or 0
|
||||
user_counts = (await session.execute(user_counts_stmt)).one()
|
||||
total_users = int(user_counts[0] or 0)
|
||||
banned_users = int(user_counts[1] or 0)
|
||||
active_today = int(user_counts[2] or 0)
|
||||
referral_users = int(user_counts[3] or 0)
|
||||
|
||||
# Users with active paid subscriptions (non-trial providers only)
|
||||
paid_subs_stmt = (
|
||||
select(func.count(func.distinct(Subscription.user_id)))
|
||||
subscription_counts_stmt = (
|
||||
select(
|
||||
func.count(
|
||||
func.distinct(
|
||||
case((Subscription.provider.is_not(None), Subscription.user_id), else_=None)
|
||||
)
|
||||
),
|
||||
func.count(
|
||||
func.distinct(
|
||||
case((Subscription.provider.is_(None), Subscription.user_id), else_=None)
|
||||
)
|
||||
),
|
||||
)
|
||||
.join(User, Subscription.user_id == User.user_id)
|
||||
.where(
|
||||
and_(
|
||||
Subscription.is_active == True,
|
||||
Subscription.end_date > now,
|
||||
Subscription.provider.is_not(None), # Not trial
|
||||
)
|
||||
)
|
||||
)
|
||||
paid_subs_users = (await session.execute(paid_subs_stmt)).scalar() or 0
|
||||
|
||||
# Users on trial period
|
||||
trial_subs_stmt = (
|
||||
select(func.count(func.distinct(Subscription.user_id)))
|
||||
.join(User, Subscription.user_id == User.user_id)
|
||||
.where(
|
||||
and_(
|
||||
Subscription.is_active == True,
|
||||
Subscription.end_date > now,
|
||||
Subscription.provider.is_(None), # Trial subscriptions
|
||||
)
|
||||
)
|
||||
)
|
||||
trial_users = (await session.execute(trial_subs_stmt)).scalar() or 0
|
||||
subscription_counts = (await session.execute(subscription_counts_stmt)).one()
|
||||
paid_subs_users = int(subscription_counts[0] or 0)
|
||||
trial_users = int(subscription_counts[1] or 0)
|
||||
|
||||
# Inactive users (no active subscription)
|
||||
inactive_users = total_users - paid_subs_users - trial_users - banned_users
|
||||
|
||||
# Users attracted via referral
|
||||
referral_users_stmt = select(func.count(User.user_id)).where(User.referred_by_id.is_not(None))
|
||||
referral_users = (await session.execute(referral_users_stmt)).scalar() or 0
|
||||
|
||||
return {
|
||||
"total_users": total_users,
|
||||
"banned_users": banned_users,
|
||||
|
||||
Reference in New Issue
Block a user