@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
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
+
import { buildAiConfigFromIntegration, SettingService } from '@hed-hog/core';
|
|
3
|
+
import { DatabaseQueueProvider, IJobHandler, QueueHandlerRegistry } from '@hed-hog/queue';
|
|
4
|
+
import {
|
|
5
|
+
BadRequestException,
|
|
6
|
+
forwardRef,
|
|
7
|
+
Inject,
|
|
8
|
+
Injectable,
|
|
9
|
+
Logger,
|
|
10
|
+
OnModuleInit,
|
|
11
|
+
} from '@nestjs/common';
|
|
12
|
+
import axios from 'axios';
|
|
13
|
+
|
|
14
|
+
type XpSegmentAiResult = {
|
|
15
|
+
startSeconds: number;
|
|
16
|
+
endSeconds: number;
|
|
17
|
+
xpValue: number;
|
|
18
|
+
difficulty: 'easy' | 'medium' | 'hard' | 'expert';
|
|
19
|
+
shouldGrantXp: boolean;
|
|
20
|
+
aiSummary: string;
|
|
21
|
+
aiConfidence: number;
|
|
22
|
+
areas: { xpAreaId: number; weightPercent: number }[];
|
|
23
|
+
skills: { xpSkillId: number; weightPercent: number }[];
|
|
24
|
+
learningTypes: { xpLearningTypeId: number; weightPercent: number }[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type LessonMeta = {
|
|
28
|
+
type: 'video' | 'text' | 'quiz';
|
|
29
|
+
content: string | null;
|
|
30
|
+
description: string | null;
|
|
31
|
+
durationSeconds: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
@Injectable()
|
|
35
|
+
export class LessonXpAiCalculationService implements OnModuleInit, IJobHandler {
|
|
36
|
+
private readonly logger = new Logger(LessonXpAiCalculationService.name);
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
@Inject(forwardRef(() => PrismaService))
|
|
40
|
+
private readonly prisma: PrismaService,
|
|
41
|
+
@Inject(forwardRef(() => SettingService))
|
|
42
|
+
private readonly settingService: SettingService,
|
|
43
|
+
@Inject(forwardRef(() => QueueHandlerRegistry))
|
|
44
|
+
private readonly registry: QueueHandlerRegistry,
|
|
45
|
+
@Inject(forwardRef(() => DatabaseQueueProvider))
|
|
46
|
+
private readonly dbQueue: DatabaseQueueProvider,
|
|
47
|
+
) {}
|
|
48
|
+
|
|
49
|
+
onModuleInit() {
|
|
50
|
+
this.registry.register('lms.lesson.xp.calculate', this);
|
|
51
|
+
this.logger.log('Registered handler for "lms.lesson.xp.calculate"');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async triggerCalculation(lessonId: number, userId: number) {
|
|
55
|
+
const lesson = await this.fetchLessonMeta(lessonId);
|
|
56
|
+
|
|
57
|
+
if (!lesson) {
|
|
58
|
+
throw new BadRequestException('Aula não encontrada.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (lesson.type === 'video') {
|
|
62
|
+
const segments = await this.prisma.$queryRawUnsafe<{ id: number }[]>(
|
|
63
|
+
`SELECT id FROM course_lesson_transcription_segment WHERE course_lesson_id = $1::int LIMIT 1`,
|
|
64
|
+
lessonId,
|
|
65
|
+
);
|
|
66
|
+
if (!segments.length) {
|
|
67
|
+
throw new BadRequestException(
|
|
68
|
+
'Esta aula ainda não possui transcrição. Gere ou importe a transcrição antes de calcular o XP.',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
} else if (lesson.type === 'text') {
|
|
72
|
+
if (!lesson.durationSeconds || lesson.durationSeconds <= 0) {
|
|
73
|
+
throw new BadRequestException(
|
|
74
|
+
'Esta aula do tipo post precisa ter uma duração prevista configurada antes de calcular o XP.',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
let parsed: any = {};
|
|
78
|
+
try { parsed = JSON.parse(lesson.content ?? '{}'); } catch { /* ignore */ }
|
|
79
|
+
if (!parsed.conteudoPost && !lesson.description) {
|
|
80
|
+
throw new BadRequestException(
|
|
81
|
+
'Esta aula do tipo post não possui conteúdo nem descrição pública. Adicione o conteúdo antes de calcular o XP.',
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
} else if (lesson.type === 'quiz') {
|
|
85
|
+
const questions = await this.prisma.$queryRawUnsafe<{ id: number }[]>(
|
|
86
|
+
`SELECT id FROM course_lesson_question WHERE course_lesson_id = $1::int LIMIT 1`,
|
|
87
|
+
lessonId,
|
|
88
|
+
);
|
|
89
|
+
if (!questions.length) {
|
|
90
|
+
throw new BadRequestException(
|
|
91
|
+
'Esta aula do tipo questão não possui questões vinculadas. Adicione questões antes de calcular o XP.',
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const existing = await this.prisma.$queryRawUnsafe<{ id: number }[]>(
|
|
97
|
+
`SELECT id FROM lesson_xp_map WHERE course_lesson_id = $1::int`,
|
|
98
|
+
lessonId,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
let mapId: number;
|
|
102
|
+
|
|
103
|
+
if (existing.length) {
|
|
104
|
+
mapId = existing[0].id;
|
|
105
|
+
await this.prisma.$executeRawUnsafe(
|
|
106
|
+
`UPDATE lesson_xp_map
|
|
107
|
+
SET status = 'processing'::lesson_xp_map_status_d4e5f6a7b8_enum,
|
|
108
|
+
processing_error = NULL,
|
|
109
|
+
updated_at = NOW()
|
|
110
|
+
WHERE id = $1`,
|
|
111
|
+
mapId,
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
const created = await this.prisma.$queryRawUnsafe<{ id: number }[]>(
|
|
115
|
+
`INSERT INTO lesson_xp_map (course_lesson_id, version, total_xp, status, created_at, updated_at)
|
|
116
|
+
VALUES ($1, 1, 0, 'processing'::lesson_xp_map_status_d4e5f6a7b8_enum, NOW(), NOW())
|
|
117
|
+
RETURNING id`,
|
|
118
|
+
lessonId,
|
|
119
|
+
);
|
|
120
|
+
mapId = created[0].id;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await this.dbQueue.enqueue({
|
|
125
|
+
type: 'lms.lesson.xp.calculate',
|
|
126
|
+
queueName: 'lms.lesson.xp.calculate',
|
|
127
|
+
payload: { lessonId, mapId, userId },
|
|
128
|
+
maxAttempts: 3,
|
|
129
|
+
sourceModule: 'lms',
|
|
130
|
+
sourceEntity: 'course_lesson',
|
|
131
|
+
sourceEntityId: String(lessonId),
|
|
132
|
+
userId,
|
|
133
|
+
});
|
|
134
|
+
} catch (err: any) {
|
|
135
|
+
// P2002 = unique constraint — a job for this lesson is already queued/running
|
|
136
|
+
if (err?.code !== 'P2002') throw err;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const rows = await this.prisma.$queryRawUnsafe<any[]>(
|
|
140
|
+
`SELECT
|
|
141
|
+
m.id,
|
|
142
|
+
m.course_lesson_id AS "courseLessonId",
|
|
143
|
+
m.version,
|
|
144
|
+
m.total_xp AS "totalXp",
|
|
145
|
+
m.status::text AS status,
|
|
146
|
+
m.generated_at::text AS "generatedAt",
|
|
147
|
+
m.processing_error AS "processingError",
|
|
148
|
+
m.created_at::text AS "createdAt",
|
|
149
|
+
m.updated_at::text AS "updatedAt"
|
|
150
|
+
FROM lesson_xp_map m WHERE m.id = $1`,
|
|
151
|
+
mapId,
|
|
152
|
+
);
|
|
153
|
+
return { ...rows[0], segments: [] };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async handle(job: {
|
|
157
|
+
id: number;
|
|
158
|
+
type: string;
|
|
159
|
+
queue_name: string;
|
|
160
|
+
payload: Record<string, any>;
|
|
161
|
+
attempts: number;
|
|
162
|
+
max_attempts: number;
|
|
163
|
+
}): Promise<void> {
|
|
164
|
+
const { lessonId, mapId } = job.payload as { lessonId: number; mapId: number };
|
|
165
|
+
|
|
166
|
+
this.logger.log(`Starting XP calculation for lesson ${lessonId}, map ${mapId}`);
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const lesson = await this.fetchLessonMeta(lessonId);
|
|
170
|
+
|
|
171
|
+
if (!lesson) {
|
|
172
|
+
await this.markError(mapId, 'Aula não encontrada.');
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let contentText: string;
|
|
177
|
+
const durationSeconds = lesson.durationSeconds ?? 0;
|
|
178
|
+
|
|
179
|
+
if (lesson.type === 'video') {
|
|
180
|
+
const transcriptionRows = await this.prisma.$queryRawUnsafe<
|
|
181
|
+
{ startSeconds: number; endSeconds: number; text: string }[]
|
|
182
|
+
>(
|
|
183
|
+
`SELECT start_seconds::float AS "startSeconds", end_seconds::float AS "endSeconds", text
|
|
184
|
+
FROM course_lesson_transcription_segment
|
|
185
|
+
WHERE course_lesson_id = $1
|
|
186
|
+
ORDER BY start_seconds ASC`,
|
|
187
|
+
lessonId,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (!transcriptionRows.length) {
|
|
191
|
+
await this.markError(mapId, 'Aula sem segmentos de transcrição.');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
contentText = transcriptionRows
|
|
196
|
+
.map((s) => {
|
|
197
|
+
const start = this.formatSeconds(s.startSeconds);
|
|
198
|
+
const end = this.formatSeconds(s.endSeconds);
|
|
199
|
+
return `[${start} - ${end}] ${s.text}`;
|
|
200
|
+
})
|
|
201
|
+
.join('\n');
|
|
202
|
+
} else if (lesson.type === 'text') {
|
|
203
|
+
contentText = this.buildPostContentText(lesson.content, lesson.description);
|
|
204
|
+
if (!contentText.trim()) {
|
|
205
|
+
await this.markError(mapId, 'Aula do tipo post sem conteúdo disponível para análise.');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
} else if (lesson.type === 'quiz') {
|
|
209
|
+
contentText = await this.buildQuizContentText(lessonId);
|
|
210
|
+
if (!contentText.trim()) {
|
|
211
|
+
await this.markError(mapId, 'Aula do tipo questão sem questões disponíveis para análise.');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
await this.markError(mapId, `Tipo de aula não suportado: ${(lesson as any).type}`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const [areas, skills, learningTypes] = await Promise.all([
|
|
220
|
+
this.prisma.$queryRawUnsafe<{ id: number; slug: string; name: string }[]>(
|
|
221
|
+
`SELECT a.id, a.slug, COALESCE(al.name, a.slug) AS name
|
|
222
|
+
FROM xp_area a
|
|
223
|
+
LEFT JOIN xp_area_locale al ON al.xp_area_id = a.id
|
|
224
|
+
LEFT JOIN locale l ON l.id = al.locale_id AND l.code = 'pt'
|
|
225
|
+
WHERE a.status = 'active'
|
|
226
|
+
ORDER BY a.sort_order ASC`,
|
|
227
|
+
),
|
|
228
|
+
this.prisma.$queryRawUnsafe<{ id: number; slug: string; name: string; areaId: number | null }[]>(
|
|
229
|
+
`SELECT s.id, s.slug, COALESCE(sl.name, s.slug) AS name, s.xp_area_id AS "areaId"
|
|
230
|
+
FROM xp_skill s
|
|
231
|
+
LEFT JOIN xp_skill_locale sl ON sl.xp_skill_id = s.id
|
|
232
|
+
LEFT JOIN locale l ON l.id = sl.locale_id AND l.code = 'pt'
|
|
233
|
+
WHERE s.status = 'active'
|
|
234
|
+
ORDER BY s.sort_order ASC`,
|
|
235
|
+
),
|
|
236
|
+
this.prisma.$queryRawUnsafe<{ id: number; slug: string; name: string; multiplier: number }[]>(
|
|
237
|
+
`SELECT lt.id, lt.slug, COALESCE(ltl.name, lt.slug) AS name, lt.multiplier::float AS multiplier
|
|
238
|
+
FROM xp_learning_type lt
|
|
239
|
+
LEFT JOIN xp_learning_type_locale ltl ON ltl.xp_learning_type_id = lt.id
|
|
240
|
+
LEFT JOIN locale l ON l.id = ltl.locale_id AND l.code = 'pt'
|
|
241
|
+
WHERE lt.status = 'active'
|
|
242
|
+
ORDER BY lt.sort_order ASC`,
|
|
243
|
+
),
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
const settings = await this.settingService.getSettingValues(['ai-openai-profile-id']);
|
|
247
|
+
const profileId = Number(settings['ai-openai-profile-id']);
|
|
248
|
+
|
|
249
|
+
if (!profileId) {
|
|
250
|
+
await this.markError(mapId, 'Perfil de IA OpenAI não configurado em Settings → AI.');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const profile = await (this.prisma as any).integration_profile.findUnique({
|
|
255
|
+
where: { id: profileId },
|
|
256
|
+
include: { integration_provider: { select: { slug: true } } },
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (!profile) {
|
|
260
|
+
await this.markError(mapId, 'Perfil de IA não encontrado.');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const aiConfig = buildAiConfigFromIntegration(profile.integration_provider.slug, profile.config);
|
|
265
|
+
|
|
266
|
+
const prompt = this.buildPrompt(contentText, areas, skills, learningTypes, lesson.type, durationSeconds);
|
|
267
|
+
|
|
268
|
+
const response = await axios.post(
|
|
269
|
+
'https://api.openai.com/v1/chat/completions',
|
|
270
|
+
{
|
|
271
|
+
model: 'gpt-4o',
|
|
272
|
+
temperature: 0.2,
|
|
273
|
+
response_format: { type: 'json_object' },
|
|
274
|
+
messages: [
|
|
275
|
+
{
|
|
276
|
+
role: 'system',
|
|
277
|
+
content:
|
|
278
|
+
'Você é um especialista em design instrucional e mapeia competências técnicas de aulas de programação. Responda apenas com JSON válido.',
|
|
279
|
+
},
|
|
280
|
+
{ role: 'user', content: prompt },
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
headers: { Authorization: `Bearer ${aiConfig.apiKey}`, 'Content-Type': 'application/json' },
|
|
285
|
+
timeout: 120_000,
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const rawContent: string = response.data?.choices?.[0]?.message?.content ?? '{}';
|
|
290
|
+
let parsed: { segments?: any[] };
|
|
291
|
+
try {
|
|
292
|
+
parsed = JSON.parse(rawContent);
|
|
293
|
+
} catch {
|
|
294
|
+
await this.markError(mapId, `IA retornou JSON inválido: ${rawContent.slice(0, 200)}`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const rawSegments: any[] = Array.isArray(parsed.segments) ? parsed.segments : [];
|
|
299
|
+
const areaIds = new Set(areas.map((a) => a.id));
|
|
300
|
+
const skillIds = new Set(skills.map((s) => s.id));
|
|
301
|
+
const ltIds = new Set(learningTypes.map((l) => l.id));
|
|
302
|
+
|
|
303
|
+
const validSegments: XpSegmentAiResult[] = rawSegments
|
|
304
|
+
.map((s: any) => ({
|
|
305
|
+
startSeconds: Number(s.startSeconds) || 0,
|
|
306
|
+
endSeconds: Number(s.endSeconds) || 60,
|
|
307
|
+
xpValue: Math.min(Math.max(Math.round(Number(s.xpValue) || 0), 0), 200),
|
|
308
|
+
difficulty: (['easy', 'medium', 'hard', 'expert'] as const).includes(s.difficulty)
|
|
309
|
+
? s.difficulty
|
|
310
|
+
: 'medium',
|
|
311
|
+
shouldGrantXp: Boolean(s.shouldGrantXp),
|
|
312
|
+
aiSummary: String(s.aiSummary || '').slice(0, 500),
|
|
313
|
+
aiConfidence: Math.min(Math.max(Number(s.aiConfidence) || 0.5, 0), 1),
|
|
314
|
+
areas: (Array.isArray(s.areas) ? s.areas : [])
|
|
315
|
+
.filter((a: any) => areaIds.has(Number(a.xpAreaId)))
|
|
316
|
+
.map((a: any) => ({ xpAreaId: Number(a.xpAreaId), weightPercent: Number(a.weightPercent) || 0 })),
|
|
317
|
+
skills: (Array.isArray(s.skills) ? s.skills : [])
|
|
318
|
+
.filter((sk: any) => skillIds.has(Number(sk.xpSkillId)))
|
|
319
|
+
.map((sk: any) => ({ xpSkillId: Number(sk.xpSkillId), weightPercent: Number(sk.weightPercent) || 0 })),
|
|
320
|
+
learningTypes: (Array.isArray(s.learningTypes) ? s.learningTypes : [])
|
|
321
|
+
.filter((lt: any) => ltIds.has(Number(lt.xpLearningTypeId)))
|
|
322
|
+
.map((lt: any) => ({
|
|
323
|
+
xpLearningTypeId: Number(lt.xpLearningTypeId),
|
|
324
|
+
weightPercent: Number(lt.weightPercent) || 0,
|
|
325
|
+
})),
|
|
326
|
+
}))
|
|
327
|
+
.filter((s) => s.endSeconds > s.startSeconds);
|
|
328
|
+
|
|
329
|
+
await this.prisma.$executeRawUnsafe(
|
|
330
|
+
`DELETE FROM lesson_xp_segment WHERE lesson_xp_map_id = $1`,
|
|
331
|
+
mapId,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
for (let i = 0; i < validSegments.length; i++) {
|
|
335
|
+
const seg = validSegments[i];
|
|
336
|
+
const inserted = await this.prisma.$queryRawUnsafe<{ id: number }[]>(
|
|
337
|
+
`INSERT INTO lesson_xp_segment
|
|
338
|
+
(lesson_xp_map_id, start_seconds, end_seconds, xp_value, difficulty,
|
|
339
|
+
should_grant_xp, ai_summary, ai_confidence, sort_order, created_at, updated_at)
|
|
340
|
+
VALUES ($1, $2, $3, $4, $5::lesson_xp_segment_difficulty_e5f6a7b8c9_enum,
|
|
341
|
+
$6, $7, $8, $9, NOW(), NOW())
|
|
342
|
+
RETURNING id`,
|
|
343
|
+
mapId, seg.startSeconds, seg.endSeconds, seg.xpValue,
|
|
344
|
+
seg.difficulty, seg.shouldGrantXp, seg.aiSummary,
|
|
345
|
+
seg.aiConfidence, i,
|
|
346
|
+
);
|
|
347
|
+
const segId = inserted[0].id;
|
|
348
|
+
|
|
349
|
+
for (const area of seg.areas) {
|
|
350
|
+
await this.prisma.$executeRawUnsafe(
|
|
351
|
+
`INSERT INTO lesson_xp_segment_area (lesson_xp_segment_id, xp_area_id, weight_percent, created_at, updated_at)
|
|
352
|
+
VALUES ($1, $2, $3, NOW(), NOW())`,
|
|
353
|
+
segId, area.xpAreaId, area.weightPercent,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
for (const skill of seg.skills) {
|
|
357
|
+
await this.prisma.$executeRawUnsafe(
|
|
358
|
+
`INSERT INTO lesson_xp_segment_skill (lesson_xp_segment_id, xp_skill_id, weight_percent, created_at, updated_at)
|
|
359
|
+
VALUES ($1, $2, $3, NOW(), NOW())`,
|
|
360
|
+
segId, skill.xpSkillId, skill.weightPercent,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
for (const lt of seg.learningTypes) {
|
|
364
|
+
await this.prisma.$executeRawUnsafe(
|
|
365
|
+
`INSERT INTO lesson_xp_segment_learning_type (lesson_xp_segment_id, xp_learning_type_id, weight_percent, created_at, updated_at)
|
|
366
|
+
VALUES ($1, $2, $3, NOW(), NOW())`,
|
|
367
|
+
segId, lt.xpLearningTypeId, lt.weightPercent,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
await this.prisma.$executeRawUnsafe(
|
|
373
|
+
`UPDATE lesson_xp_map
|
|
374
|
+
SET status = 'ready'::lesson_xp_map_status_d4e5f6a7b8_enum,
|
|
375
|
+
generated_at = NOW(),
|
|
376
|
+
ai_model_version = 'gpt-4o',
|
|
377
|
+
processing_error = NULL,
|
|
378
|
+
total_xp = COALESCE(
|
|
379
|
+
(SELECT SUM(xp_value) FROM lesson_xp_segment
|
|
380
|
+
WHERE lesson_xp_map_id = $1 AND should_grant_xp = true),
|
|
381
|
+
0
|
|
382
|
+
),
|
|
383
|
+
updated_at = NOW()
|
|
384
|
+
WHERE id = $1`,
|
|
385
|
+
mapId,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
this.logger.log(
|
|
389
|
+
`XP calculation done for lesson ${lessonId}: ${validSegments.length} segments saved.`,
|
|
390
|
+
);
|
|
391
|
+
} catch (err: any) {
|
|
392
|
+
this.logger.error(`XP calculation failed for lesson ${lessonId}: ${err?.message}`);
|
|
393
|
+
await this.markError(mapId, err?.message ?? 'Erro desconhecido');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private async fetchLessonMeta(lessonId: number): Promise<LessonMeta | null> {
|
|
398
|
+
const rows = await this.prisma.$queryRawUnsafe<LessonMeta[]>(
|
|
399
|
+
`SELECT type::text AS type, content, description, duration_seconds AS "durationSeconds"
|
|
400
|
+
FROM course_lesson WHERE id = $1`,
|
|
401
|
+
lessonId,
|
|
402
|
+
);
|
|
403
|
+
return rows[0] ?? null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private buildPostContentText(content: string | null, description: string | null): string {
|
|
407
|
+
const parts: string[] = [];
|
|
408
|
+
|
|
409
|
+
if (description?.trim()) {
|
|
410
|
+
parts.push(`Descrição pública:\n${description.trim()}`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
let postContent = '';
|
|
414
|
+
try {
|
|
415
|
+
const parsed = JSON.parse(content ?? '{}');
|
|
416
|
+
postContent = String(parsed.conteudoPost ?? '').trim();
|
|
417
|
+
} catch { /* ignore */ }
|
|
418
|
+
|
|
419
|
+
if (postContent) {
|
|
420
|
+
parts.push(`Conteúdo do post:\n${this.stripHtml(postContent)}`);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return parts.join('\n\n');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private async buildQuizContentText(lessonId: number): Promise<string> {
|
|
427
|
+
const questions = await this.prisma.$queryRawUnsafe<{
|
|
428
|
+
statement: string;
|
|
429
|
+
explanation: string | null;
|
|
430
|
+
options: { text: string; isCorrect: boolean; feedback: string | null }[] | null;
|
|
431
|
+
}[]>(
|
|
432
|
+
`SELECT
|
|
433
|
+
q.statement,
|
|
434
|
+
q.explanation,
|
|
435
|
+
COALESCE(
|
|
436
|
+
json_agg(
|
|
437
|
+
json_build_object('text', o.option_text, 'isCorrect', o.is_correct, 'feedback', o.feedback)
|
|
438
|
+
ORDER BY o.position ASC
|
|
439
|
+
) FILTER (WHERE o.id IS NOT NULL),
|
|
440
|
+
'[]'
|
|
441
|
+
) AS options
|
|
442
|
+
FROM course_lesson_question clq
|
|
443
|
+
JOIN question q ON q.id = clq.question_id
|
|
444
|
+
LEFT JOIN exam_option o ON o.question_id = q.id
|
|
445
|
+
WHERE clq.course_lesson_id = $1
|
|
446
|
+
GROUP BY clq.order, q.statement, q.explanation
|
|
447
|
+
ORDER BY clq.order ASC`,
|
|
448
|
+
lessonId,
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
if (!questions.length) return '';
|
|
452
|
+
|
|
453
|
+
return questions.map((q, i) => {
|
|
454
|
+
const lines: string[] = [`Questão ${i + 1}:`];
|
|
455
|
+
lines.push(`Enunciado: ${q.statement}`);
|
|
456
|
+
|
|
457
|
+
const opts = Array.isArray(q.options) ? q.options : [];
|
|
458
|
+
if (opts.length) {
|
|
459
|
+
const labels = 'abcdefghijklmnopqrstuvwxyz';
|
|
460
|
+
lines.push(
|
|
461
|
+
'Alternativas: ' +
|
|
462
|
+
opts
|
|
463
|
+
.map((o, idx) => `${labels[idx] ?? idx + 1}) ${o.text}${o.isCorrect ? ' [CORRETA]' : ''}`)
|
|
464
|
+
.join('; '),
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (q.explanation?.trim()) {
|
|
469
|
+
lines.push(`Explicação: ${q.explanation.trim()}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return lines.join('\n');
|
|
473
|
+
}).join('\n\n');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private async markError(mapId: number, message: string) {
|
|
477
|
+
await this.prisma.$executeRawUnsafe(
|
|
478
|
+
`UPDATE lesson_xp_map
|
|
479
|
+
SET status = 'needs_review'::lesson_xp_map_status_d4e5f6a7b8_enum,
|
|
480
|
+
processing_error = $1,
|
|
481
|
+
updated_at = NOW()
|
|
482
|
+
WHERE id = $2`,
|
|
483
|
+
message.slice(0, 1000),
|
|
484
|
+
mapId,
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
private buildPrompt(
|
|
489
|
+
content: string,
|
|
490
|
+
areas: { id: number; slug: string; name: string }[],
|
|
491
|
+
skills: { id: number; slug: string; name: string; areaId: number | null }[],
|
|
492
|
+
learningTypes: { id: number; slug: string; name: string; multiplier: number }[],
|
|
493
|
+
lessonType: 'video' | 'text' | 'quiz',
|
|
494
|
+
durationSeconds: number,
|
|
495
|
+
): string {
|
|
496
|
+
const totalMinutes = Math.max(Math.ceil(durationSeconds / 60), 1);
|
|
497
|
+
|
|
498
|
+
const contentHeader =
|
|
499
|
+
lessonType === 'video'
|
|
500
|
+
? 'TRANSCRIÇÃO DA AULA'
|
|
501
|
+
: lessonType === 'text'
|
|
502
|
+
? 'CONTEÚDO DO POST'
|
|
503
|
+
: 'QUESTÕES DA AULA';
|
|
504
|
+
|
|
505
|
+
const segmentInstruction =
|
|
506
|
+
lessonType === 'video'
|
|
507
|
+
? 'Analise a transcrição abaixo e mapeie as competências técnicas de cada segmento de ~60 segundos.'
|
|
508
|
+
: lessonType === 'text'
|
|
509
|
+
? `Analise o conteúdo do post abaixo (duração estimada: ${totalMinutes} minutos) e mapeie as competências técnicas. Divida o conteúdo em segmentos de ~60 segundos cobrindo a duração total. Os valores de startSeconds e endSeconds devem cobrir de 0 até ${durationSeconds} segundos.`
|
|
510
|
+
: `Analise as questões da aula abaixo (duração estimada: ${totalMinutes} minutos). Cada questão representa um segmento; distribua o tempo total de ${durationSeconds} segundos igualmente entre as questões. Os valores de startSeconds e endSeconds devem cobrir de 0 até ${durationSeconds} segundos.`;
|
|
511
|
+
|
|
512
|
+
return `${segmentInstruction}
|
|
513
|
+
|
|
514
|
+
CATÁLOGO DE ÁREAS MACRO (use APENAS estes IDs):
|
|
515
|
+
${JSON.stringify(areas.map((a) => ({ id: a.id, slug: a.slug, name: a.name })))}
|
|
516
|
+
|
|
517
|
+
CATÁLOGO DE SKILLS (use APENAS estes IDs):
|
|
518
|
+
${JSON.stringify(skills.map((s) => ({ id: s.id, slug: s.slug, name: s.name, areaId: s.areaId })))}
|
|
519
|
+
|
|
520
|
+
CATÁLOGO DE TIPOS DE APRENDIZADO (use APENAS estes IDs):
|
|
521
|
+
${JSON.stringify(learningTypes.map((l) => ({ id: l.id, slug: l.slug, name: l.name, multiplier: l.multiplier })))}
|
|
522
|
+
|
|
523
|
+
${contentHeader}:
|
|
524
|
+
${content}
|
|
525
|
+
|
|
526
|
+
Retorne um JSON com a chave "segments" contendo um array onde cada item representa um segmento de ~60s:
|
|
527
|
+
{
|
|
528
|
+
"segments": [
|
|
529
|
+
{
|
|
530
|
+
"startSeconds": 0,
|
|
531
|
+
"endSeconds": 60,
|
|
532
|
+
"xpValue": 10,
|
|
533
|
+
"difficulty": "medium",
|
|
534
|
+
"shouldGrantXp": true,
|
|
535
|
+
"aiSummary": "Resumo curto do conteúdo do segmento",
|
|
536
|
+
"aiConfidence": 0.9,
|
|
537
|
+
"areas": [{"xpAreaId": 1, "weightPercent": 70}, {"xpAreaId": 2, "weightPercent": 30}],
|
|
538
|
+
"skills": [{"xpSkillId": 5, "weightPercent": 100}],
|
|
539
|
+
"learningTypes": [{"xpLearningTypeId": 2, "weightPercent": 100}]
|
|
540
|
+
}
|
|
541
|
+
]
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
Regras obrigatórias:
|
|
545
|
+
- Use APENAS IDs do catálogo acima. Nunca invente IDs.
|
|
546
|
+
- xpValue entre 0 e 50 por segmento. Segmentos com conteúdo irrelevante (intro, pausa, avisos) devem ter shouldGrantXp=false e xpValue=0.
|
|
547
|
+
- Os weightPercent de areas, skills e learningTypes devem somar 100 cada.
|
|
548
|
+
- difficulty: easy (conceitos básicos), medium (aplicação), hard (avançado), expert (arquitetura/otimização).
|
|
549
|
+
- aiConfidence: 0 a 1, indica certeza do mapeamento.`;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private stripHtml(html: string): string {
|
|
553
|
+
return html
|
|
554
|
+
.replace(/<[^>]+>/g, ' ')
|
|
555
|
+
.replace(/&/g, '&')
|
|
556
|
+
.replace(/</g, '<')
|
|
557
|
+
.replace(/>/g, '>')
|
|
558
|
+
.replace(/"/g, '"')
|
|
559
|
+
.replace(/'/g, "'")
|
|
560
|
+
.replace(/ /g, ' ')
|
|
561
|
+
.replace(/\s{2,}/g, ' ')
|
|
562
|
+
.trim();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
private formatSeconds(s: number): string {
|
|
566
|
+
const m = Math.floor(s / 60);
|
|
567
|
+
const sec = Math.floor(s % 60);
|
|
568
|
+
return `${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Role } from '@hed-hog/api';
|
|
2
|
+
import {
|
|
3
|
+
Body,
|
|
4
|
+
Controller,
|
|
5
|
+
Delete,
|
|
6
|
+
Get,
|
|
7
|
+
HttpCode,
|
|
8
|
+
HttpStatus,
|
|
9
|
+
Param,
|
|
10
|
+
ParseIntPipe,
|
|
11
|
+
Patch,
|
|
12
|
+
Post,
|
|
13
|
+
Query,
|
|
14
|
+
Request,
|
|
15
|
+
} from '@nestjs/common';
|
|
16
|
+
import { CreateLessonXpMapDto } from './dto/create-lesson-xp-map.dto';
|
|
17
|
+
import { CreateLessonXpSegmentDto } from './dto/create-lesson-xp-segment.dto';
|
|
18
|
+
import { RejectLessonXpMapDto } from './dto/review-lesson-xp-map.dto';
|
|
19
|
+
import { UpdateLessonXpMapDto } from './dto/update-lesson-xp-map.dto';
|
|
20
|
+
import { LessonXpAiCalculationService } from './lesson-xp-ai-calculation.service';
|
|
21
|
+
import { LessonXpMapService } from './lesson-xp-map.service';
|
|
22
|
+
import { LessonXpSegmentService } from './lesson-xp-segment.service';
|
|
23
|
+
|
|
24
|
+
@Role()
|
|
25
|
+
@Controller('lms/xp/lesson-maps')
|
|
26
|
+
export class LessonXpMapController {
|
|
27
|
+
constructor(
|
|
28
|
+
private readonly lessonXpMapService: LessonXpMapService,
|
|
29
|
+
private readonly lessonXpSegmentService: LessonXpSegmentService,
|
|
30
|
+
private readonly lessonXpAiCalculationService: LessonXpAiCalculationService,
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
@Get()
|
|
34
|
+
list(
|
|
35
|
+
@Query('page') page?: string,
|
|
36
|
+
@Query('pageSize') pageSize?: string,
|
|
37
|
+
@Query('search') search?: string,
|
|
38
|
+
@Query('status') status?: string,
|
|
39
|
+
@Query('courseLessonId') courseLessonId?: string,
|
|
40
|
+
) {
|
|
41
|
+
return this.lessonXpMapService.list({
|
|
42
|
+
page: Number(page) || 1,
|
|
43
|
+
pageSize: Number(pageSize) || 20,
|
|
44
|
+
search,
|
|
45
|
+
status,
|
|
46
|
+
courseLessonId: courseLessonId ? Number(courseLessonId) : undefined,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Post()
|
|
51
|
+
create(@Body() dto: CreateLessonXpMapDto) {
|
|
52
|
+
return this.lessonXpMapService.create(dto);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Get('lesson/:lessonId')
|
|
56
|
+
getByLesson(@Param('lessonId', ParseIntPipe) lessonId: number) {
|
|
57
|
+
return this.lessonXpMapService.getByLessonId(lessonId);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Get('course/:courseId/overview')
|
|
61
|
+
getCourseOverview(@Param('courseId', ParseIntPipe) courseId: number) {
|
|
62
|
+
return this.lessonXpMapService.getCourseOverview(courseId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Post('lesson/:lessonId/recalculate')
|
|
66
|
+
recalculate(@Param('lessonId', ParseIntPipe) lessonId: number, @Request() req: any) {
|
|
67
|
+
return this.lessonXpAiCalculationService.triggerCalculation(lessonId, req.user?.id ?? 0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Get(':id')
|
|
71
|
+
getById(@Param('id', ParseIntPipe) id: number) {
|
|
72
|
+
return this.lessonXpMapService.getById(id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Patch(':id')
|
|
76
|
+
update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateLessonXpMapDto) {
|
|
77
|
+
return this.lessonXpMapService.update(id, dto);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Delete(':id')
|
|
81
|
+
@HttpCode(HttpStatus.NO_CONTENT)
|
|
82
|
+
remove(@Param('id', ParseIntPipe) id: number) {
|
|
83
|
+
return this.lessonXpMapService.delete(id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Post(':id/approve')
|
|
87
|
+
approve(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
|
|
88
|
+
const reviewerPersonId: number = req.user?.personId ?? req.user?.id ?? 0;
|
|
89
|
+
return this.lessonXpMapService.approve(id, reviewerPersonId);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Post(':id/reject')
|
|
93
|
+
reject(
|
|
94
|
+
@Param('id', ParseIntPipe) id: number,
|
|
95
|
+
@Body() dto: RejectLessonXpMapDto,
|
|
96
|
+
@Request() req: any,
|
|
97
|
+
) {
|
|
98
|
+
const reviewerPersonId: number = req.user?.personId ?? req.user?.id ?? 0;
|
|
99
|
+
return this.lessonXpMapService.reject(id, reviewerPersonId, dto.reviewNotes);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Segment sub-resource
|
|
103
|
+
@Get(':id/segments')
|
|
104
|
+
listSegments(
|
|
105
|
+
@Param('id', ParseIntPipe) id: number,
|
|
106
|
+
@Query('page') page?: string,
|
|
107
|
+
@Query('pageSize') pageSize?: string,
|
|
108
|
+
) {
|
|
109
|
+
return this.lessonXpSegmentService.listByMap(id, { page: Number(page) || 1, pageSize: Number(pageSize) || 50 });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Post(':id/segments')
|
|
113
|
+
createSegment(@Param('id', ParseIntPipe) id: number, @Body() dto: CreateLessonXpSegmentDto) {
|
|
114
|
+
return this.lessonXpSegmentService.create(id, dto);
|
|
115
|
+
}
|
|
116
|
+
}
|