@studious-lms/server 1.9.0 → 1.12.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.
Files changed (163) hide show
  1. package/.env.example +3 -0
  2. package/dist/index.js +40 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/config/cors.d.ts.map +1 -1
  5. package/dist/lib/config/cors.js +7 -2
  6. package/dist/lib/config/cors.js.map +1 -1
  7. package/dist/lib/config/env.d.ts +8 -0
  8. package/dist/lib/config/env.d.ts.map +1 -1
  9. package/dist/lib/config/env.js +5 -2
  10. package/dist/lib/config/env.js.map +1 -1
  11. package/dist/lib/pusher.d.ts +2 -1
  12. package/dist/lib/pusher.d.ts.map +1 -1
  13. package/dist/lib/pusher.js +4 -3
  14. package/dist/lib/pusher.js.map +1 -1
  15. package/dist/models/agenda.d.ts +6 -0
  16. package/dist/models/agenda.d.ts.map +1 -1
  17. package/dist/models/announcement.d.ts +3 -0
  18. package/dist/models/announcement.d.ts.map +1 -1
  19. package/dist/models/assignment.d.ts +61 -0
  20. package/dist/models/assignment.d.ts.map +1 -1
  21. package/dist/models/auth.d.ts +6 -0
  22. package/dist/models/auth.d.ts.map +1 -1
  23. package/dist/models/class.d.ts +18 -0
  24. package/dist/models/class.d.ts.map +1 -1
  25. package/dist/models/class.js +5 -2
  26. package/dist/models/class.js.map +1 -1
  27. package/dist/models/comment.d.ts +6 -0
  28. package/dist/models/comment.d.ts.map +1 -1
  29. package/dist/models/event.d.ts +12 -0
  30. package/dist/models/event.d.ts.map +1 -1
  31. package/dist/models/file.d.ts +38 -0
  32. package/dist/models/file.d.ts.map +1 -1
  33. package/dist/models/folder.d.ts +6 -0
  34. package/dist/models/folder.d.ts.map +1 -1
  35. package/dist/models/labChat.d.ts +6 -0
  36. package/dist/models/labChat.d.ts.map +1 -1
  37. package/dist/models/newtonChat.d.ts +3 -0
  38. package/dist/models/newtonChat.d.ts.map +1 -1
  39. package/dist/models/school.d.ts +3 -0
  40. package/dist/models/school.d.ts.map +1 -1
  41. package/dist/models/section.d.ts +3 -0
  42. package/dist/models/section.d.ts.map +1 -1
  43. package/dist/models/worksheet.d.ts +58 -0
  44. package/dist/models/worksheet.d.ts.map +1 -1
  45. package/dist/models/worksheet.js +26 -2
  46. package/dist/models/worksheet.js.map +1 -1
  47. package/dist/routers/_app.d.ts +6400 -4078
  48. package/dist/routers/_app.d.ts.map +1 -1
  49. package/dist/routers/_app.js +6 -2
  50. package/dist/routers/_app.js.map +1 -1
  51. package/dist/routers/admin.d.ts +190 -0
  52. package/dist/routers/admin.d.ts.map +1 -0
  53. package/dist/routers/admin.js +50 -0
  54. package/dist/routers/admin.js.map +1 -0
  55. package/dist/routers/agenda.d.ts +6 -0
  56. package/dist/routers/agenda.d.ts.map +1 -1
  57. package/dist/routers/announcement.d.ts +2 -0
  58. package/dist/routers/announcement.d.ts.map +1 -1
  59. package/dist/routers/assignment.d.ts +50 -0
  60. package/dist/routers/assignment.d.ts.map +1 -1
  61. package/dist/routers/class.d.ts +31 -0
  62. package/dist/routers/class.d.ts.map +1 -1
  63. package/dist/routers/class.js +5 -2
  64. package/dist/routers/class.js.map +1 -1
  65. package/dist/routers/comment.d.ts +2 -0
  66. package/dist/routers/comment.d.ts.map +1 -1
  67. package/dist/routers/discussion.d.ts +823 -0
  68. package/dist/routers/discussion.d.ts.map +1 -0
  69. package/dist/routers/discussion.js +151 -0
  70. package/dist/routers/discussion.js.map +1 -0
  71. package/dist/routers/event.d.ts +3 -0
  72. package/dist/routers/event.d.ts.map +1 -1
  73. package/dist/routers/file.d.ts +4 -0
  74. package/dist/routers/file.d.ts.map +1 -1
  75. package/dist/routers/school.d.ts +6 -0
  76. package/dist/routers/school.d.ts.map +1 -1
  77. package/dist/routers/studentProgress.d.ts +45 -0
  78. package/dist/routers/studentProgress.d.ts.map +1 -1
  79. package/dist/routers/studentProgress.js +12 -3
  80. package/dist/routers/studentProgress.js.map +1 -1
  81. package/dist/routers/worksheet.d.ts +3 -0
  82. package/dist/routers/worksheet.d.ts.map +1 -1
  83. package/dist/seedDatabase.d.ts.map +1 -1
  84. package/dist/seedDatabase.js +1490 -1307
  85. package/dist/seedDatabase.js.map +1 -1
  86. package/dist/services/adminInstitution.d.ts +102 -0
  87. package/dist/services/adminInstitution.d.ts.map +1 -0
  88. package/dist/services/adminInstitution.js +117 -0
  89. package/dist/services/adminInstitution.js.map +1 -0
  90. package/dist/services/adminRunJob.d.ts +45 -0
  91. package/dist/services/adminRunJob.d.ts.map +1 -0
  92. package/dist/services/adminRunJob.js +41 -0
  93. package/dist/services/adminRunJob.js.map +1 -0
  94. package/dist/services/agenda.d.ts +6 -0
  95. package/dist/services/agenda.d.ts.map +1 -1
  96. package/dist/services/assignment.d.ts +50 -0
  97. package/dist/services/assignment.d.ts.map +1 -1
  98. package/dist/services/assignment.js +8 -3
  99. package/dist/services/assignment.js.map +1 -1
  100. package/dist/services/class.d.ts +31 -0
  101. package/dist/services/class.d.ts.map +1 -1
  102. package/dist/services/class.js +14 -2
  103. package/dist/services/class.js.map +1 -1
  104. package/dist/services/comment.d.ts +2 -0
  105. package/dist/services/comment.d.ts.map +1 -1
  106. package/dist/services/discussion.d.ts +762 -0
  107. package/dist/services/discussion.d.ts.map +1 -0
  108. package/dist/services/discussion.js +625 -0
  109. package/dist/services/discussion.js.map +1 -0
  110. package/dist/services/event.d.ts +3 -0
  111. package/dist/services/event.d.ts.map +1 -1
  112. package/dist/services/file.d.ts +8 -0
  113. package/dist/services/file.d.ts.map +1 -1
  114. package/dist/services/school.d.ts +6 -0
  115. package/dist/services/school.d.ts.map +1 -1
  116. package/dist/services/studentProgress.d.ts +94 -0
  117. package/dist/services/studentProgress.d.ts.map +1 -1
  118. package/dist/services/studentProgress.js +116 -3
  119. package/dist/services/studentProgress.js.map +1 -1
  120. package/dist/services/studentProgressInferenceCache.d.ts +16 -0
  121. package/dist/services/studentProgressInferenceCache.d.ts.map +1 -0
  122. package/dist/services/studentProgressInferenceCache.js +43 -0
  123. package/dist/services/studentProgressInferenceCache.js.map +1 -0
  124. package/dist/services/worksheet.d.ts +3 -0
  125. package/dist/services/worksheet.d.ts.map +1 -1
  126. package/dist/services/worksheet.js +5 -5
  127. package/dist/services/worksheet.js.map +1 -1
  128. package/dist/trpc.d.ts +12 -0
  129. package/dist/trpc.d.ts.map +1 -1
  130. package/dist/trpc.js +16 -3
  131. package/dist/trpc.js.map +1 -1
  132. package/dist/utils/adminPanelAccess.d.ts +8 -0
  133. package/dist/utils/adminPanelAccess.d.ts.map +1 -0
  134. package/dist/utils/adminPanelAccess.js +44 -0
  135. package/dist/utils/adminPanelAccess.js.map +1 -0
  136. package/package.json +1 -1
  137. package/prisma/migrations/20260510183000_add_discussions/migration.sql +151 -0
  138. package/prisma/schema.prisma +97 -0
  139. package/src/index.ts +39 -2
  140. package/src/lib/config/cors.ts +5 -0
  141. package/src/lib/config/env.ts +3 -0
  142. package/src/lib/pusher.ts +2 -1
  143. package/src/models/class.ts +3 -0
  144. package/src/models/worksheet.ts +28 -0
  145. package/src/routers/_app.ts +4 -0
  146. package/src/routers/admin.ts +70 -0
  147. package/src/routers/class.ts +3 -0
  148. package/src/routers/discussion.ts +205 -0
  149. package/src/routers/studentProgress.ts +21 -0
  150. package/src/seedDatabase.ts +1713 -1361
  151. package/src/services/adminInstitution.ts +140 -0
  152. package/src/services/adminRunJob.ts +46 -0
  153. package/src/services/assignment.ts +11 -1
  154. package/src/services/class.ts +18 -0
  155. package/src/services/discussion.ts +701 -0
  156. package/src/services/studentProgress.ts +274 -11
  157. package/src/services/studentProgressInferenceCache.ts +64 -0
  158. package/src/services/worksheet.ts +4 -2
  159. package/src/trpc.ts +15 -0
  160. package/src/utils/adminPanelAccess.ts +46 -0
  161. package/tests/integration/routers/discussion.test.ts +253 -0
  162. package/tests/unit/services/discussion.test.ts +356 -0
  163. package/tests/unit/services/studentProgress.test.ts +168 -2
@@ -0,0 +1,140 @@
1
+ import type { Prisma } from '@prisma/client';
2
+ import { prisma } from '../lib/prisma.js';
3
+
4
+ export async function listSchoolsForAdmin(input: {
5
+ page: number;
6
+ pageSize: number;
7
+ query?: string;
8
+ }) {
9
+ const skip = input.page * input.pageSize;
10
+ const where: Prisma.SchoolWhereInput = {};
11
+ const q = input.query?.trim();
12
+ if (q) {
13
+ where.OR = [
14
+ { name: { contains: q, mode: 'insensitive' } },
15
+ { subdomain: { contains: q, mode: 'insensitive' } },
16
+ { description: { contains: q, mode: 'insensitive' } },
17
+ ];
18
+ }
19
+
20
+ const [items, total] = await Promise.all([
21
+ prisma.school.findMany({
22
+ where,
23
+ orderBy: { createdAt: 'desc' },
24
+ skip,
25
+ take: input.pageSize,
26
+ include: {
27
+ _count: {
28
+ select: {
29
+ memberships: true,
30
+ classes: true,
31
+ departments: true,
32
+ },
33
+ },
34
+ },
35
+ }),
36
+ prisma.school.count({ where }),
37
+ ]);
38
+
39
+ return {
40
+ items,
41
+ total,
42
+ page: input.page,
43
+ pageSize: input.pageSize,
44
+ pageCount: Math.max(1, Math.ceil(total / input.pageSize)),
45
+ };
46
+ }
47
+
48
+ export async function listDepartmentsForAdmin(input: {
49
+ page: number;
50
+ pageSize: number;
51
+ schoolId?: string;
52
+ }) {
53
+ const skip = input.page * input.pageSize;
54
+ const where: Prisma.DepartmentWhereInput = {};
55
+ if (input.schoolId) where.schoolId = input.schoolId;
56
+
57
+ const [items, total] = await Promise.all([
58
+ prisma.department.findMany({
59
+ where,
60
+ orderBy: [{ schoolId: 'asc' }, { name: 'asc' }],
61
+ skip,
62
+ take: input.pageSize,
63
+ include: {
64
+ school: { select: { id: true, name: true, subdomain: true } },
65
+ _count: { select: { classes: true, members: true } },
66
+ },
67
+ }),
68
+ prisma.department.count({ where }),
69
+ ]);
70
+
71
+ return {
72
+ items,
73
+ total,
74
+ page: input.page,
75
+ pageSize: input.pageSize,
76
+ pageCount: Math.max(1, Math.ceil(total / input.pageSize)),
77
+ };
78
+ }
79
+
80
+ export async function listSchoolDevelopmentProgramsForAdmin(input: {
81
+ page: number;
82
+ pageSize: number;
83
+ status?: string;
84
+ query?: string;
85
+ }) {
86
+ const skip = input.page * input.pageSize;
87
+ const where: Prisma.SchoolDevelopementProgramWhereInput = {};
88
+ if (input.status?.trim()) where.status = input.status.trim();
89
+ const q = input.query?.trim();
90
+ if (q) {
91
+ where.OR = [
92
+ { name: { contains: q, mode: 'insensitive' } },
93
+ { city: { contains: q, mode: 'insensitive' } },
94
+ { country: { contains: q, mode: 'insensitive' } },
95
+ { contactEmail: { contains: q, mode: 'insensitive' } },
96
+ ];
97
+ }
98
+
99
+ const [items, total] = await Promise.all([
100
+ prisma.schoolDevelopementProgram.findMany({
101
+ where,
102
+ orderBy: { submittedAt: 'desc' },
103
+ skip,
104
+ take: input.pageSize,
105
+ }),
106
+ prisma.schoolDevelopementProgram.count({ where }),
107
+ ]);
108
+
109
+ return {
110
+ items,
111
+ total,
112
+ page: input.page,
113
+ pageSize: input.pageSize,
114
+ pageCount: Math.max(1, Math.ceil(total / input.pageSize)),
115
+ };
116
+ }
117
+
118
+ export async function listEarlyAccessRequestsForAdmin(input: {
119
+ page: number;
120
+ pageSize: number;
121
+ }) {
122
+ const skip = input.page * input.pageSize;
123
+
124
+ const [items, total] = await Promise.all([
125
+ prisma.earlyAccessRequest.findMany({
126
+ orderBy: { createdAt: 'desc' },
127
+ skip,
128
+ take: input.pageSize,
129
+ }),
130
+ prisma.earlyAccessRequest.count(),
131
+ ]);
132
+
133
+ return {
134
+ items,
135
+ total,
136
+ page: input.page,
137
+ pageSize: input.pageSize,
138
+ pageCount: Math.max(1, Math.ceil(total / input.pageSize)),
139
+ };
140
+ }
@@ -0,0 +1,46 @@
1
+ import type { Prisma, RunJobStatus, RunJobType } from '@prisma/client';
2
+ import { TRPCError } from '@trpc/server';
3
+ import { prisma } from '../lib/prisma.js';
4
+
5
+ export async function listRunJobsForAdmin(input: {
6
+ page: number;
7
+ pageSize: number;
8
+ status?: RunJobStatus;
9
+ type?: RunJobType;
10
+ }) {
11
+ const { page, pageSize, status, type } = input;
12
+ const skip = page * pageSize;
13
+
14
+ const where: Prisma.RunJobWhereInput = {};
15
+ if (status) where.status = status;
16
+ if (type) where.type = type;
17
+
18
+ const [items, total] = await Promise.all([
19
+ prisma.runJob.findMany({
20
+ where,
21
+ orderBy: { createdAt: 'desc' },
22
+ skip,
23
+ take: pageSize,
24
+ }),
25
+ prisma.runJob.count({ where }),
26
+ ]);
27
+
28
+ return {
29
+ items,
30
+ total,
31
+ page,
32
+ pageSize,
33
+ pageCount: Math.max(1, Math.ceil(total / pageSize)),
34
+ };
35
+ }
36
+
37
+ export async function getRunJobForAdmin(id: string) {
38
+ const job = await prisma.runJob.findUnique({ where: { id } });
39
+ if (!job) {
40
+ throw new TRPCError({
41
+ code: 'NOT_FOUND',
42
+ message: 'Run job not found',
43
+ });
44
+ }
45
+ return job;
46
+ }
@@ -26,6 +26,7 @@ import { deleteFile } from "../lib/googleCloudStorage.js";
26
26
  import { sendToMultiple } from "./notification.js";
27
27
  import { enqueueRunJob } from "../workers/queues.js";
28
28
  import { logger } from "../utils/logger.js";
29
+ import { invalidateStudentProgressInferenceCache } from "./studentProgressInferenceCache.js";
29
30
 
30
31
  export async function assignmentExists(id: string) {
31
32
  const assignment = await findAssignmentById(id);
@@ -892,7 +893,7 @@ export async function updateSubmissionAsTeacherRecord(
892
893
  );
893
894
  }
894
895
 
895
- return prisma.submission.update({
896
+ const updated = await prisma.submission.update({
896
897
  where: { id: submissionId },
897
898
  data: {
898
899
  ...(gradeReceived !== undefined && { gradeReceived }),
@@ -906,6 +907,15 @@ export async function updateSubmissionAsTeacherRecord(
906
907
  },
907
908
  include: submissionTeacherInclude,
908
909
  });
910
+
911
+ if (gradeReceived !== undefined) {
912
+ invalidateStudentProgressInferenceCache(
913
+ submission.assignment.classId,
914
+ submission.studentId,
915
+ );
916
+ }
917
+
918
+ return updated;
909
919
  }
910
920
 
911
921
  export async function attachAssignmentToEventRecord(
@@ -26,6 +26,7 @@ import {
26
26
  createClassByImport,
27
27
  } from "../models/class.js";
28
28
  import { Class, Folder } from "@prisma/client";
29
+ import { invalidateStudentProgressInferenceCache } from "./studentProgressInferenceCache.js";
29
30
 
30
31
  async function requireActiveSchoolMembers(schoolId: string, userIds: string[]) {
31
32
  const uniqueUserIds = [...new Set(userIds)];
@@ -74,6 +75,9 @@ export async function getAllClasses(userId: string) {
74
75
  schoolId: cls.schoolId ?? null,
75
76
  school: cls.school ?? null,
76
77
  department: cls.department ?? null,
78
+ discussionsEnabled: cls.discussionsEnabled,
79
+ studentsCanCreateDiscussions: cls.studentsCanCreateDiscussions,
80
+ studentsCanReply: cls.studentsCanReply,
77
81
  })).filter((cls) => cls.members.some((member) => member.id === userId)),
78
82
  studentInClass: studentClasses.map((cls) => ({
79
83
  id: cls.id,
@@ -87,6 +91,9 @@ export async function getAllClasses(userId: string) {
87
91
  schoolId: cls.schoolId ?? null,
88
92
  school: cls.school ?? null,
89
93
  department: cls.department ?? null,
94
+ discussionsEnabled: cls.discussionsEnabled,
95
+ studentsCanCreateDiscussions: cls.studentsCanCreateDiscussions,
96
+ studentsCanReply: cls.studentsCanReply,
90
97
  })),
91
98
  };
92
99
  }
@@ -171,6 +178,9 @@ export async function updateClass(
171
178
  subject?: string;
172
179
  color?: string;
173
180
  departmentId?: string | null;
181
+ discussionsEnabled?: boolean;
182
+ studentsCanCreateDiscussions?: boolean;
183
+ studentsCanReply?: boolean;
174
184
  }
175
185
  ) {
176
186
  if (data.departmentId !== undefined) {
@@ -209,6 +219,9 @@ export async function updateClass(
209
219
  subject: true,
210
220
  color: true,
211
221
  departmentId: true,
222
+ discussionsEnabled: true,
223
+ studentsCanCreateDiscussions: true,
224
+ studentsCanReply: true,
212
225
  },
213
226
  });
214
227
  return { updatedClass };
@@ -498,10 +511,15 @@ export async function updateGrade(
498
511
  title: true,
499
512
  maxGrade: true,
500
513
  weight: true,
514
+ classId: true,
501
515
  },
502
516
  },
503
517
  },
504
518
  });
519
+ invalidateStudentProgressInferenceCache(
520
+ updatedSubmission.assignment.classId,
521
+ updatedSubmission.studentId,
522
+ );
505
523
  return updatedSubmission;
506
524
  }
507
525