- Enable meetings app in INSTALLED_APPS and add URL routing - Switch from PostgreSQL to SQLite for default database configuration - Remove meetings directory from .gitignore - Move API root endpoint from users app to main URL configuration - Remove HIPAA-specific email and compliance settings (EMAIL_ENCRYPTION_KEY, HIPAA_EMAIL_CONFIG, BAA_VERIFICATION) - Add SITE_NAME and ENCRYPTION_KEY environment variables - Regenerate initial user migrations These changes simplify the development setup by using SQLite as the default database and removing complex compliance configurations while enabling the core meetings functionality.
168 lines
6.2 KiB
Python
168 lines
6.2 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
|
|
|
|
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 = [AllowAny]
|
|
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()
|
|
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,
|
|
'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0
|
|
}) |