543 lines
16 KiB
Python
543 lines
16 KiB
Python
"""
|
|
Speech-Language Pathology (SLP) models for the Tenhal Multidisciplinary Healthcare Platform.
|
|
|
|
This module handles SLP consultations, assessments, interventions, and progress reports
|
|
based on SLP-F-1, F-2, F-3, and F-4 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 SLPConsult(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
SLP consultation form (SLP-F-1) with variants.
|
|
Initial consultation with variant-specific questionnaires.
|
|
"""
|
|
|
|
class ConsultVariant(models.TextChoices):
|
|
ASD = 'ASD', _('Autism Spectrum Disorder')
|
|
LANGUAGE_DELAY = 'LANGUAGE_DELAY', _('Language Delay')
|
|
FLUENCY = 'FLUENCY', _('Fluency Disorder')
|
|
|
|
class ServiceType(models.TextChoices):
|
|
CONSULT = 'CONSULT', _('Consultation')
|
|
EVAL = 'EVAL', _('Evaluation')
|
|
INTERVENTION = 'INTERVENTION', _('Intervention')
|
|
PARENT_TRAINING = 'PARENT_TRAINING', _('Parent Training')
|
|
|
|
# Core Relationships
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='slp_consults',
|
|
verbose_name=_("Patient")
|
|
)
|
|
appointment = models.ForeignKey(
|
|
'appointments.Appointment',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='slp_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='slp_consults_provided',
|
|
verbose_name=_("Provider")
|
|
)
|
|
|
|
# Consultation Details
|
|
consult_variant = models.CharField(
|
|
max_length=20,
|
|
choices=ConsultVariant.choices,
|
|
verbose_name=_("Consultation Variant")
|
|
)
|
|
primary_concern = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Primary Concern")
|
|
)
|
|
suspected_areas = models.TextField(
|
|
blank=True,
|
|
help_text=_("List of suspected problem areas"),
|
|
verbose_name=_("Suspected Areas")
|
|
)
|
|
type_of_service = models.CharField(
|
|
max_length=20,
|
|
choices=ServiceType.choices,
|
|
verbose_name=_("Type of Service")
|
|
)
|
|
|
|
# Communication & Environment
|
|
communication_modes = models.TextField(
|
|
blank=True,
|
|
help_text=_("Communication modes checklist (verbal, gestures, AAC, etc.)"),
|
|
verbose_name=_("Communication Modes")
|
|
)
|
|
screen_time_hours = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_("Screen Time (hours/day)")
|
|
)
|
|
|
|
# Variant-Specific Questionnaire
|
|
variant_questionnaire = models.TextField(
|
|
blank=True,
|
|
help_text=_("Variant-specific questions and answers"),
|
|
verbose_name=_("Variant Questionnaire")
|
|
)
|
|
|
|
# Skills to Observe (TextField)
|
|
skills_to_observe = models.TextField(
|
|
blank=True,
|
|
help_text=_("Skills observation matrix"),
|
|
verbose_name=_("Skills to Observe")
|
|
)
|
|
|
|
# Oral Motor Screening
|
|
oral_motor_screening = models.TextField(
|
|
blank=True,
|
|
help_text=_("Oral motor screening results"),
|
|
verbose_name=_("Oral Motor Screening")
|
|
)
|
|
|
|
# Recommendations
|
|
recommendations = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Recommendations")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("SLP Consultation")
|
|
verbose_name_plural = _("SLP Consultations")
|
|
ordering = ['-consultation_date', '-created_at']
|
|
indexes = [
|
|
models.Index(fields=['patient', 'consultation_date']),
|
|
models.Index(fields=['provider', 'consultation_date']),
|
|
models.Index(fields=['consult_variant']),
|
|
models.Index(fields=['tenant', 'consultation_date']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"SLP Consultation ({self.get_consult_variant_display()}) - {self.patient} - {self.consultation_date}"
|
|
|
|
|
|
class SLPAssessment(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
SLP assessment/reassessment form (SLP-F-2).
|
|
Comprehensive evaluation with standardized tests.
|
|
"""
|
|
|
|
# Core Relationships
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='slp_assessments',
|
|
verbose_name=_("Patient")
|
|
)
|
|
appointment = models.ForeignKey(
|
|
'appointments.Appointment',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='slp_assessments',
|
|
verbose_name=_("Appointment")
|
|
)
|
|
assessment_date = models.DateField(
|
|
verbose_name=_("Assessment Date")
|
|
)
|
|
provider = models.ForeignKey(
|
|
'core.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
related_name='slp_assessments_provided',
|
|
verbose_name=_("Provider")
|
|
)
|
|
|
|
# Diagnosis & History
|
|
diagnosis_statement = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Diagnosis Statement")
|
|
)
|
|
case_history = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Case History")
|
|
)
|
|
|
|
# History Sections
|
|
prenatal_history = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Prenatal History")
|
|
)
|
|
perinatal_history = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Perinatal History")
|
|
)
|
|
postnatal_history = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Postnatal History")
|
|
)
|
|
developmental_history = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Developmental History")
|
|
)
|
|
medical_status = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Medical Status")
|
|
)
|
|
|
|
# Speech/Language Detail
|
|
speech_language_detail = models.TextField(
|
|
blank=True,
|
|
help_text=_("Detailed speech and language assessment"),
|
|
verbose_name=_("Speech/Language Detail")
|
|
)
|
|
|
|
# Test Scores
|
|
gfta3_score = models.CharField(
|
|
max_length=100,
|
|
blank=True,
|
|
help_text=_("Goldman-Fristoe Test of Articulation-3 score"),
|
|
verbose_name=_("GFTA-3 Score")
|
|
)
|
|
jat_score = models.CharField(
|
|
max_length=100,
|
|
blank=True,
|
|
help_text=_("Joliet Articulation Test score"),
|
|
verbose_name=_("JAT Score")
|
|
)
|
|
ssi_score = models.CharField(
|
|
max_length=100,
|
|
blank=True,
|
|
help_text=_("Stuttering Severity Instrument score"),
|
|
verbose_name=_("SSI Score")
|
|
)
|
|
|
|
# Oral Mechanism
|
|
oral_mechanism = models.TextField(
|
|
blank=True,
|
|
help_text=_("Oral mechanism examination findings"),
|
|
verbose_name=_("Oral Mechanism")
|
|
)
|
|
|
|
# Rossetti Domains (TextField)
|
|
rossetti_domains = models.TextField(
|
|
blank=True,
|
|
help_text=_("Rossetti Infant-Toddler Language Scale domains and age levels"),
|
|
verbose_name=_("Rossetti Domains")
|
|
)
|
|
|
|
# Joint Attention Skills
|
|
joint_attention_skills = models.TextField(
|
|
blank=True,
|
|
help_text=_("Joint attention skills assessment (present/absent)"),
|
|
verbose_name=_("Joint Attention Skills")
|
|
)
|
|
|
|
# Summary & Plan
|
|
clinical_summary = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Clinical Summary")
|
|
)
|
|
recommendations = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Recommendations")
|
|
)
|
|
frequency_per_week = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text=_("Recommended frequency (sessions per week)"),
|
|
verbose_name=_("Frequency (per week)")
|
|
)
|
|
session_duration_minutes = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_("Session Duration (minutes)")
|
|
)
|
|
|
|
# Referral Rules
|
|
referral_rules = models.TextField(
|
|
blank=True,
|
|
help_text=_("Referral recommendations to other services"),
|
|
verbose_name=_("Referral Rules")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("SLP Assessment")
|
|
verbose_name_plural = _("SLP Assessments")
|
|
ordering = ['-assessment_date', '-created_at']
|
|
indexes = [
|
|
models.Index(fields=['patient', 'assessment_date']),
|
|
models.Index(fields=['provider', 'assessment_date']),
|
|
models.Index(fields=['tenant', 'assessment_date']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"SLP Assessment - {self.patient} - {self.assessment_date}"
|
|
|
|
|
|
class SLPIntervention(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
SLP intervention session form (SLP-F-3).
|
|
Session notes with SOAP format for targets.
|
|
"""
|
|
|
|
# Core Relationships
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='slp_interventions',
|
|
verbose_name=_("Patient")
|
|
)
|
|
appointment = models.ForeignKey(
|
|
'appointments.Appointment',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='slp_interventions',
|
|
verbose_name=_("Appointment")
|
|
)
|
|
session_number = models.PositiveIntegerField(
|
|
verbose_name=_("Session Number")
|
|
)
|
|
session_date = models.DateField(
|
|
verbose_name=_("Session Date")
|
|
)
|
|
session_time = models.TimeField(
|
|
verbose_name=_("Session Time")
|
|
)
|
|
provider = models.ForeignKey(
|
|
'core.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
related_name='slp_interventions_provided',
|
|
verbose_name=_("Provider")
|
|
)
|
|
|
|
# Previous Session Link
|
|
previous_session = models.ForeignKey(
|
|
'self',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='next_sessions',
|
|
verbose_name=_("Previous Session")
|
|
)
|
|
|
|
# Intervention Targets (JSON format - similar to OT target_skills)
|
|
intervention_targets_json = models.TextField(
|
|
blank=True,
|
|
default='[]',
|
|
help_text=_("JSON array of intervention targets with SOAP format"),
|
|
verbose_name=_("Intervention Targets (JSON)")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("SLP Intervention")
|
|
verbose_name_plural = _("SLP Interventions")
|
|
ordering = ['-session_date', '-session_number']
|
|
indexes = [
|
|
models.Index(fields=['patient', 'session_date']),
|
|
models.Index(fields=['provider', 'session_date']),
|
|
models.Index(fields=['session_number']),
|
|
models.Index(fields=['tenant', 'session_date']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"SLP Intervention Session #{self.session_number} - {self.patient} - {self.session_date}"
|
|
|
|
|
|
class SLPTarget(UUIDPrimaryKeyMixin):
|
|
"""
|
|
Intervention targets with SOAP format.
|
|
Typically 1-2 targets per session.
|
|
"""
|
|
|
|
intervention = models.ForeignKey(
|
|
SLPIntervention,
|
|
on_delete=models.CASCADE,
|
|
related_name='targets',
|
|
verbose_name=_("Intervention")
|
|
)
|
|
target_number = models.PositiveSmallIntegerField(
|
|
help_text=_("Target number (1 or 2)"),
|
|
verbose_name=_("Target Number")
|
|
)
|
|
|
|
# SOAP Format
|
|
subjective = models.TextField(
|
|
blank=True,
|
|
help_text=_("Subjective observations"),
|
|
verbose_name=_("Subjective")
|
|
)
|
|
objective = models.TextField(
|
|
blank=True,
|
|
help_text=_("Objective measurements and data"),
|
|
verbose_name=_("Objective")
|
|
)
|
|
assessment = models.TextField(
|
|
blank=True,
|
|
help_text=_("Assessment of progress"),
|
|
verbose_name=_("Assessment")
|
|
)
|
|
plan = models.TextField(
|
|
blank=True,
|
|
help_text=_("Plan for next session"),
|
|
verbose_name=_("Plan")
|
|
)
|
|
|
|
# Prompt Strategies
|
|
prompt_strategies = models.TextField(
|
|
blank=True,
|
|
help_text=_("List of prompting strategies used"),
|
|
verbose_name=_("Prompt Strategies")
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _("SLP Target")
|
|
verbose_name_plural = _("SLP Targets")
|
|
ordering = ['intervention', 'target_number']
|
|
unique_together = [['intervention', 'target_number']]
|
|
|
|
def __str__(self):
|
|
return f"Target #{self.target_number} - Session #{self.intervention.session_number}"
|
|
|
|
|
|
class SLPProgressReport(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
SLP progress report form (SLP-F-4).
|
|
Comprehensive progress summary with outcomes.
|
|
"""
|
|
|
|
# Core Relationships
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='slp_progress_reports',
|
|
verbose_name=_("Patient")
|
|
)
|
|
report_date = models.DateField(
|
|
verbose_name=_("Report Date")
|
|
)
|
|
provider = models.ForeignKey(
|
|
'core.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
related_name='slp_progress_reports_provided',
|
|
verbose_name=_("Provider")
|
|
)
|
|
|
|
# Session Summary
|
|
sessions_scheduled = models.PositiveIntegerField(
|
|
default=0,
|
|
verbose_name=_("Sessions Scheduled")
|
|
)
|
|
sessions_attended = models.PositiveIntegerField(
|
|
default=0,
|
|
verbose_name=_("Sessions Attended")
|
|
)
|
|
|
|
# Diagnosis
|
|
final_diagnosis = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Final Diagnosis")
|
|
)
|
|
|
|
# Objectives & Progress (TextField)
|
|
objectives_progress = models.TextField(
|
|
blank=True,
|
|
help_text=_("Progress on each objective with percentage accuracy"),
|
|
verbose_name=_("Objectives Progress")
|
|
)
|
|
|
|
# Plan Details
|
|
plan_details = models.TextField(
|
|
blank=True,
|
|
help_text=_("Plan details (continue/add/fade prompts/generalization)"),
|
|
verbose_name=_("Plan Details")
|
|
)
|
|
|
|
# Progress Summary
|
|
overall_progress = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Overall Progress")
|
|
)
|
|
participation_level = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Participation Level")
|
|
)
|
|
attendance_rate = models.DecimalField(
|
|
max_digits=5,
|
|
decimal_places=2,
|
|
null=True,
|
|
blank=True,
|
|
help_text=_("Attendance rate percentage"),
|
|
verbose_name=_("Attendance Rate (%)")
|
|
)
|
|
carryover_level = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Carryover Level")
|
|
)
|
|
prognosis = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Prognosis")
|
|
)
|
|
|
|
# Recommendations
|
|
recommendations = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Recommendations")
|
|
)
|
|
package_sessions_count = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text=_("Recommended number of sessions in package"),
|
|
verbose_name=_("Package Sessions Count")
|
|
)
|
|
reassessment_needed = models.BooleanField(
|
|
default=False,
|
|
verbose_name=_("Reassessment Needed")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("SLP Progress Report")
|
|
verbose_name_plural = _("SLP Progress Reports")
|
|
ordering = ['-report_date', '-created_at']
|
|
indexes = [
|
|
models.Index(fields=['patient', 'report_date']),
|
|
models.Index(fields=['provider', 'report_date']),
|
|
models.Index(fields=['tenant', 'report_date']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"SLP Progress Report - {self.patient} - {self.report_date}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
# Auto-calculate attendance rate if not set
|
|
if self.sessions_scheduled > 0 and not self.attendance_rate:
|
|
self.attendance_rate = round(
|
|
(self.sessions_attended / self.sessions_scheduled) * 100, 2
|
|
)
|
|
super().save(*args, **kwargs)
|