musora-content-services 1.2.5 → 1.3.1

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.
@@ -1,210 +1,225 @@
1
1
  import {
2
- fetchContentProgress,
3
- postContentCompleted,
4
- postContentReset,
5
- postRecordWatchSession
6
- } from "./railcontent.js";
7
- import {DataContext, ContentProgressVersionKey} from "./dataContext.js";
8
- import {fetchHierarchy} from "./sanity.js";
2
+ fetchContentProgress,
3
+ postContentCompleted,
4
+ postContentReset,
5
+ postRecordWatchSession,
6
+ } from './railcontent.js'
7
+ import { DataContext, ContentProgressVersionKey } from './dataContext.js'
8
+ import { fetchHierarchy } from './sanity.js'
9
9
 
10
- const STATE_STARTED = 'started';
11
- const STATE_COMPLETED = 'completed';
12
- const DATA_KEY_STATUS = 's';
13
- const DATA_KEY_PROGRESS = 'p';
14
- const DATA_KEY_RESUME_TIME = 't';
15
- const DATA_KEY_LAST_UPDATED_TIME = 'u';
16
- export let dataContext = new DataContext(ContentProgressVersionKey, fetchContentProgress);
10
+ const STATE_STARTED = 'started'
11
+ const STATE_COMPLETED = 'completed'
12
+ const DATA_KEY_STATUS = 's'
13
+ const DATA_KEY_PROGRESS = 'p'
14
+ const DATA_KEY_RESUME_TIME = 't'
15
+ const DATA_KEY_LAST_UPDATED_TIME = 'u'
16
+ export let dataContext = new DataContext(ContentProgressVersionKey, fetchContentProgress)
17
17
 
18
18
  export async function getProgressPercentage(contentId) {
19
- let data = await dataContext.getData();
20
- return data[contentId]?.[DATA_KEY_PROGRESS] ?? 0;
19
+ let data = await dataContext.getData()
20
+ return data[contentId]?.[DATA_KEY_PROGRESS] ?? 0
21
21
  }
22
22
 
23
23
  export async function getProgressPercentageByIds(contentIds) {
24
- const data = await dataContext.getData();
25
- let progress = {};
24
+ const data = await dataContext.getData()
25
+ let progress = {}
26
26
 
27
- contentIds?.forEach(id => progress[id] = data[id]?.[DATA_KEY_PROGRESS] ?? 0);
27
+ contentIds?.forEach((id) => (progress[id] = data[id]?.[DATA_KEY_PROGRESS] ?? 0))
28
28
 
29
- return progress;
29
+ return progress
30
30
  }
31
31
 
32
32
  export async function getProgressState(contentId) {
33
- let data = await dataContext.getData();
34
- return data[contentId]?.[DATA_KEY_STATUS] ?? "";
33
+ let data = await dataContext.getData()
34
+ return data[contentId]?.[DATA_KEY_STATUS] ?? ''
35
35
  }
36
36
 
37
37
  export async function getProgressStateByIds(contentIds) {
38
- const data = await dataContext.getData();
39
- let progress = {};
38
+ const data = await dataContext.getData()
39
+ let progress = {}
40
40
 
41
- contentIds?.forEach(id => progress[id] = data[id]?.[DATA_KEY_STATUS] ?? "");
41
+ contentIds?.forEach((id) => (progress[id] = data[id]?.[DATA_KEY_STATUS] ?? ''))
42
42
 
43
- return progress;
43
+ return progress
44
44
  }
45
45
 
46
46
  export async function getAllStarted(limit = null) {
47
- const data = await dataContext.getData();
48
- let ids = Object.keys(data).filter(function (key) {
49
- return data[parseInt(key)][DATA_KEY_STATUS] === STATE_STARTED;
50
- }).map(function (key) {
51
- return parseInt(key);
52
- }).sort(function (a, b) {
53
- let v1 = data[a][DATA_KEY_LAST_UPDATED_TIME];
54
- let v2 = data[b][DATA_KEY_LAST_UPDATED_TIME];
55
- if (v1 > v2) return -1;
56
- else if (v1 < v2) return 1;
57
- return 0;
58
- });
59
- if (limit) {
60
- ids = ids.slice(0, limit);
61
- }
62
- return ids;
47
+ const data = await dataContext.getData()
48
+ let ids = Object.keys(data)
49
+ .filter(function (key) {
50
+ return data[parseInt(key)][DATA_KEY_STATUS] === STATE_STARTED
51
+ })
52
+ .map(function (key) {
53
+ return parseInt(key)
54
+ })
55
+ .sort(function (a, b) {
56
+ let v1 = data[a][DATA_KEY_LAST_UPDATED_TIME]
57
+ let v2 = data[b][DATA_KEY_LAST_UPDATED_TIME]
58
+ if (v1 > v2) return -1
59
+ else if (v1 < v2) return 1
60
+ return 0
61
+ })
62
+ if (limit) {
63
+ ids = ids.slice(0, limit)
64
+ }
65
+ return ids
63
66
  }
64
67
 
65
68
  export async function getAllCompleted(limit = null) {
66
- const data = await dataContext.getData();
67
- let ids = Object.keys(data).filter(function (key) {
68
- return data[parseInt(key)][DATA_KEY_STATUS] === STATE_COMPLETED;
69
- }).map(function (key) {
70
- return parseInt(key);
71
- }).sort(function (a, b) {
72
- let v1 = data[a][DATA_KEY_LAST_UPDATED_TIME];
73
- let v2 = data[b][DATA_KEY_LAST_UPDATED_TIME];
74
- if (v1 > v2) return -1;
75
- else if (v1 < v2) return 1;
76
- return 0;
77
- });
78
- if (limit) {
79
- ids = ids.slice(0, limit);
80
- }
81
- return ids;
69
+ const data = await dataContext.getData()
70
+ let ids = Object.keys(data)
71
+ .filter(function (key) {
72
+ return data[parseInt(key)][DATA_KEY_STATUS] === STATE_COMPLETED
73
+ })
74
+ .map(function (key) {
75
+ return parseInt(key)
76
+ })
77
+ .sort(function (a, b) {
78
+ let v1 = data[a][DATA_KEY_LAST_UPDATED_TIME]
79
+ let v2 = data[b][DATA_KEY_LAST_UPDATED_TIME]
80
+ if (v1 > v2) return -1
81
+ else if (v1 < v2) return 1
82
+ return 0
83
+ })
84
+ if (limit) {
85
+ ids = ids.slice(0, limit)
86
+ }
87
+ return ids
82
88
  }
83
89
 
84
90
  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;
91
+ const data = await dataContext.getData()
92
+ let ids = Object.keys(data)
93
+ .filter(function (key) {
94
+ return (
95
+ data[parseInt(key)][DATA_KEY_STATUS] === STATE_STARTED ||
96
+ data[parseInt(key)][DATA_KEY_STATUS] === STATE_COMPLETED
97
+ )
98
+ })
99
+ .map(function (key) {
100
+ return parseInt(key)
101
+ })
102
+ .sort(function (a, b) {
103
+ let v1 = data[a][DATA_KEY_LAST_UPDATED_TIME]
104
+ let v2 = data[b][DATA_KEY_LAST_UPDATED_TIME]
105
+ if (v1 > v2) return -1
106
+ else if (v1 < v2) return 1
107
+ return 0
108
+ })
109
+ if (limit) {
110
+ ids = ids.slice(0, limit)
111
+ }
112
+ return ids
101
113
  }
102
114
 
103
115
  export async function getResumeTimeSeconds(contentId) {
104
- let data = await dataContext.getData();
105
- return data[contentId]?.[DATA_KEY_RESUME_TIME] ?? 0;
116
+ let data = await dataContext.getData()
117
+ return data[contentId]?.[DATA_KEY_RESUME_TIME] ?? 0
106
118
  }
107
119
 
108
120
  export async function assignmentStatusCompleted(assignmentId, parentContentId) {
109
- await dataContext.update(
110
- async function (localContext) {
111
- let hierarchy = await fetchHierarchy(parentContentId);
112
- completeStatusInLocalContext(localContext, assignmentId, hierarchy);
113
- },
114
- async function () {
115
- return postContentCompleted(assignmentId);
116
- });
121
+ await dataContext.update(
122
+ async function (localContext) {
123
+ let hierarchy = await fetchHierarchy(parentContentId)
124
+ completeStatusInLocalContext(localContext, assignmentId, hierarchy)
125
+ },
126
+ async function () {
127
+ return postContentCompleted(assignmentId)
128
+ }
129
+ )
117
130
  }
118
131
 
119
132
  export async function contentStatusCompleted(contentId) {
120
- await dataContext.update(
121
- async function (localContext) {
122
- let hierarchy = await fetchHierarchy(contentId);
123
- completeStatusInLocalContext(localContext, contentId, hierarchy);
124
- },
125
- async function () {
126
- return postContentCompleted(contentId);
127
- });
133
+ await dataContext.update(
134
+ async function (localContext) {
135
+ let hierarchy = await fetchHierarchy(contentId)
136
+ completeStatusInLocalContext(localContext, contentId, hierarchy)
137
+ },
138
+ async function () {
139
+ return postContentCompleted(contentId)
140
+ }
141
+ )
128
142
  }
129
143
 
130
-
131
144
  function saveContentProgress(localContext, contentId, progress, currentSeconds, hierarchy) {
132
- if (progress === 100) {
133
- completeStatusInLocalContext(localContext, contentId, hierarchy);
134
- return;
135
- }
145
+ if (progress === 100) {
146
+ completeStatusInLocalContext(localContext, contentId, hierarchy)
147
+ return
148
+ }
136
149
 
137
- let data = localContext.data[contentId] ?? {};
138
- const currentProgress = data[DATA_KEY_STATUS];
139
- if (!currentProgress || currentProgress !== STATE_COMPLETED) {
140
- data[DATA_KEY_PROGRESS] = progress;
141
- data[DATA_KEY_STATUS] = STATE_STARTED;
142
- }
143
- data[DATA_KEY_RESUME_TIME] = currentSeconds;
144
- data[DATA_KEY_LAST_UPDATED_TIME] = Math.round(new Date().getTime() / 1000);
145
- localContext.data[contentId] = data;
150
+ let data = localContext.data[contentId] ?? {}
151
+ const currentProgress = data[DATA_KEY_STATUS]
152
+ if (!currentProgress || currentProgress !== STATE_COMPLETED) {
153
+ data[DATA_KEY_PROGRESS] = progress
154
+ data[DATA_KEY_STATUS] = STATE_STARTED
155
+ }
156
+ data[DATA_KEY_RESUME_TIME] = currentSeconds
157
+ data[DATA_KEY_LAST_UPDATED_TIME] = Math.round(new Date().getTime() / 1000)
158
+ localContext.data[contentId] = data
146
159
 
147
- bubbleProgress(hierarchy, contentId, localContext);
160
+ bubbleProgress(hierarchy, contentId, localContext)
148
161
  }
149
162
 
150
163
  function completeStatusInLocalContext(localContext, contentId, hierarchy) {
151
- let data = localContext.data[contentId] ?? {};
152
- data[DATA_KEY_PROGRESS] = 100;
153
- data[DATA_KEY_STATUS] = STATE_COMPLETED;
154
- data[DATA_KEY_LAST_UPDATED_TIME] = Math.round(new Date().getTime() / 1000);
155
- localContext.data[contentId] = data;
164
+ let data = localContext.data[contentId] ?? {}
165
+ data[DATA_KEY_PROGRESS] = 100
166
+ data[DATA_KEY_STATUS] = STATE_COMPLETED
167
+ data[DATA_KEY_LAST_UPDATED_TIME] = Math.round(new Date().getTime() / 1000)
168
+ localContext.data[contentId] = data
156
169
 
157
- if (!hierarchy) return;
158
- let children = hierarchy.children[contentId] ?? [];
159
- for (let i = 0; i < children.length; i++) {
160
- let childId = children[i];
161
- completeStatusInLocalContext(localContext, childId, hierarchy);
162
- }
163
- bubbleProgress(hierarchy, contentId, localContext);
170
+ if (!hierarchy) return
171
+ let children = hierarchy.children[contentId] ?? []
172
+ for (let i = 0; i < children.length; i++) {
173
+ let childId = children[i]
174
+ completeStatusInLocalContext(localContext, childId, hierarchy)
175
+ }
176
+ bubbleProgress(hierarchy, contentId, localContext)
164
177
  }
165
178
 
166
179
  function getChildrenToDepth(parentId, hierarchy, depth = 1) {
167
- let childIds = hierarchy.children[parentId] ?? [];
168
- let allChildrenIds = childIds;
169
- childIds.forEach(id => {
170
- allChildrenIds = allChildrenIds.concat(getChildrenToDepth(id, hierarchy, depth - 1));
171
- })
172
- return allChildrenIds;
180
+ let childIds = hierarchy.children[parentId] ?? []
181
+ let allChildrenIds = childIds
182
+ childIds.forEach((id) => {
183
+ allChildrenIds = allChildrenIds.concat(getChildrenToDepth(id, hierarchy, depth - 1))
184
+ })
185
+ return allChildrenIds
173
186
  }
174
187
 
175
-
176
188
  export async function assignmentStatusReset(assignmentId, contentId) {
177
- await dataContext.update(
178
- async function (localContext) {
179
- let hierarchy = await fetchHierarchy(contentId);
180
- resetStatusInLocalContext(localContext, assignmentId, hierarchy);
181
- },
182
- async function () {
183
- return postContentReset(contentId);
184
- });
189
+ await dataContext.update(
190
+ async function (localContext) {
191
+ let hierarchy = await fetchHierarchy(contentId)
192
+ resetStatusInLocalContext(localContext, assignmentId, hierarchy)
193
+ },
194
+ async function () {
195
+ return postContentReset(contentId)
196
+ }
197
+ )
185
198
  }
186
199
 
187
200
  export async function contentStatusReset(contentId) {
188
- await dataContext.update(
189
- async function (localContext) {
190
- let hierarchy = await fetchHierarchy(contentId);
191
- resetStatusInLocalContext(localContext, contentId, hierarchy);
192
- },
193
- async function () {
194
- return postContentReset(contentId);
195
- });
201
+ await dataContext.update(
202
+ async function (localContext) {
203
+ let hierarchy = await fetchHierarchy(contentId)
204
+ resetStatusInLocalContext(localContext, contentId, hierarchy)
205
+ },
206
+ async function () {
207
+ return postContentReset(contentId)
208
+ }
209
+ )
196
210
  }
197
211
 
198
212
  function resetStatusInLocalContext(localContext, contentId, hierarchy) {
199
- let allChildIds = getChildrenToDepth(contentId, hierarchy, 5);
200
- allChildIds.push(contentId);
201
- allChildIds.forEach(id => {
202
- const index = Object.keys(localContext.data).indexOf(id.toString());
203
- if (index > -1) { // only splice array when item is found
204
- delete localContext.data[id];
205
- }
206
- });
207
- bubbleProgress(hierarchy, contentId, localContext);
213
+ let allChildIds = getChildrenToDepth(contentId, hierarchy, 5)
214
+ allChildIds.push(contentId)
215
+ allChildIds.forEach((id) => {
216
+ const index = Object.keys(localContext.data).indexOf(id.toString())
217
+ if (index > -1) {
218
+ // only splice array when item is found
219
+ delete localContext.data[id]
220
+ }
221
+ })
222
+ bubbleProgress(hierarchy, contentId, localContext)
208
223
  }
209
224
 
210
225
  /**
@@ -218,63 +233,81 @@ function resetStatusInLocalContext(localContext, contentId, hierarchy) {
218
233
  * @param {int} secondsPlayed
219
234
  * @param {string} sessionId - This function records a sessionId to pass into future updates to progress on the same video
220
235
  */
221
- export async function recordWatchSession(contentId, mediaType, mediaCategory, mediaLengthSeconds, currentSeconds, secondsPlayed, sessionId = null) {
222
- let mediaTypeId = getMediaTypeId(mediaType, mediaCategory);
223
- let updateLocalProgress = mediaTypeId === 1 || mediaTypeId === 2; //only update for video playback
224
- if (!sessionId) {
225
- sessionId = uuidv4();
226
- }
227
- await dataContext.update(
228
- async function (localContext) {
229
- if (contentId && updateLocalProgress) {
230
- if (mediaLengthSeconds <= 0) {
231
- return;
232
- }
233
- let progress = Math.min(99, Math.round((currentSeconds ?? 0) / Math.max(1, mediaLengthSeconds ?? 0) * 100));
234
- let hierarchy = await fetchHierarchy(contentId);
235
- saveContentProgress(localContext, contentId, progress, currentSeconds, hierarchy);
236
- }
237
- },
238
- async function () {
239
- return postRecordWatchSession(contentId, mediaTypeId, mediaLengthSeconds, currentSeconds, secondsPlayed, sessionId);
236
+ export async function recordWatchSession(
237
+ contentId,
238
+ mediaType,
239
+ mediaCategory,
240
+ mediaLengthSeconds,
241
+ currentSeconds,
242
+ secondsPlayed,
243
+ sessionId = null
244
+ ) {
245
+ let mediaTypeId = getMediaTypeId(mediaType, mediaCategory)
246
+ let updateLocalProgress = mediaTypeId === 1 || mediaTypeId === 2 //only update for video playback
247
+ if (!sessionId) {
248
+ sessionId = uuidv4()
249
+ }
250
+ await dataContext.update(
251
+ async function (localContext) {
252
+ if (contentId && updateLocalProgress) {
253
+ if (mediaLengthSeconds <= 0) {
254
+ return
240
255
  }
241
- );
242
- return sessionId;
256
+ let progress = Math.min(
257
+ 99,
258
+ Math.round(((currentSeconds ?? 0) / Math.max(1, mediaLengthSeconds ?? 0)) * 100)
259
+ )
260
+ let hierarchy = await fetchHierarchy(contentId)
261
+ saveContentProgress(localContext, contentId, progress, currentSeconds, hierarchy)
262
+ }
263
+ },
264
+ async function () {
265
+ return postRecordWatchSession(
266
+ contentId,
267
+ mediaTypeId,
268
+ mediaLengthSeconds,
269
+ currentSeconds,
270
+ secondsPlayed,
271
+ sessionId
272
+ )
273
+ }
274
+ )
275
+ return sessionId
243
276
  }
244
277
 
245
278
  function getMediaTypeId(mediaType, mediaCategory) {
246
- switch (`${mediaType}_${mediaCategory}`) {
247
- case "video_youtube":
248
- return 1;
249
- case "video_vimeo":
250
- return 2;
251
- case "assignment_soundslice":
252
- return 3;
253
- case "practice_play-alongs":
254
- return 4;
255
- default:
256
- throw Error(`Unsupported media type: ${mediaType}_${mediaCategory}`);
257
- }
279
+ switch (`${mediaType}_${mediaCategory}`) {
280
+ case 'video_youtube':
281
+ return 1
282
+ case 'video_vimeo':
283
+ return 2
284
+ case 'assignment_soundslice':
285
+ return 3
286
+ case 'practice_play-alongs':
287
+ return 4
288
+ default:
289
+ throw Error(`Unsupported media type: ${mediaType}_${mediaCategory}`)
290
+ }
258
291
  }
259
292
 
260
293
  function uuidv4() {
261
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
262
- var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
263
- return v.toString(16);
264
- });
294
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
295
+ var r = (Math.random() * 16) | 0,
296
+ v = c === 'x' ? r : (r & 0x3) | 0x8
297
+ return v.toString(16)
298
+ })
265
299
  }
266
300
 
267
301
  function bubbleProgress(hierarchy, contentId, localContext) {
268
- let parentId = hierarchy?.parents?.[contentId];
269
- if (!parentId) return;
270
- let data = localContext.data[parentId] ?? {};
271
- let childProgress = hierarchy?.children?.[parentId]?.map(function (childId) {
272
- return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0;
273
- });
274
- let progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length);
275
- data[DATA_KEY_PROGRESS] = progress;
276
- data[DATA_KEY_STATUS] = progress === 100 ? STATE_COMPLETED : STATE_STARTED;
277
- localContext.data[parentId] = data;
278
- bubbleProgress(hierarchy, parentId, localContext);
302
+ let parentId = hierarchy?.parents?.[contentId]
303
+ if (!parentId) return
304
+ let data = localContext.data[parentId] ?? {}
305
+ let childProgress = hierarchy?.children?.[parentId]?.map(function (childId) {
306
+ return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0
307
+ })
308
+ let progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length)
309
+ data[DATA_KEY_PROGRESS] = progress
310
+ data[DATA_KEY_STATUS] = progress === 100 ? STATE_COMPLETED : STATE_STARTED
311
+ localContext.data[parentId] = data
312
+ bubbleProgress(hierarchy, parentId, localContext)
279
313
  }
280
-