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

9.7 KiB

Phase 4: Core Model Consolidation - COMPLETE

Date: 2025-10-06
Status: Core models implemented, Patients app refactored
Next: Update remaining apps (emr, billing, pharmacy, etc.)


🎯 What Was Accomplished

1. Core Models Implementation

Created comprehensive core models in core/models.py:

Mixins (Reusable Components)

  • TimeStampedModel - Adds created_at, updated_at
  • UserStampedModel - Tracks created_by, updated_by
  • ActiveModel - Adds is_active flag
  • TenantManager - Custom manager with for_tenant() method
  • TenantScopedModel - Combines all mixins + tenant scoping

Core Primitives

  1. Tenant - Multi-tenant organization support

    • code, name, status
    • Timestamps and active flag
  2. Facility - Canonical facility/site entity

    • code, name, facility_type
    • Address fields (minimal)
    • Tenant-scoped with full mixins
  3. Patient - Minimal core patient identity

    • uuid (auto-generated)
    • given_name, family_name
    • birth_date, sex
    • primary_facility (optional FK)
    • Full tenant scoping and audit trail
  4. IdentifierType - Extensible identifier system

    • code, name, issuer
    • Supports MRN, National ID, Insurance ID, etc.
  5. Identifier - Patient identifiers

    • Links to Patient and IdentifierType
    • value, is_primary
    • period_start, period_end
    • Unique constraint: (tenant, id_type, value)
  6. Encounter - Clinical encounter (moved from EMR)

    • patient, facility, encounter_class, status
    • start_at, end_at, attending
    • Full audit trail
    • This is the backbone for clinical/billing/rules
  7. Attachment - Generic file storage

    • file, title, content_type, size_bytes
    • Optional links to patient/encounter
    • uploaded_by tracking
  8. AuditEvent - HIPAA/GDPR compliance

    • action, actor, target (GenericFK)
    • summary, payload (JSON)
    • occurred_at timestamp

2. Patients App Refactored

Transformed patients/models.py to use core.Patient:

PatientProfile (OneToOne with core.Patient)

  • Changed: Now links to core.Patient instead of being standalone
  • Keeps: All extended demographics and healthcare fields
  • Primary Key: Uses patient (OneToOneField) as primary key
  • Inherits: TenantScopedModel (gets all mixins automatically)

Fields Moved to Core:

  • given_name, family_name → core.Patient
  • birth_date, sex → core.Patient
  • UUID → core.Patient
  • is_active, is_deceased → core.Patient (via mixins)

Fields Kept in PatientProfile:

  • Extended name (middle_name, preferred_name, suffix)
  • Contact info (email, phone, mobile)
  • Address (full address fields)
  • Demographics (marital_status, language, etc.)
  • Healthcare (PCP, allergies, medical_alerts)
  • VIP flags, advance directives
  • Photo, registration info

Other Models Updated

All now reference core.Patient instead of PatientProfile:

  1. EmergencyContact

    • FK to core.Patient
    • Inherits TenantScopedModel
  2. InsuranceInfo

    • FK to core.Patient
    • Inherits TenantScopedModel
  3. ConsentTemplate

    • Inherits TenantScopedModel
    • No patient FK (it's a template)
  4. ConsentForm

    • FK to core.Patient
    • Inherits TenantScopedModel
  5. PatientNote

    • FK to core.Patient
    • Inherits TenantScopedModel

📊 Architecture Changes

Before (Old Structure)

patients/
  └── PatientProfile (standalone, all fields)
      ├── EmergencyContact (FK to PatientProfile)
      ├── InsuranceInfo (FK to PatientProfile)
      └── ConsentForm (FK to PatientProfile)

emr/
  └── Encounter (in EMR app)

After (New Structure)

core/
  ├── Tenant (foundation)
  ├── Facility (canonical site)
  ├── Patient (minimal identity) ← CANONICAL
  ├── IdentifierType
  ├── Identifier (links to Patient)
  ├── Encounter (moved from EMR) ← CANONICAL
  ├── Attachment
  └── AuditEvent

patients/
  └── PatientProfile (OneToOne with core.Patient, extended fields)
      ├── EmergencyContact (FK to core.Patient)
      ├── InsuranceInfo (FK to core.Patient)
      └── ConsentForm (FK to core.Patient)

🔑 Key Design Decisions

1. Minimal Core Patient

  • Only essential identity fields in core
  • Extended demographics in patients.PatientProfile
  • Rationale: Keep core lean, domain-specific in apps

2. Identifier System

  • Separate IdentifierType table (extensible)
  • Multiple identifiers per patient
  • is_primary flag for MRN
  • Rationale: Supports multiple ID schemes (MRN, National ID, Insurance, etc.)

3. Encounter in Core

  • Moved from EMR to core
  • Rationale: Used by ALL apps (billing, pharmacy, lab, radiology, etc.)
  • Backbone for clinical/billing/rules

4. TenantScopedModel Mixin

  • All models inherit standard fields
  • Automatic tenant scoping
  • Audit trail (created_by, updated_by, timestamps)
  • Rationale: DRY principle, consistent behavior

5. OneToOne for PatientProfile

  • Uses patient as primary key
  • Rationale: 1:1 relationship, no separate ID needed

📝 Database Schema Changes

New Tables Created

-- Core tables
core_tenant
core_facility
core_patient  NEW CANONICAL
core_identifier_type
core_identifier
core_encounter  MOVED FROM EMR
core_attachment
core_audit_event

-- Patients tables (refactored)
patients_patient_profile  NOW LINKS TO core_patient
patients_emergency_contact  NOW LINKS TO core_patient
patients_insurance_info  NOW LINKS TO core_patient
patients_consent_template
patients_consent_form  NOW LINKS TO core_patient
patients_patient_note  NOW LINKS TO core_patient

Key Relationships

core.Patient (1) ←→ (1) patients.PatientProfile
core.Patient (1) ←→ (N) patients.EmergencyContact
core.Patient (1) ←→ (N) patients.InsuranceInfo
core.Patient (1) ←→ (N) patients.ConsentForm
core.Patient (1) ←→ (N) core.Identifier
core.Patient (1) ←→ (N) core.Encounter
core.Facility (1) ←→ (N) core.Encounter
core.Tenant (1) ←→ (N) ALL models

Benefits Achieved

1. Canonical Patient Identity

  • Single source of truth for patient data
  • No duplication across apps
  • Consistent patient references

2. Proper Separation of Concerns

  • Core = fundamental entities
  • Patients = extended demographics
  • Other apps = domain-specific logic

3. Extensible Identifier System

  • Support multiple ID types
  • Easy to add new identifier schemes
  • Proper constraints and validation

4. Encounter as Foundation

  • Available to all apps
  • Consistent encounter tracking
  • Proper billing/clinical linkage

5. Audit Trail Everywhere

  • Every model tracks who/when
  • HIPAA/GDPR compliance
  • Tenant scoping built-in

6. Clean Migrations Path

  • Fresh start approach
  • No complex data migrations
  • Clear, simple structure

🚀 Next Steps

Phase 4.3: Update EMR App

  • Import Encounter from core
  • Update all models to use core.Patient
  • Update all models to use core.Encounter
  • Remove old Encounter model

Phase 4.4: Update Other Apps (9 apps)

  • billing - Update patient/encounter FKs
  • pharmacy - Update patient/encounter FKs
  • laboratory - Update patient/encounter FKs
  • radiology - Update patient/encounter FKs
  • operating_theatre - Update patient/encounter/facility FKs
  • inpatients - Update patient/encounter FKs
  • blood_bank - Update patient FKs
  • documentation - Update patient/encounter FKs (already done?)
  • facility_management - Remove local Facility, use core.Facility

Phase 4.5: Fresh Start

  • Delete db.sqlite3
  • Delete all migration files (keep init.py)
  • Run makemigrations
  • Run migrate
  • Create superuser
  • Reseed data

📚 Import Guide for Other Apps

How to Use Core Models

# In any app's models.py
from core.models import (
    Patient,           # Canonical patient identity
    Encounter,         # Canonical encounter
    Facility,          # Canonical facility
    Identifier,        # Patient identifiers
    IdentifierType,    # Identifier types
    Attachment,        # File attachments
    AuditEvent,        # Audit logging
    TenantScopedModel, # Base model with mixins
)

# Example: Create a model that links to core.Patient
class MyModel(TenantScopedModel):
    patient = models.ForeignKey(
        'core.Patient',
        on_delete=models.CASCADE,
        related_name='my_models'
    )
    encounter = models.ForeignKey(
        'core.Encounter',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='my_models'
    )
    # ... your fields

Accessing Patient Data

# Get patient
patient = Patient.objects.get(uuid='...')

# Get patient profile (extended demographics)
profile = patient.profile  # OneToOne relationship

# Get patient identifiers
mrn = patient.identifiers.filter(is_primary=True).first()
national_id = patient.identifiers.filter(id_type__code='national_id').first()

# Get patient encounters
encounters = patient.encounters.all()

# Get patient emergency contacts
contacts = patient.emergency_contacts.all()

🎯 Success Criteria

  • Core models created with proper mixins
  • Patient identity centralized in core
  • Encounter moved to core
  • Identifier system implemented
  • Patients app refactored to use core.Patient
  • All patients models use TenantScopedModel
  • EMR app updated (next)
  • All other apps updated (next)
  • Fresh migrations successful (next)
  • Data reseeded successfully (next)

Status: Phase 4.1 & 4.2 COMPLETE
Ready for: Phase 4.3 (Update EMR app)
Estimated Time Remaining: 3-4 hours for remaining apps + migrations