@hed-hog/lms 0.0.361 → 0.0.365
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.
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +66 -0
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
- package/dist/bitcode-wallet/bitcode-wallet.service.js +91 -0
- package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts +8 -0
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts.map +1 -0
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js +40 -0
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js.map +1 -0
- package/dist/class-group/class-group.controller.d.ts +16 -16
- package/dist/class-group/class-group.service.d.ts +12 -12
- package/dist/course/course-audio-transcription.service.d.ts +3 -2
- package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
- package/dist/course/course-audio-transcription.service.js +49 -8
- package/dist/course/course-audio-transcription.service.js.map +1 -1
- package/dist/course/course-export-scorm12-worker.service.d.ts +21 -0
- package/dist/course/course-export-scorm12-worker.service.d.ts.map +1 -0
- package/dist/course/course-export-scorm12-worker.service.js +109 -0
- package/dist/course/course-export-scorm12-worker.service.js.map +1 -0
- package/dist/course/course-export-scorm12.service.d.ts +42 -0
- package/dist/course/course-export-scorm12.service.d.ts.map +1 -0
- package/dist/course/course-export-scorm12.service.js +628 -0
- package/dist/course/course-export-scorm12.service.js.map +1 -0
- package/dist/course/course-export.service.d.ts +84 -0
- package/dist/course/course-export.service.d.ts.map +1 -0
- package/dist/course/course-export.service.js +237 -0
- package/dist/course/course-export.service.js.map +1 -0
- package/dist/course/course-lesson.controller.d.ts +4 -0
- package/dist/course/course-lesson.controller.d.ts.map +1 -1
- package/dist/course/course-lesson.controller.js +10 -0
- package/dist/course/course-lesson.controller.js.map +1 -1
- package/dist/course/course-structure.controller.d.ts +24 -9
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +30 -3
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +25 -3
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +234 -24
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-conversion.service.d.ts +8 -0
- package/dist/course/course-video-conversion.service.d.ts.map +1 -1
- package/dist/course/course-video-conversion.service.js +87 -51
- package/dist/course/course-video-conversion.service.js.map +1 -1
- package/dist/course/course-video-hls.service.d.ts +57 -0
- package/dist/course/course-video-hls.service.d.ts.map +1 -0
- package/dist/course/course-video-hls.service.js +767 -0
- package/dist/course/course-video-hls.service.js.map +1 -0
- package/dist/course/course.controller.d.ts +115 -11
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +66 -28
- package/dist/course/course.controller.js.map +1 -1
- package/dist/course/course.mcp-tools.js +1 -1
- package/dist/course/course.mcp-tools.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +13 -0
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +112 -11
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +682 -72
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.d.ts +6 -0
- package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -0
- package/dist/course/dto/cleanup-course-storage.dto.js +34 -0
- package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -0
- package/dist/course/dto/cleanup-upload-history.dto.d.ts +9 -0
- package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -0
- package/dist/course/dto/cleanup-upload-history.dto.js +36 -0
- package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -0
- package/dist/course/dto/create-course-bulk-job.dto.d.ts +5 -0
- package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -0
- package/dist/course/dto/create-course-bulk-job.dto.js +26 -0
- package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -0
- package/dist/course/dto/create-course-export.dto.d.ts +14 -0
- package/dist/course/dto/create-course-export.dto.d.ts.map +1 -0
- package/dist/course/dto/create-course-export.dto.js +71 -0
- package/dist/course/dto/create-course-export.dto.js.map +1 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +2 -2
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +3 -2
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/lms-bulk-upload-automation.service.d.ts +54 -0
- package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -0
- package/dist/course/lms-bulk-upload-automation.service.js +537 -0
- package/dist/course/lms-bulk-upload-automation.service.js.map +1 -0
- package/dist/course/lms-bulk-upload-infra.service.d.ts +32 -0
- package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -0
- package/dist/course/lms-bulk-upload-infra.service.js +301 -0
- package/dist/course/lms-bulk-upload-infra.service.js.map +1 -0
- package/dist/course/lms-bulk-upload.constants.d.ts +4 -0
- package/dist/course/lms-bulk-upload.constants.d.ts.map +1 -0
- package/dist/course/lms-bulk-upload.constants.js +7 -0
- package/dist/course/lms-bulk-upload.constants.js.map +1 -0
- package/dist/course/lms-bulk-upload.controller.d.ts +144 -1
- package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.controller.js +114 -4
- package/dist/course/lms-bulk-upload.controller.js.map +1 -1
- package/dist/course/lms-bulk-upload.service.d.ts +153 -3
- package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.service.js +659 -21
- package/dist/course/lms-bulk-upload.service.js.map +1 -1
- package/dist/course/lms-setting.controller.d.ts +6 -2
- package/dist/course/lms-setting.controller.d.ts.map +1 -1
- package/dist/course/lms-setting.controller.js +25 -8
- package/dist/course/lms-setting.controller.js.map +1 -1
- package/dist/course/scorm12-schemas.d.ts +4 -0
- package/dist/course/scorm12-schemas.d.ts.map +1 -0
- package/dist/course/scorm12-schemas.js +9 -0
- package/dist/course/scorm12-schemas.js.map +1 -0
- package/dist/enterprise/enterprise.controller.d.ts +20 -20
- package/dist/enterprise/enterprise.service.d.ts +20 -20
- package/dist/enterprise/training/training-admin.controller.d.ts +11 -11
- package/dist/enterprise/training/training-admin.service.d.ts +11 -11
- package/dist/enterprise/training/training-instructor.controller.d.ts +2 -2
- package/dist/enterprise/training/training-instructor.service.d.ts +2 -2
- package/dist/enterprise/training/training-student.controller.d.ts +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +52 -1
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +217 -4
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/enterprise/training/training-viewer.controller.d.ts +2 -2
- package/dist/evaluation/evaluation.controller.d.ts +8 -8
- package/dist/evaluation/evaluation.service.d.ts +26 -8
- package/dist/evaluation/evaluation.service.d.ts.map +1 -1
- package/dist/evaluation/evaluation.service.js +125 -0
- package/dist/evaluation/evaluation.service.js.map +1 -1
- package/dist/exam/dto/create-standalone-question.dto.d.ts +12 -0
- package/dist/exam/dto/create-standalone-question.dto.d.ts.map +1 -0
- package/dist/exam/dto/create-standalone-question.dto.js +70 -0
- package/dist/exam/dto/create-standalone-question.dto.js.map +1 -0
- package/dist/exam/exam.module.d.ts.map +1 -1
- package/dist/exam/exam.module.js +2 -1
- package/dist/exam/exam.module.js.map +1 -1
- package/dist/exam/exam.service.d.ts +21 -0
- package/dist/exam/exam.service.d.ts.map +1 -1
- package/dist/exam/exam.service.js +80 -0
- package/dist/exam/exam.service.js.map +1 -1
- package/dist/exam/question.controller.d.ts +27 -0
- package/dist/exam/question.controller.d.ts.map +1 -0
- package/dist/exam/question.controller.js +53 -0
- package/dist/exam/question.controller.js.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts +6 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js +34 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts +28 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js +123 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts +4 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js +22 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts +10 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js +52 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts +15 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js +86 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +30 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +440 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts +87 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.js +185 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.d.ts +3 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.js +34 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.d.ts +84 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.js +353 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts +10 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.js +63 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts +27 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.js +194 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.js.map +1 -0
- package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -0
- package/dist/lms-commerce-access.subscriber.d.ts +11 -0
- package/dist/lms-commerce-access.subscriber.d.ts.map +1 -0
- package/dist/lms-commerce-access.subscriber.js +74 -0
- package/dist/lms-commerce-access.subscriber.js.map +1 -0
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +21 -5
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/dto/update-profile.dto.d.ts +17 -0
- package/dist/platforma/dto/update-profile.dto.d.ts.map +1 -0
- package/dist/platforma/dto/update-profile.dto.js +87 -0
- package/dist/platforma/dto/update-profile.dto.js.map +1 -0
- package/dist/platforma/platforma-video.service.d.ts +39 -0
- package/dist/platforma/platforma-video.service.d.ts.map +1 -0
- package/dist/platforma/platforma-video.service.js +301 -0
- package/dist/platforma/platforma-video.service.js.map +1 -0
- package/dist/platforma/platforma.controller.d.ts +182 -1
- package/dist/platforma/platforma.controller.d.ts.map +1 -1
- package/dist/platforma/platforma.controller.js +243 -2
- package/dist/platforma/platforma.controller.js.map +1 -1
- package/dist/platforma/platforma.service.d.ts +27 -0
- package/dist/platforma/platforma.service.d.ts.map +1 -0
- package/dist/platforma/platforma.service.js +274 -0
- package/dist/platforma/platforma.service.js.map +1 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts +5 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts.map +1 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.js +26 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.js.map +1 -0
- package/dist/student-xp/student-xp.controller.d.ts +56 -0
- package/dist/student-xp/student-xp.controller.d.ts.map +1 -0
- package/dist/student-xp/student-xp.controller.js +138 -0
- package/dist/student-xp/student-xp.controller.js.map +1 -0
- package/dist/student-xp/student-xp.module.d.ts +3 -0
- package/dist/student-xp/student-xp.module.d.ts.map +1 -0
- package/dist/student-xp/student-xp.module.js +25 -0
- package/dist/student-xp/student-xp.module.js.map +1 -0
- package/dist/student-xp/student-xp.service.d.ts +81 -0
- package/dist/student-xp/student-xp.service.d.ts.map +1 -0
- package/dist/student-xp/student-xp.service.js +247 -0
- package/dist/student-xp/student-xp.service.js.map +1 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.d.ts +12 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.js +63 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.js.map +1 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.js +57 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.js.map +1 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.js +57 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.js.map +1 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.d.ts +12 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.js +66 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.js.map +1 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.js +60 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.js.map +1 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.js +60 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.js.map +1 -0
- package/dist/xp-catalog/xp-area.controller.d.ts +25 -0
- package/dist/xp-catalog/xp-area.controller.d.ts.map +1 -0
- package/dist/xp-catalog/xp-area.controller.js +105 -0
- package/dist/xp-catalog/xp-area.controller.js.map +1 -0
- package/dist/xp-catalog/xp-area.service.d.ts +35 -0
- package/dist/xp-catalog/xp-area.service.d.ts.map +1 -0
- package/dist/xp-catalog/xp-area.service.js +168 -0
- package/dist/xp-catalog/xp-area.service.js.map +1 -0
- package/dist/xp-catalog/xp-catalog.module.d.ts +3 -0
- package/dist/xp-catalog/xp-catalog.module.d.ts.map +1 -0
- package/dist/xp-catalog/xp-catalog.module.js +29 -0
- package/dist/xp-catalog/xp-catalog.module.js.map +1 -0
- package/dist/xp-catalog/xp-learning-type.controller.d.ts +20 -0
- package/dist/xp-catalog/xp-learning-type.controller.d.ts.map +1 -0
- package/dist/xp-catalog/xp-learning-type.controller.js +96 -0
- package/dist/xp-catalog/xp-learning-type.controller.js.map +1 -0
- package/dist/xp-catalog/xp-learning-type.service.d.ts +30 -0
- package/dist/xp-catalog/xp-learning-type.service.d.ts.map +1 -0
- package/dist/xp-catalog/xp-learning-type.service.js +146 -0
- package/dist/xp-catalog/xp-learning-type.service.js.map +1 -0
- package/dist/xp-catalog/xp-skill.controller.d.ts +26 -0
- package/dist/xp-catalog/xp-skill.controller.d.ts.map +1 -0
- package/dist/xp-catalog/xp-skill.controller.js +113 -0
- package/dist/xp-catalog/xp-skill.controller.js.map +1 -0
- package/dist/xp-catalog/xp-skill.service.d.ts +37 -0
- package/dist/xp-catalog/xp-skill.service.d.ts.map +1 -0
- package/dist/xp-catalog/xp-skill.service.js +174 -0
- package/dist/xp-catalog/xp-skill.service.js.map +1 -0
- package/hedhog/data/evaluation_topic.yaml +17 -0
- package/hedhog/data/menu.yaml +91 -7
- package/hedhog/data/queue_definition.yaml +48 -0
- package/hedhog/data/route.yaml +511 -29
- package/hedhog/data/setting_group.yaml +20 -20
- package/hedhog/data/xp_area.yaml +164 -0
- package/hedhog/data/xp_learning_type.yaml +131 -0
- package/hedhog/data/xp_skill.yaml +1834 -0
- package/hedhog/frontend/app/achievements/page.tsx.ejs +108 -118
- package/hedhog/frontend/app/bitcodes/page.tsx.ejs +22 -34
- package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +1749 -0
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +21 -45
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +40 -74
- package/hedhog/frontend/app/classes/page.tsx.ejs +56 -85
- package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +3 -2
- package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +48 -5
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +73 -8
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +420 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +308 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +19 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +1172 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +16 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +2 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +623 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +1458 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +87 -46
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +8 -3
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +31 -8
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +16 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +618 -480
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +672 -737
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +101 -85
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +24 -10
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +7 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +44 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +6 -10
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +49 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -3
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +53 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +106 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +80 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-xp-overview.ts.ejs +76 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-xp-map.ts.ejs +128 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +0 -2
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +30 -0
- package/hedhog/frontend/app/courses/[id]/structure/_utils/xp-color-config.ts.ejs +115 -0
- package/hedhog/frontend/app/courses/_components/CourseDeleteDialog.tsx.ejs +223 -0
- package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +89 -0
- package/hedhog/frontend/app/courses/page.tsx.ejs +445 -230
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -63
- package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +53 -77
- package/hedhog/frontend/app/exams/page.tsx.ejs +54 -90
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +23 -36
- package/hedhog/frontend/app/instructors/page.tsx.ejs +72 -81
- package/hedhog/frontend/app/paths/page.tsx.ejs +40 -68
- package/hedhog/frontend/app/training/page.tsx.ejs +40 -68
- package/hedhog/frontend/app/xp/areas/page.tsx.ejs +782 -0
- package/hedhog/frontend/app/xp/learning-types/page.tsx.ejs +690 -0
- package/hedhog/frontend/app/xp/skills/page.tsx.ejs +811 -0
- package/hedhog/frontend/messages/en.json +412 -31
- package/hedhog/frontend/messages/pt.json +412 -31
- package/hedhog/table/course_export.yaml +62 -0
- package/hedhog/table/lesson_xp_map.yaml +50 -0
- package/hedhog/table/lesson_xp_segment.yaml +40 -0
- package/hedhog/table/lesson_xp_segment_area.yaml +24 -0
- package/hedhog/table/lesson_xp_segment_learning_type.yaml +24 -0
- package/hedhog/table/lesson_xp_segment_skill.yaml +24 -0
- package/hedhog/table/lms_bulk_upload_item.yaml +44 -0
- package/hedhog/table/lms_bulk_upload_session.yaml +42 -0
- package/hedhog/table/student_area_xp.yaml +30 -0
- package/hedhog/table/student_learning_type_xp.yaml +30 -0
- package/hedhog/table/student_skill_xp.yaml +30 -0
- package/hedhog/table/student_xp_event.yaml +34 -0
- package/hedhog/table/xp_area.yaml +39 -0
- package/hedhog/table/xp_learning_type.yaml +34 -0
- package/hedhog/table/xp_skill.yaml +39 -0
- package/package.json +13 -8
- package/src/bitcode-wallet/bitcode-wallet.service.ts +152 -0
- package/src/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.ts +32 -0
- package/src/course/course-audio-transcription.service.ts +58 -21
- package/src/course/course-export-scorm12-worker.service.ts +124 -0
- package/src/course/course-export-scorm12.service.ts +668 -0
- package/src/course/course-export.service.ts +280 -0
- package/src/course/course-lesson.controller.ts +6 -1
- package/src/course/course-structure.controller.ts +23 -1
- package/src/course/course-structure.service.ts +273 -7
- package/src/course/course-video-conversion.service.ts +113 -75
- package/src/course/course-video-hls.service.ts +946 -0
- package/src/course/course.controller.ts +54 -21
- package/src/course/course.mcp-tools.ts +1 -1
- package/src/course/course.module.ts +13 -0
- package/src/course/course.service.ts +906 -76
- package/src/course/dto/cleanup-course-storage.dto.ts +23 -0
- package/src/course/dto/cleanup-upload-history.dto.ts +26 -0
- package/src/course/dto/create-course-bulk-job.dto.ts +10 -0
- package/src/course/dto/create-course-export.dto.ts +56 -0
- package/src/course/dto/create-course-structure-lesson.dto.ts +4 -3
- package/src/course/lms-bulk-upload-automation.service.ts +707 -0
- package/src/course/lms-bulk-upload-infra.service.ts +360 -0
- package/src/course/lms-bulk-upload.constants.ts +5 -0
- package/src/course/lms-bulk-upload.controller.ts +110 -4
- package/src/course/lms-bulk-upload.service.ts +1092 -204
- package/src/course/lms-setting.controller.ts +26 -8
- package/src/course/scorm12-schemas.ts +9 -0
- package/src/enterprise/training/training-student.service.ts +221 -2
- package/src/evaluation/evaluation.service.ts +123 -0
- package/src/exam/dto/create-standalone-question.dto.ts +66 -0
- package/src/exam/exam.module.ts +2 -1
- package/src/exam/exam.service.ts +86 -0
- package/src/exam/question.controller.ts +28 -0
- package/src/lesson-xp-map/dto/create-lesson-xp-map.dto.ts +17 -0
- package/src/lesson-xp-map/dto/create-lesson-xp-segment.dto.ts +102 -0
- package/src/lesson-xp-map/dto/review-lesson-xp-map.dto.ts +7 -0
- package/src/lesson-xp-map/dto/update-lesson-xp-map.dto.ts +36 -0
- package/src/lesson-xp-map/dto/update-lesson-xp-segment.dto.ts +78 -0
- package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +570 -0
- package/src/lesson-xp-map/lesson-xp-map.controller.ts +116 -0
- package/src/lesson-xp-map/lesson-xp-map.module.ts +21 -0
- package/src/lesson-xp-map/lesson-xp-map.service.ts +442 -0
- package/src/lesson-xp-map/lesson-xp-segment.controller.ts +36 -0
- package/src/lesson-xp-map/lesson-xp-segment.service.ts +229 -0
- package/src/lms-commerce-access.subscriber.ts +88 -0
- package/src/lms.module.ts +21 -5
- package/src/platforma/dto/update-profile.dto.ts +59 -0
- package/src/platforma/platforma-video.service.ts +346 -0
- package/src/platforma/platforma.controller.ts +152 -3
- package/src/platforma/platforma.service.ts +268 -0
- package/src/student-xp/dto/grant-skill-card-xp.dto.ts +10 -0
- package/src/student-xp/student-xp.controller.ts +92 -0
- package/src/student-xp/student-xp.module.ts +12 -0
- package/src/student-xp/student-xp.service.ts +318 -0
- package/src/xp-catalog/dto/create-xp-area.dto.ts +40 -0
- package/src/xp-catalog/dto/create-xp-learning-type.dto.ts +35 -0
- package/src/xp-catalog/dto/create-xp-skill.dto.ts +35 -0
- package/src/xp-catalog/dto/update-xp-area.dto.ts +43 -0
- package/src/xp-catalog/dto/update-xp-learning-type.dto.ts +38 -0
- package/src/xp-catalog/dto/update-xp-skill.dto.ts +38 -0
- package/src/xp-catalog/xp-area.controller.ts +64 -0
- package/src/xp-catalog/xp-area.service.ts +196 -0
- package/src/xp-catalog/xp-catalog.module.ts +16 -0
- package/src/xp-catalog/xp-learning-type.controller.ts +59 -0
- package/src/xp-catalog/xp-learning-type.service.ts +170 -0
- package/src/xp-catalog/xp-skill.controller.ts +71 -0
- package/src/xp-catalog/xp-skill.service.ts +205 -0
- package/hedhog/data/video_resolution_profile.yaml +0 -7
- package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +0 -607
- package/hedhog/table/course_video_resolution_profile.yaml +0 -22
- package/hedhog/table/video_resolution_profile.yaml +0 -18
- package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +0 -16
- package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +0 -16
- package/src/video-resolution-profile/video-resolution-profile.controller.ts +0 -62
- package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +0 -128
- package/src/video-resolution-profile/video-resolution-profile.module.ts +0 -13
- package/src/video-resolution-profile/video-resolution-profile.service.ts +0 -117
|
@@ -22,18 +22,154 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
22
22
|
}
|
|
23
23
|
return t;
|
|
24
24
|
};
|
|
25
|
+
var CourseService_1;
|
|
25
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CourseService = void 0;
|
|
27
|
+
exports.CourseService = exports.LMS_COURSE_STORAGE_CLEANUP_JOB = exports.LMS_COURSE_DELETE_JOB = void 0;
|
|
27
28
|
const api_prisma_1 = require("@hed-hog/api-prisma");
|
|
28
29
|
const core_1 = require("@hed-hog/core");
|
|
30
|
+
const queue_1 = require("@hed-hog/queue");
|
|
29
31
|
const common_1 = require("@nestjs/common");
|
|
30
32
|
const course_operations_integration_service_1 = require("./course-operations-integration.service");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const cleanup_course_storage_dto_1 = require("./dto/cleanup-course-storage.dto");
|
|
34
|
+
exports.LMS_COURSE_DELETE_JOB = 'lms.course.delete';
|
|
35
|
+
exports.LMS_COURSE_STORAGE_CLEANUP_JOB = 'lms.course.storage.cleanup';
|
|
36
|
+
let CourseService = CourseService_1 = class CourseService {
|
|
37
|
+
constructor(prisma, fileService, integrationApi, queueJobService, queueRegistry, notificationService, operationsIntegration) {
|
|
33
38
|
this.prisma = prisma;
|
|
34
39
|
this.fileService = fileService;
|
|
35
40
|
this.integrationApi = integrationApi;
|
|
41
|
+
this.queueJobService = queueJobService;
|
|
42
|
+
this.queueRegistry = queueRegistry;
|
|
43
|
+
this.notificationService = notificationService;
|
|
36
44
|
this.operationsIntegration = operationsIntegration;
|
|
45
|
+
this.logger = new common_1.Logger(CourseService_1.name);
|
|
46
|
+
}
|
|
47
|
+
onModuleInit() {
|
|
48
|
+
this.queueRegistry.register(exports.LMS_COURSE_DELETE_JOB, this);
|
|
49
|
+
this.queueRegistry.register(exports.LMS_COURSE_STORAGE_CLEANUP_JOB, this);
|
|
50
|
+
this.logger.log(`Registered handler for "${exports.LMS_COURSE_DELETE_JOB}"`);
|
|
51
|
+
this.logger.log(`Registered handler for "${exports.LMS_COURSE_STORAGE_CLEANUP_JOB}"`);
|
|
52
|
+
}
|
|
53
|
+
async handle(job) {
|
|
54
|
+
if (job.type === exports.LMS_COURSE_STORAGE_CLEANUP_JOB) {
|
|
55
|
+
return this.handleStorageCleanupJob(job);
|
|
56
|
+
}
|
|
57
|
+
if (job.type === exports.LMS_COURSE_DELETE_JOB) {
|
|
58
|
+
return this.handleCourseDeleteJob(job);
|
|
59
|
+
}
|
|
60
|
+
throw new common_1.BadRequestException(`Unsupported job type: ${job.type}`);
|
|
61
|
+
}
|
|
62
|
+
async handleCourseDeleteJob(job) {
|
|
63
|
+
const { courseId, notificationId, notificationUserId } = job.payload;
|
|
64
|
+
if (!courseId) {
|
|
65
|
+
throw new common_1.BadRequestException('Missing courseId in job payload');
|
|
66
|
+
}
|
|
67
|
+
const notify = async (progress, body, success) => {
|
|
68
|
+
if (!notificationId || !notificationUserId)
|
|
69
|
+
return;
|
|
70
|
+
await this.notificationService
|
|
71
|
+
.updateProgress(notificationUserId, notificationId, Object.assign({ progress,
|
|
72
|
+
body }, (success !== undefined ? { success } : {})))
|
|
73
|
+
.catch(() => undefined);
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
await notify(5, 'Preparando exclusão completa do curso...');
|
|
77
|
+
const course = await this.prisma.course.findUnique({
|
|
78
|
+
where: { id: courseId },
|
|
79
|
+
select: { id: true, status: true, title: true, slug: true },
|
|
80
|
+
});
|
|
81
|
+
if (!course) {
|
|
82
|
+
throw new common_1.NotFoundException('Course not found');
|
|
83
|
+
}
|
|
84
|
+
if (course.status !== 'archived') {
|
|
85
|
+
throw new common_1.BadRequestException('ONLY_ARCHIVED_COURSE_CAN_BE_DELETED');
|
|
86
|
+
}
|
|
87
|
+
const impact = await this.getCourseDeletionImpact(courseId);
|
|
88
|
+
await notify(20, `Removendo ${impact.deletionImpact.fileCount} arquivo(s) relacionados ao curso...`);
|
|
89
|
+
if (impact.deletionImpact.fileIds.length > 0) {
|
|
90
|
+
await this.fileService
|
|
91
|
+
.delete('pt', { ids: impact.deletionImpact.fileIds })
|
|
92
|
+
.catch(() => undefined);
|
|
93
|
+
}
|
|
94
|
+
await notify(75, 'Removendo registros do curso e dependências...');
|
|
95
|
+
await this.prisma.course.delete({ where: { id: courseId } });
|
|
96
|
+
await notify(90, 'Finalizando exclusão e publicando evento...');
|
|
97
|
+
await this.integrationApi
|
|
98
|
+
.publishEvent({
|
|
99
|
+
eventName: 'lms.course.deleted',
|
|
100
|
+
sourceModule: 'lms',
|
|
101
|
+
aggregateType: 'course',
|
|
102
|
+
aggregateId: String(courseId),
|
|
103
|
+
payload: { id: courseId },
|
|
104
|
+
})
|
|
105
|
+
.catch(() => null);
|
|
106
|
+
await notify(100, 'Curso e todos os arquivos relacionados foram excluídos.', true);
|
|
107
|
+
return { success: true, courseId };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const message = error instanceof Error && error.message
|
|
111
|
+
? error.message
|
|
112
|
+
: 'Falha ao excluir o curso.';
|
|
113
|
+
await notify(100, message, false);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async handleStorageCleanupJob(job) {
|
|
118
|
+
const { courseId, category, notificationId, notificationUserId } = job.payload;
|
|
119
|
+
if (!courseId) {
|
|
120
|
+
throw new common_1.BadRequestException('Missing courseId in job payload');
|
|
121
|
+
}
|
|
122
|
+
if (!this.isSupportedStorageCleanupCategory(category)) {
|
|
123
|
+
throw new common_1.BadRequestException('Invalid storage category');
|
|
124
|
+
}
|
|
125
|
+
const notify = async (progress, body, success) => {
|
|
126
|
+
if (!notificationId || !notificationUserId)
|
|
127
|
+
return;
|
|
128
|
+
await this.notificationService
|
|
129
|
+
.updateProgress(notificationUserId, notificationId, Object.assign({ progress,
|
|
130
|
+
body }, (success !== undefined ? { success } : {})))
|
|
131
|
+
.catch(() => undefined);
|
|
132
|
+
};
|
|
133
|
+
try {
|
|
134
|
+
const categoryLabel = this.getStorageCategoryLabel(category);
|
|
135
|
+
await notify(5, `Preparando limpeza da categoria ${categoryLabel}...`);
|
|
136
|
+
const impact = await this.getCourseStorageCategoryImpact(courseId, category);
|
|
137
|
+
if (impact.fileCount === 0) {
|
|
138
|
+
await notify(100, `Nenhum arquivo encontrado em ${categoryLabel}.`, true);
|
|
139
|
+
return {
|
|
140
|
+
success: true,
|
|
141
|
+
status: 'empty',
|
|
142
|
+
courseId,
|
|
143
|
+
category,
|
|
144
|
+
cleanupImpact: impact,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
await notify(25, `Removendo vínculos da categoria ${categoryLabel} (${impact.fileCount} arquivo(s))...`);
|
|
148
|
+
await this.deleteCourseStorageCategoryLinks(courseId, category);
|
|
149
|
+
await notify(65, `Excluindo ${impact.fileCount} arquivo(s) da categoria ${categoryLabel}...`);
|
|
150
|
+
if (impact.fileIds.length > 0) {
|
|
151
|
+
await this.fileService.delete('pt', { ids: impact.fileIds }).catch(() => undefined);
|
|
152
|
+
}
|
|
153
|
+
await notify(100, `Categoria ${categoryLabel} limpa com sucesso (${this.formatBytes(impact.totalBytes)} liberados).`, true);
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
status: 'processed',
|
|
157
|
+
courseId,
|
|
158
|
+
category,
|
|
159
|
+
cleanupImpact: {
|
|
160
|
+
fileCount: impact.fileCount,
|
|
161
|
+
totalBytes: impact.totalBytes,
|
|
162
|
+
formattedSize: this.formatBytes(impact.totalBytes),
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const message = error instanceof Error && error.message
|
|
168
|
+
? error.message
|
|
169
|
+
: 'Falha ao limpar a categoria de armazenamento.';
|
|
170
|
+
await notify(100, message, false);
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
37
173
|
}
|
|
38
174
|
normalizeLevel(value) {
|
|
39
175
|
if (!value)
|
|
@@ -189,16 +325,17 @@ let CourseService = class CourseService {
|
|
|
189
325
|
this.prisma.course.count({ where }),
|
|
190
326
|
]);
|
|
191
327
|
const courseIds = courses.map((course) => course.id);
|
|
192
|
-
const [extrasById, projectLinksById] = await Promise.all([
|
|
328
|
+
const [extrasById, projectLinksById, coursesWithRunningJobs] = await Promise.all([
|
|
193
329
|
this.getCourseExtras(courseIds),
|
|
194
330
|
this.operationsIntegration.getCourseProjectLinks(courseIds),
|
|
331
|
+
this.getCoursesWithRunningJobs(courseIds),
|
|
195
332
|
]);
|
|
196
333
|
return {
|
|
197
334
|
total,
|
|
198
335
|
page,
|
|
199
336
|
pageSize,
|
|
200
337
|
lastPage: Math.max(1, Math.ceil(total / pageSize)),
|
|
201
|
-
data: courses.map((c) => this.mapCourse(c, undefined, extrasById.get(c.id), projectLinksById.get(c.id))),
|
|
338
|
+
data: courses.map((c) => this.mapCourse(c, undefined, extrasById.get(c.id), projectLinksById.get(c.id), undefined, coursesWithRunningJobs.has(c.id))),
|
|
202
339
|
};
|
|
203
340
|
}
|
|
204
341
|
async stats() {
|
|
@@ -216,7 +353,7 @@ let CourseService = class CourseService {
|
|
|
216
353
|
};
|
|
217
354
|
}
|
|
218
355
|
async getById(id) {
|
|
219
|
-
var _a;
|
|
356
|
+
var _a, _b;
|
|
220
357
|
const [c, lessonInstructors] = await Promise.all([
|
|
221
358
|
this.prisma.course.findUnique({
|
|
222
359
|
where: { id },
|
|
@@ -243,7 +380,16 @@ let CourseService = class CourseService {
|
|
|
243
380
|
},
|
|
244
381
|
},
|
|
245
382
|
file: {
|
|
246
|
-
select: {
|
|
383
|
+
select: {
|
|
384
|
+
id: true,
|
|
385
|
+
filename: true,
|
|
386
|
+
size: true,
|
|
387
|
+
file_mimetype: {
|
|
388
|
+
select: {
|
|
389
|
+
name: true,
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
},
|
|
247
393
|
},
|
|
248
394
|
},
|
|
249
395
|
orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
|
|
@@ -334,13 +480,21 @@ let CourseService = class CourseService {
|
|
|
334
480
|
progress: average,
|
|
335
481
|
};
|
|
336
482
|
});
|
|
337
|
-
|
|
483
|
+
const mapped = this.mapCourse(c, {
|
|
338
484
|
lessonCount,
|
|
339
485
|
sessionCount,
|
|
340
486
|
certificatesIssued,
|
|
341
487
|
averageCompletion: (_a = avgProgress._avg.progress_percent) !== null && _a !== void 0 ? _a : 0,
|
|
342
488
|
progressByModule,
|
|
343
489
|
}, extrasById.get(id), projectLinksById.get(id), lessonInstructors);
|
|
490
|
+
const deletionImpact = await this.getCourseDeletionImpact(id);
|
|
491
|
+
return Object.assign(Object.assign({}, mapped), { deletionImpact: {
|
|
492
|
+
fileCount: deletionImpact.deletionImpact.fileCount,
|
|
493
|
+
totalBytes: deletionImpact.deletionImpact.totalBytes,
|
|
494
|
+
formattedSize: this.formatBytes(deletionImpact.deletionImpact.totalBytes),
|
|
495
|
+
countsBySource: deletionImpact.deletionImpact.countsBySource,
|
|
496
|
+
enrollmentCount: (_b = mapped.enrollmentCount) !== null && _b !== void 0 ? _b : 0,
|
|
497
|
+
} });
|
|
344
498
|
}
|
|
345
499
|
async create(dto) {
|
|
346
500
|
var _a, _b, _c, _d, _e;
|
|
@@ -548,10 +702,11 @@ let CourseService = class CourseService {
|
|
|
548
702
|
}).catch(() => null);
|
|
549
703
|
return updateResult;
|
|
550
704
|
}
|
|
551
|
-
async remove(id) {
|
|
705
|
+
async remove(id, userId) {
|
|
706
|
+
var _a;
|
|
552
707
|
const course = await this.prisma.course.findUnique({
|
|
553
708
|
where: { id },
|
|
554
|
-
select: { id: true, status: true },
|
|
709
|
+
select: { id: true, status: true, title: true, slug: true },
|
|
555
710
|
});
|
|
556
711
|
if (!course) {
|
|
557
712
|
throw new common_1.BadRequestException('Course not found');
|
|
@@ -559,64 +714,394 @@ let CourseService = class CourseService {
|
|
|
559
714
|
if (course.status !== 'archived') {
|
|
560
715
|
throw new common_1.BadRequestException('ONLY_ARCHIVED_COURSE_CAN_BE_DELETED');
|
|
561
716
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
await this.fileService.delete('pt', { ids: fileIds }).catch(() => undefined);
|
|
717
|
+
if (!userId) {
|
|
718
|
+
throw new common_1.BadRequestException('Authenticated user is required');
|
|
565
719
|
}
|
|
566
|
-
await this.
|
|
567
|
-
await this.
|
|
568
|
-
|
|
720
|
+
const impact = await this.getCourseDeletionImpact(id);
|
|
721
|
+
const notification = await this.notificationService.create({
|
|
722
|
+
user_id: userId,
|
|
723
|
+
title: `Excluindo curso "${(_a = course.title) !== null && _a !== void 0 ? _a : course.slug}"`,
|
|
724
|
+
body: `Preparando remoção de ${impact.deletionImpact.fileCount} arquivo(s) (${this.formatBytes(impact.deletionImpact.totalBytes)}).`,
|
|
725
|
+
type: 'progress',
|
|
726
|
+
progress: 1,
|
|
727
|
+
started_at: new Date().toISOString(),
|
|
728
|
+
auto_remove: false,
|
|
729
|
+
action_type: 'url',
|
|
730
|
+
action_url: '/lms/courses',
|
|
731
|
+
action_data: {
|
|
732
|
+
source: 'lms-course-delete',
|
|
733
|
+
courseId: id,
|
|
734
|
+
},
|
|
735
|
+
});
|
|
736
|
+
const job = await this.queueJobService.enqueue({
|
|
737
|
+
type: exports.LMS_COURSE_DELETE_JOB,
|
|
738
|
+
queueName: exports.LMS_COURSE_DELETE_JOB,
|
|
739
|
+
payload: {
|
|
740
|
+
courseId: id,
|
|
741
|
+
notificationId: notification.id,
|
|
742
|
+
notificationUserId: userId,
|
|
743
|
+
},
|
|
569
744
|
sourceModule: 'lms',
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
return {
|
|
745
|
+
sourceEntity: 'course',
|
|
746
|
+
sourceEntityId: String(id),
|
|
747
|
+
maxAttempts: 3,
|
|
748
|
+
}, userId);
|
|
749
|
+
return {
|
|
750
|
+
success: true,
|
|
751
|
+
status: 'queued',
|
|
752
|
+
queueJobId: job.id,
|
|
753
|
+
notificationId: notification.id,
|
|
754
|
+
deletionImpact: {
|
|
755
|
+
fileCount: impact.deletionImpact.fileCount,
|
|
756
|
+
totalBytes: impact.deletionImpact.totalBytes,
|
|
757
|
+
formattedSize: this.formatBytes(impact.deletionImpact.totalBytes),
|
|
758
|
+
},
|
|
759
|
+
};
|
|
575
760
|
}
|
|
576
|
-
async
|
|
761
|
+
async enqueueStorageCategoryCleanup(courseId, category, userId) {
|
|
762
|
+
if (!userId) {
|
|
763
|
+
throw new common_1.BadRequestException('Authenticated user is required');
|
|
764
|
+
}
|
|
765
|
+
if (!this.isSupportedStorageCleanupCategory(category)) {
|
|
766
|
+
throw new common_1.BadRequestException('Invalid storage category');
|
|
767
|
+
}
|
|
768
|
+
const course = await this.prisma.course.findUnique({
|
|
769
|
+
where: { id: courseId },
|
|
770
|
+
select: { id: true, title: true, slug: true },
|
|
771
|
+
});
|
|
772
|
+
if (!course) {
|
|
773
|
+
throw new common_1.NotFoundException('Course not found');
|
|
774
|
+
}
|
|
775
|
+
const categoryLabel = this.getStorageCategoryLabel(category);
|
|
776
|
+
const impact = await this.getCourseStorageCategoryImpact(courseId, category);
|
|
777
|
+
if (impact.fileCount === 0) {
|
|
778
|
+
return {
|
|
779
|
+
success: true,
|
|
780
|
+
status: 'empty',
|
|
781
|
+
cleanupImpact: {
|
|
782
|
+
fileCount: 0,
|
|
783
|
+
totalBytes: 0,
|
|
784
|
+
formattedSize: this.formatBytes(0),
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
const notification = await this.notificationService.create({
|
|
789
|
+
user_id: userId,
|
|
790
|
+
title: `Limpando armazenamento (${categoryLabel})`,
|
|
791
|
+
body: `Preparando remoção de ${impact.fileCount} arquivo(s) (${this.formatBytes(impact.totalBytes)}).`,
|
|
792
|
+
type: 'progress',
|
|
793
|
+
progress: 1,
|
|
794
|
+
started_at: new Date().toISOString(),
|
|
795
|
+
auto_remove: false,
|
|
796
|
+
action_type: 'url',
|
|
797
|
+
action_url: `/lms/courses/${courseId}`,
|
|
798
|
+
action_data: {
|
|
799
|
+
source: 'lms-course-storage-cleanup',
|
|
800
|
+
courseId,
|
|
801
|
+
category,
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
const job = await this.queueJobService.enqueue({
|
|
805
|
+
type: exports.LMS_COURSE_STORAGE_CLEANUP_JOB,
|
|
806
|
+
queueName: exports.LMS_COURSE_STORAGE_CLEANUP_JOB,
|
|
807
|
+
payload: {
|
|
808
|
+
courseId,
|
|
809
|
+
category,
|
|
810
|
+
notificationId: notification.id,
|
|
811
|
+
notificationUserId: userId,
|
|
812
|
+
},
|
|
813
|
+
sourceModule: 'lms',
|
|
814
|
+
sourceEntity: 'course',
|
|
815
|
+
sourceEntityId: String(courseId),
|
|
816
|
+
maxAttempts: 3,
|
|
817
|
+
}, userId);
|
|
818
|
+
return {
|
|
819
|
+
success: true,
|
|
820
|
+
status: 'queued',
|
|
821
|
+
queueJobId: job.id,
|
|
822
|
+
notificationId: notification.id,
|
|
823
|
+
category,
|
|
824
|
+
cleanupImpact: {
|
|
825
|
+
fileCount: impact.fileCount,
|
|
826
|
+
totalBytes: impact.totalBytes,
|
|
827
|
+
formattedSize: this.formatBytes(impact.totalBytes),
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
isSupportedStorageCleanupCategory(value) {
|
|
832
|
+
return (typeof value === 'string' &&
|
|
833
|
+
cleanup_course_storage_dto_1.COURSE_STORAGE_CLEANUP_CATEGORIES.includes(value));
|
|
834
|
+
}
|
|
835
|
+
getStorageCategoryLabel(category) {
|
|
836
|
+
const labels = {
|
|
837
|
+
video_original: 'Vídeos originais',
|
|
838
|
+
video_profile: 'Vídeos convertidos',
|
|
839
|
+
lesson_audio: 'Áudios',
|
|
840
|
+
extracted_image: 'Imagens extraídas',
|
|
841
|
+
student_download: 'Downloads do aluno',
|
|
842
|
+
supplementary_material: 'Materiais de apoio',
|
|
843
|
+
course_image: 'Imagens do curso',
|
|
844
|
+
course_file: 'Arquivos do curso',
|
|
845
|
+
other_lesson_file: 'Outros arquivos',
|
|
846
|
+
course_export: 'Arquivos de exportação',
|
|
847
|
+
};
|
|
848
|
+
return labels[category];
|
|
849
|
+
}
|
|
850
|
+
async getCourseStorageCategoryImpact(courseId, category) {
|
|
851
|
+
var _a, _b;
|
|
852
|
+
const rows = await this.getCourseStorageRows(courseId);
|
|
853
|
+
const uniqueFiles = new Map();
|
|
854
|
+
for (const row of rows) {
|
|
855
|
+
const rowCategory = String(row.storage_category || 'other_lesson_file');
|
|
856
|
+
if (rowCategory !== category)
|
|
857
|
+
continue;
|
|
858
|
+
const fileId = Number((_a = row.file_id) !== null && _a !== void 0 ? _a : 0);
|
|
859
|
+
if (fileId <= 0)
|
|
860
|
+
continue;
|
|
861
|
+
if (!uniqueFiles.has(fileId)) {
|
|
862
|
+
uniqueFiles.set(fileId, Number((_b = row.file_size) !== null && _b !== void 0 ? _b : 0) || 0);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const fileIds = Array.from(uniqueFiles.keys());
|
|
866
|
+
const totalBytes = Array.from(uniqueFiles.values()).reduce((sum, size) => sum + size, 0);
|
|
867
|
+
return {
|
|
868
|
+
category,
|
|
869
|
+
fileIds,
|
|
870
|
+
fileCount: fileIds.length,
|
|
871
|
+
totalBytes,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
async deleteCourseStorageCategoryLinks(courseId, category) {
|
|
875
|
+
if (category === 'course_image') {
|
|
876
|
+
await this.prisma.course_image.deleteMany({ where: { course_id: courseId } });
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (category === 'course_file') {
|
|
880
|
+
await this.prisma.course_file.deleteMany({ where: { course_id: courseId } });
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
if (category === 'extracted_image') {
|
|
884
|
+
await this.prisma.$executeRaw `
|
|
885
|
+
DELETE FROM course_lesson_video_frame clvf
|
|
886
|
+
USING course_lesson cl, course_module cm
|
|
887
|
+
WHERE clvf.course_lesson_id = cl.id
|
|
888
|
+
AND cl.course_module_id = cm.id
|
|
889
|
+
AND cm.course_id = ${courseId}
|
|
890
|
+
`;
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if (category === 'video_profile') {
|
|
894
|
+
await this.prisma.$executeRaw `
|
|
895
|
+
DELETE FROM course_lesson_file clf
|
|
896
|
+
USING course_lesson cl, course_module cm
|
|
897
|
+
WHERE clf.course_lesson_id = cl.id
|
|
898
|
+
AND cl.course_module_id = cm.id
|
|
899
|
+
AND cm.course_id = ${courseId}
|
|
900
|
+
AND clf.type LIKE 'video_profile:%'
|
|
901
|
+
`;
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (category === 'other_lesson_file') {
|
|
905
|
+
await this.prisma.$executeRaw `
|
|
906
|
+
DELETE FROM course_lesson_file clf
|
|
907
|
+
USING course_lesson cl, course_module cm
|
|
908
|
+
WHERE clf.course_lesson_id = cl.id
|
|
909
|
+
AND cl.course_module_id = cm.id
|
|
910
|
+
AND cm.course_id = ${courseId}
|
|
911
|
+
AND (
|
|
912
|
+
clf.type IS NULL
|
|
913
|
+
OR (
|
|
914
|
+
clf.type <> 'video_original'
|
|
915
|
+
AND clf.type <> 'lesson_audio'
|
|
916
|
+
AND clf.type <> 'student_download'
|
|
917
|
+
AND clf.type <> 'supplementary_material'
|
|
918
|
+
AND clf.type NOT LIKE 'video_profile:%'
|
|
919
|
+
)
|
|
920
|
+
)
|
|
921
|
+
`;
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (category === 'course_export') {
|
|
925
|
+
const exports = await this.prisma.course_export.findMany({
|
|
926
|
+
where: {
|
|
927
|
+
course_id: courseId,
|
|
928
|
+
file_id: { not: null },
|
|
929
|
+
status: { in: ['completed', 'failed'] },
|
|
930
|
+
},
|
|
931
|
+
select: { id: true, file_id: true },
|
|
932
|
+
});
|
|
933
|
+
const fileIds = exports
|
|
934
|
+
.map((e) => e.file_id)
|
|
935
|
+
.filter((id) => id != null);
|
|
936
|
+
await this.prisma.course_export.deleteMany({
|
|
937
|
+
where: { id: { in: exports.map((e) => e.id) } },
|
|
938
|
+
});
|
|
939
|
+
if (fileIds.length > 0) {
|
|
940
|
+
await this.fileService.delete('pt', { ids: fileIds }).catch(() => undefined);
|
|
941
|
+
}
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
await this.prisma.course_lesson_file.deleteMany({
|
|
945
|
+
where: {
|
|
946
|
+
course_lesson: { course_module: { course_id: courseId } },
|
|
947
|
+
type: category,
|
|
948
|
+
},
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
async getCourseStorageRows(courseId) {
|
|
952
|
+
return this.prisma.$queryRaw `
|
|
953
|
+
SELECT files.file_id, files.file_size, files.storage_category
|
|
954
|
+
FROM (
|
|
955
|
+
SELECT ci.file_id, f.size AS file_size, 'course_image' AS storage_category
|
|
956
|
+
FROM course_image ci
|
|
957
|
+
INNER JOIN file f ON f.id = ci.file_id
|
|
958
|
+
WHERE ci.course_id = ${courseId}
|
|
959
|
+
|
|
960
|
+
UNION ALL
|
|
961
|
+
|
|
962
|
+
SELECT cf.file_id, f.size AS file_size, 'course_file' AS storage_category
|
|
963
|
+
FROM course_file cf
|
|
964
|
+
INNER JOIN file f ON f.id = cf.file_id
|
|
965
|
+
WHERE cf.course_id = ${courseId}
|
|
966
|
+
|
|
967
|
+
UNION ALL
|
|
968
|
+
|
|
969
|
+
SELECT clf.file_id,
|
|
970
|
+
f.size AS file_size,
|
|
971
|
+
CASE
|
|
972
|
+
WHEN clf.type = 'video_original' THEN 'video_original'
|
|
973
|
+
WHEN clf.type LIKE 'video_profile:%' THEN 'video_profile'
|
|
974
|
+
WHEN clf.type = 'lesson_audio' THEN 'lesson_audio'
|
|
975
|
+
WHEN clf.type = 'student_download' THEN 'student_download'
|
|
976
|
+
WHEN clf.type = 'supplementary_material' THEN 'supplementary_material'
|
|
977
|
+
ELSE 'other_lesson_file'
|
|
978
|
+
END AS storage_category
|
|
979
|
+
FROM course_lesson_file clf
|
|
980
|
+
INNER JOIN file f ON f.id = clf.file_id
|
|
981
|
+
INNER JOIN course_lesson cl ON cl.id = clf.course_lesson_id
|
|
982
|
+
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
983
|
+
WHERE cm.course_id = ${courseId}
|
|
984
|
+
|
|
985
|
+
UNION ALL
|
|
986
|
+
|
|
987
|
+
SELECT clvf.file_id, f.size AS file_size, 'extracted_image' AS storage_category
|
|
988
|
+
FROM course_lesson_video_frame clvf
|
|
989
|
+
INNER JOIN file f ON f.id = clvf.file_id
|
|
990
|
+
INNER JOIN course_lesson cl ON cl.id = clvf.course_lesson_id
|
|
991
|
+
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
992
|
+
WHERE cm.course_id = ${courseId}
|
|
993
|
+
|
|
994
|
+
UNION ALL
|
|
995
|
+
|
|
996
|
+
SELECT ce.file_id, f.size AS file_size, 'course_export' AS storage_category
|
|
997
|
+
FROM course_export ce
|
|
998
|
+
INNER JOIN file f ON f.id = ce.file_id
|
|
999
|
+
WHERE ce.course_id = ${courseId}
|
|
1000
|
+
AND ce.status = 'completed'
|
|
1001
|
+
AND ce.file_id IS NOT NULL
|
|
1002
|
+
) AS files
|
|
1003
|
+
WHERE files.file_id IS NOT NULL
|
|
1004
|
+
`;
|
|
1005
|
+
}
|
|
1006
|
+
async getCourseDeletionImpact(courseId) {
|
|
577
1007
|
const rows = await this.prisma.$queryRaw `
|
|
578
|
-
SELECT DISTINCT files.file_id
|
|
1008
|
+
SELECT DISTINCT files.file_id, files.file_size, files.source_type
|
|
579
1009
|
FROM (
|
|
580
|
-
SELECT ci.file_id
|
|
1010
|
+
SELECT ci.file_id, f.size AS file_size, 'course_image' AS source_type
|
|
581
1011
|
FROM course_image ci
|
|
1012
|
+
INNER JOIN file f ON f.id = ci.file_id
|
|
582
1013
|
WHERE ci.course_id = ${courseId}
|
|
583
1014
|
|
|
584
1015
|
UNION ALL
|
|
585
1016
|
|
|
586
|
-
SELECT cf.file_id
|
|
1017
|
+
SELECT cf.file_id, f.size AS file_size, 'course_file' AS source_type
|
|
587
1018
|
FROM course_file cf
|
|
1019
|
+
INNER JOIN file f ON f.id = cf.file_id
|
|
588
1020
|
WHERE cf.course_id = ${courseId}
|
|
589
1021
|
|
|
590
1022
|
UNION ALL
|
|
591
1023
|
|
|
592
|
-
SELECT clf.file_id
|
|
1024
|
+
SELECT clf.file_id, f.size AS file_size, 'course_lesson_file' AS source_type
|
|
593
1025
|
FROM course_lesson_file clf
|
|
1026
|
+
INNER JOIN file f ON f.id = clf.file_id
|
|
594
1027
|
INNER JOIN course_lesson cl ON cl.id = clf.course_lesson_id
|
|
595
1028
|
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
596
1029
|
WHERE cm.course_id = ${courseId}
|
|
597
1030
|
|
|
598
1031
|
UNION ALL
|
|
599
1032
|
|
|
600
|
-
SELECT clvf.file_id
|
|
1033
|
+
SELECT clvf.file_id, f.size AS file_size, 'course_lesson_video_frame' AS source_type
|
|
601
1034
|
FROM course_lesson_video_frame clvf
|
|
1035
|
+
INNER JOIN file f ON f.id = clvf.file_id
|
|
602
1036
|
INNER JOIN course_lesson cl ON cl.id = clvf.course_lesson_id
|
|
603
1037
|
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
604
1038
|
WHERE cm.course_id = ${courseId}
|
|
605
1039
|
|
|
606
1040
|
UNION ALL
|
|
607
1041
|
|
|
608
|
-
SELECT cgm.file_id
|
|
1042
|
+
SELECT cgm.file_id, f.size AS file_size, 'course_class_group_material' AS source_type
|
|
609
1043
|
FROM course_class_group_material cgm
|
|
1044
|
+
INNER JOIN file f ON f.id = cgm.file_id
|
|
610
1045
|
INNER JOIN course_class_group ccg ON ccg.id = cgm.course_class_group_id
|
|
611
1046
|
WHERE ccg.course_id = ${courseId}
|
|
1047
|
+
|
|
1048
|
+
UNION ALL
|
|
1049
|
+
|
|
1050
|
+
SELECT cln.frame_file_id AS file_id, f.size AS file_size, 'course_lesson_note_frame' AS source_type
|
|
1051
|
+
FROM course_lesson_note cln
|
|
1052
|
+
INNER JOIN file f ON f.id = cln.frame_file_id
|
|
1053
|
+
INNER JOIN course_lesson cl ON cl.id = cln.course_lesson_id
|
|
1054
|
+
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
1055
|
+
WHERE cm.course_id = ${courseId}
|
|
612
1056
|
) AS files
|
|
613
1057
|
WHERE files.file_id IS NOT NULL
|
|
614
1058
|
`;
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1059
|
+
const uniqueRows = Array.from(rows.reduce((map, row) => {
|
|
1060
|
+
var _a, _b;
|
|
1061
|
+
const fileId = Number((_a = row.file_id) !== null && _a !== void 0 ? _a : 0);
|
|
1062
|
+
if (fileId > 0 && !map.has(fileId)) {
|
|
1063
|
+
map.set(fileId, {
|
|
1064
|
+
fileId,
|
|
1065
|
+
size: Number((_b = row.file_size) !== null && _b !== void 0 ? _b : 0) || 0,
|
|
1066
|
+
sourceType: row.source_type,
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return map;
|
|
1070
|
+
}, new Map()).values());
|
|
1071
|
+
const countsBySource = rows.reduce((acc, row) => {
|
|
1072
|
+
var _a;
|
|
1073
|
+
const key = String(row.source_type || 'unknown');
|
|
1074
|
+
acc[key] = ((_a = acc[key]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1075
|
+
return acc;
|
|
1076
|
+
}, {});
|
|
1077
|
+
return {
|
|
1078
|
+
deletionImpact: {
|
|
1079
|
+
fileIds: uniqueRows.map((row) => row.fileId),
|
|
1080
|
+
fileCount: uniqueRows.length,
|
|
1081
|
+
totalBytes: uniqueRows.reduce((sum, row) => sum + row.size, 0),
|
|
1082
|
+
countsBySource,
|
|
1083
|
+
},
|
|
1084
|
+
};
|
|
618
1085
|
}
|
|
619
|
-
|
|
1086
|
+
formatBytes(bytes) {
|
|
1087
|
+
if (!Number.isFinite(bytes) || bytes <= 0) {
|
|
1088
|
+
return '0 B';
|
|
1089
|
+
}
|
|
1090
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
1091
|
+
let value = bytes;
|
|
1092
|
+
let unitIndex = 0;
|
|
1093
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
1094
|
+
value /= 1024;
|
|
1095
|
+
unitIndex += 1;
|
|
1096
|
+
}
|
|
1097
|
+
const digits = unitIndex === 0 ? 0 : value >= 10 ? 1 : 2;
|
|
1098
|
+
return `${value.toFixed(digits)} ${units[unitIndex]}`;
|
|
1099
|
+
}
|
|
1100
|
+
async getCourseRelatedFileIds(courseId) {
|
|
1101
|
+
const impact = await this.getCourseDeletionImpact(courseId);
|
|
1102
|
+
return impact.deletionImpact.fileIds.filter((id) => Number.isInteger(id) && id > 0);
|
|
1103
|
+
}
|
|
1104
|
+
mapCourse(c, metrics, extras, project, lessonInstructors, hasRunningJobs = false) {
|
|
620
1105
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38;
|
|
621
1106
|
const resolvedName = (_b = (_a = extras === null || extras === void 0 ? void 0 : extras.name) !== null && _a !== void 0 ? _a : c.name) !== null && _b !== void 0 ? _b : null;
|
|
622
1107
|
const resolvedTitle = (_c = this.normalizeOptionalText(c.title)) !== null && _c !== void 0 ? _c : c.slug;
|
|
@@ -684,6 +1169,7 @@ let CourseService = class CourseService {
|
|
|
684
1169
|
certificateModel: (_37 = (_36 = c.certificate_template) === null || _36 === void 0 ? void 0 : _36.slug) !== null && _37 !== void 0 ? _37 : (c.certificate_template_id
|
|
685
1170
|
? String(c.certificate_template_id)
|
|
686
1171
|
: null),
|
|
1172
|
+
hasRunningJobs,
|
|
687
1173
|
progressByModule: (_38 = metrics === null || metrics === void 0 ? void 0 : metrics.progressByModule) !== null && _38 !== void 0 ? _38 : [],
|
|
688
1174
|
instructors: Array.from(new Map((lessonInstructors !== null && lessonInstructors !== void 0 ? lessonInstructors : []).map((item) => [item.instructor_id, item])).values()).map((item) => {
|
|
689
1175
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -739,6 +1225,21 @@ let CourseService = class CourseService {
|
|
|
739
1225
|
}
|
|
740
1226
|
return extrasById;
|
|
741
1227
|
}
|
|
1228
|
+
async getCoursesWithRunningJobs(courseIds) {
|
|
1229
|
+
if (courseIds.length === 0)
|
|
1230
|
+
return new Set();
|
|
1231
|
+
const rows = (await this.prisma.$queryRawUnsafe(`
|
|
1232
|
+
SELECT DISTINCT cm.course_id
|
|
1233
|
+
FROM queue_job qj
|
|
1234
|
+
INNER JOIN course_lesson cl ON cl.id = CAST(qj.source_entity_id AS INTEGER)
|
|
1235
|
+
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
1236
|
+
WHERE qj.source_entity = 'course_lesson'
|
|
1237
|
+
AND qj.source_module = 'lms'
|
|
1238
|
+
AND qj.status IN ('pending', 'scheduled', 'processing', 'retrying')
|
|
1239
|
+
AND cm.course_id IN (${courseIds.join(',')})
|
|
1240
|
+
`));
|
|
1241
|
+
return new Set(rows.map((r) => Number(r.course_id)));
|
|
1242
|
+
}
|
|
742
1243
|
async persistCourseExtras(id, data) {
|
|
743
1244
|
const setClauses = [];
|
|
744
1245
|
const values = [];
|
|
@@ -759,7 +1260,8 @@ let CourseService = class CourseService {
|
|
|
759
1260
|
pushSetClause('is_listed', data.isListed);
|
|
760
1261
|
}
|
|
761
1262
|
if (data.offeringType !== undefined) {
|
|
762
|
-
|
|
1263
|
+
values.push(data.offeringType);
|
|
1264
|
+
setClauses.push(`offering_type = $${values.length}::course_offering_type_8af4d2739d_enum`);
|
|
763
1265
|
}
|
|
764
1266
|
if (setClauses.length === 0) {
|
|
765
1267
|
return;
|
|
@@ -772,8 +1274,8 @@ let CourseService = class CourseService {
|
|
|
772
1274
|
WHERE id = $${values.length}
|
|
773
1275
|
`, ...values);
|
|
774
1276
|
}
|
|
775
|
-
catch (
|
|
776
|
-
|
|
1277
|
+
catch (err) {
|
|
1278
|
+
console.error('[persistCourseExtras] failed:', err);
|
|
777
1279
|
}
|
|
778
1280
|
}
|
|
779
1281
|
async syncCourseImages(courseId, data) {
|
|
@@ -886,53 +1388,161 @@ let CourseService = class CourseService {
|
|
|
886
1388
|
select: { id: true },
|
|
887
1389
|
});
|
|
888
1390
|
}
|
|
889
|
-
async
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1391
|
+
async getCourseContentOverview(courseId) {
|
|
1392
|
+
var _a, _b, _c;
|
|
1393
|
+
const [moduleCount, lessons, transcriptionRows, xpMapRows, extractedImageCount, resourceFileCount, storageRows,] = await Promise.all([
|
|
1394
|
+
this.prisma.course_module.count({ where: { course_id: courseId } }),
|
|
1395
|
+
this.prisma.course_lesson.findMany({
|
|
1396
|
+
where: { course_module: { course_id: courseId } },
|
|
1397
|
+
select: { id: true, type: true, content: true, published: true },
|
|
1398
|
+
}),
|
|
1399
|
+
this.prisma.course_lesson_transcription_segment.findMany({
|
|
1400
|
+
where: { course_lesson: { course_module: { course_id: courseId } } },
|
|
1401
|
+
select: { course_lesson_id: true },
|
|
1402
|
+
distinct: ['course_lesson_id'],
|
|
1403
|
+
}),
|
|
1404
|
+
this.prisma.lesson_xp_map.findMany({
|
|
1405
|
+
where: { course_lesson: { course_module: { course_id: courseId } } },
|
|
1406
|
+
select: { course_lesson_id: true },
|
|
1407
|
+
}),
|
|
1408
|
+
this.prisma.course_lesson_video_frame.count({
|
|
1409
|
+
where: { course_lesson: { course_module: { course_id: courseId } } },
|
|
1410
|
+
}),
|
|
1411
|
+
this.prisma.course_lesson_file.count({
|
|
1412
|
+
where: {
|
|
1413
|
+
course_lesson: { course_module: { course_id: courseId } },
|
|
1414
|
+
type: { in: ['student_download', 'supplementary_material'] },
|
|
1415
|
+
},
|
|
1416
|
+
}),
|
|
1417
|
+
this.getCourseStorageRows(courseId),
|
|
1418
|
+
]);
|
|
1419
|
+
const transcriptionLessonIds = new Set(transcriptionRows.map((r) => r.course_lesson_id));
|
|
1420
|
+
const xpLessonIds = new Set(xpMapRows.map((r) => r.course_lesson_id));
|
|
1421
|
+
const lessonsByType = { video: 0, questao: 0, post: 0 };
|
|
1422
|
+
let publishedLessonCount = 0;
|
|
1423
|
+
let videoWithTranscription = 0;
|
|
1424
|
+
let videoWithXp = 0;
|
|
1425
|
+
const categoryOrder = [
|
|
1426
|
+
'video_original',
|
|
1427
|
+
'video_profile',
|
|
1428
|
+
'lesson_audio',
|
|
1429
|
+
'extracted_image',
|
|
1430
|
+
'student_download',
|
|
1431
|
+
'supplementary_material',
|
|
1432
|
+
'course_image',
|
|
1433
|
+
'course_file',
|
|
1434
|
+
'other_lesson_file',
|
|
1435
|
+
'course_export',
|
|
1436
|
+
];
|
|
1437
|
+
const uniqueStorageFiles = new Map();
|
|
1438
|
+
const uniqueStorageCategoryEntries = new Set();
|
|
1439
|
+
const storageCategoryMap = new Map();
|
|
1440
|
+
for (const row of storageRows) {
|
|
1441
|
+
const fileId = Number((_a = row.file_id) !== null && _a !== void 0 ? _a : 0);
|
|
1442
|
+
if (fileId <= 0)
|
|
1443
|
+
continue;
|
|
1444
|
+
const size = Number((_b = row.file_size) !== null && _b !== void 0 ? _b : 0) || 0;
|
|
1445
|
+
const category = String(row.storage_category || 'other_lesson_file');
|
|
1446
|
+
if (!uniqueStorageFiles.has(fileId)) {
|
|
1447
|
+
uniqueStorageFiles.set(fileId, size);
|
|
1448
|
+
}
|
|
1449
|
+
const categoryEntryKey = `${category}:${fileId}`;
|
|
1450
|
+
if (uniqueStorageCategoryEntries.has(categoryEntryKey)) {
|
|
1451
|
+
continue;
|
|
915
1452
|
}
|
|
1453
|
+
uniqueStorageCategoryEntries.add(categoryEntryKey);
|
|
1454
|
+
const current = (_c = storageCategoryMap.get(category)) !== null && _c !== void 0 ? _c : {
|
|
1455
|
+
key: category,
|
|
1456
|
+
fileCount: 0,
|
|
1457
|
+
totalBytes: 0,
|
|
1458
|
+
};
|
|
1459
|
+
current.fileCount += 1;
|
|
1460
|
+
current.totalBytes += size;
|
|
1461
|
+
storageCategoryMap.set(category, current);
|
|
1462
|
+
}
|
|
1463
|
+
const storageCategories = Array.from(storageCategoryMap.values()).sort((a, b) => {
|
|
1464
|
+
const orderA = categoryOrder.indexOf(a.key);
|
|
1465
|
+
const orderB = categoryOrder.indexOf(b.key);
|
|
1466
|
+
if (orderA !== -1 || orderB !== -1) {
|
|
1467
|
+
return (orderA === -1 ? Number.MAX_SAFE_INTEGER : orderA) -
|
|
1468
|
+
(orderB === -1 ? Number.MAX_SAFE_INTEGER : orderB);
|
|
1469
|
+
}
|
|
1470
|
+
return b.totalBytes - a.totalBytes;
|
|
916
1471
|
});
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1472
|
+
for (const lesson of lessons) {
|
|
1473
|
+
if (lesson.published)
|
|
1474
|
+
publishedLessonCount++;
|
|
1475
|
+
let sourceType;
|
|
1476
|
+
try {
|
|
1477
|
+
const parsed = lesson.content
|
|
1478
|
+
? JSON.parse(lesson.content)
|
|
1479
|
+
: null;
|
|
1480
|
+
sourceType =
|
|
1481
|
+
typeof (parsed === null || parsed === void 0 ? void 0 : parsed.sourceType) === 'string'
|
|
1482
|
+
? parsed.sourceType
|
|
1483
|
+
: undefined;
|
|
1484
|
+
}
|
|
1485
|
+
catch (_d) {
|
|
1486
|
+
// ignore malformed content
|
|
1487
|
+
}
|
|
1488
|
+
let uiType;
|
|
1489
|
+
if (sourceType === 'video' ||
|
|
1490
|
+
sourceType === 'questao' ||
|
|
1491
|
+
sourceType === 'post') {
|
|
1492
|
+
uiType = sourceType;
|
|
1493
|
+
}
|
|
1494
|
+
else if (lesson.type === 'video') {
|
|
1495
|
+
uiType = 'video';
|
|
1496
|
+
}
|
|
1497
|
+
else if (lesson.type === 'quiz') {
|
|
1498
|
+
uiType = 'questao';
|
|
1499
|
+
}
|
|
1500
|
+
else {
|
|
1501
|
+
uiType = 'post';
|
|
1502
|
+
}
|
|
1503
|
+
lessonsByType[uiType]++;
|
|
1504
|
+
if (uiType === 'video') {
|
|
1505
|
+
if (transcriptionLessonIds.has(lesson.id))
|
|
1506
|
+
videoWithTranscription++;
|
|
1507
|
+
if (xpLessonIds.has(lesson.id))
|
|
1508
|
+
videoWithXp++;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return {
|
|
1512
|
+
structure: {
|
|
1513
|
+
moduleCount,
|
|
1514
|
+
lessonCount: lessons.length,
|
|
1515
|
+
publishedLessonCount,
|
|
1516
|
+
lessonsByType,
|
|
1517
|
+
},
|
|
1518
|
+
videos: {
|
|
1519
|
+
lessonCount: lessonsByType.video,
|
|
1520
|
+
withTranscription: videoWithTranscription,
|
|
1521
|
+
withXp: videoWithXp,
|
|
1522
|
+
},
|
|
1523
|
+
media: { extractedImageCount },
|
|
1524
|
+
resources: { fileCount: resourceFileCount },
|
|
1525
|
+
storage: {
|
|
1526
|
+
totalBytes: Array.from(uniqueStorageFiles.values()).reduce((sum, size) => sum + size, 0),
|
|
1527
|
+
totalFileCount: uniqueStorageFiles.size,
|
|
1528
|
+
categories: storageCategories,
|
|
1529
|
+
},
|
|
1530
|
+
};
|
|
927
1531
|
}
|
|
928
1532
|
};
|
|
929
1533
|
exports.CourseService = CourseService;
|
|
930
|
-
exports.CourseService = CourseService = __decorate([
|
|
1534
|
+
exports.CourseService = CourseService = CourseService_1 = __decorate([
|
|
931
1535
|
(0, common_1.Injectable)(),
|
|
932
1536
|
__param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.IntegrationDeveloperApiService))),
|
|
1537
|
+
__param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => queue_1.QueueJobService))),
|
|
1538
|
+
__param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => queue_1.QueueHandlerRegistry))),
|
|
1539
|
+
__param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.NotificationService))),
|
|
933
1540
|
__metadata("design:paramtypes", [api_prisma_1.PrismaService,
|
|
934
1541
|
core_1.FileService,
|
|
935
1542
|
core_1.IntegrationDeveloperApiService,
|
|
1543
|
+
queue_1.QueueJobService,
|
|
1544
|
+
queue_1.QueueHandlerRegistry,
|
|
1545
|
+
core_1.NotificationService,
|
|
936
1546
|
course_operations_integration_service_1.CourseOperationsIntegrationService])
|
|
937
1547
|
], CourseService);
|
|
938
1548
|
//# sourceMappingURL=course.service.js.map
|