UNPKG

@memberjunction/actions-bizapps-lms

Version:

LMS system integration actions for MemberJunction

440 lines 17.2 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { RegisterClass } from '@memberjunction/global'; import { LearnWorldsBaseAction } from '../learnworlds-base.action.js'; import { BaseAction } from '@memberjunction/actions'; /** * Action to retrieve courses from LearnWorlds LMS */ let GetLearnWorldsCoursesAction = class GetLearnWorldsCoursesAction extends LearnWorldsBaseAction { /** * Description of the action */ get Description() { return 'Retrieves the course catalog from LearnWorlds with filtering, search, and sorting options'; } /** * Typed public method for direct (non-framework) callers. * Sets company context, fetches courses, and returns a strongly-typed result. * Throws on error. */ async GetCourses(params, contextUser) { this.SetCompanyContext(params.CompanyID); const queryParams = this.buildQueryParams(params); const courses = await this.makeLearnWorldsPaginatedRequest('courses', queryParams, contextUser); const mappedCourses = courses.map((course) => this.mapLearnWorldsCourse(course)); const summary = this.calculateCourseCatalogSummary(mappedCourses); return { Courses: mappedCourses, TotalCount: mappedCourses.length, Summary: summary, }; } /** * Framework entry-point – thin wrapper around the typed public method. */ async InternalRunAction(params) { const { Params, ContextUser } = params; this.params = Params; try { const typedParams = this.extractGetCoursesParams(Params); const result = await this.GetCourses(typedParams, ContextUser); this.setOutputParam(Params, 'Courses', result.Courses); this.setOutputParam(Params, 'TotalCount', result.TotalCount); this.setOutputParam(Params, 'Summary', result.Summary); return this.buildSuccessResult(`Successfully retrieved ${result.TotalCount} courses from LearnWorlds`, Params); } catch (error) { const msg = error instanceof Error ? error.message : 'Unknown error occurred'; return this.buildErrorResult('ERROR', msg, Params); } } /** * Extract typed params from framework ActionParam[] */ extractGetCoursesParams(params) { return { CompanyID: this.getRequiredStringParam(params, 'CompanyID'), SearchText: this.getOptionalStringParam(params, 'SearchText'), Status: this.getOptionalStringParam(params, 'Status'), CategoryID: this.getOptionalStringParam(params, 'CategoryID'), Level: this.getOptionalStringParam(params, 'Level'), Language: this.getOptionalStringParam(params, 'Language'), OnlyFree: this.getOptionalBooleanParam(params, 'OnlyFree', false), MinPrice: this.getOptionalNumberParam(params, 'MinPrice', undefined), MaxPrice: this.getOptionalNumberParam(params, 'MaxPrice', undefined), Tags: this.getOptionalStringParam(params, 'Tags'), InstructorID: this.getOptionalStringParam(params, 'InstructorID'), CreatedAfter: this.getOptionalStringParam(params, 'CreatedAfter'), CreatedBefore: this.getOptionalStringParam(params, 'CreatedBefore'), SortBy: this.getOptionalStringParam(params, 'SortBy'), SortOrder: (this.getOptionalStringParam(params, 'SortOrder') || 'desc'), IncludeEnrollmentStats: this.getOptionalBooleanParam(params, 'IncludeEnrollmentStats', true), MaxResults: this.getOptionalNumberParam(params, 'MaxResults', LearnWorldsBaseAction.LW_MAX_PAGE_SIZE), }; } /** * Build query parameters for the LearnWorlds API request */ buildQueryParams(params) { const queryParams = {}; if (params.SearchText) { queryParams.search = params.SearchText; } if (params.Status) { queryParams.status = params.Status; } if (params.CategoryID) { queryParams.category_id = params.CategoryID; } if (params.Level) { queryParams.level = params.Level; } if (params.Language) { queryParams.language = params.Language; } if (params.OnlyFree) { queryParams.is_free = true; } if (params.MinPrice != null && params.MinPrice > 0) { queryParams.min_price = params.MinPrice; } if (params.MaxPrice != null && params.MaxPrice > 0) { queryParams.max_price = params.MaxPrice; } if (params.Tags) { queryParams.tags = params.Tags; } if (params.InstructorID) { queryParams.instructor_id = params.InstructorID; } if (params.CreatedAfter) { queryParams.created_after = this.formatLearnWorldsDate(new Date(params.CreatedAfter)); } if (params.CreatedBefore) { queryParams.created_before = this.formatLearnWorldsDate(new Date(params.CreatedBefore)); } const sortBy = params.SortBy || 'created'; const sortOrder = params.SortOrder || 'desc'; queryParams.sort = `${sortOrder === 'asc' ? '' : '-'}${sortBy}`; const includeEnrollmentStats = params.IncludeEnrollmentStats ?? true; if (includeEnrollmentStats) { queryParams.include = 'enrollment_stats'; } const maxResults = params.MaxResults || 100; queryParams.limit = Math.min(maxResults, 100); return queryParams; } /** * Map LearnWorlds course data to our interface */ mapLearnWorldsCourse(lwCourse) { return { ...this.mapCourseIdentity(lwCourse), ...this.mapCoursePricing(lwCourse), ...this.mapCourseInstructor(lwCourse), ...this.mapCourseContentStats(lwCourse), ...this.mapCourseMeta(lwCourse), }; } mapCourseIdentity(lw) { return { id: lw.id || lw._id || '', title: lw.title, subtitle: lw.subtitle, description: lw.description, shortDescription: lw.short_description || lw.excerpt, status: this.mapCourseStatus(lw.status || ''), visibility: this.mapCourseVisibility(lw.visibility || lw.access_type || ''), isActive: lw.is_active !== false, isFree: lw.is_free || lw.price === 0 || false, categoryId: lw.category_id || lw.category?.id, categoryName: lw.category_name || lw.category?.name, tags: lw.tags || [], level: this.mapCourseLevel(lw.level || lw.difficulty || ''), language: lw.language || 'en', duration: lw.duration || lw.estimated_duration, thumbnailUrl: lw.thumbnail_url || lw.image, coverImageUrl: lw.cover_image_url || lw.cover_image, promoVideoUrl: lw.promo_video_url || lw.video_url, }; } mapCoursePricing(lw) { return { price: lw.price, currency: lw.currency || 'USD', originalPrice: lw.original_price, discountPercentage: this.calculateDiscountPercentage(lw.original_price, lw.price), }; } mapCourseInstructor(lw) { return { instructorId: lw.instructor_id || lw.instructor?.id || lw.author_id, instructorName: lw.instructor_name || lw.instructor?.name || lw.author_name, instructorBio: lw.instructor_bio || lw.instructor?.bio, instructorAvatarUrl: lw.instructor_avatar || lw.instructor?.avatar_url, }; } mapCourseContentStats(lw) { return { totalUnits: lw.total_units || lw.sections_count || 0, totalLessons: lw.total_lessons || lw.lessons_count || 0, totalQuizzes: lw.total_quizzes || lw.quizzes_count || 0, totalAssignments: lw.total_assignments || lw.assignments_count || 0, estimatedDuration: lw.estimated_duration || lw.total_duration, totalEnrollments: lw.total_enrollments || lw.students_count || 0, activeEnrollments: lw.active_enrollments || lw.active_students || 0, completionRate: lw.completion_rate || 0, averageRating: lw.average_rating || lw.rating, totalReviews: lw.total_reviews || lw.reviews_count, }; } mapCourseMeta(lw) { return { createdAt: this.parseLearnWorldsDate(lw.created || lw.created_at || ''), updatedAt: this.parseLearnWorldsDate(lw.updated || lw.updated_at || ''), publishedAt: lw.published_at ? this.parseLearnWorldsDate(lw.published_at) : undefined, enrollmentStartDate: lw.enrollment_start ? this.parseLearnWorldsDate(lw.enrollment_start) : undefined, enrollmentEndDate: lw.enrollment_end ? this.parseLearnWorldsDate(lw.enrollment_end) : undefined, requiresApproval: lw.requires_approval || false, hasPrerequisites: lw.has_prerequisites || false, prerequisites: lw.prerequisites || [], certificateAvailable: lw.certificate_available || lw.has_certificate || false, objectives: lw.objectives || lw.learning_objectives || [], targetAudience: lw.target_audience || [], requirements: lw.requirements || lw.prerequisites_text || [], }; } /** * Map course status */ mapCourseStatus(status) { const statusMap = { published: 'published', active: 'published', draft: 'draft', unpublished: 'draft', coming_soon: 'coming_soon', upcoming: 'coming_soon', }; return statusMap[status?.toLowerCase()] || 'draft'; } /** * Map course visibility */ mapCourseVisibility(visibility) { const visibilityMap = { public: 'public', open: 'public', private: 'private', closed: 'private', hidden: 'hidden', unlisted: 'hidden', }; return visibilityMap[visibility?.toLowerCase()] || 'public'; } /** * Map course level */ mapCourseLevel(level) { const levelMap = { beginner: 'beginner', basic: 'beginner', introductory: 'beginner', intermediate: 'intermediate', medium: 'intermediate', advanced: 'advanced', expert: 'advanced', all: 'all', any: 'all', mixed: 'all', }; return levelMap[level?.toLowerCase()] || 'all'; } /** * Calculate discount percentage */ calculateDiscountPercentage(originalPrice, currentPrice) { if (!originalPrice || !currentPrice || originalPrice <= currentPrice) { return undefined; } return Math.round(((originalPrice - currentPrice) / originalPrice) * 100); } /** * Calculate course catalog summary */ calculateCourseCatalogSummary(courses) { const summary = { totalCourses: courses.length, publishedCourses: courses.filter((c) => c.status === 'published').length, draftCourses: courses.filter((c) => c.status === 'draft').length, freeCourses: courses.filter((c) => c.isFree).length, paidCourses: courses.filter((c) => !c.isFree).length, categoryCounts: {}, levelCounts: {}, languageCounts: {}, enrollmentStats: { totalEnrollments: 0, averageEnrollmentsPerCourse: 0, mostPopularCourses: [], }, priceStats: { averagePrice: 0, minPrice: 0, maxPrice: 0, currency: 'USD', }, }; this.populateCategoryCounts(courses, summary); this.populateEnrollmentStats(courses, summary); this.populatePriceStats(courses, summary); return summary; } /** * Populate category, level, and language counts in the summary */ populateCategoryCounts(courses, summary) { courses.forEach((course) => { if (course.categoryName) { summary.categoryCounts[course.categoryName] = (summary.categoryCounts[course.categoryName] || 0) + 1; } if (course.level) { summary.levelCounts[course.level] = (summary.levelCounts[course.level] || 0) + 1; } if (course.language) { summary.languageCounts[course.language] = (summary.languageCounts[course.language] || 0) + 1; } summary.enrollmentStats.totalEnrollments += course.totalEnrollments; }); } /** * Populate enrollment statistics in the summary */ populateEnrollmentStats(courses, summary) { if (courses.length > 0) { summary.enrollmentStats.averageEnrollmentsPerCourse = Math.round(summary.enrollmentStats.totalEnrollments / courses.length); } summary.enrollmentStats.mostPopularCourses = courses .filter((c) => c.totalEnrollments > 0) .sort((a, b) => b.totalEnrollments - a.totalEnrollments) .slice(0, 5) .map((c) => ({ id: c.id, title: c.title, enrollments: c.totalEnrollments, })); } /** * Populate price statistics in the summary */ populatePriceStats(courses, summary) { const paidCourses = courses.filter((c) => !c.isFree && c.price !== undefined); if (paidCourses.length > 0) { const prices = paidCourses.map((c) => c.price); summary.priceStats = { averagePrice: Math.round(prices.reduce((sum, p) => sum + p, 0) / prices.length), minPrice: Math.min(...prices), maxPrice: Math.max(...prices), currency: paidCourses[0].currency || 'USD', }; } } /** * Define the parameters for this action */ get Params() { const baseParams = this.getCommonLMSParams(); const specificParams = [ { Name: 'SearchText', Type: 'Input', Value: null, }, { Name: 'Status', Type: 'Input', Value: null, }, { Name: 'CategoryID', Type: 'Input', Value: null, }, { Name: 'Level', Type: 'Input', Value: null, }, { Name: 'Language', Type: 'Input', Value: null, }, { Name: 'OnlyFree', Type: 'Input', Value: false, }, { Name: 'MinPrice', Type: 'Input', Value: null, }, { Name: 'MaxPrice', Type: 'Input', Value: null, }, { Name: 'Tags', Type: 'Input', Value: null, }, { Name: 'InstructorID', Type: 'Input', Value: null, }, { Name: 'CreatedAfter', Type: 'Input', Value: null, }, { Name: 'CreatedBefore', Type: 'Input', Value: null, }, { Name: 'SortBy', Type: 'Input', Value: 'created', }, { Name: 'SortOrder', Type: 'Input', Value: 'desc', }, { Name: 'IncludeEnrollmentStats', Type: 'Input', Value: true, }, { Name: 'MaxResults', Type: 'Input', Value: 100, }, ]; return [...baseParams, ...specificParams]; } }; GetLearnWorldsCoursesAction = __decorate([ RegisterClass(BaseAction, 'GetLearnWorldsCoursesAction') ], GetLearnWorldsCoursesAction); export { GetLearnWorldsCoursesAction }; //# sourceMappingURL=get-courses.action.js.map