agdar/appointments/serializers.py
2025-11-02 14:35:35 +03:00

221 lines
8.2 KiB
Python

"""
DRF Serializers for Appointments app.
"""
from rest_framework import serializers
from .models import Appointment, Provider, Room, Schedule, AppointmentReminder
from core.serializers import PatientListSerializer, ClinicSerializer
class ProviderSerializer(serializers.ModelSerializer):
"""Serializer for Provider model."""
user_name = serializers.CharField(source='user.get_full_name', read_only=True)
specialties_display = serializers.SerializerMethodField()
class Meta:
model = Provider
fields = [
'id', 'user', 'user_name', 'specialties', 'specialties_display',
'is_available', 'max_daily_appointments'
]
read_only_fields = ['id']
def get_specialties_display(self, obj):
"""Return list of specialty names."""
return [clinic.name_en for clinic in obj.specialties.all()]
class RoomSerializer(serializers.ModelSerializer):
"""Serializer for Room model."""
clinic_name = serializers.CharField(source='clinic.name_en', read_only=True)
class Meta:
model = Room
fields = [
'id', 'name', 'room_number', 'clinic', 'clinic_name',
'is_available'
]
read_only_fields = ['id']
class ScheduleSerializer(serializers.ModelSerializer):
"""Serializer for Schedule model."""
provider_name = serializers.CharField(source='provider.user.get_full_name', read_only=True)
day_display = serializers.CharField(source='get_day_of_week_display', read_only=True)
class Meta:
model = Schedule
fields = [
'id', 'provider', 'provider_name', 'day_of_week', 'day_display',
'start_time', 'end_time', 'slot_duration', 'is_active'
]
read_only_fields = ['id']
class AppointmentListSerializer(serializers.ModelSerializer):
"""Lightweight serializer for appointment lists."""
patient_name = serializers.CharField(source='patient.get_full_name', read_only=True)
clinic_name = serializers.CharField(source='clinic.name_en', read_only=True)
provider_name = serializers.CharField(source='provider.user.get_full_name', read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Appointment
fields = [
'id', 'appointment_number', 'patient', 'patient_name',
'clinic', 'clinic_name', 'provider', 'provider_name',
'scheduled_date', 'scheduled_time', 'duration',
'status', 'status_display'
]
read_only_fields = ['id', 'appointment_number']
class AppointmentDetailSerializer(serializers.ModelSerializer):
"""Detailed serializer for appointment detail views."""
patient = PatientListSerializer(read_only=True)
clinic = ClinicSerializer(read_only=True)
provider = ProviderSerializer(read_only=True)
room = RoomSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
service_type_display = serializers.CharField(source='get_service_type_display', read_only=True)
confirmation_method_display = serializers.CharField(source='get_confirmation_method_display', read_only=True)
cancelled_by_name = serializers.CharField(source='cancelled_by.get_full_name', read_only=True)
class Meta:
model = Appointment
fields = [
'id', 'appointment_number', 'patient', 'clinic', 'provider', 'room',
'service_type', 'service_type_display',
'scheduled_date', 'scheduled_time', 'duration',
'status', 'status_display',
'confirmation_sent_at', 'confirmation_method', 'confirmation_method_display',
'arrival_at', 'start_at', 'end_at',
'reschedule_reason', 'reschedule_count',
'cancel_reason', 'cancelled_by', 'cancelled_by_name',
'notes', 'finance_cleared', 'consent_verified',
'created_at', 'updated_at'
]
read_only_fields = [
'id', 'appointment_number', 'created_at', 'updated_at',
'confirmation_sent_at', 'arrival_at', 'start_at', 'end_at'
]
class AppointmentCreateSerializer(serializers.ModelSerializer):
"""Serializer for creating appointments."""
class Meta:
model = Appointment
fields = [
'patient', 'clinic', 'provider', 'room',
'service_type', 'scheduled_date', 'scheduled_time',
'duration', 'notes'
]
def validate(self, data):
"""Validate appointment data."""
# Check provider availability
provider = data.get('provider')
scheduled_date = data.get('scheduled_date')
scheduled_time = data.get('scheduled_time')
if provider and scheduled_date and scheduled_time:
# Check if provider has schedule for this day
day_of_week = scheduled_date.weekday()
schedule_exists = Schedule.objects.filter(
provider=provider,
day_of_week=day_of_week,
is_active=True
).exists()
if not schedule_exists:
raise serializers.ValidationError(
f"Provider is not available on {scheduled_date.strftime('%A')}"
)
# Check for conflicting appointments
conflicts = Appointment.objects.filter(
provider=provider,
scheduled_date=scheduled_date,
scheduled_time=scheduled_time,
status__in=['BOOKED', 'CONFIRMED', 'ARRIVED', 'IN_PROGRESS']
).exists()
if conflicts:
raise serializers.ValidationError(
"This time slot is already booked"
)
return data
class AppointmentUpdateSerializer(serializers.ModelSerializer):
"""Serializer for updating appointments."""
class Meta:
model = Appointment
fields = [
'scheduled_date', 'scheduled_time', 'duration',
'room', 'notes'
]
class AppointmentStatusSerializer(serializers.Serializer):
"""Serializer for appointment status transitions."""
status = serializers.ChoiceField(choices=Appointment.Status.choices)
notes = serializers.CharField(required=False, allow_blank=True)
cancel_reason = serializers.CharField(required=False, allow_blank=True)
reschedule_reason = serializers.CharField(required=False, allow_blank=True)
def validate(self, data):
"""Validate status transition."""
new_status = data.get('status')
if new_status == 'CANCELLED' and not data.get('cancel_reason'):
raise serializers.ValidationError(
"Cancel reason is required when cancelling an appointment"
)
if new_status == 'RESCHEDULED' and not data.get('reschedule_reason'):
raise serializers.ValidationError(
"Reschedule reason is required when rescheduling an appointment"
)
return data
class AppointmentReminderSerializer(serializers.ModelSerializer):
"""Serializer for AppointmentReminder model."""
appointment_number = serializers.CharField(source='appointment.appointment_number', read_only=True)
patient_name = serializers.CharField(source='appointment.patient.get_full_name', read_only=True)
reminder_type_display = serializers.CharField(source='get_reminder_type_display', read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = AppointmentReminder
fields = [
'id', 'appointment', 'appointment_number', 'patient_name',
'reminder_type', 'reminder_type_display',
'scheduled_for', 'sent_at',
'status', 'status_display', 'message'
]
read_only_fields = ['id', 'sent_at']
class CalendarSlotSerializer(serializers.Serializer):
"""Serializer for calendar time slots."""
date = serializers.DateField()
time = serializers.TimeField()
provider = ProviderSerializer()
room = RoomSerializer()
is_available = serializers.BooleanField()
appointment = AppointmentListSerializer(required=False, allow_null=True)