musora-content-services 1.0.172 → 1.0.174

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 (64) hide show
  1. package/.github/workflows/node.js.yml +0 -0
  2. package/CHANGELOG.md +4 -0
  3. package/README.md +0 -0
  4. package/babel.config.js +0 -0
  5. package/docs/config.js.html +4 -3
  6. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  7. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  8. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  9. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  10. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  11. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  12. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  13. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  14. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  15. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  16. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  17. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  18. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  19. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  20. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  21. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  22. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  23. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  24. package/docs/index.html +2 -2
  25. package/docs/module-Config.html +28 -4
  26. package/docs/module-Railcontent-Services.html +340 -79
  27. package/docs/module-Sanity-Services.html +403 -47
  28. package/docs/railcontent.js.html +58 -48
  29. package/docs/sanity.js.html +650 -560
  30. package/docs/scripts/collapse.js +0 -0
  31. package/docs/scripts/commonNav.js +0 -0
  32. package/docs/scripts/linenumber.js +0 -0
  33. package/docs/scripts/nav.js +0 -0
  34. package/docs/scripts/polyfill.js +0 -0
  35. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  36. package/docs/scripts/prettify/lang-css.js +0 -0
  37. package/docs/scripts/prettify/prettify.js +0 -0
  38. package/docs/scripts/search.js +0 -0
  39. package/docs/styles/jsdoc.css +0 -0
  40. package/docs/styles/prettify.css +0 -0
  41. package/jest.config.js +0 -0
  42. package/jsdoc.json +0 -0
  43. package/package.json +1 -1
  44. package/src/contentMetaData.js +0 -0
  45. package/src/filterBuilder.js +0 -0
  46. package/src/index.d.ts +4 -0
  47. package/src/index.js +4 -0
  48. package/src/services/config.js +0 -0
  49. package/src/services/contentLikes.js +0 -0
  50. package/src/services/contentProgress.js +50 -19
  51. package/src/services/dataContext.js +0 -0
  52. package/src/services/lastUpdated.js +0 -0
  53. package/src/services/sanity.js +26 -2
  54. package/src/services/userPermissions.js +0 -0
  55. package/test/contentLikes.test.js +0 -0
  56. package/test/contentProgress.test.js +38 -2
  57. package/test/initializeTests.js +0 -0
  58. package/test/lastUpdated.test.js +0 -0
  59. package/test/live/contentProgressLive.test.js +30 -1
  60. package/test/localStorageMock.js +0 -0
  61. package/test/log.js +0 -0
  62. package/test/sanityQueryService.test.js +30 -0
  63. package/test/userPermissions.test.js +0 -0
  64. package/tools/generate-index.js +0 -0
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/jsdoc.json CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "1.0.172",
3
+ "version": "1.0.174",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
File without changes
File without changes
package/src/index.d.ts CHANGED
@@ -13,10 +13,12 @@ import {
13
13
 
14
14
  import {
15
15
  assignmentStatusCompleted,
16
+ assignmentStatusReset,
16
17
  contentStatusCompleted,
17
18
  contentStatusReset,
18
19
  getAllCompleted,
19
20
  getAllStarted,
21
+ getAllStartedOrCompleted,
20
22
  getProgressPercentage,
21
23
  getProgressPercentageByIds,
22
24
  getProgressState,
@@ -132,6 +134,7 @@ declare module 'musora-content-services' {
132
134
  export {
133
135
  addItemToPlaylist,
134
136
  assignmentStatusCompleted,
137
+ assignmentStatusReset,
135
138
  contentStatusCompleted,
136
139
  contentStatusReset,
137
140
  countAssignmentsAndLessons,
@@ -212,6 +215,7 @@ declare module 'musora-content-services' {
212
215
  fetchWorkouts,
213
216
  getAllCompleted,
214
217
  getAllStarted,
218
+ getAllStartedOrCompleted,
215
219
  getProgressPercentage,
216
220
  getProgressPercentageByIds,
217
221
  getProgressState,
package/src/index.js CHANGED
@@ -13,10 +13,12 @@ import {
13
13
 
14
14
  import {
15
15
  assignmentStatusCompleted,
16
+ assignmentStatusReset,
16
17
  contentStatusCompleted,
17
18
  contentStatusReset,
18
19
  getAllCompleted,
19
20
  getAllStarted,
21
+ getAllStartedOrCompleted,
20
22
  getProgressPercentage,
21
23
  getProgressPercentageByIds,
22
24
  getProgressState,
@@ -131,6 +133,7 @@ import {
131
133
  export {
132
134
  addItemToPlaylist,
133
135
  assignmentStatusCompleted,
136
+ assignmentStatusReset,
134
137
  contentStatusCompleted,
135
138
  contentStatusReset,
136
139
  countAssignmentsAndLessons,
@@ -211,6 +214,7 @@ export {
211
214
  fetchWorkouts,
212
215
  getAllCompleted,
213
216
  getAllStarted,
217
+ getAllStartedOrCompleted,
214
218
  getProgressPercentage,
215
219
  getProgressPercentageByIds,
216
220
  getProgressState,
File without changes
File without changes
@@ -81,6 +81,25 @@ export async function getAllCompleted(limit = null) {
81
81
  return ids;
82
82
  }
83
83
 
84
+ export async function getAllStartedOrCompleted(limit = null) {
85
+ const data = await dataContext.getData();
86
+ let ids = Object.keys(data).filter(function (key) {
87
+ return data[parseInt(key)][DATA_KEY_STATUS] === STATE_STARTED || data[parseInt(key)][DATA_KEY_STATUS] === STATE_COMPLETED;
88
+ }).map(function (key) {
89
+ return parseInt(key);
90
+ }).sort(function (a, b) {
91
+ let v1 = data[a][DATA_KEY_LAST_UPDATED_TIME];
92
+ let v2 = data[b][DATA_KEY_LAST_UPDATED_TIME];
93
+ if (v1 > v2) return -1;
94
+ else if (v1 < v2) return 1;
95
+ return 0;
96
+ });
97
+ if (limit) {
98
+ ids = ids.slice(0, limit);
99
+ }
100
+ return ids;
101
+ }
102
+
84
103
  export async function getResumeTimeSeconds(contentId) {
85
104
  let data = await dataContext.getData();
86
105
  return data[contentId]?.[DATA_KEY_RESUME_TIME] ?? 0;
@@ -151,24 +170,40 @@ function getChildrenToDepth(parentId, hierarchy, depth = 1) {
151
170
  }
152
171
 
153
172
 
173
+ export async function assignmentStatusReset(assignmentId, contentId) {
174
+ await dataContext.update(
175
+ async function (localContext) {
176
+ let hierarchy = await fetchHierarchy(contentId);
177
+ resetStatusInLocalContext(localContext, assignmentId, hierarchy);
178
+ },
179
+ async function () {
180
+ return postContentReset(contentId);
181
+ });
182
+ }
183
+
154
184
  export async function contentStatusReset(contentId) {
155
185
  await dataContext.update(
156
186
  async function (localContext) {
157
187
  let hierarchy = await fetchHierarchy(contentId);
158
- let allChildIds = getChildrenToDepth(contentId, hierarchy, 5);
159
- allChildIds.push(contentId);
160
- allChildIds.forEach(id => {
161
- const index = Object.keys(localContext.data).indexOf(id.toString());
162
- if (index > -1) { // only splice array when item is found
163
- delete localContext.data[id];
164
- }
165
- });
188
+ resetStatusInLocalContext(localContext, contentId, hierarchy);
166
189
  },
167
190
  async function () {
168
191
  return postContentReset(contentId);
169
192
  });
170
193
  }
171
194
 
195
+ function resetStatusInLocalContext(localContext, contentId, hierarchy) {
196
+ let allChildIds = getChildrenToDepth(contentId, hierarchy, 5);
197
+ allChildIds.push(contentId);
198
+ allChildIds.forEach(id => {
199
+ const index = Object.keys(localContext.data).indexOf(id.toString());
200
+ if (index > -1) { // only splice array when item is found
201
+ delete localContext.data[id];
202
+ }
203
+ });
204
+ bubbleProgress(hierarchy, contentId, localContext);
205
+ }
206
+
172
207
  /**
173
208
  * Record watch session
174
209
  * @return {string} sessionId - provide in future calls to update progress
@@ -230,17 +265,13 @@ function bubbleProgress(hierarchy, contentId, localContext) {
230
265
  let parentId = hierarchy.parents[contentId];
231
266
  if (!parentId) return;
232
267
  let data = localContext.data[parentId] ?? {};
233
- let progress = data[DATA_KEY_PROGRESS];
234
- let status = data[DATA_KEY_STATUS];
235
- if (status !== STATE_COMPLETED && progress !== 100) {
236
- let childProgress = hierarchy.children[parentId].map(function (childId) {
237
- return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0;
238
- });
239
- progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length);
240
- data[DATA_KEY_PROGRESS] = progress;
241
- data[DATA_KEY_STATUS] = progress === 100 ? STATE_COMPLETED : STATE_STARTED;
242
- localContext.data[parentId] = data;
243
- }
268
+ let childProgress = hierarchy.children[parentId].map(function (childId) {
269
+ return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0;
270
+ });
271
+ let progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length);
272
+ data[DATA_KEY_PROGRESS] = progress;
273
+ data[DATA_KEY_STATUS] = progress === 100 ? STATE_COMPLETED : STATE_STARTED;
274
+ localContext.data[parentId] = data;
244
275
  bubbleProgress(hierarchy, parentId, localContext);
245
276
  }
246
277
 
File without changes
File without changes
@@ -26,6 +26,7 @@ import {globalConfig} from "./config";
26
26
  import {fetchAllCompletedStates, fetchCurrentSongComplete} from './railcontent.js';
27
27
  import {arrayToStringRepresentation, FilterBuilder} from "../filterBuilder";
28
28
  import {fetchUserPermissions} from "./userPermissions";
29
+ import {getAllCompleted, getAllStarted, getAllStartedOrCompleted} from "./contentProgress";
29
30
 
30
31
  /**
31
32
  * Exported functions that are excluded from index generation.
@@ -453,6 +454,7 @@ export async function fetchByRailContentIds(ids, contentType = undefined) {
453
454
  * @param {Array<string>} [params.progressIds=undefined] - An array of railcontent IDs to filter the results by. Used for filtering by progress.
454
455
  * @param {boolean} [params.useDefaultFields=true] - use the default sanity fields for content Type
455
456
  * @param {Array<string>} [params.customFields=[]] - An array of sanity fields to include in the request
457
+ * @param {string} [params.progress="all"] - An string representing which progress filter to use ("all", "in progress", "complete", "not started").
456
458
  * @returns {Promise<Object|null>} - The fetched content data or null if not found.
457
459
  *
458
460
  * @example
@@ -480,6 +482,7 @@ export async function fetchAll(brand, type, {
480
482
  progressIds = undefined,
481
483
  useDefaultFields = true,
482
484
  customFields = [],
485
+ progress = "all"
483
486
  } = {}) {
484
487
  let config = contentTypeConfig[type] ?? {};
485
488
  let additionalFields = config?.fields ?? [];
@@ -504,8 +507,7 @@ export async function fetchAll(brand, type, {
504
507
  : "";
505
508
 
506
509
  // limits the results to supplied progressIds for started & completed filters
507
- const progressFilter = progressIds !== undefined ?
508
- `&& railcontent_id in [${progressIds.join(',')}]` : "";
510
+ const progressFilter = await getProgressFilter(progress, progressIds);
509
511
 
510
512
  // Determine the sort order
511
513
  const sortOrder = getSortOrder(sort);
@@ -564,6 +566,28 @@ export async function fetchAll(brand, type, {
564
566
  return fetchSanity(query, true);
565
567
  }
566
568
 
569
+ async function getProgressFilter(progress, progressIds) {
570
+ switch (progress) {
571
+ case "all":
572
+ return progressIds !== undefined ?
573
+ `&& railcontent_id in [${progressIds.join(',')}]` : "";
574
+ case "in progress": {
575
+ const ids = await getAllStarted();
576
+ return `&& railcontent_id in [${ids.join(',')}]`;
577
+ }
578
+ case "completed": {
579
+ const ids = await getAllCompleted();
580
+ return `&& railcontent_id in [${ids.join(',')}]`;
581
+ }
582
+ case "not started": {
583
+ const ids = await getAllStartedOrCompleted();
584
+ return `&& !(railcontent_id in [${ids.join(',')}])`;
585
+ }
586
+ default:
587
+ throw new Error(`'${progress}' progress option not implemented`);
588
+ }
589
+ }
590
+
567
591
  export function getSortOrder(sort = '-published_on', groupBy) {
568
592
  // Determine the sort order
569
593
  let sortOrder = '';
File without changes
File without changes
@@ -7,7 +7,11 @@ import {
7
7
  getProgressStateByIds,
8
8
  getAllStarted,
9
9
  getAllCompleted,
10
- contentStatusCompleted, assignmentStatusCompleted, contentStatusReset
10
+ contentStatusCompleted,
11
+ assignmentStatusCompleted,
12
+ contentStatusReset,
13
+ assignmentStatusReset,
14
+ getAllStartedOrCompleted
11
15
  } from "../src/services/contentProgress";
12
16
  import {initializeTestService} from "./initializeTests";
13
17
  import {postContentCompleted} from "../src";
@@ -71,6 +75,12 @@ describe('contentProgressDataContext', function () {
71
75
  expect(result).toStrictEqual([233955]);
72
76
  });
73
77
 
78
+ test('getAllStartedOrCompleted', async () => {
79
+ let result = await getAllStartedOrCompleted();
80
+ expect(result).toStrictEqual([259426, 233955, 234191]);
81
+ });
82
+
83
+
74
84
  // test('getAllStartedWithUpdate', async () => {
75
85
  // let mock2 = jest.spyOn(railContentModule, 'postRecordWatchSession');
76
86
  // let serverVersion = 2;
@@ -148,6 +158,33 @@ describe('contentProgressDataContext', function () {
148
158
  // state = await getProgressState(contentId); //assignment
149
159
  // expect(state).toBe("completed");
150
160
  // });
161
+ //
162
+ // test('assignmentCompleteResetBubblingMultiple', async () => {
163
+ // let contentId = 281709;
164
+ //
165
+ // let state = await getProgressState(contentId);
166
+ // expect(state).toBe("");
167
+ //
168
+ // let assignmentIds = [281710, 281711, 281712, 281713, 281714, 281715];
169
+ // for (const assignmentId of assignmentIds) {
170
+ // await assignmentStatusCompleted(assignmentId, contentId);
171
+ // state = await getProgressState(assignmentId);
172
+ // expect(state).toBe("completed");
173
+ // }
174
+ //
175
+ // state = await getProgressState(contentId);
176
+ // expect(state).toBe("completed");
177
+ //
178
+ // await assignmentStatusReset(assignmentIds[0], contentId);
179
+ // state = await getProgressState(assignmentIds[0]);
180
+ // expect(state).toBe("");
181
+ // state = await getProgressState(contentId);
182
+ // expect(state).toBe("started");
183
+ // let percentage = await getProgressPercentage(contentId);
184
+ // expect(percentage).toBe(83);
185
+ // });
186
+
187
+
151
188
  //
152
189
  // test('completeBubbling', async () => {
153
190
  // let state = await getProgressState(276698);
@@ -181,5 +218,4 @@ describe('contentProgressDataContext', function () {
181
218
  // });
182
219
 
183
220
 
184
-
185
221
  });
File without changes
File without changes
@@ -2,7 +2,7 @@ import {
2
2
  recordWatchSession,
3
3
  getProgressPercentage,
4
4
  dataContext,
5
- getProgressState, contentStatusCompleted, contentStatusReset
5
+ getProgressState, contentStatusCompleted, contentStatusReset, assignmentStatusCompleted
6
6
  } from "../../src/services/contentProgress";
7
7
  import {initializeTestService} from "../initializeTests";
8
8
 
@@ -80,6 +80,35 @@ describe('contentProgressDataContextLocal', function () {
80
80
  expect(result).toBe("");
81
81
  }, 100000);
82
82
 
83
+ test('assignmentCompleteBubblingToCompletedMultiple', async () => {
84
+ let contentId = 281709;
85
+ await contentStatusReset(contentId);
86
+
87
+ let state = await getProgressState(contentId);
88
+ expect(state).toBe("");
89
+
90
+ let assignmentIds = [281710, 281711, 281712, 281713, 281714, 281715];
91
+ for (const assignmentId of assignmentIds) {
92
+ await assignmentStatusCompleted(assignmentId, contentId);
93
+ state = await getProgressState(assignmentId);
94
+ expect(state).toBe("completed");
95
+ }
96
+
97
+ state = await getProgressState(contentId); //assignment
98
+ expect(state).toBe("completed");
99
+
100
+ dataContext.clearCache();
101
+
102
+ state = await getProgressState(contentId); //assignment
103
+ expect(state).toBe("completed");
104
+
105
+ for (const assignmentId of assignmentIds) {
106
+ state = await getProgressState(assignmentId);
107
+ expect(state).toBe("completed");
108
+ }
109
+
110
+ }, 100000);
111
+
83
112
 
84
113
  //
85
114
  // test('progressBubbling', async () => {
File without changes
package/test/log.js CHANGED
File without changes
@@ -2,6 +2,7 @@ import {getFieldsForContentType} from "../src/contentTypeConfig";
2
2
  import {fetchAssignments, fetchCommentModContentData, fetchSanity} from "../src/services/sanity";
3
3
  import {log} from './log.js';
4
4
  import {initializeTestService} from "./initializeTests";
5
+ import {dataContext} from "../src/services/contentProgress";
5
6
 
6
7
  const {
7
8
  fetchSongById,
@@ -158,6 +159,35 @@ describe('Sanity Queries', function () {
158
159
  expect(response.entity[0].instrumentless).toBeDefined();
159
160
  });
160
161
 
162
+ test('fetchAllSongsInProgress', async () => {
163
+ var mock = jest.spyOn(dataContext, 'fetchData');
164
+ var json = JSON.parse(`{"version":1,"config":{"key":1,"enabled":1,"checkInterval":1,"refreshInterval":2},"data":{"232979":{"s":"started","p":6,"t":20,"u":1731108082}}}`);
165
+ mock.mockImplementation(() =>
166
+ json);
167
+ const response = await fetchAll('drumeo', 'song',{progress:"in progress"});
168
+ expect(response.entity[0].id).toBe(232979);
169
+ expect(response.entity.length).toBe(1);
170
+ });
171
+
172
+ test('fetchAllSongsCompleted', async () => {
173
+ var mock = jest.spyOn(dataContext, 'fetchData');
174
+ var json = JSON.parse(`{"version":1,"config":{"key":1,"enabled":1,"checkInterval":1,"refreshInterval":2},"data":{"232979":{"s":"completed","p":100,"t":20,"u":1731108082}}}`);
175
+ mock.mockImplementation(() =>
176
+ json);
177
+ const response = await fetchAll('drumeo', 'song', {progress:"completed"});
178
+ expect(response.entity[0].id).toBe(232979);
179
+ expect(response.entity.length).toBe(1);
180
+ });
181
+
182
+ test('fetchAllSongsNotStarted', async () => {
183
+ var mock = jest.spyOn(dataContext, 'fetchData');
184
+ var json = JSON.parse(`{"version":1,"config":{"key":1,"enabled":1,"checkInterval":1,"refreshInterval":2},"data":{"198122":{"s":"started","p":100,"t":20,"u":1731108082},"231622":{"s":"completed","p":100,"t":20,"u":1731108082}}}`);
185
+ mock.mockImplementation(() =>
186
+ json); const response = await fetchAll('drumeo', 'song', {progress:"not started"});
187
+ expect(response.entity[0].id).not.toBe(198122);
188
+ expect(response.entity[0].id).not.toBe(231622);
189
+ });
190
+
161
191
  test('fetchSongFilterOptions', async () => {
162
192
  const response = await fetchSongFilterOptions('drumeo', {});
163
193
  log(response);
File without changes
File without changes