-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: add uniqueness constraint to task table (#454)
* Feat: add uniqueness constraint to task table Add uniqueness constraint on tasks to prevent assigning the same benchmark (subgoal) to the same para (assignee). * Katrina feedback: more informative error message * Remove comment, code is self-explanatory * Add custom error to assignTaskToParas as well, plus spec * Tweak error message * Add error message to frontend modal * cause createPara to return para or throw rather than undefined * handle type of error in frontend
- Loading branch information
Showing
7 changed files
with
203 additions
and
19 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
src/backend/db/migrations/2_add-uniqueness-constraint-on-tasks.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
-- Each task should have a unique subgoal_id - assignee_id combination | ||
-- which corresponds to a unique benchmark / para combo | ||
ALTER TABLE task | ||
ADD CONSTRAINT subgoal_assignee_unique UNIQUE (subgoal_id, assignee_id); | ||
|
||
-- Add index to allow easy queries of tasks by assignee | ||
CREATE INDEX idx_task_assignee ON task(assignee_id); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,21 @@ test("basic flow - add/get goals, subgoals, tasks", async (t) => { | |
number_of_trials: 15, | ||
}); | ||
|
||
const subgoal1 = await trpc.iep.addSubgoal.mutate({ | ||
goal_id: goal1!.goal_id, | ||
status: "Complete", | ||
description: "subgoal 1", | ||
setup: "", | ||
instructions: "", | ||
materials: "materials", | ||
target_level: 100, | ||
baseline_level: 20, | ||
metric_name: "words", | ||
attempts_per_trial: 10, | ||
number_of_trials: 30, | ||
}); | ||
const subgoal1Id = subgoal1!.subgoal_id; | ||
|
||
const subgoal2 = await trpc.iep.addSubgoal.mutate({ | ||
goal_id: goal1!.goal_id, | ||
status: "Complete", | ||
|
@@ -51,7 +66,7 @@ test("basic flow - add/get goals, subgoals, tasks", async (t) => { | |
const subgoal2Id = subgoal2!.subgoal_id; | ||
|
||
await trpc.iep.addTask.mutate({ | ||
subgoal_id: subgoal2Id, | ||
subgoal_id: subgoal1Id, | ||
assignee_id: para_id, | ||
due_date: new Date("2023-12-31"), | ||
trial_count: 5, | ||
|
@@ -70,7 +85,7 @@ test("basic flow - add/get goals, subgoals, tasks", async (t) => { | |
const gotSubgoals = await trpc.iep.getSubgoals.query({ | ||
goal_id: goal1!.goal_id, | ||
}); | ||
t.is(gotSubgoals.length, 2); | ||
t.is(gotSubgoals.length, 3); | ||
|
||
const gotSubgoal = await trpc.iep.getSubgoal.query({ | ||
subgoal_id: subgoal2Id, | ||
|
@@ -88,6 +103,124 @@ test("basic flow - add/get goals, subgoals, tasks", async (t) => { | |
); | ||
}); | ||
|
||
test("addTask - no duplicate subgoal_id + assigned_id combo", async (t) => { | ||
const { trpc, seed } = await getTestServer(t, { | ||
authenticateAs: "case_manager", | ||
}); | ||
|
||
const para_id = seed.para.user_id; | ||
|
||
const iep = await trpc.student.addIep.mutate({ | ||
student_id: seed.student.student_id, | ||
start_date: new Date("2023-01-01"), | ||
end_date: new Date("2023-12-31"), | ||
}); | ||
|
||
const goal1 = await trpc.iep.addGoal.mutate({ | ||
iep_id: iep.iep_id, | ||
description: "goal 1", | ||
category: "writing", | ||
}); | ||
|
||
const subgoal1 = await trpc.iep.addSubgoal.mutate({ | ||
goal_id: goal1!.goal_id, | ||
status: "Complete", | ||
description: "subgoal 1", | ||
setup: "", | ||
instructions: "", | ||
materials: "materials", | ||
target_level: 100, | ||
baseline_level: 20, | ||
metric_name: "words", | ||
attempts_per_trial: 10, | ||
number_of_trials: 30, | ||
}); | ||
const subgoal1Id = subgoal1!.subgoal_id; | ||
|
||
await trpc.iep.addTask.mutate({ | ||
subgoal_id: subgoal1Id, | ||
assignee_id: para_id, | ||
due_date: new Date("2023-12-31"), | ||
trial_count: 5, | ||
}); | ||
|
||
const error = await t.throwsAsync(async () => { | ||
await trpc.iep.addTask.mutate({ | ||
subgoal_id: subgoal1Id, | ||
assignee_id: para_id, | ||
due_date: new Date("2024-03-31"), | ||
trial_count: 1, | ||
}); | ||
}); | ||
|
||
t.is( | ||
error?.message, | ||
"Task already exists: This subgoal has already been assigned to the same para" | ||
); | ||
}); | ||
|
||
test("assignTaskToParas - no duplicate subgoal_id + para_id combo", async (t) => { | ||
const { trpc, seed } = await getTestServer(t, { | ||
authenticateAs: "case_manager", | ||
}); | ||
|
||
const para_1 = seed.para; | ||
|
||
const para_2 = await trpc.para.createPara.mutate({ | ||
first_name: "Foo", | ||
last_name: "Bar", | ||
email: "[email protected]", | ||
}); | ||
|
||
const iep = await trpc.student.addIep.mutate({ | ||
student_id: seed.student.student_id, | ||
start_date: new Date("2023-01-01"), | ||
end_date: new Date("2023-12-31"), | ||
}); | ||
|
||
const goal1 = await trpc.iep.addGoal.mutate({ | ||
iep_id: iep.iep_id, | ||
description: "goal 1", | ||
category: "writing", | ||
}); | ||
|
||
const subgoal1 = await trpc.iep.addSubgoal.mutate({ | ||
goal_id: goal1!.goal_id, | ||
status: "Complete", | ||
description: "subgoal 1", | ||
setup: "", | ||
instructions: "", | ||
materials: "materials", | ||
target_level: 100, | ||
baseline_level: 20, | ||
metric_name: "words", | ||
attempts_per_trial: 10, | ||
number_of_trials: 30, | ||
}); | ||
const subgoal1Id = subgoal1!.subgoal_id; | ||
|
||
await trpc.iep.assignTaskToParas.mutate({ | ||
subgoal_id: subgoal1Id, | ||
para_ids: [para_1.user_id], | ||
due_date: new Date("2023-12-31"), | ||
trial_count: 5, | ||
}); | ||
|
||
const error = await t.throwsAsync(async () => { | ||
await trpc.iep.assignTaskToParas.mutate({ | ||
subgoal_id: subgoal1Id, | ||
para_ids: [para_1.user_id, para_2.user_id], | ||
due_date: new Date("2024-03-31"), | ||
trial_count: 1, | ||
}); | ||
}); | ||
|
||
t.is( | ||
error?.message, | ||
"Task already exists: This subgoal has already been assigned to one or more of these paras" | ||
); | ||
}); | ||
|
||
test("add benchmark - check full schema", async (t) => { | ||
const { trpc, seed } = await getTestServer(t, { | ||
authenticateAs: "case_manager", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters