domovoy_bot/services/analytics.py

128 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Аналитика чата и экспорт данных
"""
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)