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