api: rework interventions from the ground up
Drop completely, and then introduce from another, more intertwined perspective: - CareActions, similar to what a intervention is, but more generalized - Medicine, MedicineRegimen, MedicineLogs: self-explanatory - Activity: Generic activity such as going outside, yoga, or doing chores - Appointment: an actual appointment that might have impacted your mental health somehow
Parents:
e32e5ac2 file(s) changed
- api/prisma/migrations/20260623031819_care_actions_and_trigger_mood_links/migration.sql +153 -0
- api/prisma/schema.prisma +144 -33
api/prisma/migrations/20260623031819_care_actions_and_trigger_mood_links/migration.sql
@@ -0,0 +1,153 @@
1 + /*
2 + Warnings:
3 +
4 + - You are about to drop the `interventions` table. If the table is not empty, all the data it contains will be lost.
5 +
6 + */
7 + -- CreateEnum
8 + CREATE TYPE "CareActionType" AS ENUM ('MEDICINE', 'APPOINTMENT', 'ACTIVITY');
9 +
10 + -- CreateEnum
11 + CREATE TYPE "AppointmentType" AS ENUM ('ANALYST', 'PSYCHIATRIST', 'GP', 'NUTRITIONIST', 'PHYSIOTHERAPIST', 'OTHER');
12 +
13 + -- CreateEnum
14 + CREATE TYPE "ActivityType" AS ENUM ('WALK', 'YOGA', 'GYM', 'MEDITATION', 'SOCIAL', 'CREATIVE', 'OTHER');
15 +
16 + -- CreateEnum
17 + CREATE TYPE "MedicinePeriodicity" AS ENUM ('ONCE', 'DAILY', 'TWICE_DAILY', 'THREE_TIMES_DAILY', 'WEEKLY', 'BIWEEKLY', 'MONTHLY');
18 +
19 + -- AlterEnum
20 + -- This migration adds more than one value to an enum.
21 + -- With PostgreSQL versions 11 and earlier, this is not possible
22 + -- in a single migration. This can be worked around by creating
23 + -- multiple migrations, each migration adding only one value to
24 + -- the enum.
25 +
26 +
27 + ALTER TYPE "TriggerType" ADD VALUE 'THERAPY';
28 + ALTER TYPE "TriggerType" ADD VALUE 'INTERNAL';
29 +
30 + -- DropForeignKey
31 + ALTER TABLE "interventions" DROP CONSTRAINT "interventions_user_id_fkey";
32 +
33 + -- AlterTable
34 + ALTER TABLE "sleep_records" ALTER COLUMN "updated_at" DROP DEFAULT;
35 +
36 + -- DropTable
37 + DROP TABLE "interventions";
38 +
39 + -- DropEnum
40 + DROP TYPE "InterventionType";
41 +
42 + -- CreateTable
43 + CREATE TABLE "trigger_mood_links" (
44 + "id" SERIAL NOT NULL,
45 + "trigger_id" INTEGER NOT NULL,
46 + "mood_id" INTEGER NOT NULL,
47 + "perceived_impact" INTEGER NOT NULL,
48 + "linked_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
49 +
50 + CONSTRAINT "trigger_mood_links_pkey" PRIMARY KEY ("id")
51 + );
52 +
53 + -- CreateTable
54 + CREATE TABLE "medicine_regimens" (
55 + "id" SERIAL NOT NULL,
56 + "name" TEXT NOT NULL,
57 + "dosage" TEXT NOT NULL,
58 + "periodicity" "MedicinePeriodicity" NOT NULL,
59 + "scheduled_at" TEXT,
60 + "active" BOOLEAN NOT NULL DEFAULT true,
61 + "user_id" INTEGER NOT NULL,
62 + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
63 + "updated_at" TIMESTAMP(3) NOT NULL,
64 +
65 + CONSTRAINT "medicine_regimens_pkey" PRIMARY KEY ("id")
66 + );
67 +
68 + -- CreateTable
69 + CREATE TABLE "care_actions" (
70 + "id" SERIAL NOT NULL,
71 + "type" "CareActionType" NOT NULL,
72 + "moment" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
73 + "user_id" INTEGER NOT NULL,
74 + "trigger_id" INTEGER,
75 + "mood_id" INTEGER,
76 + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
77 + "updated_at" TIMESTAMP(3) NOT NULL,
78 +
79 + CONSTRAINT "care_actions_pkey" PRIMARY KEY ("id")
80 + );
81 +
82 + -- CreateTable
83 + CREATE TABLE "medicine_logs" (
84 + "id" SERIAL NOT NULL,
85 + "regimen_id" INTEGER,
86 + "care_action_id" INTEGER NOT NULL,
87 + "taken_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
88 +
89 + CONSTRAINT "medicine_logs_pkey" PRIMARY KEY ("id")
90 + );
91 +
92 + -- CreateTable
93 + CREATE TABLE "appointments" (
94 + "id" SERIAL NOT NULL,
95 + "care_action_id" INTEGER NOT NULL,
96 + "type" "AppointmentType" NOT NULL,
97 + "duration" INTEGER NOT NULL,
98 + "note" TEXT,
99 +
100 + CONSTRAINT "appointments_pkey" PRIMARY KEY ("id")
101 + );
102 +
103 + -- CreateTable
104 + CREATE TABLE "activities" (
105 + "id" SERIAL NOT NULL,
106 + "care_action_id" INTEGER NOT NULL,
107 + "type" "ActivityType" NOT NULL,
108 + "duration" INTEGER,
109 +
110 + CONSTRAINT "activities_pkey" PRIMARY KEY ("id")
111 + );
112 +
113 + -- CreateIndex
114 + CREATE UNIQUE INDEX "trigger_mood_links_trigger_id_mood_id_key" ON "trigger_mood_links"("trigger_id", "mood_id");
115 +
116 + -- CreateIndex
117 + CREATE UNIQUE INDEX "medicine_logs_care_action_id_key" ON "medicine_logs"("care_action_id");
118 +
119 + -- CreateIndex
120 + CREATE UNIQUE INDEX "appointments_care_action_id_key" ON "appointments"("care_action_id");
121 +
122 + -- CreateIndex
123 + CREATE UNIQUE INDEX "activities_care_action_id_key" ON "activities"("care_action_id");
124 +
125 + -- AddForeignKey
126 + ALTER TABLE "trigger_mood_links" ADD CONSTRAINT "trigger_mood_links_trigger_id_fkey" FOREIGN KEY ("trigger_id") REFERENCES "triggers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
127 +
128 + -- AddForeignKey
129 + ALTER TABLE "trigger_mood_links" ADD CONSTRAINT "trigger_mood_links_mood_id_fkey" FOREIGN KEY ("mood_id") REFERENCES "moods"("id") ON DELETE CASCADE ON UPDATE CASCADE;
130 +
131 + -- AddForeignKey
132 + ALTER TABLE "medicine_regimens" ADD CONSTRAINT "medicine_regimens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
133 +
134 + -- AddForeignKey
135 + ALTER TABLE "care_actions" ADD CONSTRAINT "care_actions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
136 +
137 + -- AddForeignKey
138 + ALTER TABLE "care_actions" ADD CONSTRAINT "care_actions_trigger_id_fkey" FOREIGN KEY ("trigger_id") REFERENCES "triggers"("id") ON DELETE SET NULL ON UPDATE CASCADE;
139 +
140 + -- AddForeignKey
141 + ALTER TABLE "care_actions" ADD CONSTRAINT "care_actions_mood_id_fkey" FOREIGN KEY ("mood_id") REFERENCES "moods"("id") ON DELETE SET NULL ON UPDATE CASCADE;
142 +
143 + -- AddForeignKey
144 + ALTER TABLE "medicine_logs" ADD CONSTRAINT "medicine_logs_regimen_id_fkey" FOREIGN KEY ("regimen_id") REFERENCES "medicine_regimens"("id") ON DELETE SET NULL ON UPDATE CASCADE;
145 +
146 + -- AddForeignKey
147 + ALTER TABLE "medicine_logs" ADD CONSTRAINT "medicine_logs_care_action_id_fkey" FOREIGN KEY ("care_action_id") REFERENCES "care_actions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
148 +
149 + -- AddForeignKey
150 + ALTER TABLE "appointments" ADD CONSTRAINT "appointments_care_action_id_fkey" FOREIGN KEY ("care_action_id") REFERENCES "care_actions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
151 +
152 + -- AddForeignKey
153 + ALTER TABLE "activities" ADD CONSTRAINT "activities_care_action_id_fkey" FOREIGN KEY ("care_action_id") REFERENCES "care_actions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
api/prisma/schema.prisma
1 1
generator client {
2 2
provider = "prisma-client-js"
3 3
//output = "../src/generated/prisma"
4 4
}
5 5
▸ 18 unchanged lines
24 24
activationCodeExpiresAt DateTime? @map("activation_code_expires_at")
25 25
active Boolean @default(false) @map("active")
26 26
27 27
pushToken String? @map("push_token")
28 28
29 -
moods Mood[]
30 -
interventions Intervention[]
31 -
triggers Trigger[]
32 -
reminders Reminder[]
33 -
sleepRecords SleepRecord[]
34 -
insights Insight[]
29 +
moods Mood[]
30 +
triggers Trigger[]
31 +
reminders Reminder[]
32 +
sleepRecords SleepRecord[]
33 +
insights Insight[]
34 +
medicineRegimens MedicineRegimen[]
35 +
careActions CareAction[]
35 36
36 37
@@map("users")
37 38
}
38 39
39 40
model Mood {
▸ 6 unchanged lines
46 47
energyLevel Int @map("energy_level")
47 48
userId Int @map("user_id")
48 49
user User @relation(fields: [userId], references: [id])
49 50
50 51
moodComponents MoodComponent[]
52 +
triggerLinks TriggerMoodLink[]
53 +
careActions CareAction[]
51 54
52 55
@@map("moods")
53 56
}
54 57
55 58
model MoodComponent {
▸ 5 unchanged lines
61 64
mood Mood @relation(fields: [moodId], references: [id], onDelete: Cascade)
62 65
63 66
@@map("mood_components")
64 67
}
65 68
66 -
model Intervention {
67 -
id Int @id @default(autoincrement())
68 -
comment String?
69 -
interventionType InterventionType @map("intervention_type")
70 -
eficacy Int
71 -
startAt DateTime @map("start_at")
72 -
endAt DateTime @map("end_at")
73 -
74 -
userId Int @map("user_id")
75 -
user User @relation(fields: [userId], references: [id])
76 -
77 -
@@map("interventions")
78 -
}
79 -
80 69
model Trigger {
81 70
id Int @id @default(autoincrement())
82 71
comment String? @db.Text
83 72
category TriggerType
84 73
moment DateTime @default(now())
85 74
86 75
userId Int @map("user_id")
87 76
user User @relation(fields: [userId], references: [id])
88 77
89 -
createdAt DateTime @default(now()) @map("created_at")
90 -
updatedAt DateTime @updatedAt @map("updated_at")
78 +
createdAt DateTime @default(now()) @map("created_at")
79 +
updatedAt DateTime @updatedAt @map("updated_at")
80 +
81 +
moodLinks TriggerMoodLink[]
82 +
careActions CareAction[]
91 83
92 84
@@map("triggers")
93 85
}
94 86
87 +
model TriggerMoodLink {
88 +
id Int @id @default(autoincrement())
89 +
triggerId Int @map("trigger_id")
90 +
moodId Int @map("mood_id")
91 +
perceivedImpact Int @map("perceived_impact")
92 +
linkedAt DateTime @default(now()) @map("linked_at")
93 +
94 +
trigger Trigger @relation(fields: [triggerId], references: [id], onDelete: Cascade)
95 +
mood Mood @relation(fields: [moodId], references: [id], onDelete: Cascade)
96 +
97 +
@@unique([triggerId, moodId])
98 +
@@map("trigger_mood_links")
99 +
}
100 +
101 +
model MedicineRegimen {
102 +
id Int @id @default(autoincrement())
103 +
name String
104 +
dosage String
105 +
periodicity MedicinePeriodicity
106 +
scheduledAt String? @map("scheduled_at")
107 +
active Boolean @default(true)
108 +
userId Int @map("user_id")
109 +
110 +
user User @relation(fields: [userId], references: [id])
111 +
medicineLogs MedicineLog[]
112 +
113 +
createdAt DateTime @default(now()) @map("created_at")
114 +
updatedAt DateTime @updatedAt @map("updated_at")
115 +
116 +
@@map("medicine_regimens")
117 +
}
118 +
119 +
model CareAction {
120 +
id Int @id @default(autoincrement())
121 +
type CareActionType
122 +
moment DateTime @default(now())
123 +
userId Int @map("user_id")
124 +
125 +
triggerId Int? @map("trigger_id")
126 +
moodId Int? @map("mood_id")
127 +
128 +
user User @relation(fields: [userId], references: [id])
129 +
trigger Trigger? @relation(fields: [triggerId], references: [id])
130 +
mood Mood? @relation(fields: [moodId], references: [id])
131 +
132 +
medicineLog MedicineLog?
133 +
appointment Appointment?
134 +
activity Activity?
135 +
136 +
createdAt DateTime @default(now()) @map("created_at")
137 +
updatedAt DateTime @updatedAt @map("updated_at")
138 +
139 +
@@map("care_actions")
140 +
}
141 +
142 +
model MedicineLog {
143 +
id Int @id @default(autoincrement())
144 +
regimenId Int? @map("regimen_id")
145 +
careActionId Int @unique @map("care_action_id")
146 +
takenAt DateTime @default(now()) @map("taken_at")
147 +
148 +
regimen MedicineRegimen? @relation(fields: [regimenId], references: [id])
149 +
careAction CareAction @relation(fields: [careActionId], references: [id], onDelete: Cascade)
150 +
151 +
@@map("medicine_logs")
152 +
}
153 +
154 +
model Appointment {
155 +
id Int @id @default(autoincrement())
156 +
careActionId Int @unique @map("care_action_id")
157 +
type AppointmentType
158 +
duration Int
159 +
note String? @db.Text
160 +
161 +
careAction CareAction @relation(fields: [careActionId], references: [id], onDelete: Cascade)
162 +
163 +
@@map("appointments")
164 +
}
165 +
166 +
model Activity {
167 +
id Int @id @default(autoincrement())
168 +
careActionId Int @unique @map("care_action_id")
169 +
type ActivityType
170 +
duration Int?
171 +
172 +
careAction CareAction @relation(fields: [careActionId], references: [id], onDelete: Cascade)
173 +
174 +
@@map("activities")
175 +
}
176 +
95 177
model Reminder {
96 178
id Int @id @default(autoincrement())
97 179
hour Int
98 180
minute Int
99 181
description String?
▸ 12 unchanged lines
112 194
annotations String? @db.Text
113 195
114 196
userId Int @map("user_id")
115 197
user User @relation(fields: [userId], references: [id])
116 198
117 -
createdAt DateTime @default(now()) @map("created_at")
118 -
updatedAt DateTime @updatedAt @map("updated_at")
199 +
createdAt DateTime @default(now()) @map("created_at")
200 +
updatedAt DateTime @updatedAt @map("updated_at")
119 201
120 202
@@map("sleep_records")
121 203
}
122 204
123 205
model Insight {
▸ 3 unchanged lines
127 209
128 210
type InsightType
129 211
period InsightPeriod
130 212
title String
131 213
body String @db.Text
132 -
metadata Json? // flexible payload: correlations, deltas, scores
214 +
metadata Json?
133 215
generatedAt DateTime @default(now()) @map("generated_at")
134 216
periodStart DateTime @map("period_start")
135 217
periodEnd DateTime @map("period_end")
136 218
137 219
@@unique([userId, type, periodStart])
▸ 20 unchanged lines
158 240
LIGHT
159 241
MODERATE
160 242
HIGH
161 243
}
162 244
163 -
enum InterventionType {
164 -
MEDICATION
165 -
THERAPY
166 -
MEDITATION
167 -
EXERCISE
168 -
CONVERSATION
169 -
}
170 -
171 245
enum TriggerType {
172 246
SOCIAL
173 247
WORK
174 248
HEALTH
175 249
PHYSICAL
176 250
FAMILY
251 +
THERAPY
252 +
INTERNAL
177 253
OTHER
178 254
}
179 255
180 256
// XXX: Keep it in sync with constants/moods.ts
181 257
enum BaseMoodOption {
▸ 19 unchanged lines
201 277
FOCUS
202 278
RESTLESS
203 279
RELAXED
204 280
OVERWHELMED
205 281
}
282 +
283 +
enum CareActionType {
284 +
MEDICINE
285 +
APPOINTMENT
286 +
ACTIVITY
287 +
}
288 +
289 +
enum AppointmentType {
290 +
ANALYST
291 +
PSYCHIATRIST
292 +
GP
293 +
NUTRITIONIST
294 +
PHYSIOTHERAPIST
295 +
OTHER
296 +
}
297 +
298 +
enum ActivityType {
299 +
WALK
300 +
YOGA
301 +
GYM
302 +
MEDITATION
303 +
SOCIAL
304 +
CREATIVE
305 +
OTHER
306 +
}
307 +
308 +
enum MedicinePeriodicity {
309 +
ONCE
310 +
DAILY
311 +
TWICE_DAILY
312 +
THREE_TIMES_DAILY
313 +
WEEKLY
314 +
BIWEEKLY
315 +
MONTHLY
316 +
}