@patch-adams/core 1.4.9 → 1.4.11

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/cli.js CHANGED
@@ -2862,6 +2862,15 @@ function generateLrsBridgeCode(options) {
2862
2862
  ctx.extensions['http://xyleme.com/bravais/extensions/statement-schema'] = 'xyleme_10';
2863
2863
  ctx.extensions['http://xyleme.com/bravais/extensions/protect_pii'] = false;
2864
2864
 
2865
+ // Add parent course activity for document-level aggregation in Bravais
2866
+ // This allows statements with activity-specific objects to still be found
2867
+ // when searching by document name in Analytics
2868
+ var parentActivity = buildParentCourseActivity();
2869
+ if (parentActivity) {
2870
+ ctx.contextActivities = ctx.contextActivities || {};
2871
+ ctx.contextActivities.parent = [parentActivity];
2872
+ }
2873
+
2865
2874
  return ctx;
2866
2875
  }
2867
2876
 
@@ -2906,18 +2915,110 @@ function generateLrsBridgeCode(options) {
2906
2915
 
2907
2916
  /**
2908
2917
  * Build activity object for questions in Xyleme format
2918
+ * Name format: "Assessment Name - Q#: Question text..."
2909
2919
  */
2910
2920
  function buildQuestionActivityObject(questionInfo) {
2911
2921
  var questionGuid = questionInfo.questionGuid || questionInfo.id || generateUUID();
2912
2922
 
2923
+ // Build human-readable display name
2924
+ var displayName = (questionInfo.assessmentName || 'Knowledge Check');
2925
+ if (questionInfo.questionNumber) {
2926
+ displayName += ' - Q' + questionInfo.questionNumber;
2927
+ }
2928
+ var questionText = questionInfo.text || questionInfo.questionText || '';
2929
+ if (questionText.length > 50) {
2930
+ displayName += ': ' + questionText.substring(0, 47) + '...';
2931
+ } else if (questionText.length > 0) {
2932
+ displayName += ': ' + questionText;
2933
+ }
2934
+
2913
2935
  return {
2914
2936
  objectType: 'Activity',
2915
2937
  id: 'http://xyleme.com/bravais/question/' + questionGuid,
2916
2938
  definition: {
2917
2939
  type: XYLEME_ACTIVITY_TYPES.question,
2918
- name: { 'en-US': questionInfo.text || questionInfo.questionText || 'Question' },
2940
+ name: { 'en-US': displayName },
2941
+ description: { 'en-US': questionText || 'Question' },
2942
+ extensions: {
2943
+ resourceType: 'Question',
2944
+ questionNumber: questionInfo.questionNumber || null
2945
+ }
2946
+ }
2947
+ };
2948
+ }
2949
+
2950
+ /**
2951
+ * Build activity object for media (video/audio) with human-readable name
2952
+ * Name format: "Video: Title" or "Audio: Title"
2953
+ */
2954
+ function buildMediaActivityObject(mediaInfo) {
2955
+ var mediaGuid = mediaInfo.mediaGuid || generateUUID();
2956
+ var mediaType = mediaInfo.type === 'audio' ? ACTIVITY_TYPES.audio : ACTIVITY_TYPES.video;
2957
+ var resourceType = mediaInfo.type === 'audio' ? 'Audio' : 'Video';
2958
+
2959
+ // Build human-readable display name
2960
+ var displayName = mediaInfo.name || '';
2961
+ if (!displayName || displayName === 'video' || displayName === 'audio' || displayName === 'Media') {
2962
+ displayName = resourceType + ': ' + (mediaInfo.lessonName || 'Media');
2963
+ } else {
2964
+ displayName = resourceType + ': ' + displayName;
2965
+ }
2966
+
2967
+ return {
2968
+ objectType: 'Activity',
2969
+ id: 'http://xyleme.com/bravais/media/' + mediaGuid,
2970
+ definition: {
2971
+ type: mediaType,
2972
+ name: { 'en-US': displayName },
2973
+ extensions: {
2974
+ resourceType: resourceType,
2975
+ mediaSrc: mediaInfo.src || '',
2976
+ mediaType: mediaInfo.type || 'video',
2977
+ duration: mediaInfo.duration || 0
2978
+ }
2979
+ }
2980
+ };
2981
+ }
2982
+
2983
+ /**
2984
+ * Build activity object for interactions (tabs, accordions, flashcards, etc.)
2985
+ * Name format: "Tab: Label" or "Accordion: Label"
2986
+ */
2987
+ function buildInteractionActivityObject(interactionInfo) {
2988
+ var typeLabels = {
2989
+ 'tab': 'Tab',
2990
+ 'accordion': 'Accordion',
2991
+ 'flashcard': 'Flashcard',
2992
+ 'hotspot': 'Hotspot',
2993
+ 'process-step': 'Process Step',
2994
+ 'process': 'Process Step',
2995
+ 'sorting': 'Sorting Activity',
2996
+ 'button': 'Button',
2997
+ 'labeled-graphic': 'Labeled Graphic',
2998
+ 'timeline': 'Timeline',
2999
+ 'scenario': 'Scenario',
3000
+ 'checklist': 'Checklist',
3001
+ 'marker': 'Marker'
3002
+ };
3003
+
3004
+ var interactionType = interactionInfo.type || interactionInfo.interactionType || 'interaction';
3005
+ var typeLabel = typeLabels[interactionType] || 'Interaction';
3006
+
3007
+ // Build human-readable display name
3008
+ var displayName = interactionInfo.name;
3009
+ if (!displayName) {
3010
+ displayName = typeLabel + ': ' + (interactionInfo.label || interactionInfo.id || 'Item');
3011
+ }
3012
+
3013
+ return {
3014
+ objectType: 'Activity',
3015
+ id: 'http://xyleme.com/bravais/interaction/' + (interactionInfo.id || generateUUID()),
3016
+ definition: {
3017
+ type: ACTIVITY_TYPES.interaction,
3018
+ name: { 'en-US': displayName },
2919
3019
  extensions: {
2920
- resourceType: 'Question'
3020
+ resourceType: 'Interaction',
3021
+ interactionType: interactionType
2921
3022
  }
2922
3023
  }
2923
3024
  };
@@ -3124,7 +3225,8 @@ function generateLrsBridgeCode(options) {
3124
3225
  sendStatement(statement);
3125
3226
  };
3126
3227
 
3127
- // Content/Page viewed - uses document as object for Bravais aggregation
3228
+ // Content/Page viewed - uses page-specific object for human-readable display
3229
+ // Object shows page/lesson title, parent context maintains document aggregation
3128
3230
  LRS.contentOpened = function(data) {
3129
3231
  if (!TRACK_NAVIGATION) return;
3130
3232
 
@@ -3144,63 +3246,62 @@ function generateLrsBridgeCode(options) {
3144
3246
  lessonId = lessonId.substring(2); // Remove '#/'
3145
3247
  }
3146
3248
 
3147
- // Add page info to context extensions (not as main object)
3249
+ // Get the page title - prefer lesson name from DOM
3250
+ var pageTitle = data.title || lessonInfo.name || 'Page';
3251
+
3252
+ // Build page activity object with human-readable name
3253
+ // This shows the page/lesson title in the Object column
3254
+ var pageObject = buildPageActivityObject({
3255
+ pageGuid: lessonId,
3256
+ name: pageTitle,
3257
+ resourceType: 'Lesson'
3258
+ });
3259
+
3260
+ // Add page info to context extensions
3148
3261
  var additionalContext = {
3149
3262
  extensions: {
3150
3263
  pageId: lessonId || 'home',
3151
- pageTitle: data.title || lessonInfo.name || 'Page',
3264
+ pageTitle: pageTitle,
3152
3265
  lessonName: lessonInfo.name,
3153
3266
  sectionName: lessonInfo.sectionName,
3154
3267
  pageUrl: data.url || window.location.href
3155
3268
  }
3156
3269
  };
3157
3270
 
3158
- // Use document as main object (required for Bravais aggregation)
3159
- // Use 'experienced' verb which is standard xAPI for viewing content
3160
- var statement = buildStatement(
3161
- 'experienced',
3162
- 'http://xyleme.com/bravais/activities/document',
3163
- {
3164
- event: 'page_viewed',
3165
- pageId: lessonId,
3166
- lessonName: lessonInfo.name,
3167
- sectionName: lessonInfo.sectionName
3168
- },
3169
- result,
3170
- additionalContext
3171
- );
3271
+ // Use 'experienced' verb for viewing content
3272
+ // Parent course activity added by buildXylemeContext ensures document aggregation
3273
+ var statement = buildStatementXyleme('experienced', pageObject, result, additionalContext);
3172
3274
  sendStatement(statement);
3173
3275
  };
3174
3276
 
3175
- // Media events
3277
+ // Media events - uses activity-specific object with human-readable name
3278
+ // Parent context (added by buildXylemeContext) ensures document aggregation
3176
3279
  LRS.mediaPlayed = function(data) {
3177
3280
  if (!TRACK_MEDIA) return;
3178
3281
 
3179
3282
  // Get current lesson context
3180
3283
  var lessonInfo = getCachedLessonInfo();
3181
3284
 
3182
- var mediaType = data.type === 'audio' ? ACTIVITY_TYPES.audio : ACTIVITY_TYPES.video;
3183
3285
  var verbKey = data.action === 'play' ? 'played' :
3184
3286
  data.action === 'pause' ? 'paused' :
3185
3287
  data.action === 'completed' ? 'completed' : 'played';
3186
3288
 
3187
- // Activity type is video/audio, details include media info
3188
- var activityDetails = {
3189
- mediaSrc: data.src || 'media-' + Date.now(),
3190
- mediaName: data.name || (data.type || 'Media'),
3191
- mediaType: data.type || 'video',
3192
- 'https://w3id.org/xapi/video/extensions/length': data.duration || 0,
3193
- // Include lesson/section context
3194
- lessonId: lessonInfo.id,
3195
- lessonName: lessonInfo.name,
3196
- sectionName: lessonInfo.sectionName
3197
- };
3289
+ // Build activity-specific object with human-readable name
3290
+ // e.g., "Video: Introduction" or "Audio: Podcast Episode 1"
3291
+ var mediaObject = buildMediaActivityObject({
3292
+ type: data.type,
3293
+ src: data.src,
3294
+ name: data.name,
3295
+ duration: data.duration,
3296
+ lessonName: lessonInfo.name
3297
+ });
3198
3298
 
3199
3299
  var result = {
3200
3300
  extensions: {
3201
3301
  'https://w3id.org/xapi/video/extensions/time': data.currentTime || 0,
3202
3302
  'https://w3id.org/xapi/video/extensions/progress': data.duration > 0 ?
3203
- Math.round((data.currentTime / data.duration) * 100) / 100 : 0
3303
+ Math.round((data.currentTime / data.duration) * 100) / 100 : 0,
3304
+ 'https://w3id.org/xapi/video/extensions/length': data.duration || 0
3204
3305
  }
3205
3306
  };
3206
3307
 
@@ -3208,7 +3309,16 @@ function generateLrsBridgeCode(options) {
3208
3309
  result.completion = true;
3209
3310
  }
3210
3311
 
3211
- var statement = buildStatement(verbKey, mediaType, activityDetails, result);
3312
+ // Context includes lesson info; parent course activity added by buildXylemeContext
3313
+ var additionalContext = {
3314
+ extensions: {
3315
+ lessonId: lessonInfo.id,
3316
+ lessonName: lessonInfo.name,
3317
+ sectionName: lessonInfo.sectionName
3318
+ }
3319
+ };
3320
+
3321
+ var statement = buildStatementXyleme(verbKey, mediaObject, result, additionalContext);
3212
3322
  sendStatement(statement);
3213
3323
  };
3214
3324
 
@@ -3340,27 +3450,35 @@ function generateLrsBridgeCode(options) {
3340
3450
  LRS.assessmentEnded(data);
3341
3451
  };
3342
3452
 
3343
- // Interactions (tabs, accordions, etc.)
3453
+ // Interactions (tabs, accordions, etc.) - uses activity-specific object
3454
+ // Object name shows the interaction type and label, e.g., "Tab: Overview"
3455
+ // Parent context ensures these appear in document search results
3344
3456
  LRS.interacted = function(data) {
3345
3457
  if (!TRACK_INTERACTIONS) return;
3346
3458
 
3347
3459
  // Get current lesson context
3348
3460
  var lessonInfo = getCachedLessonInfo();
3349
3461
 
3350
- // Activity type is interaction, details include interaction ID and type
3351
- var statement = buildStatement(
3352
- 'interacted',
3353
- ACTIVITY_TYPES.interaction,
3354
- {
3462
+ // Build activity-specific object with human-readable name
3463
+ // e.g., "Tab: Getting Started", "Accordion: FAQ"
3464
+ var interactionObject = buildInteractionActivityObject({
3465
+ type: data.type || data.interactionType,
3466
+ id: data.id,
3467
+ name: data.name,
3468
+ label: data.name || data.type
3469
+ });
3470
+
3471
+ // Context includes lesson info; parent course activity added by buildXylemeContext
3472
+ var additionalContext = {
3473
+ extensions: {
3355
3474
  interactionId: data.id || 'interaction-' + Date.now(),
3356
- interactionName: data.name || data.type || 'Interaction',
3357
- interactionType: data.interactionType || data.type || 'other',
3358
- // Include lesson/section context
3359
3475
  lessonId: lessonInfo.id,
3360
3476
  lessonName: lessonInfo.name,
3361
3477
  sectionName: lessonInfo.sectionName
3362
3478
  }
3363
- );
3479
+ };
3480
+
3481
+ var statement = buildStatementXyleme('interacted', interactionObject, null, additionalContext);
3364
3482
  sendStatement(statement);
3365
3483
  };
3366
3484