mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
The insights alert cards computed medication compliance with the naive `schedules.length × days` denominator, so a fully-adherent weekly injectable read ~14% and fired a false low-compliance alert (and a paused medication mis-fired the same way). Route the alert path through the canonical `calculateCompliance` engine like every other compliance surface — pause eras, schedule revisions and dose windows honoured. Document confirm: committing a numeric observation against an existing biomarker silently replaced the stated unit with the catalog's, with no conversion — a stated "6.1 mmol/L" against a mg/dL marker committed "6.1 mg/dL". Reject on a unit mismatch (fail closed) so the user reconciles on the review screen, the same posture as the unit-required guard. Wire grip strength, waist (with waist-to-height) and pain into the briefing feature set and the grounding allow-set so the clinical-depth signals reach the daily narrative; the mental-health screeners stay excluded by construction. Biomarker detail: fall back to a generic description for catalog-less markers, and keep the heading + description with a retry on the error state. Collapse the medication External-API and Lifecycle groups by default. Re-enable the insights scroll-to-top regression and assert the shipped main-content reset. Align the EN haemoglobin/haematocrit display names to British spelling. Drop the redundant `(userId, deletedAt)` index on inbound documents — a strict prefix of the new three-column index. Trim the dead MedicalDisclaimer variants to the one live `dataPosture`. Move the crisis-resources card off the retired amber tint to a calm destructive-subtle border. Add a registry invariant asserting every biomarker signal key resolves to a real catalog slug.
92 lines
4.9 KiB
SQL
92 lines
4.9 KiB
SQL
-- v1.25 — documents library: store-first, optional extraction.
|
|
--
|
|
-- Inverts the inbound-documents surface from an OCR inbox into a document
|
|
-- library. A user can now STORE any file (encrypted at rest) without a
|
|
-- configured document-scan provider; extraction becomes an optional action
|
|
-- on an already-stored row.
|
|
--
|
|
-- Schema deltas on `inbound_documents`:
|
|
-- * `title` — user-given label, plaintext (mirrors `filename`) so
|
|
-- the list can ILIKE-search + ORDER BY. The document
|
|
-- BODY stays encrypted; only this short user-authored
|
|
-- title is clear-text.
|
|
-- * `document_date` — user-set filing date (editable). Distinct from
|
|
-- `report_date`, which stays the model-transcribed
|
|
-- value.
|
|
-- * a `(user_id, deleted_at, document_date)` index for date-grouped browse +
|
|
-- range filter (the list always filters `deleted_at IS NULL`).
|
|
-- * `inbound_document_kind` gains LAB_RESULT / IMAGING / PRESCRIPTION /
|
|
-- INSURANCE / VACCINATION (the existing 3 stay; OTHER stays the fallback).
|
|
-- * `inbound_document_status` gains STORED, made the column DEFAULT (the
|
|
-- library default — uploaded, no extraction run).
|
|
--
|
|
-- Additive; no existing row rewritten (the status type-swap maps every
|
|
-- existing label to itself). Nullable columns, no backfill: existing rows
|
|
-- keep their current status; `title` / `document_date` start NULL and resolve
|
|
-- through the display fallback. Idempotent guards so reruns are safe on prod.
|
|
--
|
|
-- Postgres note: a freshly-added enum value cannot be USED (e.g. as a column
|
|
-- DEFAULT) in the same transaction it is added in. Because Prisma runs each
|
|
-- migration inside a transaction, STORED cannot be added with `ALTER TYPE ...
|
|
-- ADD VALUE` and then set as the default in the same file. The status enum is
|
|
-- therefore recreated (RENAME + CREATE with the full value set + column
|
|
-- type-swap + SET DEFAULT): a type CREATEd in the current transaction MAY
|
|
-- have its values used in that same transaction, which makes the default
|
|
-- change safe. The `kind` additions are not used in this migration, so the
|
|
-- cheap `ADD VALUE IF NOT EXISTS` form is used for them.
|
|
--
|
|
-- Reversibility (down):
|
|
-- DROP INDEX IF EXISTS "inbound_documents_user_id_document_date_idx";
|
|
-- ALTER TABLE "inbound_documents" DROP COLUMN IF EXISTS "document_date";
|
|
-- ALTER TABLE "inbound_documents" DROP COLUMN IF EXISTS "title";
|
|
-- -- (added enum values are inert if unused; a roll-back leaves them
|
|
-- -- harmless rather than rewriting the type back.)
|
|
|
|
-- AlterTable: user-given title + user-set filing date (both nullable).
|
|
ALTER TABLE "inbound_documents" ADD COLUMN IF NOT EXISTS "title" TEXT;
|
|
ALTER TABLE "inbound_documents" ADD COLUMN IF NOT EXISTS "document_date" TIMESTAMP(3);
|
|
|
|
-- AlterEnum: richer document-kind taxonomy (additive; not used in this
|
|
-- migration, so the new values are safe to add in-place).
|
|
ALTER TYPE "inbound_document_kind" ADD VALUE IF NOT EXISTS 'LAB_RESULT';
|
|
ALTER TYPE "inbound_document_kind" ADD VALUE IF NOT EXISTS 'IMAGING';
|
|
ALTER TYPE "inbound_document_kind" ADD VALUE IF NOT EXISTS 'PRESCRIPTION';
|
|
ALTER TYPE "inbound_document_kind" ADD VALUE IF NOT EXISTS 'INSURANCE';
|
|
ALTER TYPE "inbound_document_kind" ADD VALUE IF NOT EXISTS 'VACCINATION';
|
|
|
|
-- AlterEnum: add STORED and make it the column default. Recreate the type so
|
|
-- the new value can be USED as the default in this same transaction (a
|
|
-- same-transaction CREATE TYPE permits same-transaction use of its values).
|
|
-- Guarded on STORED-absence so a rerun is a no-op.
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM pg_enum e
|
|
JOIN pg_type t ON e.enumtypid = t.oid
|
|
WHERE t.typname = 'inbound_document_status' AND e.enumlabel = 'STORED'
|
|
) THEN
|
|
ALTER TYPE "inbound_document_status" RENAME TO "inbound_document_status_old";
|
|
CREATE TYPE "inbound_document_status" AS ENUM (
|
|
'STORED', 'EXTRACTING', 'EXTRACTED', 'FAILED', 'CONFIRMED', 'DISCARDED'
|
|
);
|
|
ALTER TABLE "inbound_documents" ALTER COLUMN "status" DROP DEFAULT;
|
|
ALTER TABLE "inbound_documents"
|
|
ALTER COLUMN "status" TYPE "inbound_document_status"
|
|
USING ("status"::text::"inbound_document_status");
|
|
ALTER TABLE "inbound_documents" ALTER COLUMN "status" SET DEFAULT 'STORED';
|
|
DROP TYPE "inbound_document_status_old";
|
|
END IF;
|
|
END $$;
|
|
|
|
-- CreateIndex: date-grouped browse + document_date range filter. The list
|
|
-- always filters `deleted_at IS NULL` and sorts by `document_date`, so
|
|
-- deleted_at leads the sort column to keep the scan covered.
|
|
CREATE INDEX IF NOT EXISTS "inbound_documents_user_id_deleted_at_document_date_idx"
|
|
ON "inbound_documents"("user_id", "deleted_at", "document_date");
|
|
|
|
-- DropIndex: the new `(user_id, deleted_at, document_date)` index above is a
|
|
-- superset of the plain `(user_id, deleted_at)` index, so the 2-column index is
|
|
-- redundant for read planning and only adds write/maintenance cost. Drop it.
|
|
DROP INDEX IF EXISTS "inbound_documents_user_id_deleted_at_idx";
|