musora-content-services 2.3.20 → 2.3.22
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/node.js.yml +0 -0
- package/.prettierignore +0 -0
- package/.prettierrc +0 -0
- package/CHANGELOG.md +4 -0
- package/README.md +0 -0
- package/babel.config.cjs +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/scripts/collapse.js +0 -0
- package/docs/scripts/commonNav.js +0 -0
- package/docs/scripts/linenumber.js +0 -0
- package/docs/scripts/nav.js +0 -0
- package/docs/scripts/polyfill.js +0 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
- package/docs/scripts/prettify/lang-css.js +0 -0
- package/docs/scripts/prettify/prettify.js +0 -0
- package/docs/scripts/search.js +0 -0
- package/docs/styles/jsdoc.css +0 -0
- package/docs/styles/prettify.css +0 -0
- package/jest.config.js +0 -0
- package/link_mcs.sh +0 -0
- package/package.json +1 -1
- package/src/contentAggregator.js +0 -0
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +11 -0
- package/src/index.js +10 -0
- package/src/lib/httpHelper.js +0 -0
- package/src/services/api/types.js +0 -0
- package/src/services/content-org/content-org.js +0 -0
- package/src/services/content-org/playlists.js +41 -41
- package/src/services/content.js +0 -0
- package/src/services/contentLikes.js +0 -0
- package/src/services/contentProgress.js +0 -0
- package/src/services/dataContext.js +0 -0
- package/src/services/dateUtils.js +0 -0
- package/src/services/forum.js +0 -0
- package/src/services/gamification/awards.js +0 -0
- package/src/services/gamification/gamification.js +0 -0
- package/src/services/gamification/types.js +0 -0
- package/src/services/imageSRCVerify.js +0 -0
- package/src/services/railcontent.js +5 -1
- package/src/services/recommendations.js +0 -0
- package/src/services/types.js +0 -0
- package/src/services/user/management.js +0 -0
- package/src/services/user/sessions.js +0 -0
- package/src/services/user/types.js +0 -0
- package/src/services/user/user-management-system.js +0 -0
- package/src/services/userActivity.js +81 -0
- package/test/live/contentProgressLive.test.js +0 -0
- package/test/live/railcontentLive.test.js +0 -0
- package/test/localStorageMock.js +0 -0
- package/test/log.js +0 -0
|
File without changes
|
package/.prettierignore
CHANGED
|
File without changes
|
package/.prettierrc
CHANGED
|
File without changes
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
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.3.22](https://github.com/railroadmedia/musora-content-services/compare/v2.3.21...v2.3.22) (2025-05-05)
|
|
6
|
+
|
|
7
|
+
### [2.3.21](https://github.com/railroadmedia/musora-content-services/compare/v2.3.20...v2.3.21) (2025-05-02)
|
|
8
|
+
|
|
5
9
|
### [2.3.20](https://github.com/railroadmedia/musora-content-services/compare/v2.3.19...v2.3.20) (2025-04-30)
|
|
6
10
|
|
|
7
11
|
### [2.3.19](https://github.com/railroadmedia/musora-content-services/compare/v2.3.18...v2.3.19) (2025-04-30)
|
package/README.md
CHANGED
|
File without changes
|
package/babel.config.cjs
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/scripts/collapse.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/scripts/nav.js
CHANGED
|
File without changes
|
package/docs/scripts/polyfill.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/scripts/search.js
CHANGED
|
File without changes
|
package/docs/styles/jsdoc.css
CHANGED
|
File without changes
|
package/docs/styles/prettify.css
CHANGED
|
File without changes
|
package/jest.config.js
CHANGED
|
File without changes
|
package/link_mcs.sh
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/contentAggregator.js
CHANGED
|
File without changes
|
package/src/filterBuilder.js
CHANGED
|
File without changes
|
package/src/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
addItemToPlaylist,
|
|
10
10
|
createPlaylist,
|
|
11
11
|
duplicatePlaylist,
|
|
12
|
+
fetchPlaylist,
|
|
13
|
+
fetchPlaylistItems,
|
|
12
14
|
fetchUserPlaylists,
|
|
13
15
|
likePlaylist,
|
|
14
16
|
reorderPlaylistItems,
|
|
@@ -51,6 +53,10 @@ import {
|
|
|
51
53
|
recordWatchSession
|
|
52
54
|
} from './services/contentProgress.js';
|
|
53
55
|
|
|
56
|
+
import {
|
|
57
|
+
addContextToContent
|
|
58
|
+
} from './contentAggregator.js';
|
|
59
|
+
|
|
54
60
|
import {
|
|
55
61
|
verifyLocalDataContext
|
|
56
62
|
} from './services/dataContext.js';
|
|
@@ -213,6 +219,7 @@ import {
|
|
|
213
219
|
} from './services/user/sessions.js';
|
|
214
220
|
|
|
215
221
|
import {
|
|
222
|
+
calculateLongestStreaks,
|
|
216
223
|
createPracticeNotes,
|
|
217
224
|
deletePracticeSession,
|
|
218
225
|
getPracticeNotes,
|
|
@@ -231,6 +238,7 @@ import {
|
|
|
231
238
|
declare module 'musora-content-services' {
|
|
232
239
|
export {
|
|
233
240
|
addItemToPlaylist,
|
|
241
|
+
addContextToContent,
|
|
234
242
|
applyCloudflareWrapper,
|
|
235
243
|
applySanityTransformations,
|
|
236
244
|
assignModeratorToComment,
|
|
@@ -238,6 +246,7 @@ declare module 'musora-content-services' {
|
|
|
238
246
|
assignmentStatusReset,
|
|
239
247
|
blockUser,
|
|
240
248
|
buildImageSRC,
|
|
249
|
+
calculateLongestStreaks,
|
|
241
250
|
closeComment,
|
|
242
251
|
contentStatusCompleted,
|
|
243
252
|
contentStatusReset,
|
|
@@ -300,6 +309,8 @@ declare module 'musora-content-services' {
|
|
|
300
309
|
fetchPackData,
|
|
301
310
|
fetchParentForDownload,
|
|
302
311
|
fetchPlayAlongsCount,
|
|
312
|
+
fetchPlaylist,
|
|
313
|
+
fetchPlaylistItems,
|
|
303
314
|
fetchRecent,
|
|
304
315
|
fetchRelatedLessons,
|
|
305
316
|
fetchRelatedRecommendedContent,
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
addItemToPlaylist,
|
|
10
10
|
createPlaylist,
|
|
11
11
|
duplicatePlaylist,
|
|
12
|
+
fetchPlaylist,
|
|
13
|
+
fetchPlaylistItems,
|
|
12
14
|
fetchUserPlaylists,
|
|
13
15
|
likePlaylist,
|
|
14
16
|
reorderPlaylistItems,
|
|
@@ -82,6 +84,9 @@ import {
|
|
|
82
84
|
isBucketUrl,
|
|
83
85
|
verifyImageSRC
|
|
84
86
|
} from './services/imageSRCVerify.js';
|
|
87
|
+
import {
|
|
88
|
+
addContextToContent
|
|
89
|
+
} from './contentAggregator.js';
|
|
85
90
|
|
|
86
91
|
import {
|
|
87
92
|
assignModeratorToComment,
|
|
@@ -213,6 +218,7 @@ import {
|
|
|
213
218
|
} from './services/user/sessions.js';
|
|
214
219
|
|
|
215
220
|
import {
|
|
221
|
+
calculateLongestStreaks,
|
|
216
222
|
createPracticeNotes,
|
|
217
223
|
deletePracticeSession,
|
|
218
224
|
getPracticeNotes,
|
|
@@ -237,6 +243,7 @@ export {
|
|
|
237
243
|
assignmentStatusReset,
|
|
238
244
|
blockUser,
|
|
239
245
|
buildImageSRC,
|
|
246
|
+
calculateLongestStreaks,
|
|
240
247
|
closeComment,
|
|
241
248
|
contentStatusCompleted,
|
|
242
249
|
contentStatusReset,
|
|
@@ -299,6 +306,8 @@ export {
|
|
|
299
306
|
fetchPackData,
|
|
300
307
|
fetchParentForDownload,
|
|
301
308
|
fetchPlayAlongsCount,
|
|
309
|
+
fetchPlaylist,
|
|
310
|
+
fetchPlaylistItems,
|
|
302
311
|
fetchRecent,
|
|
303
312
|
fetchRelatedLessons,
|
|
304
313
|
fetchRelatedRecommendedContent,
|
|
@@ -403,4 +412,5 @@ export {
|
|
|
403
412
|
updateUserPractice,
|
|
404
413
|
verifyImageSRC,
|
|
405
414
|
verifyLocalDataContext,
|
|
415
|
+
addContextToContent
|
|
406
416
|
};
|
package/src/lib/httpHelper.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -254,6 +254,47 @@ export async function duplicatePlaylist(playlistId, playlistData) {
|
|
|
254
254
|
return await fetchHandler(url, 'POST', null, playlistData)
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Retrieves details of a specific playlist by its ID.
|
|
259
|
+
*
|
|
260
|
+
* This function sends a GET request to the `/playlists/playlist` endpoint with a specified playlist ID.
|
|
261
|
+
* The server validates the user's access to the playlist and returns playlist details if the user is authorized.
|
|
262
|
+
*
|
|
263
|
+
* @param {string|number} playlistId - The unique identifier of the playlist to retrieve.
|
|
264
|
+
*
|
|
265
|
+
* @returns {Promise<Object>} - A promise that resolves to the response from the API, containing:
|
|
266
|
+
* - `data` (Object): The playlist details, or an error message if access is denied or the playlist is not found.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* fetchPlaylist(12345)
|
|
270
|
+
* .then(response => console.log(response.data))
|
|
271
|
+
* .catch(error => console.error('Error fetching playlist:', error));
|
|
272
|
+
*/
|
|
273
|
+
export async function fetchPlaylist(playlistId) {
|
|
274
|
+
const url = `${BASE_PATH}/v1/user/playlists/${playlistId}`
|
|
275
|
+
return await fetchHandler(url, 'GET')
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Retrieves items within a specified playlist by playlist ID.
|
|
280
|
+
*
|
|
281
|
+
* This function sends a GET request to the `/playlists/playlist-lessons` endpoint to fetch items in the given playlist.
|
|
282
|
+
* The server combines data from the playlist and additional metadata from Sanity to enhance item details.
|
|
283
|
+
*
|
|
284
|
+
* @param {string|number} playlistId - The unique identifier of the playlist whose items are to be fetched.
|
|
285
|
+
*
|
|
286
|
+
* @returns {Promise<Array<Object>>} - A promise that resolves to an array of playlist items
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* fetchPlaylistItems(12345)
|
|
290
|
+
* .then(items => console.log(items))
|
|
291
|
+
* .catch(error => console.error('Error fetching playlist items:', error));
|
|
292
|
+
*/
|
|
293
|
+
export async function fetchPlaylistItems(playlistId) {
|
|
294
|
+
const url = `${BASE_PATH}/v1/user/playlists/items/${playlistId}`
|
|
295
|
+
return await fetchHandler(url, 'GET')
|
|
296
|
+
}
|
|
297
|
+
|
|
257
298
|
// Unsupported playlist endpoints are here and will need to be implemented one by one
|
|
258
299
|
//
|
|
259
300
|
//
|
|
@@ -321,47 +362,6 @@ export async function duplicatePlaylist(playlistId, playlistData) {
|
|
|
321
362
|
// }
|
|
322
363
|
//
|
|
323
364
|
//
|
|
324
|
-
// /**
|
|
325
|
-
// * Retrieves details of a specific playlist by its ID.
|
|
326
|
-
// *
|
|
327
|
-
// * This function sends a GET request to the `/playlists/playlist` endpoint with a specified playlist ID.
|
|
328
|
-
// * The server validates the user's access to the playlist and returns playlist details if the user is authorized.
|
|
329
|
-
// *
|
|
330
|
-
// * @param {string|number} playlistId - The unique identifier of the playlist to retrieve.
|
|
331
|
-
// *
|
|
332
|
-
// * @returns {Promise<Object>} - A promise that resolves to the response from the API, containing:
|
|
333
|
-
// * - `data` (Object): The playlist details, or an error message if access is denied or the playlist is not found.
|
|
334
|
-
// *
|
|
335
|
-
// * @example
|
|
336
|
-
// * fetchPlaylist(12345)
|
|
337
|
-
// * .then(response => console.log(response.data))
|
|
338
|
-
// * .catch(error => console.error('Error fetching playlist:', error));
|
|
339
|
-
// */
|
|
340
|
-
// export async function fetchPlaylist(playlistId) {
|
|
341
|
-
// const url = `/playlists/playlist/${playlistId}`
|
|
342
|
-
// return await fetchHandler(url, 'GET')
|
|
343
|
-
// }
|
|
344
|
-
//
|
|
345
|
-
// /**
|
|
346
|
-
// * Retrieves items within a specified playlist by playlist ID.
|
|
347
|
-
// *
|
|
348
|
-
// * This function sends a GET request to the `/playlists/playlist-lessons` endpoint to fetch items in the given playlist.
|
|
349
|
-
// * The server combines data from the playlist and additional metadata from Sanity to enhance item details.
|
|
350
|
-
// *
|
|
351
|
-
// * @param {string|number} playlistId - The unique identifier of the playlist whose items are to be fetched.
|
|
352
|
-
// *
|
|
353
|
-
// * @returns {Promise<Array<Object>>} - A promise that resolves to an array of playlist items
|
|
354
|
-
// *
|
|
355
|
-
// * @example
|
|
356
|
-
// * fetchPlaylistItems(12345)
|
|
357
|
-
// * .then(items => console.log(items))
|
|
358
|
-
// * .catch(error => console.error('Error fetching playlist items:', error));
|
|
359
|
-
// */
|
|
360
|
-
// export async function fetchPlaylistItems(playlistId, { sort } = {}) {
|
|
361
|
-
// const sortString = sort ? `&sort=${sort}` : ''
|
|
362
|
-
// const url = `/playlists/playlist-lessons?playlist_id=${playlistId}${sortString}`
|
|
363
|
-
// return await fetchHandler(url, 'GET')
|
|
364
|
-
// }
|
|
365
365
|
//
|
|
366
366
|
// /**
|
|
367
367
|
// * Updates a playlist item with the provided data.
|
package/src/services/content.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/services/forum.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -290,6 +290,10 @@ async function patchDataHandler(url, data) {
|
|
|
290
290
|
return fetchHandler(url, 'patch', null, data)
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
async function putDataHandler(url, data) {
|
|
294
|
+
return fetchHandler(url, 'put', null, data)
|
|
295
|
+
}
|
|
296
|
+
|
|
293
297
|
async function deleteDataHandler(url, data) {
|
|
294
298
|
return fetchHandler(url, 'delete')
|
|
295
299
|
}
|
|
@@ -726,7 +730,7 @@ export async function editComment(commentId, comment) {
|
|
|
726
730
|
const data = {
|
|
727
731
|
comment: comment,
|
|
728
732
|
}
|
|
729
|
-
return await
|
|
733
|
+
return await putDataHandler(url, data)
|
|
730
734
|
}
|
|
731
735
|
|
|
732
736
|
/**
|
|
File without changes
|
package/src/services/types.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -688,6 +688,87 @@ function calculateStreaks(practices, includeStreakMessage = false) {
|
|
|
688
688
|
return { currentDailyStreak, currentWeeklyStreak, streakMessage };
|
|
689
689
|
}
|
|
690
690
|
|
|
691
|
+
/**
|
|
692
|
+
* Calculates the longest daily, weekly streaks and totalPracticeSeconds from user practice dates.
|
|
693
|
+
* @returns {{ longestDailyStreak: number, longestWeeklyStreak: number, totalPracticeSeconds:number }}
|
|
694
|
+
*/
|
|
695
|
+
export async function calculateLongestStreaks() {
|
|
696
|
+
let data = await userActivityContext.getData()
|
|
697
|
+
let practices = data?.[DATA_KEY_PRACTICES] ?? {}
|
|
698
|
+
let totalPracticeSeconds = 0;
|
|
699
|
+
// Calculate total practice duration
|
|
700
|
+
for (const date in practices) {
|
|
701
|
+
for (const entry of practices[date]) {
|
|
702
|
+
totalPracticeSeconds += entry.duration_seconds;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
let practiceDates = Object.keys(practices)
|
|
707
|
+
.map(dateStr => {
|
|
708
|
+
const [y, m, d] = dateStr.split('-').map(Number);
|
|
709
|
+
const newDate = new Date();
|
|
710
|
+
newDate.setFullYear(y, m - 1, d);
|
|
711
|
+
return newDate;
|
|
712
|
+
})
|
|
713
|
+
.sort((a, b) => a - b);
|
|
714
|
+
|
|
715
|
+
if (!practiceDates || practiceDates.length === 0) {
|
|
716
|
+
return {longestDailyStreak: 0, longestWeeklyStreak: 0, totalPracticeSeconds: 0};
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Normalize to Date objects
|
|
720
|
+
const normalizedDates = [
|
|
721
|
+
...new Set(practiceDates.map(d => {
|
|
722
|
+
const date = new Date(d);
|
|
723
|
+
date.setHours(0, 0, 0, 0);
|
|
724
|
+
return date.getTime();
|
|
725
|
+
}))
|
|
726
|
+
].sort((a, b) => a - b);
|
|
727
|
+
|
|
728
|
+
// ----- Daily Streak -----
|
|
729
|
+
let longestDailyStreak = 1;
|
|
730
|
+
let currentDailyStreak = 1;
|
|
731
|
+
for (let i = 1; i < normalizedDates.length; i++) {
|
|
732
|
+
const diffInDays = (normalizedDates[i] - normalizedDates[i - 1]) / (1000 * 60 * 60 * 24);
|
|
733
|
+
if (diffInDays === 1) {
|
|
734
|
+
currentDailyStreak++;
|
|
735
|
+
longestDailyStreak = Math.max(longestDailyStreak, currentDailyStreak);
|
|
736
|
+
} else {
|
|
737
|
+
currentDailyStreak = 1;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// ----- Weekly Streak -----
|
|
742
|
+
const weekStartDates = [
|
|
743
|
+
...new Set(normalizedDates.map(ts => {
|
|
744
|
+
const d = new Date(ts);
|
|
745
|
+
const day = d.getDay();
|
|
746
|
+
const diff = d.getDate() - day + (day === 0 ? -6 : 1); // adjust to Monday
|
|
747
|
+
d.setDate(diff);
|
|
748
|
+
return d.getTime(); // timestamp for Monday
|
|
749
|
+
}))
|
|
750
|
+
].sort((a, b) => a - b);
|
|
751
|
+
|
|
752
|
+
let longestWeeklyStreak = 1;
|
|
753
|
+
let currentWeeklyStreak = 1;
|
|
754
|
+
|
|
755
|
+
for (let i = 1; i < weekStartDates.length; i++) {
|
|
756
|
+
const diffInWeeks = (weekStartDates[i] - weekStartDates[i - 1]) / (1000 * 60 * 60 * 24 * 7);
|
|
757
|
+
if (diffInWeeks === 1) {
|
|
758
|
+
currentWeeklyStreak++;
|
|
759
|
+
longestWeeklyStreak = Math.max(longestWeeklyStreak, currentWeeklyStreak);
|
|
760
|
+
} else {
|
|
761
|
+
currentWeeklyStreak = 1;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return {
|
|
766
|
+
longestDailyStreak,
|
|
767
|
+
longestWeeklyStreak,
|
|
768
|
+
totalPracticeSeconds
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
|
|
691
772
|
|
|
692
773
|
|
|
693
774
|
|
|
File without changes
|
|
File without changes
|
package/test/localStorageMock.js
CHANGED
|
File without changes
|
package/test/log.js
CHANGED
|
File without changes
|