236 lines
8.1 KiB
SQL
236 lines
8.1 KiB
SQL
-- ============================================================================
|
|
-- DATA MIGRATION: Legacy tables → FHIR tables
|
|
-- ============================================================================
|
|
-- Created: 2024-11-21
|
|
-- Purpose: Migrate existing data from clients/treatment_plans to patients/care_plans
|
|
--
|
|
-- IMPORTANT: This migration preserves UUIDs for referential integrity
|
|
-- Run this AFTER applying the pragmatic FHIR schema
|
|
-- ============================================================================
|
|
|
|
-- ============================================================================
|
|
-- STEP 1: Migrate clients → patients
|
|
-- ============================================================================
|
|
|
|
-- Check for existing data
|
|
DO $$
|
|
DECLARE
|
|
client_count INTEGER;
|
|
patient_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO client_count FROM clients;
|
|
SELECT COUNT(*) INTO patient_count FROM patients;
|
|
|
|
RAISE NOTICE 'Found % clients, % patients (before migration)', client_count, patient_count;
|
|
END $$;
|
|
|
|
-- Migrate clients to patients
|
|
INSERT INTO patients (
|
|
id, -- Preserve UUID for referential integrity
|
|
name_family, -- clients.last_name
|
|
name_given, -- clients.first_name (as array)
|
|
birth_date, -- clients.birth_date
|
|
gender, -- Default 'unknown' (required field)
|
|
identifier_bsn, -- Placeholder
|
|
identifier_client_number, -- Use original client ID as client number
|
|
active, -- Default true
|
|
created_at, -- Preserve timestamp
|
|
updated_at -- Preserve timestamp
|
|
)
|
|
SELECT
|
|
c.id,
|
|
c.last_name,
|
|
ARRAY[c.first_name], -- Convert string to array
|
|
c.birth_date,
|
|
'unknown'::gender_type, -- Required field, default to unknown
|
|
'999999990', -- Placeholder BSN (demo)
|
|
c.id::text, -- Use UUID as client number for traceability
|
|
true,
|
|
c.created_at,
|
|
c.updated_at
|
|
FROM clients c
|
|
WHERE NOT EXISTS (
|
|
-- Don't re-migrate if already exists
|
|
SELECT 1 FROM patients p WHERE p.id = c.id
|
|
);
|
|
|
|
-- Report migration results
|
|
DO $$
|
|
DECLARE
|
|
migrated_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO migrated_count
|
|
FROM patients p
|
|
INNER JOIN clients c ON p.id = c.id;
|
|
|
|
RAISE NOTICE 'Successfully migrated % clients to patients', migrated_count;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- STEP 2: Migrate treatment_plans → care_plans
|
|
-- ============================================================================
|
|
|
|
-- Check for existing data
|
|
DO $$
|
|
DECLARE
|
|
plan_count INTEGER;
|
|
careplan_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO plan_count FROM treatment_plans;
|
|
SELECT COUNT(*) INTO careplan_count FROM care_plans;
|
|
|
|
RAISE NOTICE 'Found % treatment_plans, % care_plans (before migration)', plan_count, careplan_count;
|
|
END $$;
|
|
|
|
-- Migrate treatment_plans to care_plans
|
|
INSERT INTO care_plans (
|
|
id, -- Preserve UUID
|
|
identifier, -- Generate from UUID
|
|
status, -- Map from treatment_plans.status
|
|
title, -- Generate default title
|
|
description, -- Extract from plan JSONB if available
|
|
patient_id, -- treatment_plans.client_id → patient_id
|
|
goals, -- Extract from plan.doelen
|
|
activities, -- Extract from plan.interventies
|
|
period_start, -- Derive from created_at
|
|
period_end, -- Derive from created_at + 3 months (default)
|
|
created_date, -- Preserve created_at
|
|
created_at, -- Preserve created_at
|
|
updated_at -- Preserve updated_at
|
|
)
|
|
SELECT
|
|
tp.id,
|
|
tp.id::text, -- Use UUID as identifier
|
|
CASE
|
|
WHEN tp.status = 'concept' THEN 'draft'::careplan_status
|
|
WHEN tp.status = 'gepubliceerd' THEN 'active'::careplan_status
|
|
ELSE 'draft'::careplan_status
|
|
END,
|
|
'Behandelplan v' || tp.version::text, -- Default title
|
|
NULL, -- No description in legacy schema
|
|
tp.client_id, -- Maps to patient_id (already migrated)
|
|
-- Transform goals from legacy structure to FHIR Goal structure
|
|
COALESCE(
|
|
(
|
|
SELECT jsonb_agg(
|
|
jsonb_build_object(
|
|
'description', jsonb_build_object('text', goal),
|
|
'lifecycleStatus', 'active'
|
|
)
|
|
)
|
|
FROM jsonb_array_elements_text(tp.plan->'doelen') goal
|
|
),
|
|
'[]'::jsonb
|
|
),
|
|
-- Transform activities from legacy structure to FHIR CarePlan.activity structure
|
|
COALESCE(
|
|
(
|
|
SELECT jsonb_agg(
|
|
jsonb_build_object(
|
|
'detail', jsonb_build_object(
|
|
'code', jsonb_build_object('text', interventie),
|
|
'status', 'in-progress',
|
|
'scheduledString', COALESCE(tp.plan->>'frequentie', 'Niet gespecificeerd')
|
|
)
|
|
)
|
|
)
|
|
FROM jsonb_array_elements_text(tp.plan->'interventies') interventie
|
|
),
|
|
'[]'::jsonb
|
|
),
|
|
tp.created_at::date, -- Start date from creation
|
|
(tp.created_at + interval '3 months')::date, -- Default 3-month treatment
|
|
tp.created_at,
|
|
tp.created_at,
|
|
tp.updated_at
|
|
FROM treatment_plans tp
|
|
WHERE NOT EXISTS (
|
|
-- Don't re-migrate if already exists
|
|
SELECT 1 FROM care_plans cp WHERE cp.id = tp.id
|
|
)
|
|
-- Ensure the client was migrated to patients first
|
|
AND EXISTS (
|
|
SELECT 1 FROM patients p WHERE p.id = tp.client_id
|
|
);
|
|
|
|
-- Report migration results
|
|
DO $$
|
|
DECLARE
|
|
migrated_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO migrated_count
|
|
FROM care_plans cp
|
|
INNER JOIN treatment_plans tp ON cp.id = tp.id;
|
|
|
|
RAISE NOTICE 'Successfully migrated % treatment_plans to care_plans', migrated_count;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- STEP 3: Verification queries
|
|
-- ============================================================================
|
|
|
|
-- Verify patient migration
|
|
DO $$
|
|
DECLARE
|
|
client_count INTEGER;
|
|
patient_count INTEGER;
|
|
match_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO client_count FROM clients;
|
|
SELECT COUNT(*) INTO patient_count FROM patients;
|
|
SELECT COUNT(*) INTO match_count
|
|
FROM patients p
|
|
INNER JOIN clients c ON p.id = c.id;
|
|
|
|
RAISE NOTICE '=== MIGRATION VERIFICATION ===';
|
|
RAISE NOTICE 'Clients: %, Patients: %, Matched: %', client_count, patient_count, match_count;
|
|
|
|
IF match_count = client_count THEN
|
|
RAISE NOTICE '✓ All clients successfully migrated to patients';
|
|
ELSE
|
|
RAISE WARNING '✗ Migration incomplete: % clients not migrated', (client_count - match_count);
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Verify care plan migration
|
|
DO $$
|
|
DECLARE
|
|
plan_count INTEGER;
|
|
careplan_count INTEGER;
|
|
match_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO plan_count FROM treatment_plans;
|
|
SELECT COUNT(*) INTO careplan_count FROM care_plans;
|
|
SELECT COUNT(*) INTO match_count
|
|
FROM care_plans cp
|
|
INNER JOIN treatment_plans tp ON cp.id = tp.id;
|
|
|
|
RAISE NOTICE 'Treatment Plans: %, Care Plans: %, Matched: %', plan_count, careplan_count, match_count;
|
|
|
|
IF plan_count = 0 THEN
|
|
RAISE NOTICE '- No treatment plans to migrate (table empty)';
|
|
ELSIF match_count = plan_count THEN
|
|
RAISE NOTICE '✓ All treatment plans successfully migrated to care_plans';
|
|
ELSE
|
|
RAISE WARNING '✗ Migration incomplete: % treatment plans not migrated', (plan_count - match_count);
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- SUMMARY
|
|
-- ============================================================================
|
|
-- This migration script:
|
|
-- ✓ Migrates clients → patients (preserving UUIDs)
|
|
-- ✓ Migrates treatment_plans → care_plans (preserving UUIDs)
|
|
-- ✓ Transforms legacy JSONB structure to FHIR-compliant structure
|
|
-- ✓ Sets sensible defaults for required FHIR fields
|
|
-- ✓ Idempotent: safe to re-run (uses NOT EXISTS checks)
|
|
-- ✓ Preserves referential integrity
|
|
--
|
|
-- NEXT STEPS:
|
|
-- 1. Run this migration: supabase db push or apply_migration
|
|
-- 2. Verify data in patients and care_plans tables
|
|
-- 3. Update application code to use new FHIR tables
|
|
-- 4. Optionally: keep legacy tables for rollback, or drop after verification
|
|
-- ============================================================================
|