nexus-5/core/graphql/queries/messaging.py
2026-01-26 11:09:40 -05:00

149 lines
5.2 KiB
Python

from typing import List, Optional, Iterable
import strawberry
import strawberry_django as sd
from strawberry import ID
from strawberry_django.relay import DjangoCursorConnection
from django.contrib.contenttypes.models import ContentType
from core.graphql.filters.messaging import ConversationFilter, MessageFilter
from core.graphql.types.messaging import ConversationType, MessageType
from core.models.messaging import Conversation, Message
from core.models.profile import TeamProfile, CustomerProfile
@strawberry.type
class Query:
"""Messaging queries"""
conversation: Optional[ConversationType] = sd.node()
conversations: List[ConversationType] = sd.field(filters=ConversationFilter)
message: Optional[MessageType] = sd.node()
messages: List[MessageType] = sd.field(filters=MessageFilter)
@sd.connection(
DjangoCursorConnection["ConversationType"],
name="getMyConversations",
description="Return conversations for the authenticated user (inbox)",
filters=ConversationFilter,
)
def get_my_conversations(
self,
info,
include_archived: bool = False,
) -> Iterable["Conversation"]:
"""
Get all conversations for the current authenticated user.
Returns conversations ordered by last message timestamp.
"""
# Get profile directly from context (not Django User model)
profile = getattr(info.context.request, 'profile', None)
if not profile:
return Conversation.objects.none()
# Determine the profile's content type
content_type = ContentType.objects.get_for_model(type(profile))
# Build query
queryset = Conversation.objects.filter(
participants__participant_content_type=content_type,
participants__participant_object_id=profile.id,
)
# Filter archived conversations unless explicitly requested
if not include_archived:
queryset = queryset.filter(participants__is_archived=False)
return queryset.prefetch_related(
'participants',
'participants__participant_content_type',
).distinct().order_by('-last_message_at', '-created_at')
@sd.connection(
DjangoCursorConnection["ConversationType"],
name="getConversationsByEntity",
description="Return conversations linked to a specific entity (Project, Service, Account, etc.)",
filters=ConversationFilter,
)
def get_conversations_by_entity(
self,
entity_type: str,
entity_id: ID,
) -> Iterable["Conversation"]:
"""
Get all conversations linked to a specific entity.
entity_type: Model name (e.g., 'Project', 'Service', 'Account')
entity_id: UUID of the entity
"""
from django.apps import apps
try:
# Get the content type for the entity
model = apps.get_model('core', entity_type)
content_type = ContentType.objects.get_for_model(model)
return Conversation.objects.filter(
entity_content_type=content_type,
entity_object_id=entity_id
).prefetch_related(
'participants',
'participants__participant_content_type',
).order_by('-last_message_at')
except Exception:
return Conversation.objects.none()
@strawberry.field(description="Get unread message count for the authenticated user")
async def unread_message_count(self, info) -> int:
"""
Get total unread message count across all conversations for the current user.
"""
from channels.db import database_sync_to_async
# Get profile directly from context (not Django User model)
profile = getattr(info.context.request, 'profile', None)
if not profile:
return 0
@database_sync_to_async
def get_count():
# Determine the profile's content type
content_type = ContentType.objects.get_for_model(type(profile))
# Sum unread counts from all participant records
from core.models.messaging import ConversationParticipant
from django.db.models import Sum
total = ConversationParticipant.objects.filter(
participant_content_type=content_type,
participant_object_id=profile.id,
is_archived=False
).aggregate(total=Sum('unread_count'))['total']
return total if total else 0
return await get_count()
@sd.connection(
DjangoCursorConnection["MessageType"],
name="getMessagesByConversation",
description="Return messages for a specific conversation",
filters=MessageFilter,
)
def get_messages_by_conversation(
self,
conversation_id: ID,
include_system: bool = True,
) -> Iterable["Message"]:
"""
Get all messages for a specific conversation.
"""
queryset = Message.objects.filter(conversation_id=conversation_id)
if not include_system:
queryset = queryset.filter(is_system_message=False)
return queryset.prefetch_related(
'read_receipts',
'sender_content_type',
).order_by('created_at')