@studious-lms/server 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/dist/index.js +40 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/config/cors.d.ts.map +1 -1
- package/dist/lib/config/cors.js +7 -2
- package/dist/lib/config/cors.js.map +1 -1
- package/dist/lib/config/env.d.ts +8 -0
- package/dist/lib/config/env.d.ts.map +1 -1
- package/dist/lib/config/env.js +5 -2
- package/dist/lib/config/env.js.map +1 -1
- package/dist/lib/pusher.d.ts +2 -1
- package/dist/lib/pusher.d.ts.map +1 -1
- package/dist/lib/pusher.js +4 -3
- package/dist/lib/pusher.js.map +1 -1
- package/dist/models/agenda.d.ts +6 -0
- package/dist/models/agenda.d.ts.map +1 -1
- package/dist/models/announcement.d.ts +3 -0
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +61 -0
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/auth.d.ts +6 -0
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/class.d.ts +28 -0
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/class.js +12 -3
- package/dist/models/class.js.map +1 -1
- package/dist/models/comment.d.ts +13 -0
- package/dist/models/comment.d.ts.map +1 -1
- package/dist/models/comment.js +3 -2
- package/dist/models/comment.js.map +1 -1
- package/dist/models/conversation.d.ts +17 -0
- package/dist/models/conversation.d.ts.map +1 -1
- package/dist/models/conversation.js +13 -2
- package/dist/models/conversation.js.map +1 -1
- package/dist/models/event.d.ts +12 -0
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/file.d.ts +38 -0
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/folder.d.ts +6 -0
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +6 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/newtonChat.d.ts +3 -0
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/models/school.d.ts +3 -0
- package/dist/models/school.d.ts.map +1 -1
- package/dist/models/section.d.ts +3 -0
- package/dist/models/section.d.ts.map +1 -1
- package/dist/models/worksheet.d.ts +62 -0
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/models/worksheet.js +37 -7
- package/dist/models/worksheet.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/pipelines/gradeWorksheet.js +59 -14
- package/dist/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/routers/_app.d.ts +4641 -2285
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +6 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/admin.d.ts +190 -0
- package/dist/routers/admin.d.ts.map +1 -0
- package/dist/routers/admin.js +50 -0
- package/dist/routers/admin.js.map +1 -0
- package/dist/routers/agenda.d.ts +6 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/announcement.d.ts +6 -0
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +6 -5
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +58 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +4 -4
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/class.d.ts +54 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +5 -2
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/comment.d.ts +26 -0
- package/dist/routers/comment.d.ts.map +1 -1
- package/dist/routers/comment.js +6 -3
- package/dist/routers/comment.js.map +1 -1
- package/dist/routers/discussion.d.ts +823 -0
- package/dist/routers/discussion.d.ts.map +1 -0
- package/dist/routers/discussion.js +151 -0
- package/dist/routers/discussion.js.map +1 -0
- package/dist/routers/event.d.ts +3 -0
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/file.d.ts +4 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/school.d.ts +6 -0
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/worksheet.d.ts +6 -0
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +4 -2
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +1490 -1307
- package/dist/seedDatabase.js.map +1 -1
- package/dist/services/adminInstitution.d.ts +102 -0
- package/dist/services/adminInstitution.d.ts.map +1 -0
- package/dist/services/adminInstitution.js +117 -0
- package/dist/services/adminInstitution.js.map +1 -0
- package/dist/services/adminRunJob.d.ts +45 -0
- package/dist/services/adminRunJob.d.ts.map +1 -0
- package/dist/services/adminRunJob.js +41 -0
- package/dist/services/adminRunJob.js.map +1 -0
- package/dist/services/agenda.d.ts +6 -0
- package/dist/services/agenda.d.ts.map +1 -1
- package/dist/services/assignment.d.ts +50 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +16 -9
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/class.d.ts +54 -0
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +20 -3
- package/dist/services/class.js.map +1 -1
- package/dist/services/comment.d.ts +20 -0
- package/dist/services/comment.d.ts.map +1 -1
- package/dist/services/comment.js +23 -2
- package/dist/services/comment.js.map +1 -1
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/conversation.js +9 -7
- package/dist/services/conversation.js.map +1 -1
- package/dist/services/discussion.d.ts +762 -0
- package/dist/services/discussion.d.ts.map +1 -0
- package/dist/services/discussion.js +625 -0
- package/dist/services/discussion.js.map +1 -0
- package/dist/services/event.d.ts +3 -0
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/file.d.ts +8 -0
- package/dist/services/file.d.ts.map +1 -1
- package/dist/services/school.d.ts +6 -0
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/studentProgress.d.ts.map +1 -1
- package/dist/services/studentProgress.js +27 -5
- package/dist/services/studentProgress.js.map +1 -1
- package/dist/services/studentProgressInferenceCache.d.ts +16 -0
- package/dist/services/studentProgressInferenceCache.d.ts.map +1 -0
- package/dist/services/studentProgressInferenceCache.js +43 -0
- package/dist/services/studentProgressInferenceCache.js.map +1 -0
- package/dist/services/worksheet.d.ts +6 -0
- package/dist/services/worksheet.d.ts.map +1 -1
- package/dist/services/worksheet.js +5 -5
- package/dist/services/worksheet.js.map +1 -1
- package/dist/trpc.d.ts +12 -0
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +16 -3
- package/dist/trpc.js.map +1 -1
- package/dist/utils/adminPanelAccess.d.ts +8 -0
- package/dist/utils/adminPanelAccess.d.ts.map +1 -0
- package/dist/utils/adminPanelAccess.js +44 -0
- package/dist/utils/adminPanelAccess.js.map +1 -0
- package/package.json +1 -1
- package/prisma/migrations/20260510183000_add_discussions/migration.sql +151 -0
- package/prisma/schema.prisma +98 -0
- package/src/index.ts +39 -2
- package/src/lib/config/cors.ts +5 -0
- package/src/lib/config/env.ts +3 -0
- package/src/lib/pusher.ts +2 -1
- package/src/models/class.ts +10 -1
- package/src/models/comment.ts +1 -0
- package/src/models/conversation.ts +12 -0
- package/src/models/worksheet.ts +39 -5
- package/src/pipelines/gradeWorksheet.ts +66 -15
- package/src/routers/_app.ts +4 -0
- package/src/routers/admin.ts +70 -0
- package/src/routers/announcement.ts +4 -3
- package/src/routers/assignment.ts +2 -2
- package/src/routers/class.ts +3 -0
- package/src/routers/comment.ts +5 -0
- package/src/routers/discussion.ts +205 -0
- package/src/routers/worksheet.ts +2 -0
- package/src/seedDatabase.ts +1713 -1361
- package/src/services/adminInstitution.ts +140 -0
- package/src/services/adminRunJob.ts +46 -0
- package/src/services/assignment.ts +19 -8
- package/src/services/class.ts +28 -1
- package/src/services/comment.ts +22 -0
- package/src/services/conversation.ts +9 -4
- package/src/services/discussion.ts +701 -0
- package/src/services/studentProgress.ts +80 -3
- package/src/services/studentProgressInferenceCache.ts +64 -0
- package/src/services/worksheet.ts +5 -2
- package/src/trpc.ts +15 -0
- package/src/utils/adminPanelAccess.ts +46 -0
- package/tests/integration/routers/conversation.test.ts +9 -7
- package/tests/integration/routers/discussion.test.ts +253 -0
- package/tests/unit/services/discussion.test.ts +356 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "DiscussionType" AS ENUM ('DISCUSSION', 'QUESTION', 'ANNOUNCEMENT');
|
|
3
|
+
|
|
4
|
+
-- AlterTable
|
|
5
|
+
ALTER TABLE "Class" ADD COLUMN "discussionsEnabled" BOOLEAN NOT NULL DEFAULT true;
|
|
6
|
+
ALTER TABLE "Class" ADD COLUMN "studentsCanCreateDiscussions" BOOLEAN NOT NULL DEFAULT true;
|
|
7
|
+
ALTER TABLE "Class" ADD COLUMN "studentsCanReply" BOOLEAN NOT NULL DEFAULT true;
|
|
8
|
+
|
|
9
|
+
-- AlterTable
|
|
10
|
+
ALTER TABLE "File" ADD COLUMN "discussionId" TEXT;
|
|
11
|
+
ALTER TABLE "File" ADD COLUMN "discussionReplyId" TEXT;
|
|
12
|
+
|
|
13
|
+
-- AlterTable
|
|
14
|
+
ALTER TABLE "Reaction" ADD COLUMN "discussionId" TEXT;
|
|
15
|
+
ALTER TABLE "Reaction" ADD COLUMN "discussionReplyId" TEXT;
|
|
16
|
+
|
|
17
|
+
-- CreateTable
|
|
18
|
+
CREATE TABLE "Discussion" (
|
|
19
|
+
"id" TEXT NOT NULL,
|
|
20
|
+
"classId" TEXT NOT NULL,
|
|
21
|
+
"authorId" TEXT NOT NULL,
|
|
22
|
+
"title" TEXT NOT NULL,
|
|
23
|
+
"body" TEXT NOT NULL,
|
|
24
|
+
"type" "DiscussionType" NOT NULL DEFAULT 'DISCUSSION',
|
|
25
|
+
"isPinned" BOOLEAN NOT NULL DEFAULT false,
|
|
26
|
+
"isLocked" BOOLEAN NOT NULL DEFAULT false,
|
|
27
|
+
"allowReplies" BOOLEAN NOT NULL DEFAULT true,
|
|
28
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
30
|
+
"lastActivityAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
31
|
+
|
|
32
|
+
CONSTRAINT "Discussion_pkey" PRIMARY KEY ("id")
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- CreateTable
|
|
36
|
+
CREATE TABLE "DiscussionReply" (
|
|
37
|
+
"id" TEXT NOT NULL,
|
|
38
|
+
"discussionId" TEXT NOT NULL,
|
|
39
|
+
"authorId" TEXT NOT NULL,
|
|
40
|
+
"parentId" TEXT,
|
|
41
|
+
"body" TEXT NOT NULL,
|
|
42
|
+
"isAcceptedAnswer" BOOLEAN NOT NULL DEFAULT false,
|
|
43
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
44
|
+
"modifiedAt" TIMESTAMP(3),
|
|
45
|
+
"deletedAt" TIMESTAMP(3),
|
|
46
|
+
|
|
47
|
+
CONSTRAINT "DiscussionReply_pkey" PRIMARY KEY ("id")
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
-- CreateTable
|
|
51
|
+
CREATE TABLE "DiscussionTag" (
|
|
52
|
+
"id" TEXT NOT NULL,
|
|
53
|
+
"classId" TEXT NOT NULL,
|
|
54
|
+
"name" TEXT NOT NULL,
|
|
55
|
+
"color" TEXT NOT NULL DEFAULT '#3B82F6',
|
|
56
|
+
|
|
57
|
+
CONSTRAINT "DiscussionTag_pkey" PRIMARY KEY ("id")
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
-- CreateTable
|
|
61
|
+
CREATE TABLE "DiscussionRead" (
|
|
62
|
+
"userId" TEXT NOT NULL,
|
|
63
|
+
"discussionId" TEXT NOT NULL,
|
|
64
|
+
"lastReadAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
65
|
+
|
|
66
|
+
CONSTRAINT "DiscussionRead_pkey" PRIMARY KEY ("userId","discussionId")
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
-- CreateTable
|
|
70
|
+
CREATE TABLE "_DiscussionToTag" (
|
|
71
|
+
"A" TEXT NOT NULL,
|
|
72
|
+
"B" TEXT NOT NULL
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
-- CreateIndex
|
|
76
|
+
CREATE INDEX "Discussion_classId_isPinned_lastActivityAt_idx" ON "Discussion"("classId", "isPinned", "lastActivityAt");
|
|
77
|
+
|
|
78
|
+
-- CreateIndex
|
|
79
|
+
CREATE INDEX "Discussion_classId_createdAt_idx" ON "Discussion"("classId", "createdAt");
|
|
80
|
+
|
|
81
|
+
-- CreateIndex
|
|
82
|
+
CREATE INDEX "DiscussionReply_discussionId_createdAt_idx" ON "DiscussionReply"("discussionId", "createdAt");
|
|
83
|
+
|
|
84
|
+
-- CreateIndex
|
|
85
|
+
CREATE INDEX "DiscussionReply_parentId_idx" ON "DiscussionReply"("parentId");
|
|
86
|
+
|
|
87
|
+
-- CreateIndex
|
|
88
|
+
CREATE UNIQUE INDEX "DiscussionTag_classId_name_key" ON "DiscussionTag"("classId", "name");
|
|
89
|
+
|
|
90
|
+
-- CreateIndex
|
|
91
|
+
CREATE INDEX "DiscussionRead_userId_idx" ON "DiscussionRead"("userId");
|
|
92
|
+
|
|
93
|
+
-- CreateIndex
|
|
94
|
+
CREATE UNIQUE INDEX "_DiscussionToTag_AB_unique" ON "_DiscussionToTag"("A", "B");
|
|
95
|
+
|
|
96
|
+
-- CreateIndex
|
|
97
|
+
CREATE INDEX "_DiscussionToTag_B_index" ON "_DiscussionToTag"("B");
|
|
98
|
+
|
|
99
|
+
-- CreateIndex
|
|
100
|
+
CREATE UNIQUE INDEX "Reaction_userId_discussionId_key" ON "Reaction"("userId", "discussionId");
|
|
101
|
+
|
|
102
|
+
-- CreateIndex
|
|
103
|
+
CREATE UNIQUE INDEX "Reaction_userId_discussionReplyId_key" ON "Reaction"("userId", "discussionReplyId");
|
|
104
|
+
|
|
105
|
+
-- CreateIndex
|
|
106
|
+
CREATE INDEX "Reaction_discussionId_idx" ON "Reaction"("discussionId");
|
|
107
|
+
|
|
108
|
+
-- CreateIndex
|
|
109
|
+
CREATE INDEX "Reaction_discussionReplyId_idx" ON "Reaction"("discussionReplyId");
|
|
110
|
+
|
|
111
|
+
-- AddForeignKey
|
|
112
|
+
ALTER TABLE "File" ADD CONSTRAINT "File_discussionId_fkey" FOREIGN KEY ("discussionId") REFERENCES "Discussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
113
|
+
|
|
114
|
+
-- AddForeignKey
|
|
115
|
+
ALTER TABLE "File" ADD CONSTRAINT "File_discussionReplyId_fkey" FOREIGN KEY ("discussionReplyId") REFERENCES "DiscussionReply"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
116
|
+
|
|
117
|
+
-- AddForeignKey
|
|
118
|
+
ALTER TABLE "Discussion" ADD CONSTRAINT "Discussion_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
119
|
+
|
|
120
|
+
-- AddForeignKey
|
|
121
|
+
ALTER TABLE "Discussion" ADD CONSTRAINT "Discussion_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
122
|
+
|
|
123
|
+
-- AddForeignKey
|
|
124
|
+
ALTER TABLE "DiscussionReply" ADD CONSTRAINT "DiscussionReply_discussionId_fkey" FOREIGN KEY ("discussionId") REFERENCES "Discussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
125
|
+
|
|
126
|
+
-- AddForeignKey
|
|
127
|
+
ALTER TABLE "DiscussionReply" ADD CONSTRAINT "DiscussionReply_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
128
|
+
|
|
129
|
+
-- AddForeignKey
|
|
130
|
+
ALTER TABLE "DiscussionReply" ADD CONSTRAINT "DiscussionReply_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "DiscussionReply"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
131
|
+
|
|
132
|
+
-- AddForeignKey
|
|
133
|
+
ALTER TABLE "DiscussionTag" ADD CONSTRAINT "DiscussionTag_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
134
|
+
|
|
135
|
+
-- AddForeignKey
|
|
136
|
+
ALTER TABLE "DiscussionRead" ADD CONSTRAINT "DiscussionRead_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
137
|
+
|
|
138
|
+
-- AddForeignKey
|
|
139
|
+
ALTER TABLE "DiscussionRead" ADD CONSTRAINT "DiscussionRead_discussionId_fkey" FOREIGN KEY ("discussionId") REFERENCES "Discussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
140
|
+
|
|
141
|
+
-- AddForeignKey
|
|
142
|
+
ALTER TABLE "Reaction" ADD CONSTRAINT "Reaction_discussionId_fkey" FOREIGN KEY ("discussionId") REFERENCES "Discussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
143
|
+
|
|
144
|
+
-- AddForeignKey
|
|
145
|
+
ALTER TABLE "Reaction" ADD CONSTRAINT "Reaction_discussionReplyId_fkey" FOREIGN KEY ("discussionReplyId") REFERENCES "DiscussionReply"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
146
|
+
|
|
147
|
+
-- AddForeignKey
|
|
148
|
+
ALTER TABLE "_DiscussionToTag" ADD CONSTRAINT "_DiscussionToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "Discussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
149
|
+
|
|
150
|
+
-- AddForeignKey
|
|
151
|
+
ALTER TABLE "_DiscussionToTag" ADD CONSTRAINT "_DiscussionToTag_B_fkey" FOREIGN KEY ("B") REFERENCES "DiscussionTag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
package/prisma/schema.prisma
CHANGED
|
@@ -84,6 +84,12 @@ enum ReactionType {
|
|
|
84
84
|
HAPPY
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
enum DiscussionType {
|
|
88
|
+
DISCUSSION
|
|
89
|
+
QUESTION
|
|
90
|
+
ANNOUNCEMENT
|
|
91
|
+
}
|
|
92
|
+
|
|
87
93
|
enum SubmissionRecommendationState {
|
|
88
94
|
NONE
|
|
89
95
|
OPEN
|
|
@@ -227,6 +233,9 @@ model User {
|
|
|
227
233
|
createdLabChats LabChat[] @relation("CreatedLabChats")
|
|
228
234
|
comments Comment[]
|
|
229
235
|
reactions Reaction[]
|
|
236
|
+
discussions Discussion[]
|
|
237
|
+
discussionReplies DiscussionReply[]
|
|
238
|
+
discussionReads DiscussionRead[]
|
|
230
239
|
}
|
|
231
240
|
|
|
232
241
|
model SchoolMembership {
|
|
@@ -311,6 +320,12 @@ model Class {
|
|
|
311
320
|
departmentId String?
|
|
312
321
|
tags Tag[] @relation("ClassTags")
|
|
313
322
|
permissions Permission[] @relation("ClassPermissions")
|
|
323
|
+
discussions Discussion[]
|
|
324
|
+
discussionTags DiscussionTag[]
|
|
325
|
+
|
|
326
|
+
discussionsEnabled Boolean @default(true)
|
|
327
|
+
studentsCanCreateDiscussions Boolean @default(true)
|
|
328
|
+
studentsCanReply Boolean @default(true)
|
|
314
329
|
|
|
315
330
|
@@index([schoolId])
|
|
316
331
|
@@index([departmentId])
|
|
@@ -422,6 +437,12 @@ model File {
|
|
|
422
437
|
announcement Announcement? @relation("AnnouncementAttachments", fields: [announcementId], references: [id], onDelete: Cascade)
|
|
423
438
|
announcementId String?
|
|
424
439
|
|
|
440
|
+
discussion Discussion? @relation("DiscussionFiles", fields: [discussionId], references: [id], onDelete: Cascade)
|
|
441
|
+
discussionId String?
|
|
442
|
+
|
|
443
|
+
discussionReply DiscussionReply? @relation("DiscussionReplyFiles", fields: [discussionReplyId], references: [id], onDelete: Cascade)
|
|
444
|
+
discussionReplyId String?
|
|
445
|
+
|
|
425
446
|
schools School[] @relation("SchoolLogo")
|
|
426
447
|
|
|
427
448
|
schoolDevelopementProgram SchoolDevelopementProgram? @relation("SchoolDevelopementProgramSupportingDocumentation", fields: [schoolDevelopementProgramId], references: [id], onDelete: Cascade)
|
|
@@ -498,6 +519,7 @@ model Comment {
|
|
|
498
519
|
parentCommentId String?
|
|
499
520
|
replies Comment[] @relation("CommentReplies")
|
|
500
521
|
reactions Reaction[]
|
|
522
|
+
isPinned Boolean @default(false)
|
|
501
523
|
createdAt DateTime @default(now())
|
|
502
524
|
modifiedAt DateTime? @updatedAt
|
|
503
525
|
studentQuestionProgress StudentQuestionProgress? @relation("StudentQuestionProgressComments", fields: [studentQuestionProgressId], references: [id], onDelete: Cascade)
|
|
@@ -507,6 +529,74 @@ model Comment {
|
|
|
507
529
|
@@index([submissionId])
|
|
508
530
|
}
|
|
509
531
|
|
|
532
|
+
model Discussion {
|
|
533
|
+
id String @id @default(uuid())
|
|
534
|
+
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
535
|
+
classId String
|
|
536
|
+
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
537
|
+
authorId String
|
|
538
|
+
title String
|
|
539
|
+
body String
|
|
540
|
+
type DiscussionType @default(DISCUSSION)
|
|
541
|
+
isPinned Boolean @default(false)
|
|
542
|
+
isLocked Boolean @default(false)
|
|
543
|
+
allowReplies Boolean @default(true)
|
|
544
|
+
attachments File[] @relation("DiscussionFiles")
|
|
545
|
+
tags DiscussionTag[] @relation("DiscussionToTag")
|
|
546
|
+
replies DiscussionReply[]
|
|
547
|
+
reactions Reaction[]
|
|
548
|
+
reads DiscussionRead[]
|
|
549
|
+
createdAt DateTime @default(now())
|
|
550
|
+
updatedAt DateTime @updatedAt
|
|
551
|
+
lastActivityAt DateTime @default(now())
|
|
552
|
+
|
|
553
|
+
@@index([classId, isPinned, lastActivityAt])
|
|
554
|
+
@@index([classId, createdAt])
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
model DiscussionReply {
|
|
558
|
+
id String @id @default(uuid())
|
|
559
|
+
discussion Discussion @relation(fields: [discussionId], references: [id], onDelete: Cascade)
|
|
560
|
+
discussionId String
|
|
561
|
+
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
562
|
+
authorId String
|
|
563
|
+
parent DiscussionReply? @relation("ReplyTree", fields: [parentId], references: [id], onDelete: Cascade)
|
|
564
|
+
parentId String?
|
|
565
|
+
children DiscussionReply[] @relation("ReplyTree")
|
|
566
|
+
body String
|
|
567
|
+
attachments File[] @relation("DiscussionReplyFiles")
|
|
568
|
+
reactions Reaction[]
|
|
569
|
+
isAcceptedAnswer Boolean @default(false)
|
|
570
|
+
createdAt DateTime @default(now())
|
|
571
|
+
modifiedAt DateTime? @updatedAt
|
|
572
|
+
deletedAt DateTime?
|
|
573
|
+
|
|
574
|
+
@@index([discussionId, createdAt])
|
|
575
|
+
@@index([parentId])
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
model DiscussionTag {
|
|
579
|
+
id String @id @default(uuid())
|
|
580
|
+
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
581
|
+
classId String
|
|
582
|
+
name String
|
|
583
|
+
color String @default("#3B82F6")
|
|
584
|
+
discussions Discussion[] @relation("DiscussionToTag")
|
|
585
|
+
|
|
586
|
+
@@unique([classId, name])
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
model DiscussionRead {
|
|
590
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
591
|
+
userId String
|
|
592
|
+
discussion Discussion @relation(fields: [discussionId], references: [id], onDelete: Cascade)
|
|
593
|
+
discussionId String
|
|
594
|
+
lastReadAt DateTime @default(now())
|
|
595
|
+
|
|
596
|
+
@@id([userId, discussionId])
|
|
597
|
+
@@index([userId])
|
|
598
|
+
}
|
|
599
|
+
|
|
510
600
|
model Reaction {
|
|
511
601
|
id String @id @default(uuid())
|
|
512
602
|
type ReactionType
|
|
@@ -516,12 +606,20 @@ model Reaction {
|
|
|
516
606
|
announcementId String?
|
|
517
607
|
comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
|
518
608
|
commentId String?
|
|
609
|
+
discussion Discussion? @relation(fields: [discussionId], references: [id], onDelete: Cascade)
|
|
610
|
+
discussionId String?
|
|
611
|
+
discussionReply DiscussionReply? @relation(fields: [discussionReplyId], references: [id], onDelete: Cascade)
|
|
612
|
+
discussionReplyId String?
|
|
519
613
|
createdAt DateTime @default(now())
|
|
520
614
|
|
|
521
615
|
@@unique([userId, announcementId])
|
|
522
616
|
@@unique([userId, commentId])
|
|
617
|
+
@@unique([userId, discussionId])
|
|
618
|
+
@@unique([userId, discussionReplyId])
|
|
523
619
|
@@index([announcementId])
|
|
524
620
|
@@index([commentId])
|
|
621
|
+
@@index([discussionId])
|
|
622
|
+
@@index([discussionReplyId])
|
|
525
623
|
}
|
|
526
624
|
|
|
527
625
|
model Submission {
|
package/src/index.ts
CHANGED
|
@@ -58,7 +58,8 @@ app.use(cors({
|
|
|
58
58
|
},
|
|
59
59
|
credentials: true,
|
|
60
60
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
61
|
-
allowedHeaders
|
|
61
|
+
// Omit allowedHeaders so `cors` echoes `Access-Control-Request-Headers` on preflight.
|
|
62
|
+
// A fixed list breaks browsers that add headers (e.g. Sentry `baggage`, `sentry-trace`).
|
|
62
63
|
preflightContinue: false,
|
|
63
64
|
optionsSuccessStatus: 204,
|
|
64
65
|
}));
|
|
@@ -221,6 +222,38 @@ app.post('/api/pusher/auth', async (req, res) => {
|
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
|
|
225
|
+
if (channel_name.startsWith('private-class-') && channel_name.endsWith('-discussions')) {
|
|
226
|
+
const classId = channel_name
|
|
227
|
+
.replace('private-class-', '')
|
|
228
|
+
.replace('-discussions', '');
|
|
229
|
+
const isMember = await prisma.class.findFirst({
|
|
230
|
+
where: {
|
|
231
|
+
id: classId,
|
|
232
|
+
discussionsEnabled: true,
|
|
233
|
+
OR: [
|
|
234
|
+
{ students: { some: { id: user.id } } },
|
|
235
|
+
{ teachers: { some: { id: user.id } } },
|
|
236
|
+
{
|
|
237
|
+
school: {
|
|
238
|
+
is: {
|
|
239
|
+
memberships: {
|
|
240
|
+
some: {
|
|
241
|
+
userId: user.id,
|
|
242
|
+
role: 'ADMIN',
|
|
243
|
+
status: 'ACTIVE',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
if (!isMember) {
|
|
253
|
+
return res.status(403).json({ error: 'Not a member of this class discussion channel' });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
224
257
|
// Verify channel access for private-class-* channels
|
|
225
258
|
// if (channel_name.startsWith('private-class-')) {
|
|
226
259
|
// const classId = channel_name.replace('private-class-', '');
|
|
@@ -586,7 +619,11 @@ app.options('/trpc/*', (req, res) => {
|
|
|
586
619
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
587
620
|
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
588
621
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
589
|
-
|
|
622
|
+
const requestedHeaders = req.headers['access-control-request-headers'];
|
|
623
|
+
if (requestedHeaders) {
|
|
624
|
+
res.setHeader('Access-Control-Allow-Headers', requestedHeaders);
|
|
625
|
+
res.setHeader('Vary', 'Origin, Access-Control-Request-Headers');
|
|
626
|
+
}
|
|
590
627
|
}
|
|
591
628
|
res.sendStatus(204);
|
|
592
629
|
});
|
package/src/lib/config/cors.ts
CHANGED
|
@@ -9,6 +9,11 @@ const productionOrigins = [
|
|
|
9
9
|
'https://www.studious.sh',
|
|
10
10
|
'https://studious.sh',
|
|
11
11
|
'https://dev.studious.sh',
|
|
12
|
+
// @todo: remove in actual prod
|
|
13
|
+
'http://localhost:3000',
|
|
14
|
+
'http://localhost:3001',
|
|
15
|
+
'http://127.0.0.1:3000',
|
|
16
|
+
'http://127.0.0.1:3001',
|
|
12
17
|
];
|
|
13
18
|
|
|
14
19
|
const nonProductionOrigins = [
|
package/src/lib/config/env.ts
CHANGED
|
@@ -63,6 +63,8 @@ const fullSchema = baseSchema.extend({
|
|
|
63
63
|
INFERENCE_API_BASE_URL: z.string().url().optional(),
|
|
64
64
|
OPENAI_API_KEY: z.string().optional(),
|
|
65
65
|
LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('normal'),
|
|
66
|
+
/** Comma/whitespace-separated user UUIDs allowed to call `admin.*` tRPC (required in production). */
|
|
67
|
+
ADMIN_PANEL_USER_IDS: z.string().optional(),
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
// Test schema - only require what's needed for tests
|
|
@@ -91,6 +93,7 @@ const testSchema = baseSchema.extend({
|
|
|
91
93
|
OPENAI_API_KEY: z.string().optional(),
|
|
92
94
|
INFERENCE_API_BASE_URL: z.string().url().optional(),
|
|
93
95
|
LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('quiet'),
|
|
96
|
+
ADMIN_PANEL_USER_IDS: z.string().optional(),
|
|
94
97
|
});
|
|
95
98
|
|
|
96
99
|
// Use test schema in test mode, full schema otherwise
|
package/src/lib/pusher.ts
CHANGED
|
@@ -12,6 +12,7 @@ const pusher = new Pusher({
|
|
|
12
12
|
const chatChannel = (conversationId: string) => `private-conversation-${conversationId}`;
|
|
13
13
|
const worksheetChannel = (worksheetResponseId: string) => `private-worksheet-${worksheetResponseId}`;
|
|
14
14
|
const teacherChannel = (classId: string) => `private-teacher-${classId}`;
|
|
15
|
+
const discussionChannel = (classId: string) => `private-class-${classId}-discussions`;
|
|
15
16
|
/**
|
|
16
17
|
* Per-worksheet job channel used for asynchronous worksheet creation
|
|
17
18
|
* (e.g. PDF → worksheet conversion). Distinct prefix from the student
|
|
@@ -19,4 +20,4 @@ const teacherChannel = (classId: string) => `private-teacher-${classId}`;
|
|
|
19
20
|
*/
|
|
20
21
|
const worksheetJobChannel = (worksheetId: string) => `private-worksheet-job-${worksheetId}`;
|
|
21
22
|
|
|
22
|
-
export { pusher, chatChannel, worksheetChannel, teacherChannel, worksheetJobChannel };
|
|
23
|
+
export { pusher, chatChannel, worksheetChannel, teacherChannel, discussionChannel, worksheetJobChannel };
|
package/src/models/class.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
const classIncludeForGetAll = {
|
|
19
19
|
assignments: {
|
|
20
20
|
where: {
|
|
21
|
-
dueDate: { lte: new Date(new Date().setHours(23, 59, 59, 999)) },
|
|
22
21
|
template: false,
|
|
23
22
|
},
|
|
24
23
|
select: {
|
|
@@ -26,6 +25,13 @@ const classIncludeForGetAll = {
|
|
|
26
25
|
title: true,
|
|
27
26
|
type: true,
|
|
28
27
|
dueDate: true,
|
|
28
|
+
submissions: {
|
|
29
|
+
select: {
|
|
30
|
+
studentId: true,
|
|
31
|
+
submitted: true,
|
|
32
|
+
returned: true,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
29
35
|
},
|
|
30
36
|
},
|
|
31
37
|
students: {
|
|
@@ -180,6 +186,9 @@ const classIncludeForGet = {
|
|
|
180
186
|
},
|
|
181
187
|
},
|
|
182
188
|
},
|
|
189
|
+
discussionsEnabled: true,
|
|
190
|
+
studentsCanCreateDiscussions: true,
|
|
191
|
+
studentsCanReply: true,
|
|
183
192
|
} as const;
|
|
184
193
|
|
|
185
194
|
const assignmentSelect = {
|
package/src/models/comment.ts
CHANGED
|
@@ -256,6 +256,18 @@ export function hideConversationMember(userId: string, conversationId: string) {
|
|
|
256
256
|
});
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/** Unhide a conversation for a single member. */
|
|
260
|
+
export function unhideConversationMember(userId: string, conversationId: string) {
|
|
261
|
+
return prisma.conversationMember.update({
|
|
262
|
+
where: {
|
|
263
|
+
userId_conversationId: { userId, conversationId },
|
|
264
|
+
},
|
|
265
|
+
data: {
|
|
266
|
+
hiddenAt: null,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
259
271
|
/** Remove a member from a conversation. */
|
|
260
272
|
export function deleteConversationMember(userId: string, conversationId: string) {
|
|
261
273
|
return prisma.conversationMember.delete({
|
package/src/models/worksheet.ts
CHANGED
|
@@ -22,7 +22,7 @@ export function findWorksheetById(id: string) {
|
|
|
22
22
|
return prisma.worksheet.findUnique({
|
|
23
23
|
where: { id },
|
|
24
24
|
include: {
|
|
25
|
-
questions: { orderBy: {
|
|
25
|
+
questions: { orderBy: { order: "asc" as const } },
|
|
26
26
|
class: true,
|
|
27
27
|
},
|
|
28
28
|
});
|
|
@@ -43,7 +43,7 @@ export function findWorksheetByIdForClassMember(id: string, userId: string) {
|
|
|
43
43
|
...worksheetClassMemberWhere(userId),
|
|
44
44
|
},
|
|
45
45
|
include: {
|
|
46
|
-
questions: { orderBy: {
|
|
46
|
+
questions: { orderBy: { order: "asc" as const } },
|
|
47
47
|
class: true,
|
|
48
48
|
},
|
|
49
49
|
});
|
|
@@ -57,7 +57,7 @@ export function findWorksheetByIdForTeacher(id: string, userId: string) {
|
|
|
57
57
|
...worksheetTeacherWhere(userId),
|
|
58
58
|
},
|
|
59
59
|
include: {
|
|
60
|
-
questions: { orderBy: {
|
|
60
|
+
questions: { orderBy: { order: "asc" as const } },
|
|
61
61
|
class: true,
|
|
62
62
|
},
|
|
63
63
|
});
|
|
@@ -273,7 +273,10 @@ export function findSubmissionByIdForWorksheetAccess(
|
|
|
273
273
|
const worksheetResponseInclude = {
|
|
274
274
|
responses: {
|
|
275
275
|
include: {
|
|
276
|
-
comments: {
|
|
276
|
+
comments: {
|
|
277
|
+
select: { id: true, isPinned: true },
|
|
278
|
+
orderBy: [{ isPinned: "desc" as const }, { createdAt: "asc" as const }],
|
|
279
|
+
},
|
|
277
280
|
},
|
|
278
281
|
},
|
|
279
282
|
};
|
|
@@ -319,7 +322,10 @@ export function createWorksheetResponse(data: {
|
|
|
319
322
|
include: {
|
|
320
323
|
responses: {
|
|
321
324
|
include: {
|
|
322
|
-
comments: {
|
|
325
|
+
comments: {
|
|
326
|
+
select: { id: true, isPinned: true },
|
|
327
|
+
orderBy: [{ isPinned: "desc" as const }, { createdAt: "asc" as const }],
|
|
328
|
+
},
|
|
323
329
|
},
|
|
324
330
|
},
|
|
325
331
|
},
|
|
@@ -456,6 +462,34 @@ export function findStudentQuestionProgressByIdForTeacherAndResponse(
|
|
|
456
462
|
});
|
|
457
463
|
}
|
|
458
464
|
|
|
465
|
+
/**
|
|
466
|
+
* @returns Student question progress if requester is the answering student (class member) or a
|
|
467
|
+
* class teacher / school admin for that worksheet.
|
|
468
|
+
*/
|
|
469
|
+
export function findStudentQuestionProgressByIdForStudentOrTeacher(
|
|
470
|
+
id: string,
|
|
471
|
+
userId: string
|
|
472
|
+
) {
|
|
473
|
+
return prisma.studentQuestionProgress.findFirst({
|
|
474
|
+
where: {
|
|
475
|
+
id,
|
|
476
|
+
OR: [
|
|
477
|
+
{
|
|
478
|
+
studentId: userId,
|
|
479
|
+
question: { worksheet: worksheetClassMemberWhere(userId) },
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
question: { worksheet: worksheetTeacherWhere(userId) },
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
include: {
|
|
487
|
+
question: true,
|
|
488
|
+
studentWorksheetResponse: true,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
459
493
|
/** Update student's question progress. */
|
|
460
494
|
export function updateStudentQuestionProgress(
|
|
461
495
|
id: string,
|
|
@@ -22,6 +22,8 @@ const removeAllPreviousAIComments = async (worksheetQuestionProgressId: string)
|
|
|
22
22
|
|
|
23
23
|
const gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {
|
|
24
24
|
|
|
25
|
+
await removeAllPreviousAIComments(worksheetQuestionProgressId);
|
|
26
|
+
|
|
25
27
|
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
26
28
|
where: { id: worksheetResponseId },
|
|
27
29
|
include: {
|
|
@@ -45,17 +47,8 @@ const gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQues
|
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
if (!studentQuestionProgress) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
studentId: worksheetResponse.studentId,
|
|
51
|
-
questionId: worksheetQuestionProgressId,
|
|
52
|
-
response: '',
|
|
53
|
-
isCorrect: false,
|
|
54
|
-
markschemeState: {},
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return updatedStudentQuestionProgress;
|
|
50
|
+
logger.error('Student question progress not found', { worksheetQuestionProgressId });
|
|
51
|
+
throw new Error('Student question progress not found');
|
|
59
52
|
}
|
|
60
53
|
|
|
61
54
|
pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {
|
|
@@ -63,9 +56,34 @@ const gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQues
|
|
|
63
56
|
});
|
|
64
57
|
|
|
65
58
|
const question = studentQuestionProgress.question;
|
|
66
|
-
const comments = studentQuestionProgress.comments;
|
|
67
59
|
const responseText = studentQuestionProgress.response;
|
|
68
60
|
|
|
61
|
+
// Short-circuit for empty responses: award 0 points without calling inference
|
|
62
|
+
if (!responseText || responseText.trim().length === 0) {
|
|
63
|
+
const updatedProgress = await prisma.studentQuestionProgress.update({
|
|
64
|
+
where: { id: studentQuestionProgress.id, status: {
|
|
65
|
+
not: { in: [JobStatus.CANCELLED] },
|
|
66
|
+
} },
|
|
67
|
+
data: {
|
|
68
|
+
status: JobStatus.COMPLETED,
|
|
69
|
+
isCorrect: false,
|
|
70
|
+
points: 0,
|
|
71
|
+
markschemeState: {},
|
|
72
|
+
comments: {
|
|
73
|
+
create: [{
|
|
74
|
+
content: 'No answer provided.',
|
|
75
|
+
authorId: getAIUserId(),
|
|
76
|
+
}],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
pusher.trigger(worksheetChannel(worksheetResponse.id), `set-completed`, {
|
|
81
|
+
id: updatedProgress.id,
|
|
82
|
+
});
|
|
83
|
+
return updatedProgress;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const comments = studentQuestionProgress.comments;
|
|
69
87
|
|
|
70
88
|
try {
|
|
71
89
|
const apiResponse = await inference(
|
|
@@ -206,7 +224,42 @@ export const gradeWorksheetPipeline = async (worksheetResponseId: string) => {
|
|
|
206
224
|
throw new Error('Worksheet response not found');
|
|
207
225
|
}
|
|
208
226
|
|
|
209
|
-
//
|
|
227
|
+
// Find worksheet questions that have no StudentQuestionProgress for this response
|
|
228
|
+
// and create empty progress records so they get graded (with 0 points).
|
|
229
|
+
const allQuestions = await prisma.worksheetQuestion.findMany({
|
|
230
|
+
where: {
|
|
231
|
+
worksheetId: worksheetResponse.worksheetId,
|
|
232
|
+
type: {
|
|
233
|
+
notIn: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
select: { id: true },
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const existingQuestionIds = new Set(
|
|
240
|
+
worksheetResponse.responses.map((r) => r.questionId),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
for (const q of allQuestions) {
|
|
244
|
+
if (existingQuestionIds.has(q.id)) continue;
|
|
245
|
+
|
|
246
|
+
const created = await prisma.studentQuestionProgress.create({
|
|
247
|
+
data: {
|
|
248
|
+
studentId: worksheetResponse.studentId,
|
|
249
|
+
questionId: q.id,
|
|
250
|
+
studentWorksheetResponseId: worksheetResponseId,
|
|
251
|
+
response: '',
|
|
252
|
+
isCorrect: false,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
worksheetResponse.responses.push({
|
|
257
|
+
...created,
|
|
258
|
+
question: { id: q.id } as any,
|
|
259
|
+
comments: [],
|
|
260
|
+
} as any);
|
|
261
|
+
}
|
|
262
|
+
|
|
210
263
|
for (const response of worksheetResponse.responses) {
|
|
211
264
|
logger.info('Grading question', { questionId: response.questionId });
|
|
212
265
|
|
|
@@ -293,8 +346,6 @@ export const regradeWorksheetPipeline = async (worksheetResponseId: string, work
|
|
|
293
346
|
where: { id: worksheetQuestionProgressId },
|
|
294
347
|
});
|
|
295
348
|
|
|
296
|
-
await removeAllPreviousAIComments(worksheetQuestionProgressId);
|
|
297
|
-
|
|
298
349
|
await gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);
|
|
299
350
|
|
|
300
351
|
return updatedStudentQuestionProgress;
|