musora-content-services 1.0.70 → 1.0.72

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/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
+ ### [1.0.72](https://github.com/railroadmedia/musora-content-services/compare/v1.0.71...v1.0.72) (2024-08-29)
6
+
7
+ ### [1.0.71](https://github.com/railroadmedia/musora-content-services/compare/v1.0.70...v1.0.71) (2024-08-28)
8
+
5
9
  ### [1.0.70](https://github.com/railroadmedia/musora-content-services/compare/v1.0.69...v1.0.70) (2024-08-27)
6
10
 
7
11
  ### [1.0.69](https://github.com/railroadmedia/musora-content-services/compare/v1.0.68...v1.0.69) (2024-08-27)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "1.0.70",
3
+ "version": "1.0.72",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -85,6 +85,11 @@ let contentTypeConfig = {
85
85
  'xp',
86
86
  ]
87
87
  },
88
+ 'workout': {
89
+ 'fields': [
90
+ artistOrInstructorNameAsArray(),
91
+ ]
92
+ },
88
93
  'quick-tips': {
89
94
  'fields': [
90
95
  '"instructors": instructor[]->name'
@@ -220,6 +225,7 @@ let contentTypeConfig = {
220
225
  '"lesson_count": child_count',
221
226
  'xp',
222
227
  `"description": ${descriptionField}`,
228
+ '"instructors": instructor[]->name'
223
229
  ],
224
230
  }
225
231
  }
@@ -237,12 +243,37 @@ function getFieldsForContentType(contentType, asQueryString=true) {
237
243
  const fields = contentType ? DEFAULT_FIELDS.concat(contentTypeConfig?.[contentType]?.fields ?? []) : DEFAULT_FIELDS;
238
244
  return asQueryString ? fields.toString() + ',' : fields;
239
245
  }
246
+ /**
247
+ * Takes the included fields array and returns a string that can be used in a groq query.
248
+ * @param {Array<string>} filters - An array of strings that represent applied filters. This should be in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock']
249
+ * @returns {string} - A string that can be used in a groq query
250
+ */
251
+ function filtersToGroq(filters) {
252
+ const groq = filters.map(field => {
253
+ let [key, value] = field.split(',');
254
+ switch (key) {
255
+ case 'difficulty':
256
+ return `&& difficulty_string == "${value}"`;
257
+ case 'genre':
258
+ return `&& genre[]->name match "${value}"`;
259
+ case 'topic':
260
+ return `&& topic[]->name match "${value}"`;
261
+ case 'instrumentless':
262
+ return `&& instrumentless == ${value}`;
263
+ default:
264
+ return `&& ${key} == "${value}"`;
265
+ }
266
+ }).join(' ');
267
+ return groq;
268
+ }
240
269
 
241
270
  module.exports = {
242
271
  contentTypeConfig,
272
+ descriptionField,
243
273
  artistOrInstructorName,
244
274
  artistOrInstructorNameAsArray,
245
275
  getFieldsForContentType,
246
276
  DEFAULT_FIELDS,
247
- assignmentsField
277
+ assignmentsField,
278
+ filtersToGroq,
248
279
  }
package/src/index.js CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  fetchChildren,
30
30
  fetchParentByRailContentId,
31
31
  fetchMethodPreviousNextLesson,
32
+ fetchLiveEvent,
32
33
  } from './services/sanity.js';
33
34
 
34
35
  import {
@@ -71,4 +72,5 @@ export {
71
72
  fetchChildren,
72
73
  fetchParentByRailContentId,
73
74
  fetchMethodPreviousNextLesson,
75
+ fetchLiveEvent,
74
76
  }
@@ -1,7 +1,16 @@
1
1
  /**
2
2
  * @module Sanity-Services
3
3
  */
4
- import {assignmentsField, contentTypeConfig, DEFAULT_FIELDS, getFieldsForContentType} from "../contentTypeConfig";
4
+ import {
5
+ artistOrInstructorName,
6
+ artistOrInstructorNameAsArray,
7
+ assignmentsField,
8
+ descriptionField,
9
+ contentTypeConfig,
10
+ DEFAULT_FIELDS,
11
+ getFieldsForContentType,
12
+ filtersToGroq
13
+ } from "../contentTypeConfig";
5
14
  import {globalConfig} from "./config";
6
15
 
7
16
  import { fetchAllCompletedStates, fetchCurrentSongComplete } from './railcontent.js';
@@ -232,18 +241,9 @@ export async function fetchSongCount(brand) {
232
241
  * .catch(error => console.error(error));
233
242
  */
234
243
  export async function fetchWorkouts(brand) {
244
+ const fields = getFieldsForContentType('workout');
235
245
  const query = `*[_type == 'workout' && brand == '${brand}'] [0...5] {
236
- "id": railcontent_id,
237
- title,
238
- "image": thumbnail.asset->url,
239
- "artist_name": instructor[0]->name,
240
- "artists": instructor[]->name,
241
- difficulty,
242
- difficulty_string,
243
- length_in_seconds,
244
- published_on,
245
- "type": _type,
246
- web_url_path,
246
+ ${fields.toString()}
247
247
  } | order(published_on desc)[0...5]`
248
248
  return fetchSanity(query, true);
249
249
  }
@@ -301,8 +301,7 @@ export async function fetchUpcomingEvents(brand) {
301
301
  };
302
302
  const typesString = arrayJoinWithQuotes(liveTypes[brand] ?? liveTypes['default']);
303
303
  const now = getSanityDate(new Date());
304
- //TODO: status = 'scheduled' is this handled in sanity?
305
- const query = `*[_type in [${typesString}] && brand == '${brand}' && published_on > '${now}']{
304
+ const query = `*[_type in [${typesString}] && brand == '${brand}' && published_on > '${now}' && status == 'scheduled']{
306
305
  "id": railcontent_id,
307
306
  title,
308
307
  "image": thumbnail.asset->url,
@@ -404,13 +403,7 @@ export async function fetchAll(brand, type, {
404
403
 
405
404
  // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
406
405
  const includedFieldsFilter = includedFields.length > 0
407
- ? includedFields.map(field => {
408
- let [key, value] = field.split(',');
409
- if (key === 'difficulty') {
410
- key = 'difficulty_string';
411
- }
412
- return `&& ${key} == "${value}"`;
413
- }).join(' ')
406
+ ? filtersToGroq(includedFields)
414
407
  : "";
415
408
 
416
409
  // Determine the sort order
@@ -524,16 +517,9 @@ export async function fetchAllFilterOptions(
524
517
  contentType,
525
518
  term
526
519
  ) {
527
- const filtersToGroq = filters?.length > 0 ? filters.map(field => {
528
- let [key, value] = field.split(',');
529
- if (key === 'difficulty') {
530
- key = 'difficulty_string';
531
- }
532
- return `&& ${key} == "${value}"`;
533
- }).join(' ')
534
- : undefined;
520
+ const includedFieldsFilter = filters?.length > 0 ? filtersToGroq(filters) : undefined;
535
521
 
536
- const commonFilter = `_type == '${contentType}' && brand == "${brand}"${style ? ` && '${style}' in genre[]->name` : ''}${artist ? ` && artist->name == '${artist}'` : ''} ${filtersToGroq ? filtersToGroq : ''}`;
522
+ const commonFilter = `_type == '${contentType}' && brand == "${brand}"${style ? ` && '${style}' in genre[]->name` : ''}${artist ? ` && artist->name == '${artist}'` : ''} ${includedFieldsFilter ? includedFieldsFilter : ''}`;
537
523
  const query = `
538
524
  {
539
525
  "meta": {
@@ -569,6 +555,8 @@ export async function fetchAllFilterOptions(
569
555
  */
570
556
  export async function fetchChildren(railcontentId, contentType) {
571
557
  const query = `*[railcontent_id == ${railcontentId}]{
558
+ title,
559
+
572
560
  'children': child[]->{
573
561
  ${getFieldsForContentType(contentType)}
574
562
  },
@@ -600,11 +588,8 @@ export async function fetchParentByRailContentId(railcontentId) {
600
588
  * @returns {Promise<Object|null>} - The fetched methods data or null if not found.
601
589
  */
602
590
  export async function fetchMethods(brand) {
603
- //TODOS
604
- //ADD INSTRUCTORS AND POSITION
605
591
  const query = `*[_type == 'learning-path' && brand == '${brand}'] {
606
- ${ getFieldsForContentType() },
607
- "position": count(*[_type == 'learning-path' && brand == '${brand}' && (published_on < ^.published_on || (published_on == ^.published_on && _id < ^._id))]) + 1
592
+ ${ getFieldsForContentType() }
608
593
  } | order(published_on asc)`
609
594
  return fetchSanity(query, true);
610
595
  }
@@ -617,7 +602,7 @@ export async function fetchMethods(brand) {
617
602
  */
618
603
  export async function fetchMethod(brand, slug) {
619
604
  const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
620
- "description": description[0].children[0].text,
605
+ "description": ${descriptionField},
621
606
  "instructors":instructor[]->name,
622
607
  published_on,
623
608
  "id": railcontent_id,
@@ -638,7 +623,7 @@ export async function fetchMethod(brand, slug) {
638
623
  "instructor": instructor[]->{name},
639
624
  title,
640
625
  "type": _type,
641
- "description": description[0].children[0].text,
626
+ "description": ${descriptionField},
642
627
  "url": web_url_path,
643
628
  xp,
644
629
  }
@@ -652,7 +637,16 @@ return fetchSanity(query, false);
652
637
  * @returns {Promise<Object|null>} - The fetched next lesson data or null if not found.
653
638
  */
654
639
  export async function fetchMethodChildren(railcontentId) {
655
- return fetchChildren(railcontentId, 'method');
640
+ const query = `*[railcontent_id == ${railcontentId}]{
641
+ child_count,
642
+ "description": ${descriptionField},
643
+ title,
644
+ xp,
645
+ 'children': child[]->{
646
+ ${getFieldsForContentType('method')}
647
+ },
648
+ }[0..1]`;
649
+ return fetchSanity(query, true);
656
650
  }
657
651
 
658
652
  /**
@@ -758,8 +752,23 @@ export async function fetchNextPreviousLesson(railcontentId) {
758
752
  * .catch(error => console.error(error));
759
753
  */
760
754
  export async function fetchLessonContent(railContentId) {
761
- const query = `*[railcontent_id == ${railContentId} ]
762
- {title, published_on,"type":_type, "resources": resource, difficulty, difficulty_string, brand, soundslice, instrumentless, railcontent_id, "id":railcontent_id, slug, artist->,"thumbnail_url":thumbnail.asset->url, "url": web_url_path, soundslice_slug,description,
755
+ const query = `*[railcontent_id == ${railContentId} ]{
756
+ title,
757
+ published_on,
758
+ "type":_type,
759
+ "resources": resource,
760
+ difficulty,
761
+ difficulty_string,
762
+ brand,
763
+ soundslice,
764
+ instrumentless,
765
+ railcontent_id,
766
+ "id":railcontent_id,
767
+ slug, artist->,
768
+ "thumbnail_url":thumbnail.asset->url,
769
+ "url": web_url_path,
770
+ soundslice_slug,
771
+ description,
763
772
  "chapters": chapter[]{
764
773
  chapter_description,
765
774
  chapter_timecode,
@@ -820,6 +829,49 @@ export async function fetchPackAll(railcontentId) {
820
829
  return fetchSanity(query, true);
821
830
  }
822
831
 
832
+ export async function fetchLiveEvent(brand) {
833
+ //calendarIDs taken from addevent.php
834
+ // TODO import instructor calendars to Sanity
835
+ let defaultCalendarID = '';
836
+ switch(brand) {
837
+ case ('drumeo'):
838
+ defaultCalendarID = 'GP142387';
839
+ break;
840
+ case ('pianote'):
841
+ defaultCalendarID = 'be142408';
842
+ break;
843
+ case ('guitareo'):
844
+ defaultCalendarID = 'IJ142407';
845
+ break;
846
+ case ('singeo'):
847
+ defaultCalendarID = 'bk354284';
848
+ break;
849
+ default:
850
+ break;
851
+ }
852
+ let dateTemp = new Date();
853
+ dateTemp.setDate(dateTemp.getDate() - 1);
854
+
855
+ // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
856
+ // this has some +- on times
857
+ // But this query just finds the first scheduled event (sorted by start_time) that ends after now()
858
+ const query = `*[status == 'scheduled' && defined(live_event_start_time) && published_on > '${getSanityDate(dateTemp)}' && live_event_end_time >= '${getSanityDate(new Date())}']{
859
+ 'slug': slug.current,
860
+ 'id': railcontent_id,
861
+ live_event_start_time,
862
+ live_event_end_time,
863
+ railcontent_id,
864
+ published_on,
865
+ 'event_coach_url' : instructor[0]->web_url_path,
866
+ 'event_coach_calendar_id': coalesce(calendar_id, '${defaultCalendarID}'),
867
+ title,
868
+ "image": thumbnail.asset->url,
869
+ "instructors": instructor[]->name,
870
+ 'videoId': coalesce(live_event_youtube_id, video.external_id),
871
+ } | order(live_event_start_time)[0...1]`;
872
+ return await fetchSanity(query, false);
873
+ }
874
+
823
875
  /**
824
876
  * Fetch all children of a specific pack by Railcontent ID.
825
877
  * @param {string} railcontentId - The Railcontent ID of the pack.