mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: mise en place de la messagerie [#17]
This commit is contained in:
@ -1,211 +1,455 @@
|
||||
from django.http.response import JsonResponse
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.parsers import JSONParser
|
||||
from django.conf import settings
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.db.models import Q
|
||||
from .models import Messagerie
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from django.db import models
|
||||
from .models import Conversation, ConversationParticipant, Message, UserPresence
|
||||
from Auth.models import Profile, ProfileRole
|
||||
from GestionMessagerie.serializers import MessageSerializer
|
||||
from School.models import Teacher
|
||||
|
||||
from School.serializers import TeacherSerializer
|
||||
|
||||
import N3wtSchool.mailManager as mailer
|
||||
from N3wtSchool import bdd
|
||||
from GestionMessagerie.serializers import (
|
||||
ConversationSerializer, MessageSerializer,
|
||||
ConversationCreateSerializer, UserPresenceSerializer,
|
||||
ProfileSimpleSerializer
|
||||
)
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from rest_framework.exceptions import NotFound
|
||||
from django.utils import timezone
|
||||
import os
|
||||
import uuid
|
||||
import logging
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.models import Q
|
||||
|
||||
class MessagerieView(APIView):
|
||||
def get(self, request, profile_id):
|
||||
messagesList = bdd.getObjects(_objectName=Messagerie, _columnName='destinataire__id', _value=profile_id)
|
||||
messages_serializer = MessageSerializer(messagesList, many=True)
|
||||
return JsonResponse(messages_serializer.data, safe=False)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class MessageView(APIView):
|
||||
def post(self, request):
|
||||
message_data=JSONParser().parse(request)
|
||||
message_serializer = MessageSerializer(data=message_data)
|
||||
# ====================== MESSAGERIE INSTANTANÉE ======================
|
||||
|
||||
if message_serializer.is_valid():
|
||||
message_serializer.save()
|
||||
|
||||
return JsonResponse('Nouveau Message ajouté', safe=False)
|
||||
|
||||
return JsonResponse(message_serializer.errors, safe=False)
|
||||
|
||||
class MessageSimpleView(APIView):
|
||||
def get(self, request, id):
|
||||
message=bdd.getObject(Messagerie, "id", id)
|
||||
message_serializer=MessageSerializer(message)
|
||||
return JsonResponse(message_serializer.data, safe=False)
|
||||
|
||||
class SendEmailView(APIView):
|
||||
class InstantConversationListView(APIView):
|
||||
"""
|
||||
API pour envoyer des emails aux parents et professeurs.
|
||||
API pour lister les conversations instantanées d'un utilisateur
|
||||
"""
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
recipients = data.get('recipients', [])
|
||||
cc = data.get('cc', [])
|
||||
bcc = data.get('bcc', [])
|
||||
subject = data.get('subject', 'Notification')
|
||||
message = data.get('message', '')
|
||||
establishment_id = data.get('establishment_id', '')
|
||||
|
||||
if not recipients or not message:
|
||||
return Response({'error': 'Les destinataires et le message sont requis.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Liste les conversations instantanées d'un utilisateur",
|
||||
responses={200: ConversationSerializer(many=True)}
|
||||
)
|
||||
def get(self, request, user_id=None):
|
||||
try:
|
||||
# Récupérer la connexion SMTP
|
||||
connection = mailer.getConnection(establishment_id)
|
||||
user = Profile.objects.get(id=user_id)
|
||||
|
||||
# Envoyer l'email
|
||||
return mailer.sendMail(
|
||||
subject=subject,
|
||||
message=message,
|
||||
recipients=recipients,
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
attachments=[],
|
||||
connection=connection
|
||||
)
|
||||
except NotFound as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_404_NOT_FOUND)
|
||||
conversations = Conversation.objects.filter(
|
||||
participants__participant=user,
|
||||
participants__is_active=True,
|
||||
is_active=True
|
||||
).distinct().order_by('-last_activity')
|
||||
|
||||
serializer = ConversationSerializer(conversations, many=True, context={'user': user})
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
except Profile.DoesNotExist:
|
||||
return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class ContactsView(APIView):
|
||||
class InstantConversationCreateView(APIView):
|
||||
"""
|
||||
API pour récupérer les contacts associés à un établissement.
|
||||
"""
|
||||
def get(self, request, establishment_id):
|
||||
try:
|
||||
# Récupérer les enseignants associés à l'établissement
|
||||
teachers = Teacher.objects.filter(profile_role__establishment_id=establishment_id)
|
||||
teachers_serializer = TeacherSerializer(teachers, many=True)
|
||||
|
||||
# Ajouter un contact pour l'administration
|
||||
admin_contact = {
|
||||
"id": "admin",
|
||||
"name": "Administration",
|
||||
"email": "admin@etablissement.com",
|
||||
"profilePic": "https://www.gravatar.com/avatar/admin"
|
||||
}
|
||||
|
||||
contacts = [admin_contact] + teachers_serializer.data
|
||||
return Response(contacts, status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def search_recipients(request):
|
||||
"""
|
||||
API pour rechercher des destinataires en fonction d'un terme de recherche et d'un établissement.
|
||||
"""
|
||||
query = request.GET.get('q', '').strip() # Récupérer le terme de recherche depuis les paramètres GET
|
||||
establishment_id = request.GET.get('establishment_id', None) # Récupérer l'ID de l'établissement
|
||||
|
||||
if not query:
|
||||
return JsonResponse([], safe=False) # Retourner une liste vide si aucun terme n'est fourni
|
||||
|
||||
if not establishment_id:
|
||||
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Rechercher dans les champs pertinents (nom, prénom, email) et filtrer par establishment_id
|
||||
profiles = Profile.objects.filter(
|
||||
Q(first_name__icontains=query) |
|
||||
Q(last_name__icontains=query) |
|
||||
Q(email__icontains=query),
|
||||
roles__establishment_id=establishment_id, # Utiliser 'roles' au lieu de 'profilerole'
|
||||
roles__is_active=True # Filtrer uniquement les ProfileRole actifs
|
||||
).distinct()
|
||||
|
||||
# Construire la réponse avec les rôles associés
|
||||
results = []
|
||||
for profile in profiles:
|
||||
profile_roles = ProfileRole.objects.filter(
|
||||
profile=profile,
|
||||
establishment_id=establishment_id,
|
||||
is_active=True # Inclure uniquement les ProfileRole actifs
|
||||
).values(
|
||||
'id', 'role_type', 'establishment__name', 'is_active'
|
||||
)
|
||||
results.append({
|
||||
'id': profile.id,
|
||||
'first_name': profile.first_name,
|
||||
'last_name': profile.last_name,
|
||||
'email': profile.email,
|
||||
'roles': list(profile_roles) # Inclure tous les rôles actifs associés pour cet établissement
|
||||
})
|
||||
|
||||
return JsonResponse(results, safe=False)
|
||||
|
||||
class ConversationListView(APIView):
|
||||
"""
|
||||
Liste les conversations d'un utilisateur (parent ou enseignant).
|
||||
Retourne la liste des interlocuteurs et le dernier message échangé.
|
||||
API pour créer une nouvelle conversation instantanée
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Liste les conversations d'un utilisateur (parent ou enseignant).",
|
||||
responses={200: openapi.Response('Liste des conversations')}
|
||||
operation_description="Crée une nouvelle conversation instantanée",
|
||||
request_body=ConversationCreateSerializer,
|
||||
responses={201: ConversationSerializer}
|
||||
)
|
||||
def get(self, request, profile_id):
|
||||
# Récupérer toutes les conversations où l'utilisateur est émetteur ou destinataire
|
||||
messages = Messagerie.objects.filter(Q(emetteur_id=profile_id) | Q(destinataire_id=profile_id))
|
||||
# Grouper par conversation_id
|
||||
conversations = {}
|
||||
for msg in messages.order_by('-date_envoi'):
|
||||
conv_id = msg.conversation_id or f"{min(msg.emetteur_id, msg.destinataire_id)}_{max(msg.emetteur_id, msg.destinataire_id)}"
|
||||
if conv_id not in conversations:
|
||||
conversations[conv_id] = msg
|
||||
# Préparer la réponse
|
||||
data = []
|
||||
for conv_id, last_msg in conversations.items():
|
||||
interlocuteur = last_msg.emetteur if last_msg.destinataire_id == int(profile_id) else last_msg.destinataire
|
||||
data.append({
|
||||
'conversation_id': conv_id,
|
||||
'last_message': MessageSerializer(last_msg).data,
|
||||
'interlocuteur': {
|
||||
'id': interlocuteur.id,
|
||||
'first_name': interlocuteur.first_name,
|
||||
'last_name': interlocuteur.last_name,
|
||||
'email': interlocuteur.email,
|
||||
}
|
||||
})
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
def post(self, request):
|
||||
serializer = ConversationCreateSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
conversation = serializer.save()
|
||||
response_serializer = ConversationSerializer(conversation, context={'user': request.user})
|
||||
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ConversationMessagesView(APIView):
|
||||
class InstantMessageListView(APIView):
|
||||
"""
|
||||
Récupère tous les messages d'une conversation donnée.
|
||||
API pour lister les messages d'une conversation
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Récupère tous les messages d'une conversation donnée.",
|
||||
responses={200: openapi.Response('Liste des messages')}
|
||||
operation_description="Liste les messages d'une conversation",
|
||||
responses={200: MessageSerializer(many=True)}
|
||||
)
|
||||
def get(self, request, conversation_id):
|
||||
messages = Messagerie.objects.filter(conversation_id=conversation_id).order_by('date_envoi')
|
||||
serializer = MessageSerializer(messages, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
try:
|
||||
conversation = Conversation.objects.get(id=conversation_id)
|
||||
messages = conversation.messages.filter(is_deleted=False).order_by('created_at')
|
||||
|
||||
class MarkAsReadView(APIView):
|
||||
# Récupérer l'utilisateur actuel depuis les paramètres de requête
|
||||
user_id = request.GET.get('user_id')
|
||||
user = None
|
||||
if user_id:
|
||||
try:
|
||||
user = Profile.objects.get(id=user_id)
|
||||
except Profile.DoesNotExist:
|
||||
pass
|
||||
|
||||
serializer = MessageSerializer(messages, many=True, context={'user': user})
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
except Conversation.DoesNotExist:
|
||||
return Response({'error': 'Conversation not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class InstantMessageCreateView(APIView):
|
||||
"""
|
||||
Marque tous les messages reçus dans une conversation comme lus pour l'utilisateur connecté.
|
||||
API pour envoyer un nouveau message instantané
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Marque tous les messages reçus dans une conversation comme lus pour l'utilisateur connecté.",
|
||||
operation_description="Envoie un nouveau message instantané",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'profile_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='ID du profil utilisateur')
|
||||
'conversation_id': openapi.Schema(type=openapi.TYPE_STRING, description='ID de la conversation'),
|
||||
'sender_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='ID de l\'expéditeur'),
|
||||
'content': openapi.Schema(type=openapi.TYPE_STRING, description='Contenu du message'),
|
||||
'message_type': openapi.Schema(type=openapi.TYPE_STRING, description='Type de message', default='text')
|
||||
},
|
||||
required=['profile_id']
|
||||
required=['conversation_id', 'sender_id', 'content']
|
||||
),
|
||||
responses={200: openapi.Response('Statut OK')}
|
||||
responses={201: MessageSerializer}
|
||||
)
|
||||
def post(self, request):
|
||||
try:
|
||||
conversation_id = request.data.get('conversation_id')
|
||||
sender_id = request.data.get('sender_id')
|
||||
content = request.data.get('content', '').strip()
|
||||
message_type = request.data.get('message_type', 'text')
|
||||
|
||||
if not all([conversation_id, sender_id, content]):
|
||||
return Response(
|
||||
{'error': 'conversation_id, sender_id, and content are required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# Vérifier que la conversation existe
|
||||
conversation = Conversation.objects.get(id=conversation_id)
|
||||
|
||||
# Vérifier que l'expéditeur existe et peut envoyer dans cette conversation
|
||||
sender = Profile.objects.get(id=sender_id)
|
||||
participant = ConversationParticipant.objects.filter(
|
||||
conversation=conversation,
|
||||
participant=sender,
|
||||
is_active=True
|
||||
).first()
|
||||
|
||||
if not participant:
|
||||
return Response(
|
||||
{'error': 'You are not a participant in this conversation'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
|
||||
# Récupérer les données de fichier si disponibles
|
||||
file_url = request.data.get('file_url')
|
||||
file_name = request.data.get('file_name')
|
||||
file_type = request.data.get('file_type')
|
||||
file_size = request.data.get('file_size')
|
||||
|
||||
# Créer le message
|
||||
message = Message.objects.create(
|
||||
conversation=conversation,
|
||||
sender=sender,
|
||||
content=content,
|
||||
message_type=message_type,
|
||||
file_url=file_url,
|
||||
file_name=file_name,
|
||||
file_type=file_type,
|
||||
file_size=file_size
|
||||
)
|
||||
|
||||
# Mettre à jour l'activité de la conversation
|
||||
conversation.last_activity = message.created_at
|
||||
conversation.save(update_fields=['last_activity'])
|
||||
|
||||
serializer = MessageSerializer(message)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
except Conversation.DoesNotExist:
|
||||
return Response({'error': 'Conversation not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Profile.DoesNotExist:
|
||||
return Response({'error': 'Sender not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class InstantMarkAsReadView(APIView):
|
||||
"""
|
||||
API pour marquer une conversation comme lue
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Marque une conversation comme lue pour un utilisateur",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'user_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='ID de l\'utilisateur')
|
||||
},
|
||||
required=['user_id']
|
||||
),
|
||||
responses={200: openapi.Response('Success')}
|
||||
)
|
||||
def post(self, request, conversation_id):
|
||||
profile_id = request.data.get('profile_id')
|
||||
Messagerie.objects.filter(conversation_id=conversation_id, destinataire_id=profile_id, is_read=False).update(is_read=True)
|
||||
return Response({'status': 'ok'}, status=status.HTTP_200_OK)
|
||||
try:
|
||||
user_id = request.data.get('user_id')
|
||||
if not user_id:
|
||||
return Response({'error': 'user_id is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
participant = ConversationParticipant.objects.get(
|
||||
conversation_id=conversation_id,
|
||||
participant_id=user_id,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
participant.last_read_at = timezone.now()
|
||||
participant.save(update_fields=['last_read_at'])
|
||||
|
||||
return Response({'status': 'success'}, status=status.HTTP_200_OK)
|
||||
|
||||
except ConversationParticipant.DoesNotExist:
|
||||
return Response({'error': 'Participant not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class UserPresenceView(APIView):
|
||||
"""
|
||||
API pour gérer la présence des utilisateurs
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Met à jour le statut de présence d'un utilisateur",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'status': openapi.Schema(type=openapi.TYPE_STRING, description='Statut de présence')
|
||||
},
|
||||
required=['status']
|
||||
),
|
||||
responses={200: UserPresenceSerializer}
|
||||
)
|
||||
def post(self, request, user_id):
|
||||
try:
|
||||
user = Profile.objects.get(id=user_id)
|
||||
status_value = request.data.get('status')
|
||||
|
||||
if status_value not in ['online', 'away', 'busy', 'offline']:
|
||||
return Response({'error': 'Invalid status'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
presence, created = UserPresence.objects.get_or_create(user=user)
|
||||
presence.status = status_value
|
||||
presence.last_seen = timezone.now()
|
||||
presence.save()
|
||||
|
||||
serializer = UserPresenceSerializer(presence)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
except Profile.DoesNotExist:
|
||||
return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Récupère le statut de présence d'un utilisateur",
|
||||
responses={200: UserPresenceSerializer}
|
||||
)
|
||||
def get(self, request, user_id):
|
||||
try:
|
||||
user = Profile.objects.get(id=user_id)
|
||||
presence, created = UserPresence.objects.get_or_create(user=user)
|
||||
|
||||
if created:
|
||||
presence.status = 'offline'
|
||||
presence.save()
|
||||
|
||||
serializer = UserPresenceSerializer(presence)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
except Profile.DoesNotExist:
|
||||
return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class FileUploadView(APIView):
|
||||
"""
|
||||
API pour l'upload de fichiers dans la messagerie instantanée
|
||||
"""
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Upload un fichier pour la messagerie",
|
||||
manual_parameters=[
|
||||
openapi.Parameter('file', openapi.IN_FORM, description="Fichier à uploader", type=openapi.TYPE_FILE, required=True),
|
||||
openapi.Parameter('conversation_id', openapi.IN_FORM, description="ID de la conversation", type=openapi.TYPE_INTEGER, required=True),
|
||||
openapi.Parameter('sender_id', openapi.IN_FORM, description="ID de l'expéditeur", type=openapi.TYPE_INTEGER, required=True),
|
||||
],
|
||||
responses={
|
||||
200: openapi.Response('Success', openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'fileUrl': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'fileName': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'fileSize': openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
'fileType': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
}
|
||||
)),
|
||||
400: 'Bad Request',
|
||||
413: 'File too large',
|
||||
415: 'Unsupported file type'
|
||||
}
|
||||
)
|
||||
def post(self, request):
|
||||
try:
|
||||
file = request.FILES.get('file')
|
||||
conversation_id = request.data.get('conversation_id')
|
||||
sender_id = request.data.get('sender_id')
|
||||
|
||||
if not file:
|
||||
return Response({'error': 'Aucun fichier fourni'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not conversation_id or not sender_id:
|
||||
return Response({'error': 'conversation_id et sender_id requis'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Vérifier que la conversation existe et que l'utilisateur y participe
|
||||
try:
|
||||
conversation = Conversation.objects.get(id=conversation_id)
|
||||
sender = Profile.objects.get(id=sender_id)
|
||||
|
||||
# Vérifier que l'expéditeur participe à la conversation
|
||||
if not ConversationParticipant.objects.filter(
|
||||
conversation=conversation,
|
||||
participant=sender,
|
||||
is_active=True
|
||||
).exists():
|
||||
return Response({'error': 'Accès non autorisé à cette conversation'}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
except (Conversation.DoesNotExist, Profile.DoesNotExist):
|
||||
return Response({'error': 'Conversation ou utilisateur introuvable'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# Valider le type de fichier
|
||||
allowed_types = [
|
||||
'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp',
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'text/plain'
|
||||
]
|
||||
|
||||
if file.content_type not in allowed_types:
|
||||
return Response({'error': 'Type de fichier non autorisé'}, status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)
|
||||
|
||||
# Valider la taille du fichier (10MB max)
|
||||
max_size = 10 * 1024 * 1024 # 10MB
|
||||
if file.size > max_size:
|
||||
return Response({'error': 'Fichier trop volumineux (max 10MB)'}, status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE)
|
||||
|
||||
# Générer un nom de fichier unique
|
||||
file_extension = os.path.splitext(file.name)[1]
|
||||
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
||||
|
||||
# Chemin de stockage : messagerie/conversation_id/
|
||||
storage_path = f"messagerie/{conversation_id}/{unique_filename}"
|
||||
|
||||
# Sauvegarder le fichier
|
||||
file_path = default_storage.save(storage_path, ContentFile(file.read()))
|
||||
|
||||
# Générer l'URL du fichier
|
||||
file_url = default_storage.url(file_path)
|
||||
if not file_url.startswith('http'):
|
||||
# Construire l'URL complète si nécessaire
|
||||
file_url = request.build_absolute_uri(file_url)
|
||||
|
||||
return Response({
|
||||
'fileUrl': file_url,
|
||||
'fileName': file.name,
|
||||
'fileSize': file.size,
|
||||
'fileType': file.content_type,
|
||||
'filePath': file_path
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': f'Erreur lors de l\'upload: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class InstantRecipientSearchView(APIView):
|
||||
"""
|
||||
API pour rechercher des destinataires pour la messagerie instantanée
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Recherche des destinataires pour la messagerie instantanée",
|
||||
manual_parameters=[
|
||||
openapi.Parameter('establishment_id', openapi.IN_QUERY, description="ID de l'établissement", type=openapi.TYPE_INTEGER, required=True),
|
||||
openapi.Parameter('q', openapi.IN_QUERY, description="Terme de recherche", type=openapi.TYPE_STRING, required=True)
|
||||
],
|
||||
responses={200: ProfileSimpleSerializer(many=True)}
|
||||
)
|
||||
def get(self, request):
|
||||
try:
|
||||
establishment_id = request.query_params.get('establishment_id')
|
||||
search_query = request.query_params.get('q', '').strip()
|
||||
|
||||
if not establishment_id:
|
||||
return Response({'error': 'establishment_id is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Récupérer les IDs des profils actifs dans l'établissement
|
||||
profile_roles = ProfileRole.objects.filter(
|
||||
establishment_id=establishment_id,
|
||||
is_active=True
|
||||
).values_list('profile_id', flat=True)
|
||||
|
||||
# Rechercher les profils correspondants
|
||||
users = Profile.objects.filter(id__in=profile_roles)
|
||||
|
||||
# Appliquer le filtre de recherche si un terme est fourni
|
||||
if search_query:
|
||||
users = users.filter(
|
||||
Q(first_name__icontains=search_query) |
|
||||
Q(last_name__icontains=search_query) |
|
||||
Q(email__icontains=search_query)
|
||||
)
|
||||
|
||||
# Exclure l'utilisateur actuel des résultats
|
||||
if request.user.is_authenticated:
|
||||
users = users.exclude(id=request.user.id)
|
||||
|
||||
serializer = ProfileSimpleSerializer(users[:10], many=True) # Limiter à 10 résultats
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class InstantConversationDeleteView(APIView):
|
||||
"""
|
||||
API pour supprimer (désactiver) une conversation instantanée
|
||||
"""
|
||||
@swagger_auto_schema(
|
||||
operation_description="Supprime une conversation instantanée (désactivation soft)",
|
||||
responses={200: openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'success': openapi.Schema(type=openapi.TYPE_BOOLEAN),
|
||||
'message': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
)}
|
||||
)
|
||||
def delete(self, request, conversation_id):
|
||||
try:
|
||||
# Récupérer la conversation par son ID UUID
|
||||
conversation = Conversation.objects.filter(id=conversation_id).first()
|
||||
|
||||
if not conversation:
|
||||
return Response({'error': 'Conversation not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# Suppression simple : désactiver la conversation
|
||||
conversation.is_active = False
|
||||
conversation.save()
|
||||
|
||||
return Response({
|
||||
'success': True,
|
||||
'message': 'Conversation deleted successfully'
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting conversation: {str(e)}")
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user