""" Medical models for the Tenhal Multidisciplinary Healthcare Platform. This module handles medical consultations and follow-ups based on MD-F-1 and MD-F-2 forms. """ from django.db import models from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords from core.models import ( UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin, ) class MedicalConsultation(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """ Medical consultation form (MD-F-1). Comprehensive medical history and assessment. """ # Core Relationships patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='medical_consultations', verbose_name=_("Patient") ) appointment = models.ForeignKey( 'appointments.Appointment', on_delete=models.SET_NULL, null=True, blank=True, related_name='medical_consultations', verbose_name=_("Appointment") ) consultation_date = models.DateField( verbose_name=_("Consultation Date") ) provider = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, related_name='medical_consultations_provided', verbose_name=_("Provider") ) # History Sections chief_complaint = models.TextField( blank=True, verbose_name=_("Chief Complaint") ) present_illness_history = models.TextField( blank=True, verbose_name=_("History of Present Illness") ) past_medical_history = models.TextField( blank=True, verbose_name=_("Past Medical History") ) vaccination_status = models.TextField( blank=True, verbose_name=_("Vaccination Status") ) family_history = models.TextField( blank=True, verbose_name=_("Family History") ) social_history = models.TextField( blank=True, verbose_name=_("Social History") ) pregnancy_history = models.TextField( blank=True, verbose_name=_("Pregnancy History") ) neonatal_history = models.TextField( blank=True, verbose_name=_("Neonatal History") ) # Developmental Milestones developmental_motor_milestones = models.TextField( blank=True, verbose_name=_("Developmental Motor Milestones") ) developmental_language_milestones = models.TextField( blank=True, verbose_name=_("Developmental Language Milestones") ) developmental_social_milestones = models.TextField( blank=True, verbose_name=_("Developmental Social Milestones") ) developmental_cognitive_milestones = models.TextField( blank=True, verbose_name=_("Developmental Cognitive Milestones") ) # Behavioral Symptoms (JSONField - checklist) behavioral_symptoms = models.JSONField( default=dict, blank=True, help_text=_("Behavioral symptoms checklist with yes/no/notes"), verbose_name=_("Behavioral Symptoms") ) # Physical Exam (JSONField - structured) physical_exam = models.JSONField( default=dict, blank=True, help_text=_("Structured physical examination findings"), verbose_name=_("Physical Exam") ) # Assessment & Plan clinical_summary = models.TextField( blank=True, verbose_name=_("Clinical Summary") ) recommendations = models.TextField( blank=True, verbose_name=_("Recommendations") ) referrals_needed = models.TextField( blank=True, verbose_name=_("Referrals Needed") ) # Medications (JSONField - like target skills) medications = models.JSONField( default=list, blank=True, help_text=_("List of current medications with compliance and effectiveness"), verbose_name=_("Medications") ) # Lab & Radiology Orders lab_orders = models.JSONField( default=list, blank=True, help_text=_("List of lab tests ordered"), verbose_name=_("Lab Orders") ) radiology_orders = models.JSONField( default=list, blank=True, help_text=_("List of radiology studies ordered"), verbose_name=_("Radiology Orders") ) history = HistoricalRecords() @property def medications_json(self): """Return medications as JSON string for form field.""" import json return json.dumps(self.medications) if self.medications else '[]' class Meta: verbose_name = _("Medical Consultation") verbose_name_plural = _("Medical Consultations") ordering = ['-consultation_date', '-created_at'] indexes = [ models.Index(fields=['patient', 'consultation_date']), models.Index(fields=['provider', 'consultation_date']), models.Index(fields=['tenant', 'consultation_date']), ] def __str__(self): return f"Medical Consultation - {self.patient} - {self.consultation_date}" class MedicationPlan(UUIDPrimaryKeyMixin): """ Medication details for a medical consultation. Tracks current medications, compliance, and effectiveness. """ class Frequency(models.TextChoices): DAILY = 'DAILY', _('Daily') BID = 'BID', _('BID (Twice daily)') TID = 'TID', _('TID (Three times daily)') QID = 'QID', _('QID (Four times daily)') PRN = 'PRN', _('PRN (As needed)') OTHER = 'OTHER', _('Other') class Compliance(models.TextChoices): GOOD = 'GOOD', _('Good') PARTIAL = 'PARTIAL', _('Partial') BAD = 'BAD', _('Bad') consultation = models.ForeignKey( MedicalConsultation, on_delete=models.CASCADE, related_name='medication_plans_old', verbose_name=_("Consultation") ) drug_name = models.CharField( max_length=200, verbose_name=_("Drug Name") ) dose = models.CharField( max_length=100, verbose_name=_("Dose") ) frequency = models.CharField( max_length=20, choices=Frequency.choices, verbose_name=_("Frequency") ) frequency_other = models.CharField( max_length=100, blank=True, verbose_name=_("Frequency (Other)") ) compliance = models.CharField( max_length=20, choices=Compliance.choices, blank=True, verbose_name=_("Compliance") ) gains = models.TextField( blank=True, verbose_name=_("Gains/Benefits") ) side_effects = models.TextField( blank=True, verbose_name=_("Side Effects") ) target_behavior = models.TextField( blank=True, verbose_name=_("Target Behavior") ) improved = models.BooleanField( default=False, verbose_name=_("Improved") ) class Meta: verbose_name = _("Medication Plan") verbose_name_plural = _("Medication Plans") ordering = ['consultation', 'drug_name'] def __str__(self): return f"{self.drug_name} - {self.dose} {self.get_frequency_display()}" class ConsultationResponse(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """ Response to medical consultation from other disciplines. Allows OT, SLP, ABA, etc. to respond to medical consultations. """ class ResponseType(models.TextChoices): OT = 'OT', _('Occupational Therapy') SLP = 'SLP', _('Speech-Language Pathology') ABA = 'ABA', _('ABA Therapy') NURSING = 'NURSING', _('Nursing') OTHER = 'OTHER', _('Other') consultation = models.ForeignKey( MedicalConsultation, on_delete=models.CASCADE, related_name='responses', verbose_name=_("Consultation") ) response_type = models.CharField( max_length=20, choices=ResponseType.choices, verbose_name=_("Response Type") ) responder = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, related_name='consultation_responses', verbose_name=_("Responder") ) response_date = models.DateField( verbose_name=_("Response Date") ) assessment = models.TextField( verbose_name=_("Assessment") ) recommendations = models.TextField( blank=True, verbose_name=_("Recommendations") ) follow_up_needed = models.BooleanField( default=False, verbose_name=_("Follow-up Needed") ) notes = models.TextField( blank=True, verbose_name=_("Additional Notes") ) history = HistoricalRecords() class Meta: verbose_name = _("Consultation Response") verbose_name_plural = _("Consultation Responses") ordering = ['-response_date', '-created_at'] indexes = [ models.Index(fields=['consultation', 'response_date']), models.Index(fields=['responder', 'response_date']), ] def __str__(self): return f"{self.get_response_type_display()} Response to {self.consultation}" class ConsultationFeedback(UUIDPrimaryKeyMixin, TimeStampedMixin): """ Feedback on medical consultation. Can be from family, caregivers, or interdisciplinary team. """ class FeedbackType(models.TextChoices): FAMILY = 'FAMILY', _('Family/Caregiver') TEAM = 'TEAM', _('Interdisciplinary Team') PEER = 'PEER', _('Peer Review') SUPERVISOR = 'SUPERVISOR', _('Supervisor') class SatisfactionRating(models.IntegerChoices): VERY_DISSATISFIED = 1, _('Very Dissatisfied') DISSATISFIED = 2, _('Dissatisfied') NEUTRAL = 3, _('Neutral') SATISFIED = 4, _('Satisfied') VERY_SATISFIED = 5, _('Very Satisfied') consultation = models.ForeignKey( MedicalConsultation, on_delete=models.CASCADE, related_name='feedback', verbose_name=_("Consultation") ) feedback_type = models.CharField( max_length=20, choices=FeedbackType.choices, verbose_name=_("Feedback Type") ) submitted_by = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='consultation_feedback_submitted', verbose_name=_("Submitted By") ) submitted_by_name = models.CharField( max_length=200, blank=True, verbose_name=_("Submitted By Name"), help_text=_("For family/caregiver feedback") ) feedback_date = models.DateField( verbose_name=_("Feedback Date") ) satisfaction_rating = models.IntegerField( choices=SatisfactionRating.choices, null=True, blank=True, verbose_name=_("Satisfaction Rating") ) communication_rating = models.IntegerField( choices=SatisfactionRating.choices, null=True, blank=True, verbose_name=_("Communication Rating") ) care_quality_rating = models.IntegerField( choices=SatisfactionRating.choices, null=True, blank=True, verbose_name=_("Care Quality Rating") ) comments = models.TextField( verbose_name=_("Comments") ) concerns = models.TextField( blank=True, verbose_name=_("Concerns/Issues") ) suggestions = models.TextField( blank=True, verbose_name=_("Suggestions for Improvement") ) history = HistoricalRecords() class Meta: verbose_name = _("Consultation Feedback") verbose_name_plural = _("Consultation Feedback") ordering = ['-feedback_date', '-created_at'] indexes = [ models.Index(fields=['consultation', 'feedback_date']), models.Index(fields=['feedback_type']), ] def __str__(self): return f"{self.get_feedback_type_display()} Feedback for {self.consultation}" @property def average_rating(self): """Calculate average rating across all rating fields.""" ratings = [ r for r in [ self.satisfaction_rating, self.communication_rating, self.care_quality_rating ] if r is not None ] return sum(ratings) / len(ratings) if ratings else None class MedicalFollowUp(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """ Medical follow-up form (MD-F-2). Tracks progress since previous consultation. """ class FamilySatisfaction(models.TextChoices): ZERO = '0', _('0%') FIFTY = '50', _('50%') HUNDRED = '100', _('100%') # Core Relationships patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='medical_followups', verbose_name=_("Patient") ) appointment = models.ForeignKey( 'appointments.Appointment', on_delete=models.SET_NULL, null=True, blank=True, related_name='medical_followups', verbose_name=_("Appointment") ) previous_consultation = models.ForeignKey( MedicalConsultation, on_delete=models.SET_NULL, null=True, blank=True, related_name='followups', verbose_name=_("Previous Consultation") ) followup_date = models.DateField( verbose_name=_("Follow-up Date") ) provider = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, related_name='medical_followups_provided', verbose_name=_("Provider") ) # Previous Complaints Status previous_complaints_status = models.JSONField( default=dict, blank=True, help_text=_("Status of previous complaints: complaint → RESOLVED/STATIC/WORSE"), verbose_name=_("Previous Complaints Status") ) new_complaints = models.TextField( blank=True, verbose_name=_("New Complaints") ) # Link to Nursing Vitals nursing_vitals = models.ForeignKey( 'nursing.NursingEncounter', on_delete=models.SET_NULL, null=True, blank=True, related_name='medical_followups', verbose_name=_("Nursing Vitals") ) # Assessment assessment = models.TextField( blank=True, verbose_name=_("Assessment") ) recommendations = models.TextField( blank=True, verbose_name=_("Recommendations") ) family_satisfaction = models.CharField( max_length=10, choices=FamilySatisfaction.choices, blank=True, verbose_name=_("Family Satisfaction") ) # Medication Snapshot medication_snapshot = models.JSONField( default=list, blank=True, help_text=_("Snapshot of current medications at time of follow-up"), verbose_name=_("Medication Snapshot") ) history = HistoricalRecords() class Meta: verbose_name = _("Medical Follow-up") verbose_name_plural = _("Medical Follow-ups") ordering = ['-followup_date', '-created_at'] indexes = [ models.Index(fields=['patient', 'followup_date']), models.Index(fields=['previous_consultation']), models.Index(fields=['provider', 'followup_date']), models.Index(fields=['tenant', 'followup_date']), ] def __str__(self): return f"Medical Follow-up - {self.patient} - {self.followup_date}" def save(self, *args, **kwargs): # Auto-populate medication snapshot from previous consultation if not set if self.previous_consultation and not self.medication_snapshot: self.medication_snapshot = [ { 'drug_name': med.drug_name, 'dose': med.dose, 'frequency': med.get_frequency_display(), 'compliance': med.get_compliance_display() if med.compliance else '', 'improved': med.improved, } for med in self.previous_consultation.medication_plans_old.all() ] super().save(*args, **kwargs)