mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
feat(symptoms): add person-defined symptoms and timestamped occurrences
A symptom of one's own ("aura", "headache") is a definition with an
encrypted name and an icon from the cycle custom-symptom allowlist; each
occurrence carries a time, a 0-10 intensity (the pain score's scale), an
optional encrypted note and an optional link to one of the record's open
illness episodes. Both ride the illness module: every route answers
illness.disabled when it is off.
- Migration 0369 (idempotent): symptom_definitions and symptom_events,
intensity CHECK 0-10, episode link ON DELETE SET NULL.
- /api/symptoms/definitions{,/[id]} and /api/symptoms/events{,/[id]}:
eight active definitions per record (a hidden one frees its slot,
showing it again re-checks), hide or purge, events create/list/edit/
delete, a new link must name an open episode of the same record
(422 symptoms.episode.notOpen). Error group symptoms.*.
- Sharing: reads at READ, logging an occurrence at WRITE, defining and
editing at MANAGE; C1/C3/C4 audit details; frozen lists, the handler
matrix and the activity verbs updated.
- Both encrypted columns registered and rotated; both models in the wipe
plan; both carried at both backup ends with occurrences nested in their
definition, plaintext in a portable file and ciphertext in a recovery
file, and a link to an episode the file lacks dropped and named
(symptomEpisodeReference).
- OpenAPI regenerated.
This commit is contained in:
1 parent
7cd323499e
commit
d6571c4e9c
30 files changed
+3091
-16
No files matched your search
@@ -0,0 +1,73 @@
|
||||
-- Person-defined symptoms and their timestamped events. A definition is the
|
||||
-- person's own name for a symptom (encrypted); an event is one occurrence with
|
||||
-- a 0-10 intensity and an optional link to an illness episode. Idempotent, so
|
||||
-- a re-run of a half-applied migration converges.
|
||||
CREATE TABLE IF NOT EXISTS "symptom_definitions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"label_encrypted" BYTEA NOT NULL,
|
||||
"icon" TEXT,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "symptom_definitions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "symptom_definitions_user_id_sort_order_idx"
|
||||
ON "symptom_definitions" ("user_id", "sort_order");
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "symptom_definitions"
|
||||
ADD CONSTRAINT "symptom_definitions_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "symptom_events" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"definition_id" TEXT NOT NULL,
|
||||
"occurred_at" TIMESTAMP(3) NOT NULL,
|
||||
"intensity" INTEGER NOT NULL,
|
||||
"note_encrypted" BYTEA,
|
||||
"episode_id" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "symptom_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "symptom_events_user_id_occurred_at_idx"
|
||||
ON "symptom_events" ("user_id", "occurred_at");
|
||||
CREATE INDEX IF NOT EXISTS "symptom_events_definition_id_occurred_at_idx"
|
||||
ON "symptom_events" ("definition_id", "occurred_at");
|
||||
CREATE INDEX IF NOT EXISTS "symptom_events_episode_id_idx"
|
||||
ON "symptom_events" ("episode_id");
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "symptom_events"
|
||||
ADD CONSTRAINT "symptom_events_intensity_check"
|
||||
CHECK ("intensity" >= 0 AND "intensity" <= 10);
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "symptom_events"
|
||||
ADD CONSTRAINT "symptom_events_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "symptom_events"
|
||||
ADD CONSTRAINT "symptom_events_definition_id_fkey"
|
||||
FOREIGN KEY ("definition_id") REFERENCES "symptom_definitions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "symptom_events"
|
||||
ADD CONSTRAINT "symptom_events_episode_id_fkey"
|
||||
FOREIGN KEY ("episode_id") REFERENCES "illness_episodes"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
Reference in new issue
Block a user