musora-content-services 2.92.7 → 2.93.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/.github/workflows/docs.js.yml +5 -10
- package/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/src/contentTypeConfig.js +2 -2
- package/src/services/content-org/learning-paths.ts +12 -22
- package/src/services/contentProgress.js +2 -12
- package/src/services/permissions/PermissionsV2Adapter.ts +1 -3
- package/src/services/progress-row/method-card.js +8 -13
- package/src/services/sanity.js +10 -10
- package/src/services/sync/repositories/content-progress.ts +7 -0
|
@@ -39,14 +39,6 @@ jobs:
|
|
|
39
39
|
with:
|
|
40
40
|
node-version: '20'
|
|
41
41
|
|
|
42
|
-
- name: Install dependencies for v1
|
|
43
|
-
working-directory: main-content
|
|
44
|
-
run: npm ci
|
|
45
|
-
|
|
46
|
-
- name: Generate v1 documentation
|
|
47
|
-
working-directory: main-content
|
|
48
|
-
run: npm run doc
|
|
49
|
-
|
|
50
42
|
- name: Install dependencies for v2
|
|
51
43
|
working-directory: v2-content
|
|
52
44
|
run: npm ci
|
|
@@ -58,8 +50,11 @@ jobs:
|
|
|
58
50
|
- name: Combine v1 and v2 docs into deployment structure
|
|
59
51
|
run: |
|
|
60
52
|
mkdir -p _site
|
|
61
|
-
# Copy v1 docs
|
|
62
|
-
|
|
53
|
+
# Copy existing v1 docs from main branch (already committed)
|
|
54
|
+
if [ -d "main-content/docs" ]; then
|
|
55
|
+
cp -r main-content/docs/* _site/
|
|
56
|
+
echo "✅ Copied existing v1 docs from main branch"
|
|
57
|
+
fi
|
|
63
58
|
# Copy v2 docs to /v2/ subdirectory
|
|
64
59
|
mkdir -p _site/v2
|
|
65
60
|
cp -r v2-content/docs/* _site/v2/
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
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.93.0](https://github.com/railroadmedia/musora-content-services/compare/v2.92.7...v2.93.0) (2025-12-02)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **BEH-1192:** revise fetchLearningPathProgressCheckLessons ([#604](https://github.com/railroadmedia/musora-content-services/issues/604)) ([db8cb70](https://github.com/railroadmedia/musora-content-services/commit/db8cb70ed70f4341fdd4651cdd3fea2b8047dc21))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* method progress card subtitle ([#608](https://github.com/railroadmedia/musora-content-services/issues/608)) ([3bbfb56](https://github.com/railroadmedia/musora-content-services/commit/3bbfb56fce08e0753e7708bdf2d1356000b1c398))
|
|
16
|
+
* Pull in permission_v2 ([#602](https://github.com/railroadmedia/musora-content-services/issues/602)) ([289e32d](https://github.com/railroadmedia/musora-content-services/commit/289e32d08bd3a6448191b9f6034801e4cf8d40e5))
|
|
17
|
+
|
|
5
18
|
### [2.92.7](https://github.com/railroadmedia/musora-content-services/compare/v2.92.6...v2.92.7) (2025-12-02)
|
|
6
19
|
|
|
7
20
|
|
package/package.json
CHANGED
package/src/contentTypeConfig.js
CHANGED
|
@@ -61,7 +61,7 @@ export const DEFAULT_FIELDS = [
|
|
|
61
61
|
`'genre': ${genreField}`,
|
|
62
62
|
'status',
|
|
63
63
|
"'slug' : slug.current",
|
|
64
|
-
"'permission_id':
|
|
64
|
+
"'permission_id': permission_v2",
|
|
65
65
|
'child_count',
|
|
66
66
|
'"parent_id": parent_content_data[0].id',
|
|
67
67
|
]
|
|
@@ -84,7 +84,7 @@ export const DEFAULT_CHILD_FIELDS = [
|
|
|
84
84
|
`'genre': ${genreField}`,
|
|
85
85
|
'status',
|
|
86
86
|
"'slug' : slug.current",
|
|
87
|
-
"'permission_id':
|
|
87
|
+
"'permission_id': permission_v2",
|
|
88
88
|
'child_count',
|
|
89
89
|
'"parent_id": parent_content_data[0].id',
|
|
90
90
|
]
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getAllCompletedByIds,
|
|
12
12
|
getProgressState,
|
|
13
13
|
} from '../contentProgress.js'
|
|
14
|
+
import { STATE } from '../sync/models/ContentProgress'
|
|
14
15
|
|
|
15
16
|
const BASE_PATH: string = `/api/content-org`
|
|
16
17
|
const LEARNING_PATHS_PATH = `${BASE_PATH}/v1/user/learning-paths`
|
|
@@ -225,29 +226,18 @@ export async function fetchLearningPathLessons(
|
|
|
225
226
|
* For an array of contentIds, fetch any content progress with state=completed,
|
|
226
227
|
* including other learning paths and a la carte progress.
|
|
227
228
|
*
|
|
228
|
-
* @param contentIds The array of content IDs within the learning path
|
|
229
|
-
* @
|
|
230
|
-
* @returns {Promise<Object>} Response object
|
|
231
|
-
* @returns {Array} result.lessons - Array of all learning path lesson contentIds.
|
|
232
|
-
* @returns {Array} result.completed_lessons - Array of learning path lesson contentIds that are completed.
|
|
233
|
-
* @returns {Array} result.lessons_count - Count of learning path lessons.
|
|
234
|
-
* @returns {Array} result.completed_lessons_count - Count of learning path completed lessons.
|
|
229
|
+
* @param {number[]} contentIds The array of content IDs within the learning path
|
|
230
|
+
* @returns {Promise<Object>} Object with content IDs as keys and the progress state as values
|
|
235
231
|
*/
|
|
236
|
-
export async function fetchLearningPathProgressCheckLessons(
|
|
237
|
-
contentIds
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
lessons: contentIds,
|
|
247
|
-
completed_lessons: completedContentIds,
|
|
248
|
-
lessons_count: contentIds.length,
|
|
249
|
-
completed_lessons_count: completedContentIds.length,
|
|
250
|
-
}
|
|
232
|
+
export async function fetchLearningPathProgressCheckLessons(contentIds: number[]): Promise<Object> {
|
|
233
|
+
let query = await getAllCompletedByIds(contentIds)
|
|
234
|
+
let completedLessons = query.data.map(lesson => lesson.content_id)
|
|
235
|
+
|
|
236
|
+
return contentIds.reduce((obj, contentId) => {
|
|
237
|
+
let lessonIsCompleted = completedLessons.includes(contentId)
|
|
238
|
+
obj[contentId] = lessonIsCompleted ? STATE.COMPLETED : ""
|
|
239
|
+
return obj
|
|
240
|
+
}, {})
|
|
251
241
|
}
|
|
252
242
|
|
|
253
243
|
interface completeMethodIntroVideo {
|
|
@@ -162,18 +162,8 @@ export async function getAllCompleted(limit = null) {
|
|
|
162
162
|
return db.contentProgress.completedIds(limit).then(r => r.data.map(id => parseInt(id)))
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
* @param {array} contentIds List of content children within learning path
|
|
168
|
-
* @param {object} collection Learning path object
|
|
169
|
-
* @returns {Promise<array>} Filtered list of contentIds that are completed
|
|
170
|
-
*/
|
|
171
|
-
export async function getAllCompletedByIds(contentIds, collection) {
|
|
172
|
-
// TODO - implement collection filtering
|
|
173
|
-
return db.contentProgress.queryAllIds(
|
|
174
|
-
Q.whereIn('content_id', contentIds),
|
|
175
|
-
Q.where('state', STATE_COMPLETED)
|
|
176
|
-
)
|
|
165
|
+
export async function getAllCompletedByIds(contentIds) {
|
|
166
|
+
return db.contentProgress.completedByContentIds(contentIds)
|
|
177
167
|
}
|
|
178
168
|
|
|
179
169
|
export async function getAllStartedOrCompleted({
|
|
@@ -51,9 +51,7 @@ export class PermissionsV2Adapter extends PermissionsAdapter {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Get content's required permissions
|
|
54
|
-
const
|
|
55
|
-
const newPermissions = content?.permission_v2 ?? []
|
|
56
|
-
const contentPermissions = new Set([...oldPermissions, ...newPermissions])
|
|
54
|
+
const contentPermissions = new Set(content?.permission_id ?? [])
|
|
57
55
|
|
|
58
56
|
// Content with no permissions is accessible to all
|
|
59
57
|
if (contentPermissions.size === 0) {
|
|
@@ -2,16 +2,9 @@
|
|
|
2
2
|
* @module ProgressRow
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
getDailySession,
|
|
7
|
-
getActivePath,
|
|
8
|
-
resetAllLearningPaths,
|
|
9
|
-
startLearningPath,
|
|
10
|
-
fetchLearningPathLessons,
|
|
11
|
-
} from '../content-org/learning-paths'
|
|
5
|
+
import { getActivePath, fetchLearningPathLessons } from '../content-org/learning-paths'
|
|
12
6
|
import { getToday } from '../dateUtils.js'
|
|
13
|
-
import {
|
|
14
|
-
import { addContextToContent } from '../contentAggregator.js'
|
|
7
|
+
import { fetchMethodV2IntroVideo } from '../sanity'
|
|
15
8
|
import { getProgressState } from '../contentProgress'
|
|
16
9
|
|
|
17
10
|
export async function getMethodCard(brand) {
|
|
@@ -21,6 +14,10 @@ export async function getMethodCard(brand) {
|
|
|
21
14
|
if (introVideoProgressState !== 'completed') {
|
|
22
15
|
//startLearningPath('drumeo', 422533)
|
|
23
16
|
const timestamp = Math.floor(Date.now() / 1000)
|
|
17
|
+
const instructorText =
|
|
18
|
+
introVideo.instructor?.length > 1
|
|
19
|
+
? 'Multiple Instructors'
|
|
20
|
+
: introVideo.instructor?.[0]?.name || ''
|
|
24
21
|
return {
|
|
25
22
|
id: 1, // method card has no id
|
|
26
23
|
type: 'method',
|
|
@@ -29,7 +26,7 @@ export async function getMethodCard(brand) {
|
|
|
29
26
|
body: {
|
|
30
27
|
thumbnail: introVideo.thumbnail,
|
|
31
28
|
title: introVideo.title,
|
|
32
|
-
subtitle: `${introVideo.difficulty_string} • ${
|
|
29
|
+
subtitle: `${introVideo.difficulty_string} • ${instructorText}`,
|
|
33
30
|
},
|
|
34
31
|
cta: {
|
|
35
32
|
text: 'Get Started',
|
|
@@ -59,9 +56,7 @@ export async function getMethodCard(brand) {
|
|
|
59
56
|
const nextIncompleteLesson = learningPath?.todays_lessons.find(
|
|
60
57
|
(lesson) => lesson.progressStatus !== 'completed'
|
|
61
58
|
)
|
|
62
|
-
let ctaText,
|
|
63
|
-
action,
|
|
64
|
-
nextLesson = null
|
|
59
|
+
let ctaText, action
|
|
65
60
|
if (noneCompleted) {
|
|
66
61
|
ctaText = 'Start Session'
|
|
67
62
|
action = getMethodActionCTA(nextIncompleteLesson)
|
package/src/services/sanity.js
CHANGED
|
@@ -219,7 +219,7 @@ export async function fetchRelatedSongs(brand, songId) {
|
|
|
219
219
|
"published_on": published_on,
|
|
220
220
|
status,
|
|
221
221
|
"image": thumbnail.asset->url,
|
|
222
|
-
"permission_id":
|
|
222
|
+
"permission_id": permission_v2,
|
|
223
223
|
"fields": [
|
|
224
224
|
{
|
|
225
225
|
"key": "title",
|
|
@@ -245,7 +245,7 @@ export async function fetchRelatedSongs(brand, songId) {
|
|
|
245
245
|
"id": railcontent_id,
|
|
246
246
|
"url": web_url_path,
|
|
247
247
|
"published_on": published_on,
|
|
248
|
-
"permission_id":
|
|
248
|
+
"permission_id": permission_v2,
|
|
249
249
|
status,
|
|
250
250
|
"fields": [
|
|
251
251
|
{
|
|
@@ -306,7 +306,7 @@ export async function fetchNewReleases(
|
|
|
306
306
|
published_on,
|
|
307
307
|
"type": _type,
|
|
308
308
|
web_url_path,
|
|
309
|
-
"permission_id":
|
|
309
|
+
"permission_id": permission_v2,
|
|
310
310
|
`
|
|
311
311
|
const query = buildRawQuery(filter, fields, { sortOrder: sortOrder, start, end: end })
|
|
312
312
|
return fetchSanity(query, true)
|
|
@@ -343,7 +343,7 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
|
|
|
343
343
|
published_on,
|
|
344
344
|
"type": _type,
|
|
345
345
|
web_url_path,
|
|
346
|
-
"permission_id":
|
|
346
|
+
"permission_id": permission_v2,
|
|
347
347
|
live_event_start_time,
|
|
348
348
|
live_event_end_time,
|
|
349
349
|
"isLive": live_event_start_time <= '${now}' && live_event_end_time >= '${now}'`
|
|
@@ -395,7 +395,7 @@ export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
|
|
|
395
395
|
published_on,
|
|
396
396
|
"type": _type,
|
|
397
397
|
web_url_path,
|
|
398
|
-
"permission_id":
|
|
398
|
+
"permission_id": permission_v2,
|
|
399
399
|
} | order(published_on asc)[${start}...${end}]`
|
|
400
400
|
return fetchSanity(query, true)
|
|
401
401
|
}
|
|
@@ -911,7 +911,7 @@ export async function fetchMethod(brand, slug) {
|
|
|
911
911
|
"url": *[railcontent_id == ^.id][0].web_url_path
|
|
912
912
|
} | order(length(url)),
|
|
913
913
|
"type": _type,
|
|
914
|
-
"permission_id":
|
|
914
|
+
"permission_id": permission_v2,
|
|
915
915
|
"levels": child[${childrenFilter}]->
|
|
916
916
|
{
|
|
917
917
|
"id": railcontent_id,
|
|
@@ -1128,7 +1128,7 @@ export async function fetchLessonContent(railContentId, { addParent = false } =
|
|
|
1128
1128
|
mp3_no_drums_yes_click_url,
|
|
1129
1129
|
mp3_yes_drums_no_click_url,
|
|
1130
1130
|
mp3_yes_drums_yes_click_url,
|
|
1131
|
-
"permission_id":
|
|
1131
|
+
"permission_id": permission_v2,
|
|
1132
1132
|
${parentQuery}
|
|
1133
1133
|
...select(
|
|
1134
1134
|
defined(live_event_start_time) => {
|
|
@@ -1266,7 +1266,7 @@ export async function fetchSiblingContent(railContentId, brand = null) {
|
|
|
1266
1266
|
}).buildFilter()
|
|
1267
1267
|
|
|
1268
1268
|
const brandString = brand ? ` && brand == "${brand}"` : ''
|
|
1269
|
-
const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, artist->, "permission_id":
|
|
1269
|
+
const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, artist->, "permission_id": permission_v2, "genre": genre[]->name, "parent_id": parent_content_data[0].id`
|
|
1270
1270
|
|
|
1271
1271
|
const query = `*[railcontent_id == ${railContentId}${brandString}]{
|
|
1272
1272
|
_type, parent_type, 'parent_id': parent_content_data[0].id, railcontent_id,
|
|
@@ -1313,7 +1313,7 @@ export async function fetchRelatedLessons(railContentId) {
|
|
|
1313
1313
|
{ showMembershipRestrictedContent: true }
|
|
1314
1314
|
).buildFilter()
|
|
1315
1315
|
|
|
1316
|
-
const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id":
|
|
1316
|
+
const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission_v2,_type, "genre": genre[]->name`
|
|
1317
1317
|
|
|
1318
1318
|
const query = `*[railcontent_id == ${railContentId} && (!defined(permission) || references(*[_type=='permission']._id))]{
|
|
1319
1319
|
_type, parent_type, railcontent_id,
|
|
@@ -2134,7 +2134,7 @@ export async function fetchScheduledAndNewReleases(
|
|
|
2134
2134
|
published_on,
|
|
2135
2135
|
"type": _type,
|
|
2136
2136
|
show_in_new_feed,
|
|
2137
|
-
"permission_id":
|
|
2137
|
+
"permission_id": permission_v2,
|
|
2138
2138
|
"isLive": live_event_start_time <= '${now}' && live_event_end_time >= '${now}',
|
|
2139
2139
|
}`
|
|
2140
2140
|
|
|
@@ -23,6 +23,13 @@ export default class ProgressRepository extends SyncRepository<ContentProgress>
|
|
|
23
23
|
)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
async completedByContentIds(contentIds: number[]) {
|
|
27
|
+
return this.queryAll(
|
|
28
|
+
Q.where('content_id', Q.oneOf(contentIds)),
|
|
29
|
+
Q.where('state', STATE.COMPLETED)
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
// null collection only
|
|
27
34
|
async startedOrCompleted(opts: Parameters<typeof this.startedOrCompletedClauses>[0] = {}) {
|
|
28
35
|
return this.queryAll(...this.startedOrCompletedClauses(opts))
|