musora-content-services 2.77.2 → 2.78.0
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 +7 -0
- package/package.json +1 -1
- package/src/index.d.ts +8 -2
- package/src/index.js +8 -2
- package/src/services/content-org/learning-paths.ts +96 -11
- package/src/services/sanity.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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.78.0](https://github.com/railroadmedia/musora-content-services/compare/v2.77.2...v2.78.0) (2025-11-15)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **BEH-1409:** complete intro video ([#566](https://github.com/railroadmedia/musora-content-services/issues/566)) ([fb2fa96](https://github.com/railroadmedia/musora-content-services/commit/fb2fa96f408948cf4c3501709dc2ec2c17f23163))
|
|
11
|
+
|
|
5
12
|
### [2.77.2](https://github.com/railroadmedia/musora-content-services/compare/v2.77.1...v2.77.2) (2025-11-14)
|
|
6
13
|
|
|
7
14
|
### [2.77.1](https://github.com/railroadmedia/musora-content-services/compare/v2.77.0...v2.77.1) (2025-11-13)
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
} from './services/content-org/guided-courses.ts';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
+
completeLearningPathIntroVideo,
|
|
17
|
+
completeMethodIntroVideo,
|
|
16
18
|
fetchLearningPathLessons,
|
|
17
19
|
getActivePath,
|
|
18
20
|
getDailySession,
|
|
19
|
-
|
|
21
|
+
getEnrichedLearningPath,
|
|
20
22
|
getLearningPathLessonsByIds,
|
|
21
23
|
mapContentToParent,
|
|
22
24
|
resetAllLearningPaths,
|
|
@@ -132,6 +134,7 @@ import {
|
|
|
132
134
|
fetchThreads,
|
|
133
135
|
followThread,
|
|
134
136
|
lockThread,
|
|
137
|
+
markThreadAsRead,
|
|
135
138
|
pinThread,
|
|
136
139
|
unfollowThread,
|
|
137
140
|
unlockThread,
|
|
@@ -406,6 +409,8 @@ declare module 'musora-content-services' {
|
|
|
406
409
|
buildImageSRC,
|
|
407
410
|
calculateLongestStreaks,
|
|
408
411
|
closeComment,
|
|
412
|
+
completeLearningPathIntroVideo,
|
|
413
|
+
completeMethodIntroVideo,
|
|
409
414
|
confirmEmailChange,
|
|
410
415
|
contentStatusCompleted,
|
|
411
416
|
contentStatusReset,
|
|
@@ -542,8 +547,8 @@ declare module 'musora-content-services' {
|
|
|
542
547
|
getAwardDataForGuidedContent,
|
|
543
548
|
getContentRows,
|
|
544
549
|
getDailySession,
|
|
550
|
+
getEnrichedLearningPath,
|
|
545
551
|
getLastInteractedOf,
|
|
546
|
-
getLearningPath,
|
|
547
552
|
getLearningPathLessonsByIds,
|
|
548
553
|
getLegacyMethods,
|
|
549
554
|
getLessonContentRows,
|
|
@@ -604,6 +609,7 @@ declare module 'musora-content-services' {
|
|
|
604
609
|
markContentAsNotInterested,
|
|
605
610
|
markNotificationAsRead,
|
|
606
611
|
markNotificationAsUnread,
|
|
612
|
+
markThreadAsRead,
|
|
607
613
|
numberOfActiveUsers,
|
|
608
614
|
openComment,
|
|
609
615
|
otherStats,
|
package/src/index.js
CHANGED
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
} from './services/content-org/guided-courses.ts';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
+
completeLearningPathIntroVideo,
|
|
17
|
+
completeMethodIntroVideo,
|
|
16
18
|
fetchLearningPathLessons,
|
|
17
19
|
getActivePath,
|
|
18
20
|
getDailySession,
|
|
19
|
-
|
|
21
|
+
getEnrichedLearningPath,
|
|
20
22
|
getLearningPathLessonsByIds,
|
|
21
23
|
mapContentToParent,
|
|
22
24
|
resetAllLearningPaths,
|
|
@@ -132,6 +134,7 @@ import {
|
|
|
132
134
|
fetchThreads,
|
|
133
135
|
followThread,
|
|
134
136
|
lockThread,
|
|
137
|
+
markThreadAsRead,
|
|
135
138
|
pinThread,
|
|
136
139
|
unfollowThread,
|
|
137
140
|
unlockThread,
|
|
@@ -405,6 +408,8 @@ export {
|
|
|
405
408
|
buildImageSRC,
|
|
406
409
|
calculateLongestStreaks,
|
|
407
410
|
closeComment,
|
|
411
|
+
completeLearningPathIntroVideo,
|
|
412
|
+
completeMethodIntroVideo,
|
|
408
413
|
confirmEmailChange,
|
|
409
414
|
contentStatusCompleted,
|
|
410
415
|
contentStatusReset,
|
|
@@ -541,8 +546,8 @@ export {
|
|
|
541
546
|
getAwardDataForGuidedContent,
|
|
542
547
|
getContentRows,
|
|
543
548
|
getDailySession,
|
|
549
|
+
getEnrichedLearningPath,
|
|
544
550
|
getLastInteractedOf,
|
|
545
|
-
getLearningPath,
|
|
546
551
|
getLearningPathLessonsByIds,
|
|
547
552
|
getLegacyMethods,
|
|
548
553
|
getLessonContentRows,
|
|
@@ -603,6 +608,7 @@ export {
|
|
|
603
608
|
markContentAsNotInterested,
|
|
604
609
|
markNotificationAsRead,
|
|
605
610
|
markNotificationAsUnread,
|
|
611
|
+
markThreadAsRead,
|
|
606
612
|
numberOfActiveUsers,
|
|
607
613
|
openComment,
|
|
608
614
|
otherStats,
|
|
@@ -5,9 +5,25 @@
|
|
|
5
5
|
import { fetchHandler } from '../railcontent.js'
|
|
6
6
|
import { fetchByRailContentId, fetchByRailContentIds, fetchMethodV2Structure } from '../sanity.js'
|
|
7
7
|
import { addContextToContent } from '../contentAggregator.js'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
contentStatusCompleted,
|
|
10
|
+
contentStatusReset,
|
|
11
|
+
getProgressState,
|
|
12
|
+
getProgressStateByIds
|
|
13
|
+
} from '../contentProgress.js'
|
|
9
14
|
|
|
10
15
|
const BASE_PATH: string = `/api/content-org`
|
|
16
|
+
const LEARNING_PATHS_PATH = `${BASE_PATH}/v1/user/learning-paths`
|
|
17
|
+
|
|
18
|
+
interface ActiveLearningPathResponse {
|
|
19
|
+
user_id: number,
|
|
20
|
+
brand: string,
|
|
21
|
+
active_learning_path_id: number,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
11
27
|
|
|
12
28
|
/**
|
|
13
29
|
* Gets today's daily session for the user.
|
|
@@ -16,7 +32,7 @@ const BASE_PATH: string = `/api/content-org`
|
|
|
16
32
|
*/
|
|
17
33
|
export async function getDailySession(brand: string, userDate: Date) {
|
|
18
34
|
const stringDate = userDate.toISOString().split('T')[0]
|
|
19
|
-
const url: string = `${
|
|
35
|
+
const url: string = `${LEARNING_PATHS_PATH}/daily-session/get-or-create`
|
|
20
36
|
const body = { brand: brand, userDate: stringDate }
|
|
21
37
|
return await fetchHandler(url, 'POST', null, body)
|
|
22
38
|
}
|
|
@@ -33,7 +49,7 @@ export async function updateDailySession(
|
|
|
33
49
|
keepFirstLearningPath: boolean
|
|
34
50
|
) {
|
|
35
51
|
const stringDate = userDate.toISOString().split('T')[0]
|
|
36
|
-
const url: string = `${
|
|
52
|
+
const url: string = `${LEARNING_PATHS_PATH}/daily-session/update`
|
|
37
53
|
const body = { brand: brand, userDate: stringDate, keepFirstLearningPath: keepFirstLearningPath }
|
|
38
54
|
return await fetchHandler(url, 'POST', null, body)
|
|
39
55
|
}
|
|
@@ -43,17 +59,19 @@ export async function updateDailySession(
|
|
|
43
59
|
* @param brand
|
|
44
60
|
*/
|
|
45
61
|
export async function getActivePath(brand: string) {
|
|
46
|
-
const url: string = `${
|
|
62
|
+
const url: string = `${LEARNING_PATHS_PATH}/active-path/get-or-create`
|
|
47
63
|
const body = { brand: brand }
|
|
48
64
|
return await fetchHandler(url, 'POST', null, body)
|
|
49
65
|
}
|
|
50
66
|
|
|
67
|
+
// todo this should be removed once we handle active path gen only through
|
|
68
|
+
// finish method intro or complete current active path
|
|
51
69
|
/**
|
|
52
70
|
* Updates user's active learning path.
|
|
53
71
|
* @param brand
|
|
54
72
|
*/
|
|
55
73
|
export async function updateActivePath(brand: string) {
|
|
56
|
-
const url: string = `${
|
|
74
|
+
const url: string = `${LEARNING_PATHS_PATH}/active-path/update`
|
|
57
75
|
const body = { brand: brand }
|
|
58
76
|
return await fetchHandler(url, 'POST', null, body)
|
|
59
77
|
}
|
|
@@ -64,7 +82,7 @@ export async function updateActivePath(brand: string) {
|
|
|
64
82
|
* @param learningPathId
|
|
65
83
|
*/
|
|
66
84
|
export async function startLearningPath(brand: string, learningPathId: number) {
|
|
67
|
-
const url: string = `${
|
|
85
|
+
const url: string = `${LEARNING_PATHS_PATH}/start`
|
|
68
86
|
const body = { brand: brand, learning_path_id: learningPathId }
|
|
69
87
|
return await fetchHandler(url, 'POST', null, body)
|
|
70
88
|
}
|
|
@@ -73,7 +91,7 @@ export async function startLearningPath(brand: string, learningPathId: number) {
|
|
|
73
91
|
* Resets the user's learning path.
|
|
74
92
|
*/
|
|
75
93
|
export async function resetAllLearningPaths() {
|
|
76
|
-
const url: string = `${
|
|
94
|
+
const url: string = `${LEARNING_PATHS_PATH}/reset`
|
|
77
95
|
return await fetchHandler(url, 'POST', null, {})
|
|
78
96
|
}
|
|
79
97
|
|
|
@@ -82,8 +100,8 @@ export async function resetAllLearningPaths() {
|
|
|
82
100
|
* @param {number} learningPathId - The learning path ID
|
|
83
101
|
* @returns {Promise<Object>} Learning path with enriched lesson data
|
|
84
102
|
*/
|
|
85
|
-
export async function
|
|
86
|
-
//TODO:
|
|
103
|
+
export async function getEnrichedLearningPath(learningPathId) {
|
|
104
|
+
//TODO BEH-1410: refactor/cleanup
|
|
87
105
|
let learningPath = await fetchByRailContentId(learningPathId, 'learning-path-v2')
|
|
88
106
|
learningPath.children = mapContentToParent(
|
|
89
107
|
learningPath.children,
|
|
@@ -109,10 +127,16 @@ export async function getLearningPath(learningPathId) {
|
|
|
109
127
|
export async function getLearningPathLessonsByIds(contentIds, learningPathId) {
|
|
110
128
|
// It is more efficient to load the entire learning path than individual lessons
|
|
111
129
|
// Also adds reliability check whether content is actually in the learning path
|
|
112
|
-
const learningPath = await
|
|
130
|
+
const learningPath = await getEnrichedLearningPath(learningPathId)
|
|
113
131
|
return learningPath.children.filter((lesson) => contentIds.includes(lesson.id))
|
|
114
132
|
}
|
|
115
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Maps content to its parent learning path - fixes multi-parent problems for cta when lessons have a special collection.
|
|
136
|
+
* @param lessons
|
|
137
|
+
* @param parentContentType
|
|
138
|
+
* @param parentContentId
|
|
139
|
+
*/
|
|
116
140
|
export function mapContentToParent(lessons, parentContentType, parentContentId) {
|
|
117
141
|
return lessons.map((lesson: any) => {
|
|
118
142
|
return { ...lesson, type: parentContentType, parent_id: parentContentId }
|
|
@@ -142,7 +166,7 @@ export async function fetchLearningPathLessons(
|
|
|
142
166
|
userDate: Date
|
|
143
167
|
) {
|
|
144
168
|
const [learningPath, dailySession] = await Promise.all([
|
|
145
|
-
|
|
169
|
+
getEnrichedLearningPath(learningPathId),
|
|
146
170
|
getDailySession(brand, userDate),
|
|
147
171
|
])
|
|
148
172
|
|
|
@@ -203,3 +227,64 @@ export async function fetchLearningPathLessons(
|
|
|
203
227
|
previous_learning_path_todays: previousLearningPathTodays,
|
|
204
228
|
}
|
|
205
229
|
}
|
|
230
|
+
|
|
231
|
+
interface completeMethodIntroVideo {
|
|
232
|
+
intro_video_response: Object | null,
|
|
233
|
+
active_path_response: ActiveLearningPathResponse
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Handles completion of method intro video and other related actions.
|
|
237
|
+
* @param introVideoId - The intro video content ID.
|
|
238
|
+
* @param brand
|
|
239
|
+
* @returns {Promise<Array>} response - The response object.
|
|
240
|
+
* @returns {Promise<Object|null>} response.intro_video_response - The intro video completion response or null if already completed.
|
|
241
|
+
* @returns {Promise<Object>} response.active_path_response - The set active learning path response.
|
|
242
|
+
*/
|
|
243
|
+
export async function completeMethodIntroVideo(introVideoId: number, brand: string): Promise<completeMethodIntroVideo> {
|
|
244
|
+
let response = {} as completeMethodIntroVideo
|
|
245
|
+
|
|
246
|
+
response.intro_video_response = await completeIfNotCompleted(introVideoId)
|
|
247
|
+
|
|
248
|
+
const url: string = `${LEARNING_PATHS_PATH}/start`
|
|
249
|
+
const body = { brand: brand }
|
|
250
|
+
response.active_path_response = await fetchHandler(url, 'POST', null, body)
|
|
251
|
+
|
|
252
|
+
return response
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface completeLearningPathIntroVideo {
|
|
256
|
+
intro_video_response: Object | null,
|
|
257
|
+
learning_path_reset_response: void | Object[] | Object
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Handles completion of learning path intro video and other related actions.
|
|
261
|
+
* @param introVideoId
|
|
262
|
+
* @param learningPathId
|
|
263
|
+
* @param lessonsToImport
|
|
264
|
+
*/
|
|
265
|
+
export async function completeLearningPathIntroVideo(introVideoId: number, learningPathId: number, lessonsToImport: number[] | null) {
|
|
266
|
+
let response = {} as completeLearningPathIntroVideo
|
|
267
|
+
|
|
268
|
+
response.intro_video_response = await completeIfNotCompleted(introVideoId)
|
|
269
|
+
|
|
270
|
+
if (!lessonsToImport) {
|
|
271
|
+
// reset progress within the learning path
|
|
272
|
+
response.learning_path_reset_response = await contentStatusReset(learningPathId)
|
|
273
|
+
} else {
|
|
274
|
+
response.learning_path_reset_response = null
|
|
275
|
+
|
|
276
|
+
// todo: add collection context + optimize with bulk calls with watermelon
|
|
277
|
+
for (const contentId of lessonsToImport) {
|
|
278
|
+
response.learning_path_reset_response[contentId] = await contentStatusCompleted(contentId)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return response
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
async function completeIfNotCompleted(contentId: number): Promise<Object | null> {
|
|
287
|
+
const introVideoStatus = await getProgressState(contentId)
|
|
288
|
+
|
|
289
|
+
return introVideoStatus !== 'completed' ? await contentStatusCompleted(contentId) : null
|
|
290
|
+
}
|
package/src/services/sanity.js
CHANGED
|
@@ -904,6 +904,7 @@ export async function fetchFoundation(slug) {
|
|
|
904
904
|
* @param {string} slug - The slug of the method.
|
|
905
905
|
* @returns {Promise<Object|null>} - The fetched methods data or null if not found.
|
|
906
906
|
*/
|
|
907
|
+
//todo BEH-1446 depreciated. remove all old method functions
|
|
907
908
|
export async function fetchMethod(brand, slug) {
|
|
908
909
|
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
909
910
|
|