agdar/slp/models.py
2025-11-02 14:35:35 +03:00

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)