""" Аналитика чата и экспорт данных """ import json import logging from datetime import datetime, timedelta, timezone 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) self.uly_tz = timezone(timedelta(hours=4)) async def get_general_stats(self) -> Dict[str, Any]: """Общая статистика чата (Оригинальная логика из Gitea)""" # Всего пользователей 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_susp = select(func.count(User.user_id)).where(User.spy_score >= 31).where(User.spy_score <= 60) suspicious_count = (await self.session.execute(stmt_susp)).scalar() or 0 stmt_spies = select(func.count(User.user_id)).where(User.spy_score > 60) spies_count = (await self.session.execute(stmt_spies)).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 'нет данных' return ', '.join([f'{i}. {r[1] or f"user{r[0]}"}: {r[2]}' for i, r in enumerate(rows, 1)]) async def export_to_json(self, full_history: bool = False) -> str: """ Экспорт в JSON. full_history=False -> за неделю (для планировщика) full_history=True -> всё (для кнопки в меню) """ now_local = datetime.now(self.uly_tz) prefix = "full_" if full_history else "weekly_" filename = f'chat_export_{prefix}{now_local.strftime("%Y%m%d_%H%M%S")}.json' filepath = self.export_dir / filename # Пользователи users = list((await self.session.execute(select(User).order_by(User.user_id))).scalars().all()) # Сообщения stmt = select(Message).order_by(Message.timestamp.asc()) if not full_history: week_ago = datetime.utcnow() - timedelta(days=7) stmt = stmt.where(Message.timestamp >= week_ago) messages = list((await self.session.execute(stmt)).scalars().all()) export_data = { 'export_date': now_local.isoformat(), 'is_full_history': full_history, 'summary': { 'total_users': len(users), 'total_messages': len(messages), }, 'users': [{ 'user_id': u.user_id, 'username': u.username, 'first_name': u.first_name, 'apartment': u.apartment, 'verified': u.verified, 'rating': u.rating } for u in users], 'messages': [{ 'id': m.id, 'user_id': m.user_id, 'text': m.text, 'date': m.timestamp.isoformat() if m.timestamp else None, 'topic': m.topic } for m in messages] } with open(filepath, 'w', encoding='utf-8') as f: json.dump(export_data, f, ensure_ascii=False, indent=2) return str(filepath)