""" ABA (Applied Behavior Analysis) models for the Tenhal Multidisciplinary Healthcare Platform. This module handles ABA consultations and behavior tracking based on ABA-F-1 form. """ 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 ABAConsult(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """ ABA consultation form (ABA-F-1). Functional behavior assessment and intervention planning. """ class ReasonOfReferral(models.TextChoices): DEVELOPMENTAL_DELAY = 'DEVELOPMENTAL_DELAY', _('Developmental Delay') AUTISM_SPECTRUM = 'AUTISM_SPECTRUM', _('Autism Spectrum Disorder') BEHAVIORAL_ISSUES = 'BEHAVIORAL_ISSUES', _('Behavioral Issues') LEARNING_DIFFICULTIES = 'LEARNING_DIFFICULTIES', _('Learning Difficulties') SOCIAL_SKILLS = 'SOCIAL_SKILLS', _('Social Skills Deficits') COMMUNICATION = 'COMMUNICATION', _('Communication Difficulties') SELF_CARE = 'SELF_CARE', _('Self-Care Skills') OTHER = 'OTHER', _('Other') # Core Relationships patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='aba_consults', verbose_name=_("Patient") ) appointment = models.ForeignKey( 'appointments.Appointment', on_delete=models.SET_NULL, null=True, blank=True, related_name='aba_consults', 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='aba_consults_provided', verbose_name=_("Provider") ) # Referral Information reason_of_referral = models.CharField( max_length=50, choices=ReasonOfReferral.choices, verbose_name=_("Reason of Referral") ) parental_concern = models.TextField( blank=True, verbose_name=_("Parental Concern") ) school_concern = models.TextField( blank=True, verbose_name=_("School Concern") ) # Interview Details respondents = models.CharField( max_length=200, blank=True, help_text=_("Who provided the information (e.g., mother, father, teacher)"), verbose_name=_("Respondents") ) interviewer = models.CharField( max_length=200, blank=True, verbose_name=_("Interviewer") ) # Diagnosis & Interaction diagnosed_condition = models.CharField( max_length=200, blank=True, verbose_name=_("Diagnosed Condition") ) interaction_hours_per_day = models.PositiveIntegerField( null=True, blank=True, help_text=_("Hours of interaction per day"), verbose_name=_("Interaction Hours/Day") ) # Physiological Factors (TextField for user-friendly input) physiological_factors = models.TextField( blank=True, help_text=_("Physiological factors affecting behavior (e.g., sleep, appetite, medical conditions)"), verbose_name=_("Physiological Factors") ) # Medical Factors (TextField for user-friendly input) medical_factors = models.TextField( blank=True, help_text=_("Medical factors and conditions"), verbose_name=_("Medical Factors") ) # Recommendations recommendations = models.TextField( blank=True, verbose_name=_("Recommendations") ) history = HistoricalRecords() class Meta: verbose_name = _("ABA Consultation") verbose_name_plural = _("ABA 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"ABA Consultation - {self.patient} - {self.consultation_date}" class ABABehavior(UUIDPrimaryKeyMixin, TimeStampedMixin): """ Specific behavior details for ABA consultation. Tracks frequency, intensity, and functional analysis. """ class Frequency(models.TextChoices): HOURLY = 'HOURLY', _('Hourly') DAILY = 'DAILY', _('Daily') WEEKLY = 'WEEKLY', _('Weekly') LESS_THAN_WEEKLY = 'LESS_THAN_WEEKLY', _('Less than Weekly') class Intensity(models.TextChoices): MILD = 'MILD', _('Mild') MODERATE = 'MODERATE', _('Moderate') SEVERE = 'SEVERE', _('Severe') consult = models.ForeignKey( ABAConsult, on_delete=models.CASCADE, related_name='behaviors', verbose_name=_("Consultation") ) behavior_description = models.TextField( verbose_name=_("Behavior Description") ) frequency = models.CharField( max_length=20, choices=Frequency.choices, verbose_name=_("Frequency") ) duration = models.CharField( max_length=100, blank=True, help_text=_("How long the behavior typically lasts"), verbose_name=_("Duration") ) intensity = models.CharField( max_length=20, choices=Intensity.choices, verbose_name=_("Intensity") ) # Functional Analysis antecedents_likely = models.TextField( blank=True, help_text=_("Contexts where behavior is most likely to occur"), verbose_name=_("Antecedents (Most Likely)") ) antecedents_least_likely = models.TextField( blank=True, help_text=_("Contexts where behavior is least likely to occur"), verbose_name=_("Antecedents (Least Likely)") ) consequences = models.TextField( blank=True, help_text=_("What happens after the behavior occurs"), verbose_name=_("Consequences") ) # Ordering order = models.PositiveIntegerField( default=0, help_text=_("Order of behaviors in the list"), verbose_name=_("Order") ) class Meta: verbose_name = _("ABA Behavior") verbose_name_plural = _("ABA Behaviors") ordering = ['consult', 'order', 'created_at'] def __str__(self): return f"{self.behavior_description[:50]} - {self.get_frequency_display()} - {self.get_intensity_display()}" class ABAGoal(UUIDPrimaryKeyMixin, TimeStampedMixin): """ Treatment goals for ABA intervention. Tracks progress towards behavioral objectives. """ class Status(models.TextChoices): NOT_STARTED = 'NOT_STARTED', _('Not Started') IN_PROGRESS = 'IN_PROGRESS', _('In Progress') ACHIEVED = 'ACHIEVED', _('Achieved') DISCONTINUED = 'DISCONTINUED', _('Discontinued') consult = models.ForeignKey( ABAConsult, on_delete=models.CASCADE, related_name='goals', verbose_name=_("Consultation") ) goal_description = models.TextField( verbose_name=_("Goal Description") ) target_date = models.DateField( null=True, blank=True, verbose_name=_("Target Date") ) status = models.CharField( max_length=20, choices=Status.choices, default=Status.NOT_STARTED, verbose_name=_("Status") ) progress_notes = models.TextField( blank=True, verbose_name=_("Progress Notes") ) achieved_date = models.DateField( null=True, blank=True, verbose_name=_("Achieved Date") ) class Meta: verbose_name = _("ABA Goal") verbose_name_plural = _("ABA Goals") ordering = ['consult', 'target_date'] def __str__(self): return f"{self.goal_description[:50]} - {self.get_status_display()}" class ABASession(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """ ABA session notes and progress tracking. Documents individual therapy sessions and behavioral interventions. """ class SessionType(models.TextChoices): INDIVIDUAL = 'INDIVIDUAL', _('Individual Session') GROUP = 'GROUP', _('Group Session') PARENT_TRAINING = 'PARENT_TRAINING', _('Parent Training') OBSERVATION = 'OBSERVATION', _('Observation') # Core Relationships patient = models.ForeignKey( 'core.Patient', on_delete=models.CASCADE, related_name='aba_sessions', verbose_name=_("Patient") ) appointment = models.ForeignKey( 'appointments.Appointment', on_delete=models.SET_NULL, null=True, blank=True, related_name='aba_sessions', verbose_name=_("Appointment") ) session_date = models.DateField( verbose_name=_("Session Date") ) provider = models.ForeignKey( 'core.User', on_delete=models.SET_NULL, null=True, related_name='aba_sessions_provided', verbose_name=_("Provider") ) # Session Details session_type = models.CharField( max_length=20, choices=SessionType.choices, verbose_name=_("Session Type") ) duration_minutes = models.PositiveIntegerField( null=True, blank=True, help_text=_("Session duration in minutes"), verbose_name=_("Duration (minutes)") ) # Behavioral Observations engagement_level = models.PositiveSmallIntegerField( null=True, blank=True, help_text=_("Engagement level (1-5 scale)"), verbose_name=_("Engagement Level (1-5)") ) cooperation_level = models.PositiveSmallIntegerField( null=True, blank=True, help_text=_("Cooperation level (1-5 scale)"), verbose_name=_("Cooperation Level (1-5)") ) # Session Content target_behaviors = models.TextField( blank=True, help_text=_("Target behaviors addressed in this session"), verbose_name=_("Target Behaviors") ) interventions_used = models.TextField( blank=True, help_text=_("Interventions and strategies used"), verbose_name=_("Interventions Used") ) activities_performed = models.TextField( blank=True, verbose_name=_("Activities Performed") ) # Progress Notes behavioral_observations = models.TextField( blank=True, verbose_name=_("Behavioral Observations") ) progress_notes = models.TextField( blank=True, verbose_name=_("Progress Notes") ) # Recommendations recommendations = models.TextField( blank=True, verbose_name=_("Recommendations") ) home_program = models.TextField( blank=True, help_text=_("Home program recommendations for parents"), verbose_name=_("Home Program") ) history = HistoricalRecords() class Meta: verbose_name = _("ABA Session") verbose_name_plural = _("ABA Sessions") ordering = ['-session_date', '-created_at'] indexes = [ models.Index(fields=['patient', 'session_date']), models.Index(fields=['provider', 'session_date']), models.Index(fields=['tenant', 'session_date']), ] def __str__(self): return f"ABA Session - {self.patient} - {self.session_date}" @property def engagement_level_display(self): """Return engagement level with description.""" levels = { 1: _("Very Low"), 2: _("Low"), 3: _("Moderate"), 4: _("Good"), 5: _("Excellent") } return levels.get(self.engagement_level, "") @property def cooperation_level_display(self): """Return cooperation level with description.""" levels = { 1: _("Very Low"), 2: _("Low"), 3: _("Moderate"), 4: _("Good"), 5: _("Excellent") } return levels.get(self.cooperation_level, "") class ABASkillTarget(UUIDPrimaryKeyMixin): """ Skill targets with progress tracking for ABA sessions. Tracks mastery of specific behavioral and developmental skills. """ class MasteryLevel(models.TextChoices): NOT_STARTED = 'NOT_STARTED', _('Not Started') EMERGING = 'EMERGING', _('Emerging (0-25%)') DEVELOPING = 'DEVELOPING', _('Developing (26-50%)') PROGRESSING = 'PROGRESSING', _('Progressing (51-75%)') MASTERED = 'MASTERED', _('Mastered (76-100%)') session = models.ForeignKey( ABASession, on_delete=models.CASCADE, related_name='skill_targets', verbose_name=_("Session") ) skill_name = models.CharField( max_length=200, verbose_name=_("Skill Name") ) mastery_level = models.CharField( max_length=20, choices=MasteryLevel.choices, default=MasteryLevel.NOT_STARTED, verbose_name=_("Mastery Level") ) trials_correct = models.PositiveIntegerField( null=True, blank=True, help_text=_("Number of correct trials"), verbose_name=_("Trials Correct") ) trials_total = models.PositiveIntegerField( null=True, blank=True, help_text=_("Total number of trials"), verbose_name=_("Trials Total") ) notes = models.TextField( blank=True, verbose_name=_("Notes") ) order = models.PositiveIntegerField( default=0, help_text=_("Order of skills in the list"), verbose_name=_("Order") ) class Meta: verbose_name = _("ABA Skill Target") verbose_name_plural = _("ABA Skill Targets") ordering = ['session', 'order'] def __str__(self): return f"{self.skill_name} - {self.get_mastery_level_display()}" @property def success_rate(self): """Calculate success rate percentage.""" if self.trials_total and self.trials_total > 0: return round((self.trials_correct / self.trials_total) * 100, 2) return 0