105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
import logging
|
|
from collections.abc import Iterable
|
|
|
|
from asgiref.sync import async_to_sync
|
|
from channels.layers import get_channel_layer
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.db import models, transaction
|
|
from schedule.models import Calendar, CalendarRelation, Event, EventRelation
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_calendar_recipient_ids(
|
|
calendar: Calendar | None,
|
|
*,
|
|
event: Event | None = None,
|
|
) -> set[int]:
|
|
if calendar is None or not calendar.pk:
|
|
return set()
|
|
|
|
User = get_user_model()
|
|
user_content_type = ContentType.objects.get_for_model(User)
|
|
recipient_ids = set(
|
|
CalendarRelation.objects.filter(
|
|
calendar=calendar,
|
|
content_type=user_content_type,
|
|
object_id__isnull=False,
|
|
).values_list("object_id", flat=True)
|
|
)
|
|
|
|
if event is not None and event.pk:
|
|
recipient_ids.update(
|
|
EventRelation.objects.filter(
|
|
event=event,
|
|
content_type=user_content_type,
|
|
object_id__isnull=False,
|
|
).values_list("object_id", flat=True)
|
|
)
|
|
|
|
# Администраторы могут работать в scope=all без CalendarRelation.
|
|
recipient_ids.update(
|
|
User.objects.filter(is_active=True).filter(
|
|
models.Q(is_staff=True) | models.Q(is_superuser=True)
|
|
).values_list("id", flat=True)
|
|
)
|
|
return set(
|
|
User.objects.filter(id__in=recipient_ids, is_active=True).values_list(
|
|
"id", flat=True
|
|
)
|
|
)
|
|
|
|
|
|
def send_calendar_update(
|
|
*,
|
|
action: str,
|
|
entity: str,
|
|
calendar_id: int,
|
|
recipient_ids: Iterable[int],
|
|
event_id: int | None = None,
|
|
changed_fields: Iterable[str] | None = None,
|
|
extra: dict | None = None,
|
|
) -> None:
|
|
recipients = {int(user_id) for user_id in recipient_ids if user_id}
|
|
if not recipients or not calendar_id:
|
|
return
|
|
|
|
payload = {
|
|
"action": action,
|
|
"entity": entity,
|
|
"calendar_id": calendar_id,
|
|
"event_id": event_id,
|
|
"changed_fields": list(changed_fields or []),
|
|
}
|
|
if extra:
|
|
payload.update(extra)
|
|
|
|
def _send() -> None:
|
|
channel_layer = get_channel_layer()
|
|
if channel_layer is None:
|
|
return
|
|
|
|
message = {
|
|
"type": "calendar_update",
|
|
"event": f"{entity}_{action}",
|
|
"data": payload,
|
|
}
|
|
for user_id in recipients:
|
|
try:
|
|
async_to_sync(channel_layer.group_send)(f"user_{user_id}", message)
|
|
except Exception as error:
|
|
logger.exception(
|
|
"Calendar WebSocket update failed",
|
|
extra={
|
|
"event": "calendar.websocket_update_failed",
|
|
"user_id": user_id,
|
|
"calendar_id": calendar_id,
|
|
"schedule_event_id": event_id,
|
|
"calendar_action": action,
|
|
"error_type": type(error).__name__,
|
|
},
|
|
)
|
|
|
|
transaction.on_commit(_send)
|