377 lines
11 KiB
Python
377 lines
11 KiB
Python
"""
|
|
Occupational Therapy (OT) models for the Tenhal Multidisciplinary Healthcare Platform.
|
|
|
|
This module handles OT consultations and session notes based on OT-F-1 and OT-F-3 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 OTConsult(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
OT consultation form (OT-F-1).
|
|
Initial assessment and evaluation.
|
|
"""
|
|
|
|
class Recommendation(models.TextChoices):
|
|
CONTINUE = 'CONTINUE', _('Continue Treatment')
|
|
DISCHARGE = 'DISCHARGE', _('Discharge')
|
|
REFER_TO_OTHER = 'REFER_TO_OTHER', _('Refer to Other Service')
|
|
|
|
# Core Relationships
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='ot_consults',
|
|
verbose_name=_("Patient")
|
|
)
|
|
appointment = models.ForeignKey(
|
|
'appointments.Appointment',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='ot_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='ot_consults_provided',
|
|
verbose_name=_("Provider")
|
|
)
|
|
|
|
# Reasons for Referral (TextField for user-friendly input)
|
|
reasons = models.TextField(
|
|
blank=True,
|
|
help_text=_("List of reasons for referral (e.g., motor skills, sensory processing, self-care)"),
|
|
verbose_name=_("Reasons for Referral")
|
|
)
|
|
|
|
# Top 3 Difficulty Areas
|
|
top_difficulty_areas = models.TextField(
|
|
blank=True,
|
|
help_text=_("Three main areas of difficulty"),
|
|
verbose_name=_("Top Difficulty Areas")
|
|
)
|
|
|
|
# Developmental Motor Milestones (TextField for user-friendly input)
|
|
developmental_motor_milestones = models.TextField(
|
|
blank=True,
|
|
help_text=_("Motor milestone achievements"),
|
|
verbose_name=_("Developmental Motor Milestones")
|
|
)
|
|
|
|
# Self-Help Skills (TextField for user-friendly input)
|
|
self_help_skills = models.TextField(
|
|
blank=True,
|
|
help_text=_("Self-care and daily living skills assessment"),
|
|
verbose_name=_("Self-Help Skills")
|
|
)
|
|
|
|
# Feeding Participation
|
|
feeding_participation = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Feeding Participation")
|
|
)
|
|
|
|
# Behavior Descriptors
|
|
infant_behavior_descriptors = models.TextField(
|
|
blank=True,
|
|
help_text=_("Behavior descriptors during infancy"),
|
|
verbose_name=_("Infant Behavior Descriptors")
|
|
)
|
|
current_behavior_descriptors = models.TextField(
|
|
blank=True,
|
|
help_text=_("Current behavior descriptors"),
|
|
verbose_name=_("Current Behavior Descriptors")
|
|
)
|
|
|
|
# Recommendation
|
|
recommendation = models.CharField(
|
|
max_length=20,
|
|
choices=Recommendation.choices,
|
|
blank=True,
|
|
verbose_name=_("Recommendation")
|
|
)
|
|
recommendation_notes = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Recommendation Notes")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("OT Consultation")
|
|
verbose_name_plural = _("OT 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"OT Consultation - {self.patient} - {self.consultation_date}"
|
|
|
|
|
|
class OTSession(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
OT session notes form (OT-F-3).
|
|
Progress tracking and session documentation.
|
|
"""
|
|
|
|
class SessionType(models.TextChoices):
|
|
CONSULT = 'CONSULT', _('Consultation')
|
|
INDIVIDUAL = 'INDIVIDUAL', _('Individual Session')
|
|
GROUP = 'GROUP', _('Group Session')
|
|
PARENT_TRAINING = 'PARENT_TRAINING', _('Parent Training')
|
|
|
|
# Core Relationships
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='ot_sessions',
|
|
verbose_name=_("Patient")
|
|
)
|
|
appointment = models.ForeignKey(
|
|
'appointments.Appointment',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='ot_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='ot_sessions_provided',
|
|
verbose_name=_("Provider")
|
|
)
|
|
|
|
# Session Details
|
|
session_type = models.CharField(
|
|
max_length=20,
|
|
choices=SessionType.choices,
|
|
verbose_name=_("Session Type")
|
|
)
|
|
cooperative_level = models.PositiveSmallIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text=_("Cooperative level (1-4 scale)"),
|
|
verbose_name=_("Cooperative Level (1-4)")
|
|
)
|
|
distraction_tolerance = models.PositiveSmallIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text=_("Distraction tolerance (1-4 scale)"),
|
|
verbose_name=_("Distraction Tolerance (1-4)")
|
|
)
|
|
|
|
# Activities Checklist (TextField: "Today we work on...")
|
|
activities_checklist = models.TextField(
|
|
blank=True,
|
|
help_text=_("List of activities worked on during session"),
|
|
verbose_name=_("Activities Checklist")
|
|
)
|
|
|
|
# Session Notes
|
|
observations = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Observations")
|
|
)
|
|
activities_performed = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Activities Performed")
|
|
)
|
|
recommendations = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Recommendations")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("OT Session")
|
|
verbose_name_plural = _("OT 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"OT Session - {self.patient} - {self.session_date}"
|
|
|
|
@property
|
|
def cooperative_level_display(self):
|
|
"""Return cooperative level with description."""
|
|
levels = {
|
|
1: _("Poor"),
|
|
2: _("Fair"),
|
|
3: _("Good"),
|
|
4: _("Excellent")
|
|
}
|
|
return levels.get(self.cooperative_level, "")
|
|
|
|
@property
|
|
def distraction_tolerance_display(self):
|
|
"""Return distraction tolerance with description."""
|
|
levels = {
|
|
1: _("Low"),
|
|
2: _("Moderate"),
|
|
3: _("Good"),
|
|
4: _("High")
|
|
}
|
|
return levels.get(self.distraction_tolerance, "")
|
|
|
|
|
|
class OTTargetSkill(UUIDPrimaryKeyMixin):
|
|
"""
|
|
Target skills with 0-10 scoring for OT sessions.
|
|
Tracks progress on specific therapeutic goals.
|
|
"""
|
|
|
|
session = models.ForeignKey(
|
|
OTSession,
|
|
on_delete=models.CASCADE,
|
|
related_name='target_skills',
|
|
verbose_name=_("Session")
|
|
)
|
|
skill_name = models.CharField(
|
|
max_length=200,
|
|
verbose_name=_("Skill Name")
|
|
)
|
|
score = models.PositiveSmallIntegerField(
|
|
help_text=_("Score from 0 (not achieved) to 10 (fully achieved)"),
|
|
verbose_name=_("Score (0-10)")
|
|
)
|
|
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 = _("OT Target Skill")
|
|
verbose_name_plural = _("OT Target Skills")
|
|
ordering = ['session', 'order']
|
|
|
|
def __str__(self):
|
|
return f"{self.skill_name} - Score: {self.score}/10"
|
|
|
|
@property
|
|
def score_percentage(self):
|
|
"""Return score as percentage."""
|
|
return (self.score / 10) * 100
|
|
|
|
@property
|
|
def achievement_level(self):
|
|
"""Return achievement level description."""
|
|
if self.score == 0:
|
|
return _("Not Achieved")
|
|
elif self.score <= 3:
|
|
return _("Emerging")
|
|
elif self.score <= 6:
|
|
return _("Developing")
|
|
elif self.score <= 9:
|
|
return _("Proficient")
|
|
else:
|
|
return _("Mastered")
|
|
|
|
|
|
class OTProgressReport(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
|
|
"""
|
|
OT progress report summarizing treatment outcomes.
|
|
"""
|
|
|
|
patient = models.ForeignKey(
|
|
'core.Patient',
|
|
on_delete=models.CASCADE,
|
|
related_name='ot_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='ot_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")
|
|
)
|
|
|
|
# Progress Summary
|
|
goals_progress = models.TextField(
|
|
blank=True,
|
|
help_text=_("Progress on each goal"),
|
|
verbose_name=_("Goals Progress")
|
|
)
|
|
overall_progress = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Overall Progress")
|
|
)
|
|
|
|
# Recommendations
|
|
recommendations = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Recommendations")
|
|
)
|
|
continue_treatment = models.BooleanField(
|
|
default=True,
|
|
verbose_name=_("Continue Treatment")
|
|
)
|
|
|
|
history = HistoricalRecords()
|
|
|
|
class Meta:
|
|
verbose_name = _("OT Progress Report")
|
|
verbose_name_plural = _("OT Progress Reports")
|
|
ordering = ['-report_date', '-created_at']
|
|
indexes = [
|
|
models.Index(fields=['patient', 'report_date']),
|
|
models.Index(fields=['tenant', 'report_date']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"OT Progress Report - {self.patient} - {self.report_date}"
|
|
|
|
@property
|
|
def attendance_rate(self):
|
|
"""Calculate attendance rate percentage."""
|
|
if self.sessions_scheduled > 0:
|
|
return round((self.sessions_attended / self.sessions_scheduled) * 100, 2)
|
|
return 0
|