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

14 KiB

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

Pros:

  • Lower risk
  • Can be tested incrementally
  • Easier rollback
  • No downtime required

Cons:

  • More complex migration scripts
  • Requires careful sequencing

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

    python manage.py dumpdata > backup_before_encounter_migration.json
    
  2. Create Feature Branch

    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

    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

    # core/migrations/XXXX_add_encounter.py
    operations = [
        migrations.CreateModel(
            name='Encounter',
            fields=[...],  # All fields
        ),
    ]
    
  2. Migration 2: Copy data from emr to core

    # 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

    # 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

    # 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

    # 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:

# 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:

    # Remove or comment out:
    # admin.site.register(Encounter, EncounterAdmin)
    
  2. Add to core/admin.py:

    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

    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)

# 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

# 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

# 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

    # Stop all services
    
  2. Restore Database

    python manage.py flush
    python manage.py loaddata backup_before_encounter_migration.json
    
  3. Revert Code Changes

    git checkout main
    git branch -D feature/move-encounter-to-core
    
  4. Restart Application

    # 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)