@studious-lms/server 1.2.45 → 1.2.47

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 (241) hide show
  1. package/.env.example +45 -0
  2. package/.env.test.example +37 -0
  3. package/README.md +34 -7
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/clover.xml +12110 -0
  7. package/coverage/coverage-final.json +44 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +221 -0
  10. package/coverage/prettify.css +1 -0
  11. package/coverage/prettify.js +2 -0
  12. package/coverage/server/index.html +116 -0
  13. package/coverage/server/src/exportType.ts.html +109 -0
  14. package/coverage/server/src/index.html +161 -0
  15. package/coverage/server/src/index.ts.html +1702 -0
  16. package/coverage/server/src/instrument.ts.html +130 -0
  17. package/coverage/server/src/lib/config/env.ts.html +448 -0
  18. package/coverage/server/src/lib/config/index.html +116 -0
  19. package/coverage/server/src/lib/fileUpload.ts.html +1138 -0
  20. package/coverage/server/src/lib/googleCloudStorage.ts.html +334 -0
  21. package/coverage/server/src/lib/index.html +206 -0
  22. package/coverage/server/src/lib/jsonConversion.ts.html +2323 -0
  23. package/coverage/server/src/lib/jsonStyles.ts.html +193 -0
  24. package/coverage/server/src/lib/notificationHandler.ts.html +193 -0
  25. package/coverage/server/src/lib/pusher.ts.html +121 -0
  26. package/coverage/server/src/lib/thumbnailGenerator.ts.html +592 -0
  27. package/coverage/server/src/middleware/auth.ts.html +646 -0
  28. package/coverage/server/src/middleware/index.html +146 -0
  29. package/coverage/server/src/middleware/logging.ts.html +244 -0
  30. package/coverage/server/src/middleware/security.ts.html +271 -0
  31. package/coverage/server/src/routers/_app.ts.html +232 -0
  32. package/coverage/server/src/routers/agenda.ts.html +319 -0
  33. package/coverage/server/src/routers/announcement.ts.html +3481 -0
  34. package/coverage/server/src/routers/assignment.ts.html +7633 -0
  35. package/coverage/server/src/routers/attendance.ts.html +1030 -0
  36. package/coverage/server/src/routers/auth.ts.html +1081 -0
  37. package/coverage/server/src/routers/class.ts.html +3535 -0
  38. package/coverage/server/src/routers/comment.ts.html +991 -0
  39. package/coverage/server/src/routers/conversation.ts.html +982 -0
  40. package/coverage/server/src/routers/event.ts.html +1609 -0
  41. package/coverage/server/src/routers/file.ts.html +1144 -0
  42. package/coverage/server/src/routers/folder.ts.html +2797 -0
  43. package/coverage/server/src/routers/index.html +386 -0
  44. package/coverage/server/src/routers/labChat.ts.html +3073 -0
  45. package/coverage/server/src/routers/marketing.ts.html +340 -0
  46. package/coverage/server/src/routers/message.ts.html +1912 -0
  47. package/coverage/server/src/routers/notifications.ts.html +364 -0
  48. package/coverage/server/src/routers/section.ts.html +1120 -0
  49. package/coverage/server/src/routers/user.ts.html +862 -0
  50. package/coverage/server/src/routers/worksheet.ts.html +1729 -0
  51. package/coverage/server/src/trpc.ts.html +397 -0
  52. package/coverage/server/src/types/index.html +116 -0
  53. package/coverage/server/src/types/trpc.ts.html +127 -0
  54. package/coverage/server/src/utils/aiUser.ts.html +280 -0
  55. package/coverage/server/src/utils/email.ts.html +121 -0
  56. package/coverage/server/src/utils/generateInviteCode.ts.html +106 -0
  57. package/coverage/server/src/utils/index.html +206 -0
  58. package/coverage/server/src/utils/inference.ts.html +709 -0
  59. package/coverage/server/src/utils/logger.ts.html +664 -0
  60. package/coverage/server/src/utils/prismaErrorHandler.ts.html +907 -0
  61. package/coverage/server/src/utils/prismaWrapper.ts.html +355 -0
  62. package/coverage/server/vitest.config.ts.html +196 -0
  63. package/coverage/sort-arrow-sprite.png +0 -0
  64. package/coverage/sorter.js +210 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +83 -52
  67. package/dist/index.js.map +1 -1
  68. package/dist/instrument.js +15 -8
  69. package/dist/instrument.js.map +1 -1
  70. package/dist/lib/config/env.d.ts +169 -0
  71. package/dist/lib/config/env.d.ts.map +1 -0
  72. package/dist/lib/config/env.js +115 -0
  73. package/dist/lib/config/env.js.map +1 -0
  74. package/dist/lib/fileUpload.d.ts.map +1 -1
  75. package/dist/lib/fileUpload.js +5 -4
  76. package/dist/lib/fileUpload.js.map +1 -1
  77. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  78. package/dist/lib/googleCloudStorage.js +7 -8
  79. package/dist/lib/googleCloudStorage.js.map +1 -1
  80. package/dist/lib/jsonConversion.d.ts.map +1 -1
  81. package/dist/lib/jsonConversion.js +14 -16
  82. package/dist/lib/jsonConversion.js.map +1 -1
  83. package/dist/lib/notificationHandler.d.ts +2 -2
  84. package/dist/lib/prisma.d.ts +2 -2
  85. package/dist/lib/prisma.d.ts.map +1 -1
  86. package/dist/lib/prisma.js +22 -3
  87. package/dist/lib/prisma.js.map +1 -1
  88. package/dist/lib/pusher.d.ts.map +1 -1
  89. package/dist/lib/pusher.js +8 -7
  90. package/dist/lib/pusher.js.map +1 -1
  91. package/dist/middleware/auth.d.ts.map +1 -1
  92. package/dist/middleware/auth.js +7 -5
  93. package/dist/middleware/auth.js.map +1 -1
  94. package/dist/middleware/security.d.ts +5 -0
  95. package/dist/middleware/security.d.ts.map +1 -0
  96. package/dist/middleware/security.js +77 -0
  97. package/dist/middleware/security.js.map +1 -0
  98. package/dist/routers/_app.d.ts +368 -108
  99. package/dist/routers/_app.d.ts.map +1 -1
  100. package/dist/routers/_app.js +4 -2
  101. package/dist/routers/_app.js.map +1 -1
  102. package/dist/routers/agenda.d.ts.map +1 -1
  103. package/dist/routers/agenda.js +12 -9
  104. package/dist/routers/agenda.js.map +1 -1
  105. package/dist/routers/announcement.d.ts +8 -0
  106. package/dist/routers/announcement.d.ts.map +1 -1
  107. package/dist/routers/announcement.js +6 -4
  108. package/dist/routers/announcement.js.map +1 -1
  109. package/dist/routers/assignment.d.ts +17 -4
  110. package/dist/routers/assignment.d.ts.map +1 -1
  111. package/dist/routers/assignment.js +51 -19
  112. package/dist/routers/assignment.js.map +1 -1
  113. package/dist/routers/attendance.d.ts +1 -0
  114. package/dist/routers/attendance.d.ts.map +1 -1
  115. package/dist/routers/attendance.js +4 -4
  116. package/dist/routers/attendance.js.map +1 -1
  117. package/dist/routers/auth.d.ts +20 -0
  118. package/dist/routers/auth.d.ts.map +1 -1
  119. package/dist/routers/auth.js +132 -15
  120. package/dist/routers/auth.js.map +1 -1
  121. package/dist/routers/class.d.ts +10 -0
  122. package/dist/routers/class.d.ts.map +1 -1
  123. package/dist/routers/class.js +49 -5
  124. package/dist/routers/class.js.map +1 -1
  125. package/dist/routers/comment.d.ts +2 -0
  126. package/dist/routers/comment.d.ts.map +1 -1
  127. package/dist/routers/conversation.d.ts +2 -0
  128. package/dist/routers/conversation.d.ts.map +1 -1
  129. package/dist/routers/conversation.js +46 -31
  130. package/dist/routers/conversation.js.map +1 -1
  131. package/dist/routers/file.d.ts.map +1 -1
  132. package/dist/routers/file.js +30 -7
  133. package/dist/routers/file.js.map +1 -1
  134. package/dist/routers/labChat.d.ts +2 -0
  135. package/dist/routers/labChat.d.ts.map +1 -1
  136. package/dist/routers/labChat.js +5 -322
  137. package/dist/routers/labChat.js.map +1 -1
  138. package/dist/routers/marketing.d.ts +1 -1
  139. package/dist/routers/message.d.ts +1 -0
  140. package/dist/routers/message.d.ts.map +1 -1
  141. package/dist/routers/message.js +3 -2
  142. package/dist/routers/message.js.map +1 -1
  143. package/dist/routers/newtonChat.d.ts +55 -0
  144. package/dist/routers/newtonChat.d.ts.map +1 -0
  145. package/dist/routers/newtonChat.js +262 -0
  146. package/dist/routers/newtonChat.js.map +1 -0
  147. package/dist/routers/notifications.d.ts +4 -4
  148. package/dist/routers/section.d.ts +19 -4
  149. package/dist/routers/section.d.ts.map +1 -1
  150. package/dist/routers/section.js +26 -8
  151. package/dist/routers/section.js.map +1 -1
  152. package/dist/routers/user.d.ts.map +1 -1
  153. package/dist/routers/user.js +5 -4
  154. package/dist/routers/user.js.map +1 -1
  155. package/dist/routers/worksheet.d.ts +44 -41
  156. package/dist/routers/worksheet.d.ts.map +1 -1
  157. package/dist/routers/worksheet.js +25 -34
  158. package/dist/routers/worksheet.js.map +1 -1
  159. package/dist/seedDatabase.d.ts +1 -1
  160. package/dist/seedDatabase.js +275 -284
  161. package/dist/seedDatabase.js.map +1 -1
  162. package/dist/server/pipelines/aiLabChat.d.ts +21 -0
  163. package/dist/server/pipelines/aiLabChat.d.ts.map +1 -0
  164. package/dist/server/pipelines/aiLabChat.js +456 -0
  165. package/dist/server/pipelines/aiLabChat.js.map +1 -0
  166. package/dist/server/pipelines/aiNewtonChat.d.ts +30 -0
  167. package/dist/server/pipelines/aiNewtonChat.d.ts.map +1 -0
  168. package/dist/server/pipelines/aiNewtonChat.js +280 -0
  169. package/dist/server/pipelines/aiNewtonChat.js.map +1 -0
  170. package/dist/server/pipelines/gradeWorksheet.d.ts +15 -0
  171. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -0
  172. package/dist/server/pipelines/gradeWorksheet.js +139 -0
  173. package/dist/server/pipelines/gradeWorksheet.js.map +1 -0
  174. package/dist/trpc.d.ts.map +1 -1
  175. package/dist/trpc.js +2 -2
  176. package/dist/trpc.js.map +1 -1
  177. package/dist/utils/email.d.ts +9 -1
  178. package/dist/utils/email.d.ts.map +1 -1
  179. package/dist/utils/email.js +20 -5
  180. package/dist/utils/email.js.map +1 -1
  181. package/dist/utils/inference.d.ts +5 -0
  182. package/dist/utils/inference.d.ts.map +1 -1
  183. package/dist/utils/inference.js +71 -7
  184. package/dist/utils/inference.js.map +1 -1
  185. package/dist/utils/logger.d.ts.map +1 -1
  186. package/dist/utils/logger.js +3 -3
  187. package/dist/utils/logger.js.map +1 -1
  188. package/docker-compose.yml +14 -0
  189. package/package.json +13 -4
  190. package/prisma/schema.prisma +34 -5
  191. package/scripts/test-pre-push.ts +14 -0
  192. package/src/index.ts +98 -54
  193. package/src/instrument.ts +13 -6
  194. package/src/lib/config/env.ts +126 -0
  195. package/src/lib/fileUpload.ts +3 -2
  196. package/src/lib/googleCloudStorage.ts +6 -6
  197. package/src/lib/jsonConversion.ts +12 -14
  198. package/src/lib/prisma.ts +23 -2
  199. package/src/lib/pusher.ts +6 -5
  200. package/src/middleware/auth.ts +5 -3
  201. package/src/middleware/security.ts +80 -0
  202. package/src/routers/_app.ts +2 -0
  203. package/src/routers/agenda.ts +10 -7
  204. package/src/routers/announcement.ts +4 -2
  205. package/src/routers/assignment.ts +74 -41
  206. package/src/routers/attendance.ts +2 -2
  207. package/src/routers/auth.ts +143 -14
  208. package/src/routers/class.ts +52 -3
  209. package/src/routers/conversation.ts +49 -29
  210. package/src/routers/file.ts +29 -5
  211. package/src/routers/labChat.ts +3 -367
  212. package/src/routers/message.ts +1 -1
  213. package/src/routers/newtonChat.ts +299 -0
  214. package/src/routers/section.ts +26 -6
  215. package/src/routers/user.ts +3 -2
  216. package/src/routers/worksheet.ts +26 -38
  217. package/src/seedDatabase.ts +290 -283
  218. package/src/server/pipelines/aiLabChat.ts +507 -0
  219. package/src/server/pipelines/aiNewtonChat.ts +338 -0
  220. package/src/server/pipelines/gradeWorksheet.ts +151 -0
  221. package/src/trpc.ts +2 -0
  222. package/src/utils/email.ts +30 -3
  223. package/src/utils/inference.ts +85 -5
  224. package/src/utils/logger.ts +2 -1
  225. package/tests/announcement.test.ts +164 -0
  226. package/tests/assignment.test.ts +296 -0
  227. package/tests/attendance.test.ts +168 -0
  228. package/tests/auth.test.ts +33 -10
  229. package/tests/class.test.ts +34 -9
  230. package/tests/event.test.ts +228 -0
  231. package/tests/section.test.ts +216 -0
  232. package/tests/setup.ts +70 -16
  233. package/tests/user.test.ts +158 -0
  234. package/vitest.config.ts +26 -0
  235. package/API_SPECIFICATION.md +0 -1597
  236. package/BASE64_REMOVAL_SUMMARY.md +0 -164
  237. package/CHAT_API_SPEC.md +0 -579
  238. package/LAB_CHAT_API_SPEC.md +0 -518
  239. package/dist/routers/school.d.ts +0 -208
  240. package/dist/routers/school.d.ts.map +0 -1
  241. package/dist/routers/school.js +0 -483
@@ -0,0 +1,1120 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for server/src/routers/section.ts</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../../../prettify.css" />
9
+ <link rel="stylesheet" href="../../../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../../../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../../../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../../../index.html">All files</a> / <a href="index.html">server/src/routers</a> section.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">64.16% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>188/293</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">61.7% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>29/47</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">100% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>0/0</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">64.16% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>188/293</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line medium'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a>
68
+ <a name='L3'></a><a href='#L3'>3</a>
69
+ <a name='L4'></a><a href='#L4'>4</a>
70
+ <a name='L5'></a><a href='#L5'>5</a>
71
+ <a name='L6'></a><a href='#L6'>6</a>
72
+ <a name='L7'></a><a href='#L7'>7</a>
73
+ <a name='L8'></a><a href='#L8'>8</a>
74
+ <a name='L9'></a><a href='#L9'>9</a>
75
+ <a name='L10'></a><a href='#L10'>10</a>
76
+ <a name='L11'></a><a href='#L11'>11</a>
77
+ <a name='L12'></a><a href='#L12'>12</a>
78
+ <a name='L13'></a><a href='#L13'>13</a>
79
+ <a name='L14'></a><a href='#L14'>14</a>
80
+ <a name='L15'></a><a href='#L15'>15</a>
81
+ <a name='L16'></a><a href='#L16'>16</a>
82
+ <a name='L17'></a><a href='#L17'>17</a>
83
+ <a name='L18'></a><a href='#L18'>18</a>
84
+ <a name='L19'></a><a href='#L19'>19</a>
85
+ <a name='L20'></a><a href='#L20'>20</a>
86
+ <a name='L21'></a><a href='#L21'>21</a>
87
+ <a name='L22'></a><a href='#L22'>22</a>
88
+ <a name='L23'></a><a href='#L23'>23</a>
89
+ <a name='L24'></a><a href='#L24'>24</a>
90
+ <a name='L25'></a><a href='#L25'>25</a>
91
+ <a name='L26'></a><a href='#L26'>26</a>
92
+ <a name='L27'></a><a href='#L27'>27</a>
93
+ <a name='L28'></a><a href='#L28'>28</a>
94
+ <a name='L29'></a><a href='#L29'>29</a>
95
+ <a name='L30'></a><a href='#L30'>30</a>
96
+ <a name='L31'></a><a href='#L31'>31</a>
97
+ <a name='L32'></a><a href='#L32'>32</a>
98
+ <a name='L33'></a><a href='#L33'>33</a>
99
+ <a name='L34'></a><a href='#L34'>34</a>
100
+ <a name='L35'></a><a href='#L35'>35</a>
101
+ <a name='L36'></a><a href='#L36'>36</a>
102
+ <a name='L37'></a><a href='#L37'>37</a>
103
+ <a name='L38'></a><a href='#L38'>38</a>
104
+ <a name='L39'></a><a href='#L39'>39</a>
105
+ <a name='L40'></a><a href='#L40'>40</a>
106
+ <a name='L41'></a><a href='#L41'>41</a>
107
+ <a name='L42'></a><a href='#L42'>42</a>
108
+ <a name='L43'></a><a href='#L43'>43</a>
109
+ <a name='L44'></a><a href='#L44'>44</a>
110
+ <a name='L45'></a><a href='#L45'>45</a>
111
+ <a name='L46'></a><a href='#L46'>46</a>
112
+ <a name='L47'></a><a href='#L47'>47</a>
113
+ <a name='L48'></a><a href='#L48'>48</a>
114
+ <a name='L49'></a><a href='#L49'>49</a>
115
+ <a name='L50'></a><a href='#L50'>50</a>
116
+ <a name='L51'></a><a href='#L51'>51</a>
117
+ <a name='L52'></a><a href='#L52'>52</a>
118
+ <a name='L53'></a><a href='#L53'>53</a>
119
+ <a name='L54'></a><a href='#L54'>54</a>
120
+ <a name='L55'></a><a href='#L55'>55</a>
121
+ <a name='L56'></a><a href='#L56'>56</a>
122
+ <a name='L57'></a><a href='#L57'>57</a>
123
+ <a name='L58'></a><a href='#L58'>58</a>
124
+ <a name='L59'></a><a href='#L59'>59</a>
125
+ <a name='L60'></a><a href='#L60'>60</a>
126
+ <a name='L61'></a><a href='#L61'>61</a>
127
+ <a name='L62'></a><a href='#L62'>62</a>
128
+ <a name='L63'></a><a href='#L63'>63</a>
129
+ <a name='L64'></a><a href='#L64'>64</a>
130
+ <a name='L65'></a><a href='#L65'>65</a>
131
+ <a name='L66'></a><a href='#L66'>66</a>
132
+ <a name='L67'></a><a href='#L67'>67</a>
133
+ <a name='L68'></a><a href='#L68'>68</a>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a>
138
+ <a name='L73'></a><a href='#L73'>73</a>
139
+ <a name='L74'></a><a href='#L74'>74</a>
140
+ <a name='L75'></a><a href='#L75'>75</a>
141
+ <a name='L76'></a><a href='#L76'>76</a>
142
+ <a name='L77'></a><a href='#L77'>77</a>
143
+ <a name='L78'></a><a href='#L78'>78</a>
144
+ <a name='L79'></a><a href='#L79'>79</a>
145
+ <a name='L80'></a><a href='#L80'>80</a>
146
+ <a name='L81'></a><a href='#L81'>81</a>
147
+ <a name='L82'></a><a href='#L82'>82</a>
148
+ <a name='L83'></a><a href='#L83'>83</a>
149
+ <a name='L84'></a><a href='#L84'>84</a>
150
+ <a name='L85'></a><a href='#L85'>85</a>
151
+ <a name='L86'></a><a href='#L86'>86</a>
152
+ <a name='L87'></a><a href='#L87'>87</a>
153
+ <a name='L88'></a><a href='#L88'>88</a>
154
+ <a name='L89'></a><a href='#L89'>89</a>
155
+ <a name='L90'></a><a href='#L90'>90</a>
156
+ <a name='L91'></a><a href='#L91'>91</a>
157
+ <a name='L92'></a><a href='#L92'>92</a>
158
+ <a name='L93'></a><a href='#L93'>93</a>
159
+ <a name='L94'></a><a href='#L94'>94</a>
160
+ <a name='L95'></a><a href='#L95'>95</a>
161
+ <a name='L96'></a><a href='#L96'>96</a>
162
+ <a name='L97'></a><a href='#L97'>97</a>
163
+ <a name='L98'></a><a href='#L98'>98</a>
164
+ <a name='L99'></a><a href='#L99'>99</a>
165
+ <a name='L100'></a><a href='#L100'>100</a>
166
+ <a name='L101'></a><a href='#L101'>101</a>
167
+ <a name='L102'></a><a href='#L102'>102</a>
168
+ <a name='L103'></a><a href='#L103'>103</a>
169
+ <a name='L104'></a><a href='#L104'>104</a>
170
+ <a name='L105'></a><a href='#L105'>105</a>
171
+ <a name='L106'></a><a href='#L106'>106</a>
172
+ <a name='L107'></a><a href='#L107'>107</a>
173
+ <a name='L108'></a><a href='#L108'>108</a>
174
+ <a name='L109'></a><a href='#L109'>109</a>
175
+ <a name='L110'></a><a href='#L110'>110</a>
176
+ <a name='L111'></a><a href='#L111'>111</a>
177
+ <a name='L112'></a><a href='#L112'>112</a>
178
+ <a name='L113'></a><a href='#L113'>113</a>
179
+ <a name='L114'></a><a href='#L114'>114</a>
180
+ <a name='L115'></a><a href='#L115'>115</a>
181
+ <a name='L116'></a><a href='#L116'>116</a>
182
+ <a name='L117'></a><a href='#L117'>117</a>
183
+ <a name='L118'></a><a href='#L118'>118</a>
184
+ <a name='L119'></a><a href='#L119'>119</a>
185
+ <a name='L120'></a><a href='#L120'>120</a>
186
+ <a name='L121'></a><a href='#L121'>121</a>
187
+ <a name='L122'></a><a href='#L122'>122</a>
188
+ <a name='L123'></a><a href='#L123'>123</a>
189
+ <a name='L124'></a><a href='#L124'>124</a>
190
+ <a name='L125'></a><a href='#L125'>125</a>
191
+ <a name='L126'></a><a href='#L126'>126</a>
192
+ <a name='L127'></a><a href='#L127'>127</a>
193
+ <a name='L128'></a><a href='#L128'>128</a>
194
+ <a name='L129'></a><a href='#L129'>129</a>
195
+ <a name='L130'></a><a href='#L130'>130</a>
196
+ <a name='L131'></a><a href='#L131'>131</a>
197
+ <a name='L132'></a><a href='#L132'>132</a>
198
+ <a name='L133'></a><a href='#L133'>133</a>
199
+ <a name='L134'></a><a href='#L134'>134</a>
200
+ <a name='L135'></a><a href='#L135'>135</a>
201
+ <a name='L136'></a><a href='#L136'>136</a>
202
+ <a name='L137'></a><a href='#L137'>137</a>
203
+ <a name='L138'></a><a href='#L138'>138</a>
204
+ <a name='L139'></a><a href='#L139'>139</a>
205
+ <a name='L140'></a><a href='#L140'>140</a>
206
+ <a name='L141'></a><a href='#L141'>141</a>
207
+ <a name='L142'></a><a href='#L142'>142</a>
208
+ <a name='L143'></a><a href='#L143'>143</a>
209
+ <a name='L144'></a><a href='#L144'>144</a>
210
+ <a name='L145'></a><a href='#L145'>145</a>
211
+ <a name='L146'></a><a href='#L146'>146</a>
212
+ <a name='L147'></a><a href='#L147'>147</a>
213
+ <a name='L148'></a><a href='#L148'>148</a>
214
+ <a name='L149'></a><a href='#L149'>149</a>
215
+ <a name='L150'></a><a href='#L150'>150</a>
216
+ <a name='L151'></a><a href='#L151'>151</a>
217
+ <a name='L152'></a><a href='#L152'>152</a>
218
+ <a name='L153'></a><a href='#L153'>153</a>
219
+ <a name='L154'></a><a href='#L154'>154</a>
220
+ <a name='L155'></a><a href='#L155'>155</a>
221
+ <a name='L156'></a><a href='#L156'>156</a>
222
+ <a name='L157'></a><a href='#L157'>157</a>
223
+ <a name='L158'></a><a href='#L158'>158</a>
224
+ <a name='L159'></a><a href='#L159'>159</a>
225
+ <a name='L160'></a><a href='#L160'>160</a>
226
+ <a name='L161'></a><a href='#L161'>161</a>
227
+ <a name='L162'></a><a href='#L162'>162</a>
228
+ <a name='L163'></a><a href='#L163'>163</a>
229
+ <a name='L164'></a><a href='#L164'>164</a>
230
+ <a name='L165'></a><a href='#L165'>165</a>
231
+ <a name='L166'></a><a href='#L166'>166</a>
232
+ <a name='L167'></a><a href='#L167'>167</a>
233
+ <a name='L168'></a><a href='#L168'>168</a>
234
+ <a name='L169'></a><a href='#L169'>169</a>
235
+ <a name='L170'></a><a href='#L170'>170</a>
236
+ <a name='L171'></a><a href='#L171'>171</a>
237
+ <a name='L172'></a><a href='#L172'>172</a>
238
+ <a name='L173'></a><a href='#L173'>173</a>
239
+ <a name='L174'></a><a href='#L174'>174</a>
240
+ <a name='L175'></a><a href='#L175'>175</a>
241
+ <a name='L176'></a><a href='#L176'>176</a>
242
+ <a name='L177'></a><a href='#L177'>177</a>
243
+ <a name='L178'></a><a href='#L178'>178</a>
244
+ <a name='L179'></a><a href='#L179'>179</a>
245
+ <a name='L180'></a><a href='#L180'>180</a>
246
+ <a name='L181'></a><a href='#L181'>181</a>
247
+ <a name='L182'></a><a href='#L182'>182</a>
248
+ <a name='L183'></a><a href='#L183'>183</a>
249
+ <a name='L184'></a><a href='#L184'>184</a>
250
+ <a name='L185'></a><a href='#L185'>185</a>
251
+ <a name='L186'></a><a href='#L186'>186</a>
252
+ <a name='L187'></a><a href='#L187'>187</a>
253
+ <a name='L188'></a><a href='#L188'>188</a>
254
+ <a name='L189'></a><a href='#L189'>189</a>
255
+ <a name='L190'></a><a href='#L190'>190</a>
256
+ <a name='L191'></a><a href='#L191'>191</a>
257
+ <a name='L192'></a><a href='#L192'>192</a>
258
+ <a name='L193'></a><a href='#L193'>193</a>
259
+ <a name='L194'></a><a href='#L194'>194</a>
260
+ <a name='L195'></a><a href='#L195'>195</a>
261
+ <a name='L196'></a><a href='#L196'>196</a>
262
+ <a name='L197'></a><a href='#L197'>197</a>
263
+ <a name='L198'></a><a href='#L198'>198</a>
264
+ <a name='L199'></a><a href='#L199'>199</a>
265
+ <a name='L200'></a><a href='#L200'>200</a>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a>
269
+ <a name='L204'></a><a href='#L204'>204</a>
270
+ <a name='L205'></a><a href='#L205'>205</a>
271
+ <a name='L206'></a><a href='#L206'>206</a>
272
+ <a name='L207'></a><a href='#L207'>207</a>
273
+ <a name='L208'></a><a href='#L208'>208</a>
274
+ <a name='L209'></a><a href='#L209'>209</a>
275
+ <a name='L210'></a><a href='#L210'>210</a>
276
+ <a name='L211'></a><a href='#L211'>211</a>
277
+ <a name='L212'></a><a href='#L212'>212</a>
278
+ <a name='L213'></a><a href='#L213'>213</a>
279
+ <a name='L214'></a><a href='#L214'>214</a>
280
+ <a name='L215'></a><a href='#L215'>215</a>
281
+ <a name='L216'></a><a href='#L216'>216</a>
282
+ <a name='L217'></a><a href='#L217'>217</a>
283
+ <a name='L218'></a><a href='#L218'>218</a>
284
+ <a name='L219'></a><a href='#L219'>219</a>
285
+ <a name='L220'></a><a href='#L220'>220</a>
286
+ <a name='L221'></a><a href='#L221'>221</a>
287
+ <a name='L222'></a><a href='#L222'>222</a>
288
+ <a name='L223'></a><a href='#L223'>223</a>
289
+ <a name='L224'></a><a href='#L224'>224</a>
290
+ <a name='L225'></a><a href='#L225'>225</a>
291
+ <a name='L226'></a><a href='#L226'>226</a>
292
+ <a name='L227'></a><a href='#L227'>227</a>
293
+ <a name='L228'></a><a href='#L228'>228</a>
294
+ <a name='L229'></a><a href='#L229'>229</a>
295
+ <a name='L230'></a><a href='#L230'>230</a>
296
+ <a name='L231'></a><a href='#L231'>231</a>
297
+ <a name='L232'></a><a href='#L232'>232</a>
298
+ <a name='L233'></a><a href='#L233'>233</a>
299
+ <a name='L234'></a><a href='#L234'>234</a>
300
+ <a name='L235'></a><a href='#L235'>235</a>
301
+ <a name='L236'></a><a href='#L236'>236</a>
302
+ <a name='L237'></a><a href='#L237'>237</a>
303
+ <a name='L238'></a><a href='#L238'>238</a>
304
+ <a name='L239'></a><a href='#L239'>239</a>
305
+ <a name='L240'></a><a href='#L240'>240</a>
306
+ <a name='L241'></a><a href='#L241'>241</a>
307
+ <a name='L242'></a><a href='#L242'>242</a>
308
+ <a name='L243'></a><a href='#L243'>243</a>
309
+ <a name='L244'></a><a href='#L244'>244</a>
310
+ <a name='L245'></a><a href='#L245'>245</a>
311
+ <a name='L246'></a><a href='#L246'>246</a>
312
+ <a name='L247'></a><a href='#L247'>247</a>
313
+ <a name='L248'></a><a href='#L248'>248</a>
314
+ <a name='L249'></a><a href='#L249'>249</a>
315
+ <a name='L250'></a><a href='#L250'>250</a>
316
+ <a name='L251'></a><a href='#L251'>251</a>
317
+ <a name='L252'></a><a href='#L252'>252</a>
318
+ <a name='L253'></a><a href='#L253'>253</a>
319
+ <a name='L254'></a><a href='#L254'>254</a>
320
+ <a name='L255'></a><a href='#L255'>255</a>
321
+ <a name='L256'></a><a href='#L256'>256</a>
322
+ <a name='L257'></a><a href='#L257'>257</a>
323
+ <a name='L258'></a><a href='#L258'>258</a>
324
+ <a name='L259'></a><a href='#L259'>259</a>
325
+ <a name='L260'></a><a href='#L260'>260</a>
326
+ <a name='L261'></a><a href='#L261'>261</a>
327
+ <a name='L262'></a><a href='#L262'>262</a>
328
+ <a name='L263'></a><a href='#L263'>263</a>
329
+ <a name='L264'></a><a href='#L264'>264</a>
330
+ <a name='L265'></a><a href='#L265'>265</a>
331
+ <a name='L266'></a><a href='#L266'>266</a>
332
+ <a name='L267'></a><a href='#L267'>267</a>
333
+ <a name='L268'></a><a href='#L268'>268</a>
334
+ <a name='L269'></a><a href='#L269'>269</a>
335
+ <a name='L270'></a><a href='#L270'>270</a>
336
+ <a name='L271'></a><a href='#L271'>271</a>
337
+ <a name='L272'></a><a href='#L272'>272</a>
338
+ <a name='L273'></a><a href='#L273'>273</a>
339
+ <a name='L274'></a><a href='#L274'>274</a>
340
+ <a name='L275'></a><a href='#L275'>275</a>
341
+ <a name='L276'></a><a href='#L276'>276</a>
342
+ <a name='L277'></a><a href='#L277'>277</a>
343
+ <a name='L278'></a><a href='#L278'>278</a>
344
+ <a name='L279'></a><a href='#L279'>279</a>
345
+ <a name='L280'></a><a href='#L280'>280</a>
346
+ <a name='L281'></a><a href='#L281'>281</a>
347
+ <a name='L282'></a><a href='#L282'>282</a>
348
+ <a name='L283'></a><a href='#L283'>283</a>
349
+ <a name='L284'></a><a href='#L284'>284</a>
350
+ <a name='L285'></a><a href='#L285'>285</a>
351
+ <a name='L286'></a><a href='#L286'>286</a>
352
+ <a name='L287'></a><a href='#L287'>287</a>
353
+ <a name='L288'></a><a href='#L288'>288</a>
354
+ <a name='L289'></a><a href='#L289'>289</a>
355
+ <a name='L290'></a><a href='#L290'>290</a>
356
+ <a name='L291'></a><a href='#L291'>291</a>
357
+ <a name='L292'></a><a href='#L292'>292</a>
358
+ <a name='L293'></a><a href='#L293'>293</a>
359
+ <a name='L294'></a><a href='#L294'>294</a>
360
+ <a name='L295'></a><a href='#L295'>295</a>
361
+ <a name='L296'></a><a href='#L296'>296</a>
362
+ <a name='L297'></a><a href='#L297'>297</a>
363
+ <a name='L298'></a><a href='#L298'>298</a>
364
+ <a name='L299'></a><a href='#L299'>299</a>
365
+ <a name='L300'></a><a href='#L300'>300</a>
366
+ <a name='L301'></a><a href='#L301'>301</a>
367
+ <a name='L302'></a><a href='#L302'>302</a>
368
+ <a name='L303'></a><a href='#L303'>303</a>
369
+ <a name='L304'></a><a href='#L304'>304</a>
370
+ <a name='L305'></a><a href='#L305'>305</a>
371
+ <a name='L306'></a><a href='#L306'>306</a>
372
+ <a name='L307'></a><a href='#L307'>307</a>
373
+ <a name='L308'></a><a href='#L308'>308</a>
374
+ <a name='L309'></a><a href='#L309'>309</a>
375
+ <a name='L310'></a><a href='#L310'>310</a>
376
+ <a name='L311'></a><a href='#L311'>311</a>
377
+ <a name='L312'></a><a href='#L312'>312</a>
378
+ <a name='L313'></a><a href='#L313'>313</a>
379
+ <a name='L314'></a><a href='#L314'>314</a>
380
+ <a name='L315'></a><a href='#L315'>315</a>
381
+ <a name='L316'></a><a href='#L316'>316</a>
382
+ <a name='L317'></a><a href='#L317'>317</a>
383
+ <a name='L318'></a><a href='#L318'>318</a>
384
+ <a name='L319'></a><a href='#L319'>319</a>
385
+ <a name='L320'></a><a href='#L320'>320</a>
386
+ <a name='L321'></a><a href='#L321'>321</a>
387
+ <a name='L322'></a><a href='#L322'>322</a>
388
+ <a name='L323'></a><a href='#L323'>323</a>
389
+ <a name='L324'></a><a href='#L324'>324</a>
390
+ <a name='L325'></a><a href='#L325'>325</a>
391
+ <a name='L326'></a><a href='#L326'>326</a>
392
+ <a name='L327'></a><a href='#L327'>327</a>
393
+ <a name='L328'></a><a href='#L328'>328</a>
394
+ <a name='L329'></a><a href='#L329'>329</a>
395
+ <a name='L330'></a><a href='#L330'>330</a>
396
+ <a name='L331'></a><a href='#L331'>331</a>
397
+ <a name='L332'></a><a href='#L332'>332</a>
398
+ <a name='L333'></a><a href='#L333'>333</a>
399
+ <a name='L334'></a><a href='#L334'>334</a>
400
+ <a name='L335'></a><a href='#L335'>335</a>
401
+ <a name='L336'></a><a href='#L336'>336</a>
402
+ <a name='L337'></a><a href='#L337'>337</a>
403
+ <a name='L338'></a><a href='#L338'>338</a>
404
+ <a name='L339'></a><a href='#L339'>339</a>
405
+ <a name='L340'></a><a href='#L340'>340</a>
406
+ <a name='L341'></a><a href='#L341'>341</a>
407
+ <a name='L342'></a><a href='#L342'>342</a>
408
+ <a name='L343'></a><a href='#L343'>343</a>
409
+ <a name='L344'></a><a href='#L344'>344</a>
410
+ <a name='L345'></a><a href='#L345'>345</a>
411
+ <a name='L346'></a><a href='#L346'>346</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
412
+ <span class="cline-any cline-yes">1x</span>
413
+ <span class="cline-any cline-yes">1x</span>
414
+ <span class="cline-any cline-yes">1x</span>
415
+ <span class="cline-any cline-neutral">&nbsp;</span>
416
+ <span class="cline-any cline-yes">1x</span>
417
+ <span class="cline-any cline-yes">1x</span>
418
+ <span class="cline-any cline-yes">1x</span>
419
+ <span class="cline-any cline-yes">1x</span>
420
+ <span class="cline-any cline-yes">1x</span>
421
+ <span class="cline-any cline-neutral">&nbsp;</span>
422
+ <span class="cline-any cline-yes">1x</span>
423
+ <span class="cline-any cline-yes">1x</span>
424
+ <span class="cline-any cline-yes">1x</span>
425
+ <span class="cline-any cline-yes">1x</span>
426
+ <span class="cline-any cline-yes">1x</span>
427
+ <span class="cline-any cline-yes">1x</span>
428
+ <span class="cline-any cline-neutral">&nbsp;</span>
429
+ <span class="cline-any cline-yes">1x</span>
430
+ <span class="cline-any cline-yes">1x</span>
431
+ <span class="cline-any cline-yes">1x</span>
432
+ <span class="cline-any cline-yes">1x</span>
433
+ <span class="cline-any cline-neutral">&nbsp;</span>
434
+ <span class="cline-any cline-yes">1x</span>
435
+ <span class="cline-any cline-yes">1x</span>
436
+ <span class="cline-any cline-yes">1x</span>
437
+ <span class="cline-any cline-yes">1x</span>
438
+ <span class="cline-any cline-yes">19x</span>
439
+ <span class="cline-any cline-no">&nbsp;</span>
440
+ <span class="cline-any cline-no">&nbsp;</span>
441
+ <span class="cline-any cline-no">&nbsp;</span>
442
+ <span class="cline-any cline-no">&nbsp;</span>
443
+ <span class="cline-any cline-no">&nbsp;</span>
444
+ <span class="cline-any cline-neutral">&nbsp;</span>
445
+ <span class="cline-any cline-neutral">&nbsp;</span>
446
+ <span class="cline-any cline-yes">19x</span>
447
+ <span class="cline-any cline-yes">19x</span>
448
+ <span class="cline-any cline-yes">19x</span>
449
+ <span class="cline-any cline-yes">19x</span>
450
+ <span class="cline-any cline-yes">19x</span>
451
+ <span class="cline-any cline-yes">19x</span>
452
+ <span class="cline-any cline-yes">19x</span>
453
+ <span class="cline-any cline-yes">19x</span>
454
+ <span class="cline-any cline-yes">19x</span>
455
+ <span class="cline-any cline-yes">19x</span>
456
+ <span class="cline-any cline-neutral">&nbsp;</span>
457
+ <span class="cline-any cline-yes">19x</span>
458
+ <span class="cline-any cline-no">&nbsp;</span>
459
+ <span class="cline-any cline-no">&nbsp;</span>
460
+ <span class="cline-any cline-no">&nbsp;</span>
461
+ <span class="cline-any cline-no">&nbsp;</span>
462
+ <span class="cline-any cline-no">&nbsp;</span>
463
+ <span class="cline-any cline-neutral">&nbsp;</span>
464
+ <span class="cline-any cline-yes">19x</span>
465
+ <span class="cline-any cline-yes">19x</span>
466
+ <span class="cline-any cline-yes">19x</span>
467
+ <span class="cline-any cline-yes">19x</span>
468
+ <span class="cline-any cline-yes">19x</span>
469
+ <span class="cline-any cline-yes">19x</span>
470
+ <span class="cline-any cline-yes">19x</span>
471
+ <span class="cline-any cline-yes">19x</span>
472
+ <span class="cline-any cline-yes">13x</span>
473
+ <span class="cline-any cline-yes">13x</span>
474
+ <span class="cline-any cline-yes">19x</span>
475
+ <span class="cline-any cline-yes">19x</span>
476
+ <span class="cline-any cline-neutral">&nbsp;</span>
477
+ <span class="cline-any cline-neutral">&nbsp;</span>
478
+ <span class="cline-any cline-yes">19x</span>
479
+ <span class="cline-any cline-yes">19x</span>
480
+ <span class="cline-any cline-yes">19x</span>
481
+ <span class="cline-any cline-yes">19x</span>
482
+ <span class="cline-any cline-yes">19x</span>
483
+ <span class="cline-any cline-yes">19x</span>
484
+ <span class="cline-any cline-yes">19x</span>
485
+ <span class="cline-any cline-yes">19x</span>
486
+ <span class="cline-any cline-yes">19x</span>
487
+ <span class="cline-any cline-yes">19x</span>
488
+ <span class="cline-any cline-neutral">&nbsp;</span>
489
+ <span class="cline-any cline-yes">19x</span>
490
+ <span class="cline-any cline-yes">19x</span>
491
+ <span class="cline-any cline-yes">19x</span>
492
+ <span class="cline-any cline-yes">19x</span>
493
+ <span class="cline-any cline-neutral">&nbsp;</span>
494
+ <span class="cline-any cline-yes">19x</span>
495
+ <span class="cline-any cline-yes">19x</span>
496
+ <span class="cline-any cline-neutral">&nbsp;</span>
497
+ <span class="cline-any cline-yes">19x</span>
498
+ <span class="cline-any cline-yes">19x</span>
499
+ <span class="cline-any cline-yes">24x</span>
500
+ <span class="cline-any cline-yes">24x</span>
501
+ <span class="cline-any cline-yes">24x</span>
502
+ <span class="cline-any cline-no">&nbsp;</span>
503
+ <span class="cline-any cline-no">&nbsp;</span>
504
+ <span class="cline-any cline-yes">19x</span>
505
+ <span class="cline-any cline-yes">19x</span>
506
+ <span class="cline-any cline-neutral">&nbsp;</span>
507
+ <span class="cline-any cline-yes">19x</span>
508
+ <span class="cline-any cline-yes">1x</span>
509
+ <span class="cline-any cline-neutral">&nbsp;</span>
510
+ <span class="cline-any cline-yes">1x</span>
511
+ <span class="cline-any cline-yes">1x</span>
512
+ <span class="cline-any cline-yes">1x</span>
513
+ <span class="cline-any cline-yes">1x</span>
514
+ <span class="cline-any cline-neutral">&nbsp;</span>
515
+ <span class="cline-any cline-yes">1x</span>
516
+ <span class="cline-any cline-yes">1x</span>
517
+ <span class="cline-any cline-yes">1x</span>
518
+ <span class="cline-any cline-yes">1x</span>
519
+ <span class="cline-any cline-yes">3x</span>
520
+ <span class="cline-any cline-no">&nbsp;</span>
521
+ <span class="cline-any cline-no">&nbsp;</span>
522
+ <span class="cline-any cline-no">&nbsp;</span>
523
+ <span class="cline-any cline-no">&nbsp;</span>
524
+ <span class="cline-any cline-no">&nbsp;</span>
525
+ <span class="cline-any cline-neutral">&nbsp;</span>
526
+ <span class="cline-any cline-yes">3x</span>
527
+ <span class="cline-any cline-neutral">&nbsp;</span>
528
+ <span class="cline-any cline-yes">3x</span>
529
+ <span class="cline-any cline-yes">3x</span>
530
+ <span class="cline-any cline-yes">3x</span>
531
+ <span class="cline-any cline-yes">3x</span>
532
+ <span class="cline-any cline-neutral">&nbsp;</span>
533
+ <span class="cline-any cline-yes">3x</span>
534
+ <span class="cline-any cline-no">&nbsp;</span>
535
+ <span class="cline-any cline-no">&nbsp;</span>
536
+ <span class="cline-any cline-neutral">&nbsp;</span>
537
+ <span class="cline-any cline-yes">3x</span>
538
+ <span class="cline-any cline-no">&nbsp;</span>
539
+ <span class="cline-any cline-no">&nbsp;</span>
540
+ <span class="cline-any cline-neutral">&nbsp;</span>
541
+ <span class="cline-any cline-yes">3x</span>
542
+ <span class="cline-any cline-yes">3x</span>
543
+ <span class="cline-any cline-yes">3x</span>
544
+ <span class="cline-any cline-yes">3x</span>
545
+ <span class="cline-any cline-yes">3x</span>
546
+ <span class="cline-any cline-yes">3x</span>
547
+ <span class="cline-any cline-yes">3x</span>
548
+ <span class="cline-any cline-yes">3x</span>
549
+ <span class="cline-any cline-yes">3x</span>
550
+ <span class="cline-any cline-yes">3x</span>
551
+ <span class="cline-any cline-yes">3x</span>
552
+ <span class="cline-any cline-neutral">&nbsp;</span>
553
+ <span class="cline-any cline-yes">3x</span>
554
+ <span class="cline-any cline-yes">3x</span>
555
+ <span class="cline-any cline-yes">3x</span>
556
+ <span class="cline-any cline-yes">3x</span>
557
+ <span class="cline-any cline-neutral">&nbsp;</span>
558
+ <span class="cline-any cline-yes">3x</span>
559
+ <span class="cline-any cline-yes">3x</span>
560
+ <span class="cline-any cline-no">&nbsp;</span>
561
+ <span class="cline-any cline-no">&nbsp;</span>
562
+ <span class="cline-any cline-neutral">&nbsp;</span>
563
+ <span class="cline-any cline-yes">3x</span>
564
+ <span class="cline-any cline-neutral">&nbsp;</span>
565
+ <span class="cline-any cline-yes">3x</span>
566
+ <span class="cline-any cline-neutral">&nbsp;</span>
567
+ <span class="cline-any cline-yes">3x</span>
568
+ <span class="cline-any cline-yes">1x</span>
569
+ <span class="cline-any cline-yes">3x</span>
570
+ <span class="cline-any cline-yes">1x</span>
571
+ <span class="cline-any cline-yes">1x</span>
572
+ <span class="cline-any cline-yes">1x</span>
573
+ <span class="cline-any cline-yes">1x</span>
574
+ <span class="cline-any cline-no">&nbsp;</span>
575
+ <span class="cline-any cline-no">&nbsp;</span>
576
+ <span class="cline-any cline-yes">1x</span>
577
+ <span class="cline-any cline-yes">1x</span>
578
+ <span class="cline-any cline-yes">1x</span>
579
+ <span class="cline-any cline-yes">1x</span>
580
+ <span class="cline-any cline-yes">1x</span>
581
+ <span class="cline-any cline-yes">1x</span>
582
+ <span class="cline-any cline-yes">1x</span>
583
+ <span class="cline-any cline-no">&nbsp;</span>
584
+ <span class="cline-any cline-no">&nbsp;</span>
585
+ <span class="cline-any cline-no">&nbsp;</span>
586
+ <span class="cline-any cline-no">&nbsp;</span>
587
+ <span class="cline-any cline-no">&nbsp;</span>
588
+ <span class="cline-any cline-no">&nbsp;</span>
589
+ <span class="cline-any cline-yes">1x</span>
590
+ <span class="cline-any cline-neutral">&nbsp;</span>
591
+ <span class="cline-any cline-neutral">&nbsp;</span>
592
+ <span class="cline-any cline-yes">3x</span>
593
+ <span class="cline-any cline-yes">3x</span>
594
+ <span class="cline-any cline-yes">6x</span>
595
+ <span class="cline-any cline-yes">6x</span>
596
+ <span class="cline-any cline-yes">6x</span>
597
+ <span class="cline-any cline-no">&nbsp;</span>
598
+ <span class="cline-any cline-no">&nbsp;</span>
599
+ <span class="cline-any cline-yes">3x</span>
600
+ <span class="cline-any cline-yes">3x</span>
601
+ <span class="cline-any cline-neutral">&nbsp;</span>
602
+ <span class="cline-any cline-yes">3x</span>
603
+ <span class="cline-any cline-yes">3x</span>
604
+ <span class="cline-any cline-neutral">&nbsp;</span>
605
+ <span class="cline-any cline-yes">3x</span>
606
+ <span class="cline-any cline-yes">1x</span>
607
+ <span class="cline-any cline-neutral">&nbsp;</span>
608
+ <span class="cline-any cline-yes">1x</span>
609
+ <span class="cline-any cline-yes">1x</span>
610
+ <span class="cline-any cline-yes">1x</span>
611
+ <span class="cline-any cline-yes">3x</span>
612
+ <span class="cline-any cline-no">&nbsp;</span>
613
+ <span class="cline-any cline-no">&nbsp;</span>
614
+ <span class="cline-any cline-no">&nbsp;</span>
615
+ <span class="cline-any cline-no">&nbsp;</span>
616
+ <span class="cline-any cline-no">&nbsp;</span>
617
+ <span class="cline-any cline-neutral">&nbsp;</span>
618
+ <span class="cline-any cline-neutral">&nbsp;</span>
619
+ <span class="cline-any cline-yes">3x</span>
620
+ <span class="cline-any cline-yes">3x</span>
621
+ <span class="cline-any cline-yes">3x</span>
622
+ <span class="cline-any cline-yes">3x</span>
623
+ <span class="cline-any cline-yes">3x</span>
624
+ <span class="cline-any cline-yes">3x</span>
625
+ <span class="cline-any cline-yes">3x</span>
626
+ <span class="cline-any cline-yes">3x</span>
627
+ <span class="cline-any cline-yes">3x</span>
628
+ <span class="cline-any cline-yes">3x</span>
629
+ <span class="cline-any cline-neutral">&nbsp;</span>
630
+ <span class="cline-any cline-yes">3x</span>
631
+ <span class="cline-any cline-no">&nbsp;</span>
632
+ <span class="cline-any cline-no">&nbsp;</span>
633
+ <span class="cline-any cline-no">&nbsp;</span>
634
+ <span class="cline-any cline-no">&nbsp;</span>
635
+ <span class="cline-any cline-no">&nbsp;</span>
636
+ <span class="cline-any cline-neutral">&nbsp;</span>
637
+ <span class="cline-any cline-yes">3x</span>
638
+ <span class="cline-any cline-yes">3x</span>
639
+ <span class="cline-any cline-yes">3x</span>
640
+ <span class="cline-any cline-yes">3x</span>
641
+ <span class="cline-any cline-yes">3x</span>
642
+ <span class="cline-any cline-yes">1x</span>
643
+ <span class="cline-any cline-yes">1x</span>
644
+ <span class="cline-any cline-yes">3x</span>
645
+ <span class="cline-any cline-yes">3x</span>
646
+ <span class="cline-any cline-neutral">&nbsp;</span>
647
+ <span class="cline-any cline-yes">2x</span>
648
+ <span class="cline-any cline-yes">1x</span>
649
+ <span class="cline-any cline-neutral">&nbsp;</span>
650
+ <span class="cline-any cline-yes">1x</span>
651
+ <span class="cline-any cline-yes">1x</span>
652
+ <span class="cline-any cline-yes">1x</span>
653
+ <span class="cline-any cline-yes">1x</span>
654
+ <span class="cline-any cline-yes">1x</span>
655
+ <span class="cline-any cline-yes">1x</span>
656
+ <span class="cline-any cline-yes">1x</span>
657
+ <span class="cline-any cline-no">&nbsp;</span>
658
+ <span class="cline-any cline-no">&nbsp;</span>
659
+ <span class="cline-any cline-no">&nbsp;</span>
660
+ <span class="cline-any cline-no">&nbsp;</span>
661
+ <span class="cline-any cline-no">&nbsp;</span>
662
+ <span class="cline-any cline-no">&nbsp;</span>
663
+ <span class="cline-any cline-neutral">&nbsp;</span>
664
+ <span class="cline-any cline-neutral">&nbsp;</span>
665
+ <span class="cline-any cline-no">&nbsp;</span>
666
+ <span class="cline-any cline-no">&nbsp;</span>
667
+ <span class="cline-any cline-no">&nbsp;</span>
668
+ <span class="cline-any cline-no">&nbsp;</span>
669
+ <span class="cline-any cline-no">&nbsp;</span>
670
+ <span class="cline-any cline-no">&nbsp;</span>
671
+ <span class="cline-any cline-no">&nbsp;</span>
672
+ <span class="cline-any cline-no">&nbsp;</span>
673
+ <span class="cline-any cline-no">&nbsp;</span>
674
+ <span class="cline-any cline-no">&nbsp;</span>
675
+ <span class="cline-any cline-neutral">&nbsp;</span>
676
+ <span class="cline-any cline-no">&nbsp;</span>
677
+ <span class="cline-any cline-no">&nbsp;</span>
678
+ <span class="cline-any cline-no">&nbsp;</span>
679
+ <span class="cline-any cline-no">&nbsp;</span>
680
+ <span class="cline-any cline-no">&nbsp;</span>
681
+ <span class="cline-any cline-no">&nbsp;</span>
682
+ <span class="cline-any cline-neutral">&nbsp;</span>
683
+ <span class="cline-any cline-neutral">&nbsp;</span>
684
+ <span class="cline-any cline-no">&nbsp;</span>
685
+ <span class="cline-any cline-no">&nbsp;</span>
686
+ <span class="cline-any cline-no">&nbsp;</span>
687
+ <span class="cline-any cline-no">&nbsp;</span>
688
+ <span class="cline-any cline-no">&nbsp;</span>
689
+ <span class="cline-any cline-neutral">&nbsp;</span>
690
+ <span class="cline-any cline-neutral">&nbsp;</span>
691
+ <span class="cline-any cline-no">&nbsp;</span>
692
+ <span class="cline-any cline-no">&nbsp;</span>
693
+ <span class="cline-any cline-no">&nbsp;</span>
694
+ <span class="cline-any cline-no">&nbsp;</span>
695
+ <span class="cline-any cline-no">&nbsp;</span>
696
+ <span class="cline-any cline-no">&nbsp;</span>
697
+ <span class="cline-any cline-no">&nbsp;</span>
698
+ <span class="cline-any cline-no">&nbsp;</span>
699
+ <span class="cline-any cline-no">&nbsp;</span>
700
+ <span class="cline-any cline-no">&nbsp;</span>
701
+ <span class="cline-any cline-neutral">&nbsp;</span>
702
+ <span class="cline-any cline-no">&nbsp;</span>
703
+ <span class="cline-any cline-no">&nbsp;</span>
704
+ <span class="cline-any cline-no">&nbsp;</span>
705
+ <span class="cline-any cline-no">&nbsp;</span>
706
+ <span class="cline-any cline-neutral">&nbsp;</span>
707
+ <span class="cline-any cline-no">&nbsp;</span>
708
+ <span class="cline-any cline-no">&nbsp;</span>
709
+ <span class="cline-any cline-no">&nbsp;</span>
710
+ <span class="cline-any cline-no">&nbsp;</span>
711
+ <span class="cline-any cline-no">&nbsp;</span>
712
+ <span class="cline-any cline-no">&nbsp;</span>
713
+ <span class="cline-any cline-no">&nbsp;</span>
714
+ <span class="cline-any cline-no">&nbsp;</span>
715
+ <span class="cline-any cline-no">&nbsp;</span>
716
+ <span class="cline-any cline-no">&nbsp;</span>
717
+ <span class="cline-any cline-neutral">&nbsp;</span>
718
+ <span class="cline-any cline-no">&nbsp;</span>
719
+ <span class="cline-any cline-yes">1x</span>
720
+ <span class="cline-any cline-neutral">&nbsp;</span>
721
+ <span class="cline-any cline-yes">1x</span>
722
+ <span class="cline-any cline-yes">1x</span>
723
+ <span class="cline-any cline-yes">1x</span>
724
+ <span class="cline-any cline-yes">1x</span>
725
+ <span class="cline-any cline-no">&nbsp;</span>
726
+ <span class="cline-any cline-no">&nbsp;</span>
727
+ <span class="cline-any cline-no">&nbsp;</span>
728
+ <span class="cline-any cline-no">&nbsp;</span>
729
+ <span class="cline-any cline-no">&nbsp;</span>
730
+ <span class="cline-any cline-neutral">&nbsp;</span>
731
+ <span class="cline-any cline-neutral">&nbsp;</span>
732
+ <span class="cline-any cline-yes">1x</span>
733
+ <span class="cline-any cline-yes">1x</span>
734
+ <span class="cline-any cline-yes">1x</span>
735
+ <span class="cline-any cline-yes">1x</span>
736
+ <span class="cline-any cline-yes">1x</span>
737
+ <span class="cline-any cline-yes">1x</span>
738
+ <span class="cline-any cline-yes">1x</span>
739
+ <span class="cline-any cline-yes">1x</span>
740
+ <span class="cline-any cline-yes">1x</span>
741
+ <span class="cline-any cline-yes">1x</span>
742
+ <span class="cline-any cline-neutral">&nbsp;</span>
743
+ <span class="cline-any cline-yes">1x</span>
744
+ <span class="cline-any cline-no">&nbsp;</span>
745
+ <span class="cline-any cline-no">&nbsp;</span>
746
+ <span class="cline-any cline-no">&nbsp;</span>
747
+ <span class="cline-any cline-no">&nbsp;</span>
748
+ <span class="cline-any cline-no">&nbsp;</span>
749
+ <span class="cline-any cline-neutral">&nbsp;</span>
750
+ <span class="cline-any cline-yes">1x</span>
751
+ <span class="cline-any cline-yes">1x</span>
752
+ <span class="cline-any cline-yes">1x</span>
753
+ <span class="cline-any cline-neutral">&nbsp;</span>
754
+ <span class="cline-any cline-yes">1x</span>
755
+ <span class="cline-any cline-yes">1x</span>
756
+ <span class="cline-any cline-yes">1x</span></td><td class="text"><pre class="prettyprint lang-js">import { z } from "zod";
757
+ import { createTRPCRouter, protectedProcedure, protectedTeacherProcedure } from "../trpc.js";
758
+ import { TRPCError } from "@trpc/server";
759
+ import { prisma } from "../lib/prisma.js";
760
+ &nbsp;
761
+ const createSectionSchema = z.object({
762
+ classId: z.string(),
763
+ name: z.string(),
764
+ color: z.string().optional(),
765
+ });
766
+ &nbsp;
767
+ const updateSectionSchema = z.object({
768
+ id: z.string(),
769
+ classId: z.string(),
770
+ name: z.string(),
771
+ color: z.string().optional(),
772
+ });
773
+ &nbsp;
774
+ const deleteSectionSchema = z.object({
775
+ id: z.string(),
776
+ classId: z.string(),
777
+ });
778
+ &nbsp;
779
+ export const sectionRouter = createTRPCRouter({
780
+ create: protectedTeacherProcedure
781
+ .input(createSectionSchema)
782
+ .mutation(async ({ ctx, input }) =&gt; {
783
+ if (!ctx.user) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
784
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
785
+ <span class="cstat-no" title="statement not covered" > code: "UNAUTHORIZED",</span>
786
+ <span class="cstat-no" title="statement not covered" > message: "User must be authenticated",</span>
787
+ <span class="cstat-no" title="statement not covered" > });</span>
788
+ <span class="cstat-no" title="statement not covered" > }</span>
789
+ &nbsp;
790
+ // Verify user is a teacher of the class
791
+ const classData = await prisma.class.findFirst({
792
+ where: {
793
+ id: input.classId,
794
+ teachers: {
795
+ some: {
796
+ id: ctx.user.id,
797
+ },
798
+ },
799
+ },
800
+ });
801
+ &nbsp;
802
+ if (!classData) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
803
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
804
+ <span class="cstat-no" title="statement not covered" > code: "NOT_FOUND",</span>
805
+ <span class="cstat-no" title="statement not covered" > message: "Class not found or you are not a teacher",</span>
806
+ <span class="cstat-no" title="statement not covered" > });</span>
807
+ <span class="cstat-no" title="statement not covered" > }</span>
808
+ &nbsp;
809
+ const section = await prisma.section.create({
810
+ data: {
811
+ name: input.name,
812
+ order: 0,
813
+ class: {
814
+ connect: { id: input.classId },
815
+ },
816
+ ...(input.color &amp;&amp; {
817
+ color: input.color,
818
+ }),
819
+ },
820
+ });
821
+ &nbsp;
822
+ // Insert new section at top of unified list (sections + assignments) and normalize
823
+ const [sections, assignments] = await Promise.all([
824
+ prisma.section.findMany({
825
+ where: { classId: input.classId },
826
+ select: { id: true, order: true },
827
+ }),
828
+ prisma.assignment.findMany({
829
+ where: { classId: input.classId },
830
+ select: { id: true, order: true },
831
+ }),
832
+ ]);
833
+ &nbsp;
834
+ const unified = [
835
+ ...sections.map(s =&gt; ({ id: s.id, order: s.order, type: 'section' as const })),
836
+ ...assignments.map(a =&gt; ({ id: a.id, order: a.order, type: 'assignment' as const })),
837
+ ].sort((a, b) =&gt; (a.<span class="branch-0 cbranch-no" title="branch not covered" >order ?? Number.MAX_SAFE_INTEGER) - (b</span>.<span class="branch-0 cbranch-no" title="branch not covered" >order ?? Number.MAX_SAFE_INTEGER))</span>;
838
+ &nbsp;
839
+ const withoutNew = unified.filter(item =&gt; !(item.id === section.id &amp;&amp; item.type === 'section'));
840
+ const reindexed = [{ id: section.id, type: 'section' as const }, ...withoutNew.map(item =&gt; ({ id: item.id, type: item.type }))];
841
+ &nbsp;
842
+ await Promise.all(
843
+ reindexed.map((item, index) =&gt; {
844
+ if (item.type === 'section') {
845
+ return prisma.section.update({ where: { id: item.id }, data: { order: index + 1 } });
846
+ <span class="branch-0 cbranch-no" title="branch not covered" > } else {</span>
847
+ <span class="cstat-no" title="statement not covered" > return prisma.assignment.update({ where: { id: item.id }, data: { order: index + 1 } });</span>
848
+ <span class="cstat-no" title="statement not covered" > }</span>
849
+ })
850
+ );
851
+ &nbsp;
852
+ return section;
853
+ }),
854
+ &nbsp;
855
+ reorder: protectedTeacherProcedure
856
+ .input(z.object({
857
+ classId: z.string(),
858
+ movedId: z.string(), // Section ID
859
+ // One of: place at start/end of unified list, or relative to targetId (can be section or assignment)
860
+ position: z.enum(['start', 'end', 'before', 'after']),
861
+ targetId: z.string().optional(), // Can be a section ID or assignment ID
862
+ }))
863
+ .mutation(async ({ ctx, input }) =&gt; {
864
+ if (!ctx.user) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
865
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
866
+ <span class="cstat-no" title="statement not covered" > code: "UNAUTHORIZED",</span>
867
+ <span class="cstat-no" title="statement not covered" > message: "User must be authenticated",</span>
868
+ <span class="cstat-no" title="statement not covered" > });</span>
869
+ <span class="cstat-no" title="statement not covered" > }</span>
870
+ &nbsp;
871
+ const { classId, movedId, position, targetId } = input;
872
+ &nbsp;
873
+ const moved = await prisma.section.findFirst({
874
+ where: { id: movedId, classId },
875
+ select: { id: true, classId: true },
876
+ });
877
+ &nbsp;
878
+ if (!moved) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
879
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({ code: 'NOT_FOUND', message: 'Section not found' });</span>
880
+ <span class="cstat-no" title="statement not covered" > }</span>
881
+ &nbsp;
882
+ if ((position === 'before' || position === 'after') &amp;&amp; !targetId) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
883
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({ code: 'BAD_REQUEST', message: 'targetId required for before/after' });</span>
884
+ <span class="cstat-no" title="statement not covered" > }</span>
885
+ &nbsp;
886
+ const result = await prisma.$transaction(async (tx) =&gt; {
887
+ const [sections, assignments] = await Promise.all([
888
+ tx.section.findMany({
889
+ where: { classId },
890
+ select: { id: true, order: true },
891
+ }),
892
+ tx.assignment.findMany({
893
+ where: { classId },
894
+ select: { id: true, order: true },
895
+ }),
896
+ ]);
897
+ &nbsp;
898
+ const unified = [
899
+ ...sections.map(s =&gt; ({ id: s.id, order: s.order, type: 'section' as const })),
900
+ ...assignments.map(a =&gt; ({ id: a.id, order: a.order, type: 'assignment' as const })),
901
+ ].sort((a, b) =&gt; (a.<span class="branch-0 cbranch-no" title="branch not covered" >order ?? Number.MAX_SAFE_INTEGER) - (b</span>.<span class="branch-0 cbranch-no" title="branch not covered" >order ?? Number.MAX_SAFE_INTEGER))</span>;
902
+ &nbsp;
903
+ const movedIdx = unified.findIndex(item =&gt; item.id === movedId &amp;&amp; item.type === 'section');
904
+ if (movedIdx === -1) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
905
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({ code: 'NOT_FOUND', message: 'Section not found in unified list' });</span>
906
+ <span class="cstat-no" title="statement not covered" > }</span>
907
+ &nbsp;
908
+ const withoutMoved = unified.filter(item =&gt; !(item.id === movedId &amp;&amp; item.type === 'section'));
909
+ &nbsp;
910
+ let next: Array&lt;{ id: string; type: 'section' | 'assignment' }&gt; = [];
911
+ &nbsp;
912
+ if (position === 'start') {
913
+ next = [{ id: movedId, type: 'section' }, ...withoutMoved.map(item =&gt; ({ id: item.id, type: item.type }))];
914
+ } else if (position === 'end') {
915
+ next = [...withoutMoved.map(item =&gt; ({ id: item.id, type: item.type })), { id: movedId, type: 'section' }];
916
+ } else {
917
+ const targetIdx = withoutMoved.findIndex(item =&gt; item.id === targetId);
918
+ if (targetIdx === -1) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
919
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({ code: 'BAD_REQUEST', message: 'targetId not found in unified list' });</span>
920
+ <span class="cstat-no" title="statement not covered" > }</span>
921
+ if (position === 'before') {
922
+ next = [
923
+ ...withoutMoved.slice(0, targetIdx).map(item =&gt; ({ id: item.id, type: item.type })),
924
+ { id: movedId, type: 'section' },
925
+ ...withoutMoved.slice(targetIdx).map(item =&gt; ({ id: item.id, type: item.type })),
926
+ ];
927
+ <span class="branch-0 cbranch-no" title="branch not covered" > } else {</span>
928
+ <span class="cstat-no" title="statement not covered" > next = [</span>
929
+ <span class="cstat-no" title="statement not covered" > ...withoutMoved.slice(0, targetIdx + 1).map(item =&gt; ({ id: item.id, type: item.type })),</span>
930
+ <span class="cstat-no" title="statement not covered" > { id: movedId, type: 'section' },</span>
931
+ <span class="cstat-no" title="statement not covered" > ...withoutMoved.slice(targetIdx + 1).map(item =&gt; ({ id: item.id, type: item.type })),</span>
932
+ <span class="cstat-no" title="statement not covered" > ];</span>
933
+ <span class="cstat-no" title="statement not covered" > }</span>
934
+ }
935
+ &nbsp;
936
+ // Normalize to 1..n
937
+ await Promise.all(
938
+ next.map((item, index) =&gt; {
939
+ if (item.type === 'section') {
940
+ return tx.section.update({ where: { id: item.id }, data: { order: index + 1 } });
941
+ <span class="branch-0 cbranch-no" title="branch not covered" > } else {</span>
942
+ <span class="cstat-no" title="statement not covered" > return tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } });</span>
943
+ <span class="cstat-no" title="statement not covered" > }</span>
944
+ })
945
+ );
946
+ &nbsp;
947
+ return tx.section.findUnique({ where: { id: movedId } });
948
+ });
949
+ &nbsp;
950
+ return result;
951
+ }),
952
+ &nbsp;
953
+ update: protectedTeacherProcedure
954
+ .input(updateSectionSchema)
955
+ .mutation(async ({ ctx, input }) =&gt; {
956
+ if (!ctx.user) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
957
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
958
+ <span class="cstat-no" title="statement not covered" > code: "UNAUTHORIZED",</span>
959
+ <span class="cstat-no" title="statement not covered" > message: "User must be authenticated",</span>
960
+ <span class="cstat-no" title="statement not covered" > });</span>
961
+ <span class="cstat-no" title="statement not covered" > }</span>
962
+ &nbsp;
963
+ // Verify user is a teacher of the class
964
+ const classData = await prisma.class.findFirst({
965
+ where: {
966
+ id: input.classId,
967
+ teachers: {
968
+ some: {
969
+ id: ctx.user.id,
970
+ },
971
+ },
972
+ },
973
+ });
974
+ &nbsp;
975
+ if (!classData) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
976
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
977
+ <span class="cstat-no" title="statement not covered" > code: "NOT_FOUND",</span>
978
+ <span class="cstat-no" title="statement not covered" > message: "Class not found or you are not a teacher",</span>
979
+ <span class="cstat-no" title="statement not covered" > });</span>
980
+ <span class="cstat-no" title="statement not covered" > }</span>
981
+ &nbsp;
982
+ const section = await prisma.section.update({
983
+ where: { id: input.id },
984
+ data: {
985
+ name: input.name,
986
+ ...(input.color &amp;&amp; {
987
+ color: input.color,
988
+ }),
989
+ },
990
+ });
991
+ &nbsp;
992
+ return section;
993
+ }),
994
+ &nbsp;
995
+ reOrder: protectedTeacherProcedure
996
+ .input(z.object({
997
+ id: z.string(),
998
+ classId: z.string(),
999
+ order: z.number(),
1000
+ }))
1001
+ .mutation(async ({ ctx, input }) =&gt; {
1002
+ <span class="cstat-no" title="statement not covered" > if (!ctx.user) {</span>
1003
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
1004
+ <span class="cstat-no" title="statement not covered" > code: "UNAUTHORIZED",</span>
1005
+ <span class="cstat-no" title="statement not covered" > message: "User must be authenticated",</span>
1006
+ <span class="cstat-no" title="statement not covered" > });</span>
1007
+ <span class="cstat-no" title="statement not covered" > }</span>
1008
+ &nbsp;
1009
+ // Verify user is a teacher of the class
1010
+ <span class="cstat-no" title="statement not covered" > const classData = await prisma.class.findFirst({</span>
1011
+ <span class="cstat-no" title="statement not covered" > where: {</span>
1012
+ <span class="cstat-no" title="statement not covered" > id: input.classId,</span>
1013
+ <span class="cstat-no" title="statement not covered" > teachers: {</span>
1014
+ <span class="cstat-no" title="statement not covered" > some: {</span>
1015
+ <span class="cstat-no" title="statement not covered" > id: ctx.user.id,</span>
1016
+ <span class="cstat-no" title="statement not covered" > },</span>
1017
+ <span class="cstat-no" title="statement not covered" > },</span>
1018
+ <span class="cstat-no" title="statement not covered" > },</span>
1019
+ <span class="cstat-no" title="statement not covered" > });</span>
1020
+ &nbsp;
1021
+ <span class="cstat-no" title="statement not covered" > if (!classData) {</span>
1022
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
1023
+ <span class="cstat-no" title="statement not covered" > code: "NOT_FOUND",</span>
1024
+ <span class="cstat-no" title="statement not covered" > message: "Class not found or you are not a teacher",</span>
1025
+ <span class="cstat-no" title="statement not covered" > });</span>
1026
+ <span class="cstat-no" title="statement not covered" > }</span>
1027
+ &nbsp;
1028
+ // Update order and normalize unified list
1029
+ <span class="cstat-no" title="statement not covered" > await prisma.$transaction(async (tx) =&gt; {</span>
1030
+ <span class="cstat-no" title="statement not covered" > await tx.section.update({</span>
1031
+ <span class="cstat-no" title="statement not covered" > where: { id: input.id },</span>
1032
+ <span class="cstat-no" title="statement not covered" > data: { order: input.order },</span>
1033
+ <span class="cstat-no" title="statement not covered" > });</span>
1034
+ &nbsp;
1035
+ // Normalize entire unified list
1036
+ <span class="cstat-no" title="statement not covered" > const [sections, assignments] = await Promise.all([</span>
1037
+ <span class="cstat-no" title="statement not covered" > tx.section.findMany({</span>
1038
+ <span class="cstat-no" title="statement not covered" > where: { classId: input.classId },</span>
1039
+ <span class="cstat-no" title="statement not covered" > select: { id: true, order: true },</span>
1040
+ <span class="cstat-no" title="statement not covered" > }),</span>
1041
+ <span class="cstat-no" title="statement not covered" > tx.assignment.findMany({</span>
1042
+ <span class="cstat-no" title="statement not covered" > where: { classId: input.classId },</span>
1043
+ <span class="cstat-no" title="statement not covered" > select: { id: true, order: true },</span>
1044
+ <span class="cstat-no" title="statement not covered" > }),</span>
1045
+ <span class="cstat-no" title="statement not covered" > ]);</span>
1046
+ &nbsp;
1047
+ <span class="cstat-no" title="statement not covered" > const unified = [</span>
1048
+ <span class="cstat-no" title="statement not covered" > ...sections.map(s =&gt; ({ id: s.id, order: s.order, type: 'section' as const })),</span>
1049
+ <span class="cstat-no" title="statement not covered" > ...assignments.map(a =&gt; ({ id: a.id, order: a.order, type: 'assignment' as const })),</span>
1050
+ <span class="cstat-no" title="statement not covered" > ].sort((a, b) =&gt; (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));</span>
1051
+ &nbsp;
1052
+ <span class="cstat-no" title="statement not covered" > await Promise.all(</span>
1053
+ <span class="cstat-no" title="statement not covered" > unified.map((item, index) =&gt; {</span>
1054
+ <span class="cstat-no" title="statement not covered" > if (item.type === 'section') {</span>
1055
+ <span class="cstat-no" title="statement not covered" > return tx.section.update({ where: { id: item.id }, data: { order: index + 1 } });</span>
1056
+ <span class="cstat-no" title="statement not covered" > } else {</span>
1057
+ <span class="cstat-no" title="statement not covered" > return tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } });</span>
1058
+ <span class="cstat-no" title="statement not covered" > }</span>
1059
+ <span class="cstat-no" title="statement not covered" > })</span>
1060
+ <span class="cstat-no" title="statement not covered" > );</span>
1061
+ <span class="cstat-no" title="statement not covered" > });</span>
1062
+ &nbsp;
1063
+ <span class="cstat-no" title="statement not covered" > return { id: input.id };</span>
1064
+ }),
1065
+ &nbsp;
1066
+ delete: protectedTeacherProcedure
1067
+ .input(deleteSectionSchema)
1068
+ .mutation(async ({ ctx, input }) =&gt; {
1069
+ if (!ctx.user) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1070
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
1071
+ <span class="cstat-no" title="statement not covered" > code: "UNAUTHORIZED",</span>
1072
+ <span class="cstat-no" title="statement not covered" > message: "User must be authenticated",</span>
1073
+ <span class="cstat-no" title="statement not covered" > });</span>
1074
+ <span class="cstat-no" title="statement not covered" > }</span>
1075
+ &nbsp;
1076
+ // Verify user is a teacher of the class
1077
+ const classData = await prisma.class.findFirst({
1078
+ where: {
1079
+ id: input.classId,
1080
+ teachers: {
1081
+ some: {
1082
+ id: ctx.user.id,
1083
+ },
1084
+ },
1085
+ },
1086
+ });
1087
+ &nbsp;
1088
+ if (!classData) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1089
+ <span class="cstat-no" title="statement not covered" > throw new TRPCError({</span>
1090
+ <span class="cstat-no" title="statement not covered" > code: "NOT_FOUND",</span>
1091
+ <span class="cstat-no" title="statement not covered" > message: "Class not found or you are not a teacher",</span>
1092
+ <span class="cstat-no" title="statement not covered" > });</span>
1093
+ <span class="cstat-no" title="statement not covered" > }</span>
1094
+ &nbsp;
1095
+ await prisma.section.delete({
1096
+ where: { id: input.id },
1097
+ });
1098
+ &nbsp;
1099
+ return { id: input.id };
1100
+ }),
1101
+ }); </pre></td></tr></table></pre>
1102
+
1103
+ <div class='push'></div><!-- for sticky footer -->
1104
+ </div><!-- /wrapper -->
1105
+ <div class='footer quiet pad2 space-top1 center small'>
1106
+ Code coverage generated by
1107
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1108
+ at 2025-11-22T10:42:40.796Z
1109
+ </div>
1110
+ <script src="../../../prettify.js"></script>
1111
+ <script>
1112
+ window.onload = function () {
1113
+ prettyPrint();
1114
+ };
1115
+ </script>
1116
+ <script src="../../../sorter.js"></script>
1117
+ <script src="../../../block-navigation.js"></script>
1118
+ </body>
1119
+ </html>
1120
+