185 lines
6.5 KiB
Python
185 lines
6.5 KiB
Python
"""Batch enrichment used by document read serializers."""
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.db.models import Count
|
|
|
|
from communications.models import Message
|
|
from documents.models import Document, DocumentAcknowledgement
|
|
from employees.models import EmployeeDepartment, RoleAssignment
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
def attach_linked_task_payloads(documents: list[Document], *, user) -> None:
|
|
"""Attach accessible task badges to a materialized document collection."""
|
|
if not documents:
|
|
return
|
|
|
|
document_ids = [document.id for document in documents]
|
|
mapping = {document_id: [] for document_id in document_ids}
|
|
if user and user.is_authenticated:
|
|
from tasks.access import task_board_access_q
|
|
from tasks.models import TaskBoard, TaskLinkedObject, TaskLinkedObjectKind
|
|
|
|
document_content_type = ContentType.objects.get_for_model(Document)
|
|
accessible_boards = TaskBoard.objects.filter(is_archived=False).filter(
|
|
task_board_access_q(user)
|
|
)
|
|
links = (
|
|
TaskLinkedObject.objects.filter(
|
|
kind=TaskLinkedObjectKind.DOCUMENT,
|
|
content_type=document_content_type,
|
|
object_id__in=document_ids,
|
|
task__board__in=accessible_boards,
|
|
)
|
|
.select_related("task", "task__board", "task__column")
|
|
.order_by("object_id", "task__title", "task_id")
|
|
)
|
|
for link in links:
|
|
mapping.setdefault(link.object_id, []).append(
|
|
{
|
|
"link_id": link.id,
|
|
"id": link.task_id,
|
|
"title": link.task.title,
|
|
"board_id": link.task.board_id,
|
|
"board_name": link.task.board.name,
|
|
"column_id": link.task.column_id,
|
|
"column_name": link.task.column.name,
|
|
"column_color": link.task.column.color,
|
|
"priority": link.task.priority,
|
|
"priority_display": link.task.get_priority_display(),
|
|
}
|
|
)
|
|
|
|
for document in documents:
|
|
document._linked_task_payloads = mapping.get(document.id, [])
|
|
|
|
|
|
def _department_ids(documents: list[Document]) -> set[int]:
|
|
return {
|
|
department.id
|
|
for document in documents
|
|
for department in (
|
|
list(document.departments.all())
|
|
+ list(document.acknowledgement_departments.all())
|
|
)
|
|
}
|
|
|
|
|
|
def _employees_by_department(department_ids: set[int]) -> dict[int, set[int]]:
|
|
mapping = {department_id: set() for department_id in department_ids}
|
|
if not department_ids:
|
|
return mapping
|
|
|
|
membership_rows = EmployeeDepartment.objects.filter(
|
|
department_id__in=department_ids,
|
|
is_active=True,
|
|
employee__is_active=True,
|
|
).values_list("department_id", "employee_id")
|
|
role_rows = RoleAssignment.objects.filter(
|
|
role__department_id__in=department_ids,
|
|
is_active=True,
|
|
employee__is_active=True,
|
|
).values_list("role__department_id", "employee_id")
|
|
for department_id, employee_id in (*membership_rows, *role_rows):
|
|
mapping.setdefault(department_id, set()).add(employee_id)
|
|
return mapping
|
|
|
|
|
|
def _acknowledged_users(document_ids: list[int]) -> dict[int, set[int]]:
|
|
mapping = {document_id: set() for document_id in document_ids}
|
|
rows = DocumentAcknowledgement.objects.filter(
|
|
document_id__in=document_ids,
|
|
user__is_active=True,
|
|
).values_list("document_id", "user_id")
|
|
for document_id, user_id in rows:
|
|
mapping.setdefault(document_id, set()).add(user_id)
|
|
return mapping
|
|
|
|
|
|
def _comment_counts(document_ids: list[int]) -> dict[int, int]:
|
|
document_content_type = ContentType.objects.get_for_model(Document)
|
|
return {
|
|
row["chat__context_object_id"]: row["total"]
|
|
for row in Message.objects.filter(
|
|
chat__type="comments",
|
|
chat__context_content_type=document_content_type,
|
|
chat__context_object_id__in=document_ids,
|
|
is_deleted=False,
|
|
)
|
|
.values("chat__context_object_id")
|
|
.annotate(total=Count("id"))
|
|
}
|
|
|
|
|
|
def _document_access_ids(
|
|
document: Document,
|
|
active_user_ids: set[int],
|
|
employees_by_department: dict[int, set[int]],
|
|
) -> set[int]:
|
|
if document.sent_to_all:
|
|
return set(active_user_ids)
|
|
access_ids = {
|
|
recipient.id
|
|
for recipient in document.recipients.all()
|
|
if recipient.id in active_user_ids
|
|
}
|
|
for department in document.departments.all():
|
|
access_ids.update(employees_by_department.get(department.id, set()))
|
|
return access_ids
|
|
|
|
|
|
def _acknowledgement_ids(
|
|
document: Document,
|
|
access_ids: set[int],
|
|
active_user_ids: set[int],
|
|
employees_by_department: dict[int, set[int]],
|
|
) -> set[int]:
|
|
if not document.acknowledgement_required:
|
|
return set()
|
|
if document.acknowledgement_for_all:
|
|
return set(access_ids)
|
|
|
|
required_ids = {
|
|
recipient.id
|
|
for recipient in document.acknowledgement_recipients.all()
|
|
if recipient.id in active_user_ids
|
|
}
|
|
for department in document.acknowledgement_departments.all():
|
|
required_ids.update(employees_by_department.get(department.id, set()))
|
|
return required_ids & access_ids
|
|
|
|
|
|
def attach_document_metrics(documents: list[Document]) -> None:
|
|
"""Attach comment and acknowledgement aggregates without N+1 queries."""
|
|
if not documents:
|
|
return
|
|
|
|
document_ids = [document.id for document in documents]
|
|
active_user_ids = set(
|
|
User.objects.filter(is_active=True).values_list("id", flat=True)
|
|
)
|
|
employees_by_department = _employees_by_department(_department_ids(documents))
|
|
acknowledged_users = _acknowledged_users(document_ids)
|
|
comment_counts = _comment_counts(document_ids)
|
|
|
|
for document in documents:
|
|
access_ids = _document_access_ids(
|
|
document,
|
|
active_user_ids,
|
|
employees_by_department,
|
|
)
|
|
required_ids = _acknowledgement_ids(
|
|
document,
|
|
access_ids,
|
|
active_user_ids,
|
|
employees_by_department,
|
|
)
|
|
document._acknowledgement_user_ids = required_ids
|
|
document._acknowledgement_total = len(required_ids)
|
|
document._acknowledged_count = len(
|
|
required_ids & acknowledged_users.get(document.id, set())
|
|
)
|
|
document._comments_count = comment_counts.get(document.id, 0)
|