@studious-lms/server 1.20.0 → 1.22.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/Dockerfile +5 -2
- package/dist/models/agenda.d.ts +6 -0
- package/dist/models/agenda.d.ts.map +1 -1
- package/dist/models/announcement.d.ts +2 -0
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +27 -0
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/auth.d.ts +4 -0
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/class.d.ts +14 -0
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/event.d.ts +11 -0
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/file.d.ts +19 -0
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/folder.d.ts +4 -0
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/gradebook.d.ts +180 -0
- package/dist/models/gradebook.d.ts.map +1 -0
- package/dist/models/gradebook.js +88 -0
- package/dist/models/gradebook.js.map +1 -0
- package/dist/models/labChat.d.ts +4 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/newtonChat.d.ts +2 -0
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/models/school.d.ts +2 -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 +6 -0
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/routers/_app.d.ts +1359 -37
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/admin.d.ts +164 -0
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/admin.js +23 -4
- package/dist/routers/admin.js.map +1 -1
- package/dist/routers/agenda.d.ts +6 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +102 -1
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +180 -2
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/class.d.ts +17 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/event.d.ts +4 -0
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/file.d.ts +66 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +111 -2
- package/dist/routers/file.js.map +1 -1
- package/dist/routers/gradebook.d.ts +299 -0
- package/dist/routers/gradebook.d.ts.map +1 -0
- package/dist/routers/gradebook.js +107 -0
- package/dist/routers/gradebook.js.map +1 -0
- package/dist/routers/school.d.ts +4 -0
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/section.d.ts +1 -1
- package/dist/routers/worksheet.d.ts +3 -1
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/services/adminInstitution.d.ts +51 -0
- package/dist/services/adminInstitution.d.ts.map +1 -1
- package/dist/services/adminInstitution.js +52 -2
- package/dist/services/adminInstitution.js.map +1 -1
- package/dist/services/adminMetrics.d.ts +17 -0
- package/dist/services/adminMetrics.d.ts.map +1 -0
- package/dist/services/adminMetrics.js +37 -0
- package/dist/services/adminMetrics.js.map +1 -0
- package/dist/services/adminRunJob.d.ts +9 -0
- package/dist/services/adminRunJob.d.ts.map +1 -1
- package/dist/services/adminRunJob.js +18 -2
- package/dist/services/adminRunJob.js.map +1 -1
- package/dist/services/adminUser.d.ts +67 -0
- package/dist/services/adminUser.d.ts.map +1 -0
- package/dist/services/adminUser.js +87 -0
- package/dist/services/adminUser.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 +27 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/class.d.ts +17 -0
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/event.d.ts +4 -0
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/gradeCalculation.d.ts +70 -0
- package/dist/services/gradeCalculation.d.ts.map +1 -0
- package/dist/services/gradeCalculation.js +148 -0
- package/dist/services/gradeCalculation.js.map +1 -0
- package/dist/services/gradebook.d.ts +197 -0
- package/dist/services/gradebook.d.ts.map +1 -0
- package/dist/services/gradebook.js +263 -0
- package/dist/services/gradebook.js.map +1 -0
- package/dist/services/school.d.ts +4 -0
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/worksheet.d.ts +2 -0
- package/dist/services/worksheet.d.ts.map +1 -1
- package/docker-entrypoint.sh +7 -0
- package/package.json +1 -1
- package/prisma/migrations/20260708011456_add_file_annotations/migration.sql +35 -0
- package/prisma/migrations/20260708011629_add_gradebook_categories_columns_policies/migration.sql +78 -0
- package/prisma/migrations/20260723142314_make_file_annotation_submission_optional/migration.sql +2 -0
- package/prisma/schema.prisma +130 -38
- package/src/models/gradebook.ts +91 -0
- package/src/routers/_app.ts +2 -0
- package/src/routers/admin.ts +28 -0
- package/src/routers/assignment.ts +204 -0
- package/src/routers/file.ts +128 -0
- package/src/routers/gradebook.ts +173 -0
- package/src/services/adminInstitution.ts +52 -0
- package/src/services/adminMetrics.ts +46 -0
- package/src/services/adminRunJob.ts +24 -0
- package/src/services/adminUser.ts +93 -0
- package/src/services/gradeCalculation.ts +253 -0
- package/src/services/gradebook.ts +383 -0
- package/tests/unit/services/gradeCalculation.test.ts +206 -0
package/prisma/migrations/20260708011629_add_gradebook_categories_columns_policies/migration.sql
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "GradeRoundingMode" AS ENUM ('NONE', 'HALF_UP', 'FLOOR', 'CEILING');
|
|
3
|
+
|
|
4
|
+
-- AlterTable
|
|
5
|
+
ALTER TABLE "Assignment" ADD COLUMN "gradeCategoryId" TEXT;
|
|
6
|
+
|
|
7
|
+
-- AlterTable
|
|
8
|
+
ALTER TABLE "Class" ADD COLUMN "gradeRoundingMode" "GradeRoundingMode" NOT NULL DEFAULT 'NONE',
|
|
9
|
+
ADD COLUMN "treatMissingAsZero" BOOLEAN NOT NULL DEFAULT false;
|
|
10
|
+
|
|
11
|
+
-- CreateTable
|
|
12
|
+
CREATE TABLE "GradeCategory" (
|
|
13
|
+
"id" TEXT NOT NULL,
|
|
14
|
+
"classId" TEXT NOT NULL,
|
|
15
|
+
"name" TEXT NOT NULL,
|
|
16
|
+
"weight" DOUBLE PRECISION NOT NULL,
|
|
17
|
+
"dropLowest" INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
"position" INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
|
|
20
|
+
CONSTRAINT "GradeCategory_pkey" PRIMARY KEY ("id")
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
-- CreateTable
|
|
24
|
+
CREATE TABLE "CustomGradeColumn" (
|
|
25
|
+
"id" TEXT NOT NULL,
|
|
26
|
+
"classId" TEXT NOT NULL,
|
|
27
|
+
"gradeCategoryId" TEXT,
|
|
28
|
+
"name" TEXT NOT NULL,
|
|
29
|
+
"maxPoints" DOUBLE PRECISION NOT NULL,
|
|
30
|
+
"position" INTEGER NOT NULL DEFAULT 0,
|
|
31
|
+
|
|
32
|
+
CONSTRAINT "CustomGradeColumn_pkey" PRIMARY KEY ("id")
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- CreateTable
|
|
36
|
+
CREATE TABLE "CustomGradeEntry" (
|
|
37
|
+
"id" TEXT NOT NULL,
|
|
38
|
+
"columnId" TEXT NOT NULL,
|
|
39
|
+
"studentId" TEXT NOT NULL,
|
|
40
|
+
"points" DOUBLE PRECISION NOT NULL,
|
|
41
|
+
"note" TEXT,
|
|
42
|
+
|
|
43
|
+
CONSTRAINT "CustomGradeEntry_pkey" PRIMARY KEY ("id")
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- CreateIndex
|
|
47
|
+
CREATE INDEX "GradeCategory_classId_idx" ON "GradeCategory"("classId");
|
|
48
|
+
|
|
49
|
+
-- CreateIndex
|
|
50
|
+
CREATE INDEX "CustomGradeColumn_classId_idx" ON "CustomGradeColumn"("classId");
|
|
51
|
+
|
|
52
|
+
-- CreateIndex
|
|
53
|
+
CREATE INDEX "CustomGradeColumn_gradeCategoryId_idx" ON "CustomGradeColumn"("gradeCategoryId");
|
|
54
|
+
|
|
55
|
+
-- CreateIndex
|
|
56
|
+
CREATE INDEX "CustomGradeEntry_studentId_idx" ON "CustomGradeEntry"("studentId");
|
|
57
|
+
|
|
58
|
+
-- CreateIndex
|
|
59
|
+
CREATE UNIQUE INDEX "CustomGradeEntry_columnId_studentId_key" ON "CustomGradeEntry"("columnId", "studentId");
|
|
60
|
+
|
|
61
|
+
-- AddForeignKey
|
|
62
|
+
ALTER TABLE "GradeCategory" ADD CONSTRAINT "GradeCategory_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
63
|
+
|
|
64
|
+
-- AddForeignKey
|
|
65
|
+
ALTER TABLE "CustomGradeColumn" ADD CONSTRAINT "CustomGradeColumn_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
66
|
+
|
|
67
|
+
-- AddForeignKey
|
|
68
|
+
ALTER TABLE "CustomGradeColumn" ADD CONSTRAINT "CustomGradeColumn_gradeCategoryId_fkey" FOREIGN KEY ("gradeCategoryId") REFERENCES "GradeCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
69
|
+
|
|
70
|
+
-- AddForeignKey
|
|
71
|
+
ALTER TABLE "CustomGradeEntry" ADD CONSTRAINT "CustomGradeEntry_columnId_fkey" FOREIGN KEY ("columnId") REFERENCES "CustomGradeColumn"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
72
|
+
|
|
73
|
+
-- AddForeignKey
|
|
74
|
+
ALTER TABLE "CustomGradeEntry" ADD CONSTRAINT "CustomGradeEntry_studentId_fkey" FOREIGN KEY ("studentId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
75
|
+
|
|
76
|
+
-- AddForeignKey
|
|
77
|
+
ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_gradeCategoryId_fkey" FOREIGN KEY ("gradeCategoryId") REFERENCES "GradeCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
78
|
+
|
package/prisma/schema.prisma
CHANGED
|
@@ -128,14 +128,14 @@ model School {
|
|
|
128
128
|
createdAt DateTime @default(now())
|
|
129
129
|
updatedAt DateTime @updatedAt
|
|
130
130
|
|
|
131
|
-
users
|
|
132
|
-
memberships
|
|
133
|
-
invites
|
|
134
|
-
classes
|
|
135
|
-
departments
|
|
136
|
-
tags
|
|
137
|
-
storageRoot
|
|
138
|
-
permissions
|
|
131
|
+
users User[]
|
|
132
|
+
memberships SchoolMembership[]
|
|
133
|
+
invites SchoolInvite[]
|
|
134
|
+
classes Class[]
|
|
135
|
+
departments Department[]
|
|
136
|
+
tags Tag[]
|
|
137
|
+
storageRoot Folder? @relation("SchoolStorageRoot")
|
|
138
|
+
permissions Permission[] @relation("SchoolPermissions")
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
model Department {
|
|
@@ -175,13 +175,13 @@ model Tag {
|
|
|
175
175
|
createdAt DateTime @default(now())
|
|
176
176
|
updatedAt DateTime @updatedAt
|
|
177
177
|
|
|
178
|
-
school
|
|
179
|
-
department
|
|
180
|
-
users
|
|
181
|
-
classes
|
|
182
|
-
rubrics
|
|
183
|
-
boundaries
|
|
184
|
-
permissions Permission[]
|
|
178
|
+
school School @relation(fields: [schoolId], references: [id], onDelete: Cascade)
|
|
179
|
+
department Department? @relation(fields: [departmentId], references: [id], onDelete: SetNull)
|
|
180
|
+
users User[] @relation("UserTags")
|
|
181
|
+
classes Class[] @relation("ClassTags")
|
|
182
|
+
rubrics MarkScheme[] @relation("MarkSchemeTags")
|
|
183
|
+
boundaries GradingBoundary[] @relation("GradingBoundaryTags")
|
|
184
|
+
permissions Permission[] @relation("TagPermissions")
|
|
185
185
|
|
|
186
186
|
@@unique([schoolId, name])
|
|
187
187
|
@@index([schoolId])
|
|
@@ -197,7 +197,7 @@ model User {
|
|
|
197
197
|
verified Boolean @default(false)
|
|
198
198
|
|
|
199
199
|
termsAcceptedAt DateTime?
|
|
200
|
-
termsVersion
|
|
200
|
+
termsVersion String?
|
|
201
201
|
|
|
202
202
|
teacherIn Class[] @relation("UserTeacherToClass")
|
|
203
203
|
studentIn Class[] @relation("UserStudentToClass")
|
|
@@ -241,6 +241,8 @@ model User {
|
|
|
241
241
|
discussions Discussion[]
|
|
242
242
|
discussionReplies DiscussionReply[]
|
|
243
243
|
discussionReads DiscussionRead[]
|
|
244
|
+
customGradeEntries CustomGradeEntry[] @relation("UserCustomGradeEntries")
|
|
245
|
+
fileAnnotations FileAnnotation[] @relation("AuthoredFileAnnotations")
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
model SchoolMembership {
|
|
@@ -330,14 +332,71 @@ model Class {
|
|
|
330
332
|
classTags ClassTag[]
|
|
331
333
|
embeddings Embedding[]
|
|
332
334
|
|
|
333
|
-
discussionsEnabled
|
|
334
|
-
studentsCanCreateDiscussions
|
|
335
|
-
studentsCanReply
|
|
335
|
+
discussionsEnabled Boolean @default(true)
|
|
336
|
+
studentsCanCreateDiscussions Boolean @default(true)
|
|
337
|
+
studentsCanReply Boolean @default(true)
|
|
338
|
+
|
|
339
|
+
gradeCategories GradeCategory[]
|
|
340
|
+
customGradeColumns CustomGradeColumn[]
|
|
341
|
+
|
|
342
|
+
treatMissingAsZero Boolean @default(false)
|
|
343
|
+
gradeRoundingMode GradeRoundingMode @default(NONE)
|
|
336
344
|
|
|
337
345
|
@@index([schoolId])
|
|
338
346
|
@@index([departmentId])
|
|
339
347
|
}
|
|
340
348
|
|
|
349
|
+
enum GradeRoundingMode {
|
|
350
|
+
NONE
|
|
351
|
+
HALF_UP
|
|
352
|
+
FLOOR
|
|
353
|
+
CEILING
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
model GradeCategory {
|
|
357
|
+
id String @id @default(uuid())
|
|
358
|
+
classId String
|
|
359
|
+
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
360
|
+
name String
|
|
361
|
+
weight Float
|
|
362
|
+
dropLowest Int @default(0)
|
|
363
|
+
position Int @default(0)
|
|
364
|
+
|
|
365
|
+
assignments Assignment[]
|
|
366
|
+
customColumns CustomGradeColumn[]
|
|
367
|
+
|
|
368
|
+
@@index([classId])
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
model CustomGradeColumn {
|
|
372
|
+
id String @id @default(uuid())
|
|
373
|
+
classId String
|
|
374
|
+
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
375
|
+
gradeCategoryId String?
|
|
376
|
+
gradeCategory GradeCategory? @relation(fields: [gradeCategoryId], references: [id], onDelete: SetNull)
|
|
377
|
+
name String
|
|
378
|
+
maxPoints Float
|
|
379
|
+
position Int @default(0)
|
|
380
|
+
|
|
381
|
+
entries CustomGradeEntry[]
|
|
382
|
+
|
|
383
|
+
@@index([classId])
|
|
384
|
+
@@index([gradeCategoryId])
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
model CustomGradeEntry {
|
|
388
|
+
id String @id @default(uuid())
|
|
389
|
+
columnId String
|
|
390
|
+
column CustomGradeColumn @relation(fields: [columnId], references: [id], onDelete: Cascade)
|
|
391
|
+
studentId String
|
|
392
|
+
student User @relation("UserCustomGradeEntries", fields: [studentId], references: [id], onDelete: Cascade)
|
|
393
|
+
points Float
|
|
394
|
+
note String?
|
|
395
|
+
|
|
396
|
+
@@unique([columnId, studentId])
|
|
397
|
+
@@index([studentId])
|
|
398
|
+
}
|
|
399
|
+
|
|
341
400
|
model MarkScheme {
|
|
342
401
|
id String @id @default(uuid())
|
|
343
402
|
classId String?
|
|
@@ -454,6 +513,35 @@ model File {
|
|
|
454
513
|
|
|
455
514
|
schoolDevelopementProgram SchoolDevelopementProgram? @relation("SchoolDevelopementProgramSupportingDocumentation", fields: [schoolDevelopementProgramId], references: [id], onDelete: Cascade)
|
|
456
515
|
schoolDevelopementProgramId String?
|
|
516
|
+
|
|
517
|
+
fileAnnotations FileAnnotation[] @relation("FileAnnotations")
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
enum FileAnnotationType {
|
|
521
|
+
HIGHLIGHT
|
|
522
|
+
INK
|
|
523
|
+
COMMENT
|
|
524
|
+
TEXT
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
model FileAnnotation {
|
|
528
|
+
id String @id @default(uuid())
|
|
529
|
+
file File @relation("FileAnnotations", fields: [fileId], references: [id], onDelete: Cascade)
|
|
530
|
+
fileId String
|
|
531
|
+
submission Submission? @relation("SubmissionFileAnnotations", fields: [submissionId], references: [id], onDelete: Cascade)
|
|
532
|
+
submissionId String?
|
|
533
|
+
author User @relation("AuthoredFileAnnotations", fields: [authorId], references: [id], onDelete: Cascade)
|
|
534
|
+
authorId String
|
|
535
|
+
page Int
|
|
536
|
+
type FileAnnotationType
|
|
537
|
+
geometry Json
|
|
538
|
+
text String?
|
|
539
|
+
color String?
|
|
540
|
+
createdAt DateTime @default(now())
|
|
541
|
+
updatedAt DateTime @updatedAt
|
|
542
|
+
|
|
543
|
+
@@index([fileId])
|
|
544
|
+
@@index([submissionId])
|
|
457
545
|
}
|
|
458
546
|
|
|
459
547
|
model Assignment {
|
|
@@ -493,6 +581,8 @@ model Assignment {
|
|
|
493
581
|
order Int?
|
|
494
582
|
gradingBoundary GradingBoundary? @relation(fields: [gradingBoundaryId], references: [id], onDelete: Cascade)
|
|
495
583
|
gradingBoundaryId String?
|
|
584
|
+
gradeCategory GradeCategory? @relation(fields: [gradeCategoryId], references: [id], onDelete: SetNull)
|
|
585
|
+
gradeCategoryId String?
|
|
496
586
|
worksheets Worksheet[] @relation("AssignmentWorksheets")
|
|
497
587
|
targetedBySubmissions Submission[] @relation("SubmissionTargetedAssignment")
|
|
498
588
|
}
|
|
@@ -607,19 +697,19 @@ model DiscussionRead {
|
|
|
607
697
|
}
|
|
608
698
|
|
|
609
699
|
model Reaction {
|
|
610
|
-
id
|
|
611
|
-
type
|
|
612
|
-
user
|
|
613
|
-
userId
|
|
614
|
-
announcement
|
|
615
|
-
announcementId
|
|
616
|
-
comment
|
|
617
|
-
commentId
|
|
700
|
+
id String @id @default(uuid())
|
|
701
|
+
type ReactionType
|
|
702
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
703
|
+
userId String
|
|
704
|
+
announcement Announcement? @relation(fields: [announcementId], references: [id], onDelete: Cascade)
|
|
705
|
+
announcementId String?
|
|
706
|
+
comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
|
707
|
+
commentId String?
|
|
618
708
|
discussion Discussion? @relation(fields: [discussionId], references: [id], onDelete: Cascade)
|
|
619
709
|
discussionId String?
|
|
620
710
|
discussionReply DiscussionReply? @relation(fields: [discussionReplyId], references: [id], onDelete: Cascade)
|
|
621
711
|
discussionReplyId String?
|
|
622
|
-
createdAt
|
|
712
|
+
createdAt DateTime @default(now())
|
|
623
713
|
|
|
624
714
|
@@unique([userId, announcementId])
|
|
625
715
|
@@unique([userId, commentId])
|
|
@@ -661,6 +751,8 @@ model Submission {
|
|
|
661
751
|
returned Boolean? @default(false)
|
|
662
752
|
newtonTutors NewtonChat[]
|
|
663
753
|
|
|
754
|
+
fileAnnotations FileAnnotation[] @relation("SubmissionFileAnnotations")
|
|
755
|
+
|
|
664
756
|
@@index([studentId, recommendationState])
|
|
665
757
|
}
|
|
666
758
|
|
|
@@ -890,11 +982,11 @@ model Worksheet {
|
|
|
890
982
|
questions WorksheetQuestion[]
|
|
891
983
|
studentWorksheetResponses StudentWorksheetResponse[]
|
|
892
984
|
|
|
893
|
-
runJobId
|
|
894
|
-
runJob
|
|
985
|
+
runJobId String?
|
|
986
|
+
runJob RunJob? @relation("WorksheetRunJob", fields: [runJobId], references: [id], onDelete: SetNull)
|
|
895
987
|
|
|
896
|
-
status
|
|
897
|
-
tags
|
|
988
|
+
status JobStatus? @default(NOT_STARTED)
|
|
989
|
+
tags ClassTag[] @relation("WorksheetToTag")
|
|
898
990
|
|
|
899
991
|
@@unique([classId, sourceUrl])
|
|
900
992
|
}
|
|
@@ -1011,14 +1103,14 @@ enum EmbeddingSourceType {
|
|
|
1011
1103
|
}
|
|
1012
1104
|
|
|
1013
1105
|
model Embedding {
|
|
1014
|
-
id String
|
|
1106
|
+
id String @id @default(uuid())
|
|
1015
1107
|
classId String
|
|
1016
1108
|
sourceType EmbeddingSourceType
|
|
1017
1109
|
sourceId String
|
|
1018
1110
|
content String
|
|
1019
1111
|
vector Unsupported("vector(1536)")
|
|
1020
|
-
metadata Json?
|
|
1021
|
-
createdAt DateTime
|
|
1112
|
+
metadata Json? @default("{}")
|
|
1113
|
+
createdAt DateTime @default(now())
|
|
1022
1114
|
|
|
1023
1115
|
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
1024
1116
|
|
|
@@ -1062,7 +1154,7 @@ model RunJob {
|
|
|
1062
1154
|
/// most jobs touch zero or one of these.
|
|
1063
1155
|
messages Message[] @relation("MessageRunJob")
|
|
1064
1156
|
studentQuestionProgresses StudentQuestionProgress[] @relation("StudentQuestionProgressRunJob")
|
|
1065
|
-
worksheet Worksheet[]
|
|
1157
|
+
worksheet Worksheet[] @relation("WorksheetRunJob")
|
|
1066
1158
|
|
|
1067
1159
|
@@index([userId, type, status])
|
|
1068
1160
|
@@index([classId, status])
|
|
@@ -1098,9 +1190,9 @@ enum PermissionLevel {
|
|
|
1098
1190
|
/// `schoolId` is set, matching `principalType`. CLASS, CLASS_TEACHERS,
|
|
1099
1191
|
/// CLASS_STUDENTS all share `classId`.
|
|
1100
1192
|
model Permission {
|
|
1101
|
-
id String
|
|
1193
|
+
id String @id @default(uuid())
|
|
1102
1194
|
folderId String
|
|
1103
|
-
folder Folder
|
|
1195
|
+
folder Folder @relation("FolderPermissions", fields: [folderId], references: [id], onDelete: Cascade)
|
|
1104
1196
|
|
|
1105
1197
|
principalType PermissionPrincipalType
|
|
1106
1198
|
userId String?
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gradebook model – grade categories, custom gradebook columns/entries,
|
|
3
|
+
* and the queries backing the computed gradebook.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { prisma } from "../lib/prisma.js";
|
|
7
|
+
|
|
8
|
+
/** @returns Grade categories for a class, ordered by position. */
|
|
9
|
+
export function findGradeCategoriesByClassId(classId: string) {
|
|
10
|
+
return prisma.gradeCategory.findMany({
|
|
11
|
+
where: { classId },
|
|
12
|
+
orderBy: { position: "asc" },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @returns A grade category scoped to a class. */
|
|
17
|
+
export function findGradeCategory(categoryId: string, classId: string) {
|
|
18
|
+
return prisma.gradeCategory.findFirst({
|
|
19
|
+
where: { id: categoryId, classId },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @returns Custom gradebook columns for a class with their entries. */
|
|
24
|
+
export function findCustomGradeColumnsByClassId(classId: string) {
|
|
25
|
+
return prisma.customGradeColumn.findMany({
|
|
26
|
+
where: { classId },
|
|
27
|
+
orderBy: { position: "asc" },
|
|
28
|
+
include: {
|
|
29
|
+
entries: {
|
|
30
|
+
select: { id: true, studentId: true, points: true, note: true },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @returns A custom gradebook column scoped to a class. */
|
|
37
|
+
export function findCustomGradeColumn(columnId: string, classId: string) {
|
|
38
|
+
return prisma.customGradeColumn.findFirst({
|
|
39
|
+
where: { id: columnId, classId },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @returns Class data needed to compute the gradebook. */
|
|
44
|
+
export function findClassForGradebook(classId: string) {
|
|
45
|
+
return prisma.class.findUnique({
|
|
46
|
+
where: { id: classId },
|
|
47
|
+
select: {
|
|
48
|
+
id: true,
|
|
49
|
+
treatMissingAsZero: true,
|
|
50
|
+
gradeRoundingMode: true,
|
|
51
|
+
students: {
|
|
52
|
+
select: {
|
|
53
|
+
id: true,
|
|
54
|
+
username: true,
|
|
55
|
+
profile: { select: { displayName: true } },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
gradingBoundaries: {
|
|
59
|
+
take: 1,
|
|
60
|
+
select: { id: true, name: true, structured: true },
|
|
61
|
+
},
|
|
62
|
+
assignments: {
|
|
63
|
+
where: { graded: true, template: false, inProgress: false },
|
|
64
|
+
orderBy: [{ order: "asc" }, { dueDate: "asc" }],
|
|
65
|
+
select: {
|
|
66
|
+
id: true,
|
|
67
|
+
title: true,
|
|
68
|
+
maxGrade: true,
|
|
69
|
+
weight: true,
|
|
70
|
+
gradeCategoryId: true,
|
|
71
|
+
dueDate: true,
|
|
72
|
+
submissions: {
|
|
73
|
+
select: { studentId: true, gradeReceived: true },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @returns Grade policy fields for a class. */
|
|
82
|
+
export function findClassGradePolicies(classId: string) {
|
|
83
|
+
return prisma.class.findUnique({
|
|
84
|
+
where: { id: classId },
|
|
85
|
+
select: {
|
|
86
|
+
id: true,
|
|
87
|
+
treatMissingAsZero: true,
|
|
88
|
+
gradeRoundingMode: true,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
package/src/routers/_app.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { permissionsRouter } from "./permissions.js";
|
|
|
29
29
|
import { discussionRouter } from "./discussion.js";
|
|
30
30
|
import { adminRouter } from "./admin.js";
|
|
31
31
|
import { embeddingRouter } from "./embedding.js";
|
|
32
|
+
import { gradebookRouter } from "./gradebook.js";
|
|
32
33
|
|
|
33
34
|
export const appRouter = createTRPCRouter({
|
|
34
35
|
class: classRouter,
|
|
@@ -59,6 +60,7 @@ export const appRouter = createTRPCRouter({
|
|
|
59
60
|
discussion: discussionRouter,
|
|
60
61
|
admin: adminRouter,
|
|
61
62
|
embedding: embeddingRouter,
|
|
63
|
+
gradebook: gradebookRouter,
|
|
62
64
|
});
|
|
63
65
|
|
|
64
66
|
// Export type router type definition
|
package/src/routers/admin.ts
CHANGED
|
@@ -4,15 +4,21 @@ import { createTRPCRouter, adminProcedure } from '../trpc.js';
|
|
|
4
4
|
import {
|
|
5
5
|
getRunJobForAdmin,
|
|
6
6
|
listRunJobsForAdmin,
|
|
7
|
+
retryRunJobForAdmin,
|
|
7
8
|
} from '../services/adminRunJob.js';
|
|
8
9
|
import {
|
|
10
|
+
getSchoolForAdmin,
|
|
9
11
|
listDepartmentsForAdmin,
|
|
10
12
|
listEarlyAccessRequestsForAdmin,
|
|
11
13
|
listSchoolDevelopmentProgramsForAdmin,
|
|
12
14
|
listSchoolsForAdmin,
|
|
13
15
|
} from '../services/adminInstitution.js';
|
|
16
|
+
import { getOverviewMetricsForAdmin } from '../services/adminMetrics.js';
|
|
17
|
+
import { getUserForAdmin, searchUsersForAdmin } from '../services/adminUser.js';
|
|
14
18
|
|
|
15
19
|
export const adminRouter = createTRPCRouter({
|
|
20
|
+
getOverviewMetrics: adminProcedure.query(() => getOverviewMetricsForAdmin()),
|
|
21
|
+
|
|
16
22
|
listRunJobs: adminProcedure
|
|
17
23
|
.input(
|
|
18
24
|
z.object({
|
|
@@ -28,6 +34,14 @@ export const adminRouter = createTRPCRouter({
|
|
|
28
34
|
.input(z.object({ id: z.string().uuid() }))
|
|
29
35
|
.query(({ input }) => getRunJobForAdmin(input.id)),
|
|
30
36
|
|
|
37
|
+
retryRunJob: adminProcedure
|
|
38
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
39
|
+
.mutation(({ input }) => retryRunJobForAdmin(input.id)),
|
|
40
|
+
|
|
41
|
+
getSchool: adminProcedure
|
|
42
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
43
|
+
.query(({ input }) => getSchoolForAdmin(input.id)),
|
|
44
|
+
|
|
31
45
|
listSchools: adminProcedure
|
|
32
46
|
.input(
|
|
33
47
|
z.object({
|
|
@@ -67,4 +81,18 @@ export const adminRouter = createTRPCRouter({
|
|
|
67
81
|
}),
|
|
68
82
|
)
|
|
69
83
|
.query(({ input }) => listEarlyAccessRequestsForAdmin(input)),
|
|
84
|
+
|
|
85
|
+
searchUsers: adminProcedure
|
|
86
|
+
.input(
|
|
87
|
+
z.object({
|
|
88
|
+
page: z.number().int().min(0).default(0),
|
|
89
|
+
pageSize: z.number().int().min(1).max(100).default(25),
|
|
90
|
+
query: z.string().optional(),
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
.query(({ input }) => searchUsersForAdmin(input)),
|
|
94
|
+
|
|
95
|
+
getUser: adminProcedure
|
|
96
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
97
|
+
.query(({ input }) => getUserForAdmin(input.id)),
|
|
70
98
|
});
|