@studious-lms/server 1.2.53 → 1.3.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 (477) 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 +439 -0
  53. package/dist/models/class.d.ts.map +1 -0
  54. package/dist/models/class.js +546 -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 +21 -0
  109. package/dist/pipelines/aiLabChat.d.ts.map +1 -0
  110. package/dist/pipelines/aiLabChat.js +460 -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 +1393 -1267
  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 +139 -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 +11 -10
  167. package/dist/routers/labChat.d.ts.map +1 -1
  168. package/dist/routers/labChat.js +19 -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 +2 -2
  175. package/dist/routers/message.d.ts.map +1 -1
  176. package/dist/routers/message.js +27 -522
  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 +51 -38
  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 +621 -0
  224. package/dist/services/class.d.ts.map +1 -0
  225. package/dist/services/class.js +474 -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 +165 -0
  248. package/dist/services/labChat.d.ts.map +1 -0
  249. package/dist/services/labChat.js +289 -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 +95 -0
  256. package/dist/services/message.d.ts.map +1 -0
  257. package/dist/services/message.js +350 -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/src/index.ts +119 -12
  309. package/src/lib/config/env.ts +6 -0
  310. package/src/lib/fileUpload.ts +0 -1
  311. package/src/lib/googleCloudStorage.ts +17 -0
  312. package/src/lib/pusher.ts +5 -1
  313. package/src/lib/redis.ts +56 -0
  314. package/src/lib/thumbnailGenerator.ts +170 -168
  315. package/src/middleware/auth.ts +80 -137
  316. package/src/models/agenda.ts +46 -0
  317. package/src/models/announcement.ts +134 -0
  318. package/src/models/assignment.ts +322 -0
  319. package/src/models/attendance.ts +208 -0
  320. package/src/models/auth.ts +247 -0
  321. package/src/models/class.ts +598 -0
  322. package/src/models/comment.ts +152 -0
  323. package/src/models/conversation.ts +200 -0
  324. package/src/models/event.ts +177 -0
  325. package/src/models/file.ts +129 -0
  326. package/src/models/folder.ts +225 -0
  327. package/src/models/labChat.ts +213 -0
  328. package/src/models/marketing.ts +45 -0
  329. package/src/models/message.ts +153 -0
  330. package/src/models/newtonChat.ts +70 -0
  331. package/src/models/notification.ts +54 -0
  332. package/src/models/section.ts +98 -0
  333. package/src/models/user.ts +47 -0
  334. package/src/models/worksheet.ts +294 -0
  335. package/src/{server/pipelines → pipelines}/aiLabChat.ts +11 -7
  336. package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +9 -5
  337. package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
  338. package/src/routers/agenda.ts +3 -66
  339. package/src/routers/announcement.ts +54 -495
  340. package/src/routers/assignment.ts +126 -2018
  341. package/src/routers/attendance.ts +15 -276
  342. package/src/routers/auth.ts +79 -442
  343. package/src/routers/class.ts +263 -1187
  344. package/src/routers/comment.ts +61 -288
  345. package/src/routers/conversation.ts +51 -360
  346. package/src/routers/event.ts +50 -481
  347. package/src/routers/file.ts +45 -368
  348. package/src/routers/folder.ts +107 -836
  349. package/src/routers/labChat.ts +29 -605
  350. package/src/routers/marketing.ts +35 -77
  351. package/src/routers/message.ts +45 -571
  352. package/src/routers/newtonChat.ts +17 -278
  353. package/src/routers/notifications.ts +32 -82
  354. package/src/routers/section.ts +46 -330
  355. package/src/routers/user.ts +49 -227
  356. package/src/routers/worksheet.ts +215 -503
  357. package/src/services/agenda.ts +21 -0
  358. package/src/services/announcement.ts +290 -0
  359. package/src/services/assignment.ts +1198 -0
  360. package/src/services/attendance.ts +85 -0
  361. package/src/services/auth.ts +277 -0
  362. package/src/services/class.ts +622 -0
  363. package/src/services/comment.ts +106 -0
  364. package/src/services/conversation.ts +213 -0
  365. package/src/services/event.ts +231 -0
  366. package/src/services/file.ts +167 -0
  367. package/src/services/folder.ts +316 -0
  368. package/src/services/labChat.ts +352 -0
  369. package/src/services/marketing.ts +57 -0
  370. package/src/services/message.ts +461 -0
  371. package/src/services/newtonChat.ts +222 -0
  372. package/src/services/notification.ts +50 -0
  373. package/src/services/section.ts +283 -0
  374. package/src/services/user.ts +172 -0
  375. package/src/services/worksheet.ts +358 -0
  376. package/src/utils/aiUser.ts +4 -3
  377. package/src/utils/email.ts +5 -3
  378. package/src/utils/generateInviteCode.ts +1 -3
  379. package/src/utils/inference.ts +5 -2
  380. package/src/utils/logger.ts +3 -1
  381. package/src/utils/prismaErrorHandler.ts +3 -0
  382. package/src/utils/prismaWrapper.ts +4 -0
  383. package/tests/globalSetup.ts +62 -0
  384. package/tests/helpers.ts +22 -0
  385. package/tests/middleware/security.test.ts +42 -0
  386. package/tests/routers/agenda.test.ts +138 -0
  387. package/tests/routers/announcement.test.ts +490 -0
  388. package/tests/routers/assignment.test.ts +837 -0
  389. package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
  390. package/tests/routers/auth.test.ts +171 -0
  391. package/tests/{class.test.ts → routers/class.test.ts} +131 -85
  392. package/tests/routers/comment.test.ts +126 -0
  393. package/tests/routers/conversation.test.ts +145 -0
  394. package/tests/{event.test.ts → routers/event.test.ts} +93 -32
  395. package/tests/routers/folder.test.ts +178 -0
  396. package/tests/routers/labChat.test.ts +115 -0
  397. package/tests/routers/marketing.test.ts +59 -0
  398. package/tests/routers/message.test.ts +123 -0
  399. package/tests/routers/notification.test.ts +69 -0
  400. package/tests/{section.test.ts → routers/section.test.ts} +5 -13
  401. package/tests/server/rateLimit.test.ts +73 -0
  402. package/tests/setup.ts +18 -92
  403. package/tests/user.test.ts +9 -31
  404. package/tests/utils/aiUser.test.ts +22 -0
  405. package/tests/utils/generateInviteCode.test.ts +24 -0
  406. package/tests/utils/logger.test.ts +74 -0
  407. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  408. package/tests/utils/prismaWrapper.test.ts +82 -0
  409. package/tests/worksheet.test.ts +181 -0
  410. package/vitest.config.ts +6 -3
  411. package/vitest.unit.config.ts +21 -0
  412. package/TODO.md +0 -2
  413. package/coverage/base.css +0 -224
  414. package/coverage/block-navigation.js +0 -87
  415. package/coverage/clover.xml +0 -12110
  416. package/coverage/coverage-final.json +0 -44
  417. package/coverage/favicon.png +0 -0
  418. package/coverage/index.html +0 -221
  419. package/coverage/prettify.css +0 -1
  420. package/coverage/prettify.js +0 -2
  421. package/coverage/server/index.html +0 -116
  422. package/coverage/server/src/exportType.ts.html +0 -109
  423. package/coverage/server/src/index.html +0 -161
  424. package/coverage/server/src/index.ts.html +0 -1702
  425. package/coverage/server/src/instrument.ts.html +0 -130
  426. package/coverage/server/src/lib/config/env.ts.html +0 -448
  427. package/coverage/server/src/lib/config/index.html +0 -116
  428. package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
  429. package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
  430. package/coverage/server/src/lib/index.html +0 -206
  431. package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
  432. package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
  433. package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
  434. package/coverage/server/src/lib/pusher.ts.html +0 -121
  435. package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
  436. package/coverage/server/src/middleware/auth.ts.html +0 -646
  437. package/coverage/server/src/middleware/index.html +0 -146
  438. package/coverage/server/src/middleware/logging.ts.html +0 -244
  439. package/coverage/server/src/middleware/security.ts.html +0 -271
  440. package/coverage/server/src/routers/_app.ts.html +0 -232
  441. package/coverage/server/src/routers/agenda.ts.html +0 -319
  442. package/coverage/server/src/routers/announcement.ts.html +0 -3481
  443. package/coverage/server/src/routers/assignment.ts.html +0 -7633
  444. package/coverage/server/src/routers/attendance.ts.html +0 -1030
  445. package/coverage/server/src/routers/auth.ts.html +0 -1081
  446. package/coverage/server/src/routers/class.ts.html +0 -3535
  447. package/coverage/server/src/routers/comment.ts.html +0 -991
  448. package/coverage/server/src/routers/conversation.ts.html +0 -982
  449. package/coverage/server/src/routers/event.ts.html +0 -1609
  450. package/coverage/server/src/routers/file.ts.html +0 -1144
  451. package/coverage/server/src/routers/folder.ts.html +0 -2797
  452. package/coverage/server/src/routers/index.html +0 -386
  453. package/coverage/server/src/routers/labChat.ts.html +0 -3073
  454. package/coverage/server/src/routers/marketing.ts.html +0 -340
  455. package/coverage/server/src/routers/message.ts.html +0 -1912
  456. package/coverage/server/src/routers/notifications.ts.html +0 -364
  457. package/coverage/server/src/routers/section.ts.html +0 -1120
  458. package/coverage/server/src/routers/user.ts.html +0 -862
  459. package/coverage/server/src/routers/worksheet.ts.html +0 -1729
  460. package/coverage/server/src/trpc.ts.html +0 -397
  461. package/coverage/server/src/types/index.html +0 -116
  462. package/coverage/server/src/types/trpc.ts.html +0 -127
  463. package/coverage/server/src/utils/aiUser.ts.html +0 -280
  464. package/coverage/server/src/utils/email.ts.html +0 -121
  465. package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
  466. package/coverage/server/src/utils/index.html +0 -206
  467. package/coverage/server/src/utils/inference.ts.html +0 -709
  468. package/coverage/server/src/utils/logger.ts.html +0 -664
  469. package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
  470. package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
  471. package/coverage/server/vitest.config.ts.html +0 -196
  472. package/coverage/sort-arrow-sprite.png +0 -0
  473. package/coverage/sorter.js +0 -210
  474. package/src/lib/notificationHandler.ts +0 -36
  475. package/tests/announcement.test.ts +0 -164
  476. package/tests/assignment.test.ts +0 -296
  477. 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: protectedTeacherProcedure
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
  });