@ -1,18 +1,49 @@
import math
import re
from django.contrib.auth import get_user_model
from django.db import transaction
from django.db import transaction
from django.db.models import Q , Sum
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework import status
from rest_framework.authentication import TokenAuthentication
from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.models import Token
from rest_framework.permissions import IsAuthenticated
from rest_framework.permissions import BasePermission , IsAuthenticated
from rest_framework.response import Response
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.views import APIView
from apps.support.models import SupportMessage , SupportRoom
from apps.support.models import SupportMessage , SupportRoom
from apps.support.serializers import (
from apps.support.serializers import (
AdminSupportMessageSerializer ,
AdminSupportRoomSerializer ,
SendSupportMessageSerializer ,
SendSupportMessageSerializer ,
SupportMessageSerializer ,
SupportMessageSerializer ,
SupportRoomSerializer ,
SupportRoomSerializer ,
serialize_admin_chat_user ,
)
)
User = get_user_model ( )
class IsSupportAdminPanelUser ( BasePermission ) :
"""
Allows access to users who can log into the admin panel ( super_admin , admin , professor , staff ) .
"""
def has_permission ( self , request , view ) :
user = request . user
if not user or not user . is_authenticated :
return False
if getattr ( user , ' is_superuser ' , False ) or getattr ( user , ' is_staff ' , False ) :
return True
if hasattr ( user , ' can_access_admin_panel ' ) and user . can_access_admin_panel ( ) :
return True
return getattr ( user , ' user_type ' , None ) in ( ' super_admin ' , ' admin ' , ' professor ' , ' consultant ' )
def get_total_admin_unread_count ( ) - > int :
agg = SupportRoom . objects . aggregate ( total = Sum ( ' unread_by_admin ' ) )
return int ( agg . get ( ' total ' ) or 0 )
def resolve_user_support_room ( request , create_if_missing : bool = False ) :
def resolve_user_support_room ( request , create_if_missing : bool = False ) :
"""
"""
@ -144,3 +175,284 @@ class SupportUnreadCountView(APIView):
} ,
} ,
status = status . HTTP_200_OK ,
status = status . HTTP_200_OK ,
)
)
class AdminSupportRoomListView ( APIView ) :
"""
GET / api / support / admin / rooms / ? page = 1 & per_page = 20 & search = . . .
Lists all SupportRooms for the admin dashboard ( `dovodi-panel` ) .
"""
authentication_classes = [ TokenAuthentication ]
permission_classes = [ IsAuthenticated , IsSupportAdminPanelUser ]
def get ( self , request , * args , * * kwargs ) :
try :
page = max ( 1 , int ( request . query_params . get ( ' page ' , 1 ) ) )
except ( TypeError , ValueError ) :
page = 1
try :
per_page = max ( 1 , min ( 100 , int ( request . query_params . get ( ' per_page ' , 20 ) ) ) )
except ( TypeError , ValueError ) :
per_page = 20
search = ( request . query_params . get ( ' search ' ) or ' ' ) . strip ( )
qs = SupportRoom . objects . select_related ( ' user ' , ' assigned_to ' ) . order_by (
' -last_message_at ' ,
' -updated_at ' ,
' -id ' ,
)
if search :
q_filter = (
Q ( user__fullname__icontains = search )
| Q ( user__email__icontains = search )
| Q ( user__phone_number__icontains = search )
| Q ( messages__content__icontains = search )
)
digits_match = re . search ( r ' ( \ d+) ' , search )
if digits_match :
num_val = int ( digits_match . group ( 1 ) )
q_filter | = Q ( id = num_val ) | Q ( user__id = num_val )
elif search . lower ( ) == ' guest ' :
q_filter | = Q ( user__email__isnull = True )
qs = qs . filter ( q_filter ) . distinct ( )
total_rooms = qs . count ( )
total_pages = max ( 1 , math . ceil ( total_rooms / per_page ) )
offset = ( page - 1 ) * per_page
rooms_page = list ( qs [ offset : offset + per_page ] )
serialized_rooms = AdminSupportRoomSerializer (
rooms_page ,
many = True ,
context = { ' request ' : request } ,
) . data
return Response (
{
' status ' : ' success ' ,
' current_user ' : serialize_admin_chat_user ( request . user , request = request ) ,
' room_messages ' : serialized_rooms ,
' total_pages ' : total_pages ,
' current_page ' : page ,
' per_page ' : per_page ,
' total_rooms ' : total_rooms ,
' total_unread_count ' : get_total_admin_unread_count ( ) ,
} ,
status = status . HTTP_200_OK ,
)
class AdminSupportRoomByUserView ( APIView ) :
"""
POST / api / support / admin / rooms / by - user /
Body : { " user_id " : < int > }
Gets or creates a SupportRoom for a specific user when an admin starts a new chat from the panel.
"""
authentication_classes = [ TokenAuthentication ]
permission_classes = [ IsAuthenticated , IsSupportAdminPanelUser ]
@transaction.atomic
def post ( self , request , * args , * * kwargs ) :
user_id = request . data . get ( ' user_id ' ) or request . data . get ( ' recipient_id ' )
target_user = get_object_or_404 ( User , pk = user_id )
room , _ = SupportRoom . objects . get_or_create (
user = target_user ,
defaults = { ' assigned_to ' : request . user } ,
)
# Mark any unread messages from this user as read
if room . unread_by_admin > 0 :
SupportMessage . objects . filter (
room = room ,
sender_type = SupportMessage . SenderType . USER ,
is_read = False ,
) . update ( is_read = True )
room . unread_by_admin = 0
room . save ( update_fields = [ ' unread_by_admin ' , ' updated_at ' ] )
messages_qs = room . messages . select_related ( ' sender ' ) . order_by ( ' id ' )
messages_list = list ( messages_qs )
return Response (
{
' status ' : ' success ' ,
' room ' : AdminSupportRoomSerializer ( room , context = { ' request ' : request } ) . data ,
' messages ' : AdminSupportMessageSerializer (
messages_list ,
many = True ,
context = { ' request ' : request } ,
) . data ,
' current_user ' : serialize_admin_chat_user ( request . user , request = request ) ,
' total_pages ' : 1 ,
' current_page ' : 1 ,
' per_page ' : max ( 20 , len ( messages_list ) ) ,
' total_messages ' : len ( messages_list ) ,
' total_unread_count ' : get_total_admin_unread_count ( ) ,
} ,
status = status . HTTP_200_OK ,
)
class AdminSupportRoomMessagesView ( APIView ) :
"""
GET / api / support / admin / rooms / < int : room_id > / messages / ? after_id = < id >
Returns messages for a SupportRoom and marks unread user messages as read .
POST / api / support / admin / rooms / < int : room_id > / messages /
Sends a reply from the admin / specialist ( `sender_type='support'` ) .
"""
authentication_classes = [ TokenAuthentication ]
permission_classes = [ IsAuthenticated , IsSupportAdminPanelUser ]
def get ( self , request , room_id : int , * args , * * kwargs ) :
room = get_object_or_404 (
SupportRoom . objects . select_related ( ' user ' , ' assigned_to ' ) ,
pk = room_id ,
)
after_id_param = request . query_params . get ( ' after_id ' )
messages_qs = room . messages . select_related ( ' sender ' ) . order_by ( ' id ' )
if after_id_param is not None :
try :
after_id = int ( after_id_param )
if after_id > 0 :
messages_qs = messages_qs . filter ( id__gt = after_id )
except ( TypeError , ValueError ) :
pass
messages_list = list ( messages_qs )
# Mark user messages as read when admin views the room
if room . unread_by_admin > 0 or any (
m . sender_type == SupportMessage . SenderType . USER and not m . is_read
for m in messages_list
) :
SupportMessage . objects . filter (
room = room ,
sender_type = SupportMessage . SenderType . USER ,
is_read = False ,
) . update ( is_read = True )
if room . unread_by_admin != 0 :
room . unread_by_admin = 0
room . save ( update_fields = [ ' unread_by_admin ' , ' updated_at ' ] )
total_messages = room . messages . count ( )
return Response (
{
' status ' : ' success ' ,
' room ' : AdminSupportRoomSerializer ( room , context = { ' request ' : request } ) . data ,
' messages ' : AdminSupportMessageSerializer (
messages_list ,
many = True ,
context = { ' request ' : request } ,
) . data ,
' current_user ' : serialize_admin_chat_user ( request . user , request = request ) ,
' total_pages ' : 1 ,
' current_page ' : 1 ,
' per_page ' : max ( 50 , total_messages ) ,
' total_messages ' : total_messages ,
' total_unread_count ' : get_total_admin_unread_count ( ) ,
} ,
status = status . HTTP_200_OK ,
)
@transaction.atomic
def post ( self , request , room_id : int , * args , * * kwargs ) :
room = get_object_or_404 (
SupportRoom . objects . select_related ( ' user ' , ' assigned_to ' ) ,
pk = room_id ,
)
serializer = SendSupportMessageSerializer ( data = request . data )
serializer . is_valid ( raise_exception = True )
if not room . assigned_to_id :
room . assigned_to = request . user
room . save ( update_fields = [ ' assigned_to ' , ' updated_at ' ] )
message = SupportMessage . objects . create (
room = room ,
sender_type = SupportMessage . SenderType . SUPPORT ,
sender = request . user ,
content = serializer . validated_data [ ' content ' ] ,
)
room . refresh_from_db ( )
return Response (
{
' status ' : ' success ' ,
' room ' : AdminSupportRoomSerializer ( room , context = { ' request ' : request } ) . data ,
' message ' : AdminSupportMessageSerializer (
message ,
context = { ' request ' : request } ,
) . data ,
' current_user ' : serialize_admin_chat_user ( request . user , request = request ) ,
' total_unread_count ' : get_total_admin_unread_count ( ) ,
} ,
status = status . HTTP_201_CREATED ,
)
class AdminSupportMessageDetailView ( APIView ) :
"""
PATCH / api / support / admin / messages / < int : message_id > /
Updates the content of a support message .
DELETE / api / support / admin / messages / < int : message_id > /
Deletes a support message .
"""
authentication_classes = [ TokenAuthentication ]
permission_classes = [ IsAuthenticated , IsSupportAdminPanelUser ]
@transaction.atomic
def patch ( self , request , message_id : int , * args , * * kwargs ) :
message = get_object_or_404 (
SupportMessage . objects . select_related ( ' room ' , ' sender ' ) ,
pk = message_id ,
)
serializer = SendSupportMessageSerializer ( data = request . data )
serializer . is_valid ( raise_exception = True )
message . content = serializer . validated_data [ ' content ' ]
message . save ( update_fields = [ ' content ' ] )
return Response (
{
' status ' : ' success ' ,
' room_id ' : message . room_id ,
' message ' : AdminSupportMessageSerializer (
message ,
context = { ' request ' : request } ,
) . data ,
' total_unread_count ' : get_total_admin_unread_count ( ) ,
} ,
status = status . HTTP_200_OK ,
)
@transaction.atomic
def delete ( self , request , message_id : int , * args , * * kwargs ) :
message = get_object_or_404 (
SupportMessage . objects . select_related ( ' room ' ) ,
pk = message_id ,
)
room = message . room
deleted_id = message . id
message . delete ( )
last_msg = room . messages . order_by ( ' -id ' ) . first ( )
room . last_message_at = last_msg . created_at if last_msg else None
room . save ( update_fields = [ ' last_message_at ' , ' updated_at ' ] )
return Response (
{
' status ' : ' success ' ,
' room_id ' : room . id ,
' deleted_message_id ' : deleted_id ,
' room ' : AdminSupportRoomSerializer ( room , context = { ' request ' : request } ) . data ,
' total_unread_count ' : get_total_admin_unread_count ( ) ,
} ,
status = status . HTTP_200_OK ,
)