eletrotupi / tcc/ commit / f17ee88

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
Pedro Lucas Porcellis porcellis@eletrotupi.com 1 month ago f17ee884c98a261ad175813d7c02467ded137778
Parents: e32e5ac
2 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 +
}