Add comprehensive API documentation for user management endpoints including profile updates, user listing, and admin user management features. Update appointment model to include additional status options (completed, cancelled) and add max_length constraint to email field. Change appointment creation endpoint to require user authentication instead of being public. Changes: - Add API docs for update_profile, get_profile, all-users endpoints - Add API docs for activate-deactivate-user and delete-user admin endpoints - Update appointment creation to require authentication - Add 'completed' and 'cancelled' status options to Appointment model - Add max_length constraint to EncryptedEmailField - Regenerate initial migration with updated model definitions
171 lines
6.3 KiB
Python
171 lines
6.3 KiB
Python
from rest_framework import generics, status
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
|
from django.utils import timezone
|
|
from datetime import datetime, timedelta
|
|
from .models import AdminWeeklyAvailability, AppointmentRequest
|
|
from .serializers import (
|
|
AdminWeeklyAvailabilitySerializer,
|
|
AppointmentRequestSerializer,
|
|
AppointmentRequestCreateSerializer,
|
|
AppointmentScheduleSerializer,
|
|
AppointmentRejectSerializer
|
|
)
|
|
from .email_service import EmailService
|
|
from users.models import CustomUser
|
|
|
|
class AdminAvailabilityView(generics.RetrieveUpdateAPIView):
|
|
permission_classes = [IsAuthenticated]
|
|
serializer_class = AdminWeeklyAvailabilitySerializer
|
|
|
|
def get_object(self):
|
|
obj, created = AdminWeeklyAvailability.objects.get_or_create(
|
|
defaults={'available_days': []}
|
|
)
|
|
return obj
|
|
|
|
class AppointmentRequestListView(generics.ListAPIView):
|
|
serializer_class = AppointmentRequestSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
def get_queryset(self):
|
|
queryset = AppointmentRequest.objects.all()
|
|
|
|
if self.request.user.is_staff:
|
|
return queryset
|
|
|
|
return queryset.filter(email=self.request.user.email)
|
|
|
|
class AppointmentRequestCreateView(generics.CreateAPIView):
|
|
permission_classes = [IsAuthenticated]
|
|
queryset = AppointmentRequest.objects.all()
|
|
serializer_class = AppointmentRequestCreateSerializer
|
|
|
|
def perform_create(self, serializer):
|
|
availability = AdminWeeklyAvailability.objects.first()
|
|
if availability:
|
|
available_days = availability.available_days
|
|
preferred_dates = serializer.validated_data['preferred_dates']
|
|
|
|
for date_str in preferred_dates:
|
|
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
|
if date_obj.weekday() not in available_days:
|
|
from rest_framework.exceptions import ValidationError
|
|
raise ValidationError(f'Date {date_str} is not available for appointments.')
|
|
|
|
appointment = serializer.save()
|
|
EmailService.send_admin_notification(appointment)
|
|
|
|
class AppointmentRequestDetailView(generics.RetrieveAPIView):
|
|
permission_classes = [IsAuthenticated]
|
|
queryset = AppointmentRequest.objects.all()
|
|
serializer_class = AppointmentRequestSerializer
|
|
lookup_field = 'pk'
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsAuthenticated])
|
|
def schedule_appointment(request, pk):
|
|
try:
|
|
appointment = AppointmentRequest.objects.get(pk=pk)
|
|
except AppointmentRequest.DoesNotExist:
|
|
return Response({'error': 'Appointment not found'}, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
if appointment.status != 'pending_review':
|
|
return Response(
|
|
{'error': 'Only pending review appointments can be scheduled.'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
serializer = AppointmentScheduleSerializer(data=request.data)
|
|
if serializer.is_valid():
|
|
appointment.schedule_appointment(
|
|
datetime_obj=serializer.validated_data['scheduled_datetime'],
|
|
duration=serializer.validated_data['scheduled_duration']
|
|
)
|
|
|
|
EmailService.send_appointment_scheduled(appointment)
|
|
|
|
response_serializer = AppointmentRequestSerializer(appointment)
|
|
return Response({
|
|
**response_serializer.data,
|
|
'message': 'Appointment scheduled successfully. Jitsi meeting created.',
|
|
'jitsi_meeting_created': appointment.has_jitsi_meeting
|
|
})
|
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsAuthenticated])
|
|
def reject_appointment(request, pk):
|
|
try:
|
|
appointment = AppointmentRequest.objects.get(pk=pk)
|
|
except AppointmentRequest.DoesNotExist:
|
|
return Response({'error': 'Appointment not found'}, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
if appointment.status != 'pending_review':
|
|
return Response(
|
|
{'error': 'Only pending appointments can be rejected'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
serializer = AppointmentRejectSerializer(data=request.data)
|
|
if serializer.is_valid():
|
|
appointment.reject_appointment(serializer.validated_data.get('rejection_reason', ''))
|
|
EmailService.send_appointment_rejected(appointment)
|
|
return Response(AppointmentRequestSerializer(appointment).data)
|
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([AllowAny])
|
|
def available_dates(request):
|
|
availability = AdminWeeklyAvailability.objects.first()
|
|
if not availability:
|
|
return Response([])
|
|
|
|
available_days = availability.available_days
|
|
today = timezone.now().date()
|
|
available_dates = []
|
|
|
|
for i in range(1, 31):
|
|
date = today + timedelta(days=i)
|
|
if date.weekday() in available_days:
|
|
available_dates.append(date.strftime('%Y-%m-%d'))
|
|
|
|
return Response(available_dates)
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def user_appointments(request):
|
|
appointments = AppointmentRequest.objects.filter(
|
|
email=request.user.email
|
|
).order_by('-created_at')
|
|
|
|
serializer = AppointmentRequestSerializer(appointments, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def appointment_stats(request):
|
|
if not request.user.is_staff:
|
|
return Response(
|
|
{'error': 'Unauthorized'},
|
|
status=status.HTTP_403_FORBIDDEN
|
|
)
|
|
|
|
total = AppointmentRequest.objects.count()
|
|
users = CustomUser.objects.filter(is_staff=False).count()
|
|
pending = AppointmentRequest.objects.filter(status='pending_review').count()
|
|
scheduled = AppointmentRequest.objects.filter(status='scheduled').count()
|
|
rejected = AppointmentRequest.objects.filter(status='rejected').count()
|
|
|
|
return Response({
|
|
'total_requests': total,
|
|
'pending_review': pending,
|
|
'scheduled': scheduled,
|
|
'rejected': rejected,
|
|
'users': users,
|
|
'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0
|
|
}) |