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