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

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