You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
6.3 KiB
148 lines
6.3 KiB
from django.db.models import Q
|
|
from django.shortcuts import get_object_or_404
|
|
from rest_framework import generics, status, permissions
|
|
from rest_framework.response import Response
|
|
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiTypes
|
|
|
|
from apps.meetings.models import MeetingRequest, MeetingStatus
|
|
from apps.meetings.serializers import (
|
|
MeetingListSerializer,
|
|
MeetingDetailSerializer,
|
|
MeetingCreateSerializer,
|
|
MeetingUpdateSerializer,
|
|
)
|
|
from apps.meetings.permissions import IsMeetingParticipantOrStaff, CanManageMeeting
|
|
from apps.profiles.models import InstitutionMember
|
|
|
|
|
|
@extend_schema_view(
|
|
get=extend_schema(
|
|
tags=['Meetings'],
|
|
summary="List scheduled & pending meetings",
|
|
description="List bilateral video meetings involving the authenticated user's affiliated institutions.",
|
|
parameters=[
|
|
OpenApiParameter('status', OpenApiTypes.STR, description="Meeting status (pending, scheduled, completed, cancelled, declined)"),
|
|
OpenApiParameter('institution_id', OpenApiTypes.INT, description="Filter meetings by participating institution ID"),
|
|
OpenApiParameter('start_date', OpenApiTypes.DATE, description="Filter meetings from date (YYYY-MM-DD)"),
|
|
OpenApiParameter('end_date', OpenApiTypes.DATE, description="Filter meetings to date (YYYY-MM-DD)"),
|
|
OpenApiParameter('search', OpenApiTypes.STR, description="Search in title or agenda"),
|
|
]
|
|
),
|
|
post=extend_schema(
|
|
tags=['Meetings'],
|
|
summary="Propose a new video meeting",
|
|
description="Schedule/propose a bilateral meeting between institutions (requires institution admin/editor role).",
|
|
request=MeetingCreateSerializer,
|
|
responses={201: MeetingDetailSerializer}
|
|
)
|
|
)
|
|
class MeetingListCreateView(generics.ListCreateAPIView):
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'POST':
|
|
return MeetingCreateSerializer
|
|
return MeetingListSerializer
|
|
|
|
def get_queryset(self):
|
|
user = self.request.user
|
|
if not user.is_authenticated:
|
|
return MeetingRequest.objects.none()
|
|
|
|
if user.is_superuser or getattr(user, 'user_type', None) in ['super_admin', 'regional_admin']:
|
|
qs = MeetingRequest.objects.select_related('requester_institution', 'recipient_institution', 'created_by').all()
|
|
else:
|
|
user_inst_ids = InstitutionMember.objects.filter(user=user).values_list('institution_id', flat=True)
|
|
qs = MeetingRequest.objects.select_related('requester_institution', 'recipient_institution', 'created_by').filter(
|
|
Q(requester_institution_id__in=user_inst_ids) |
|
|
Q(recipient_institution_id__in=user_inst_ids) |
|
|
Q(created_by=user)
|
|
)
|
|
|
|
params = self.request.query_params
|
|
status_param = params.get('status')
|
|
if status_param:
|
|
qs = qs.filter(status=status_param)
|
|
|
|
institution_id = params.get('institution_id')
|
|
if institution_id and str(institution_id).isdigit():
|
|
inst_id = int(institution_id)
|
|
qs = qs.filter(Q(requester_institution_id=inst_id) | Q(recipient_institution_id=inst_id))
|
|
|
|
start_date = params.get('start_date')
|
|
if start_date:
|
|
qs = qs.filter(meeting_date__gte=start_date)
|
|
|
|
end_date = params.get('end_date')
|
|
if end_date:
|
|
qs = qs.filter(meeting_date__lte=end_date)
|
|
|
|
search = params.get('search')
|
|
if search:
|
|
qs = qs.filter(Q(title__icontains=search) | Q(agenda__icontains=search))
|
|
|
|
return qs.distinct()
|
|
|
|
def perform_create(self, serializer):
|
|
return serializer.save()
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
serializer = self.get_serializer(data=request.data, context={'request': request})
|
|
serializer.is_valid(raise_exception=True)
|
|
meeting = self.perform_create(serializer)
|
|
out_serializer = MeetingDetailSerializer(meeting, context={'request': request})
|
|
return Response(out_serializer.data, status=status.HTTP_201_CREATED)
|
|
|
|
|
|
@extend_schema_view(
|
|
get=extend_schema(
|
|
tags=['Meetings'],
|
|
summary="Retrieve meeting details",
|
|
description="Get full meeting details including video link, agenda, and participants."
|
|
),
|
|
patch=extend_schema(
|
|
tags=['Meetings'],
|
|
summary="Update or respond to meeting request",
|
|
description="Accept, reschedule, decline, complete, or update meeting room URL and agenda notes.",
|
|
request=MeetingUpdateSerializer,
|
|
responses={200: MeetingDetailSerializer}
|
|
),
|
|
delete=extend_schema(
|
|
tags=['Meetings'],
|
|
summary="Cancel / delete meeting",
|
|
description="Cancel or delete a meeting request."
|
|
)
|
|
)
|
|
class MeetingDetailView(generics.RetrieveUpdateDestroyAPIView):
|
|
permission_classes = [IsMeetingParticipantOrStaff]
|
|
lookup_field = 'pk'
|
|
|
|
def get_queryset(self):
|
|
return MeetingRequest.objects.select_related('requester_institution', 'recipient_institution', 'created_by').all()
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method in ['PUT', 'PATCH']:
|
|
return MeetingUpdateSerializer
|
|
return MeetingDetailSerializer
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
partial = kwargs.pop('partial', True)
|
|
instance = self.get_object()
|
|
|
|
# Check manage permissions
|
|
checker = CanManageMeeting()
|
|
if not checker.has_object_permission(request, self, instance):
|
|
self.permission_denied(request, message="You do not have permission to manage this meeting.")
|
|
|
|
serializer = MeetingUpdateSerializer(instance, data=request.data, partial=partial, context={'request': request})
|
|
serializer.is_valid(raise_exception=True)
|
|
self.perform_update(serializer)
|
|
out_serializer = MeetingDetailSerializer(instance, context={'request': request})
|
|
return Response(out_serializer.data)
|
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
instance = self.get_object()
|
|
checker = CanManageMeeting()
|
|
if not checker.has_object_permission(request, self, instance):
|
|
self.permission_denied(request, message="You do not have permission to cancel this meeting.")
|
|
return super().destroy(request, *args, **kwargs)
|