@studious-lms/server 1.2.53 → 1.4.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 (479) hide show
  1. package/.coderabbit.yaml +9 -0
  2. package/.env.example +9 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +102 -8
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/config/env.d.ts +21 -0
  7. package/dist/lib/config/env.d.ts.map +1 -1
  8. package/dist/lib/config/env.js +8 -2
  9. package/dist/lib/config/env.js.map +1 -1
  10. package/dist/lib/fileUpload.d.ts.map +1 -1
  11. package/dist/lib/fileUpload.js +2 -2
  12. package/dist/lib/fileUpload.js.map +1 -1
  13. package/dist/lib/googleCloudStorage.d.ts +6 -0
  14. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  15. package/dist/lib/googleCloudStorage.js +19 -2
  16. package/dist/lib/googleCloudStorage.js.map +1 -1
  17. package/dist/lib/pusher.d.ts +4 -1
  18. package/dist/lib/pusher.d.ts.map +1 -1
  19. package/dist/lib/pusher.js +6 -3
  20. package/dist/lib/pusher.js.map +1 -1
  21. package/dist/lib/redis.d.ts +5 -0
  22. package/dist/lib/redis.d.ts.map +1 -0
  23. package/dist/lib/redis.js +53 -0
  24. package/dist/lib/redis.js.map +1 -0
  25. package/dist/lib/thumbnailGenerator.d.ts +0 -21
  26. package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
  27. package/dist/lib/thumbnailGenerator.js +157 -160
  28. package/dist/lib/thumbnailGenerator.js.map +1 -1
  29. package/dist/middleware/auth.d.ts.map +1 -1
  30. package/dist/middleware/auth.js +33 -95
  31. package/dist/middleware/auth.js.map +1 -1
  32. package/dist/models/agenda.d.ts +97 -0
  33. package/dist/models/agenda.d.ts.map +1 -0
  34. package/dist/models/agenda.js +40 -0
  35. package/dist/models/agenda.js.map +1 -0
  36. package/dist/models/announcement.d.ts +223 -0
  37. package/dist/models/announcement.d.ts.map +1 -0
  38. package/dist/models/announcement.js +120 -0
  39. package/dist/models/announcement.js.map +1 -0
  40. package/dist/models/assignment.d.ts +1292 -0
  41. package/dist/models/assignment.d.ts.map +1 -0
  42. package/dist/models/assignment.js +309 -0
  43. package/dist/models/assignment.js.map +1 -0
  44. package/dist/models/attendance.d.ts +180 -0
  45. package/dist/models/attendance.d.ts.map +1 -0
  46. package/dist/models/attendance.js +188 -0
  47. package/dist/models/attendance.js.map +1 -0
  48. package/dist/models/auth.d.ts +153 -0
  49. package/dist/models/auth.d.ts.map +1 -0
  50. package/dist/models/auth.js +217 -0
  51. package/dist/models/auth.js.map +1 -0
  52. package/dist/models/class.d.ts +461 -0
  53. package/dist/models/class.d.ts.map +1 -0
  54. package/dist/models/class.js +645 -0
  55. package/dist/models/class.js.map +1 -0
  56. package/dist/models/comment.d.ts +171 -0
  57. package/dist/models/comment.d.ts.map +1 -0
  58. package/dist/models/comment.js +138 -0
  59. package/dist/models/comment.js.map +1 -0
  60. package/dist/models/conversation.d.ts +164 -0
  61. package/dist/models/conversation.d.ts.map +1 -0
  62. package/dist/models/conversation.js +175 -0
  63. package/dist/models/conversation.js.map +1 -0
  64. package/dist/models/event.d.ts +295 -0
  65. package/dist/models/event.d.ts.map +1 -0
  66. package/dist/models/event.js +145 -0
  67. package/dist/models/event.js.map +1 -0
  68. package/dist/models/file.d.ts +536 -0
  69. package/dist/models/file.d.ts.map +1 -0
  70. package/dist/models/file.js +126 -0
  71. package/dist/models/file.js.map +1 -0
  72. package/dist/models/folder.d.ts +295 -0
  73. package/dist/models/folder.d.ts.map +1 -0
  74. package/dist/models/folder.js +202 -0
  75. package/dist/models/folder.js.map +1 -0
  76. package/dist/models/labChat.d.ts +243 -0
  77. package/dist/models/labChat.d.ts.map +1 -0
  78. package/dist/models/labChat.js +204 -0
  79. package/dist/models/labChat.js.map +1 -0
  80. package/dist/models/marketing.d.ts +72 -0
  81. package/dist/models/marketing.d.ts.map +1 -0
  82. package/dist/models/marketing.js +26 -0
  83. package/dist/models/marketing.js.map +1 -0
  84. package/dist/models/message.d.ts +100 -0
  85. package/dist/models/message.d.ts.map +1 -0
  86. package/dist/models/message.js +131 -0
  87. package/dist/models/message.js.map +1 -0
  88. package/dist/models/newtonChat.d.ts +72 -0
  89. package/dist/models/newtonChat.d.ts.map +1 -0
  90. package/dist/models/newtonChat.js +61 -0
  91. package/dist/models/newtonChat.js.map +1 -0
  92. package/dist/models/notification.d.ts +65 -0
  93. package/dist/models/notification.d.ts.map +1 -0
  94. package/dist/models/notification.js +46 -0
  95. package/dist/models/notification.js.map +1 -0
  96. package/dist/models/section.d.ts +102 -0
  97. package/dist/models/section.d.ts.map +1 -0
  98. package/dist/models/section.js +83 -0
  99. package/dist/models/section.js.map +1 -0
  100. package/dist/models/user.d.ts +39 -0
  101. package/dist/models/user.d.ts.map +1 -0
  102. package/dist/models/user.js +38 -0
  103. package/dist/models/user.js.map +1 -0
  104. package/dist/models/worksheet.d.ts +460 -0
  105. package/dist/models/worksheet.d.ts.map +1 -0
  106. package/dist/models/worksheet.js +200 -0
  107. package/dist/models/worksheet.js.map +1 -0
  108. package/dist/pipelines/aiLabChat.d.ts +76 -0
  109. package/dist/pipelines/aiLabChat.d.ts.map +1 -0
  110. package/dist/pipelines/aiLabChat.js +599 -0
  111. package/dist/pipelines/aiLabChat.js.map +1 -0
  112. package/dist/pipelines/aiNewtonChat.d.ts +30 -0
  113. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -0
  114. package/dist/pipelines/aiNewtonChat.js +289 -0
  115. package/dist/pipelines/aiNewtonChat.js.map +1 -0
  116. package/dist/pipelines/gradeWorksheet.d.ts +30 -0
  117. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -0
  118. package/dist/pipelines/gradeWorksheet.js +252 -0
  119. package/dist/pipelines/gradeWorksheet.js.map +1 -0
  120. package/dist/routers/_app.d.ts +1523 -1315
  121. package/dist/routers/_app.d.ts.map +1 -1
  122. package/dist/routers/agenda.d.ts +22 -22
  123. package/dist/routers/agenda.d.ts.map +1 -1
  124. package/dist/routers/agenda.js +4 -65
  125. package/dist/routers/agenda.js.map +1 -1
  126. package/dist/routers/announcement.d.ts +16 -16
  127. package/dist/routers/announcement.d.ts.map +1 -1
  128. package/dist/routers/announcement.js +37 -446
  129. package/dist/routers/announcement.js.map +1 -1
  130. package/dist/routers/assignment.d.ts +300 -378
  131. package/dist/routers/assignment.d.ts.map +1 -1
  132. package/dist/routers/assignment.js +78 -1868
  133. package/dist/routers/assignment.js.map +1 -1
  134. package/dist/routers/attendance.d.ts +19 -9
  135. package/dist/routers/attendance.d.ts.map +1 -1
  136. package/dist/routers/attendance.js +7 -264
  137. package/dist/routers/attendance.js.map +1 -1
  138. package/dist/routers/auth.d.ts +2 -2
  139. package/dist/routers/auth.d.ts.map +1 -1
  140. package/dist/routers/auth.js +29 -354
  141. package/dist/routers/auth.js.map +1 -1
  142. package/dist/routers/class.d.ts +160 -68
  143. package/dist/routers/class.d.ts.map +1 -1
  144. package/dist/routers/class.js +82 -1052
  145. package/dist/routers/class.js.map +1 -1
  146. package/dist/routers/comment.d.ts +6 -42
  147. package/dist/routers/comment.d.ts.map +1 -1
  148. package/dist/routers/comment.js +24 -244
  149. package/dist/routers/comment.js.map +1 -1
  150. package/dist/routers/conversation.d.ts +45 -7
  151. package/dist/routers/conversation.d.ts.map +1 -1
  152. package/dist/routers/conversation.js +19 -327
  153. package/dist/routers/conversation.js.map +1 -1
  154. package/dist/routers/event.d.ts +36 -36
  155. package/dist/routers/event.d.ts.map +1 -1
  156. package/dist/routers/event.js +13 -433
  157. package/dist/routers/event.js.map +1 -1
  158. package/dist/routers/file.d.ts +2 -2
  159. package/dist/routers/file.d.ts.map +1 -1
  160. package/dist/routers/file.js +9 -323
  161. package/dist/routers/file.js.map +1 -1
  162. package/dist/routers/folder.d.ts +21 -14
  163. package/dist/routers/folder.d.ts.map +1 -1
  164. package/dist/routers/folder.js +34 -745
  165. package/dist/routers/folder.js.map +1 -1
  166. package/dist/routers/labChat.d.ts +21 -11
  167. package/dist/routers/labChat.d.ts.map +1 -1
  168. package/dist/routers/labChat.js +22 -570
  169. package/dist/routers/labChat.js.map +1 -1
  170. package/dist/routers/marketing.d.ts +1 -1
  171. package/dist/routers/marketing.d.ts.map +1 -1
  172. package/dist/routers/marketing.js +7 -56
  173. package/dist/routers/marketing.js.map +1 -1
  174. package/dist/routers/message.d.ts +13 -2
  175. package/dist/routers/message.d.ts.map +1 -1
  176. package/dist/routers/message.js +32 -520
  177. package/dist/routers/message.js.map +1 -1
  178. package/dist/routers/newtonChat.d.ts +1 -1
  179. package/dist/routers/newtonChat.d.ts.map +1 -1
  180. package/dist/routers/newtonChat.js +7 -246
  181. package/dist/routers/newtonChat.js.map +1 -1
  182. package/dist/routers/notifications.d.ts +4 -4
  183. package/dist/routers/notifications.d.ts.map +1 -1
  184. package/dist/routers/notifications.js +18 -83
  185. package/dist/routers/notifications.js.map +1 -1
  186. package/dist/routers/section.d.ts +4 -4
  187. package/dist/routers/section.d.ts.map +1 -1
  188. package/dist/routers/section.js +14 -286
  189. package/dist/routers/section.js.map +1 -1
  190. package/dist/routers/user.d.ts +1 -1
  191. package/dist/routers/user.d.ts.map +1 -1
  192. package/dist/routers/user.js +32 -207
  193. package/dist/routers/user.js.map +1 -1
  194. package/dist/routers/worksheet.d.ts +68 -55
  195. package/dist/routers/worksheet.d.ts.map +1 -1
  196. package/dist/routers/worksheet.js +79 -394
  197. package/dist/routers/worksheet.js.map +1 -1
  198. package/dist/seedDatabase.d.ts +1 -1
  199. package/dist/server/pipelines/gradeWorksheet.d.ts +6 -6
  200. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
  201. package/dist/server/pipelines/gradeWorksheet.js +12 -5
  202. package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
  203. package/dist/services/agenda.d.ts +100 -0
  204. package/dist/services/agenda.d.ts.map +1 -0
  205. package/dist/services/agenda.js +21 -0
  206. package/dist/services/agenda.js.map +1 -0
  207. package/dist/services/announcement.d.ts +135 -0
  208. package/dist/services/announcement.d.ts.map +1 -0
  209. package/dist/services/announcement.js +223 -0
  210. package/dist/services/announcement.js.map +1 -0
  211. package/dist/services/assignment.d.ts +1462 -0
  212. package/dist/services/assignment.d.ts.map +1 -0
  213. package/dist/services/assignment.js +898 -0
  214. package/dist/services/assignment.js.map +1 -0
  215. package/dist/services/attendance.d.ts +93 -0
  216. package/dist/services/attendance.d.ts.map +1 -0
  217. package/dist/services/attendance.js +61 -0
  218. package/dist/services/attendance.js.map +1 -0
  219. package/dist/services/auth.d.ts +68 -0
  220. package/dist/services/auth.d.ts.map +1 -0
  221. package/dist/services/auth.js +218 -0
  222. package/dist/services/auth.js.map +1 -0
  223. package/dist/services/class.d.ts +643 -0
  224. package/dist/services/class.d.ts.map +1 -0
  225. package/dist/services/class.js +486 -0
  226. package/dist/services/class.js.map +1 -0
  227. package/dist/services/comment.d.ts +100 -0
  228. package/dist/services/comment.d.ts.map +1 -0
  229. package/dist/services/comment.js +83 -0
  230. package/dist/services/comment.js.map +1 -0
  231. package/dist/services/conversation.d.ts +159 -0
  232. package/dist/services/conversation.d.ts.map +1 -0
  233. package/dist/services/conversation.js +138 -0
  234. package/dist/services/conversation.js.map +1 -0
  235. package/dist/services/event.d.ts +216 -0
  236. package/dist/services/event.d.ts.map +1 -0
  237. package/dist/services/event.js +168 -0
  238. package/dist/services/event.js.map +1 -0
  239. package/dist/services/file.d.ts +74 -0
  240. package/dist/services/file.d.ts.map +1 -0
  241. package/dist/services/file.js +133 -0
  242. package/dist/services/file.js.map +1 -0
  243. package/dist/services/folder.d.ts +239 -0
  244. package/dist/services/folder.d.ts.map +1 -0
  245. package/dist/services/folder.js +248 -0
  246. package/dist/services/folder.js.map +1 -0
  247. package/dist/services/labChat.d.ts +169 -0
  248. package/dist/services/labChat.d.ts.map +1 -0
  249. package/dist/services/labChat.js +381 -0
  250. package/dist/services/labChat.js.map +1 -0
  251. package/dist/services/marketing.d.ts +50 -0
  252. package/dist/services/marketing.d.ts.map +1 -0
  253. package/dist/services/marketing.js +32 -0
  254. package/dist/services/marketing.js.map +1 -0
  255. package/dist/services/message.d.ts +103 -0
  256. package/dist/services/message.d.ts.map +1 -0
  257. package/dist/services/message.js +422 -0
  258. package/dist/services/message.js.map +1 -0
  259. package/dist/services/newtonChat.d.ts +22 -0
  260. package/dist/services/newtonChat.d.ts.map +1 -0
  261. package/dist/services/newtonChat.js +174 -0
  262. package/dist/services/newtonChat.js.map +1 -0
  263. package/dist/services/notification.d.ts +65 -0
  264. package/dist/services/notification.d.ts.map +1 -0
  265. package/dist/services/notification.js +33 -0
  266. package/dist/services/notification.js.map +1 -0
  267. package/dist/services/section.d.ts +53 -0
  268. package/dist/services/section.d.ts.map +1 -0
  269. package/dist/services/section.js +199 -0
  270. package/dist/services/section.js.map +1 -0
  271. package/dist/services/user.d.ts +48 -0
  272. package/dist/services/user.d.ts.map +1 -0
  273. package/dist/services/user.js +141 -0
  274. package/dist/services/user.js.map +1 -0
  275. package/dist/services/worksheet.d.ts +239 -0
  276. package/dist/services/worksheet.d.ts.map +1 -0
  277. package/dist/services/worksheet.js +235 -0
  278. package/dist/services/worksheet.js.map +1 -0
  279. package/dist/utils/aiUser.d.ts +1 -3
  280. package/dist/utils/aiUser.d.ts.map +1 -1
  281. package/dist/utils/aiUser.js +6 -5
  282. package/dist/utils/aiUser.js.map +1 -1
  283. package/dist/utils/email.d.ts +3 -0
  284. package/dist/utils/email.d.ts.map +1 -1
  285. package/dist/utils/email.js +7 -4
  286. package/dist/utils/email.js.map +1 -1
  287. package/dist/utils/generateInviteCode.d.ts +1 -2
  288. package/dist/utils/generateInviteCode.d.ts.map +1 -1
  289. package/dist/utils/generateInviteCode.js +3 -4
  290. package/dist/utils/generateInviteCode.js.map +1 -1
  291. package/dist/utils/inference.d.ts +3 -0
  292. package/dist/utils/inference.d.ts.map +1 -1
  293. package/dist/utils/inference.js +7 -4
  294. package/dist/utils/inference.js.map +1 -1
  295. package/dist/utils/logger.d.ts +3 -0
  296. package/dist/utils/logger.d.ts.map +1 -1
  297. package/dist/utils/logger.js +5 -2
  298. package/dist/utils/logger.js.map +1 -1
  299. package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
  300. package/dist/utils/prismaErrorHandler.js +5 -2
  301. package/dist/utils/prismaErrorHandler.js.map +1 -1
  302. package/dist/utils/prismaWrapper.d.ts +1 -0
  303. package/dist/utils/prismaWrapper.d.ts.map +1 -1
  304. package/dist/utils/prismaWrapper.js +6 -2
  305. package/dist/utils/prismaWrapper.js.map +1 -1
  306. package/docker-compose.yml +5 -0
  307. package/package.json +4 -3
  308. package/prisma/schema.prisma +1 -1
  309. package/src/index.ts +119 -12
  310. package/src/lib/config/env.ts +6 -0
  311. package/src/lib/fileUpload.ts +0 -1
  312. package/src/lib/googleCloudStorage.ts +17 -0
  313. package/src/lib/pusher.ts +5 -1
  314. package/src/lib/redis.ts +56 -0
  315. package/src/lib/thumbnailGenerator.ts +170 -168
  316. package/src/middleware/auth.ts +80 -137
  317. package/src/models/agenda.ts +46 -0
  318. package/src/models/announcement.ts +134 -0
  319. package/src/models/assignment.ts +322 -0
  320. package/src/models/attendance.ts +208 -0
  321. package/src/models/auth.ts +247 -0
  322. package/src/models/class.ts +703 -0
  323. package/src/models/comment.ts +152 -0
  324. package/src/models/conversation.ts +200 -0
  325. package/src/models/event.ts +177 -0
  326. package/src/models/file.ts +129 -0
  327. package/src/models/folder.ts +225 -0
  328. package/src/models/labChat.ts +213 -0
  329. package/src/models/marketing.ts +45 -0
  330. package/src/models/message.ts +153 -0
  331. package/src/models/newtonChat.ts +70 -0
  332. package/src/models/notification.ts +54 -0
  333. package/src/models/section.ts +98 -0
  334. package/src/models/user.ts +47 -0
  335. package/src/models/worksheet.ts +294 -0
  336. package/src/pipelines/aiLabChat.ts +684 -0
  337. package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +9 -5
  338. package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
  339. package/src/routers/agenda.ts +3 -66
  340. package/src/routers/announcement.ts +54 -495
  341. package/src/routers/assignment.ts +126 -2018
  342. package/src/routers/attendance.ts +15 -276
  343. package/src/routers/auth.ts +79 -442
  344. package/src/routers/class.ts +263 -1187
  345. package/src/routers/comment.ts +61 -288
  346. package/src/routers/conversation.ts +51 -360
  347. package/src/routers/event.ts +50 -481
  348. package/src/routers/file.ts +45 -368
  349. package/src/routers/folder.ts +107 -836
  350. package/src/routers/labChat.ts +35 -604
  351. package/src/routers/marketing.ts +35 -77
  352. package/src/routers/message.ts +54 -567
  353. package/src/routers/newtonChat.ts +17 -278
  354. package/src/routers/notifications.ts +32 -82
  355. package/src/routers/section.ts +46 -330
  356. package/src/routers/user.ts +49 -227
  357. package/src/routers/worksheet.ts +215 -503
  358. package/src/services/agenda.ts +21 -0
  359. package/src/services/announcement.ts +290 -0
  360. package/src/services/assignment.ts +1198 -0
  361. package/src/services/attendance.ts +85 -0
  362. package/src/services/auth.ts +277 -0
  363. package/src/services/class.ts +629 -0
  364. package/src/services/comment.ts +106 -0
  365. package/src/services/conversation.ts +213 -0
  366. package/src/services/event.ts +231 -0
  367. package/src/services/file.ts +167 -0
  368. package/src/services/folder.ts +316 -0
  369. package/src/services/labChat.ts +458 -0
  370. package/src/services/marketing.ts +57 -0
  371. package/src/services/message.ts +554 -0
  372. package/src/services/newtonChat.ts +222 -0
  373. package/src/services/notification.ts +50 -0
  374. package/src/services/section.ts +283 -0
  375. package/src/services/user.ts +172 -0
  376. package/src/services/worksheet.ts +358 -0
  377. package/src/utils/aiUser.ts +4 -3
  378. package/src/utils/email.ts +5 -3
  379. package/src/utils/generateInviteCode.ts +1 -3
  380. package/src/utils/inference.ts +5 -2
  381. package/src/utils/logger.ts +3 -1
  382. package/src/utils/prismaErrorHandler.ts +3 -0
  383. package/src/utils/prismaWrapper.ts +4 -0
  384. package/tests/globalSetup.ts +62 -0
  385. package/tests/helpers.ts +22 -0
  386. package/tests/middleware/security.test.ts +42 -0
  387. package/tests/routers/agenda.test.ts +138 -0
  388. package/tests/routers/announcement.test.ts +490 -0
  389. package/tests/routers/assignment.test.ts +837 -0
  390. package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
  391. package/tests/routers/auth.test.ts +171 -0
  392. package/tests/{class.test.ts → routers/class.test.ts} +131 -85
  393. package/tests/routers/comment.test.ts +126 -0
  394. package/tests/routers/conversation.test.ts +145 -0
  395. package/tests/{event.test.ts → routers/event.test.ts} +93 -32
  396. package/tests/routers/folder.test.ts +178 -0
  397. package/tests/routers/labChat.test.ts +115 -0
  398. package/tests/routers/marketing.test.ts +59 -0
  399. package/tests/routers/message.test.ts +123 -0
  400. package/tests/routers/notification.test.ts +69 -0
  401. package/tests/{section.test.ts → routers/section.test.ts} +5 -13
  402. package/tests/server/rateLimit.test.ts +73 -0
  403. package/tests/setup.ts +18 -92
  404. package/tests/user.test.ts +9 -31
  405. package/tests/utils/aiUser.test.ts +22 -0
  406. package/tests/utils/generateInviteCode.test.ts +24 -0
  407. package/tests/utils/logger.test.ts +74 -0
  408. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  409. package/tests/utils/prismaWrapper.test.ts +82 -0
  410. package/tests/worksheet.test.ts +181 -0
  411. package/vitest.config.ts +6 -3
  412. package/vitest.unit.config.ts +21 -0
  413. package/TODO.md +0 -2
  414. package/coverage/base.css +0 -224
  415. package/coverage/block-navigation.js +0 -87
  416. package/coverage/clover.xml +0 -12110
  417. package/coverage/coverage-final.json +0 -44
  418. package/coverage/favicon.png +0 -0
  419. package/coverage/index.html +0 -221
  420. package/coverage/prettify.css +0 -1
  421. package/coverage/prettify.js +0 -2
  422. package/coverage/server/index.html +0 -116
  423. package/coverage/server/src/exportType.ts.html +0 -109
  424. package/coverage/server/src/index.html +0 -161
  425. package/coverage/server/src/index.ts.html +0 -1702
  426. package/coverage/server/src/instrument.ts.html +0 -130
  427. package/coverage/server/src/lib/config/env.ts.html +0 -448
  428. package/coverage/server/src/lib/config/index.html +0 -116
  429. package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
  430. package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
  431. package/coverage/server/src/lib/index.html +0 -206
  432. package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
  433. package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
  434. package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
  435. package/coverage/server/src/lib/pusher.ts.html +0 -121
  436. package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
  437. package/coverage/server/src/middleware/auth.ts.html +0 -646
  438. package/coverage/server/src/middleware/index.html +0 -146
  439. package/coverage/server/src/middleware/logging.ts.html +0 -244
  440. package/coverage/server/src/middleware/security.ts.html +0 -271
  441. package/coverage/server/src/routers/_app.ts.html +0 -232
  442. package/coverage/server/src/routers/agenda.ts.html +0 -319
  443. package/coverage/server/src/routers/announcement.ts.html +0 -3481
  444. package/coverage/server/src/routers/assignment.ts.html +0 -7633
  445. package/coverage/server/src/routers/attendance.ts.html +0 -1030
  446. package/coverage/server/src/routers/auth.ts.html +0 -1081
  447. package/coverage/server/src/routers/class.ts.html +0 -3535
  448. package/coverage/server/src/routers/comment.ts.html +0 -991
  449. package/coverage/server/src/routers/conversation.ts.html +0 -982
  450. package/coverage/server/src/routers/event.ts.html +0 -1609
  451. package/coverage/server/src/routers/file.ts.html +0 -1144
  452. package/coverage/server/src/routers/folder.ts.html +0 -2797
  453. package/coverage/server/src/routers/index.html +0 -386
  454. package/coverage/server/src/routers/labChat.ts.html +0 -3073
  455. package/coverage/server/src/routers/marketing.ts.html +0 -340
  456. package/coverage/server/src/routers/message.ts.html +0 -1912
  457. package/coverage/server/src/routers/notifications.ts.html +0 -364
  458. package/coverage/server/src/routers/section.ts.html +0 -1120
  459. package/coverage/server/src/routers/user.ts.html +0 -862
  460. package/coverage/server/src/routers/worksheet.ts.html +0 -1729
  461. package/coverage/server/src/trpc.ts.html +0 -397
  462. package/coverage/server/src/types/index.html +0 -116
  463. package/coverage/server/src/types/trpc.ts.html +0 -127
  464. package/coverage/server/src/utils/aiUser.ts.html +0 -280
  465. package/coverage/server/src/utils/email.ts.html +0 -121
  466. package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
  467. package/coverage/server/src/utils/index.html +0 -206
  468. package/coverage/server/src/utils/inference.ts.html +0 -709
  469. package/coverage/server/src/utils/logger.ts.html +0 -664
  470. package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
  471. package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
  472. package/coverage/server/vitest.config.ts.html +0 -196
  473. package/coverage/sort-arrow-sprite.png +0 -0
  474. package/coverage/sorter.js +0 -210
  475. package/src/lib/notificationHandler.ts +0 -36
  476. package/src/server/pipelines/aiLabChat.ts +0 -507
  477. package/tests/announcement.test.ts +0 -164
  478. package/tests/assignment.test.ts +0 -296
  479. package/tests/auth.test.ts +0 -48
@@ -1,1021 +1,248 @@
1
1
  import { z } from "zod";
2
- import { createTRPCRouter, protectedProcedure, protectedTeacherProcedure, protectedClassMemberProcedure } from "../trpc.js";
3
- import { prisma } from "../lib/prisma.js";
4
- import { TRPCError } from "@trpc/server";
5
- import { generateInviteCode } from "../utils/generateInviteCode.js";
2
+ import {
3
+ createTRPCRouter,
4
+ protectedProcedure,
5
+ protectedTeacherProcedure,
6
+ protectedClassMemberProcedure,
7
+ } from "../trpc.js";
8
+ import {
9
+ getAllClasses,
10
+ getClass,
11
+ updateClass,
12
+ createClass,
13
+ deleteClass,
14
+ addStudent,
15
+ changeRole,
16
+ removeMember,
17
+ leaveClass,
18
+ joinClass,
19
+ getInviteCode,
20
+ createInviteCode,
21
+ getGrades,
22
+ updateGrade,
23
+ getEvents,
24
+ listMarkSchemes,
25
+ createMarkScheme,
26
+ updateMarkScheme,
27
+ deleteMarkScheme,
28
+ listGradingBoundaries,
29
+ createGradingBoundary,
30
+ updateGradingBoundary,
31
+ deleteGradingBoundary,
32
+ getSyllabus,
33
+ updateSyllabus,
34
+ listLabDrafts,
35
+ createLabDraft,
36
+ updateLabDraft,
37
+ deleteLabDraft,
38
+ publishLabDraft,
39
+ getFiles,
40
+ exportClass,
41
+ importClass,
42
+ } from "../services/class.js";
6
43
 
7
44
  export const classRouter = createTRPCRouter({
8
- getAll: protectedProcedure
9
-
10
- .query(async ({ ctx }) => {
11
- const [teacherClasses, studentClasses] = await Promise.all([
12
- prisma.class.findMany({
13
- where: {
14
- teachers: {
15
- some: {
16
- id: ctx.user?.id,
17
- },
18
- },
19
- },
20
- include: {
21
- assignments: {
22
- where: {
23
- dueDate: {
24
- lte: new Date(new Date().setHours(23, 59, 59, 999)),
25
- },
26
- template: false,
27
- },
28
- select: {
45
+ getAll: protectedProcedure.query(({ ctx }) =>
46
+ getAllClasses(ctx.user?.id ?? "")
47
+ ),
29
48
 
30
- id: true,
31
- title: true,
32
- type: true,
33
- dueDate: true,
34
- },
35
- },
36
- students: {
37
- select: {
38
- id: true,
39
- username: true,
40
- profile: {
41
- select: {
42
- displayName: true,
43
- profilePicture: true,
44
- profilePictureThumbnail: true,
45
- },
46
- },
47
- },
48
- },
49
- teachers: {
50
- select: {
51
- id: true,
52
- username: true,
53
- profile: {
54
- select: {
55
- displayName: true,
56
- profilePicture: true,
57
- profilePictureThumbnail: true,
58
- },
59
- },
60
- },
61
- },
62
- },
63
- }),
64
- prisma.class.findMany({
65
- where: {
66
- students: {
67
- some: {
68
- id: ctx.user?.id,
69
- },
70
- },
71
- },
72
- include: {
73
- assignments: {
74
- where: {
75
- dueDate: {
76
- lte: new Date(new Date().setHours(23, 59, 59, 999)),
77
- },
78
- template: false,
79
- },
80
- select: {
81
- id: true,
82
- title: true,
83
- type: true,
84
- dueDate: true,
85
- },
86
- },
87
- students: {
88
- select: {
89
- id: true,
90
- username: true,
91
- profile: {
92
- select: {
93
- displayName: true,
94
- profilePicture: true,
95
- profilePictureThumbnail: true,
96
- },
97
- },
98
- },
99
- },
100
- teachers: {
101
- select: {
102
- id: true,
103
- username: true,
104
- profile: {
105
- select: {
106
- displayName: true,
107
- profilePicture: true,
108
- profilePictureThumbnail: true,
109
- },
110
- },
111
- },
112
- },
113
- },
114
- }),
115
- ]);
116
-
117
- return {
118
- teacherInClass: teacherClasses.map(cls => ({
119
- id: cls.id,
120
- name: cls.name,
121
- section: cls.section,
122
- subject: cls.subject,
123
- dueToday: cls.assignments,
124
- assignments: cls.assignments,
125
- members: [...cls.students, ...cls.teachers],
126
- color: cls.color,
127
- })),
128
- studentInClass: studentClasses.map(cls => ({
129
- id: cls.id,
130
- name: cls.name,
131
- section: cls.section,
132
- subject: cls.subject,
133
- dueToday: cls.assignments,
134
- assignments: cls.assignments,
135
- members: [...cls.students, ...cls.teachers],
136
- color: cls.color,
137
- })),
138
- };
139
- }),
140
49
  get: protectedProcedure
141
- .input(z.object({
142
- classId: z.string(),
143
- }))
144
- .query(async ({ ctx, input }) => {
145
- const { classId } = input;
146
-
147
- const isTeacher = await prisma.class.findFirst({
148
- where: {
149
- id: classId,
150
- teachers: {
151
- some: { id: ctx.user?.id },
152
- },
153
- },
154
- });
155
-
156
- const classData = await prisma.class.findUnique({
157
- where: {
158
- id: classId,
159
- },
160
- include: {
161
- teachers: {
162
- select: {
163
- id: true,
164
- username: true,
165
- profile: {
166
- select: {
167
- displayName: true,
168
- profilePicture: true,
169
- profilePictureThumbnail: true,
170
- }
171
- }
172
- },
173
- },
174
- students: {
175
- select: {
176
- id: true,
177
- username: true,
178
- profile: {
179
- select: {
180
- displayName: true,
181
- profilePicture: true,
182
- profilePictureThumbnail: true,
183
- },
184
- },
185
- },
186
- },
187
- announcements: {
188
- orderBy: {
189
- createdAt: 'desc',
190
- },
191
- select: {
192
- id: true,
193
- remarks: true,
194
- createdAt: true,
195
- modifiedAt: true,
196
- teacher: {
197
- select: {
198
- id: true,
199
- username: true,
200
- profile: {
201
- select: {
202
- displayName: true,
203
- profilePicture: true,
204
- profilePictureThumbnail: true,
205
- },
206
- },
207
- },
208
- },
209
- },
210
- },
211
- assignments: {
212
- ...(!isTeacher && {
213
- where: {OR: [
214
- {
215
- assignedTo: {
216
- some: {
217
- id: ctx.user?.id,
218
- },
219
- },
220
- },
221
- {
222
- assignedTo: {
223
- none: {},
224
- },
225
- },
226
- ],}
227
- }),
228
- select: {
229
- type: true,
230
- id: true,
231
- title: true,
232
- dueDate: true,
233
- createdAt: true,
234
- weight: true,
235
- order: true,
236
- graded: true,
237
- maxGrade: true,
238
- instructions: true,
239
- inProgress: true,
240
- template: false,
241
- section: {
242
- select: {
243
- id: true,
244
- name: true,
245
- },
246
- },
247
- markScheme: {
248
- select: {
249
- id: true,
250
- structured: true,
251
- },
252
- },
253
- gradingBoundary: {
254
- select: {
255
- id: true,
256
- structured: true,
257
- },
258
- },
259
- submissions: {
260
- ...(!isTeacher && {
261
- where: {
262
- studentId: ctx.user?.id,
263
- },
264
- }),
265
- select: {
266
- studentId: true,
267
- id: true,
268
- submitted: true,
269
- gradeReceived: true,
270
- rubricState: true,
271
- teacherComments: true,
272
- returned: true,
273
- submittedAt: true,
274
- },
275
- },
276
- },
277
- },
278
- },
279
- });
280
-
281
-
282
- if (!classData) {
283
- throw new Error('Class not found');
284
- }
285
-
286
- const formattedClassData = {
287
- ...classData,
288
- assignments: classData.assignments.map(assignment => ({
289
- ...assignment,
290
- late: assignment.dueDate < new Date(),
291
- submitted: assignment.submissions.find(submission => submission.studentId === ctx.user?.id)?.submitted,
292
- returned: assignment.submissions.find(submission => submission.studentId === ctx.user?.id)?.returned,
293
- })),
294
- }
295
-
296
- const sections = await prisma.section.findMany({
297
- where: {
298
- classId: classId,
299
- },
300
- });
301
-
302
- return {
303
- class: {
304
- ...formattedClassData,
305
- sections,
306
- },
307
- };
308
- }),
50
+ .input(z.object({ classId: z.string() }))
51
+ .query(({ ctx, input }) =>
52
+ getClass(ctx.user?.id ?? "", input.classId)
53
+ ),
309
54
  update: protectedTeacherProcedure
310
- .input(z.object({
311
- classId: z.string(),
312
- name: z.string().optional(),
313
- section: z.string().optional(),
314
- subject: z.string().optional(),
315
- }))
316
- .mutation(async ({ ctx, input }) => {
55
+ .input(
56
+ z.object({
57
+ classId: z.string(),
58
+ name: z.string().optional(),
59
+ section: z.string().optional(),
60
+ subject: z.string().optional(),
61
+ })
62
+ )
63
+ .mutation(({ input }) => {
317
64
  const { classId, ...updateData } = input;
318
-
319
- const updatedClass = await prisma.class.update({
320
- where: {
321
- id: classId,
322
- },
323
- data: updateData,
324
- select: {
325
- id: true,
326
- name: true,
327
- section: true,
328
- subject: true,
329
- }
330
- });
331
-
332
- return {
333
- updatedClass,
334
- }
65
+ return updateClass(classId, updateData);
335
66
  }),
67
+
336
68
  create: protectedProcedure
337
- .input(z.object({
338
- students: z.array(z.string()).optional(),
339
- teachers: z.array(z.string()).optional(),
340
- name: z.string(),
341
- section: z.string(),
342
- subject: z.string(),
343
- color: z.string().optional(),
344
- }))
345
- .mutation(async ({ ctx, input }) => {
346
- const { students, teachers, name, section, subject, color } = input;
347
-
348
- if (teachers && teachers.length > 0 && students && students.length > 0) {
349
- const newClass = await prisma.class.create({
350
- data: {
351
- name,
352
- section,
353
- subject,
354
- color,
355
- teachers: {
356
- connect: teachers.map(teacher => ({ id: teacher })),
357
- },
358
- students: {
359
- connect: students.map(student => ({ id: student })),
360
- },
361
- },
362
- include: {
363
- teachers: true,
364
- students: true,
365
- },
366
- });
367
- return newClass;
368
- }
69
+ .input(
70
+ z.object({
71
+ students: z.array(z.string()).optional(),
72
+ teachers: z.array(z.string()).optional(),
73
+ name: z.string(),
74
+ section: z.string(),
75
+ subject: z.string(),
76
+ color: z.string().optional(),
77
+ })
78
+ )
79
+ .mutation(({ ctx, input }) =>
80
+ createClass(ctx.user?.id ?? "", input)
81
+ ),
369
82
 
370
- const newClass = await prisma.class.create({
371
- data: {
372
- name,
373
- section,
374
- subject,
375
- color,
376
- teachers: {
377
- connect: {
378
- id: ctx.user?.id,
379
- },
380
- },
381
- },
382
- });
383
-
384
- return newClass;
385
- }),
386
83
  delete: protectedTeacherProcedure
387
- .input(z.object({
388
- classId: z.string(),
389
- id: z.string(),
390
- }))
391
- .mutation(async ({ ctx, input }) => {
392
- // Verify user is the teacher of this class
393
- const classToDelete = await prisma.class.findFirst({
394
- where: {
395
- id: input.id,
396
- },
397
- });
398
-
399
- if (!classToDelete) {
400
- throw new Error("Class not found or you don't have permission to delete it");
401
- }
402
-
403
- await prisma.class.delete({
404
- where: {
405
- id: input.id,
406
- },
407
- });
408
-
409
- return {
410
- deletedClass: {
411
- id: input.id,
412
- }
413
- }
414
- }),
84
+ .input(z.object({ classId: z.string(), id: z.string() }))
85
+ .mutation(({ input }) => deleteClass(input.id)),
415
86
  addStudent: protectedTeacherProcedure
416
- .input(z.object({
417
- classId: z.string(),
418
- studentId: z.string(),
419
- }))
420
- .mutation(async ({ ctx, input }) => {
421
- const { classId, studentId } = input;
422
-
423
- const student = await prisma.user.findUnique({
424
- where: {
425
- id: studentId,
426
- },
427
- });
87
+ .input(z.object({ classId: z.string(), studentId: z.string() }))
88
+ .mutation(({ input }) => addStudent(input.classId, input.studentId)),
428
89
 
429
- if (!student) {
430
- throw new Error("Student not found");
431
- }
432
-
433
- const updatedClass = await prisma.class.update({
434
- where: {
435
- id: classId,
436
- },
437
- data: {
438
- students: {
439
- connect: {
440
- id: studentId,
441
- },
442
- },
443
- },
444
- select: {
445
- id: true,
446
- name: true,
447
- section: true,
448
- subject: true,
449
- }
450
- });
451
-
452
- return {
453
- updatedClass,
454
- newStudent: student,
455
- }
456
- }),
457
90
  changeRole: protectedTeacherProcedure
458
- .input(z.object({
459
- classId: z.string(),
460
- userId: z.string(),
461
- type: z.enum(['teacher', 'student']),
462
- }))
463
- .mutation(async ({ ctx, input }) => {
464
- const { classId, userId, type } = input;
465
-
466
- const user = await prisma.user.findUnique({
467
- where: { id: userId },
468
- select: {
469
- id: true,
470
- username: true,
471
- },
472
- });
473
-
474
- if (!user) {
475
- throw new Error("User not found");
476
- }
477
-
478
- const updatedClass = await prisma.class.update({
479
- where: { id: classId },
480
- data: {
481
- [type === 'teacher' ? 'teachers' : 'students']: {
482
- connect: { id: userId },
483
- },
484
- [type === 'teacher' ? 'students' : 'teachers']: {
485
- disconnect: { id: userId },
486
- },
487
- },
488
- });
91
+ .input(
92
+ z.object({
93
+ classId: z.string(),
94
+ userId: z.string(),
95
+ type: z.enum(["teacher", "student"]),
96
+ })
97
+ )
98
+ .mutation(({ input }) =>
99
+ changeRole(input.classId, input.userId, input.type)
100
+ ),
489
101
 
490
- return {
491
- updatedClass,
492
- user: {
493
- ...user,
494
- type,
495
- },
496
- };
497
- }),
498
102
  removeMember: protectedTeacherProcedure
499
- .input(z.object({
500
- classId: z.string(),
501
- userId: z.string(),
502
- }))
503
- .mutation(async ({ ctx, input }) => {
504
- const { classId, userId } = input;
103
+ .input(z.object({ classId: z.string(), userId: z.string() }))
104
+ .mutation(({ input }) => removeMember(input.classId, input.userId)),
505
105
 
506
- const updatedClass = await prisma.class.update({
507
- where: { id: classId },
508
- data: {
509
- teachers: {
510
- disconnect: { id: userId },
511
- },
512
- students: {
513
- disconnect: { id: userId },
514
- },
515
- },
516
- });
517
-
518
- return {
519
- updatedClass,
520
- removedUserId: userId,
521
- };
522
- }),
523
106
  leaveClass: protectedProcedure
524
- .input(z.object({
525
- classId: z.string(),
526
- }))
527
- .mutation(async ({ ctx, input }) => {
528
- const { classId } = input;
529
- const userId = ctx.user?.id;
530
-
531
- if (!userId) {
532
- throw new TRPCError({
533
- code: 'UNAUTHORIZED',
534
- message: 'User not authenticated',
535
- });
536
- }
537
-
538
- const classData = await prisma.class.findFirst({
539
- where: {
540
- id: classId,
541
- students: {
542
- some: { id: userId },
543
- },
544
- },
545
- });
546
-
547
- if (!classData) {
548
- throw new TRPCError({
549
- code: 'NOT_FOUND',
550
- message: 'Class not found or you are not a student in this class',
551
- });
107
+ .input(z.object({ classId: z.string() }))
108
+ .mutation(({ ctx, input }) => {
109
+ if (!ctx.user?.id) {
110
+ throw new Error("User not authenticated");
552
111
  }
553
-
554
- await prisma.class.update({
555
- where: { id: classId },
556
- data: {
557
- students: {
558
- disconnect: { id: userId },
559
- },
560
- },
561
- });
562
-
563
- return {
564
- success: true,
565
- leftClassId: classId,
566
- };
112
+ return leaveClass(ctx.user.id, input.classId);
567
113
  }),
568
- join: protectedProcedure
569
- .input(z.object({
570
- classCode: z.string(),
571
- }))
572
- .mutation(async ({ ctx, input }) => {
573
- const { classCode } = input;
574
-
575
- // Case-insensitive search for invite code
576
- const session = await prisma.session.findFirst({
577
- where: {
578
- id: {
579
- equals: classCode,
580
- mode: 'insensitive',
581
- },
582
- },
583
- });
584
-
585
- if (!session || !session.classId) {
586
- throw new Error("Class not found");
587
- }
588
114
 
589
- if (session.expiresAt && session.expiresAt < new Date()) {
590
- throw new Error("Session expired");
591
- }
592
-
593
- const updatedClass = await prisma.class.update({
594
- where: { id: session.classId },
595
- data: {
596
- students: {
597
- connect: { id: ctx.user?.id },
598
- },
599
- },
600
- select: {
601
- id: true,
602
- name: true,
603
- section: true,
604
- subject: true,
605
- },
606
- });
115
+ join: protectedProcedure
116
+ .input(z.object({ classCode: z.string() }))
117
+ .mutation(({ ctx, input }) =>
118
+ joinClass(ctx.user?.id ?? "", input.classCode)
119
+ ),
607
120
 
608
- return {
609
- joinedClass: updatedClass,
610
- }
611
- }),
612
121
  getInviteCode: protectedTeacherProcedure
613
- .input(z.object({
614
- classId: z.string(),
615
- }))
616
- .query(async ({ ctx, input }) => {
617
- const { classId } = input;
618
-
619
- const session = await prisma.session.findFirst({
620
- where: {
621
- classId,
622
- },
623
- });
624
-
625
- if ((session?.expiresAt && session.expiresAt < new Date()) || !session) {
626
- const newSession = await prisma.session.create({
627
- data: {
628
- id: generateInviteCode(),
629
- classId,
630
- expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours from now
631
- }
632
- });
633
- return {
634
- code: newSession.id,
635
- }
636
- }
122
+ .input(z.object({ classId: z.string() }))
123
+ .query(({ input }) => getInviteCode(input.classId)),
637
124
 
638
- return {
639
- code: session?.id,
640
- };
641
- }),
642
125
  createInviteCode: protectedTeacherProcedure
643
- .input(z.object({
644
- classId: z.string(),
645
- }))
646
- .mutation(async ({ ctx, input }) => {
647
- const { classId } = input;
648
-
649
- await prisma.session.deleteMany({
650
- where: {
651
- classId,
652
- },
653
- });
654
-
655
- // Create a new session for the invite code
656
- const session = await prisma.session.create({
657
- data: {
658
- id: generateInviteCode(),
659
- classId,
660
- expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours from now
661
- }
662
- });
663
-
664
- return {
665
- code: session.id,
666
- };
667
- }),
126
+ .input(z.object({ classId: z.string() }))
127
+ .mutation(({ input }) => createInviteCode(input.classId)),
668
128
  getGrades: protectedClassMemberProcedure
669
- .input(z.object({
670
- classId: z.string(),
671
- userId: z.string(),
672
- }))
673
- .query(async ({ ctx, input }) => {
674
- const { classId, userId } = input;
675
-
676
- const isTeacher = await prisma.class.findFirst({
677
- where: {
678
- id: classId,
679
- teachers: {
680
- some: { id: ctx.user?.id }
681
- }
682
- }
683
- });
684
- // If student, only allow viewing their own grades
685
- if (ctx.user?.id !== userId && !isTeacher) {
686
- throw new TRPCError({
687
- code: 'UNAUTHORIZED',
688
- message: 'You can only view your own grades',
689
- });
690
- }
129
+ .input(z.object({ classId: z.string(), userId: z.string() }))
130
+ .query(({ ctx, input }) =>
131
+ getGrades(ctx.user?.id ?? "", input.classId, input.userId)
132
+ ),
691
133
 
692
- const grades = await prisma.submission.findMany({
693
- where: {
694
- studentId: userId,
695
- assignment: {
696
- classId: classId,
697
- graded: true
698
- }
699
- },
700
- include: {
701
- assignment: {
702
- select: {
703
- id: true,
704
- title: true,
705
- maxGrade: true,
706
- weight: true,
707
- markSchemeId: true,
708
- dueDate: true,
709
- markScheme: {
710
- select: {
711
- structured: true,
712
- }
713
- },
714
- gradingBoundaryId: true,
715
- gradingBoundary: {
716
- select: {
717
- structured: true,
718
- }
719
- },
720
- }
721
- },
722
- }
723
- });
724
-
725
- return {
726
- grades,
727
- };
728
- }),
729
134
  updateGrade: protectedTeacherProcedure
730
- .input(z.object({
731
- classId: z.string(),
732
- assignmentId: z.string(),
733
- submissionId: z.string(),
734
- gradeReceived: z.number().nullable(),
735
- }))
736
- .mutation(async ({ ctx, input }) => {
737
- const { classId, assignmentId, submissionId, gradeReceived } = input;
738
-
739
- // Update the grade
740
- const updatedSubmission = await prisma.submission.update({
741
- where: {
742
- id: submissionId,
743
- assignmentId: assignmentId,
744
- },
745
- data: {
746
- gradeReceived,
747
- },
748
- include: {
749
- assignment: {
750
- select: {
751
- id: true,
752
- title: true,
753
- maxGrade: true,
754
- weight: true,
755
- }
756
- }
757
- }
758
- });
759
-
760
- return updatedSubmission;
761
- }),
762
- getEvents: protectedTeacherProcedure
763
- .input(z.object({
764
- classId: z.string(),
765
- }))
766
- .query(async ({ ctx, input }) => {
767
- const { classId } = input;
768
-
769
- const events = await prisma.event.findMany({
770
- where: {
771
- class: {
772
- id: classId,
773
- }
774
- },
775
- select: {
776
- name: true,
777
- startTime: true,
778
- endTime: true,
779
- }
780
- });
781
-
782
- return events;
783
- }),
784
- listMarkSchemes: protectedClassMemberProcedure
785
- .input(z.object({
786
- classId: z.string(),
787
- }))
788
- .query(async ({ ctx, input }) => {
789
- const { classId } = input;
790
-
791
- const markSchemes = await prisma.markScheme.findMany({
792
- where: {
793
- class: {
794
- id: classId,
795
- },
796
- },
797
- });
798
-
799
- return markSchemes;
800
- }),
801
- createMarkScheme: protectedTeacherProcedure
802
- .input(z.object({
135
+ .input(
136
+ z.object({
803
137
  classId: z.string(),
804
- structure: z.string(),
805
- }))
806
- .mutation(async ({ ctx, input }) => {
807
- const { classId, structure } = input;
808
-
809
- const validatedStructure = structure.replace(/\\n/g, '\n');
810
-
811
- const markScheme = await prisma.markScheme.create({
812
- data: {
813
- class: {
814
- connect: {
815
- id: classId,
816
- },
817
- },
818
- structured: validatedStructure,
819
- },
820
- });
821
-
822
- return markScheme;
823
- }),
824
- updateMarkScheme: protectedTeacherProcedure
825
- .input(z.object({
138
+ assignmentId: z.string(),
139
+ submissionId: z.string(),
140
+ gradeReceived: z.number().nullable(),
141
+ })
142
+ )
143
+ .mutation(({ input }) =>
144
+ updateGrade(
145
+ input.assignmentId,
146
+ input.submissionId,
147
+ input.gradeReceived
148
+ )
149
+ ),
150
+
151
+ getEvents: protectedTeacherProcedure
152
+ .input(z.object({ classId: z.string() }))
153
+ .query(({ input }) => getEvents(input.classId)),
154
+ listMarkSchemes: protectedClassMemberProcedure
155
+ .input(z.object({ classId: z.string() }))
156
+ .query(({ input }) => listMarkSchemes(input.classId)),
157
+
158
+ createMarkScheme: protectedTeacherProcedure
159
+ .input(z.object({ classId: z.string(), structure: z.string() }))
160
+ .mutation(({ input }) =>
161
+ createMarkScheme(input.classId, input.structure)
162
+ ),
163
+
164
+ updateMarkScheme: protectedTeacherProcedure
165
+ .input(
166
+ z.object({
826
167
  classId: z.string(),
827
168
  markSchemeId: z.string(),
828
169
  structure: z.string(),
829
- }))
830
- .mutation(async ({ ctx, input }) => {
831
- const { classId, markSchemeId, structure } = input;
832
-
833
- const validatedStructure = structure.replace(/\\n/g, '\n');
834
-
835
- const markScheme = await prisma.markScheme.update({
836
- where: { id: markSchemeId },
837
- data: {
838
- class: {
839
- connect: {
840
- id: classId,
841
- },
842
- },
843
- structured: validatedStructure,
844
- },
845
- });
846
-
847
- return markScheme;
848
- }),
849
- deleteMarkScheme: protectedTeacherProcedure
850
- .input(z.object({
851
- classId: z.string(),
852
- markSchemeId: z.string(),
853
- }))
854
- .mutation(async ({ ctx, input }) => {
855
- const { classId, markSchemeId } = input;
856
-
857
- const markScheme = await prisma.markScheme.delete({
858
- where: { id: markSchemeId },
859
- });
860
-
861
- return markScheme;
862
- }),
863
- listGradingBoundaries: protectedClassMemberProcedure
864
- .input(z.object({
865
- classId: z.string(),
866
- }))
867
- .query(async ({ ctx, input }) => {
868
- const { classId } = input;
869
-
870
- const gradingBoundaries = await prisma.gradingBoundary.findMany({
871
- where: {
872
- class: {
873
- id: classId,
874
- },
875
- },
876
- });
877
-
878
- return gradingBoundaries;
879
- }),
880
- createGradingBoundary: protectedTeacherProcedure
881
- .input(z.object({
882
- classId: z.string(),
883
- structure: z.string(),
884
- }))
885
- .mutation(async ({ ctx, input }) => {
886
- const { classId, structure } = input;
887
-
888
- const validatedStructure = structure.replace(/\\n/g, '\n');
889
-
890
- const gradingBoundary = await prisma.gradingBoundary.create({
891
- data: {
892
- class: {
893
- connect: {
894
- id: classId,
895
- },
896
- },
897
- structured: validatedStructure,
898
- },
899
- });
900
-
901
- return gradingBoundary;
902
- }),
903
- updateGradingBoundary: protectedTeacherProcedure
904
- .input(z.object({
170
+ })
171
+ )
172
+ .mutation(({ input }) =>
173
+ updateMarkScheme(
174
+ input.markSchemeId,
175
+ input.classId,
176
+ input.structure
177
+ )
178
+ ),
179
+
180
+ deleteMarkScheme: protectedTeacherProcedure
181
+ .input(z.object({ classId: z.string(), markSchemeId: z.string() }))
182
+ .mutation(({ input }) => deleteMarkScheme(input.markSchemeId)),
183
+
184
+ listGradingBoundaries: protectedClassMemberProcedure
185
+ .input(z.object({ classId: z.string() }))
186
+ .query(({ input }) => listGradingBoundaries(input.classId)),
187
+
188
+ createGradingBoundary: protectedTeacherProcedure
189
+ .input(z.object({ classId: z.string(), structure: z.string() }))
190
+ .mutation(({ input }) =>
191
+ createGradingBoundary(input.classId, input.structure)
192
+ ),
193
+
194
+ updateGradingBoundary: protectedTeacherProcedure
195
+ .input(
196
+ z.object({
905
197
  classId: z.string(),
906
198
  gradingBoundaryId: z.string(),
907
199
  structure: z.string(),
908
- }))
909
- .mutation(async ({ ctx, input }) => {
910
- const { classId, gradingBoundaryId, structure } = input;
911
-
912
- const validatedStructure = structure.replace(/\\n/g, '\n');
913
-
914
- const gradingBoundary = await prisma.gradingBoundary.update({
915
- where: { id: gradingBoundaryId },
916
- data: {
917
- class: {
918
- connect: {
919
- id: classId,
920
- },
921
- },
922
- structured: validatedStructure,
923
- },
924
- });
925
-
926
- return gradingBoundary;
927
- }),
928
- deleteGradingBoundary: protectedTeacherProcedure
929
- .input(z.object({
930
- classId: z.string(),
931
- gradingBoundaryId: z.string(),
932
- }))
933
- .mutation(async ({ ctx, input }) => {
934
- const { classId, gradingBoundaryId } = input;
935
-
936
- const gradingBoundary = await prisma.gradingBoundary.delete({
937
- where: { id: gradingBoundaryId },
938
- });
939
-
940
- return gradingBoundary;
941
- }),
942
- getSyllabus: protectedClassMemberProcedure
943
- .input(z.object({
944
- classId: z.string(),
945
- }))
946
- .query(async ({input}) => {
947
- const {classId} = input;
948
-
949
- const syllabus = (await prisma.class.findUnique({
950
- where: {
951
- id: classId,
952
- },
953
- }))?.syllabus;
954
-
955
- const markSchemes = await prisma.markScheme.findMany({
956
- where: {
957
- classId,
958
- }
959
- });
960
-
961
- const gradingBoundaries = await prisma.gradingBoundary.findMany({
962
- where: {
963
- classId,
964
- }
965
- });
966
-
967
- return {syllabus, gradingBoundaries, markSchemes};
968
- }),
969
- updateSyllabus: protectedTeacherProcedure
970
- .input(z.object({
971
- classId: z.string(),
972
- contents: z.string(),
973
- }))
974
- .mutation(async ({ input }) => {
975
- const { contents, classId } = input;
976
-
977
- if (!contents) throw new TRPCError({
978
- code: 'BAD_REQUEST',
979
- message: "Missing key contents",
980
- });
981
-
982
- const updated = await prisma.class.update({
983
- where: {
984
- id: classId
985
- },
986
- data: {
987
- syllabus: contents,
988
- }
989
- });
990
-
991
- return updated;
992
- }),
993
- // Lab Management Endpoints (Assignment-based)
994
- listLabDrafts: protectedTeacherProcedure
995
- .input(z.object({
996
- classId: z.string(),
997
- }))
998
- .query(async ({ ctx, input }) => {
999
- const { classId } = input;
1000
-
1001
- const labDrafts = await prisma.assignment.findMany({
1002
- where: {
1003
- classId: classId,
1004
- teacherId: ctx.user?.id,
1005
- inProgress: true,
1006
- },
1007
- orderBy: {
1008
- modifiedAt: 'desc',
1009
- },
1010
- });
1011
-
1012
- return labDrafts;
1013
- }),
1014
- createLabDraft: protectedTeacherProcedure
1015
- .input(z.object({
200
+ })
201
+ )
202
+ .mutation(({ input }) =>
203
+ updateGradingBoundary(
204
+ input.gradingBoundaryId,
205
+ input.classId,
206
+ input.structure
207
+ )
208
+ ),
209
+
210
+ deleteGradingBoundary: protectedTeacherProcedure
211
+ .input(
212
+ z.object({ classId: z.string(), gradingBoundaryId: z.string() })
213
+ )
214
+ .mutation(({ input }) => deleteGradingBoundary(input.gradingBoundaryId)),
215
+ getSyllabus: protectedClassMemberProcedure
216
+ .input(z.object({ classId: z.string() }))
217
+ .query(({ input }) => getSyllabus(input.classId)),
218
+
219
+ updateSyllabus: protectedTeacherProcedure
220
+ .input(z.object({ classId: z.string(), contents: z.string() }))
221
+ .mutation(({ input }) =>
222
+ updateSyllabus(input.classId, input.contents)
223
+ ),
224
+ listLabDrafts: protectedTeacherProcedure
225
+ .input(z.object({ classId: z.string() }))
226
+ .query(({ ctx, input }) =>
227
+ listLabDrafts(input.classId, ctx.user?.id ?? "")
228
+ ),
229
+
230
+ createLabDraft: protectedTeacherProcedure
231
+ .input(
232
+ z.object({
1016
233
  classId: z.string(),
1017
234
  title: z.string(),
1018
- type: z.enum(['LAB', 'HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'OTHER']),
235
+ type: z.enum([
236
+ "LAB",
237
+ "HOMEWORK",
238
+ "QUIZ",
239
+ "TEST",
240
+ "PROJECT",
241
+ "ESSAY",
242
+ "DISCUSSION",
243
+ "PRESENTATION",
244
+ "OTHER",
245
+ ]),
1019
246
  instructions: z.string(),
1020
247
  dueDate: z.date().optional(),
1021
248
  maxGrade: z.number().optional(),
@@ -1024,32 +251,16 @@ export const classRouter = createTRPCRouter({
1024
251
  sectionId: z.string().optional(),
1025
252
  markSchemeId: z.string().optional(),
1026
253
  gradingBoundaryId: z.string().optional(),
1027
- }))
1028
- .mutation(async ({ ctx, input }) => {
1029
- const { classId, ...draftData } = input;
1030
-
1031
- const labDraft = await prisma.assignment.create({
1032
- data: {
1033
- classId: classId,
1034
- teacherId: ctx.user?.id!,
1035
- inProgress: true,
1036
- graded: draftData.graded ?? false,
1037
- maxGrade: draftData.maxGrade ?? 0,
1038
- weight: draftData.weight ?? 1,
1039
- dueDate: draftData.dueDate || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // Default 1 week from now
1040
- title: draftData.title,
1041
- instructions: draftData.instructions,
1042
- type: draftData.type,
1043
- ...(draftData.sectionId && { sectionId: draftData.sectionId }),
1044
- ...(draftData.markSchemeId && { markSchemeId: draftData.markSchemeId }),
1045
- ...(draftData.gradingBoundaryId && { gradingBoundaryId: draftData.gradingBoundaryId }),
1046
- },
1047
- });
254
+ })
255
+ )
256
+ .mutation(({ ctx, input }) => {
257
+ const { classId, ...draftData } = input;
258
+ return createLabDraft(classId, ctx.user?.id ?? "", draftData);
259
+ }),
1048
260
 
1049
- return labDraft;
1050
- }),
1051
- updateLabDraft: protectedTeacherProcedure
1052
- .input(z.object({
261
+ updateLabDraft: protectedTeacherProcedure
262
+ .input(
263
+ z.object({
1053
264
  classId: z.string(),
1054
265
  draftId: z.string(),
1055
266
  title: z.string().optional(),
@@ -1061,196 +272,61 @@ export const classRouter = createTRPCRouter({
1061
272
  sectionId: z.string().optional(),
1062
273
  markSchemeId: z.string().optional(),
1063
274
  gradingBoundaryId: z.string().optional(),
1064
- }))
1065
- .mutation(async ({ ctx, input }) => {
1066
- const { classId, draftId, ...updateData } = input;
1067
-
1068
- const labDraft = await prisma.assignment.update({
1069
- where: {
1070
- id: draftId,
1071
- classId: classId,
1072
- teacherId: ctx.user?.id!,
1073
- inProgress: true,
1074
- },
1075
- data: {
1076
- ...(updateData.title && { title: updateData.title }),
1077
- ...(updateData.instructions && { instructions: updateData.instructions }),
1078
- ...(updateData.dueDate && { dueDate: updateData.dueDate }),
1079
- ...(updateData.maxGrade !== undefined && { maxGrade: updateData.maxGrade }),
1080
- ...(updateData.weight !== undefined && { weight: updateData.weight }),
1081
- ...(updateData.graded !== undefined && { graded: updateData.graded }),
1082
- ...(updateData.sectionId !== undefined && { sectionId: updateData.sectionId }),
1083
- ...(updateData.markSchemeId !== undefined && { markSchemeId: updateData.markSchemeId }),
1084
- ...(updateData.gradingBoundaryId !== undefined && { gradingBoundaryId: updateData.gradingBoundaryId }),
1085
- modifiedAt: new Date(),
1086
- },
1087
- });
1088
-
1089
- return labDraft;
1090
- }),
1091
- deleteLabDraft: protectedTeacherProcedure
1092
- .input(z.object({
1093
- classId: z.string(),
1094
- draftId: z.string(),
1095
- }))
1096
- .mutation(async ({ ctx, input }) => {
1097
- const { classId, draftId } = input;
275
+ })
276
+ )
277
+ .mutation(({ ctx, input }) => {
278
+ const { classId, draftId, ...updateData } = input;
279
+ return updateLabDraft(
280
+ classId,
281
+ ctx.user?.id ?? "",
282
+ draftId,
283
+ updateData
284
+ );
285
+ }),
1098
286
 
1099
- const labDraft = await prisma.assignment.delete({
1100
- where: {
1101
- id: draftId,
1102
- classId: classId,
1103
- teacherId: ctx.user?.id!,
1104
- inProgress: true,
1105
- },
1106
- });
287
+ deleteLabDraft: protectedTeacherProcedure
288
+ .input(z.object({ classId: z.string(), draftId: z.string() }))
289
+ .mutation(({ ctx, input }) =>
290
+ deleteLabDraft(input.classId, ctx.user?.id ?? "", input.draftId)
291
+ ),
1107
292
 
1108
- return labDraft;
1109
- }),
1110
- publishLabDraft: protectedTeacherProcedure
1111
- .input(z.object({
293
+ publishLabDraft: protectedTeacherProcedure
294
+ .input(
295
+ z.object({
1112
296
  classId: z.string(),
1113
297
  draftId: z.string(),
1114
298
  dueDate: z.date().optional(),
1115
299
  maxGrade: z.number().optional(),
1116
300
  weight: z.number().optional(),
1117
301
  graded: z.boolean().optional(),
1118
- }))
1119
- .mutation(async ({ ctx, input }) => {
1120
- const { classId, draftId, ...publishData } = input;
1121
-
1122
- // Get the lab draft
1123
- const labDraft = await prisma.assignment.findUnique({
1124
- where: {
1125
- id: draftId,
1126
- classId: classId,
1127
- teacherId: ctx.user?.id!,
1128
- inProgress: true,
1129
- },
1130
- });
1131
-
1132
- if (!labDraft) {
1133
- throw new TRPCError({
1134
- code: 'NOT_FOUND',
1135
- message: 'Lab draft not found',
1136
- });
1137
- }
302
+ })
303
+ )
304
+ .mutation(({ ctx, input }) => {
305
+ const { classId, draftId, ...publishData } = input;
306
+ return publishLabDraft(
307
+ classId,
308
+ ctx.user?.id ?? "",
309
+ draftId,
310
+ publishData
311
+ );
312
+ }),
1138
313
 
1139
- // Publish the draft by updating it to not be in progress
1140
- const publishedAssignment = await prisma.assignment.update({
1141
- where: { id: draftId },
1142
- data: {
1143
- inProgress: false,
1144
- dueDate: publishData.dueDate || labDraft.dueDate,
1145
- maxGrade: publishData.maxGrade || labDraft.maxGrade || 100,
1146
- weight: publishData.weight || labDraft.weight || 1,
1147
- graded: publishData.graded !== undefined ? publishData.graded : true,
1148
- modifiedAt: new Date(),
1149
- },
1150
- });
314
+ getFiles: protectedClassMemberProcedure
315
+ .input(z.object({ classId: z.string() }))
316
+ .query(({ input }) => getFiles(input.classId)),
317
+ exportClass: protectedTeacherProcedure
318
+ .input(z.object({ classId: z.string() }))
319
+ .mutation(({ ctx, input }) => exportClass(input.classId, ctx.user?.id ?? "")),
1151
320
 
1152
- return publishedAssignment;
1153
- }),
1154
- getFiles: protectedClassMemberProcedure
1155
- .input(z.object({
321
+ importClass: protectedProcedure
322
+ .input(
323
+ z.object({
1156
324
  classId: z.string(),
1157
- }))
1158
- .query(async ({ ctx, input }) => {
1159
- const { classId } = input;
1160
-
1161
- // Get all assignments with their files and submissions
1162
- const assignments = await prisma.assignment.findMany({
1163
- where: {
1164
- classId: classId,
1165
- },
1166
- include: {
1167
- attachments: {
1168
- select: {
1169
- id: true,
1170
- name: true,
1171
- type: true,
1172
- size: true,
1173
- path: true,
1174
- thumbnailId: true,
1175
- uploadedAt: true,
1176
- user: {
1177
- select: {
1178
- id: true,
1179
- username: true,
1180
- },
1181
- },
1182
- },
1183
- },
1184
- submissions: {
1185
- include: {
1186
- attachments: {
1187
- select: {
1188
- id: true,
1189
- name: true,
1190
- type: true,
1191
- size: true,
1192
- path: true,
1193
- thumbnailId: true,
1194
- uploadedAt: true,
1195
- user: {
1196
- select: {
1197
- id: true,
1198
- username: true,
1199
- },
1200
- },
1201
- },
1202
- },
1203
- annotations: {
1204
- select: {
1205
- id: true,
1206
- name: true,
1207
- type: true,
1208
- size: true,
1209
- path: true,
1210
- thumbnailId: true,
1211
- uploadedAt: true,
1212
- user: {
1213
- select: {
1214
- id: true,
1215
- username: true,
1216
- },
1217
- },
1218
- },
1219
- },
1220
- student: {
1221
- select: {
1222
- id: true,
1223
- username: true,
1224
- },
1225
- },
1226
- },
1227
- },
1228
- teacher: {
1229
- select: {
1230
- id: true,
1231
- username: true,
1232
- },
1233
- },
1234
- },
1235
- orderBy: {
1236
- createdAt: 'desc',
1237
- },
1238
- });
1239
-
1240
- // Organize files by assignment structure
1241
- const organizedFiles = assignments.map(assignment => ({
1242
- id: assignment.id,
1243
- title: assignment.title,
1244
- teacher: assignment.teacher,
1245
- teacherAttachments: assignment.attachments,
1246
- students: assignment.submissions.map(submission => ({
1247
- id: submission.student.id,
1248
- username: submission.student.username,
1249
- attachments: submission.attachments,
1250
- annotations: submission.annotations,
1251
- })),
1252
- }));
1253
-
1254
- return organizedFiles;
1255
- }),
325
+ year: z.number(),
326
+ classData: z.any(),
327
+ })
328
+ )
329
+ .mutation(({ ctx, input }) =>
330
+ importClass(input.classId, ctx.user?.id ?? "", input.year, input.classData)
331
+ ),
1256
332
  });