hospital-management/tools/markdown/PHASE3_ENCOUNTER_MIGRATION_PLAN.md
Marwan Alwali 263292f6be update
2025-11-04 00:50:06 +03:00

549 lines
14 KiB
Markdown

# Phase 3: Move Encounter Model to Core - Implementation Plan
## Executive Summary
**Objective:** Move the `Encounter` model from `emr` app to `core` app to establish it as a fundamental entity alongside `Patient` and `Tenant`.
**Complexity:** HIGH - This is a breaking change requiring database migration and updates across 7+ apps.
**Status:** READY FOR IMPLEMENTATION
---
## 📊 IMPACT ANALYSIS
### Apps with FK References to Encounter (7 apps):
1. **emr** (source app) - 8 internal references
- VitalSigns.encounter
- ProblemList.related_encounter
- CarePlan (no direct FK, but related via problems)
- ClinicalNote.encounter
- ClinicalRecommendation.related_encounter
- CriticalAlert.related_encounter
2. **billing** - 1 reference
- Invoice.encounter (or similar billing model)
3. **pharmacy** - 2 references
- Prescription.encounter
- MedicationAdministration.encounter
4. **laboratory** - 1 reference
- LabOrder.encounter (or similar)
5. **radiology** - 2 references
- RadOrder.encounter
- Study.encounter (or similar)
6. **operating_theatre** - 1 reference
- SurgicalCase.encounter
7. **documentation** - 1 reference
- Document.encounter
**Total FK References:** ~16 across 7 apps
---
## 🎯 IMPLEMENTATION STRATEGY
### Option A: Incremental Migration (RECOMMENDED for Production)
**Pros:**
- Lower risk
- Can be tested incrementally
- Easier rollback
- No downtime required
**Cons:**
- More complex migration scripts
- Requires careful sequencing
### Option B: Drop and Reseed (RECOMMENDED for Development)
**Pros:**
- Clean slate
- Simpler implementation
- No migration complexity
**Cons:**
- Data loss
- Requires complete reseed
- Not suitable for production
**RECOMMENDATION:** Use Option A for this implementation
---
## 📋 STEP-BY-STEP IMPLEMENTATION PLAN
### Step 1: Preparation ✅
**Duration:** 30 minutes
1. **Backup Database**
```bash
python manage.py dumpdata > backup_before_encounter_migration.json
```
2. **Create Feature Branch**
```bash
git checkout -b feature/move-encounter-to-core
```
3. **Document Current State**
- Record all FK references
- Note any custom managers/methods
- Identify dependent migrations
### Step 2: Copy Encounter to Core 🔄
**Duration:** 1 hour
1. **Copy Model Class**
- Copy `Encounter` model from `emr/models.py` to `core/models.py`
- Copy `EncounterManager` as well
- Keep all fields, methods, and Meta options identical
2. **Update Meta.db_table**
```python
class Meta:
db_table = 'core_encounter' # Changed from 'emr_encounter'
# ... rest of Meta
```
3. **Add to core/__init__.py** (if needed for imports)
### Step 3: Create Migration Strategy 🔄
**Duration:** 2 hours
**Migration Sequence:**
1. **Migration 1: Create new table in core**
```python
# core/migrations/XXXX_add_encounter.py
operations = [
migrations.CreateModel(
name='Encounter',
fields=[...], # All fields
),
]
```
2. **Migration 2: Copy data from emr to core**
```python
# core/migrations/XXXX_copy_encounter_data.py
def copy_encounter_data(apps, schema_editor):
OldEncounter = apps.get_model('emr', 'Encounter')
NewEncounter = apps.get_model('core', 'Encounter')
for old in OldEncounter.objects.all():
NewEncounter.objects.create(
id=old.id,
tenant=old.tenant,
patient=old.patient,
# ... copy all fields
)
operations = [
migrations.RunPython(copy_encounter_data),
]
```
3. **Migration 3: Update FK references in each app**
```python
# For each app (billing, pharmacy, etc.)
operations = [
migrations.AlterField(
model_name='invoice', # or whatever model
name='encounter',
field=models.ForeignKey('core.Encounter', ...),
),
]
```
4. **Migration 4: Remove old Encounter from emr**
```python
# emr/migrations/XXXX_remove_encounter.py
operations = [
migrations.DeleteModel(name='Encounter'),
]
```
### Step 4: Update All Model References 🔄
**Duration:** 2 hours
**Files to Update:**
1. **billing/models.py**
```python
# Change from:
encounter = models.ForeignKey('emr.Encounter', ...)
# To:
encounter = models.ForeignKey('core.Encounter', ...)
```
2. **pharmacy/models.py** (2 models)
- Prescription.encounter
- MedicationAdministration.encounter
3. **laboratory/models.py**
- LabOrder.encounter
4. **radiology/models.py** (2 models)
- RadOrder.encounter
- Study.encounter
5. **operating_theatre/models.py**
- SurgicalCase.encounter
6. **documentation/models.py**
- Document.encounter (already references core.Encounter!)
7. **emr/models.py** (8 models)
- VitalSigns.encounter
- ProblemList.related_encounter
- ClinicalNote.encounter
- ClinicalRecommendation.related_encounter
- CriticalAlert.related_encounter
### Step 5: Update Imports 🔄
**Duration:** 1 hour
**Add backward compatibility import to emr/models.py:**
```python
# At the top of emr/models.py
from core.models import Encounter, EncounterManager
# This allows existing code to still import from emr
# from emr.models import Encounter # Still works!
```
**Update any direct imports in:**
- Views
- Serializers
- Forms
- Admin
- Tests
### Step 6: Update Admin Registrations 🔄
**Duration:** 30 minutes
1. **Remove from emr/admin.py:**
```python
# Remove or comment out:
# admin.site.register(Encounter, EncounterAdmin)
```
2. **Add to core/admin.py:**
```python
from .models import Encounter
@admin.register(Encounter)
class EncounterAdmin(admin.ModelAdmin):
# ... admin configuration
```
### Step 7: Update Serializers 🔄
**Duration:** 1 hour
**Update all DRF serializers:**
1. **emr/api/serializers.py**
```python
from core.models import Encounter # Changed from emr.models
```
2. **Check other apps** for serializers that reference Encounter
### Step 8: Run Migrations 🔄
**Duration:** 30 minutes (depends on data volume)
```bash
# 1. Create migrations
python manage.py makemigrations core
python manage.py makemigrations billing
python manage.py makemigrations pharmacy
python manage.py makemigrations laboratory
python manage.py makemigrations radiology
python manage.py makemigrations operating_theatre
python manage.py makemigrations emr
# 2. Review migration files
# Check that the sequence is correct
# 3. Run migrations
python manage.py migrate core
python manage.py migrate billing
python manage.py migrate pharmacy
python manage.py migrate laboratory
python manage.py migrate radiology
python manage.py migrate operating_theatre
python manage.py migrate emr
# 4. Verify data integrity
python manage.py shell
>>> from core.models import Encounter
>>> from emr.models import Encounter as EMREncounter # Should import from core
>>> Encounter.objects.count() # Should match old count
```
### Step 9: Testing 🔄
**Duration:** 2 hours
**Test Checklist:**
1. **Model Tests**
- [ ] Encounter model accessible from core
- [ ] Encounter model accessible from emr (backward compat)
- [ ] All FK relationships work
- [ ] Manager methods work (EncounterManager)
2. **Admin Tests**
- [ ] Encounter admin accessible
- [ ] Can create/edit/delete encounters
- [ ] Related objects display correctly
3. **API Tests**
- [ ] Encounter endpoints work
- [ ] Serializers work correctly
- [ ] Nested relationships work
4. **Integration Tests**
- [ ] Create encounter from appointment
- [ ] Create admission with encounter
- [ ] Create prescription with encounter
- [ ] Create lab order with encounter
- [ ] Create surgical case with encounter
5. **Data Integrity Tests**
- [ ] All encounters migrated
- [ ] No orphaned records
- [ ] FK constraints intact
### Step 10: Documentation Updates 🔄
**Duration:** 1 hour
1. **Update README**
- Note that Encounter is now in core
2. **Update API Documentation**
- Update endpoint references
3. **Update Developer Guide**
- Import from core.models, not emr.models
4. **Create Migration Guide**
- For other developers
- For deployment
---
## 🔧 MIGRATION SCRIPTS
### Script 1: Verify Current State
```python
# tools/verify_encounter_state.py
from django.apps import apps
def verify_encounter_references():
"""Verify all FK references to Encounter before migration."""
encounter_refs = []
for model in apps.get_models():
for field in model._meta.get_fields():
if hasattr(field, 'related_model'):
if field.related_model and field.related_model.__name__ == 'Encounter':
encounter_refs.append({
'app': model._meta.app_label,
'model': model.__name__,
'field': field.name,
'related_model': f"{field.related_model._meta.app_label}.{field.related_model.__name__}"
})
print(f"Found {len(encounter_refs)} FK references to Encounter:")
for ref in encounter_refs:
print(f" {ref['app']}.{ref['model']}.{ref['field']} -> {ref['related_model']}")
return encounter_refs
if __name__ == '__main__':
verify_encounter_references()
```
### Script 2: Data Migration Helper
```python
# core/migrations/XXXX_copy_encounter_data.py
from django.db import migrations
def copy_encounter_data(apps, schema_editor):
"""Copy all encounter data from emr to core."""
OldEncounter = apps.get_model('emr', 'Encounter')
NewEncounter = apps.get_model('core', 'Encounter')
encounters_to_create = []
for old in OldEncounter.objects.all():
encounters_to_create.append(NewEncounter(
id=old.id,
encounter_id=old.encounter_id,
tenant=old.tenant,
patient=old.patient,
provider=old.provider,
encounter_type=old.encounter_type,
encounter_class=old.encounter_class,
start_datetime=old.start_datetime,
end_datetime=old.end_datetime,
status=old.status,
location=old.location,
room_number=old.room_number,
appointment=old.appointment,
admission=old.admission,
chief_complaint=old.chief_complaint,
reason_for_visit=old.reason_for_visit,
priority=old.priority,
acuity_level=old.acuity_level,
documentation_complete=old.documentation_complete,
signed_off=old.signed_off,
signed_by=old.signed_by,
signed_datetime=old.signed_datetime,
billable=old.billable,
billing_codes=old.billing_codes,
quality_measures=old.quality_measures,
created_at=old.created_at,
updated_at=old.updated_at,
created_by=old.created_by,
))
# Bulk create for efficiency
NewEncounter.objects.bulk_create(encounters_to_create, batch_size=1000)
print(f"Copied {len(encounters_to_create)} encounters from emr to core")
def reverse_copy(apps, schema_editor):
"""Reverse migration - copy back from core to emr."""
NewEncounter = apps.get_model('core', 'Encounter')
NewEncounter.objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('core', 'XXXX_add_encounter'), # Previous migration
('emr', 'YYYY_current_state'), # Current emr state
]
operations = [
migrations.RunPython(copy_encounter_data, reverse_copy),
]
```
---
## ⚠️ RISKS & MITIGATION
### Risk 1: Data Loss During Migration
**Mitigation:**
- Full database backup before starting
- Test migration on copy of production data
- Verify data counts before/after
- Keep old table until verified
### Risk 2: Broken FK Constraints
**Mitigation:**
- Careful migration sequencing
- Test on development environment first
- Use transactions in migrations
- Rollback plan ready
### Risk 3: Application Downtime
**Mitigation:**
- Use backward compatibility imports
- Deploy during maintenance window
- Blue-green deployment if possible
- Quick rollback procedure
### Risk 4: Third-party Dependencies
**Mitigation:**
- Check for any packages that import Encounter
- Update package configurations
- Test all integrations
---
## 📝 ROLLBACK PLAN
If migration fails:
1. **Stop Application**
```bash
# Stop all services
```
2. **Restore Database**
```bash
python manage.py flush
python manage.py loaddata backup_before_encounter_migration.json
```
3. **Revert Code Changes**
```bash
git checkout main
git branch -D feature/move-encounter-to-core
```
4. **Restart Application**
```bash
# Restart services
```
---
## ✅ SUCCESS CRITERIA
Migration is successful when:
1. [ ] All encounters migrated to core.Encounter
2. [ ] All FK references updated to core.Encounter
3. [ ] Old emr.Encounter table removed
4. [ ] Backward compatibility imports work
5. [ ] All tests pass
6. [ ] Admin interface works
7. [ ] API endpoints work
8. [ ] No data loss
9. [ ] No orphaned records
10. [ ] Performance unchanged
---
## 📊 ESTIMATED TIMELINE
| Phase | Duration | Complexity |
|-------|----------|------------|
| Preparation | 30 min | Low |
| Copy Model to Core | 1 hour | Medium |
| Create Migrations | 2 hours | High |
| Update References | 2 hours | Medium |
| Update Imports | 1 hour | Low |
| Update Admin | 30 min | Low |
| Update Serializers | 1 hour | Medium |
| Run Migrations | 30 min | High |
| Testing | 2 hours | High |
| Documentation | 1 hour | Low |
| **TOTAL** | **~11 hours** | **HIGH** |
---
## 🎯 NEXT STEPS
1. **Review this plan** with team
2. **Get approval** for breaking change
3. **Schedule maintenance window** (if needed)
4. **Create backup** of production database
5. **Execute migration** on development first
6. **Test thoroughly**
7. **Deploy to production**
---
**Created:** 2025-10-06
**Status:** READY FOR IMPLEMENTATION
**Priority:** HIGH (Breaking Change)