import logging from datetime import datetime, timedelta from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.database.models import SubscriptionConversion, User logger = logging.getLogger(__name__) async def create_subscription_conversion( db: AsyncSession, user_id: int, trial_duration_days: int, payment_method: str, first_payment_amount_kopeks: int, first_paid_period_days: int, ) -> SubscriptionConversion: conversion = SubscriptionConversion( user_id=user_id, converted_at=datetime.utcnow(), trial_duration_days=trial_duration_days, payment_method=payment_method, first_payment_amount_kopeks=first_payment_amount_kopeks, first_paid_period_days=first_paid_period_days, ) db.add(conversion) await db.commit() await db.refresh(conversion) logger.info( f'✅ Создана запись о конверсии для пользователя {user_id}: {trial_duration_days} дн. → {first_paid_period_days} дн. за {first_payment_amount_kopeks / 100}₽' ) return conversion async def get_conversion_by_user_id(db: AsyncSession, user_id: int) -> SubscriptionConversion | None: result = await db.execute( select(SubscriptionConversion) .where(SubscriptionConversion.user_id == user_id) .order_by(SubscriptionConversion.converted_at.desc()) .limit(1) ) return result.scalar_one_or_none() async def get_conversion_statistics(db: AsyncSession) -> dict: from app.database.models import Subscription # Получаем количество записей о конверсиях в таблице total_conversions_result = await db.execute(select(func.count(SubscriptionConversion.id))) total_conversions = total_conversions_result.scalar() or 0 # Подсчитываем пользователей с платными подписками users_with_paid_result = await db.execute(select(func.count(User.id)).where(User.has_had_paid_subscription == True)) users_with_paid = users_with_paid_result.scalar() or 0 # Подсчитываем всех пользователей с подписками (использовавших триал) # Считаем что все новые пользователи начинают с триала total_users_with_subscriptions_result = await db.execute(select(func.count(func.distinct(Subscription.user_id)))) total_users_with_subscriptions = total_users_with_subscriptions_result.scalar() or 0 # Расчёт конверсии: (оплатившие) / (всего с подписками) * 100 # Это показывает какой % пользователей, получивших подписку, в итоге оплатили if total_users_with_subscriptions > 0: conversion_rate = round((users_with_paid / total_users_with_subscriptions) * 100, 1) else: conversion_rate = 0.0 avg_trial_duration_result = await db.execute(select(func.avg(SubscriptionConversion.trial_duration_days))) avg_trial_duration = avg_trial_duration_result.scalar() or 0 avg_first_payment_result = await db.execute(select(func.avg(SubscriptionConversion.first_payment_amount_kopeks))) avg_first_payment = avg_first_payment_result.scalar() or 0 month_ago = datetime.utcnow() - timedelta(days=30) month_conversions_result = await db.execute( select(func.count(SubscriptionConversion.id)).where(SubscriptionConversion.converted_at >= month_ago) ) month_conversions = month_conversions_result.scalar() or 0 logger.info('📊 Статистика конверсий:') logger.info(f' Всего пользователей с подписками: {total_users_with_subscriptions}') logger.info(f' Оплативших подписку: {users_with_paid}') logger.info(f' Рассчитанная конверсия: {conversion_rate}%') return { 'total_conversions': total_conversions, 'conversion_rate': conversion_rate, 'users_with_trial': total_users_with_subscriptions, 'converted_users': users_with_paid, 'avg_trial_duration_days': round(avg_trial_duration, 1), 'avg_first_payment_rubles': round((avg_first_payment or 0) / 100, 2), 'month_conversions': month_conversions, } async def get_users_had_trial_count(db: AsyncSession) -> int: conversions_count_result = await db.execute(select(func.count(func.distinct(SubscriptionConversion.user_id)))) conversions_count = conversions_count_result.scalar() paid_users_result = await db.execute(select(func.count(User.id)).where(User.has_had_paid_subscription == True)) paid_users_count = paid_users_result.scalar() return max(conversions_count, paid_users_count)