Files
redtea 4bc5fcef7a hw6, hw7
- Redis / Celery / Celery Beat
- кэширование популярных тегов и лучших участников
- асинхронные уведомления в Centrifugo
- отправка email-уведомлений через Celery
- полнотекстовый поиск и подсказки
- Docker Compose инфраструктура
2026-05-28 09:48:16 +03:00

95 lines
2.8 KiB
Python

from __future__ import annotations
from celery import shared_task
from django.core.cache import cache
from django.conf import settings
import requests
from django.core.mail import send_mail
from django.utils import timezone
from datetime import timedelta
from django.db.models import Count, Sum, Q
from django.contrib.auth.models import User
from .models import Tag, Question, Answer
@shared_task
def rebuild_popular_tags():
# compute top 10 tags by number of questions in last 3 months
three_months_ago = timezone.now() - timedelta(days=90)
popular_qs = Tag.objects.annotate(
q_count=Count('question', filter=Q(question__created_at__gte=three_months_ago))
).order_by('-q_count')[:10]
popular = [
{'name': t.name, 'count': t.q_count}
for t in popular_qs
]
# store in cache
cache.set('popular_tags', popular, timeout=60 * 60)
return popular
@shared_task
def rebuild_best_members():
# compute top 10 users by activity (questions created in last week + total answers)
week_ago = timezone.now() - timedelta(days=7)
# users with most questions in the last week (by count)
users_q = User.objects.annotate(
q_count=Count('question', filter=Q(question__created_at__gte=week_ago))
)
# users with most answers in the last week
users_a = User.objects.annotate(
a_count=Count('answer', filter=Q(answer__created_at__gte=week_ago))
)
# combine scores
scores = {}
for u in users_q:
scores[u.id] = scores.get(u.id, 0) + (u.q_count or 0)
for u in users_a:
scores[u.id] = scores.get(u.id, 0) + (u.a_count or 0)
# build list of users sorted by score
best_users = User.objects.filter(id__in=list(scores.keys()))
best = []
for u in best_users:
best.append({
'id': u.id,
'username': u.username,
'score': scores.get(u.id, 0)
})
best.sort(key=lambda x: x['score'], reverse=True)
best = best[:10]
cache.set('best_members', best, timeout=60 * 60)
return best
@shared_task
def publish_new_answer_centrifugo(question_id: int, payload: dict):
# Send message to centrifugo pushed via HTTP API
# Use Centrifugo HTTP API (v4) publish endpoint
api_url = settings.CENTRIFUGO_URL.rstrip('/') + '/api'
headers = {'Authorization': f'apikey {settings.CENTRIFUGO_API_KEY}'}
data = {
'method': 'publish',
'params': {
'channel': f'question_{question_id}',
'data': payload,
}
}
try:
requests.post(api_url, json=data, headers=headers, timeout=5)
except Exception:
pass
@shared_task
def send_new_answer_email(subject: str, message: str, recipient_list: list[str]):
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, recipient_list)