""" Аналитика чата и экспорт данных """ import json import logging from datetime import datetime, timedelta from typing import Dict, List, Any from pathlib import Path from sqlalchemy import select, func, distinct from sqlalchemy.ext.asyncio import AsyncSession from database.models import User, Message from config import EXPORT_DIR logger = logging.getLogger(__name__) class ChatAnalytics: """Аналитика чата""" def __init__(self, session: AsyncSession): self.session = session self.export_dir = Path(EXPORT_DIR) self.export_dir.mkdir(parents=True, exist_ok=True) async def get_general_stats(self) -> Dict[str, Any]: """Общая статистика чата""" # Всего пользователей stmt = select(func.count(User.user_id)) result = await self.session.execute(stmt) total_users = result.scalar() or 0 # Верифицированы stmt = select(func.count(User.user_id)).where(User.verified == True) result = await self.session.execute(stmt) verified_users = result.scalar() or 0 # Сообщения всего stmt = select(func.count(Message.id)) result = await self.session.execute(stmt) total_messages = result.scalar() or 0 # Сообщения за неделю week_ago = datetime.utcnow() - timedelta(days=7) stmt = ( select(func.count(Message.id)) .where(Message.timestamp >= week_ago) ) result = await self.session.execute(stmt) messages_week = result.scalar() or 0 # Сообщения за день day_ago = datetime.utcnow() - timedelta(days=1) stmt = ( select(func.count(Message.id)) .where(Message.timestamp >= day_ago) ) result = await self.session.execute(stmt) messages_day = result.scalar() or 0 # Подозрительные stmt = ( select(func.count(User.user_id)) .where(User.spy_score >= 31) .where(User.spy_score <= 60) ) result = await self.session.execute(stmt) suspicious_count = result.scalar() or 0 # Шпионы stmt = ( select(func.count(User.user_id)) .where(User.spy_score > 60) ) result = await self.session.execute(stmt) spies_count = result.scalar() or 0 # Топ пользователей за неделю top_users = await self._get_top_users(week_ago, limit=5) return { 'total_users': total_users, 'verified_users': verified_users, 'unverified_users': total_users - verified_users, 'total_messages': total_messages, 'messages_week': messages_week, 'messages_day': messages_day, 'suspicious_count': suspicious_count, 'spies_count': spies_count, 'top_users': top_users, } async def _get_top_users(self, since: datetime, limit: int = 5) -> str: """Топ пользователей по активности""" stmt = ( select(User.user_id, User.first_name, func.count(Message.id).label('count')) .join(Message, User.user_id == Message.user_id) .where(Message.timestamp >= since) .group_by(User.user_id, User.first_name) .order_by(func.count(Message.id).desc()) .limit(limit) ) result = await self.session.execute(stmt) rows = result.all() if not rows: return 'нет данных' lines = [] for i, (user_id, name, count) in enumerate(rows, 1): display_name = name or f'user{user_id}' lines.append(f'{i}. {display_name}: {count}') return ', '.join(lines) async def export_to_json(self) -> str: """ Экспорт данных чата в JSON Возвращает путь к файлу """ timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S') filename = f'chat_export_{timestamp}.json' filepath = self.export_dir / filename # Получаем всех пользователей stmt = select(User).order_by(User.join_date.desc()) result = await self.session.execute(stmt) users = list(result.scalars().all()) # Получаем сообщения за последнюю неделю week_ago = datetime.utcnow() - timedelta(days=7) stmt = ( select(Message) .where(Message.timestamp >= week_ago) .order_by(Message.timestamp.desc()) .limit(10000) # Ограничение на количество ) result = await self.session.execute(stmt) messages = list(result.scalars().all()) # Формируем экспорт export_data = { 'export_date': datetime.utcnow().isoformat(), 'chat_id': None, # Заполнится при использовании 'period': { 'from': week_ago.isoformat(), 'to': datetime.utcnow().isoformat(), }, 'summary': { 'total_users': len(users), 'verified_users': sum(1 for u in users if u.verified), 'total_messages': len(messages), }, 'users': [ { 'user_id': u.user_id, 'username': u.username, 'first_name': u.first_name, 'last_name': u.last_name, 'apartment': u.apartment, 'verified': u.verified, 'join_date': u.join_date.isoformat() if u.join_date else None, 'message_count': u.message_count, 'spy_score': u.spy_score, 'spy_flags': json.loads(u.spy_flags) if u.spy_flags else [], } for u in users ], 'messages': [ { 'message_id': m.message_id, 'user_id': m.user_id, 'text': m.text[:500] if m.text else None, # Обрезаем длинные 'timestamp': m.timestamp.isoformat() if m.timestamp else None, 'reply_to': m.reply_to_message_id, } for m in messages ] } # Сохраняем with open(filepath, 'w', encoding='utf-8') as f: json.dump(export_data, f, ensure_ascii=False, indent=2) logger.info(f'Экспорт сохранён: {filepath}') return str(filepath) async def get_activity_report(self, days: int = 7) -> Dict[str, Any]: """Отчёт по активности за период""" since = datetime.utcnow() - timedelta(days=days) # Активные пользователи stmt = ( select(distinct(Message.user_id)) .where(Message.timestamp >= since) ) result = await self.session.execute(stmt) active_users = len(list(result.scalars().all())) # Самые активные часы stmt = ( select( func.strftime('%H', Message.timestamp).label('hour'), func.count().label('count') ) .where(Message.timestamp >= since) .group_by(func.strftime('%H', Message.timestamp)) .order_by(func.count().desc()) .limit(5) ) result = await self.session.execute(stmt) peak_hours = [(row.hour, row.count) for row in result.all()] return { 'active_users': active_users, 'peak_hours': peak_hours, 'period_days': days, }