""" Appointments app models for hospital management system. Provides appointment scheduling, queue management, and telemedicine functionality. """ import uuid from django.db import models from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator from django.utils import timezone from django.conf import settings from datetime import timedelta, datetime, time import json class AppointmentRequest(models.Model): """ Appointment request model for scheduling patient appointments. """ APPOINTMENT_TYPE_CHOICES = [ ('CONSULTATION', 'Consultation'), ('FOLLOW_UP', 'Follow-up'), ('PROCEDURE', 'Procedure'), ('SURGERY', 'Surgery'), ('DIAGNOSTIC', 'Diagnostic'), ('THERAPY', 'Therapy'), ('VACCINATION', 'Vaccination'), ('SCREENING', 'Screening'), ('EMERGENCY', 'Emergency'), ('TELEMEDICINE', 'Telemedicine'), ('OTHER', 'Other'), ] SPECIALTY_CHOICES = [ ('FAMILY_MEDICINE', 'Family Medicine'), ('INTERNAL_MEDICINE', 'Internal Medicine'), ('PEDIATRICS', 'Pediatrics'), ('CARDIOLOGY', 'Cardiology'), ('DERMATOLOGY', 'Dermatology'), ('ENDOCRINOLOGY', 'Endocrinology'), ('GASTROENTEROLOGY', 'Gastroenterology'), ('NEUROLOGY', 'Neurology'), ('ONCOLOGY', 'Oncology'), ('ORTHOPEDICS', 'Orthopedics'), ('PSYCHIATRY', 'Psychiatry'), ('RADIOLOGY', 'Radiology'), ('SURGERY', 'Surgery'), ('UROLOGY', 'Urology'), ('GYNECOLOGY', 'Gynecology'), ('OPHTHALMOLOGY', 'Ophthalmology'), ('ENT', 'Ear, Nose & Throat'), ('EMERGENCY', 'Emergency Medicine'), ('OTHER', 'Other'), ] PRIORITY_CHOICES=[ ('ROUTINE', 'Routine'), ('URGENT', 'Urgent'), ('STAT', 'STAT'), ('EMERGENCY', 'Emergency'), ] STATUS_CHOICES=[ ('PENDING', 'Pending'), ('SCHEDULED', 'Scheduled'), ('CONFIRMED', 'Confirmed'), ('CHECKED_IN', 'Checked In'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('CANCELLED', 'Cancelled'), ('NO_SHOW', 'No Show'), ('RESCHEDULED', 'Rescheduled'), ] TELEMEDICINE_PLATFORM_CHOICES=[ ('ZOOM', 'Zoom'), ('TEAMS', 'Microsoft Teams'), ('WEBEX', 'Cisco Webex'), ('DOXY', 'Doxy.me'), ('CUSTOM', 'Custom Platform'), ('OTHER', 'Other'), ] # Basic Identifiers request_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique appointment request identifier' ) # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='appointment_requests', help_text='Organization tenant' ) # Patient Information patient = models.ForeignKey( 'patients.PatientProfile', on_delete=models.CASCADE, related_name='appointment_requests', help_text='Patient requesting appointment' ) # Provider Information provider = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='provider_appointments', help_text='Healthcare provider' ) # Appointment Details appointment_type = models.CharField( max_length=50, choices=APPOINTMENT_TYPE_CHOICES, help_text='Type of appointment' ) specialty = models.CharField( max_length=100, choices=SPECIALTY_CHOICES, help_text='Medical specialty' ) # Scheduling Information preferred_date = models.DateField( help_text='Patient preferred appointment date' ) preferred_time = models.TimeField( blank=True, null=True, help_text='Patient preferred appointment time' ) duration_minutes = models.PositiveIntegerField( default=30, validators=[MinValueValidator(15), MaxValueValidator(480)], help_text='Appointment duration in minutes' ) # Scheduling Flexibility flexible_scheduling = models.BooleanField( default=True, help_text='Patient accepts alternative times' ) earliest_acceptable_date = models.DateField( blank=True, null=True, help_text='Earliest acceptable appointment date' ) latest_acceptable_date = models.DateField( blank=True, null=True, help_text='Latest acceptable appointment date' ) acceptable_times = models.JSONField( default=list, blank=True, help_text='Acceptable time slots (JSON array)' ) # Priority and Urgency priority = models.CharField( max_length=20, choices=PRIORITY_CHOICES, default='ROUTINE', help_text='Appointment priority' ) urgency_score = models.PositiveIntegerField( default=1, validators=[MinValueValidator(1), MaxValueValidator(10)], help_text='Urgency score (1-10, 10 being most urgent)' ) # Clinical Information chief_complaint = models.TextField( help_text='Patient chief complaint or reason for visit' ) clinical_notes = models.TextField( blank=True, null=True, help_text='Additional clinical notes' ) referring_provider = models.CharField( max_length=200, blank=True, null=True, help_text='Referring provider name' ) # Insurance and Authorization insurance_verified = models.BooleanField( default=False, help_text='Insurance coverage verified' ) authorization_required = models.BooleanField( default=False, help_text='Prior authorization required' ) authorization_number = models.CharField( max_length=100, blank=True, null=True, help_text='Authorization number' ) # Status and Workflow status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='PENDING', help_text='Appointment status' ) # Scheduled Information scheduled_datetime = models.DateTimeField( blank=True, null=True, help_text='Scheduled appointment date and time' ) scheduled_end_datetime = models.DateTimeField( blank=True, null=True, help_text='Scheduled appointment end time' ) # Location Information location = models.CharField( max_length=200, blank=True, null=True, help_text='Appointment location' ) room_number = models.CharField( max_length=50, blank=True, null=True, help_text='Room number' ) # Telemedicine Information is_telemedicine = models.BooleanField( default=False, help_text='Telemedicine appointment' ) telemedicine_platform = models.CharField( max_length=50, choices=TELEMEDICINE_PLATFORM_CHOICES, blank=True, null=True, help_text='Telemedicine platform' ) meeting_url = models.URLField( blank=True, null=True, help_text='Telemedicine meeting URL' ) meeting_id = models.CharField( max_length=100, blank=True, null=True, help_text='Meeting ID or room number' ) meeting_password = models.CharField( max_length=100, blank=True, null=True, help_text='Meeting password' ) # Check-in Information checked_in_at = models.DateTimeField( blank=True, null=True, help_text='Patient check-in time' ) checked_in_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='checked_in_appointments', help_text='Staff member who checked in patient' ) # Completion Information completed_at = models.DateTimeField( blank=True, null=True, help_text='Appointment completion time' ) actual_duration_minutes = models.PositiveIntegerField( blank=True, null=True, help_text='Actual appointment duration' ) # Cancellation Information cancelled_at = models.DateTimeField( blank=True, null=True, help_text='Cancellation timestamp' ) cancelled_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='cancelled_appointments', help_text='User who cancelled appointment' ) cancellation_reason = models.TextField( blank=True, null=True, help_text='Reason for cancellation' ) # Rescheduling Information rescheduled_from = models.ForeignKey( 'self', on_delete=models.SET_NULL, null=True, blank=True, related_name='rescheduled_appointments', help_text='Original appointment if rescheduled' ) # Communication Preferences reminder_preferences = models.JSONField( default=dict, blank=True, help_text='Reminder preferences (email, SMS, phone)' ) # Special Requirements special_requirements = models.TextField( blank=True, null=True, help_text='Special requirements or accommodations' ) interpreter_needed = models.BooleanField( default=False, help_text='Interpreter services needed' ) interpreter_language = models.CharField( max_length=50, blank=True, null=True, help_text='Required interpreter language' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_appointments', help_text='User who created the appointment request' ) class Meta: db_table = 'appointments_appointment_request' verbose_name = 'Appointment Request' verbose_name_plural = 'Appointment Requests' ordering = ['-created_at'] indexes = [ models.Index(fields=['tenant', 'status']), models.Index(fields=['patient', 'status']), models.Index(fields=['provider', 'scheduled_datetime']), models.Index(fields=['scheduled_datetime']), models.Index(fields=['priority', 'urgency_score']), models.Index(fields=['appointment_type', 'specialty']), ] def __str__(self): return f"{self.patient.get_full_name()} - {self.appointment_type} ({self.status})" @property def is_overdue(self): """ Check if appointment is overdue. """ if self.scheduled_datetime and self.status in ['SCHEDULED', 'CONFIRMED']: return timezone.now() > self.scheduled_datetime return False @property def wait_time_minutes(self): """ Calculate wait time if checked in. """ if self.checked_in_at and self.status == 'CHECKED_IN': return int((timezone.now() - self.checked_in_at).total_seconds() / 60) return None class SlotAvailability(models.Model): """ Provider availability slots for appointment scheduling. """ AVAILABILITY_TYPE_CHOICES=[ ('REGULAR', 'Regular Hours'), ('EXTENDED', 'Extended Hours'), ('EMERGENCY', 'Emergency'), ('ON_CALL', 'On Call'), ('TELEMEDICINE', 'Telemedicine Only'), ] # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='availability_slots', help_text='Organization tenant' ) # Provider Information provider = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='availability_slots', help_text='Healthcare provider' ) # Slot Information slot_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique slot identifier' ) # Date and Time date = models.DateField( help_text='Availability date' ) start_time = models.TimeField( help_text='Slot start time' ) end_time = models.TimeField( help_text='Slot end time' ) duration_minutes = models.PositiveIntegerField( help_text='Slot duration in minutes' ) # Availability Type availability_type = models.CharField( max_length=20, choices=AVAILABILITY_TYPE_CHOICES, default='REGULAR', help_text='Type of availability' ) # Capacity and Booking max_appointments = models.PositiveIntegerField( default=1, help_text='Maximum appointments for this slot' ) booked_appointments = models.PositiveIntegerField( default=0, help_text='Number of booked appointments' ) # Location Information location = models.CharField( max_length=200, help_text='Appointment location' ) room_number = models.CharField( max_length=50, blank=True, null=True, help_text='Room number' ) # Specialty and Services specialty = models.CharField( max_length=100, help_text='Medical specialty for this slot' ) appointment_types = models.JSONField( default=list, help_text='Allowed appointment types for this slot' ) # Restrictions patient_restrictions = models.JSONField( default=dict, blank=True, help_text='Patient restrictions (age, gender, etc.)' ) insurance_restrictions = models.JSONField( default=list, blank=True, help_text='Accepted insurance types' ) # Telemedicine Support supports_telemedicine = models.BooleanField( default=False, help_text='Slot supports telemedicine appointments' ) telemedicine_only = models.BooleanField( default=False, help_text='Telemedicine only slot' ) # Status is_active = models.BooleanField( default=True, help_text='Slot is active and bookable' ) is_blocked = models.BooleanField( default=False, help_text='Slot is temporarily blocked' ) block_reason = models.CharField( max_length=200, blank=True, null=True, help_text='Reason for blocking slot' ) # Recurring Pattern is_recurring = models.BooleanField( default=False, help_text='Slot is part of recurring pattern' ) recurrence_pattern = models.JSONField( default=dict, blank=True, help_text='Recurrence pattern configuration' ) recurrence_end_date = models.DateField( blank=True, null=True, help_text='End date for recurring pattern' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_availability_slots', help_text='User who created the slot' ) class Meta: db_table = 'appointments_slot_availability' verbose_name = 'Slot Availability' verbose_name_plural = 'Slot Availability' ordering = ['date', 'start_time'] indexes = [ models.Index(fields=['tenant', 'provider', 'date']), models.Index(fields=['date', 'start_time']), models.Index(fields=['specialty']), models.Index(fields=['is_active', 'is_blocked']), ] unique_together = ['provider', 'date', 'start_time'] def __str__(self): return f"{self.provider.get_full_name()} - {self.date} {self.start_time}-{self.end_time}" @property def is_available(self): """ Check if slot has availability. """ return ( self.is_active and not self.is_blocked and self.booked_appointments < self.max_appointments ) @property def available_capacity(self): """ Get available capacity for slot. """ return max(0, self.max_appointments - self.booked_appointments) class WaitingQueue(models.Model): """ Waiting queue for managing patient flow. """ QUEUE_TYPE_CHOICES=[ ('PROVIDER', 'Provider Queue'), ('SPECIALTY', 'Specialty Queue'), ('LOCATION', 'Location Queue'), ('PROCEDURE', 'Procedure Queue'), ('EMERGENCY', 'Emergency Queue'), ] # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='waiting_queues', help_text='Organization tenant' ) # Queue Information queue_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique queue identifier' ) name = models.CharField( max_length=200, help_text='Queue name' ) description = models.TextField( blank=True, null=True, help_text='Queue description' ) # Queue Type and Configuration queue_type = models.CharField( max_length=20, choices=QUEUE_TYPE_CHOICES, help_text='Type of queue' ) # Associated Resources providers = models.ManyToManyField( settings.AUTH_USER_MODEL, related_name='waiting_queues', blank=True, help_text='Providers associated with this queue' ) specialty = models.CharField( max_length=100, blank=True, null=True, help_text='Medical specialty' ) location = models.CharField( max_length=200, blank=True, null=True, help_text='Queue location' ) # Queue Management max_queue_size = models.PositiveIntegerField( default=50, help_text='Maximum queue size' ) average_service_time_minutes = models.PositiveIntegerField( default=30, help_text='Average service time in minutes' ) # Priority Configuration priority_weights = models.JSONField( default=dict, help_text='Priority weights for queue ordering' ) # Status is_active = models.BooleanField( default=True, help_text='Queue is active' ) is_accepting_patients = models.BooleanField( default=True, help_text='Queue is accepting new patients' ) # Operating Hours operating_hours = models.JSONField( default=dict, help_text='Queue operating hours by day' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_waiting_queues', help_text='User who created the queue' ) class Meta: db_table = 'appointments_waiting_queue' verbose_name = 'Waiting Queue' verbose_name_plural = 'Waiting Queues' ordering = ['name'] indexes = [ models.Index(fields=['tenant', 'queue_type']), models.Index(fields=['specialty']), models.Index(fields=['is_active']), ] def __str__(self): return f"{self.name} ({self.queue_type})" @property def current_queue_size(self): """ Get current queue size. """ return self.queue_entries.filter(status='WAITING').count() @property def estimated_wait_time_minutes(self): """ Calculate estimated wait time. """ queue_size = self.current_queue_size return queue_size * self.average_service_time_minutes class QueueEntry(models.Model): """ Individual entry in a waiting queue. """ STATUS_CHOICES=[ ('WAITING', 'Waiting'), ('CALLED', 'Called'), ('IN_SERVICE', 'In Service'), ('COMPLETED', 'Completed'), ('LEFT', 'Left Queue'), ('NO_SHOW', 'No Show'), ] # Queue and Patient queue = models.ForeignKey( WaitingQueue, on_delete=models.CASCADE, related_name='queue_entries', help_text='Waiting queue' ) patient = models.ForeignKey( 'patients.PatientProfile', on_delete=models.CASCADE, related_name='queue_entries', help_text='Patient in queue' ) appointment = models.ForeignKey( AppointmentRequest, on_delete=models.CASCADE, related_name='queue_entries', help_text='Associated appointment' ) # Entry Information entry_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique entry identifier' ) # Queue Position and Priority queue_position = models.PositiveIntegerField( help_text='Position in queue' ) priority_score = models.FloatField( default=1.0, help_text='Priority score for queue ordering' ) # Timing Information joined_at = models.DateTimeField( auto_now_add=True, help_text='Time patient joined queue' ) estimated_service_time = models.DateTimeField( blank=True, null=True, help_text='Estimated service time' ) called_at = models.DateTimeField( blank=True, null=True, help_text='Time patient was called' ) served_at = models.DateTimeField( blank=True, null=True, help_text='Time patient was served' ) # Status status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='WAITING', help_text='Queue entry status' ) # Provider Assignment assigned_provider = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_queue_entries', help_text='Assigned provider' ) # Communication notification_sent = models.BooleanField( default=False, help_text='Notification sent to patient' ) notification_method = models.CharField( max_length=20, choices=[ ('SMS', 'SMS'), ('EMAIL', 'Email'), ('PHONE', 'Phone Call'), ('PAGER', 'Pager'), ('APP', 'Mobile App'), ], blank=True, null=True, help_text='Notification method used' ) # Notes notes = models.TextField( blank=True, null=True, help_text='Additional notes' ) # Metadata updated_at = models.DateTimeField(auto_now=True) updated_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='updated_queue_entries', help_text='User who last updated entry' ) class Meta: db_table = 'appointments_queue_entry' verbose_name = 'Queue Entry' verbose_name_plural = 'Queue Entries' ordering = ['queue', 'priority_score', 'joined_at'] indexes = [ models.Index(fields=['queue', 'status']), models.Index(fields=['patient']), models.Index(fields=['priority_score']), models.Index(fields=['joined_at']), ] def __str__(self): return f"{self.patient.get_full_name()} - {self.queue.name} (#{self.queue_position})" @property def wait_time_minutes(self): """ Calculate current wait time. """ if self.status == 'WAITING': return int((timezone.now() - self.joined_at).total_seconds() / 60) elif self.served_at: return int((self.served_at - self.joined_at).total_seconds() / 60) return None class TelemedicineSession(models.Model): """ Telemedicine session management. """ PLATFORM_CHOICES=[ ('ZOOM', 'Zoom'), ('TEAMS', 'Microsoft Teams'), ('WEBEX', 'Cisco Webex'), ('DOXY', 'Doxy.me'), ('CUSTOM', 'Custom Platform'), ('OTHER', 'Other'), ] STATUS_CHOICES=[ ('SCHEDULED', 'Scheduled'), ('READY', 'Ready to Start'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('CANCELLED', 'Cancelled'), ('FAILED', 'Failed'), ] # Session Information session_id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, help_text='Unique session identifier' ) # Associated Appointment appointment = models.OneToOneField( AppointmentRequest, on_delete=models.CASCADE, related_name='telemedicine_session', help_text='Associated appointment' ) # Platform Information platform = models.CharField( max_length=50, choices=PLATFORM_CHOICES, help_text='Telemedicine platform' ) # Meeting Details meeting_url = models.URLField( help_text='Meeting URL' ) meeting_id = models.CharField( max_length=100, help_text='Meeting ID or room number' ) meeting_password = models.CharField( max_length=100, blank=True, null=True, help_text='Meeting password' ) # Session Configuration waiting_room_enabled = models.BooleanField( default=True, help_text='Waiting room enabled' ) recording_enabled = models.BooleanField( default=False, help_text='Session recording enabled' ) recording_consent = models.BooleanField( default=False, help_text='Patient consent for recording' ) # Security Settings encryption_enabled = models.BooleanField( default=True, help_text='End-to-end encryption enabled' ) password_required = models.BooleanField( default=True, help_text='Password required to join' ) # Session Status status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='SCHEDULED', help_text='Session status' ) # Timing Information scheduled_start = models.DateTimeField( help_text='Scheduled start time' ) scheduled_end = models.DateTimeField( help_text='Scheduled end time' ) actual_start = models.DateTimeField( blank=True, null=True, help_text='Actual start time' ) actual_end = models.DateTimeField( blank=True, null=True, help_text='Actual end time' ) # Participants provider_joined_at = models.DateTimeField( blank=True, null=True, help_text='Provider join time' ) patient_joined_at = models.DateTimeField( blank=True, null=True, help_text='Patient join time' ) # Technical Information connection_quality = models.CharField( max_length=20, choices=[ ('EXCELLENT', 'Excellent'), ('GOOD', 'Good'), ('FAIR', 'Fair'), ('POOR', 'Poor'), ], blank=True, null=True, help_text='Connection quality' ) technical_issues = models.TextField( blank=True, null=True, help_text='Technical issues encountered' ) # Recording Information recording_url = models.URLField( blank=True, null=True, help_text='Recording URL' ) recording_duration_minutes = models.PositiveIntegerField( blank=True, null=True, help_text='Recording duration in minutes' ) # Session Notes session_notes = models.TextField( blank=True, null=True, help_text='Session notes' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_telemedicine_sessions', help_text='User who created the session' ) class Meta: db_table = 'appointments_telemedicine_session' verbose_name = 'Telemedicine Session' verbose_name_plural = 'Telemedicine Sessions' ordering = ['-scheduled_start'] indexes = [ models.Index(fields=['appointment']), models.Index(fields=['status']), models.Index(fields=['scheduled_start']), ] def __str__(self): return f"Telemedicine - {self.appointment.patient.get_full_name()} ({self.status})" @property def duration_minutes(self): """ Calculate session duration. """ if self.actual_start and self.actual_end: return int((self.actual_end - self.actual_start).total_seconds() / 60) return None class AppointmentTemplate(models.Model): """ Templates for common appointment types. """ # Tenant relationship tenant = models.ForeignKey( 'core.Tenant', on_delete=models.CASCADE, related_name='appointment_templates', help_text='Organization tenant' ) # Template Information name = models.CharField( max_length=200, help_text='Template name' ) description = models.TextField( blank=True, null=True, help_text='Template description' ) # Appointment Configuration appointment_type = models.CharField( max_length=50, help_text='Default appointment type' ) specialty = models.CharField( max_length=100, help_text='Medical specialty' ) duration_minutes = models.PositiveIntegerField( help_text='Default duration in minutes' ) # Scheduling Rules advance_booking_days = models.PositiveIntegerField( default=30, help_text='Maximum advance booking days' ) minimum_notice_hours = models.PositiveIntegerField( default=24, help_text='Minimum notice required in hours' ) # Requirements insurance_verification_required = models.BooleanField( default=False, help_text='Insurance verification required' ) authorization_required = models.BooleanField( default=False, help_text='Prior authorization required' ) # Instructions pre_appointment_instructions = models.TextField( blank=True, null=True, help_text='Pre-appointment instructions for patient' ) post_appointment_instructions = models.TextField( blank=True, null=True, help_text='Post-appointment instructions template' ) # Forms and Documents required_forms = models.JSONField( default=list, blank=True, help_text='Required forms for this appointment type' ) # Status is_active = models.BooleanField( default=True, help_text='Template is active' ) # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_appointment_templates', help_text='User who created the template' ) class Meta: db_table = 'appointments_appointment_template' verbose_name = 'Appointment Template' verbose_name_plural = 'Appointment Templates' ordering = ['specialty', 'name'] indexes = [ models.Index(fields=['tenant', 'specialty']), models.Index(fields=['appointment_type']), models.Index(fields=['is_active']), ] def __str__(self): return f"{self.name} ({self.specialty})"