musora-content-services 2.101.1 → 2.102.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [2.102.1](https://github.com/railroadmedia/musora-content-services/compare/v2.102.0...v2.102.1) (2025-12-10)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **agi:** offset default and slug clause on artistBySlug ([10cc6d0](https://github.com/railroadmedia/musora-content-services/commit/10cc6d0390a12715aeba01c5dc72ecdb53d0f236))
11
+
12
+ ## [2.102.0](https://github.com/railroadmedia/musora-content-services/compare/v2.101.1...v2.102.0) (2025-12-10)
13
+
14
+
15
+ ### Features
16
+
17
+ * **BEH-1457:** method progress duplication ([#617](https://github.com/railroadmedia/musora-content-services/issues/617)) ([917cb9d](https://github.com/railroadmedia/musora-content-services/commit/917cb9dedce2a21c64af4e39eef013a30b236e77))
18
+
5
19
  ### [2.101.1](https://github.com/railroadmedia/musora-content-services/compare/v2.101.0...v2.101.1) (2025-12-10)
6
20
 
7
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.101.1",
3
+ "version": "2.102.1",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -89,7 +89,7 @@ export async function fetchArtistBySlug(
89
89
 
90
90
  const q = query()
91
91
  .and(`_type == "artist"`)
92
- .and(`&& slug.current == '${slug}'`)
92
+ .and(`slug.current == '${slug}'`)
93
93
  .select(
94
94
  'name',
95
95
  `"slug": slug.current`,
@@ -139,7 +139,7 @@ export async function fetchArtistLessons(
139
139
  {
140
140
  sort = '-published_on',
141
141
  searchTerm = '',
142
- offset = 1,
142
+ offset = 0,
143
143
  limit = 10,
144
144
  includedFields = [],
145
145
  progressIds = [],
@@ -138,7 +138,7 @@ export async function fetchGenreLessons(
138
138
  {
139
139
  sort = '-published_on',
140
140
  searchTerm = '',
141
- offset = 1,
141
+ offset = 0,
142
142
  limit = 10,
143
143
  includedFields = [],
144
144
  progressIds = [],
@@ -138,7 +138,7 @@ export async function fetchInstructorLessons(
138
138
  {
139
139
  sort = '-published_on',
140
140
  searchTerm = '',
141
- offset = 1,
141
+ offset = 0,
142
142
  limit = 20,
143
143
  includedFields = [],
144
144
  }: InstructorLessonsOptions = {}
@@ -388,30 +388,29 @@ async function resetIfPossible(contentId: number, collection: CollectionParamete
388
388
  }
389
389
 
390
390
  export async function onContentCompletedLearningPathListener(event) {
391
- console.log('if')
392
- if (event?.collection?.type !== 'learning-path-v2') return
391
+ if (event?.collection?.type !== COLLECTION_TYPE.LEARNING_PATH) return
393
392
  if (event.contentId !== event?.collection?.id) return
393
+
394
394
  const learningPathId = event.contentId
395
395
  const learningPath = await getEnrichedLearningPath(learningPathId)
396
- console.log('LP', learningPath)
396
+
397
397
  const brand = learningPath.brand
398
398
  const activeLearningPath = await getActivePath(brand)
399
- console.log('Active LP', activeLearningPath)
399
+
400
400
  if (activeLearningPath.active_learning_path_id !== learningPathId) return
401
401
  const method = await fetchMethodV2Structure(brand)
402
- console.log('Method', method)
402
+
403
403
  const currentIndex = method.learning_paths.findIndex((lp) => lp.id === learningPathId)
404
404
  if (currentIndex === -1) {
405
405
  return
406
406
  }
407
407
  const nextLearningPath = method.learning_paths[currentIndex + 1]
408
- console.log('Next LP', nextLearningPath)
409
408
  if (!nextLearningPath) {
410
409
  return
411
410
  }
412
411
 
413
412
  await startLearningPath(brand, nextLearningPath.id)
414
413
  const nextLearningPathData = await getEnrichedLearningPath(nextLearningPath.id)
415
- console.log('Next LP Data', nextLearningPathData)
414
+
416
415
  await contentStatusReset(nextLearningPathData.intro_video.id)
417
416
  }
@@ -12,7 +12,7 @@ const STATE_COMPLETED = STATE.COMPLETED
12
12
  const MAX_DEPTH = 3
13
13
 
14
14
  export async function getProgressState(contentId, collection = null) {
15
- return getById(contentId, collection, 'state', '')
15
+ return getById(normalizeContentId(contentId), normalizeCollection(collection), 'state', '')
16
16
  }
17
17
 
18
18
  export async function getProgressStateByIds(contentIds, collection = null) {
@@ -472,13 +472,17 @@ async function saveContentProgress(contentId, collection, progress, currentSecon
472
472
  // note - previous implementation explicitly did not trickle progress to children here
473
473
  // (only to siblings/parents via le bubbles)
474
474
 
475
- const bubbledProgresses = await bubbleProgress(
476
- await getHierarchy(contentId, collection),
477
- contentId,
478
- collection
479
- )
475
+ const hierarchy = await getHierarchy(contentId, collection)
476
+
477
+ const bubbledProgresses = await bubbleProgress(hierarchy, contentId, collection)
480
478
  await db.contentProgress.recordProgressesTentative(bubbledProgresses, collection)
481
479
 
480
+ if (collection && collection.type === COLLECTION_TYPE.LEARNING_PATH) {
481
+ let exportIds = bubbledProgresses
482
+ exportIds[contentId] = progress
483
+ await duplicateLearningPathProgressToExternalContents(exportIds, collection, hierarchy)
484
+ }
485
+
482
486
  for (const [bubbledContentId, bubbledProgress] of Object.entries(bubbledProgresses)) {
483
487
  if (bubbledProgress === 100) {
484
488
  emitContentCompleted(Number(bubbledContentId), collection)
@@ -490,25 +494,56 @@ async function saveContentProgress(contentId, collection, progress, currentSecon
490
494
  async function setStartedOrCompletedStatus(contentId, collection, isCompleted) {
491
495
  const progress = isCompleted ? 100 : 0
492
496
  const response = await db.contentProgress.recordProgress(contentId, collection, progress)
497
+
493
498
  if (progress === 100) emitContentCompleted(contentId, collection)
499
+
494
500
  const hierarchy = await getHierarchy(contentId, collection)
495
- await Promise.all([
496
- db.contentProgress.recordProgressesTentative(
497
- trickleProgress(hierarchy, contentId, collection, progress),
498
- collection
499
- ),
500
- bubbleProgress(hierarchy, contentId, collection).then(async (bubbledProgresses) => {
501
- await db.contentProgress.recordProgressesTentative(bubbledProgresses, collection)
502
- for (const [bubbledContentId, bubbledProgress] of Object.entries(bubbledProgresses)) {
503
- if (bubbledProgress === 100) {
504
- emitContentCompleted(Number(bubbledContentId), collection)
505
- }
506
- }
507
- }),
508
- ])
501
+
502
+ let ids = {
503
+ ...trickleProgress(hierarchy, contentId, collection, progress),
504
+ ...await bubbleProgress(hierarchy, contentId, collection)
505
+ }
506
+ await db.contentProgress.recordProgressesTentative(ids, collection)
507
+
508
+ if (collection && collection.type === COLLECTION_TYPE.LEARNING_PATH) {
509
+ let exportIds = ids
510
+ exportIds[contentId] = progress
511
+ await duplicateLearningPathProgressToExternalContents(exportIds, collection, hierarchy)
512
+ }
513
+
514
+ for (const [id, progress] of Object.entries(ids)) {
515
+ if (progress === 100) {
516
+ emitContentCompleted(Number(id), collection)
517
+ }
518
+ }
519
+
509
520
  return response
510
521
  }
511
522
 
523
+ async function duplicateLearningPathProgressToExternalContents(ids, collection, hierarchy) {
524
+ // filter out LPs. we dont want to duplicate to LP's while we dont have a-la-cart LP's set up.
525
+ let filteredIds = Object.fromEntries(
526
+ Object.entries(ids).filter((id) => {
527
+ return hierarchy.parents[parseInt(id)] !== null
528
+ })
529
+ )
530
+
531
+ const extProgresses = await getProgressDataByIds(Object.keys(filteredIds), null)
532
+
533
+ // overwrite if LP progress greater, unless LP progress was reset to 0
534
+ filteredIds = Object.entries(filteredIds).filter(([id, pct]) => {
535
+ const extPct = extProgresses[id]?.progress
536
+ return (pct !== 0)
537
+ ? pct > extPct
538
+ : false
539
+ })
540
+
541
+ // each handles its own bubbling.
542
+ filteredIds.forEach(([id, pct]) => {
543
+ saveContentProgress(parseInt(id), null, pct)
544
+ })
545
+ }
546
+
512
547
  async function getHierarchy(contentId, collection) {
513
548
  if (collection && collection.type === COLLECTION_TYPE.LEARNING_PATH) {
514
549
  return await fetchLearningPathHierarchy(contentId, collection)
@@ -519,13 +554,10 @@ async function getHierarchy(contentId, collection) {
519
554
 
520
555
  async function setStartedOrCompletedStatuses(contentIds, collection, isCompleted) {
521
556
  const progress = isCompleted ? 100 : 0
522
- // we explicitly pessimistically await a remote push here
523
- // because awards may be generated (on server) on completion
524
- // which we would want to toast the user about *in band*
525
557
  const response = await db.contentProgress.recordProgresses(contentIds, collection, progress)
526
558
 
527
559
  // we assume this is used only for contents within the same hierarchy
528
- const hierarchy = await getHierarchy(contentIds[0], collection)
560
+ const hierarchy = await getHierarchy(collection.id, collection)
529
561
 
530
562
  let ids = {}
531
563
  for (const contentId of contentIds) {
@@ -535,25 +567,34 @@ async function setStartedOrCompletedStatuses(contentIds, collection, isCompleted
535
567
  ...(await bubbleProgress(hierarchy, contentId, collection)),
536
568
  }
537
569
  }
570
+ await db.contentProgress.recordProgressesTentative(ids, collection)
538
571
 
539
- await Promise.all([db.contentProgress.recordProgressesTentative(ids, collection)])
572
+ if (collection && collection.type === COLLECTION_TYPE.LEARNING_PATH) {
573
+ let exportIds = ids
574
+ for (const contentId of contentIds){
575
+ exportIds[contentId] = progress
576
+ }
577
+ await duplicateLearningPathProgressToExternalContents(exportIds, collection, hierarchy)
578
+ }
540
579
 
541
580
  return response
542
581
  }
543
582
 
544
583
  async function resetStatus(contentId, collection = null) {
584
+ const progress = 0
545
585
  const response = await db.contentProgress.eraseProgress(contentId, collection)
546
586
  const hierarchy = await getHierarchy(contentId, collection)
547
587
 
548
- await Promise.all([
549
- db.contentProgress.recordProgressesTentative(
550
- trickleProgress(hierarchy, contentId, collection, 0),
551
- collection
552
- ),
553
- bubbleProgress(hierarchy, contentId, collection).then((bubbledProgresses) =>
554
- db.contentProgress.recordProgressesTentative(bubbledProgresses, collection)
555
- ),
556
- ])
588
+ let ids = {
589
+ ...trickleProgress(hierarchy, contentId, collection, progress),
590
+ ...await bubbleProgress(hierarchy, contentId, collection)
591
+ }
592
+ await db.contentProgress.recordProgressesTentative(ids, collection)
593
+
594
+ if (collection && collection.type === COLLECTION_TYPE.LEARNING_PATH) {
595
+ ids[contentId] = progress
596
+ await duplicateLearningPathProgressToExternalContents(ids, collection, hierarchy)
597
+ }
557
598
 
558
599
  return response
559
600
  }