This commit is contained in:
2026-04-05 00:57:25 +03:30
parent 6d5ece1f5d
commit 32dbbed1af
26 changed files with 825 additions and 291 deletions
+43 -26
View File
@@ -1,33 +1,50 @@
import json
import uuid
from datetime import datetime, timezone
import time
from django.conf import settings
from redis import Redis
from django.db import OperationalError, ProgrammingError
from django.db.models import QuerySet
from farm_hub.models import FarmHub
from .models import FarmNotification
def get_notifications_redis_client():
redis_url = getattr(settings, "NOTIFICATION_REDIS_URL", None) or _default_redis_url()
return Redis.from_url(redis_url, decode_responses=True)
DEFAULT_POLL_TIMEOUT_SECONDS = 15
DEFAULT_POLL_INTERVAL_SECONDS = 1
def publish_notification(channel, title, message, *, level="info", metadata=None, event="notification"):
payload = {
"id": str(uuid.uuid4()),
"event": event,
"title": title,
"message": message,
"level": level,
"metadata": metadata or {},
"created_at": datetime.now(timezone.utc).isoformat(),
}
redis_client = get_notifications_redis_client()
redis_client.publish(channel, json.dumps(payload))
return payload
def create_notification_for_farm_uuid(*, farm_uuid, title, message, level="info", metadata=None):
farm = FarmHub.objects.filter(farm_uuid=farm_uuid).first()
if farm is None:
raise ValueError("Farm not found.")
try:
return FarmNotification.objects.create(
farm=farm,
title=title,
message=message,
level=level,
metadata=metadata or {},
)
except (ProgrammingError, OperationalError) as exc:
raise ValueError("Notifications table is not migrated.") from exc
def _default_redis_url():
broker_url = getattr(settings, "CELERY_BROKER_URL", "")
if isinstance(broker_url, str) and broker_url.startswith("redis://"):
return broker_url
return "redis://127.0.0.1:6379/1"
def get_notifications_for_farm(*, farm: FarmHub, since_id=None) -> QuerySet[FarmNotification]:
try:
queryset = FarmNotification.objects.filter(farm=farm)
if since_id is not None:
queryset = queryset.filter(id__gt=since_id)
return queryset.order_by("created_at", "id")
except (ProgrammingError, OperationalError) as exc:
raise ValueError("Notifications table is not migrated.") from exc
def long_poll_notifications(*, farm: FarmHub, since_id=None, timeout_seconds=DEFAULT_POLL_TIMEOUT_SECONDS, interval_seconds=DEFAULT_POLL_INTERVAL_SECONDS):
deadline = time.monotonic() + max(timeout_seconds, 0)
while True:
notifications = list(get_notifications_for_farm(farm=farm, since_id=since_id))
if notifications:
return notifications
if time.monotonic() >= deadline:
return []
time.sleep(max(interval_seconds, 0))