@studious-lms/server 1.2.52 → 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 (480) 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 +36 -94
  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 +1399 -1271
  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 +140 -68
  143. package/dist/routers/class.d.ts.map +1 -1
  144. package/dist/routers/class.js +82 -1051
  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/aiNewtonChat.d.ts.map +1 -1
  200. package/dist/server/pipelines/aiNewtonChat.js +8 -3
  201. package/dist/server/pipelines/aiNewtonChat.js.map +1 -1
  202. package/dist/server/pipelines/gradeWorksheet.d.ts +6 -6
  203. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
  204. package/dist/server/pipelines/gradeWorksheet.js +12 -5
  205. package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
  206. package/dist/services/agenda.d.ts +100 -0
  207. package/dist/services/agenda.d.ts.map +1 -0
  208. package/dist/services/agenda.js +21 -0
  209. package/dist/services/agenda.js.map +1 -0
  210. package/dist/services/announcement.d.ts +135 -0
  211. package/dist/services/announcement.d.ts.map +1 -0
  212. package/dist/services/announcement.js +223 -0
  213. package/dist/services/announcement.js.map +1 -0
  214. package/dist/services/assignment.d.ts +1462 -0
  215. package/dist/services/assignment.d.ts.map +1 -0
  216. package/dist/services/assignment.js +898 -0
  217. package/dist/services/assignment.js.map +1 -0
  218. package/dist/services/attendance.d.ts +93 -0
  219. package/dist/services/attendance.d.ts.map +1 -0
  220. package/dist/services/attendance.js +61 -0
  221. package/dist/services/attendance.js.map +1 -0
  222. package/dist/services/auth.d.ts +68 -0
  223. package/dist/services/auth.d.ts.map +1 -0
  224. package/dist/services/auth.js +218 -0
  225. package/dist/services/auth.js.map +1 -0
  226. package/dist/services/class.d.ts +621 -0
  227. package/dist/services/class.d.ts.map +1 -0
  228. package/dist/services/class.js +474 -0
  229. package/dist/services/class.js.map +1 -0
  230. package/dist/services/comment.d.ts +100 -0
  231. package/dist/services/comment.d.ts.map +1 -0
  232. package/dist/services/comment.js +83 -0
  233. package/dist/services/comment.js.map +1 -0
  234. package/dist/services/conversation.d.ts +159 -0
  235. package/dist/services/conversation.d.ts.map +1 -0
  236. package/dist/services/conversation.js +138 -0
  237. package/dist/services/conversation.js.map +1 -0
  238. package/dist/services/event.d.ts +216 -0
  239. package/dist/services/event.d.ts.map +1 -0
  240. package/dist/services/event.js +168 -0
  241. package/dist/services/event.js.map +1 -0
  242. package/dist/services/file.d.ts +74 -0
  243. package/dist/services/file.d.ts.map +1 -0
  244. package/dist/services/file.js +133 -0
  245. package/dist/services/file.js.map +1 -0
  246. package/dist/services/folder.d.ts +239 -0
  247. package/dist/services/folder.d.ts.map +1 -0
  248. package/dist/services/folder.js +248 -0
  249. package/dist/services/folder.js.map +1 -0
  250. package/dist/services/labChat.d.ts +165 -0
  251. package/dist/services/labChat.d.ts.map +1 -0
  252. package/dist/services/labChat.js +289 -0
  253. package/dist/services/labChat.js.map +1 -0
  254. package/dist/services/marketing.d.ts +50 -0
  255. package/dist/services/marketing.d.ts.map +1 -0
  256. package/dist/services/marketing.js +32 -0
  257. package/dist/services/marketing.js.map +1 -0
  258. package/dist/services/message.d.ts +95 -0
  259. package/dist/services/message.d.ts.map +1 -0
  260. package/dist/services/message.js +350 -0
  261. package/dist/services/message.js.map +1 -0
  262. package/dist/services/newtonChat.d.ts +22 -0
  263. package/dist/services/newtonChat.d.ts.map +1 -0
  264. package/dist/services/newtonChat.js +174 -0
  265. package/dist/services/newtonChat.js.map +1 -0
  266. package/dist/services/notification.d.ts +65 -0
  267. package/dist/services/notification.d.ts.map +1 -0
  268. package/dist/services/notification.js +33 -0
  269. package/dist/services/notification.js.map +1 -0
  270. package/dist/services/section.d.ts +53 -0
  271. package/dist/services/section.d.ts.map +1 -0
  272. package/dist/services/section.js +199 -0
  273. package/dist/services/section.js.map +1 -0
  274. package/dist/services/user.d.ts +48 -0
  275. package/dist/services/user.d.ts.map +1 -0
  276. package/dist/services/user.js +141 -0
  277. package/dist/services/user.js.map +1 -0
  278. package/dist/services/worksheet.d.ts +239 -0
  279. package/dist/services/worksheet.d.ts.map +1 -0
  280. package/dist/services/worksheet.js +235 -0
  281. package/dist/services/worksheet.js.map +1 -0
  282. package/dist/utils/aiUser.d.ts +1 -3
  283. package/dist/utils/aiUser.d.ts.map +1 -1
  284. package/dist/utils/aiUser.js +6 -5
  285. package/dist/utils/aiUser.js.map +1 -1
  286. package/dist/utils/email.d.ts +3 -0
  287. package/dist/utils/email.d.ts.map +1 -1
  288. package/dist/utils/email.js +7 -4
  289. package/dist/utils/email.js.map +1 -1
  290. package/dist/utils/generateInviteCode.d.ts +1 -2
  291. package/dist/utils/generateInviteCode.d.ts.map +1 -1
  292. package/dist/utils/generateInviteCode.js +3 -4
  293. package/dist/utils/generateInviteCode.js.map +1 -1
  294. package/dist/utils/inference.d.ts +3 -0
  295. package/dist/utils/inference.d.ts.map +1 -1
  296. package/dist/utils/inference.js +7 -4
  297. package/dist/utils/inference.js.map +1 -1
  298. package/dist/utils/logger.d.ts +3 -0
  299. package/dist/utils/logger.d.ts.map +1 -1
  300. package/dist/utils/logger.js +5 -2
  301. package/dist/utils/logger.js.map +1 -1
  302. package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
  303. package/dist/utils/prismaErrorHandler.js +5 -2
  304. package/dist/utils/prismaErrorHandler.js.map +1 -1
  305. package/dist/utils/prismaWrapper.d.ts +1 -0
  306. package/dist/utils/prismaWrapper.d.ts.map +1 -1
  307. package/dist/utils/prismaWrapper.js +6 -2
  308. package/dist/utils/prismaWrapper.js.map +1 -1
  309. package/docker-compose.yml +5 -0
  310. package/package.json +4 -3
  311. package/src/index.ts +119 -12
  312. package/src/lib/config/env.ts +6 -0
  313. package/src/lib/fileUpload.ts +0 -1
  314. package/src/lib/googleCloudStorage.ts +17 -0
  315. package/src/lib/pusher.ts +5 -1
  316. package/src/lib/redis.ts +56 -0
  317. package/src/lib/thumbnailGenerator.ts +170 -168
  318. package/src/middleware/auth.ts +83 -136
  319. package/src/models/agenda.ts +46 -0
  320. package/src/models/announcement.ts +134 -0
  321. package/src/models/assignment.ts +322 -0
  322. package/src/models/attendance.ts +208 -0
  323. package/src/models/auth.ts +247 -0
  324. package/src/models/class.ts +598 -0
  325. package/src/models/comment.ts +152 -0
  326. package/src/models/conversation.ts +200 -0
  327. package/src/models/event.ts +177 -0
  328. package/src/models/file.ts +129 -0
  329. package/src/models/folder.ts +225 -0
  330. package/src/models/labChat.ts +213 -0
  331. package/src/models/marketing.ts +45 -0
  332. package/src/models/message.ts +153 -0
  333. package/src/models/newtonChat.ts +70 -0
  334. package/src/models/notification.ts +54 -0
  335. package/src/models/section.ts +98 -0
  336. package/src/models/user.ts +47 -0
  337. package/src/models/worksheet.ts +294 -0
  338. package/src/{server/pipelines → pipelines}/aiLabChat.ts +11 -7
  339. package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +15 -6
  340. package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
  341. package/src/routers/agenda.ts +3 -66
  342. package/src/routers/announcement.ts +54 -495
  343. package/src/routers/assignment.ts +126 -2018
  344. package/src/routers/attendance.ts +15 -276
  345. package/src/routers/auth.ts +79 -442
  346. package/src/routers/class.ts +263 -1186
  347. package/src/routers/comment.ts +61 -288
  348. package/src/routers/conversation.ts +51 -360
  349. package/src/routers/event.ts +50 -481
  350. package/src/routers/file.ts +45 -368
  351. package/src/routers/folder.ts +107 -836
  352. package/src/routers/labChat.ts +29 -605
  353. package/src/routers/marketing.ts +35 -77
  354. package/src/routers/message.ts +45 -571
  355. package/src/routers/newtonChat.ts +17 -278
  356. package/src/routers/notifications.ts +32 -82
  357. package/src/routers/section.ts +46 -330
  358. package/src/routers/user.ts +49 -227
  359. package/src/routers/worksheet.ts +215 -503
  360. package/src/services/agenda.ts +21 -0
  361. package/src/services/announcement.ts +290 -0
  362. package/src/services/assignment.ts +1198 -0
  363. package/src/services/attendance.ts +85 -0
  364. package/src/services/auth.ts +277 -0
  365. package/src/services/class.ts +622 -0
  366. package/src/services/comment.ts +106 -0
  367. package/src/services/conversation.ts +213 -0
  368. package/src/services/event.ts +231 -0
  369. package/src/services/file.ts +167 -0
  370. package/src/services/folder.ts +316 -0
  371. package/src/services/labChat.ts +352 -0
  372. package/src/services/marketing.ts +57 -0
  373. package/src/services/message.ts +461 -0
  374. package/src/services/newtonChat.ts +222 -0
  375. package/src/services/notification.ts +50 -0
  376. package/src/services/section.ts +283 -0
  377. package/src/services/user.ts +172 -0
  378. package/src/services/worksheet.ts +358 -0
  379. package/src/utils/aiUser.ts +4 -3
  380. package/src/utils/email.ts +5 -3
  381. package/src/utils/generateInviteCode.ts +1 -3
  382. package/src/utils/inference.ts +5 -2
  383. package/src/utils/logger.ts +3 -1
  384. package/src/utils/prismaErrorHandler.ts +3 -0
  385. package/src/utils/prismaWrapper.ts +4 -0
  386. package/tests/globalSetup.ts +62 -0
  387. package/tests/helpers.ts +22 -0
  388. package/tests/middleware/security.test.ts +42 -0
  389. package/tests/routers/agenda.test.ts +138 -0
  390. package/tests/routers/announcement.test.ts +490 -0
  391. package/tests/routers/assignment.test.ts +837 -0
  392. package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
  393. package/tests/routers/auth.test.ts +171 -0
  394. package/tests/{class.test.ts → routers/class.test.ts} +131 -85
  395. package/tests/routers/comment.test.ts +126 -0
  396. package/tests/routers/conversation.test.ts +145 -0
  397. package/tests/{event.test.ts → routers/event.test.ts} +93 -32
  398. package/tests/routers/folder.test.ts +178 -0
  399. package/tests/routers/labChat.test.ts +115 -0
  400. package/tests/routers/marketing.test.ts +59 -0
  401. package/tests/routers/message.test.ts +123 -0
  402. package/tests/routers/notification.test.ts +69 -0
  403. package/tests/{section.test.ts → routers/section.test.ts} +5 -13
  404. package/tests/server/rateLimit.test.ts +73 -0
  405. package/tests/setup.ts +18 -92
  406. package/tests/user.test.ts +9 -31
  407. package/tests/utils/aiUser.test.ts +22 -0
  408. package/tests/utils/generateInviteCode.test.ts +24 -0
  409. package/tests/utils/logger.test.ts +74 -0
  410. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  411. package/tests/utils/prismaWrapper.test.ts +82 -0
  412. package/tests/worksheet.test.ts +181 -0
  413. package/vitest.config.ts +6 -3
  414. package/vitest.unit.config.ts +21 -0
  415. package/TODO.md +0 -2
  416. package/coverage/base.css +0 -224
  417. package/coverage/block-navigation.js +0 -87
  418. package/coverage/clover.xml +0 -12110
  419. package/coverage/coverage-final.json +0 -44
  420. package/coverage/favicon.png +0 -0
  421. package/coverage/index.html +0 -221
  422. package/coverage/prettify.css +0 -1
  423. package/coverage/prettify.js +0 -2
  424. package/coverage/server/index.html +0 -116
  425. package/coverage/server/src/exportType.ts.html +0 -109
  426. package/coverage/server/src/index.html +0 -161
  427. package/coverage/server/src/index.ts.html +0 -1702
  428. package/coverage/server/src/instrument.ts.html +0 -130
  429. package/coverage/server/src/lib/config/env.ts.html +0 -448
  430. package/coverage/server/src/lib/config/index.html +0 -116
  431. package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
  432. package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
  433. package/coverage/server/src/lib/index.html +0 -206
  434. package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
  435. package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
  436. package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
  437. package/coverage/server/src/lib/pusher.ts.html +0 -121
  438. package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
  439. package/coverage/server/src/middleware/auth.ts.html +0 -646
  440. package/coverage/server/src/middleware/index.html +0 -146
  441. package/coverage/server/src/middleware/logging.ts.html +0 -244
  442. package/coverage/server/src/middleware/security.ts.html +0 -271
  443. package/coverage/server/src/routers/_app.ts.html +0 -232
  444. package/coverage/server/src/routers/agenda.ts.html +0 -319
  445. package/coverage/server/src/routers/announcement.ts.html +0 -3481
  446. package/coverage/server/src/routers/assignment.ts.html +0 -7633
  447. package/coverage/server/src/routers/attendance.ts.html +0 -1030
  448. package/coverage/server/src/routers/auth.ts.html +0 -1081
  449. package/coverage/server/src/routers/class.ts.html +0 -3535
  450. package/coverage/server/src/routers/comment.ts.html +0 -991
  451. package/coverage/server/src/routers/conversation.ts.html +0 -982
  452. package/coverage/server/src/routers/event.ts.html +0 -1609
  453. package/coverage/server/src/routers/file.ts.html +0 -1144
  454. package/coverage/server/src/routers/folder.ts.html +0 -2797
  455. package/coverage/server/src/routers/index.html +0 -386
  456. package/coverage/server/src/routers/labChat.ts.html +0 -3073
  457. package/coverage/server/src/routers/marketing.ts.html +0 -340
  458. package/coverage/server/src/routers/message.ts.html +0 -1912
  459. package/coverage/server/src/routers/notifications.ts.html +0 -364
  460. package/coverage/server/src/routers/section.ts.html +0 -1120
  461. package/coverage/server/src/routers/user.ts.html +0 -862
  462. package/coverage/server/src/routers/worksheet.ts.html +0 -1729
  463. package/coverage/server/src/trpc.ts.html +0 -397
  464. package/coverage/server/src/types/index.html +0 -116
  465. package/coverage/server/src/types/trpc.ts.html +0 -127
  466. package/coverage/server/src/utils/aiUser.ts.html +0 -280
  467. package/coverage/server/src/utils/email.ts.html +0 -121
  468. package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
  469. package/coverage/server/src/utils/index.html +0 -206
  470. package/coverage/server/src/utils/inference.ts.html +0 -709
  471. package/coverage/server/src/utils/logger.ts.html +0 -664
  472. package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
  473. package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
  474. package/coverage/server/vitest.config.ts.html +0 -196
  475. package/coverage/sort-arrow-sprite.png +0 -0
  476. package/coverage/sorter.js +0 -210
  477. package/src/lib/notificationHandler.ts +0 -36
  478. package/tests/announcement.test.ts +0 -164
  479. package/tests/assignment.test.ts +0 -296
  480. package/tests/auth.test.ts +0 -48
@@ -1,1020 +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
- markScheme: {
709
- select: {
710
- structured: true,
711
- }
712
- },
713
- gradingBoundaryId: true,
714
- gradingBoundary: {
715
- select: {
716
- structured: true,
717
- }
718
- },
719
- }
720
- },
721
- }
722
- });
723
-
724
- return {
725
- grades,
726
- };
727
- }),
728
134
  updateGrade: protectedTeacherProcedure
729
- .input(z.object({
730
- classId: z.string(),
731
- assignmentId: z.string(),
732
- submissionId: z.string(),
733
- gradeReceived: z.number().nullable(),
734
- }))
735
- .mutation(async ({ ctx, input }) => {
736
- const { classId, assignmentId, submissionId, gradeReceived } = input;
737
-
738
- // Update the grade
739
- const updatedSubmission = await prisma.submission.update({
740
- where: {
741
- id: submissionId,
742
- assignmentId: assignmentId,
743
- },
744
- data: {
745
- gradeReceived,
746
- },
747
- include: {
748
- assignment: {
749
- select: {
750
- id: true,
751
- title: true,
752
- maxGrade: true,
753
- weight: true,
754
- }
755
- }
756
- }
757
- });
758
-
759
- return updatedSubmission;
760
- }),
761
- getEvents: protectedTeacherProcedure
762
- .input(z.object({
763
- classId: z.string(),
764
- }))
765
- .query(async ({ ctx, input }) => {
766
- const { classId } = input;
767
-
768
- const events = await prisma.event.findMany({
769
- where: {
770
- class: {
771
- id: classId,
772
- }
773
- },
774
- select: {
775
- name: true,
776
- startTime: true,
777
- endTime: true,
778
- }
779
- });
780
-
781
- return events;
782
- }),
783
- listMarkSchemes: protectedClassMemberProcedure
784
- .input(z.object({
785
- classId: z.string(),
786
- }))
787
- .query(async ({ ctx, input }) => {
788
- const { classId } = input;
789
-
790
- const markSchemes = await prisma.markScheme.findMany({
791
- where: {
792
- class: {
793
- id: classId,
794
- },
795
- },
796
- });
797
-
798
- return markSchemes;
799
- }),
800
- createMarkScheme: protectedTeacherProcedure
801
- .input(z.object({
135
+ .input(
136
+ z.object({
802
137
  classId: z.string(),
803
- structure: z.string(),
804
- }))
805
- .mutation(async ({ ctx, input }) => {
806
- const { classId, structure } = input;
807
-
808
- const validatedStructure = structure.replace(/\\n/g, '\n');
809
-
810
- const markScheme = await prisma.markScheme.create({
811
- data: {
812
- class: {
813
- connect: {
814
- id: classId,
815
- },
816
- },
817
- structured: validatedStructure,
818
- },
819
- });
820
-
821
- return markScheme;
822
- }),
823
- updateMarkScheme: protectedTeacherProcedure
824
- .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({
825
167
  classId: z.string(),
826
168
  markSchemeId: z.string(),
827
169
  structure: z.string(),
828
- }))
829
- .mutation(async ({ ctx, input }) => {
830
- const { classId, markSchemeId, structure } = input;
831
-
832
- const validatedStructure = structure.replace(/\\n/g, '\n');
833
-
834
- const markScheme = await prisma.markScheme.update({
835
- where: { id: markSchemeId },
836
- data: {
837
- class: {
838
- connect: {
839
- id: classId,
840
- },
841
- },
842
- structured: validatedStructure,
843
- },
844
- });
845
-
846
- return markScheme;
847
- }),
848
- deleteMarkScheme: protectedTeacherProcedure
849
- .input(z.object({
850
- classId: z.string(),
851
- markSchemeId: z.string(),
852
- }))
853
- .mutation(async ({ ctx, input }) => {
854
- const { classId, markSchemeId } = input;
855
-
856
- const markScheme = await prisma.markScheme.delete({
857
- where: { id: markSchemeId },
858
- });
859
-
860
- return markScheme;
861
- }),
862
- listGradingBoundaries: protectedClassMemberProcedure
863
- .input(z.object({
864
- classId: z.string(),
865
- }))
866
- .query(async ({ ctx, input }) => {
867
- const { classId } = input;
868
-
869
- const gradingBoundaries = await prisma.gradingBoundary.findMany({
870
- where: {
871
- class: {
872
- id: classId,
873
- },
874
- },
875
- });
876
-
877
- return gradingBoundaries;
878
- }),
879
- createGradingBoundary: protectedTeacherProcedure
880
- .input(z.object({
881
- classId: z.string(),
882
- structure: z.string(),
883
- }))
884
- .mutation(async ({ ctx, input }) => {
885
- const { classId, structure } = input;
886
-
887
- const validatedStructure = structure.replace(/\\n/g, '\n');
888
-
889
- const gradingBoundary = await prisma.gradingBoundary.create({
890
- data: {
891
- class: {
892
- connect: {
893
- id: classId,
894
- },
895
- },
896
- structured: validatedStructure,
897
- },
898
- });
899
-
900
- return gradingBoundary;
901
- }),
902
- updateGradingBoundary: protectedTeacherProcedure
903
- .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({
904
197
  classId: z.string(),
905
198
  gradingBoundaryId: z.string(),
906
199
  structure: z.string(),
907
- }))
908
- .mutation(async ({ ctx, input }) => {
909
- const { classId, gradingBoundaryId, structure } = input;
910
-
911
- const validatedStructure = structure.replace(/\\n/g, '\n');
912
-
913
- const gradingBoundary = await prisma.gradingBoundary.update({
914
- where: { id: gradingBoundaryId },
915
- data: {
916
- class: {
917
- connect: {
918
- id: classId,
919
- },
920
- },
921
- structured: validatedStructure,
922
- },
923
- });
924
-
925
- return gradingBoundary;
926
- }),
927
- deleteGradingBoundary: protectedTeacherProcedure
928
- .input(z.object({
929
- classId: z.string(),
930
- gradingBoundaryId: z.string(),
931
- }))
932
- .mutation(async ({ ctx, input }) => {
933
- const { classId, gradingBoundaryId } = input;
934
-
935
- const gradingBoundary = await prisma.gradingBoundary.delete({
936
- where: { id: gradingBoundaryId },
937
- });
938
-
939
- return gradingBoundary;
940
- }),
941
- getSyllabus: protectedClassMemberProcedure
942
- .input(z.object({
943
- classId: z.string(),
944
- }))
945
- .query(async ({input}) => {
946
- const {classId} = input;
947
-
948
- const syllabus = (await prisma.class.findUnique({
949
- where: {
950
- id: classId,
951
- },
952
- }))?.syllabus;
953
-
954
- const markSchemes = await prisma.markScheme.findMany({
955
- where: {
956
- classId,
957
- }
958
- });
959
-
960
- const gradingBoundaries = await prisma.gradingBoundary.findMany({
961
- where: {
962
- classId,
963
- }
964
- });
965
-
966
- return {syllabus, gradingBoundaries, markSchemes};
967
- }),
968
- updateSyllabus: protectedTeacherProcedure
969
- .input(z.object({
970
- classId: z.string(),
971
- contents: z.string(),
972
- }))
973
- .mutation(async ({ input }) => {
974
- const { contents, classId } = input;
975
-
976
- if (!contents) throw new TRPCError({
977
- code: 'BAD_REQUEST',
978
- message: "Missing key contents",
979
- });
980
-
981
- const updated = await prisma.class.update({
982
- where: {
983
- id: classId
984
- },
985
- data: {
986
- syllabus: contents,
987
- }
988
- });
989
-
990
- return updated;
991
- }),
992
- // Lab Management Endpoints (Assignment-based)
993
- listLabDrafts: protectedTeacherProcedure
994
- .input(z.object({
995
- classId: z.string(),
996
- }))
997
- .query(async ({ ctx, input }) => {
998
- const { classId } = input;
999
-
1000
- const labDrafts = await prisma.assignment.findMany({
1001
- where: {
1002
- classId: classId,
1003
- teacherId: ctx.user?.id,
1004
- inProgress: true,
1005
- },
1006
- orderBy: {
1007
- modifiedAt: 'desc',
1008
- },
1009
- });
1010
-
1011
- return labDrafts;
1012
- }),
1013
- createLabDraft: protectedTeacherProcedure
1014
- .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({
1015
233
  classId: z.string(),
1016
234
  title: z.string(),
1017
- 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
+ ]),
1018
246
  instructions: z.string(),
1019
247
  dueDate: z.date().optional(),
1020
248
  maxGrade: z.number().optional(),
@@ -1023,32 +251,16 @@ export const classRouter = createTRPCRouter({
1023
251
  sectionId: z.string().optional(),
1024
252
  markSchemeId: z.string().optional(),
1025
253
  gradingBoundaryId: z.string().optional(),
1026
- }))
1027
- .mutation(async ({ ctx, input }) => {
1028
- const { classId, ...draftData } = input;
1029
-
1030
- const labDraft = await prisma.assignment.create({
1031
- data: {
1032
- classId: classId,
1033
- teacherId: ctx.user?.id!,
1034
- inProgress: true,
1035
- graded: draftData.graded ?? false,
1036
- maxGrade: draftData.maxGrade ?? 0,
1037
- weight: draftData.weight ?? 1,
1038
- dueDate: draftData.dueDate || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // Default 1 week from now
1039
- title: draftData.title,
1040
- instructions: draftData.instructions,
1041
- type: draftData.type,
1042
- ...(draftData.sectionId && { sectionId: draftData.sectionId }),
1043
- ...(draftData.markSchemeId && { markSchemeId: draftData.markSchemeId }),
1044
- ...(draftData.gradingBoundaryId && { gradingBoundaryId: draftData.gradingBoundaryId }),
1045
- },
1046
- });
254
+ })
255
+ )
256
+ .mutation(({ ctx, input }) => {
257
+ const { classId, ...draftData } = input;
258
+ return createLabDraft(classId, ctx.user?.id ?? "", draftData);
259
+ }),
1047
260
 
1048
- return labDraft;
1049
- }),
1050
- updateLabDraft: protectedTeacherProcedure
1051
- .input(z.object({
261
+ updateLabDraft: protectedTeacherProcedure
262
+ .input(
263
+ z.object({
1052
264
  classId: z.string(),
1053
265
  draftId: z.string(),
1054
266
  title: z.string().optional(),
@@ -1060,196 +272,61 @@ export const classRouter = createTRPCRouter({
1060
272
  sectionId: z.string().optional(),
1061
273
  markSchemeId: z.string().optional(),
1062
274
  gradingBoundaryId: z.string().optional(),
1063
- }))
1064
- .mutation(async ({ ctx, input }) => {
1065
- const { classId, draftId, ...updateData } = input;
1066
-
1067
- const labDraft = await prisma.assignment.update({
1068
- where: {
1069
- id: draftId,
1070
- classId: classId,
1071
- teacherId: ctx.user?.id!,
1072
- inProgress: true,
1073
- },
1074
- data: {
1075
- ...(updateData.title && { title: updateData.title }),
1076
- ...(updateData.instructions && { instructions: updateData.instructions }),
1077
- ...(updateData.dueDate && { dueDate: updateData.dueDate }),
1078
- ...(updateData.maxGrade !== undefined && { maxGrade: updateData.maxGrade }),
1079
- ...(updateData.weight !== undefined && { weight: updateData.weight }),
1080
- ...(updateData.graded !== undefined && { graded: updateData.graded }),
1081
- ...(updateData.sectionId !== undefined && { sectionId: updateData.sectionId }),
1082
- ...(updateData.markSchemeId !== undefined && { markSchemeId: updateData.markSchemeId }),
1083
- ...(updateData.gradingBoundaryId !== undefined && { gradingBoundaryId: updateData.gradingBoundaryId }),
1084
- modifiedAt: new Date(),
1085
- },
1086
- });
1087
-
1088
- return labDraft;
1089
- }),
1090
- deleteLabDraft: protectedTeacherProcedure
1091
- .input(z.object({
1092
- classId: z.string(),
1093
- draftId: z.string(),
1094
- }))
1095
- .mutation(async ({ ctx, input }) => {
1096
- 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
+ }),
1097
286
 
1098
- const labDraft = await prisma.assignment.delete({
1099
- where: {
1100
- id: draftId,
1101
- classId: classId,
1102
- teacherId: ctx.user?.id!,
1103
- inProgress: true,
1104
- },
1105
- });
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
+ ),
1106
292
 
1107
- return labDraft;
1108
- }),
1109
- publishLabDraft: protectedTeacherProcedure
1110
- .input(z.object({
293
+ publishLabDraft: protectedTeacherProcedure
294
+ .input(
295
+ z.object({
1111
296
  classId: z.string(),
1112
297
  draftId: z.string(),
1113
298
  dueDate: z.date().optional(),
1114
299
  maxGrade: z.number().optional(),
1115
300
  weight: z.number().optional(),
1116
301
  graded: z.boolean().optional(),
1117
- }))
1118
- .mutation(async ({ ctx, input }) => {
1119
- const { classId, draftId, ...publishData } = input;
1120
-
1121
- // Get the lab draft
1122
- const labDraft = await prisma.assignment.findUnique({
1123
- where: {
1124
- id: draftId,
1125
- classId: classId,
1126
- teacherId: ctx.user?.id!,
1127
- inProgress: true,
1128
- },
1129
- });
1130
-
1131
- if (!labDraft) {
1132
- throw new TRPCError({
1133
- code: 'NOT_FOUND',
1134
- message: 'Lab draft not found',
1135
- });
1136
- }
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
+ }),
1137
313
 
1138
- // Publish the draft by updating it to not be in progress
1139
- const publishedAssignment = await prisma.assignment.update({
1140
- where: { id: draftId },
1141
- data: {
1142
- inProgress: false,
1143
- dueDate: publishData.dueDate || labDraft.dueDate,
1144
- maxGrade: publishData.maxGrade || labDraft.maxGrade || 100,
1145
- weight: publishData.weight || labDraft.weight || 1,
1146
- graded: publishData.graded !== undefined ? publishData.graded : true,
1147
- modifiedAt: new Date(),
1148
- },
1149
- });
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 ?? "")),
1150
320
 
1151
- return publishedAssignment;
1152
- }),
1153
- getFiles: protectedClassMemberProcedure
1154
- .input(z.object({
321
+ importClass: protectedTeacherProcedure
322
+ .input(
323
+ z.object({
1155
324
  classId: z.string(),
1156
- }))
1157
- .query(async ({ ctx, input }) => {
1158
- const { classId } = input;
1159
-
1160
- // Get all assignments with their files and submissions
1161
- const assignments = await prisma.assignment.findMany({
1162
- where: {
1163
- classId: classId,
1164
- },
1165
- include: {
1166
- attachments: {
1167
- select: {
1168
- id: true,
1169
- name: true,
1170
- type: true,
1171
- size: true,
1172
- path: true,
1173
- thumbnailId: true,
1174
- uploadedAt: true,
1175
- user: {
1176
- select: {
1177
- id: true,
1178
- username: true,
1179
- },
1180
- },
1181
- },
1182
- },
1183
- submissions: {
1184
- include: {
1185
- attachments: {
1186
- select: {
1187
- id: true,
1188
- name: true,
1189
- type: true,
1190
- size: true,
1191
- path: true,
1192
- thumbnailId: true,
1193
- uploadedAt: true,
1194
- user: {
1195
- select: {
1196
- id: true,
1197
- username: true,
1198
- },
1199
- },
1200
- },
1201
- },
1202
- annotations: {
1203
- select: {
1204
- id: true,
1205
- name: true,
1206
- type: true,
1207
- size: true,
1208
- path: true,
1209
- thumbnailId: true,
1210
- uploadedAt: true,
1211
- user: {
1212
- select: {
1213
- id: true,
1214
- username: true,
1215
- },
1216
- },
1217
- },
1218
- },
1219
- student: {
1220
- select: {
1221
- id: true,
1222
- username: true,
1223
- },
1224
- },
1225
- },
1226
- },
1227
- teacher: {
1228
- select: {
1229
- id: true,
1230
- username: true,
1231
- },
1232
- },
1233
- },
1234
- orderBy: {
1235
- createdAt: 'desc',
1236
- },
1237
- });
1238
-
1239
- // Organize files by assignment structure
1240
- const organizedFiles = assignments.map(assignment => ({
1241
- id: assignment.id,
1242
- title: assignment.title,
1243
- teacher: assignment.teacher,
1244
- teacherAttachments: assignment.attachments,
1245
- students: assignment.submissions.map(submission => ({
1246
- id: submission.student.id,
1247
- username: submission.student.username,
1248
- attachments: submission.attachments,
1249
- annotations: submission.annotations,
1250
- })),
1251
- }));
1252
-
1253
- return organizedFiles;
1254
- }),
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
+ ),
1255
332
  });