@hed-hog/lms 0.0.358 → 0.0.364

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 (330) hide show
  1. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +65 -0
  2. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
  3. package/dist/bitcode-wallet/bitcode-wallet.service.js +72 -0
  4. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
  5. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts +8 -0
  6. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts.map +1 -0
  7. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js +40 -0
  8. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js.map +1 -0
  9. package/dist/class-group/class-group.controller.d.ts +16 -16
  10. package/dist/class-group/class-group.service.d.ts +12 -12
  11. package/dist/course/course-audio-transcription.service.d.ts +3 -2
  12. package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
  13. package/dist/course/course-audio-transcription.service.js +49 -8
  14. package/dist/course/course-audio-transcription.service.js.map +1 -1
  15. package/dist/course/course-lesson.controller.d.ts +4 -0
  16. package/dist/course/course-lesson.controller.d.ts.map +1 -1
  17. package/dist/course/course-lesson.controller.js +10 -0
  18. package/dist/course/course-lesson.controller.js.map +1 -1
  19. package/dist/course/course-structure.controller.d.ts +11 -4
  20. package/dist/course/course-structure.controller.d.ts.map +1 -1
  21. package/dist/course/course-structure.controller.js +14 -0
  22. package/dist/course/course-structure.controller.js.map +1 -1
  23. package/dist/course/course-structure.service.d.ts +15 -1
  24. package/dist/course/course-structure.service.d.ts.map +1 -1
  25. package/dist/course/course-structure.service.js +139 -4
  26. package/dist/course/course-structure.service.js.map +1 -1
  27. package/dist/course/course-video-conversion.service.d.ts +8 -0
  28. package/dist/course/course-video-conversion.service.d.ts.map +1 -1
  29. package/dist/course/course-video-conversion.service.js +87 -51
  30. package/dist/course/course-video-conversion.service.js.map +1 -1
  31. package/dist/course/course.controller.d.ts +73 -1
  32. package/dist/course/course.controller.d.ts.map +1 -1
  33. package/dist/course/course.controller.js +27 -3
  34. package/dist/course/course.controller.js.map +1 -1
  35. package/dist/course/course.module.d.ts.map +1 -1
  36. package/dist/course/course.module.js +15 -3
  37. package/dist/course/course.module.js.map +1 -1
  38. package/dist/course/course.service.d.ts +108 -4
  39. package/dist/course/course.service.d.ts.map +1 -1
  40. package/dist/course/course.service.js +631 -30
  41. package/dist/course/course.service.js.map +1 -1
  42. package/dist/course/dto/cleanup-course-storage.dto.d.ts +6 -0
  43. package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -0
  44. package/dist/course/dto/cleanup-course-storage.dto.js +33 -0
  45. package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -0
  46. package/dist/course/dto/cleanup-upload-history.dto.d.ts +9 -0
  47. package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -0
  48. package/dist/course/dto/cleanup-upload-history.dto.js +36 -0
  49. package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -0
  50. package/dist/course/dto/create-course-bulk-job.dto.d.ts +4 -0
  51. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -0
  52. package/dist/course/dto/create-course-bulk-job.dto.js +21 -0
  53. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -0
  54. package/dist/course/lms-bulk-upload-automation.service.d.ts +39 -0
  55. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -0
  56. package/dist/course/lms-bulk-upload-automation.service.js +443 -0
  57. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -0
  58. package/dist/course/lms-bulk-upload-infra.service.d.ts +31 -0
  59. package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -0
  60. package/dist/course/lms-bulk-upload-infra.service.js +277 -0
  61. package/dist/course/lms-bulk-upload-infra.service.js.map +1 -0
  62. package/dist/course/lms-bulk-upload.constants.d.ts +4 -0
  63. package/dist/course/lms-bulk-upload.constants.d.ts.map +1 -0
  64. package/dist/course/lms-bulk-upload.constants.js +7 -0
  65. package/dist/course/lms-bulk-upload.constants.js.map +1 -0
  66. package/dist/course/lms-bulk-upload.controller.d.ts +153 -0
  67. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -0
  68. package/dist/course/lms-bulk-upload.controller.js +129 -0
  69. package/dist/course/lms-bulk-upload.controller.js.map +1 -0
  70. package/dist/course/lms-bulk-upload.service.d.ts +181 -0
  71. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -0
  72. package/dist/course/lms-bulk-upload.service.js +754 -0
  73. package/dist/course/lms-bulk-upload.service.js.map +1 -0
  74. package/dist/course/lms-setting.controller.d.ts +7 -1
  75. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  76. package/dist/course/lms-setting.controller.js +26 -3
  77. package/dist/course/lms-setting.controller.js.map +1 -1
  78. package/dist/enterprise/enterprise.controller.d.ts +20 -20
  79. package/dist/enterprise/enterprise.service.d.ts +20 -20
  80. package/dist/enterprise/training/training-admin.controller.d.ts +11 -11
  81. package/dist/enterprise/training/training-admin.service.d.ts +11 -11
  82. package/dist/enterprise/training/training-instructor.controller.d.ts +2 -2
  83. package/dist/enterprise/training/training-instructor.service.d.ts +2 -2
  84. package/dist/enterprise/training/training-student.controller.d.ts +1 -1
  85. package/dist/enterprise/training/training-student.service.d.ts +1 -1
  86. package/dist/enterprise/training/training-viewer.controller.d.ts +2 -2
  87. package/dist/evaluation/evaluation.controller.d.ts +8 -8
  88. package/dist/evaluation/evaluation.service.d.ts +8 -8
  89. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts +6 -0
  90. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts.map +1 -0
  91. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js +34 -0
  92. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js.map +1 -0
  93. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts +28 -0
  94. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts.map +1 -0
  95. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js +123 -0
  96. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js.map +1 -0
  97. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts +4 -0
  98. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts.map +1 -0
  99. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js +22 -0
  100. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js.map +1 -0
  101. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts +10 -0
  102. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts.map +1 -0
  103. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js +52 -0
  104. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js.map +1 -0
  105. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts +15 -0
  106. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts.map +1 -0
  107. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js +86 -0
  108. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js.map +1 -0
  109. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +26 -0
  110. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -0
  111. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +304 -0
  112. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -0
  113. package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts +87 -0
  114. package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts.map +1 -0
  115. package/dist/lesson-xp-map/lesson-xp-map.controller.js +185 -0
  116. package/dist/lesson-xp-map/lesson-xp-map.controller.js.map +1 -0
  117. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts +3 -0
  118. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -0
  119. package/dist/lesson-xp-map/lesson-xp-map.module.js +34 -0
  120. package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -0
  121. package/dist/lesson-xp-map/lesson-xp-map.service.d.ts +84 -0
  122. package/dist/lesson-xp-map/lesson-xp-map.service.d.ts.map +1 -0
  123. package/dist/lesson-xp-map/lesson-xp-map.service.js +353 -0
  124. package/dist/lesson-xp-map/lesson-xp-map.service.js.map +1 -0
  125. package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts +10 -0
  126. package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts.map +1 -0
  127. package/dist/lesson-xp-map/lesson-xp-segment.controller.js +63 -0
  128. package/dist/lesson-xp-map/lesson-xp-segment.controller.js.map +1 -0
  129. package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts +27 -0
  130. package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts.map +1 -0
  131. package/dist/lesson-xp-map/lesson-xp-segment.service.js +194 -0
  132. package/dist/lesson-xp-map/lesson-xp-segment.service.js.map +1 -0
  133. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -0
  134. package/dist/lms.module.d.ts.map +1 -1
  135. package/dist/lms.module.js +17 -2
  136. package/dist/lms.module.js.map +1 -1
  137. package/dist/platforma/dto/update-profile.dto.d.ts +17 -0
  138. package/dist/platforma/dto/update-profile.dto.d.ts.map +1 -0
  139. package/dist/platforma/dto/update-profile.dto.js +87 -0
  140. package/dist/platforma/dto/update-profile.dto.js.map +1 -0
  141. package/dist/platforma/platforma.controller.d.ts +94 -7
  142. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  143. package/dist/platforma/platforma.controller.js +85 -2
  144. package/dist/platforma/platforma.controller.js.map +1 -1
  145. package/dist/platforma/platforma.service.d.ts +27 -0
  146. package/dist/platforma/platforma.service.d.ts.map +1 -0
  147. package/dist/platforma/platforma.service.js +274 -0
  148. package/dist/platforma/platforma.service.js.map +1 -0
  149. package/dist/student-xp/student-xp.controller.d.ts +41 -0
  150. package/dist/student-xp/student-xp.controller.d.ts.map +1 -0
  151. package/dist/student-xp/student-xp.controller.js +114 -0
  152. package/dist/student-xp/student-xp.controller.js.map +1 -0
  153. package/dist/student-xp/student-xp.module.d.ts +3 -0
  154. package/dist/student-xp/student-xp.module.d.ts.map +1 -0
  155. package/dist/student-xp/student-xp.module.js +25 -0
  156. package/dist/student-xp/student-xp.module.js.map +1 -0
  157. package/dist/student-xp/student-xp.service.d.ts +65 -0
  158. package/dist/student-xp/student-xp.service.d.ts.map +1 -0
  159. package/dist/student-xp/student-xp.service.js +197 -0
  160. package/dist/student-xp/student-xp.service.js.map +1 -0
  161. package/dist/xp-catalog/dto/create-xp-area.dto.d.ts +12 -0
  162. package/dist/xp-catalog/dto/create-xp-area.dto.d.ts.map +1 -0
  163. package/dist/xp-catalog/dto/create-xp-area.dto.js +63 -0
  164. package/dist/xp-catalog/dto/create-xp-area.dto.js.map +1 -0
  165. package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts +11 -0
  166. package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts.map +1 -0
  167. package/dist/xp-catalog/dto/create-xp-learning-type.dto.js +57 -0
  168. package/dist/xp-catalog/dto/create-xp-learning-type.dto.js.map +1 -0
  169. package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts +11 -0
  170. package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts.map +1 -0
  171. package/dist/xp-catalog/dto/create-xp-skill.dto.js +57 -0
  172. package/dist/xp-catalog/dto/create-xp-skill.dto.js.map +1 -0
  173. package/dist/xp-catalog/dto/update-xp-area.dto.d.ts +12 -0
  174. package/dist/xp-catalog/dto/update-xp-area.dto.d.ts.map +1 -0
  175. package/dist/xp-catalog/dto/update-xp-area.dto.js +66 -0
  176. package/dist/xp-catalog/dto/update-xp-area.dto.js.map +1 -0
  177. package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts +11 -0
  178. package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts.map +1 -0
  179. package/dist/xp-catalog/dto/update-xp-learning-type.dto.js +60 -0
  180. package/dist/xp-catalog/dto/update-xp-learning-type.dto.js.map +1 -0
  181. package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts +11 -0
  182. package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts.map +1 -0
  183. package/dist/xp-catalog/dto/update-xp-skill.dto.js +60 -0
  184. package/dist/xp-catalog/dto/update-xp-skill.dto.js.map +1 -0
  185. package/dist/xp-catalog/xp-area.controller.d.ts +25 -0
  186. package/dist/xp-catalog/xp-area.controller.d.ts.map +1 -0
  187. package/dist/xp-catalog/xp-area.controller.js +105 -0
  188. package/dist/xp-catalog/xp-area.controller.js.map +1 -0
  189. package/dist/xp-catalog/xp-area.service.d.ts +35 -0
  190. package/dist/xp-catalog/xp-area.service.d.ts.map +1 -0
  191. package/dist/xp-catalog/xp-area.service.js +168 -0
  192. package/dist/xp-catalog/xp-area.service.js.map +1 -0
  193. package/dist/xp-catalog/xp-catalog.module.d.ts +3 -0
  194. package/dist/xp-catalog/xp-catalog.module.d.ts.map +1 -0
  195. package/dist/xp-catalog/xp-catalog.module.js +29 -0
  196. package/dist/xp-catalog/xp-catalog.module.js.map +1 -0
  197. package/dist/xp-catalog/xp-learning-type.controller.d.ts +20 -0
  198. package/dist/xp-catalog/xp-learning-type.controller.d.ts.map +1 -0
  199. package/dist/xp-catalog/xp-learning-type.controller.js +96 -0
  200. package/dist/xp-catalog/xp-learning-type.controller.js.map +1 -0
  201. package/dist/xp-catalog/xp-learning-type.service.d.ts +30 -0
  202. package/dist/xp-catalog/xp-learning-type.service.d.ts.map +1 -0
  203. package/dist/xp-catalog/xp-learning-type.service.js +146 -0
  204. package/dist/xp-catalog/xp-learning-type.service.js.map +1 -0
  205. package/dist/xp-catalog/xp-skill.controller.d.ts +26 -0
  206. package/dist/xp-catalog/xp-skill.controller.d.ts.map +1 -0
  207. package/dist/xp-catalog/xp-skill.controller.js +113 -0
  208. package/dist/xp-catalog/xp-skill.controller.js.map +1 -0
  209. package/dist/xp-catalog/xp-skill.service.d.ts +37 -0
  210. package/dist/xp-catalog/xp-skill.service.d.ts.map +1 -0
  211. package/dist/xp-catalog/xp-skill.service.js +174 -0
  212. package/dist/xp-catalog/xp-skill.service.js.map +1 -0
  213. package/hedhog/data/menu.yaml +101 -0
  214. package/hedhog/data/role.yaml +8 -0
  215. package/hedhog/data/route.yaml +547 -0
  216. package/hedhog/data/setting_group.yaml +33 -0
  217. package/hedhog/data/xp_area.yaml +164 -0
  218. package/hedhog/data/xp_learning_type.yaml +131 -0
  219. package/hedhog/data/xp_skill.yaml +1834 -0
  220. package/hedhog/frontend/app/achievements/page.tsx.ejs +108 -118
  221. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +22 -34
  222. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +1453 -0
  223. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +21 -45
  224. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +40 -74
  225. package/hedhog/frontend/app/classes/page.tsx.ejs +56 -85
  226. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +3 -2
  227. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +48 -5
  228. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +4 -4
  229. package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +19 -2
  230. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +1170 -0
  231. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +16 -0
  232. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +2 -0
  233. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +623 -0
  234. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +1458 -0
  235. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +55 -2
  236. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +442 -104
  237. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +296 -49
  238. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +3 -0
  239. package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +1 -0
  240. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +101 -85
  241. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +21 -1
  242. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +3 -0
  243. package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +7 -1
  244. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +44 -0
  245. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +54 -0
  246. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +52 -0
  247. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-xp-overview.ts.ejs +76 -0
  248. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-xp-map.ts.ejs +128 -0
  249. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +30 -0
  250. package/hedhog/frontend/app/courses/[id]/structure/_utils/xp-color-config.ts.ejs +115 -0
  251. package/hedhog/frontend/app/courses/_components/CourseDeleteDialog.tsx.ejs +223 -0
  252. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +89 -0
  253. package/hedhog/frontend/app/courses/page.tsx.ejs +400 -230
  254. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -63
  255. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +53 -77
  256. package/hedhog/frontend/app/exams/page.tsx.ejs +54 -90
  257. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +23 -36
  258. package/hedhog/frontend/app/instructors/page.tsx.ejs +72 -81
  259. package/hedhog/frontend/app/paths/page.tsx.ejs +40 -68
  260. package/hedhog/frontend/app/training/page.tsx.ejs +40 -68
  261. package/hedhog/frontend/app/xp/areas/page.tsx.ejs +782 -0
  262. package/hedhog/frontend/app/xp/learning-types/page.tsx.ejs +690 -0
  263. package/hedhog/frontend/app/xp/skills/page.tsx.ejs +811 -0
  264. package/hedhog/frontend/messages/en.json +386 -3
  265. package/hedhog/frontend/messages/pt.json +386 -3
  266. package/hedhog/table/lesson_xp_map.yaml +50 -0
  267. package/hedhog/table/lesson_xp_segment.yaml +40 -0
  268. package/hedhog/table/lesson_xp_segment_area.yaml +24 -0
  269. package/hedhog/table/lesson_xp_segment_learning_type.yaml +24 -0
  270. package/hedhog/table/lesson_xp_segment_skill.yaml +24 -0
  271. package/hedhog/table/lms_bulk_upload_item.yaml +44 -0
  272. package/hedhog/table/lms_bulk_upload_session.yaml +42 -0
  273. package/hedhog/table/student_area_xp.yaml +30 -0
  274. package/hedhog/table/student_learning_type_xp.yaml +30 -0
  275. package/hedhog/table/student_skill_xp.yaml +30 -0
  276. package/hedhog/table/student_xp_event.yaml +34 -0
  277. package/hedhog/table/xp_area.yaml +39 -0
  278. package/hedhog/table/xp_learning_type.yaml +34 -0
  279. package/hedhog/table/xp_skill.yaml +39 -0
  280. package/package.json +9 -8
  281. package/src/bitcode-wallet/bitcode-wallet.service.ts +113 -0
  282. package/src/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.ts +32 -0
  283. package/src/course/course-audio-transcription.service.ts +58 -21
  284. package/src/course/course-lesson.controller.ts +6 -1
  285. package/src/course/course-structure.controller.ts +10 -0
  286. package/src/course/course-structure.service.ts +174 -1
  287. package/src/course/course-video-conversion.service.ts +113 -75
  288. package/src/course/course.controller.ts +22 -3
  289. package/src/course/course.module.ts +15 -3
  290. package/src/course/course.service.ts +847 -30
  291. package/src/course/dto/cleanup-course-storage.dto.ts +22 -0
  292. package/src/course/dto/cleanup-upload-history.dto.ts +26 -0
  293. package/src/course/dto/create-course-bulk-job.dto.ts +6 -0
  294. package/src/course/lms-bulk-upload-automation.service.ts +560 -0
  295. package/src/course/lms-bulk-upload-infra.service.ts +327 -0
  296. package/src/course/lms-bulk-upload.constants.ts +5 -0
  297. package/src/course/lms-bulk-upload.controller.ts +103 -0
  298. package/src/course/lms-bulk-upload.service.ts +1029 -0
  299. package/src/course/lms-setting.controller.ts +28 -1
  300. package/src/lesson-xp-map/dto/create-lesson-xp-map.dto.ts +17 -0
  301. package/src/lesson-xp-map/dto/create-lesson-xp-segment.dto.ts +102 -0
  302. package/src/lesson-xp-map/dto/review-lesson-xp-map.dto.ts +7 -0
  303. package/src/lesson-xp-map/dto/update-lesson-xp-map.dto.ts +36 -0
  304. package/src/lesson-xp-map/dto/update-lesson-xp-segment.dto.ts +78 -0
  305. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +396 -0
  306. package/src/lesson-xp-map/lesson-xp-map.controller.ts +116 -0
  307. package/src/lesson-xp-map/lesson-xp-map.module.ts +21 -0
  308. package/src/lesson-xp-map/lesson-xp-map.service.ts +442 -0
  309. package/src/lesson-xp-map/lesson-xp-segment.controller.ts +36 -0
  310. package/src/lesson-xp-map/lesson-xp-segment.service.ts +229 -0
  311. package/src/lms.module.ts +17 -2
  312. package/src/platforma/dto/update-profile.dto.ts +59 -0
  313. package/src/platforma/platforma.controller.ts +57 -2
  314. package/src/platforma/platforma.service.ts +268 -0
  315. package/src/student-xp/student-xp.controller.ts +76 -0
  316. package/src/student-xp/student-xp.module.ts +12 -0
  317. package/src/student-xp/student-xp.service.ts +236 -0
  318. package/src/xp-catalog/dto/create-xp-area.dto.ts +40 -0
  319. package/src/xp-catalog/dto/create-xp-learning-type.dto.ts +35 -0
  320. package/src/xp-catalog/dto/create-xp-skill.dto.ts +35 -0
  321. package/src/xp-catalog/dto/update-xp-area.dto.ts +43 -0
  322. package/src/xp-catalog/dto/update-xp-learning-type.dto.ts +38 -0
  323. package/src/xp-catalog/dto/update-xp-skill.dto.ts +38 -0
  324. package/src/xp-catalog/xp-area.controller.ts +64 -0
  325. package/src/xp-catalog/xp-area.service.ts +196 -0
  326. package/src/xp-catalog/xp-catalog.module.ts +16 -0
  327. package/src/xp-catalog/xp-learning-type.controller.ts +59 -0
  328. package/src/xp-catalog/xp-learning-type.service.ts +170 -0
  329. package/src/xp-catalog/xp-skill.controller.ts +71 -0
  330. package/src/xp-catalog/xp-skill.service.ts +205 -0
@@ -1,6 +1,7 @@
1
1
  import { PrismaService } from '@hed-hog/api-prisma';
2
2
  import { FileService } from '@hed-hog/core';
3
- import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
3
+ import { QueueJobService } from '@hed-hog/queue';
4
+ import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
4
5
  import { InstructorService } from '../instructor/instructor.service';
5
6
  import { CourseOperationsIntegrationService } from './course-operations-integration.service';
6
7
  import { CreateCourseLessonFrameDto } from './dto/create-course-lesson-frame.dto';
@@ -33,11 +34,14 @@ type LessonFileType =
33
34
 
34
35
  @Injectable()
35
36
  export class CourseStructureService {
37
+ private readonly logger = new Logger(CourseStructureService.name);
38
+
36
39
  constructor(
37
40
  private readonly prisma: PrismaService,
38
41
  private readonly fileService: FileService,
39
42
  private readonly instructorService: InstructorService,
40
43
  private readonly operationsIntegration: CourseOperationsIntegrationService,
44
+ private readonly queueJob: QueueJobService,
41
45
  ) {}
42
46
 
43
47
  async getStructure(courseId: number) {
@@ -1151,6 +1155,175 @@ export class CourseStructureService {
1151
1155
  });
1152
1156
  }
1153
1157
 
1158
+ async startTranscription(lessonId: number) {
1159
+ const lesson = await (this.prisma as any).course_lesson.findUnique({
1160
+ where: { id: lessonId },
1161
+ select: { id: true, course_module: { select: { course_id: true } } },
1162
+ });
1163
+
1164
+ if (!lesson) {
1165
+ throw new NotFoundException(`Lesson ${lessonId} not found`);
1166
+ }
1167
+
1168
+ const audioFiles = await this.getAudioFiles(lessonId);
1169
+
1170
+ if (!audioFiles.length) {
1171
+ throw new BadRequestException('No audio file found for this lesson. Upload or convert a video first.');
1172
+ }
1173
+
1174
+ const audioFileId = audioFiles[audioFiles.length - 1].file_id;
1175
+
1176
+ const job = await this.queueJob.enqueue({
1177
+ type: 'lms.audio.transcribe',
1178
+ queueName: 'lms.audio.transcribe',
1179
+ payload: {
1180
+ courseId: lesson.course_module.course_id,
1181
+ lessonId,
1182
+ audioFileId,
1183
+ },
1184
+ sourceModule: 'lms',
1185
+ sourceEntity: 'course_lesson',
1186
+ sourceEntityId: String(lessonId),
1187
+ maxAttempts: 3,
1188
+ });
1189
+
1190
+ const startedOnDemand = await this.startTranscriptionOnDemandIfNeeded(job.id);
1191
+
1192
+ if (startedOnDemand) {
1193
+ return { queueJobId: job.id, status: 'processing' };
1194
+ }
1195
+
1196
+ return { queueJobId: job.id, status: job.status };
1197
+ }
1198
+
1199
+ private async startTranscriptionOnDemandIfNeeded(jobId: number): Promise<boolean> {
1200
+ const freshWorkerCutoff = new Date(Date.now() - 2 * 60 * 1000);
1201
+
1202
+ const onlineWorkers = await (this.prisma as any).queue_worker_heartbeat.count({
1203
+ where: {
1204
+ status: 'online',
1205
+ last_heartbeat_at: { gte: freshWorkerCutoff },
1206
+ },
1207
+ });
1208
+
1209
+ if (onlineWorkers > 0) {
1210
+ return false;
1211
+ }
1212
+
1213
+ void this.queueJob
1214
+ .execute(jobId, undefined, 15 * 60 * 1000)
1215
+ .then(() => {
1216
+ this.logger.debug(
1217
+ `Started on-demand transcription execution for queue job ${jobId} because no online workers were found.`,
1218
+ );
1219
+ })
1220
+ .catch((error: unknown) => {
1221
+ this.logger.warn(
1222
+ `Failed to execute transcription job ${jobId} on-demand fallback: ${error instanceof Error ? error.message : String(error)}`,
1223
+ );
1224
+ });
1225
+
1226
+ return true;
1227
+ }
1228
+
1229
+ async bulkEnqueueJobs(
1230
+ courseId: number,
1231
+ jobType: 'transcription' | 'xp_recalculation',
1232
+ userId: number,
1233
+ ): Promise<{
1234
+ queued: number;
1235
+ skipped: number;
1236
+ skippedWithoutTranscription: number;
1237
+ warnings: string[];
1238
+ }> {
1239
+ const lessons = await (this.prisma as any).course_lesson.findMany({
1240
+ where: {
1241
+ course_module: { course_id: courseId },
1242
+ },
1243
+ select: { id: true },
1244
+ });
1245
+
1246
+ let queued = 0;
1247
+ let skipped = 0;
1248
+ let skippedWithoutTranscription = 0;
1249
+ const warnings: string[] = [];
1250
+
1251
+ if (jobType === 'transcription') {
1252
+ for (const lesson of lessons) {
1253
+ const audioFiles = await (this.prisma as any).course_lesson_file.findMany({
1254
+ where: { course_lesson_id: lesson.id, type: 'lesson_audio' },
1255
+ select: { file_id: true },
1256
+ orderBy: { created_at: 'asc' },
1257
+ });
1258
+
1259
+ if (!audioFiles.length) {
1260
+ skipped++;
1261
+ continue;
1262
+ }
1263
+
1264
+ const audioFileId = audioFiles[audioFiles.length - 1].file_id;
1265
+ await this.queueJob.enqueue({
1266
+ type: 'lms.audio.transcribe',
1267
+ queueName: 'lms.audio.transcribe',
1268
+ payload: { courseId, lessonId: lesson.id, audioFileId },
1269
+ sourceModule: 'lms',
1270
+ sourceEntity: 'course_lesson',
1271
+ sourceEntityId: String(lesson.id),
1272
+ maxAttempts: 3,
1273
+ });
1274
+ queued++;
1275
+ }
1276
+ } else {
1277
+ for (const lesson of lessons) {
1278
+ const segments = await (this.prisma as any).course_lesson_transcription_segment.findFirst({
1279
+ where: { course_lesson_id: lesson.id },
1280
+ select: { id: true },
1281
+ });
1282
+
1283
+ if (!segments) {
1284
+ skippedWithoutTranscription++;
1285
+ skipped++;
1286
+ continue;
1287
+ }
1288
+
1289
+ let xpMap = await (this.prisma as any).lesson_xp_map.findFirst({
1290
+ where: { course_lesson_id: lesson.id },
1291
+ select: { id: true },
1292
+ });
1293
+
1294
+ if (!xpMap) {
1295
+ const created = await (this.prisma as any).lesson_xp_map.create({
1296
+ data: {
1297
+ course_lesson_id: lesson.id,
1298
+ status: 'pending',
1299
+ },
1300
+ select: { id: true },
1301
+ });
1302
+ xpMap = created;
1303
+ }
1304
+
1305
+ await this.queueJob.enqueue({
1306
+ type: 'lms.lesson.xp.calculate',
1307
+ queueName: 'lms.lesson.xp.calculate',
1308
+ payload: { lessonId: lesson.id, mapId: xpMap.id, userId },
1309
+ sourceModule: 'lms',
1310
+ sourceEntity: 'course_lesson',
1311
+ sourceEntityId: String(lesson.id),
1312
+ maxAttempts: 3,
1313
+ });
1314
+ queued++;
1315
+ }
1316
+
1317
+ if (skippedWithoutTranscription > 0) {
1318
+ warnings.push(
1319
+ `${skippedWithoutTranscription} aula(s) sem transcrição foram ignoradas. Gere a transcrição antes de recalcular o XP.`,
1320
+ );
1321
+ }
1322
+ }
1323
+
1324
+ return { queued, skipped, skippedWithoutTranscription, warnings };
1325
+ }
1326
+
1154
1327
  private async syncLessonRelations(
1155
1328
  lessonId: number,
1156
1329
  dto: Partial<
@@ -13,7 +13,7 @@ import {
13
13
  import { execFile } from 'child_process';
14
14
  import { existsSync, promises as fs, readdirSync } from 'fs';
15
15
  import { tmpdir } from 'os';
16
- import { basename, join } from 'path';
16
+ import { basename, extname, join } from 'path';
17
17
  import { promisify } from 'util';
18
18
 
19
19
  export const LMS_VIDEO_CONVERSION_JOB = 'lms.video.convert';
@@ -32,6 +32,20 @@ type LessonFrameResult = {
32
32
 
33
33
  const execFileAsync = promisify(execFile);
34
34
 
35
+ const VIDEO_FILE_EXTENSIONS = new Set([
36
+ '.mp4',
37
+ '.m4v',
38
+ '.mov',
39
+ '.mkv',
40
+ '.webm',
41
+ '.avi',
42
+ '.wmv',
43
+ '.flv',
44
+ '.ogv',
45
+ '.m2ts',
46
+ '.ts',
47
+ ]);
48
+
35
49
  type NotificationContext = {
36
50
  userId: number;
37
51
  notificationId: number;
@@ -187,7 +201,7 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
187
201
  }
188
202
 
189
203
  const mimetype = original.file_mimetype?.name ?? '';
190
- if (!mimetype.startsWith('video/')) {
204
+ if (!this.isVideoOriginalFile(mimetype, original.filename, original.path)) {
191
205
  throw new BadRequestException('Original file must be a video');
192
206
  }
193
207
 
@@ -257,7 +271,14 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
257
271
 
258
272
  async handle(job: {
259
273
  id: number;
274
+ type: string;
275
+ queue_name: string;
260
276
  payload: Record<string, any>;
277
+ attempts: number;
278
+ max_attempts: number;
279
+ source_module?: string | null;
280
+ source_entity?: string | null;
281
+ source_entity_id?: string | null;
261
282
  }): Promise<any> {
262
283
  const { courseId, sessionId, lessonId, originalFileId, profiles, overwrite } =
263
284
  job.payload as {
@@ -299,6 +320,14 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
299
320
  throw new Error('Course has no active profiles to process');
300
321
  }
301
322
 
323
+ const workerConcurrency =
324
+ parseInt(process.env.QUEUE_WORKER_CONCURRENCY ?? '1', 10) || 1;
325
+ const queueDef = await (this.prisma as any).queue_definition.findUnique({
326
+ where: { name: LMS_VIDEO_CONVERSION_JOB },
327
+ select: { max_concurrency: true },
328
+ });
329
+ const queueMaxConcurrency: number | null = queueDef?.max_concurrency ?? null;
330
+
302
331
  const maxInputBytes = this.getPositiveIntegerEnv('LMS_VIDEO_MAX_INPUT_BYTES');
303
332
  const ffmpegTimeoutMs =
304
333
  this.getPositiveIntegerEnv('LMS_VIDEO_FFMPEG_TIMEOUT_MS') ?? 1000 * 60 * 60;
@@ -333,12 +362,17 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
333
362
  maxInputBytes ? { maxBytes: maxInputBytes } : undefined,
334
363
  );
335
364
 
365
+ const inputSizeBytes = downloadedOriginal.size;
366
+ const inputSizeMb = parseFloat((inputSizeBytes / (1024 * 1024)).toFixed(2));
367
+
336
368
  this.logger.log(
337
- `Queue job ${job.id}: original file downloaded (lesson=${lessonId}, size=${downloadedOriginal.size})`,
369
+ `Queue job ${job.id}: original file downloaded (lesson=${lessonId}, size=${inputSizeBytes})`,
338
370
  );
339
- await emitProgress('Vídeo original baixado. Lendo duração...', {
371
+ await emitProgress(`Vídeo original baixado (${inputSizeMb} MB). Lendo duração...`, {
340
372
  phase: 'probe_duration',
341
373
  lessonId,
374
+ inputSizeBytes,
375
+ inputSizeMb,
342
376
  });
343
377
 
344
378
  let probedDuration: number | null = null;
@@ -368,10 +402,65 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
368
402
  uploadMBps: number;
369
403
  totalMs: number;
370
404
  }> = [];
405
+
406
+ const buildMetrics = () => ({
407
+ totalMs: Date.now() - jobStartedAt,
408
+ totalOutputBytes: results.reduce((acc, item) => acc + item.outputBytes, 0),
409
+ inputSizeBytes,
410
+ inputSizeMb,
411
+ averageProfileMs:
412
+ results.length > 0
413
+ ? Math.round(results.reduce((acc, item) => acc + item.totalMs, 0) / results.length)
414
+ : 0,
415
+ averageConversionMBps:
416
+ results.length > 0
417
+ ? Number(
418
+ (results.reduce((acc, item) => acc + item.conversionMBps, 0) / results.length).toFixed(2),
419
+ )
420
+ : 0,
421
+ averageUploadMBps:
422
+ results.length > 0
423
+ ? Number(
424
+ (results.reduce((acc, item) => acc + item.uploadMBps, 0) / results.length).toFixed(2),
425
+ )
426
+ : 0,
427
+ workerConcurrency,
428
+ queueMaxConcurrency,
429
+ });
371
430
  let frameResults: LessonFrameResult[] = [];
372
431
 
432
+ // On retries (attempts > 1), skip profiles that were already successfully
433
+ // converted and uploaded in a previous attempt of this same job.
434
+ const alreadyDoneProfileIds = new Set<number>();
435
+ if (job.attempts > 1) {
436
+ const existingFiles = await (this.prisma as any).course_lesson_file.findMany({
437
+ where: {
438
+ course_lesson_id: lessonId,
439
+ type: { startsWith: 'video_profile:' },
440
+ },
441
+ select: { type: true },
442
+ });
443
+ for (const f of existingFiles as Array<{ type: string }>) {
444
+ const profileId = parseInt(f.type.split(':')[1], 10);
445
+ if (!isNaN(profileId)) alreadyDoneProfileIds.add(profileId);
446
+ }
447
+ if (alreadyDoneProfileIds.size > 0) {
448
+ this.logger.log(
449
+ `Queue job ${job.id}: retry attempt ${job.attempts}, skipping already-converted profiles: [${[...alreadyDoneProfileIds].join(', ')}]`,
450
+ );
451
+ }
452
+ }
453
+
373
454
  try {
374
455
  for (const profile of profilesToProcess) {
456
+ if (alreadyDoneProfileIds.has(profile.id)) {
457
+ await emitProgress(
458
+ `Perfil ${profile.name || profile.id} já convertido em tentativa anterior, pulando.`,
459
+ { phase: 'skip_profile', profileId: profile.id, profileName: profile.name },
460
+ );
461
+ continue;
462
+ }
463
+
375
464
  const profileStartedAt = Date.now();
376
465
  const outputPath = join(
377
466
  workDir,
@@ -381,7 +470,7 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
381
470
  `Queue job ${job.id}: converting profile ${profile.id} for lesson ${lessonId}`,
382
471
  );
383
472
  await emitProgress(
384
- `Convertendo vídeo para o perfil ${profile.name || profile.id}...`,
473
+ `Convertendo vídeo para o perfil ${profile.name || profile.id} (entrada: ${inputSizeMb} MB)...`,
385
474
  {
386
475
  phase: 'convert_profile',
387
476
  lessonId,
@@ -389,6 +478,7 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
389
478
  profileName: profile.name,
390
479
  profileIndex: results.length + 1,
391
480
  profileCount: profilesToProcess.length,
481
+ inputSizeMb,
392
482
  },
393
483
  );
394
484
  this.logger.debug(
@@ -529,28 +619,7 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
529
619
  extractedFrames: frameResults.length,
530
620
  frames: frameResults,
531
621
  transcriptionJobId: transcriptionJob.id,
532
- metrics: {
533
- totalMs: Date.now() - jobStartedAt,
534
- totalOutputBytes: results.reduce((acc, item) => acc + item.outputBytes, 0),
535
- averageProfileMs:
536
- results.length > 0
537
- ? Math.round(results.reduce((acc, item) => acc + item.totalMs, 0) / results.length)
538
- : 0,
539
- averageConversionMBps:
540
- results.length > 0
541
- ? Number(
542
- (
543
- results.reduce((acc, item) => acc + item.conversionMBps, 0) / results.length
544
- ).toFixed(2),
545
- )
546
- : 0,
547
- averageUploadMBps:
548
- results.length > 0
549
- ? Number(
550
- (results.reduce((acc, item) => acc + item.uploadMBps, 0) / results.length).toFixed(2),
551
- )
552
- : 0,
553
- },
622
+ metrics: buildMetrics(),
554
623
  };
555
624
  } else {
556
625
  let transcriptionSkipReason = 'audio extraction returned no file id';
@@ -617,28 +686,7 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
617
686
  extractedFrames: frameResults.length,
618
687
  frames: frameResults,
619
688
  transcriptionJobId: transcriptionJob.id,
620
- metrics: {
621
- totalMs: Date.now() - jobStartedAt,
622
- totalOutputBytes: results.reduce((acc, item) => acc + item.outputBytes, 0),
623
- averageProfileMs:
624
- results.length > 0
625
- ? Math.round(results.reduce((acc, item) => acc + item.totalMs, 0) / results.length)
626
- : 0,
627
- averageConversionMBps:
628
- results.length > 0
629
- ? Number(
630
- (
631
- results.reduce((acc, item) => acc + item.conversionMBps, 0) / results.length
632
- ).toFixed(2),
633
- )
634
- : 0,
635
- averageUploadMBps:
636
- results.length > 0
637
- ? Number(
638
- (results.reduce((acc, item) => acc + item.uploadMBps, 0) / results.length).toFixed(2),
639
- )
640
- : 0,
641
- },
689
+ metrics: buildMetrics(),
642
690
  };
643
691
  }
644
692
  } catch (fallbackError) {
@@ -693,11 +741,8 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
693
741
  this.logger.log(`Queue job ${job.id}: cleaned temporary folder ${workDir}`);
694
742
  }
695
743
 
696
- const totalMs = Date.now() - jobStartedAt;
697
- const totalOutputBytes = results.reduce((acc, item) => acc + item.outputBytes, 0);
698
-
699
744
  this.logger.log(
700
- `Queue job ${job.id}: LMS conversion finished (lesson=${lessonId}, profiles=${results.length}, totalMs=${totalMs}, totalOutputBytes=${totalOutputBytes})`,
745
+ `Queue job ${job.id}: LMS conversion finished (lesson=${lessonId}, profiles=${results.length})`,
701
746
  );
702
747
 
703
748
  if (notificationContext) {
@@ -717,28 +762,7 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
717
762
  extractedFrames: frameResults.length,
718
763
  frames: frameResults,
719
764
  transcriptionJobId: null,
720
- metrics: {
721
- totalMs,
722
- totalOutputBytes,
723
- averageProfileMs:
724
- results.length > 0
725
- ? Math.round(results.reduce((acc, item) => acc + item.totalMs, 0) / results.length)
726
- : 0,
727
- averageConversionMBps:
728
- results.length > 0
729
- ? Number(
730
- (
731
- results.reduce((acc, item) => acc + item.conversionMBps, 0) / results.length
732
- ).toFixed(2),
733
- )
734
- : 0,
735
- averageUploadMBps:
736
- results.length > 0
737
- ? Number(
738
- (results.reduce((acc, item) => acc + item.uploadMBps, 0) / results.length).toFixed(2),
739
- )
740
- : 0,
741
- },
765
+ metrics: buildMetrics(),
742
766
  };
743
767
  }
744
768
 
@@ -1378,4 +1402,18 @@ export class CourseVideoConversionService implements OnModuleInit, IJobHandler {
1378
1402
  return null;
1379
1403
  }
1380
1404
  }
1405
+
1406
+ private isVideoOriginalFile(
1407
+ mimetype?: string | null,
1408
+ filename?: string | null,
1409
+ path?: string | null,
1410
+ ): boolean {
1411
+ if (typeof mimetype === 'string' && mimetype.startsWith('video/')) {
1412
+ return true;
1413
+ }
1414
+
1415
+ const candidateName = filename ?? path ?? '';
1416
+ const extension = extname(candidateName).toLowerCase();
1417
+ return VIDEO_FILE_EXTENSIONS.has(extension);
1418
+ }
1381
1419
  }
@@ -1,5 +1,5 @@
1
1
 
2
- import { Role } from '@hed-hog/api';
2
+ import { Role, User } from '@hed-hog/api';
3
3
  import {
4
4
  Body,
5
5
  Controller,
@@ -15,6 +15,7 @@ import {
15
15
  } from '@nestjs/common';
16
16
  import { CourseService } from './course.service';
17
17
  import { CreateCourseDto } from './dto/create-course.dto';
18
+ import { CleanupCourseStorageDto } from './dto/cleanup-course-storage.dto';
18
19
  import { UpdateCourseDto } from './dto/update-course.dto';
19
20
 
20
21
  @Role()
@@ -64,6 +65,24 @@ export class CourseController {
64
65
  return this.courseService.stats();
65
66
  }
66
67
 
68
+ @Get(':id/overview')
69
+ getCourseContentOverview(@Param('id', ParseIntPipe) id: number) {
70
+ return this.courseService.getCourseContentOverview(id);
71
+ }
72
+
73
+ @Post(':id/storage/cleanup')
74
+ cleanupCourseStorageCategory(
75
+ @Param('id', ParseIntPipe) id: number,
76
+ @Body() body: CleanupCourseStorageDto,
77
+ @User() user: any,
78
+ ) {
79
+ return this.courseService.enqueueStorageCategoryCleanup(
80
+ id,
81
+ body.category,
82
+ Number(user?.id || 0) || null,
83
+ );
84
+ }
85
+
67
86
  @Get(':id')
68
87
  getById(@Param('id', ParseIntPipe) id: number) {
69
88
  return this.courseService.getById(id);
@@ -83,8 +102,8 @@ export class CourseController {
83
102
  }
84
103
 
85
104
  @Delete(':id')
86
- remove(@Param('id', ParseIntPipe) id: number) {
87
- return this.courseService.remove(id);
105
+ remove(@Param('id', ParseIntPipe) id: number, @User() user: any) {
106
+ return this.courseService.remove(id, Number(user?.id || 0) || null);
88
107
  }
89
108
 
90
109
  @Get(':courseId/video-resolution-profiles')
@@ -4,16 +4,19 @@ import { QueueModule } from '@hed-hog/queue';
4
4
  import { forwardRef, Module } from '@nestjs/common';
5
5
  import { InstructorModule } from '../instructor/instructor.module';
6
6
  import { CourseLessonController } from './course-lesson.controller';
7
- import { CourseOperationsController } from './course-operations.controller';
8
7
  import { CourseOperationsIntegrationService } from './course-operations-integration.service';
8
+ import { CourseOperationsController } from './course-operations.controller';
9
9
  import { CourseStructureController } from './course-structure.controller';
10
- import { LmsSettingController } from './lms-setting.controller';
11
10
  import { CourseStructureService } from './course-structure.service';
12
11
  import { CourseVideoConversionService } from './course-video-conversion.service';
13
12
  import { CourseController } from './course.controller';
14
13
  import { LmsCoursesMcpTools } from './course.mcp-tools';
14
+ import { LmsBulkUploadInfraService } from './lms-bulk-upload-infra.service';
15
15
  import { CourseService } from './course.service';
16
+ import { LmsBulkUploadController } from './lms-bulk-upload.controller';
17
+ import { LmsBulkUploadService } from './lms-bulk-upload.service';
16
18
  import { LmsOperationsTaskSubscriber } from './lms-operations-task.subscriber';
19
+ import { LmsSettingController } from './lms-setting.controller';
17
20
 
18
21
  @Module({
19
22
  imports: [
@@ -22,12 +25,21 @@ import { LmsOperationsTaskSubscriber } from './lms-operations-task.subscriber';
22
25
  forwardRef(() => CoreModule),
23
26
  forwardRef(() => QueueModule),
24
27
  ],
25
- controllers: [CourseController, CourseStructureController, CourseLessonController, LmsSettingController, CourseOperationsController],
28
+ controllers: [
29
+ CourseController,
30
+ CourseStructureController,
31
+ CourseLessonController,
32
+ LmsSettingController,
33
+ CourseOperationsController,
34
+ LmsBulkUploadController,
35
+ ],
26
36
  providers: [
27
37
  CourseOperationsIntegrationService,
28
38
  CourseService,
29
39
  CourseStructureService,
30
40
  CourseVideoConversionService,
41
+ LmsBulkUploadInfraService,
42
+ LmsBulkUploadService,
31
43
  LmsCoursesMcpTools,
32
44
  LmsOperationsTaskSubscriber,
33
45
  ],