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.
Files changed (68) hide show
  1. package/.github/workflows/node.js.yml +0 -0
  2. package/.prettierignore +0 -0
  3. package/.prettierrc +0 -0
  4. package/CHANGELOG.md +4 -0
  5. package/README.md +0 -0
  6. package/babel.config.cjs +0 -0
  7. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  8. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  9. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  10. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  11. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  12. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  13. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  14. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  15. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  16. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  17. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  18. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  19. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  20. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  21. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  22. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  23. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  24. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  25. package/docs/scripts/collapse.js +0 -0
  26. package/docs/scripts/commonNav.js +0 -0
  27. package/docs/scripts/linenumber.js +0 -0
  28. package/docs/scripts/nav.js +0 -0
  29. package/docs/scripts/polyfill.js +0 -0
  30. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  31. package/docs/scripts/prettify/lang-css.js +0 -0
  32. package/docs/scripts/prettify/prettify.js +0 -0
  33. package/docs/scripts/search.js +0 -0
  34. package/docs/styles/jsdoc.css +0 -0
  35. package/docs/styles/prettify.css +0 -0
  36. package/jest.config.js +0 -0
  37. package/link_mcs.sh +0 -0
  38. package/package.json +1 -1
  39. package/src/contentAggregator.js +0 -0
  40. package/src/filterBuilder.js +0 -0
  41. package/src/index.d.ts +11 -0
  42. package/src/index.js +10 -0
  43. package/src/lib/httpHelper.js +0 -0
  44. package/src/services/api/types.js +0 -0
  45. package/src/services/content-org/content-org.js +0 -0
  46. package/src/services/content-org/playlists.js +41 -41
  47. package/src/services/content.js +0 -0
  48. package/src/services/contentLikes.js +0 -0
  49. package/src/services/contentProgress.js +0 -0
  50. package/src/services/dataContext.js +0 -0
  51. package/src/services/dateUtils.js +0 -0
  52. package/src/services/forum.js +0 -0
  53. package/src/services/gamification/awards.js +0 -0
  54. package/src/services/gamification/gamification.js +0 -0
  55. package/src/services/gamification/types.js +0 -0
  56. package/src/services/imageSRCVerify.js +0 -0
  57. package/src/services/railcontent.js +5 -1
  58. package/src/services/recommendations.js +0 -0
  59. package/src/services/types.js +0 -0
  60. package/src/services/user/management.js +0 -0
  61. package/src/services/user/sessions.js +0 -0
  62. package/src/services/user/types.js +0 -0
  63. package/src/services/user/user-management-system.js +0 -0
  64. package/src/services/userActivity.js +81 -0
  65. package/test/live/contentProgressLive.test.js +0 -0
  66. package/test/live/railcontentLive.test.js +0 -0
  67. package/test/localStorageMock.js +0 -0
  68. 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
File without changes
package/jest.config.js CHANGED
File without changes
package/link_mcs.sh CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.3.20",
3
+ "version": "2.3.22",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
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
  };
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.
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
@@ -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 patchDataHandler(url, data)
733
+ return await putDataHandler(url, data)
730
734
  }
731
735
 
732
736
  /**
File without changes
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
File without changes
package/test/log.js CHANGED
File without changes