agdar/psychology/models.py
Marwan Alwali 2f1681b18c update
2025-11-11 13:44:48 +03:00

732 lines
21 KiB
Python

"""
Psychology models for the Tenhal Multidisciplinary Healthcare Platform.
This module handles psychological consultations, assessments, and interventions.
"""
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 PsychologyConsultation(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
"""
Psychology consultation form.
Initial psychological evaluation and assessment.
"""
class ReferralReason(models.TextChoices):
BEHAVIORAL_ISSUES = 'BEHAVIORAL', _('Behavioral Issues')
EMOTIONAL_DIFFICULTIES = 'EMOTIONAL', _('Emotional Difficulties')
DEVELOPMENTAL_CONCERNS = 'DEVELOPMENTAL', _('Developmental Concerns')
LEARNING_DIFFICULTIES = 'LEARNING', _('Learning Difficulties')
SOCIAL_SKILLS = 'SOCIAL', _('Social Skills Deficits')
ANXIETY = 'ANXIETY', _('Anxiety')
DEPRESSION = 'DEPRESSION', _('Depression')
TRAUMA = 'TRAUMA', _('Trauma/PTSD')
ADHD_ASSESSMENT = 'ADHD', _('ADHD Assessment')
AUTISM_ASSESSMENT = 'AUTISM', _('Autism Spectrum Assessment')
OTHER = 'OTHER', _('Other')
# Core Relationships
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='psychology_consultations',
verbose_name=_("Patient")
)
appointment = models.ForeignKey(
'appointments.Appointment',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='psychology_consultations',
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='psychology_consultations_provided',
verbose_name=_("Provider")
)
# Referral Information
referral_reason = models.CharField(
max_length=50,
choices=ReferralReason.choices,
verbose_name=_("Referral Reason")
)
referral_source = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Referral Source")
)
presenting_problem = models.TextField(
verbose_name=_("Presenting Problem")
)
# Background Information
family_history = models.TextField(
blank=True,
verbose_name=_("Family History")
)
medical_history = models.TextField(
blank=True,
verbose_name=_("Medical History")
)
developmental_history = models.TextField(
blank=True,
verbose_name=_("Developmental History")
)
educational_history = models.TextField(
blank=True,
verbose_name=_("Educational History")
)
social_history = models.TextField(
blank=True,
verbose_name=_("Social History")
)
# Mental Status Examination
appearance = models.TextField(
blank=True,
verbose_name=_("Appearance")
)
behavior = models.TextField(
blank=True,
verbose_name=_("Behavior")
)
mood = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Mood")
)
affect = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Affect")
)
speech = models.TextField(
blank=True,
verbose_name=_("Speech")
)
thought_process = models.TextField(
blank=True,
verbose_name=_("Thought Process")
)
thought_content = models.TextField(
blank=True,
verbose_name=_("Thought Content")
)
perception = models.TextField(
blank=True,
verbose_name=_("Perception")
)
cognition = models.TextField(
blank=True,
verbose_name=_("Cognition")
)
insight = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Insight")
)
judgment = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Judgment")
)
# Risk Assessment
suicide_risk = models.CharField(
max_length=50,
blank=True,
choices=[
('NONE', _('None')),
('LOW', _('Low')),
('MODERATE', _('Moderate')),
('HIGH', _('High')),
],
verbose_name=_("Suicide Risk")
)
homicide_risk = models.CharField(
max_length=50,
blank=True,
choices=[
('NONE', _('None')),
('LOW', _('Low')),
('MODERATE', _('Moderate')),
('HIGH', _('High')),
],
verbose_name=_("Homicide Risk")
)
risk_assessment_notes = models.TextField(
blank=True,
verbose_name=_("Risk Assessment Notes")
)
# Clinical Impressions
clinical_impressions = models.TextField(
blank=True,
verbose_name=_("Clinical Impressions")
)
provisional_diagnosis = models.TextField(
blank=True,
verbose_name=_("Provisional Diagnosis")
)
# Treatment Plan
treatment_goals = models.TextField(
blank=True,
verbose_name=_("Treatment Goals")
)
treatment_approach = models.TextField(
blank=True,
verbose_name=_("Treatment Approach")
)
recommendations = models.TextField(
blank=True,
verbose_name=_("Recommendations")
)
frequency_duration = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Recommended Frequency & Duration")
)
# Referrals
referrals_needed = models.TextField(
blank=True,
verbose_name=_("Referrals Needed")
)
history = HistoricalRecords()
class Meta:
verbose_name = _("Psychology Consultation")
verbose_name_plural = _("Psychology Consultations")
ordering = ['-consultation_date', '-created_at']
indexes = [
models.Index(fields=['patient', 'consultation_date']),
models.Index(fields=['provider', 'consultation_date']),
models.Index(fields=['referral_reason']),
models.Index(fields=['tenant', 'consultation_date']),
]
def __str__(self):
return f"Psychology Consultation - {self.patient} - {self.consultation_date}"
class PsychologyAssessment(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
"""
Psychological assessment with standardized testing.
Comprehensive evaluation using psychological tests and measures.
"""
class AssessmentType(models.TextChoices):
COGNITIVE = 'COGNITIVE', _('Cognitive Assessment')
DEVELOPMENTAL = 'DEVELOPMENTAL', _('Developmental Assessment')
BEHAVIORAL = 'BEHAVIORAL', _('Behavioral Assessment')
EMOTIONAL = 'EMOTIONAL', _('Emotional Assessment')
PERSONALITY = 'PERSONALITY', _('Personality Assessment')
NEUROPSYCHOLOGICAL = 'NEUROPSYCH', _('Neuropsychological Assessment')
COMPREHENSIVE = 'COMPREHENSIVE', _('Comprehensive Assessment')
# Core Relationships
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='psychology_assessments',
verbose_name=_("Patient")
)
appointment = models.ForeignKey(
'appointments.Appointment',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='psychology_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='psychology_assessments_provided',
verbose_name=_("Provider")
)
# Assessment Details
assessment_type = models.CharField(
max_length=50,
choices=AssessmentType.choices,
verbose_name=_("Assessment Type")
)
reason_for_assessment = models.TextField(
verbose_name=_("Reason for Assessment")
)
# Background Information
relevant_history = models.TextField(
blank=True,
verbose_name=_("Relevant History")
)
current_medications = models.TextField(
blank=True,
verbose_name=_("Current Medications")
)
# Tests Administered (JSON for flexibility)
tests_administered = models.JSONField(
default=list,
blank=True,
help_text=_("List of tests with scores and interpretations"),
verbose_name=_("Tests Administered")
)
# Behavioral Observations
behavioral_observations = models.TextField(
blank=True,
verbose_name=_("Behavioral Observations During Testing")
)
test_validity = models.TextField(
blank=True,
verbose_name=_("Test Validity & Reliability")
)
# Results Summary
cognitive_functioning = models.TextField(
blank=True,
verbose_name=_("Cognitive Functioning")
)
emotional_functioning = models.TextField(
blank=True,
verbose_name=_("Emotional Functioning")
)
behavioral_functioning = models.TextField(
blank=True,
verbose_name=_("Behavioral Functioning")
)
social_functioning = models.TextField(
blank=True,
verbose_name=_("Social Functioning")
)
adaptive_functioning = models.TextField(
blank=True,
verbose_name=_("Adaptive Functioning")
)
# Strengths & Weaknesses
strengths = models.TextField(
blank=True,
verbose_name=_("Strengths")
)
weaknesses = models.TextField(
blank=True,
verbose_name=_("Areas of Concern/Weakness")
)
# Diagnosis
diagnostic_impressions = models.TextField(
blank=True,
verbose_name=_("Diagnostic Impressions")
)
dsm5_diagnosis = models.TextField(
blank=True,
verbose_name=_("DSM-5 Diagnosis")
)
# Recommendations
recommendations = models.TextField(
blank=True,
verbose_name=_("Recommendations")
)
treatment_recommendations = models.TextField(
blank=True,
verbose_name=_("Treatment Recommendations")
)
educational_recommendations = models.TextField(
blank=True,
verbose_name=_("Educational Recommendations")
)
follow_up_recommendations = models.TextField(
blank=True,
verbose_name=_("Follow-up Recommendations")
)
history = HistoricalRecords()
class Meta:
verbose_name = _("Psychology Assessment")
verbose_name_plural = _("Psychology Assessments")
ordering = ['-assessment_date', '-created_at']
indexes = [
models.Index(fields=['patient', 'assessment_date']),
models.Index(fields=['provider', 'assessment_date']),
models.Index(fields=['assessment_type']),
models.Index(fields=['tenant', 'assessment_date']),
]
def __str__(self):
return f"Psychology Assessment ({self.get_assessment_type_display()}) - {self.patient} - {self.assessment_date}"
class PsychologySession(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
"""
Psychology therapy session notes.
Individual or group therapy session documentation.
"""
class SessionType(models.TextChoices):
INDIVIDUAL = 'INDIVIDUAL', _('Individual Therapy')
GROUP = 'GROUP', _('Group Therapy')
FAMILY = 'FAMILY', _('Family Therapy')
PARENT_TRAINING = 'PARENT_TRAINING', _('Parent Training')
CONSULTATION = 'CONSULTATION', _('Consultation')
class TherapyModality(models.TextChoices):
CBT = 'CBT', _('Cognitive Behavioral Therapy')
DBT = 'DBT', _('Dialectical Behavior Therapy')
PSYCHODYNAMIC = 'PSYCHODYNAMIC', _('Psychodynamic Therapy')
HUMANISTIC = 'HUMANISTIC', _('Humanistic Therapy')
PLAY_THERAPY = 'PLAY_THERAPY', _('Play Therapy')
ART_THERAPY = 'ART_THERAPY', _('Art Therapy')
MINDFULNESS = 'MINDFULNESS', _('Mindfulness-Based Therapy')
SOLUTION_FOCUSED = 'SOLUTION_FOCUSED', _('Solution-Focused Therapy')
OTHER = 'OTHER', _('Other')
# Core Relationships
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='psychology_sessions',
verbose_name=_("Patient")
)
appointment = models.ForeignKey(
'appointments.Appointment',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='psychology_sessions',
verbose_name=_("Appointment")
)
# NEW: Link to centralized session (for scheduling/capacity management)
session = models.ForeignKey(
'appointments.Session',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='psychology_notes',
verbose_name=_("Session"),
help_text=_("Link to centralized session for scheduling")
)
# NEW: Link to specific participant (for group sessions)
session_participant = models.ForeignKey(
'appointments.SessionParticipant',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='psychology_notes',
verbose_name=_("Session Participant"),
help_text=_("For group sessions: which participant these notes are for")
)
session_number = models.PositiveIntegerField(
verbose_name=_("Session Number")
)
session_date = models.DateField(
verbose_name=_("Session Date")
)
provider = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
related_name='psychology_sessions_provided',
verbose_name=_("Provider")
)
# Session Details
session_type = models.CharField(
max_length=50,
choices=SessionType.choices,
verbose_name=_("Session Type")
)
therapy_modality = models.CharField(
max_length=50,
choices=TherapyModality.choices,
blank=True,
verbose_name=_("Therapy Modality")
)
duration_minutes = models.PositiveIntegerField(
null=True,
blank=True,
verbose_name=_("Duration (minutes)")
)
# Session Content
presenting_issues = models.TextField(
blank=True,
verbose_name=_("Presenting Issues This Session")
)
interventions_used = models.TextField(
blank=True,
verbose_name=_("Interventions Used")
)
client_response = models.TextField(
blank=True,
verbose_name=_("Client Response to Interventions")
)
# Progress Notes
progress_toward_goals = models.TextField(
blank=True,
verbose_name=_("Progress Toward Goals")
)
behavioral_observations = models.TextField(
blank=True,
verbose_name=_("Behavioral Observations")
)
mood_affect = models.CharField(
max_length=200,
blank=True,
verbose_name=_("Mood & Affect")
)
# Risk Assessment
current_risk_level = models.CharField(
max_length=50,
blank=True,
choices=[
('NONE', _('None')),
('LOW', _('Low')),
('MODERATE', _('Moderate')),
('HIGH', _('High')),
],
verbose_name=_("Current Risk Level")
)
risk_notes = models.TextField(
blank=True,
verbose_name=_("Risk Assessment Notes")
)
# Homework & Plan
homework_assigned = models.TextField(
blank=True,
verbose_name=_("Homework Assigned")
)
plan_for_next_session = models.TextField(
blank=True,
verbose_name=_("Plan for Next Session")
)
# Clinical Notes
clinical_notes = models.TextField(
blank=True,
verbose_name=_("Additional Clinical Notes")
)
history = HistoricalRecords()
class Meta:
verbose_name = _("Psychology Session")
verbose_name_plural = _("Psychology Sessions")
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"Psychology Session #{self.session_number} - {self.patient} - {self.session_date}"
class PsychologyGoal(UUIDPrimaryKeyMixin, TimeStampedMixin):
"""
Treatment goals for psychology therapy.
Tracks progress toward therapeutic objectives.
"""
class Status(models.TextChoices):
NOT_STARTED = 'NOT_STARTED', _('Not Started')
IN_PROGRESS = 'IN_PROGRESS', _('In Progress')
ACHIEVED = 'ACHIEVED', _('Achieved')
DISCONTINUED = 'DISCONTINUED', _('Discontinued')
MODIFIED = 'MODIFIED', _('Modified')
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='psychology_goals',
verbose_name=_("Patient")
)
consultation = models.ForeignKey(
PsychologyConsultation,
on_delete=models.CASCADE,
null=True,
blank=True,
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_percentage = models.PositiveSmallIntegerField(
default=0,
help_text=_("Progress percentage (0-100)"),
verbose_name=_("Progress (%)")
)
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 = _("Psychology Goal")
verbose_name_plural = _("Psychology Goals")
ordering = ['patient', 'target_date']
def __str__(self):
return f"{self.goal_description[:50]} - {self.get_status_display()}"
class PsychologyProgressReport(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin):
"""
Psychology progress report.
Comprehensive summary of treatment progress.
"""
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='psychology_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='psychology_progress_reports_provided',
verbose_name=_("Provider")
)
# Treatment Summary
treatment_start_date = models.DateField(
null=True,
blank=True,
verbose_name=_("Treatment Start Date")
)
sessions_scheduled = models.PositiveIntegerField(
default=0,
verbose_name=_("Sessions Scheduled")
)
sessions_attended = models.PositiveIntegerField(
default=0,
verbose_name=_("Sessions Attended")
)
# Progress Summary
presenting_problems_summary = models.TextField(
blank=True,
verbose_name=_("Presenting Problems Summary")
)
treatment_provided = models.TextField(
blank=True,
verbose_name=_("Treatment Provided")
)
goals_progress = models.TextField(
blank=True,
verbose_name=_("Progress Toward Goals")
)
overall_progress = models.TextField(
blank=True,
verbose_name=_("Overall Progress")
)
# Current Status
current_functioning = models.TextField(
blank=True,
verbose_name=_("Current Functioning")
)
current_symptoms = models.TextField(
blank=True,
verbose_name=_("Current Symptoms")
)
# Recommendations
recommendations = models.TextField(
blank=True,
verbose_name=_("Recommendations")
)
continue_treatment = models.BooleanField(
default=True,
verbose_name=_("Continue Treatment")
)
discharge_plan = models.TextField(
blank=True,
verbose_name=_("Discharge Plan")
)
# Prognosis
prognosis = models.TextField(
blank=True,
verbose_name=_("Prognosis")
)
history = HistoricalRecords()
class Meta:
verbose_name = _("Psychology Progress Report")
verbose_name_plural = _("Psychology 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"Psychology 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