538 lines
14 KiB
Markdown
538 lines
14 KiB
Markdown
# 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
|
|
|
|
### Phase 1: Add Session Links (Non-Breaking) ✅ RECOMMENDED START
|
|
|
|
**Goal:** Add new FKs without breaking existing functionality
|
|
|
|
#### 1.1 Update Psychology Model
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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:**
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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)
|
|
|
|
```python
|
|
# 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.
|