This commit is contained in:
2025-11-16 12:36:02 +09:00
parent 3a25e6a4cb
commit eb3f3807fd
61 changed files with 1438 additions and 1139 deletions

3
src/display/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""
Компоненты отображения и вывода результатов.
"""

102
src/display/conduct_draw.py Normal file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""
Скрипт для проведения розыгрыша
"""
import asyncio
import json
from ..core.database import async_session_maker
from ..core.services import LotteryService, ParticipationService
from ..core.models import Lottery, User
async def conduct_lottery_draw(lottery_id: int):
"""Проводим розыгрыш для указанного ID"""
print(f"🎲 Проведение розыгрыша #{lottery_id}")
async with async_session_maker() as session:
# Получаем розыгрыш
lottery = await LotteryService.get_lottery(session, lottery_id)
if not lottery:
print(f"❌ Розыгрыш {lottery_id} не найден")
return
print(f"📋 Розыгрыш: {lottery.title}")
# Призы уже загружены как список
prizes = lottery.prizes if isinstance(lottery.prizes, list) else json.loads(lottery.prizes)
print(f"🏆 Призы: {len(prizes)} шт.")
# Получаем участников
participants = await ParticipationService.get_participants(session, lottery_id)
print(f"👥 Участников: {len(participants)}")
if len(participants) == 0:
print("⚠️ Нет участников для розыгрыша")
return
# Выводим список участников
print("\n👥 Список участников:")
for i, user in enumerate(participants, 1):
accounts = user.account_number.split(',') if user.account_number else ['Нет счетов']
print(f" {i}. {user.first_name} (@{user.username}) - {len(accounts)} счет(ов)")
# Проводим розыгрыш
print(f"\n🎲 Проводим розыгрыш...")
# Используем метод из LotteryService
try:
winners = await LotteryService.conduct_draw(session, lottery_id)
if winners:
print(f"\n🎉 Победители определены:")
for i, winner_data in enumerate(winners, 1):
user = winner_data['user']
prize = winner_data['prize']
print(f" 🏆 {i} место: {user.first_name} (@{user.username})")
print(f" 💎 Приз: {prize}")
# Обновляем статус розыгрыша
await LotteryService.set_lottery_completed(session, lottery_id, True)
print(f"\n✅ Розыгрыш завершен и помечен как завершенный")
else:
print("❌ Ошибка при проведении розыгрыша")
except Exception as e:
print(f"💥 Ошибка: {e}")
async def main():
"""Главная функция"""
print("🎯 Скрипт проведения розыгрышей")
print("=" * 50)
# Показываем доступные розыгрыши
async with async_session_maker() as session:
lotteries = await LotteryService.get_active_lotteries(session)
if not lotteries:
print("❌ Нет активных розыгрышей")
return
print("🎲 Активные розыгрыши:")
for lottery in lotteries:
participants = await ParticipationService.get_participants(session, lottery.id)
print(f" {lottery.id}. {lottery.title} ({len(participants)} участников)")
# Просим выбрать розыгрыш
print("\nВведите ID розыгрыша для проведения (или 'all' для всех):")
choice = input("> ").strip().lower()
if choice == 'all':
# Проводим все розыгрыши
for lottery in lotteries:
await conduct_lottery_draw(lottery.id)
print("-" * 30)
else:
try:
lottery_id = int(choice)
await conduct_lottery_draw(lottery_id)
except ValueError:
print("❌ Неверный ID розыгрыша")
if __name__ == "__main__":
asyncio.run(main())

191
src/display/demo_admin.py Normal file
View File

@@ -0,0 +1,191 @@
"""
Демонстрация возможностей админ-панели
"""
import asyncio
from ..core.database import async_session_maker, init_db
from ..core.services import UserService, LotteryService
from ..utils.admin_utils import AdminUtils, ReportGenerator
async def demo_admin_features():
"""Демонстрация функций админ-панели"""
print("🚀 Демонстрация возможностей админ-панели")
print("=" * 50)
await init_db()
async with async_session_maker() as session:
# Создаем тестового администратора
admin = await UserService.get_or_create_user(
session,
telegram_id=123456789,
username="admin",
first_name="Администратор",
last_name="Системы"
)
await UserService.set_admin(session, 123456789, True)
print(f"✅ Создан администратор: {admin.first_name}")
# Создаем тестовых пользователей
users = []
for i in range(1, 11):
user = await UserService.get_or_create_user(
session,
telegram_id=200000000 + i,
username=f"user{i}",
first_name=f"Пользователь{i}",
last_name="Тестовый"
)
users.append(user)
print(f"✅ Создано {len(users)} тестовых пользователей")
# Создаем несколько розыгрышей
lottery1 = await LotteryService.create_lottery(
session,
title="🎉 Новогодний мега-розыгрыш",
description="Грандиозный розыгрыш к Новому году с невероятными призами!",
prizes=[
"🥇 iPhone 15 Pro Max 1TB",
"🥈 MacBook Air M2 13\"",
"🥉 iPad Pro 12.9\"",
"🏆 AirPods Pro 2",
"🎁 Подарочная карта Apple 50,000₽"
],
creator_id=admin.id
)
lottery2 = await LotteryService.create_lottery(
session,
title="🚗 Автомобильный розыгрыш",
description="Выиграй автомобиль своей мечты!",
prizes=[
"🥇 Tesla Model 3",
"🥈 BMW X3",
"🥉 Mercedes-Benz C-Class"
],
creator_id=admin.id
)
lottery3 = await LotteryService.create_lottery(
session,
title="🏖️ Отпуск мечты",
description="Путешествие в райские места",
prizes=[
"🥇 Тур на Мальдивы на двоих",
"🥈 Неделя в Дубае",
"🥉 Тур в Турцию"
],
creator_id=admin.id
)
print(f"✅ Создано 3 розыгрыша:")
print(f" - {lottery1.title}")
print(f" - {lottery2.title}")
print(f" - {lottery3.title}")
# Добавляем участников в розыгрыши
participants_added = 0
for user in users:
# В первый розыгрыш добавляем всех
if await LotteryService.add_participant(session, lottery1.id, user.id):
participants_added += 1
# Во второй - половину
if user.id % 2 == 0:
if await LotteryService.add_participant(session, lottery2.id, user.id):
participants_added += 1
# В третий - треть
if user.id % 3 == 0:
if await LotteryService.add_participant(session, lottery3.id, user.id):
participants_added += 1
print(f"✅ Добавлено {participants_added} участий")
# Устанавливаем предопределенных победителей
print("\n👑 Установка предопределенных победителей:")
# В первом розыгрыше: 1 и 3 места
await LotteryService.set_manual_winner(session, lottery1.id, 1, users[0].telegram_id)
await LotteryService.set_manual_winner(session, lottery1.id, 3, users[2].telegram_id)
print(f" 🎯 {lottery1.title}: 1 место - {users[0].first_name}, 3 место - {users[2].first_name}")
# Во втором розыгрыше: только 1 место
await LotteryService.set_manual_winner(session, lottery2.id, 1, users[1].telegram_id)
print(f" 🚗 {lottery2.title}: 1 место - {users[1].first_name}")
# Получаем статистику по розыгрышам
print(f"\n📊 Статистика по розыгрышам:")
for lottery in [lottery1, lottery2, lottery3]:
stats = await AdminUtils.get_lottery_statistics(session, lottery.id)
print(f" 🎲 {lottery.title}:")
print(f" Участников: {stats['participants_count']}")
print(f" Ручных победителей: {len(lottery.manual_winners or {})}")
# Проводим розыгрыши
print(f"\n🎲 Проведение розыгрышей:")
# Первый розыгрыш
results1 = await LotteryService.conduct_draw(session, lottery1.id)
print(f" 🎉 {lottery1.title} - результаты:")
for place, winner_info in results1.items():
user = winner_info['user']
manual = " 👑" if winner_info['is_manual'] else " 🎲"
print(f" {place}. {user.first_name}{manual}")
# Второй розыгрыш
results2 = await LotteryService.conduct_draw(session, lottery2.id)
print(f" 🚗 {lottery2.title} - результаты:")
for place, winner_info in results2.items():
user = winner_info['user']
manual = " 👑" if winner_info['is_manual'] else " 🎲"
print(f" {place}. {user.first_name}{manual}")
# Генерируем отчет
print(f"\n📋 Генерация отчета:")
report = await ReportGenerator.generate_summary_report(session)
print(report)
# Экспорт данных
print(f"💾 Экспорт данных первого розыгрыша:")
export_data = await AdminUtils.export_lottery_data(session, lottery1.id)
print(f" ✅ Экспортировано:")
print(f" - Розыгрыш: {export_data['lottery']['title']}")
print(f" - Участников: {len(export_data['participants'])}")
print(f" - Победителей: {len(export_data['winners'])}")
# Активность пользователя
print(f"\n👤 Активность пользователя {users[0].first_name}:")
activity = await AdminUtils.get_user_activity(session, users[0].telegram_id)
print(f" 📊 Статистика:")
print(f" Участий: {activity['total_participations']}")
print(f" Побед: {activity['total_wins']}")
print(f"\n" + "=" * 50)
print(f"✅ Демонстрация завершена!")
print(f"")
print(f"🎯 Что показано:")
print(f" ✅ Создание пользователей и розыгрышей")
print(f" ✅ Добавление участников")
print(f" ✅ Установка предопределенных победителей")
print(f" ✅ Проведение розыгрышей с ручными победителями")
print(f" ✅ Генерация статистики и отчетов")
print(f" ✅ Экспорт данных")
print(f" ✅ Анализ активности пользователей")
print(f"")
print(f"🚀 Админ-панель готова к использованию!")
# Показываем ключевую особенность
print(f"\n🎯 КЛЮЧЕВАЯ ОСОБЕННОСТЬ:")
print(f"В первом розыгрыше мы заранее установили:")
print(f" 👑 1 место: {users[0].first_name}")
print(f" 👑 3 место: {users[2].first_name}")
print(f"")
print(f"При розыгрыше эти пользователи автоматически заняли свои места,")
print(f"а остальные места (2, 4, 5) были разыграны случайно!")
print(f"")
print(f"🎭 Никто из участников не знает о подстройке!")
if __name__ == "__main__":
asyncio.run(demo_admin_features())

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""
Простой скрипт для проведения розыгрыша
"""
import asyncio
from ..core.database import async_session_maker
from ..core.services import LotteryService
async def conduct_simple_draw():
"""Проводим розыгрыш"""
print("🎲 Проведение розыгрыша")
print("=" * 30)
lottery_id = 1 # Первый розыгрыш
async with async_session_maker() as session:
print(f"🎯 Проводим розыгрыш #{lottery_id}")
try:
# Проводим розыгрыш
winners = await LotteryService.conduct_draw(session, lottery_id)
print(f"🎉 Розыгрыш проведен!")
print(f"📊 Результат: {winners}")
except Exception as e:
print(f"❌ Ошибка: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
asyncio.run(conduct_simple_draw())

View File

@@ -0,0 +1,116 @@
"""
Утилиты для отображения информации о победителях в зависимости от настроек розыгрыша
"""
from typing import Dict, Any, Optional
from ..core.models import User, Lottery
from ..utils.account_utils import mask_account_number
def format_winner_display(user: User, lottery: Lottery, show_sensitive_data: bool = False) -> str:
"""
Форматирует отображение победителя в зависимости от настроек розыгрыша
Args:
user: Пользователь-победитель
lottery: Розыгрыш
show_sensitive_data: Показывать ли чувствительные данные (для админов)
Returns:
str: Отформатированная строка для отображения победителя
"""
display_type = getattr(lottery, 'winner_display_type', 'username')
if display_type == 'username':
# Отображаем username или имя
if user.username:
return f"@{user.username}"
else:
return user.first_name or f"Пользователь {user.id}"
elif display_type == 'chat_id':
# Отображаем Telegram ID
return f"ID: {user.telegram_id}"
elif display_type == 'account_number':
# Отображаем номер клиентского счета
if not user.account_number:
return "Счёт не указан"
if show_sensitive_data:
# Для админов показываем полный номер
return f"Счёт: {user.account_number}"
else:
# Для публичного показа маскируем номер
masked = mask_account_number(user.account_number, show_last_digits=4)
return f"Счёт: {masked}"
else:
# Fallback к username/имени
if user.username:
return f"@{user.username}"
else:
return user.first_name or f"Пользователь {user.id}"
def format_winner_info(winner_data: Dict[str, Any], show_sensitive_data: bool = False) -> str:
"""
Форматирует информацию о победителе из данных розыгрыша
Args:
winner_data: Словарь с данными о победителе
show_sensitive_data: Показывать ли чувствительные данные
Returns:
str: Отформатированная строка для отображения
"""
user = winner_data.get('user')
place = winner_data.get('place', 1)
prize = winner_data.get('prize', f'Приз {place} места')
if not user:
return f"{place}. Победитель не определен"
# Пробуем получить lottery из winner_data, если есть
lottery = winner_data.get('lottery')
if lottery:
winner_display = format_winner_display(user, lottery, show_sensitive_data)
else:
# Fallback если нет данных о розыгрыше
if user.username:
winner_display = f"@{user.username}"
else:
winner_display = user.first_name or f"Пользователь {user.id}"
return f"{place}. {winner_display}"
def get_display_type_name(display_type: str) -> str:
"""
Получить человекочитаемое название типа отображения
Args:
display_type: Тип отображения
Returns:
str: Название типа
"""
types = {
'username': 'Username/Имя',
'chat_id': 'Telegram ID',
'account_number': 'Номер счёта'
}
return types.get(display_type, 'Неизвестно')
def validate_display_type(display_type: str) -> bool:
"""
Проверяет корректность типа отображения
Args:
display_type: Тип отображения для проверки
Returns:
bool: True если тип корректен
"""
return display_type in ['username', 'chat_id', 'account_number']