@memberjunction/actions-bizapps-lms
Version:
LMS system integration actions for MemberJunction
209 lines • 10.7 kB
JavaScript
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 update a user's course progress in LearnWorlds
*/
let UpdateUserProgressAction = class UpdateUserProgressAction extends LearnWorldsBaseAction {
// ----------------------------------------------------------------
// Typed public method – can be called directly from code
// ----------------------------------------------------------------
/**
* Update user progress for a course or lesson.
* Throws on any error.
*/
async UpdateProgress(params, contextUser) {
this.SetCompanyContext(params.CompanyID);
const { UserID, CourseID, LessonID, ProgressPercentage, Completed, TimeSpent, Score, Notes } = params;
// Validate required fields
if (!UserID) {
throw new Error('UserID is required');
}
if (!CourseID) {
throw new Error('CourseID is required');
}
this.validatePathSegment(UserID, 'UserID');
this.validatePathSegment(CourseID, 'CourseID');
if (LessonID) {
this.validatePathSegment(LessonID, 'LessonID');
}
if (ProgressPercentage !== undefined && (ProgressPercentage < 0 || ProgressPercentage > 100)) {
throw new Error('ProgressPercentage must be between 0 and 100');
}
// Get current enrollment first
const currentEnrollment = await this.fetchCurrentEnrollment(UserID, CourseID, contextUser);
const updateResult = {};
// Update lesson progress if lessonId is provided
if (LessonID) {
updateResult.lessonProgress = await this.updateLessonProgress(UserID, CourseID, LessonID, { Completed, ProgressPercentage, TimeSpent, Score, Notes }, contextUser);
}
// Update overall course progress if progressPercentage is provided at course level
if (ProgressPercentage !== undefined && !LessonID) {
updateResult.courseProgress = await this.updateCourseProgress(UserID, CourseID, currentEnrollment, { Completed, ProgressPercentage, TimeSpent }, contextUser);
}
// Get updated enrollment details
const updatedProgress = await this.fetchUpdatedEnrollment(UserID, CourseID, currentEnrollment, contextUser);
// Build typed result
const updateType = LessonID ? 'lesson' : 'course';
const progressDetails = this.buildProgressDetails(UserID, CourseID, LessonID, currentEnrollment, updatedProgress, updateType, updateResult);
const summary = this.buildProgressSummary(UserID, CourseID, LessonID, currentEnrollment, updatedProgress, TimeSpent, updateType);
return { ProgressDetails: progressDetails, Summary: summary };
}
// ----------------------------------------------------------------
// Framework wrapper – thin delegation to the public method
// ----------------------------------------------------------------
async InternalRunAction(params) {
const { Params, ContextUser } = params;
this.params = Params;
try {
const typedParams = this.extractUpdateProgressParams(Params);
const result = await this.UpdateProgress(typedParams, ContextUser);
this.setOutputParam(Params, 'ProgressDetails', result.ProgressDetails);
this.setOutputParam(Params, 'Summary', result.Summary);
return this.buildSuccessResult(`Successfully updated ${result.ProgressDetails.updateType} progress`, Params);
}
catch (error) {
const msg = error instanceof Error ? error.message : 'Unknown error';
return this.buildErrorResult('ERROR', `Error updating progress: ${msg}`, Params);
}
}
// ----------------------------------------------------------------
// Private helpers
// ----------------------------------------------------------------
extractUpdateProgressParams(params) {
return {
CompanyID: this.getRequiredStringParam(params, 'CompanyID'),
UserID: this.getRequiredStringParam(params, 'UserID'),
CourseID: this.getRequiredStringParam(params, 'CourseID'),
LessonID: this.getOptionalStringParam(params, 'LessonID'),
ProgressPercentage: this.getOptionalNumberParam(params, 'ProgressPercentage', undefined),
Completed: this.getOptionalBooleanParam(params, 'Completed', undefined),
TimeSpent: this.getOptionalNumberParam(params, 'TimeSpent', undefined),
Score: this.getOptionalNumberParam(params, 'Score', undefined),
Notes: this.getOptionalStringParam(params, 'Notes'),
};
}
async fetchCurrentEnrollment(userId, courseId, contextUser) {
try {
return await this.makeLearnWorldsRequest(`users/${userId}/enrollments/${courseId}`, 'GET', undefined, contextUser);
}
catch (error) {
throw new Error('User is not enrolled in this course: ' + (error instanceof Error ? error.message : String(error)));
}
}
async updateLessonProgress(userId, courseId, lessonId, data, contextUser) {
const body = {
completed: data.Completed !== undefined ? data.Completed : false,
progress_percentage: data.ProgressPercentage,
};
if (data.TimeSpent !== undefined) {
body.time_spent = data.TimeSpent;
}
if (data.Score !== undefined) {
body.score = data.Score;
}
if (data.Notes) {
body.notes = data.Notes;
}
return await this.makeLearnWorldsRequest(`users/${userId}/courses/${courseId}/lessons/${lessonId}/progress`, 'PUT', body, contextUser);
}
async updateCourseProgress(userId, courseId, currentEnrollment, data, contextUser) {
const body = {
progress_percentage: data.ProgressPercentage,
};
if (data.Completed !== undefined) {
body.completed = data.Completed;
}
if (data.TimeSpent !== undefined) {
body.total_time_spent = (currentEnrollment.total_time_spent || 0) + data.TimeSpent;
}
return await this.makeLearnWorldsRequest(`users/${userId}/enrollments/${courseId}/progress`, 'PUT', body, contextUser);
}
async fetchUpdatedEnrollment(userId, courseId, fallback, contextUser) {
try {
return await this.makeLearnWorldsRequest(`users/${userId}/enrollments/${courseId}`, 'GET', undefined, contextUser);
}
catch (error) {
// If we can't get updated details, use previous enrollment
console.warn('Failed to get updated enrollment details:', error);
return fallback;
}
}
buildProgressDetails(userId, courseId, lessonId, currentEnrollment, updatedProgress, updateType, updateResult) {
return {
userId,
courseId,
lessonId,
previousProgress: {
percentage: currentEnrollment.progress_percentage || 0,
completedUnits: currentEnrollment.completed_units || 0,
totalTimeSpent: currentEnrollment.total_time_spent || 0,
},
updatedProgress: {
percentage: updatedProgress.progress_percentage || 0,
completedUnits: updatedProgress.completed_units || 0,
totalUnits: updatedProgress.total_units || 0,
totalTimeSpent: updatedProgress.total_time_spent || 0,
totalTimeSpentText: this.formatDuration(updatedProgress.total_time_spent || 0),
lastAccessedAt: updatedProgress.last_accessed_at || new Date().toISOString(),
completed: updatedProgress.completed || false,
completedAt: updatedProgress.completed_at,
},
updateType,
updateResult,
};
}
buildProgressSummary(userId, courseId, lessonId, currentEnrollment, updatedProgress, timeSpent, updateType) {
return {
userId,
courseId,
lessonId,
progressIncreased: (updatedProgress.progress_percentage || 0) > (currentEnrollment.progress_percentage || 0),
previousPercentage: currentEnrollment.progress_percentage || 0,
newPercentage: updatedProgress.progress_percentage || 0,
timeAdded: timeSpent || 0,
totalTimeSpent: updatedProgress.total_time_spent || 0,
isCompleted: updatedProgress.completed || false,
updateType,
};
}
// ----------------------------------------------------------------
// Params & Description metadata
// ----------------------------------------------------------------
/**
* Define the parameters this action expects
*/
get Params() {
const baseParams = this.getCommonLMSParams();
const specificParams = [
{ Name: 'UserID', Type: 'Input', Value: null },
{ Name: 'CourseID', Type: 'Input', Value: null },
{ Name: 'LessonID', Type: 'Input', Value: null },
{ Name: 'ProgressPercentage', Type: 'Input', Value: null },
{ Name: 'Completed', Type: 'Input', Value: null },
{ Name: 'TimeSpent', Type: 'Input', Value: null },
{ Name: 'Score', Type: 'Input', Value: null },
{ Name: 'Notes', Type: 'Input', Value: null },
{ Name: 'ProgressDetails', Type: 'Output', Value: null },
{ Name: 'Summary', Type: 'Output', Value: null },
];
return [...baseParams, ...specificParams];
}
/**
* Metadata about this action
*/
get Description() {
return "Updates a user's progress for a course or specific lesson in LearnWorlds";
}
};
UpdateUserProgressAction = __decorate([
RegisterClass(BaseAction, 'UpdateUserProgressAction')
], UpdateUserProgressAction);
export { UpdateUserProgressAction };
//# sourceMappingURL=update-user-progress.action.js.map