feat: add start and end meeting endpoints for scheduled appointments #56

Merged
Saani merged 1 commits from feature/meetings into main 2025-12-04 10:57:56 +00:00
3 changed files with 86 additions and 1 deletions

View File

@ -360,6 +360,29 @@ def api_root(request, format=None):
"Clears scheduled datetime if any" "Clears scheduled datetime if any"
] ]
}, },
"start_meeting": {
"description": "Start the Jitsi meeting for a scheduled appointment",
"url": request.build_absolute_uri("/api/meetings/appointments/<uuid:pk>/start/"),
"methods": ["POST"],
"authentication": "Required",
"prerequisites": "Appointment must be in 'scheduled' status",
"side_effects": [
"Updates meeting status to 'active'",
"Allows participants to join the meeting"
]
},
"end_meeting": {
"description": "End the Jitsi meeting for a scheduled appointment",
"url": request.build_absolute_uri("/api/meetings/appointments/<uuid:pk>/end/"),
"methods": ["POST"],
"authentication": "Required",
"prerequisites": "Appointment must be in 'scheduled' or 'active' status",
"side_effects": [
"Updates meeting status to 'completed'",
"Clears Jitsi meeting information",
"Sends completion email to user"
]
},
"appointment_stats": { "appointment_stats": {
"description": "Get appointment statistics and analytics with availability metrics (Admin only)", "description": "Get appointment statistics and analytics with availability metrics (Admin only)",

View File

@ -19,7 +19,9 @@ from .views import (
UpcomingMeetingsView, UpcomingMeetingsView,
MeetingAnalyticsView, MeetingAnalyticsView,
BulkMeetingActionsView, BulkMeetingActionsView,
availability_overview availability_overview,
EndMeetingView,
StartMeetingView
) )
urlpatterns = [ urlpatterns = [
@ -96,6 +98,9 @@ urlpatterns = [
# Meeting Status & Info # Meeting Status & Info
path('meetings/<uuid:pk>/status/', MeetingActionView.as_view(), {'get_status': True}, name='meeting-status'), path('meetings/<uuid:pk>/status/', MeetingActionView.as_view(), {'get_status': True}, name='meeting-status'),
path('meetings/<uuid:pk>/info/', JoinMeetingView.as_view(), {'info_only': True}, name='meeting-info'), path('meetings/<uuid:pk>/info/', JoinMeetingView.as_view(), {'info_only': True}, name='meeting-info'),
path('meetings/<uuid:pk>/end/', EndMeetingView.as_view(), name='end-meeting-simple'),
path('meetings/<uuid:pk>/start/', StartMeetingView.as_view(), name='start-meeting-simple'),
# Meeting Webhook/Notification endpoints (for Jitsi callbacks) # Meeting Webhook/Notification endpoints (for Jitsi callbacks)
path('meetings/webhook/jitsi/', MeetingActionView.as_view(), {'webhook': True}, name='jitsi-webhook'), path('meetings/webhook/jitsi/', MeetingActionView.as_view(), {'webhook': True}, name='jitsi-webhook'),

View File

@ -407,6 +407,63 @@ class MatchingAvailabilityView(generics.GenericAPIView):
) )
class StartMeetingView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
serializer_class = MeetingActionSerializer
queryset = AppointmentRequest.objects.all()
lookup_field = 'pk'
def post(self, request, pk):
appointment = self.get_object()
if not request.user.is_staff:
return Response(
{'error': 'Only staff members can start meetings'},
status=status.HTTP_403_FORBIDDEN
)
if appointment.status != 'scheduled':
return Response(
{'error': 'Only scheduled appointments can be started'},
status=status.HTTP_400_BAD_REQUEST
)
appointment.start_meeting()
return Response({
'appointment_id': str(appointment.id),
'message': 'Meeting started successfully',
'meeting_started_at': appointment.meeting_started_at,
})
class EndMeetingView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
serializer_class = MeetingActionSerializer
queryset = AppointmentRequest.objects.all()
lookup_field = 'pk'
def post(self, request, pk):
appointment = self.get_object()
if not request.user.is_staff:
return Response(
{'error': 'Only staff members can end meetings'},
status=status.HTTP_403_FORBIDDEN
)
if appointment.status != 'scheduled':
return Response(
{'error': 'Only scheduled appointments can be ended'},
status=status.HTTP_400_BAD_REQUEST
)
appointment.end_meeting()
return Response({
'appointment_id': str(appointment.id),
'message': 'Meeting ended successfully',
'meeting_ended_at': appointment.meeting_ended_at,
})
class JoinMeetingView(generics.GenericAPIView): class JoinMeetingView(generics.GenericAPIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
serializer_class = MeetingJoinSerializer serializer_class = MeetingJoinSerializer