еще - лишнее в проекте

This commit is contained in:
lizerk 2026-03-10 12:41:04 +03:00
parent a784212ea1
commit a196a2b941
5 changed files with 0 additions and 689 deletions

View File

@ -1,201 +0,0 @@
# Celery Integration - Следующие шаги
## ✅ Что уже сделано
1. ✅ Добавлены зависимости в `requirements.txt`:
- `celery==5.5.3`
- `redis==5.2.1`
- `django-celery-beat==2.7.0`
- `django-celery-results==2.5.1`
- `flower==2.0.1`
2. ✅ Создан файл конфигурации `eusrr_backend/celery.py`
- Настроена автоматическая регистрация задач
- Добавлен debug task для тестирования
3. ✅ Обновлен `eusrr_backend/__init__.py`
- Автоматический импорт celery_app при запуске Django
4. ✅ Добавлены настройки Celery в `settings.py`:
- `CELERY_BROKER_URL` - Redis как брокер сообщений
- `CELERY_RESULT_BACKEND` - Redis для хранения результатов
- Настройка роутинга задач по очередям
- Таймауты и лимиты для workers
5. ✅ Добавлены приложения в `INSTALLED_APPS`:
- `django_celery_beat` - для периодических задач
- `django_celery_results` - для хранения результатов
6. ✅ Созданы Celery задачи в `notifications/tasks.py`:
- `send_notification_task` - отправка одного уведомления
- `send_bulk_notifications_task` - массовая отправка
- `cleanup_old_notifications_task` - очистка старых уведомлений
- `send_digest_email_task` - email-дайджесты
## 🔄 Следующие шаги (выполнить вручную)
### Шаг 1: Установка зависимостей
Запустите задачу в VS Code:
- **Pip: install requirements**
Или вручную:
```bash
cd backend
..\.venv\Scripts\pip install -r requirements.txt
```
### Шаг 2: Установка и запуск Redis
#### Вариант 1: Windows (рекомендуется Memurai)
1. Скачайте Memurai (Redis для Windows): https://www.memurai.com/
2. Установите и запустите как службу Windows
3. Redis будет доступен на `localhost:6379`
#### Вариант 2: Docker
```bash
docker run -d -p 6379:6379 --name redis redis:7-alpine
```
#### Проверка Redis:
```bash
redis-cli ping
# Должно вернуть: PONG
```
### Шаг 3: Применение миграций
Запустите:
```bash
cd backend
migrate_celery.bat
```
Или вручную:
```bash
..\.venv\Scripts\python manage.py migrate django_celery_beat
..\.venv\Scripts\python manage.py migrate django_celery_results
```
### Шаг 4: Тестирование Celery Worker
В отдельном терминале запустите worker:
```bash
cd backend
..\.venv\Scripts\celery -A eusrr_backend worker -l info --pool=solo
```
**Примечание**: На Windows используйте `--pool=solo` или `--pool=gevent`
### Шаг 5: Тестирование отправки задачи
В Django shell:
```python
..\.venv\Scripts\python manage.py shell
from celery import current_app
current_app.send_task('eusrr_backend.debug_task')
```
Вы должны увидеть выполнение задачи в логах worker.
### Шаг 6: Интеграция с NotificationService
Обновите `notifications/services.py`:
```python
from notifications.tasks import send_notification_task, send_bulk_notifications_task
class NotificationService:
@staticmethod
def create_notification(...):
# Вместо синхронного создания
send_notification_task.delay(
notification_type=notification_type,
user_id=user.id,
title=title,
message=message,
link=link,
sender_id=sender.id if sender else None,
metadata=metadata,
)
```
### Шаг 7: Запуск Flower (мониторинг)
```bash
cd backend
..\.venv\Scripts\celery -A eusrr_backend flower
```
Откройте http://localhost:5555 для просмотра статистики задач.
### Шаг 8: Настройка периодических задач
В Django Admin:
1. Перейдите в "Periodic Tasks"
2. Добавьте задачу для `notifications.cleanup_old_notifications`
3. Настройте интервал (например, раз в день)
Или программно в `eusrr_backend/celery.py`:
```python
from celery.schedules import crontab
app.conf.beat_schedule = {
'cleanup-old-notifications': {
'task': 'notifications.cleanup_old_notifications',
'schedule': crontab(hour=3, minute=0), # Каждый день в 3:00
'args': (90,) # Удалять уведомления старше 90 дней
},
}
```
Запустите beat scheduler:
```bash
..\.venv\Scripts\celery -A eusrr_backend beat -l info
```
## 📊 Ожидаемый результат
**До внедрения Celery:**
- Отправка сообщения в группу с 8 участниками: ~500ms
- Блокировка API response до отправки всех уведомлений
**После внедрения Celery:**
- Отправка сообщения: <50ms (только создание задачи)
- Уведомления обрабатываются асинхронно
- API мгновенно отвечает пользователю
## 🔍 Отладка
### Проверка статуса Celery
```bash
..\.venv\Scripts\celery -A eusrr_backend inspect active
..\.venv\Scripts\celery -A eusrr_backend inspect stats
```
### Проверка задач в очереди
```bash
..\.venv\Scripts\celery -A eusrr_backend inspect reserved
```
### Логи
- Worker логи: консоль где запущен worker
- Flower: http://localhost:5555
- Django логи: стандартные логи приложения
## 📚 Документация
- Подробная документация: `docs/guides/CELERY_SETUP.md`
- План внедрения (5 дней): `docs/in-progress/CELERY_INTEGRATION_PLAN.md`
- Celery docs: https://docs.celeryq.dev/
## ⚠️ Production Deployment
Для production:
1. Используйте Redis Cluster или Redis Sentinel
2. Запускайте несколько workers с supervisord/systemd
3. Настройте мониторинг (Flower + Prometheus)
4. Добавьте retry policies и dead letter queue
5. Настройте автоматический перезапуск workers
См. `docs/in-progress/CELERY_INTEGRATION_PLAN.md` для деталей.

View File

@ -1,48 +0,0 @@
#!/usr/bin/env python
"""Проверка параметров события в базе данных."""
import os
import sys
import django
# Setup Django
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eusrr_backend.settings")
django.setup()
# ЯВНЫЙ импорт патча
import schedule_patch # noqa
from schedule.models import Event, Rule
# Найдём событие по названию
event = Event.objects.filter(title__icontains='ыфвыфыф').first()
if event:
print(f"=" * 80)
print(f"Event ID: {event.id}")
print(f"Title: {event.title}")
print(f"Start: {event.start} (weekday: {event.start.weekday()})")
print(f"End recurring: {event.end_recurring_period}")
print(f"=" * 80)
if event.rule:
print(f"\nRule ID: {event.rule.id}")
print(f"Frequency: {event.rule.frequency}")
print(f"Params: {event.rule.params}")
print(f"=" * 80)
# Проверим occurrences
from datetime import datetime
from django.utils import timezone
start = timezone.make_aware(datetime(2026, 2, 1, 0, 0, 0))
end = timezone.make_aware(datetime(2026, 2, 28, 23, 59, 59))
occurrences = event.get_occurrences(start, end)
print(f"\nOccurrences в феврале 2026:")
for occ in occurrences:
print(f" - {occ.start.strftime('%Y-%m-%d %A')} {occ.start.strftime('%H:%M')}")
print(f"=" * 80)
else:
print("НЕТ ПРАВИЛА!")
else:
print("Событие не найдено!")

View File

@ -1,57 +0,0 @@
version: '3.8'
services:
# OpenLDAP сервер для тестирования
test-ldap:
image: osixia/openldap:1.5.0
container_name: eusrr-test-ldap
hostname: ldap.test.local
environment:
# Базовая конфигурация
LDAP_ORGANISATION: "EUSRR Test"
LDAP_DOMAIN: "test.local"
LDAP_BASE_DN: "dc=test,dc=local"
LDAP_ADMIN_PASSWORD: "test_change-me-redacted-secret"
LDAP_CONFIG_PASSWORD: "test_config_password"
LDAP_READONLY_USER: "false"
# Настройки для совместимости с вашим кодом
LDAP_RFC2307BIS_SCHEMA: "true"
LDAP_BACKEND: "mdb"
LDAP_TLS: "false"
LDAP_TLS_VERIFY_CLIENT: "never"
# Логирование для отладки
LDAP_LOG_LEVEL: "256"
ports:
- "10389:389" # Используем порт 10389 чтобы не конфликтовать с prod
- "10636:636" # SSL порт (если понадобится)
volumes:
- ./ldap_test_data:/var/lib/ldap
- ./ldap_test_config:/etc/ldap/slapd.d
- ./tests/ldap_fixtures:/container/service/slapd/assets/config/bootstrap/ldif/custom
networks:
- test-network
command: --copy-service --loglevel debug
# phpLDAPadmin - веб-интерфейс для управления (опционально)
ldap-admin:
image: osixia/phpldapadmin:0.9.0
container_name: eusrr-ldap-admin
environment:
PHPLDAPADMIN_LDAP_HOSTS: "test-ldap"
PHPLDAPADMIN_HTTPS: "false"
ports:
- "8090:80"
depends_on:
- test-ldap
networks:
- test-network
networks:
test-network:
driver: bridge
volumes:
ldap_test_data:
ldap_test_config:

View File

@ -1,146 +0,0 @@
#!/usr/bin/env python
"""
Тест полного цикла уведомлений
Проверяет:
1. Создание уведомления через notify.send()
2. Срабатывание post_save сигнала
3. Отправку задач в Celery
4. Наличие уведомления в БД
5. Выполнение задач Celery
"""
import django
import os
import sys
import time
# Setup Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eusrr_backend.settings')
sys.path.insert(0, os.path.dirname(__file__))
django.setup()
from django.contrib.auth import get_user_model
from notifications.signals import notify
from notifications.models import Notification
User = get_user_model()
def test_notification_creation():
"""Тест создания уведомления"""
print("\n" + "="*60)
print("ТЕСТ СИСТЕМЫ УВЕДОМЛЕНИЙ")
print("="*60)
# 1. Находим двух пользователей
print("\n1. Поиск пользователей...")
users = User.objects.filter(is_active=True)[:2]
if users.count() < 2:
print("❌ Нужно минимум 2 активных пользователя")
return
sender = users[0]
recipient = users[1]
print(f" ✅ Отправитель: {sender.username} (ID: {sender.id})")
print(f" ✅ Получатель: {recipient.username} (ID: {recipient.id})")
# 2. Проверяем настройки получателя
print("\n2. Проверка настроек получателя...")
try:
prefs = recipient.channel_preferences
print(f" ✅ web_enabled: {prefs.web_enabled}")
print(f" ✅ email_enabled: {prefs.email_enabled}")
print(f" ✅ push_enabled: {prefs.push_enabled}")
print(f" ✅ email_frequency: {prefs.email_frequency}")
print(f" ✅ DND период: {prefs.is_in_dnd_period()}")
except Exception as e:
print(f" ⚠️ Нет настроек (будут созданы): {e}")
# 3. Подсчет уведомлений до создания
print("\n3. Подсчет уведомлений перед тестом...")
count_before = Notification.objects.count()
print(f" Всего уведомлений в БД: {count_before}")
# 4. Создаем уведомление
print("\n4. Создание уведомления через notify.send()...")
try:
result = notify.send(
sender=sender,
recipient=recipient,
verb='test_notification',
description=f'🧪 Тестовое уведомление от {sender.username}',
action_url='/test/',
data={
'test': True,
'timestamp': time.time(),
}
)
print(f" ✅ notify.send() выполнен")
print(f" Результат: {result}")
except Exception as e:
print(f" ❌ Ошибка при создании: {e}")
import traceback
traceback.print_exc()
return
# 5. Проверяем что уведомление создано в БД
print("\n5. Проверка создания в БД...")
time.sleep(0.1) # Небольшая задержка для сохранения
count_after = Notification.objects.count()
new_notifications = count_after - count_before
print(f" Всего уведомлений в БД: {count_after}")
print(f" Создано новых: {new_notifications}")
if new_notifications == 0:
print(" ❌ Уведомление не создано в БД!")
return
# 6. Получаем созданное уведомление
print("\n6. Детали созданного уведомления...")
notification = Notification.objects.filter(recipient=recipient).order_by('-id').first()
if notification:
print(f" ✅ ID: {notification.id}")
print(f" ✅ verb: {notification.verb}")
print(f" ✅ description: {notification.description}")
print(f" ✅ recipient: {notification.recipient.username}")
print(f" ✅ actor: {notification.actor}")
print(f" ✅ unread: {notification.unread}")
print(f" ✅ timestamp: {notification.timestamp}")
else:
print(" ❌ Уведомление не найдено!")
return
# 7. Ждем выполнения Celery задач
print("\n7. Ожидание выполнения Celery задач...")
print(" ⏳ Ждем 2 секунды...")
time.sleep(2)
# 8. Проверяем логи Celery
print("\n8. Результаты:")
print(" 📋 Проверьте логи Celery worker на наличие:")
print(f" - Task notifications.send_websocket_notification[...] received")
print(f" - Notification {notification.id} not found (если есть race condition)")
print(f" - Task ... succeeded (если всё ОК)")
print("\n" + "="*60)
print("ТЕСТ ЗАВЕРШЕН")
print("="*60)
print("\nПроверьте:")
print("1. Логи Celery worker")
print("2. WebSocket соединение в браузере")
print("3. Появилось ли уведомление в UI")
print()
if __name__ == '__main__':
test_notification_creation()

View File

@ -1,237 +0,0 @@
#!/usr/bin/env python
"""
Тестовый скрипт для проверки системы уведомлений v2.0
Использование:
python test_notifications.py
Что тестируется:
- Создание уведомления через notify.send()
- Сохранение в БД
- Отправка через Celery задачи (WebSocket, Email, Push)
- Channel preferences
- API endpoints
"""
import os
import django
# Настройка Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eusrr_backend.settings')
django.setup()
from django.contrib.auth import get_user_model
from notifications.signals import notify
from notifications.models import Notification, UserChannelPreferences
User = get_user_model()
def test_notify_send():
"""Тест создания уведомления через notify.send()"""
print("\n" + "="*80)
print("ТЕСТ 1: Создание уведомления через notify.send()")
print("="*80)
try:
# Получаем первых двух пользователей
users = User.objects.all()[:2]
if len(users) < 2:
print("❌ Нужно минимум 2 пользователя в БД")
return False
sender_user = users[0]
recipient_user = users[1]
print(f"📤 Отправитель: {sender_user.username}")
print(f"📥 Получатель: {recipient_user.username}")
# Создаем уведомление
notification = notify.send(
sender=sender_user,
recipient=recipient_user,
verb='liked',
description=f'{sender_user.get_full_name() or sender_user.username} liked your post',
action_url='/posts/123/',
)
if isinstance(notification, list):
notification = notification[0]
print(f"✅ Уведомление создано: ID={notification.id}")
print(f" Verb: {notification.verb}")
print(f" Description: {notification.description}")
print(f" Actor: {notification.actor}")
print(f" Unread: {notification.unread}")
return True
except Exception as e:
print(f"❌ Ошибка: {e}")
import traceback
traceback.print_exc()
return False
def test_channel_preferences():
"""Тест настроек каналов"""
print("\n" + "="*80)
print("ТЕСТ 2: UserChannelPreferences")
print("="*80)
try:
user = User.objects.first()
if not user:
print("❌ Нет пользователей в БД")
return False
print(f"👤 Пользователь: {user.username}")
# Получаем или создаем настройки
prefs, created = UserChannelPreferences.objects.get_or_create(user=user)
if created:
print("✅ Созданы новые настройки с дефолтными значениями")
else:
print("✅ Настройки уже существуют")
print(f" Web: {prefs.web_enabled}")
print(f" Email: {prefs.email_enabled} (frequency: {prefs.email_frequency})")
print(f" Push: {prefs.push_enabled}")
print(f" DND: {prefs.dnd_enabled} ({prefs.dnd_start_time} - {prefs.dnd_end_time})")
# Проверяем is_in_dnd_period()
is_dnd = prefs.is_in_dnd_period()
print(f" Сейчас в DND: {is_dnd}")
return True
except Exception as e:
print(f"❌ Ошибка: {e}")
import traceback
traceback.print_exc()
return False
def test_queryset_methods():
"""Тест методов QuerySet"""
print("\n" + "="*80)
print("ТЕСТ 3: QuerySet методы")
print("="*80)
try:
user = User.objects.first()
if not user:
print("❌ Нет пользователей в БД")
return False
print(f"👤 Пользователь: {user.username}")
# Получаем статистику
total = Notification.objects.filter(recipient=user).count()
unread = Notification.objects.filter(recipient=user).unread().count()
read_count = Notification.objects.filter(recipient=user).read().count()
print(f"📊 Статистика:")
print(f" Всего: {total}")
print(f" Непрочитанных: {unread}")
print(f" Прочитанных: {read_count}")
# Последние 5 уведомлений
recent = Notification.objects.filter(
recipient=user
).order_by('-timestamp')[:5]
if recent:
print(f"\n📬 Последние {recent.count()} уведомлений:")
for n in recent:
status = "🔵" if n.unread else "⚪"
print(f" {status} [{n.verb}] {n.description[:50]}... ({n.timesince()} ago)")
return True
except Exception as e:
print(f"❌ Ошибка: {e}")
import traceback
traceback.print_exc()
return False
def test_mark_as_read():
"""Тест отметки как прочитанного"""
print("\n" + "="*80)
print("ТЕСТ 4: Отметка как прочитанного")
print("="*80)
try:
user = User.objects.first()
if not user:
print("❌ Нет пользователей в БД")
return False
# Находим непрочитанное уведомление
notification = Notification.objects.filter(
recipient=user,
unread=True
).first()
if not notification:
print("⚠️ Нет непрочитанных уведомлений")
return True
print(f"📬 Уведомление ID={notification.id}: unread={notification.unread}")
# Отмечаем как прочитанное
notification.mark_as_read()
# Перезагружаем из БД
notification.refresh_from_db()
print(f"✅ После mark_as_read(): unread={notification.unread}")
print(f" timestamp_read: {notification.timestamp_read}")
return True
except Exception as e:
print(f"❌ Ошибка: {e}")
import traceback
traceback.print_exc()
return False
def main():
"""Запуск всех тестов"""
print("\n" * 2)
print("╔" + "="*78 + "╗")
print("║" + " "*20 + "ТЕСТИРОВАНИЕ NOTIFICATIONS V2.0" + " "*25 + "║")
print("╚" + "="*78 + "╝")
results = []
# Запускаем тесты
results.append(("notify.send()", test_notify_send()))
results.append(("UserChannelPreferences", test_channel_preferences()))
results.append(("QuerySet методы", test_queryset_methods()))
results.append(("mark_as_read()", test_mark_as_read()))
# Итоги
print("\n" + "="*80)
print("ИТОГИ")
print("="*80)
for test_name, result in results:
status = "✅ PASSED" if result else "❌ FAILED"
print(f"{status}: {test_name}")
passed = sum(1 for _, r in results if r)
total = len(results)
print(f"\n{'='*80}")
print(f"Пройдено: {passed}/{total} тестов")
print(f"{'='*80}\n")
return all(r for _, r in results)
if __name__ == "__main__":
success = main()
exit(0 if success else 1)