150 lines
4.2 KiB
Python
150 lines
4.2 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.db import transaction
|
|
|
|
from employees.models import Department, EmployeeDepartment, RoleAssignment
|
|
|
|
from .models import TaskBoard, TaskBoardAccessScope
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_task_board_recipient_ids(board: TaskBoard) -> set[int]:
|
|
if not board or not board.pk:
|
|
return set()
|
|
|
|
User = get_user_model()
|
|
is_private = board.access_scope == TaskBoardAccessScope.PRIVATE
|
|
|
|
if (
|
|
not is_private
|
|
and board.access_scope == TaskBoardAccessScope.ALL
|
|
and not board.members.exists()
|
|
and not board.departments.exists()
|
|
):
|
|
return set(User.objects.filter(is_active=True).values_list("id", flat=True))
|
|
|
|
department_ids = (
|
|
[] if is_private else list(board.departments.values_list("id", flat=True))
|
|
)
|
|
recipient_ids = {board.created_by_id}
|
|
if not is_private:
|
|
recipient_ids.update(board.members.values_list("id", flat=True))
|
|
|
|
if department_ids:
|
|
recipient_ids.update(
|
|
EmployeeDepartment.objects.filter(
|
|
department_id__in=department_ids,
|
|
is_active=True,
|
|
employee__is_active=True,
|
|
).values_list("employee_id", flat=True)
|
|
)
|
|
recipient_ids.update(
|
|
Department.objects.filter(
|
|
id__in=department_ids,
|
|
head__is_active=True,
|
|
).values_list("head_id", flat=True)
|
|
)
|
|
recipient_ids.update(
|
|
RoleAssignment.objects.filter(
|
|
role__department_id__in=department_ids,
|
|
is_active=True,
|
|
employee__is_active=True,
|
|
).values_list("employee_id", flat=True)
|
|
)
|
|
|
|
return set(
|
|
User.objects.filter(
|
|
id__in={user_id for user_id in recipient_ids if user_id},
|
|
is_active=True,
|
|
).values_list("id", flat=True)
|
|
)
|
|
|
|
|
|
def send_task_board_update(
|
|
board: TaskBoard | None,
|
|
event: str,
|
|
model: str,
|
|
object_id: int | None = None,
|
|
*,
|
|
recipient_ids: Iterable[int] | None = None,
|
|
board_id: int | None = None,
|
|
extra: dict | None = None,
|
|
) -> None:
|
|
resolved_board_id = board_id or getattr(board, "id", None)
|
|
if not resolved_board_id:
|
|
return
|
|
|
|
recipients = set(
|
|
recipient_ids
|
|
or (get_task_board_recipient_ids(board) if board and board.pk else set())
|
|
)
|
|
if not recipients:
|
|
return
|
|
|
|
payload = {
|
|
"board_id": resolved_board_id,
|
|
"event": event,
|
|
"model": model,
|
|
"object_id": object_id,
|
|
}
|
|
if extra:
|
|
payload.update(extra)
|
|
|
|
def _send() -> None:
|
|
channel_layer = get_channel_layer()
|
|
if channel_layer is None:
|
|
return
|
|
|
|
message = {
|
|
"type": "task_board_update",
|
|
"event": event,
|
|
"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(
|
|
"Task board WebSocket update failed",
|
|
extra={
|
|
"event": "task.board.websocket_update_failed",
|
|
"user_id": user_id,
|
|
"task_board_event": event,
|
|
"error_type": type(error).__name__,
|
|
},
|
|
)
|
|
|
|
transaction.on_commit(_send)
|
|
|
|
|
|
def send_task_checklist_update(
|
|
task,
|
|
event: str,
|
|
item_id: int,
|
|
*,
|
|
cover_cleared: bool = False,
|
|
) -> None:
|
|
"""Publish checklist counters together with a checklist item change."""
|
|
extra = {
|
|
"task_id": task.id,
|
|
"checklist_total": task.checklist_items.count(),
|
|
"checklist_completed": task.checklist_items.filter(is_completed=True).count(),
|
|
}
|
|
if cover_cleared:
|
|
extra["cover"] = None
|
|
send_task_board_update(
|
|
task.board,
|
|
event,
|
|
"checklist_item",
|
|
item_id,
|
|
extra=extra,
|
|
)
|