mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
Largest feature delta in the v1.4.x line. Insights expands into seven dedicated metric sub-pages; GLP-1 medication tracking lands end-to-end (drug-level chart with Research Mode acknowledgment, EMA titration ladder, cadence + compliance, side-effect taxonomy, pen/vial inventory); cross-source priority becomes a two-axis resolver; per-user timezone threads through ten analytics surfaces; Withings activity + sleep v2 sync; onboarding rebuilt as a nested-route wizard; Personal Records ship end-to-end; Health Score provenance accordion; Coach runs native first-party prompts across six locales with a 1800-assertion refusal-probe matrix; OpenAPI drift gate hard-fails; multi-arch GHCR image. Migrations 0043 through 0060 are additive and forward-only. PROMPT_VERSION 4.24.0 → 4.25.0 with GROUND RULE 9 (refuses dose recommendations) and GROUND RULE 15 (refuses drug-level estimates with MDR + MDCG 2021-24 cites). See CHANGELOG.md for the full per-section delta.
128 lines
5.2 KiB
SQL
128 lines
5.2 KiB
SQL
-- v1.4.25 W4d — GLP-1 full integration (research §5).
|
|
--
|
|
-- Adds the foundation for first-class GLP-1 receptor agonist support
|
|
-- (Mounjaro, Ozempic, Wegovy, Zepbound, Trulicity, Saxenda, Rybelsus,
|
|
-- compounded semaglutide/tirzepatide). Holistic landing — schema +
|
|
-- snapshot + Coach guardrails + UI variants — happens together so the
|
|
-- feature surfaces all at once instead of trickling in across releases.
|
|
--
|
|
-- Design principle: every change is additive. Existing medications keep
|
|
-- working untouched. The new columns default to NULL / GENERIC so the
|
|
-- previous read paths read the same shape they did before the migration.
|
|
--
|
|
-- Shapes:
|
|
-- - MedicationCategory enum (GENERIC default; GLP1 turns on the
|
|
-- specialist code paths). Future categories — INSULIN, BIOLOGIC,
|
|
-- FERTILITY — drop into this enum.
|
|
-- - Medication.treatment_class column with GENERIC default. The
|
|
-- existing `medication_categories` side-table (BLOOD_PRESSURE /
|
|
-- VITAMIN / ...) tracks the clinical taxonomy and stays put — it
|
|
-- is a different concept (clinical category) and the UI keeps
|
|
-- reading it under the existing `category` slot. The new column
|
|
-- models "medication kind that needs special handling" so the
|
|
-- GLP-1 surfaces (card variant, dashboard tile, Coach snapshot)
|
|
-- have a single boolean-ish discriminator.
|
|
-- - Medication.doses_per_unit — nullable integer; only meaningful for
|
|
-- pen / vial meds (Mounjaro KwikPen = 4 doses, Ozempic 0.25/0.5 mg
|
|
-- = 4 doses, Wegovy single-dose pen = 1 dose, compounded vials
|
|
-- vary). NULL = inventory tracking is off for this medication.
|
|
-- - MedicationDoseChange — append-only titration history. The
|
|
-- current dose is "latest row by effective_from". Existing
|
|
-- Medication.dose remains the convenience pointer; this table is
|
|
-- the history of record.
|
|
-- - InjectionSite enum (8 zones — front-of-body view: 4 abdomen
|
|
-- quadrants, 2 thighs, 2 upper arms). The body-map picker maps each
|
|
-- zone to a clickable region.
|
|
-- - MedicationIntakeEvent.injection_site — nullable; only populated
|
|
-- for GLP-1 intake events. Existing rows stay NULL and read
|
|
-- untouched.
|
|
-- - MedicationInventoryEvent — append-only inventory log. Positive
|
|
-- delta = stock added (purchase), negative = consumed (one per
|
|
-- intake event when doses_per_unit is set). Reason is a free-text
|
|
-- audit slot ("purchased", "consumed", "expired", "damaged").
|
|
--
|
|
-- Forward-only, additive, idempotent. No backfill: every new column
|
|
-- carries a sensible NULL/default, and the read paths handle the
|
|
-- pre-migration shape verbatim.
|
|
|
|
-- 1. MedicationCategory enum.
|
|
DO $$ BEGIN
|
|
CREATE TYPE "medication_category" AS ENUM ('GENERIC', 'GLP1');
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- 2. Medication.treatment_class + Medication.doses_per_unit columns.
|
|
ALTER TABLE "medications"
|
|
ADD COLUMN IF NOT EXISTS "treatment_class" "medication_category" NOT NULL DEFAULT 'GENERIC';
|
|
|
|
ALTER TABLE "medications"
|
|
ADD COLUMN IF NOT EXISTS "doses_per_unit" INTEGER;
|
|
|
|
CREATE INDEX IF NOT EXISTS "medications_treatment_class_idx" ON "medications" ("treatment_class");
|
|
|
|
-- 3. InjectionSite enum + MedicationIntakeEvent.injection_site column.
|
|
DO $$ BEGIN
|
|
CREATE TYPE "injection_site" AS ENUM (
|
|
'ABDOMEN_LEFT',
|
|
'ABDOMEN_RIGHT',
|
|
'ABDOMEN_UPPER_LEFT',
|
|
'ABDOMEN_UPPER_RIGHT',
|
|
'THIGH_LEFT',
|
|
'THIGH_RIGHT',
|
|
'UPPER_ARM_LEFT',
|
|
'UPPER_ARM_RIGHT'
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
ALTER TABLE "medication_intake_events"
|
|
ADD COLUMN IF NOT EXISTS "injection_site" "injection_site";
|
|
|
|
-- 4. MedicationDoseChange — titration history.
|
|
CREATE TABLE IF NOT EXISTS "medication_dose_changes" (
|
|
"id" TEXT NOT NULL,
|
|
"medication_id" TEXT NOT NULL,
|
|
"effective_from" TIMESTAMP(3) NOT NULL,
|
|
"dose_value" DOUBLE PRECISION NOT NULL,
|
|
"dose_unit" TEXT NOT NULL,
|
|
"note" TEXT,
|
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "medication_dose_changes_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
DO $$ BEGIN
|
|
ALTER TABLE "medication_dose_changes"
|
|
ADD CONSTRAINT "medication_dose_changes_medication_id_fkey"
|
|
FOREIGN KEY ("medication_id") REFERENCES "medications"("id")
|
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
CREATE INDEX IF NOT EXISTS "medication_dose_changes_medication_id_effective_from_idx"
|
|
ON "medication_dose_changes" ("medication_id", "effective_from");
|
|
|
|
-- 5. MedicationInventoryEvent — append-only inventory ledger.
|
|
CREATE TABLE IF NOT EXISTS "medication_inventory_events" (
|
|
"id" TEXT NOT NULL,
|
|
"medication_id" TEXT NOT NULL,
|
|
"delta" INTEGER NOT NULL,
|
|
"reason" TEXT NOT NULL,
|
|
"occurred_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "medication_inventory_events_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
DO $$ BEGIN
|
|
ALTER TABLE "medication_inventory_events"
|
|
ADD CONSTRAINT "medication_inventory_events_medication_id_fkey"
|
|
FOREIGN KEY ("medication_id") REFERENCES "medications"("id")
|
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
CREATE INDEX IF NOT EXISTS "medication_inventory_events_medication_id_occurred_at_idx"
|
|
ON "medication_inventory_events" ("medication_id", "occurred_at");
|