@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/index.cjs CHANGED
@@ -2535,6 +2535,15 @@ function generateLrsBridgeCode(options) {
2535
2535
  ctx.extensions['http://xyleme.com/bravais/extensions/statement-schema'] = 'xyleme_10';
2536
2536
  ctx.extensions['http://xyleme.com/bravais/extensions/protect_pii'] = false;
2537
2537
 
2538
+ // Add parent course activity for document-level aggregation in Bravais
2539
+ // This allows statements with activity-specific objects to still be found
2540
+ // when searching by document name in Analytics
2541
+ var parentActivity = buildParentCourseActivity();
2542
+ if (parentActivity) {
2543
+ ctx.contextActivities = ctx.contextActivities || {};
2544
+ ctx.contextActivities.parent = [parentActivity];
2545
+ }
2546
+
2538
2547
  return ctx;
2539
2548
  }
2540
2549
 
@@ -2579,18 +2588,110 @@ function generateLrsBridgeCode(options) {
2579
2588
 
2580
2589
  /**
2581
2590
  * Build activity object for questions in Xyleme format
2591
+ * Name format: "Assessment Name - Q#: Question text..."
2582
2592
  */
2583
2593
  function buildQuestionActivityObject(questionInfo) {
2584
2594
  var questionGuid = questionInfo.questionGuid || questionInfo.id || generateUUID();
2585
2595
 
2596
+ // Build human-readable display name
2597
+ var displayName = (questionInfo.assessmentName || 'Knowledge Check');
2598
+ if (questionInfo.questionNumber) {
2599
+ displayName += ' - Q' + questionInfo.questionNumber;
2600
+ }
2601
+ var questionText = questionInfo.text || questionInfo.questionText || '';
2602
+ if (questionText.length > 50) {
2603
+ displayName += ': ' + questionText.substring(0, 47) + '...';
2604
+ } else if (questionText.length > 0) {
2605
+ displayName += ': ' + questionText;
2606
+ }
2607
+
2586
2608
  return {
2587
2609
  objectType: 'Activity',
2588
2610
  id: 'http://xyleme.com/bravais/question/' + questionGuid,
2589
2611
  definition: {
2590
2612
  type: XYLEME_ACTIVITY_TYPES.question,
2591
- name: { 'en-US': questionInfo.text || questionInfo.questionText || 'Question' },
2613
+ name: { 'en-US': displayName },
2614
+ description: { 'en-US': questionText || 'Question' },
2615
+ extensions: {
2616
+ resourceType: 'Question',
2617
+ questionNumber: questionInfo.questionNumber || null
2618
+ }
2619
+ }
2620
+ };
2621
+ }
2622
+
2623
+ /**
2624
+ * Build activity object for media (video/audio) with human-readable name
2625
+ * Name format: "Video: Title" or "Audio: Title"
2626
+ */
2627
+ function buildMediaActivityObject(mediaInfo) {
2628
+ var mediaGuid = mediaInfo.mediaGuid || generateUUID();
2629
+ var mediaType = mediaInfo.type === 'audio' ? ACTIVITY_TYPES.audio : ACTIVITY_TYPES.video;
2630
+ var resourceType = mediaInfo.type === 'audio' ? 'Audio' : 'Video';
2631
+
2632
+ // Build human-readable display name
2633
+ var displayName = mediaInfo.name || '';
2634
+ if (!displayName || displayName === 'video' || displayName === 'audio' || displayName === 'Media') {
2635
+ displayName = resourceType + ': ' + (mediaInfo.lessonName || 'Media');
2636
+ } else {
2637
+ displayName = resourceType + ': ' + displayName;
2638
+ }
2639
+
2640
+ return {
2641
+ objectType: 'Activity',
2642
+ id: 'http://xyleme.com/bravais/media/' + mediaGuid,
2643
+ definition: {
2644
+ type: mediaType,
2645
+ name: { 'en-US': displayName },
2646
+ extensions: {
2647
+ resourceType: resourceType,
2648
+ mediaSrc: mediaInfo.src || '',
2649
+ mediaType: mediaInfo.type || 'video',
2650
+ duration: mediaInfo.duration || 0
2651
+ }
2652
+ }
2653
+ };
2654
+ }
2655
+
2656
+ /**
2657
+ * Build activity object for interactions (tabs, accordions, flashcards, etc.)
2658
+ * Name format: "Tab: Label" or "Accordion: Label"
2659
+ */
2660
+ function buildInteractionActivityObject(interactionInfo) {
2661
+ var typeLabels = {
2662
+ 'tab': 'Tab',
2663
+ 'accordion': 'Accordion',
2664
+ 'flashcard': 'Flashcard',
2665
+ 'hotspot': 'Hotspot',
2666
+ 'process-step': 'Process Step',
2667
+ 'process': 'Process Step',
2668
+ 'sorting': 'Sorting Activity',
2669
+ 'button': 'Button',
2670
+ 'labeled-graphic': 'Labeled Graphic',
2671
+ 'timeline': 'Timeline',
2672
+ 'scenario': 'Scenario',
2673
+ 'checklist': 'Checklist',
2674
+ 'marker': 'Marker'
2675
+ };
2676
+
2677
+ var interactionType = interactionInfo.type || interactionInfo.interactionType || 'interaction';
2678
+ var typeLabel = typeLabels[interactionType] || 'Interaction';
2679
+
2680
+ // Build human-readable display name
2681
+ var displayName = interactionInfo.name;
2682
+ if (!displayName) {
2683
+ displayName = typeLabel + ': ' + (interactionInfo.label || interactionInfo.id || 'Item');
2684
+ }
2685
+
2686
+ return {
2687
+ objectType: 'Activity',
2688
+ id: 'http://xyleme.com/bravais/interaction/' + (interactionInfo.id || generateUUID()),
2689
+ definition: {
2690
+ type: ACTIVITY_TYPES.interaction,
2691
+ name: { 'en-US': displayName },
2592
2692
  extensions: {
2593
- resourceType: 'Question'
2693
+ resourceType: 'Interaction',
2694
+ interactionType: interactionType
2594
2695
  }
2595
2696
  }
2596
2697
  };
@@ -2797,7 +2898,8 @@ function generateLrsBridgeCode(options) {
2797
2898
  sendStatement(statement);
2798
2899
  };
2799
2900
 
2800
- // Content/Page viewed - uses document as object for Bravais aggregation
2901
+ // Content/Page viewed - uses page-specific object for human-readable display
2902
+ // Object shows page/lesson title, parent context maintains document aggregation
2801
2903
  LRS.contentOpened = function(data) {
2802
2904
  if (!TRACK_NAVIGATION) return;
2803
2905
 
@@ -2817,63 +2919,62 @@ function generateLrsBridgeCode(options) {
2817
2919
  lessonId = lessonId.substring(2); // Remove '#/'
2818
2920
  }
2819
2921
 
2820
- // Add page info to context extensions (not as main object)
2922
+ // Get the page title - prefer lesson name from DOM
2923
+ var pageTitle = data.title || lessonInfo.name || 'Page';
2924
+
2925
+ // Build page activity object with human-readable name
2926
+ // This shows the page/lesson title in the Object column
2927
+ var pageObject = buildPageActivityObject({
2928
+ pageGuid: lessonId,
2929
+ name: pageTitle,
2930
+ resourceType: 'Lesson'
2931
+ });
2932
+
2933
+ // Add page info to context extensions
2821
2934
  var additionalContext = {
2822
2935
  extensions: {
2823
2936
  pageId: lessonId || 'home',
2824
- pageTitle: data.title || lessonInfo.name || 'Page',
2937
+ pageTitle: pageTitle,
2825
2938
  lessonName: lessonInfo.name,
2826
2939
  sectionName: lessonInfo.sectionName,
2827
2940
  pageUrl: data.url || window.location.href
2828
2941
  }
2829
2942
  };
2830
2943
 
2831
- // Use document as main object (required for Bravais aggregation)
2832
- // Use 'experienced' verb which is standard xAPI for viewing content
2833
- var statement = buildStatement(
2834
- 'experienced',
2835
- 'http://xyleme.com/bravais/activities/document',
2836
- {
2837
- event: 'page_viewed',
2838
- pageId: lessonId,
2839
- lessonName: lessonInfo.name,
2840
- sectionName: lessonInfo.sectionName
2841
- },
2842
- result,
2843
- additionalContext
2844
- );
2944
+ // Use 'experienced' verb for viewing content
2945
+ // Parent course activity added by buildXylemeContext ensures document aggregation
2946
+ var statement = buildStatementXyleme('experienced', pageObject, result, additionalContext);
2845
2947
  sendStatement(statement);
2846
2948
  };
2847
2949
 
2848
- // Media events
2950
+ // Media events - uses activity-specific object with human-readable name
2951
+ // Parent context (added by buildXylemeContext) ensures document aggregation
2849
2952
  LRS.mediaPlayed = function(data) {
2850
2953
  if (!TRACK_MEDIA) return;
2851
2954
 
2852
2955
  // Get current lesson context
2853
2956
  var lessonInfo = getCachedLessonInfo();
2854
2957
 
2855
- var mediaType = data.type === 'audio' ? ACTIVITY_TYPES.audio : ACTIVITY_TYPES.video;
2856
2958
  var verbKey = data.action === 'play' ? 'played' :
2857
2959
  data.action === 'pause' ? 'paused' :
2858
2960
  data.action === 'completed' ? 'completed' : 'played';
2859
2961
 
2860
- // Activity type is video/audio, details include media info
2861
- var activityDetails = {
2862
- mediaSrc: data.src || 'media-' + Date.now(),
2863
- mediaName: data.name || (data.type || 'Media'),
2864
- mediaType: data.type || 'video',
2865
- 'https://w3id.org/xapi/video/extensions/length': data.duration || 0,
2866
- // Include lesson/section context
2867
- lessonId: lessonInfo.id,
2868
- lessonName: lessonInfo.name,
2869
- sectionName: lessonInfo.sectionName
2870
- };
2962
+ // Build activity-specific object with human-readable name
2963
+ // e.g., "Video: Introduction" or "Audio: Podcast Episode 1"
2964
+ var mediaObject = buildMediaActivityObject({
2965
+ type: data.type,
2966
+ src: data.src,
2967
+ name: data.name,
2968
+ duration: data.duration,
2969
+ lessonName: lessonInfo.name
2970
+ });
2871
2971
 
2872
2972
  var result = {
2873
2973
  extensions: {
2874
2974
  'https://w3id.org/xapi/video/extensions/time': data.currentTime || 0,
2875
2975
  'https://w3id.org/xapi/video/extensions/progress': data.duration > 0 ?
2876
- Math.round((data.currentTime / data.duration) * 100) / 100 : 0
2976
+ Math.round((data.currentTime / data.duration) * 100) / 100 : 0,
2977
+ 'https://w3id.org/xapi/video/extensions/length': data.duration || 0
2877
2978
  }
2878
2979
  };
2879
2980
 
@@ -2881,7 +2982,16 @@ function generateLrsBridgeCode(options) {
2881
2982
  result.completion = true;
2882
2983
  }
2883
2984
 
2884
- var statement = buildStatement(verbKey, mediaType, activityDetails, result);
2985
+ // Context includes lesson info; parent course activity added by buildXylemeContext
2986
+ var additionalContext = {
2987
+ extensions: {
2988
+ lessonId: lessonInfo.id,
2989
+ lessonName: lessonInfo.name,
2990
+ sectionName: lessonInfo.sectionName
2991
+ }
2992
+ };
2993
+
2994
+ var statement = buildStatementXyleme(verbKey, mediaObject, result, additionalContext);
2885
2995
  sendStatement(statement);
2886
2996
  };
2887
2997
 
@@ -3013,27 +3123,35 @@ function generateLrsBridgeCode(options) {
3013
3123
  LRS.assessmentEnded(data);
3014
3124
  };
3015
3125
 
3016
- // Interactions (tabs, accordions, etc.)
3126
+ // Interactions (tabs, accordions, etc.) - uses activity-specific object
3127
+ // Object name shows the interaction type and label, e.g., "Tab: Overview"
3128
+ // Parent context ensures these appear in document search results
3017
3129
  LRS.interacted = function(data) {
3018
3130
  if (!TRACK_INTERACTIONS) return;
3019
3131
 
3020
3132
  // Get current lesson context
3021
3133
  var lessonInfo = getCachedLessonInfo();
3022
3134
 
3023
- // Activity type is interaction, details include interaction ID and type
3024
- var statement = buildStatement(
3025
- 'interacted',
3026
- ACTIVITY_TYPES.interaction,
3027
- {
3135
+ // Build activity-specific object with human-readable name
3136
+ // e.g., "Tab: Getting Started", "Accordion: FAQ"
3137
+ var interactionObject = buildInteractionActivityObject({
3138
+ type: data.type || data.interactionType,
3139
+ id: data.id,
3140
+ name: data.name,
3141
+ label: data.name || data.type
3142
+ });
3143
+
3144
+ // Context includes lesson info; parent course activity added by buildXylemeContext
3145
+ var additionalContext = {
3146
+ extensions: {
3028
3147
  interactionId: data.id || 'interaction-' + Date.now(),
3029
- interactionName: data.name || data.type || 'Interaction',
3030
- interactionType: data.interactionType || data.type || 'other',
3031
- // Include lesson/section context
3032
3148
  lessonId: lessonInfo.id,
3033
3149
  lessonName: lessonInfo.name,
3034
3150
  sectionName: lessonInfo.sectionName
3035
3151
  }
3036
- );
3152
+ };
3153
+
3154
+ var statement = buildStatementXyleme('interacted', interactionObject, null, additionalContext);
3037
3155
  sendStatement(statement);
3038
3156
  };
3039
3157