agdar/SESSION_CONSOLIDATION_PLAN.md
Marwan Alwali 2f1681b18c update
2025-11-11 13:44:48 +03:00

14 KiB

Session Consolidation Plan

Centralizing All Sessions in appointments.Session

Date: 2025-11-11
Status: Planning Phase
Goal: ONE centralized place for all sessions (appointments.Session)


Current State Analysis

Session Models Found Across System:

  1. appointments.Session (NEW - Just Implemented)

    • Purpose: Scheduling, capacity management, billing
    • Features: Group sessions (1-20 patients), individual sessions
    • Status: Complete implementation
  2. psychology.PsychologySession (EXISTING)

    • Purpose: Clinical therapy notes
    • Links to: appointments.Appointment (FK)
    • Fields: Therapy modality, interventions, progress notes
  3. ot.OTSession (EXISTING)

    • Purpose: OT session notes & progress tracking
    • Links to: appointments.Appointment (FK)
    • Fields: Cooperative level, target skills, activities
  4. aba.ABASession (EXISTING)

    • Purpose: ABA session documentation
    • Links to: appointments.Appointment (FK)
    • Fields: Behavior data, interventions, progress

Apps WITHOUT Session Models:

  • SLP - No session model
  • Medical - No session model
  • Nursing - No session model

The Problem

Scattered Session Management:

  • 4 different "Session" models across apps
  • Confusion about what "Session" means
  • No unified scheduling/capacity management
  • Difficult to implement group sessions consistently

Violates Principle: ONE centralized place for sessions


Solution: Consolidation Strategy

Core Principle

appointments.Session = Scheduling + Capacity + Billing
*SessionNote models = Clinical Documentation

Architecture

┌──────────────────────────────────────────────┐
│  appointments.Session (SINGLE SOURCE)        │
│  ├─ Scheduling (date, time, room, provider) │
│  ├─ Capacity (1-20 patients, group support) │
│  ├─ Billing (appointment numbers)           │
│  └─ Status (scheduled, in progress, etc.)   │
└────────────┬─────────────────────────────────┘
             │
             ├─→ psychology.PsychologySessionNote
             ├─→ ot.OTSessionNote
             ├─→ aba.ABASessionNote
             └─→ (future clinical notes)

Implementation Plan

Goal: Add new FKs without breaking existing functionality

1.1 Update Psychology Model

# psychology/models.py

class PsychologySession(models.Model):  # Will rename later
    """Clinical notes for psychology session"""
    
    # EXISTING - Keep for backward compatibility
    appointment = models.ForeignKey(
        'appointments.Appointment',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='psychology_sessions'
    )
    
    # NEW - Link to centralized session
    session = models.ForeignKey(
        'appointments.Session',
        on_delete=models.CASCADE,
        null=True,  # Nullable during migration
        blank=True,
        related_name='psychology_notes',
        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',
        help_text="For group sessions: which participant these notes are for"
    )
    
    # All existing fields remain unchanged...

1.2 Update OT Model

# ot/models.py

class OTSession(models.Model):  # Will rename later
    """Clinical notes for OT session"""
    
    # EXISTING
    appointment = models.ForeignKey(
        'appointments.Appointment',
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )
    
    # NEW
    session = models.ForeignKey(
        'appointments.Session',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='ot_notes'
    )
    
    session_participant = models.ForeignKey(
        'appointments.SessionParticipant',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='ot_notes'
    )
    
    # All existing fields...

1.3 Update ABA Model

# aba/models.py

class ABASession(models.Model):  # Will rename later
    """Clinical notes for ABA session"""
    
    # EXISTING
    appointment = models.ForeignKey(
        'appointments.Appointment',
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )
    
    # NEW
    session = models.ForeignKey(
        'appointments.Session',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='aba_notes'
    )
    
    session_participant = models.ForeignKey(
        'appointments.SessionParticipant',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='aba_notes'
    )
    
    # All existing fields...

1.4 Create Migrations

# Generate migrations for each app
python manage.py makemigrations psychology
python manage.py makemigrations ot
python manage.py makemigrations aba

# Review migrations before applying
python manage.py sqlmigrate psychology XXXX
python manage.py sqlmigrate ot XXXX
python manage.py sqlmigrate aba XXXX

# Apply migrations
python manage.py migrate

Result: New fields added, existing functionality unchanged


Phase 2: Data Migration (Backward Compatible)

Goal: Create appointments.Session for existing clinical sessions

2.1 Migration Strategy

For each existing clinical session:

  1. Check if linked appointment exists
  2. Create corresponding appointments.Session
  3. Link clinical note to new session
  4. Preserve appointment link (don't break anything)

2.2 Example Migration Script

# psychology/migrations/XXXX_migrate_to_centralized_sessions.py

from django.db import migrations
from datetime import time

def migrate_psychology_sessions(apps, schema_editor):
    """Create appointments.Session for existing PsychologySession records"""
    PsychologySession = apps.get_model('psychology', 'PsychologySession')
    Session = apps.get_model('appointments', 'Session')
    
    for psych_session in PsychologySession.objects.filter(session__isnull=True):
        if psych_session.appointment:
            # Create centralized session from appointment
            session = Session.objects.create(
                tenant=psych_session.tenant,
                session_type='INDIVIDUAL',
                max_capacity=1,
                provider=psych_session.appointment.provider,
                clinic=psych_session.appointment.clinic,
                room=psych_session.appointment.room,
                service_type=psych_session.appointment.service_type or 'psychology_therapy',
                scheduled_date=psych_session.session_date,
                scheduled_time=psych_session.appointment.scheduled_time or time(9, 0),
                duration=psych_session.duration_minutes or 60,
                status='COMPLETED' if psych_session.is_signed else 'SCHEDULED'
            )
            
            # Link psychology session to centralized session
            psych_session.session = session
            psych_session.save()

class Migration(migrations.Migration):
    dependencies = [
        ('psychology', 'XXXX_add_session_fields'),
        ('appointments', '0004_add_session_models'),
    ]
    
    operations = [
        migrations.RunPython(migrate_psychology_sessions),
    ]

Repeat for OT and ABA apps


Phase 3: Rename Models (Breaking Change)

Goal: Rename to clarify purpose (Session → SessionNote)

3.1 Model Renaming

# psychology/models.py
class PsychologySession(models.Model):  # OLD NAME
    
class PsychologySessionNote(models.Model):  # NEW NAME

# ot/models.py
class OTSession(models.Model):  # OLD NAME
    
class OTSessionNote(models.Model):  # NEW NAME

# aba/models.py
class ABASession(models.Model):  # OLD NAME
    
class ABASessionNote(models.Model):  # NEW NAME

3.2 Database Table Renaming

# psychology/migrations/XXXX_rename_to_session_note.py

from django.db import migrations

class Migration(migrations.Migration):
    dependencies = [
        ('psychology', 'XXXX_migrate_to_centralized_sessions'),
    ]
    
    operations = [
        migrations.RenameModel(
            old_name='PsychologySession',
            new_name='PsychologySessionNote',
        ),
    ]

3.3 Update All References

Files to Update:

  • Models: Import statements
  • Views: All view classes
  • Forms: Form classes
  • Templates: Template references
  • Admin: Admin classes
  • Serializers: API serializers
  • URLs: URL patterns
  • Tests: Test cases

Example:

# Before
from psychology.models import PsychologySession

# After
from psychology.models import PsychologySessionNote

Phase 4: Update Views & Forms

Goal: Support both individual and group sessions

4.1 Update Session Creation Views

# psychology/views.py

class PsychologySessionNoteCreateView(CreateView):
    """Create clinical notes for a session"""
    model = PsychologySessionNote
    
    def form_valid(self, form):
        # Get session from URL or form
        session = get_object_or_404(Session, pk=self.kwargs['session_pk'])
        
        # For group sessions, get participant
        if session.session_type == 'GROUP':
            participant = get_object_or_404(
                SessionParticipant,
                pk=self.kwargs['participant_pk']
            )
            form.instance.session_participant = participant
        
        form.instance.session = session
        form.instance.patient = session.patient  # or participant.patient
        form.instance.provider = self.request.user
        
        return super().form_valid(form)

4.2 Update Forms

# psychology/forms.py

class PsychologySessionNoteForm(forms.ModelForm):
    class Meta:
        model = PsychologySessionNote
        fields = [
            'presenting_issues',
            'interventions_used',
            'client_response',
            'progress_toward_goals',
            # ... all clinical fields
        ]
        # Exclude: session, session_participant (set in view)

Phase 5: Cleanup (Optional)

Goal: Remove deprecated fields

5.1 Make session FK Required

# After all data migrated
class PsychologySessionNote(models.Model):
    session = models.ForeignKey(
        'appointments.Session',
        on_delete=models.CASCADE,
        null=False,  # Changed from True
        blank=False,  # Changed from True
        related_name='psychology_notes'
    )

5.2 Remove appointment FK (Optional)

# Can keep for historical reference or remove
# If removing:
class Migration(migrations.Migration):
    operations = [
        migrations.RemoveField(
            model_name='psychologysessionnote',
            name='appointment',
        ),
    ]

Benefits of This Approach

Advantages

  1. ONE Session Model - appointments.Session is single source of truth
  2. Clear Naming - *SessionNote clearly indicates clinical documentation
  3. No Confusion - "Session" always means scheduling/capacity
  4. Backward Compatible - Can migrate gradually without breaking
  5. Group Session Ready - All apps can support group sessions
  6. Scalable - Easy pattern for future apps (SLP, Medical, etc.)
  7. Maintains History - Can keep appointment FK for historical data

📊 Impact Analysis

Models: 3 files (psychology, ot, aba)
Views: ~15-20 view classes
Forms: ~10 form classes
Templates: ~15-20 templates
Admin: 3 admin classes
Migrations: 3-4 per app (9-12 total)

Estimated Effort: 2-3 days for complete migration


Testing Strategy

Test Cases

  1. Existing Individual Sessions

    • Verify old sessions still work
    • Verify clinical notes accessible
    • Verify no data loss
  2. New Individual Sessions

    • Create via appointments.Session
    • Add clinical notes
    • Verify linking works
  3. New Group Sessions

    • Create group session (capacity > 1)
    • Add multiple patients
    • Add clinical notes per participant
    • Verify each patient has separate notes
  4. Migration Verification

    • Count records before/after
    • Verify all links correct
    • Check for orphaned records

Rollback Plan

If Issues Arise

Phase 1 Rollback:

  • New fields are nullable
  • Simply don't use them
  • No data loss

Phase 2 Rollback:

  • Keep appointment FK
  • Can revert to using appointment
  • Session data can be deleted if needed

Phase 3 Rollback:

  • Rename back to original names
  • Update imports
  • More complex but possible

Next Steps

Immediate Actions

  1. Review this plan - Get stakeholder approval
  2. Phase 1 Implementation - Add session FKs (non-breaking)
  3. Test Phase 1 - Verify no regressions
  4. Phase 2 Implementation - Data migration
  5. Test Phase 2 - Verify data integrity
  6. Phase 3 Implementation - Rename models
  7. Update all references - Views, forms, templates
  8. Final testing - End-to-end verification

Timeline

  • Week 1: Phase 1 + Testing
  • Week 2: Phase 2 + Testing
  • Week 3: Phase 3 + Updates
  • Week 4: Final testing + Documentation

Conclusion

This consolidation plan achieves the goal of ONE centralized place for sessions while:

  • Maintaining backward compatibility
  • Preserving existing data
  • Enabling group session support across all clinical apps
  • Following clear naming conventions
  • Providing a scalable pattern for future apps

Key Decision: Rename clinical "Session" models to "SessionNote" to eliminate confusion and make appointments.Session the single source of truth.