« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/things
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things')
-rw-r--r--src/data/things/album.js87
-rw-r--r--src/data/things/art-tag.js21
-rw-r--r--src/data/things/artist.js31
-rw-r--r--src/data/things/flash.js47
-rw-r--r--src/data/things/group.js47
-rw-r--r--src/data/things/homepage-layout.js38
-rw-r--r--src/data/things/news-entry.js21
-rw-r--r--src/data/things/static-page.js28
-rw-r--r--src/data/things/track.js3
-rw-r--r--src/data/things/wiki-info.js21
10 files changed, 343 insertions, 1 deletions
diff --git a/src/data/things/album.js b/src/data/things/album.js
index 9e6d28d9..9d4729e4 100644
--- a/src/data/things/album.js
+++ b/src/data/things/album.js
@@ -1,5 +1,11 @@
+export const DATA_ALBUM_DIRECTORY = 'album';
+
+import * as path from 'node:path';
+
 import {input} from '#composite';
 import find from '#find';
+import {traverse} from '#node-utils';
+import {empty} from '#sugar';
 import Thing from '#thing';
 import {isDate} from '#validators';
 import {parseAdditionalFiles, parseContributors, parseDate, parseDimensions}
@@ -283,6 +289,87 @@ export class Album extends Thing {
       'Review Points': {ignore: true},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {headerAndEntries},
+    thingConstructors: {Album, Track, TrackSectionHelper},
+  }) => ({
+    title: `Process album files`,
+
+    files: dataPath =>
+      traverse(path.join(dataPath, DATA_ALBUM_DIRECTORY), {
+        filterFile: name => path.extname(name) === '.yaml',
+        prefixPath: DATA_ALBUM_DIRECTORY,
+      }),
+
+    documentMode: headerAndEntries,
+    headerDocumentThing: Album,
+    entryDocumentThing: document =>
+      ('Section' in document
+        ? TrackSectionHelper
+        : Track),
+
+    save(results) {
+      const albumData = [];
+      const trackData = [];
+
+      for (const {header: album, entries} of results) {
+        // We can't mutate an array once it's set as a property value,
+        // so prepare the track sections that will show up in a track list
+        // all the way before actually applying them. (It's okay to mutate
+        // an individual section before applying it, since those are just
+        // generic objects; they aren't Things in and of themselves.)
+        const trackSections = [];
+        const ownTrackData = [];
+
+        let currentTrackSection = {
+          name: `Default Track Section`,
+          isDefaultTrackSection: true,
+          tracks: [],
+        };
+
+        const albumRef = Thing.getReference(album);
+
+        const closeCurrentTrackSection = () => {
+          if (!empty(currentTrackSection.tracks)) {
+            trackSections.push(currentTrackSection);
+          }
+        };
+
+        for (const entry of entries) {
+          if (entry instanceof TrackSectionHelper) {
+            closeCurrentTrackSection();
+
+            currentTrackSection = {
+              name: entry.name,
+              color: entry.color,
+              dateOriginallyReleased: entry.dateOriginallyReleased,
+              isDefaultTrackSection: false,
+              tracks: [],
+            };
+
+            continue;
+          }
+
+          trackData.push(entry);
+
+          entry.dataSourceAlbum = albumRef;
+
+          ownTrackData.push(entry);
+          currentTrackSection.tracks.push(Thing.getReference(entry));
+        }
+
+        closeCurrentTrackSection();
+
+        albumData.push(album);
+
+        album.trackSections = trackSections;
+        album.ownTrackData = ownTrackData;
+      }
+
+      return {albumData, trackData};
+    },
+  });
 }
 
 export class TrackSectionHelper extends Thing {
diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js
index af6677f0..29cd2990 100644
--- a/src/data/things/art-tag.js
+++ b/src/data/things/art-tag.js
@@ -1,7 +1,9 @@
+export const ART_TAG_DATA_FILE = 'tags.yaml';
+
 import {input} from '#composite';
 import Thing from '#thing';
 import {isName} from '#validators';
-import {sortAlbumsTracksChronologically} from '#wiki-data';
+import {sortAlphabetically, sortAlbumsTracksChronologically} from '#wiki-data';
 
 import {exposeUpdateValueOrContinue} from '#composite/control-flow';
 
@@ -73,4 +75,21 @@ export class ArtTag extends Thing {
       'Is CW': {property: 'isContentWarning'},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {allInOne},
+    thingConstructors: {ArtTag},
+  }) => ({
+    title: `Process art tags file`,
+    file: ART_TAG_DATA_FILE,
+
+    documentMode: allInOne,
+    documentThing: ArtTag,
+
+    save(artTagData) {
+      sortAlphabetically(artTagData);
+
+      return {artTagData};
+    },
+  });
 }
diff --git a/src/data/things/artist.js b/src/data/things/artist.js
index ab08f522..8f32afd7 100644
--- a/src/data/things/artist.js
+++ b/src/data/things/artist.js
@@ -1,3 +1,5 @@
+export const ARTIST_DATA_FILE = 'artists.yaml';
+
 import {input} from '#composite';
 import find from '#find';
 import {unique} from '#sugar';
@@ -257,4 +259,33 @@ export class Artist extends Thing {
       'Review Points': {ignore: true},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {allInOne},
+    thingConstructors: {Artist},
+  }) => ({
+    title: `Process artists file`,
+    file: ARTIST_DATA_FILE,
+
+    documentMode: allInOne,
+    documentThing: Artist,
+
+    save(results) {
+      const artistData = results;
+
+      const artistAliasData = results.flatMap((artist) => {
+        const origRef = Thing.getReference(artist);
+        return artist.aliasNames?.map((name) => {
+          const alias = new Artist();
+          alias.name = name;
+          alias.isAlias = true;
+          alias.aliasedArtist = origRef;
+          alias.artistData = artistData;
+          return alias;
+        }) ?? [];
+      });
+
+      return {artistData, artistAliasData};
+    },
+  });
 }
diff --git a/src/data/things/flash.js b/src/data/things/flash.js
index 945d80dd..ad80cbf2 100644
--- a/src/data/things/flash.js
+++ b/src/data/things/flash.js
@@ -1,3 +1,5 @@
+export const FLASH_DATA_FILE = 'flashes.yaml';
+
 import {input} from '#composite';
 import find from '#find';
 import Thing from '#thing';
@@ -204,4 +206,49 @@ export class FlashAct extends Thing {
       'Review Points': {ignore: true},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {allInOne},
+    thingConstructors: {Flash, FlashAct},
+  }) => ({
+    title: `Process flashes file`,
+    file: FLASH_DATA_FILE,
+
+    documentMode: allInOne,
+    documentThing: document =>
+      ('Act' in document
+        ? FlashAct
+        : Flash),
+
+    save(results) {
+      let flashAct;
+      let flashRefs = [];
+
+      if (results[0] && !(results[0] instanceof FlashAct)) {
+        throw new Error(`Expected an act at top of flash data file`);
+      }
+
+      for (const thing of results) {
+        if (thing instanceof FlashAct) {
+          if (flashAct) {
+            Object.assign(flashAct, {flashes: flashRefs});
+          }
+
+          flashAct = thing;
+          flashRefs = [];
+        } else {
+          flashRefs.push(Thing.getReference(thing));
+        }
+      }
+
+      if (flashAct) {
+        Object.assign(flashAct, {flashes: flashRefs});
+      }
+
+      const flashData = results.filter(x => x instanceof Flash);
+      const flashActData = results.filter(x => x instanceof FlashAct);
+
+      return {flashData, flashActData};
+    },
+  });
 }
diff --git a/src/data/things/group.js b/src/data/things/group.js
index adcd6ad1..a32cd64d 100644
--- a/src/data/things/group.js
+++ b/src/data/things/group.js
@@ -1,3 +1,5 @@
+export const GROUP_DATA_FILE = 'groups.yaml';
+
 import {input} from '#composite';
 import find from '#find';
 import Thing from '#thing';
@@ -97,6 +99,51 @@ export class Group extends Thing {
       'Review Points': {ignore: true},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {allInOne},
+    thingConstructors: {Group, GroupCategory},
+  }) => ({
+    title: `Process groups file`,
+    file: GROUP_DATA_FILE,
+
+    documentMode: allInOne,
+    documentThing: document =>
+      ('Category' in document
+        ? GroupCategory
+        : Group),
+
+    save(results) {
+      let groupCategory;
+      let groupRefs = [];
+
+      if (results[0] && !(results[0] instanceof GroupCategory)) {
+        throw new Error(`Expected a category at top of group data file`);
+      }
+
+      for (const thing of results) {
+        if (thing instanceof GroupCategory) {
+          if (groupCategory) {
+            Object.assign(groupCategory, {groups: groupRefs});
+          }
+
+          groupCategory = thing;
+          groupRefs = [];
+        } else {
+          groupRefs.push(Thing.getReference(thing));
+        }
+      }
+
+      if (groupCategory) {
+        Object.assign(groupCategory, {groups: groupRefs});
+      }
+
+      const groupData = results.filter(x => x instanceof Group);
+      const groupCategoryData = results.filter(x => x instanceof GroupCategory);
+
+      return {groupData, groupCategoryData};
+    },
+  });
 }
 
 export class GroupCategory extends Thing {
diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js
index 38fd5a7a..00d6aef5 100644
--- a/src/data/things/homepage-layout.js
+++ b/src/data/things/homepage-layout.js
@@ -1,3 +1,5 @@
+export const HOMEPAGE_LAYOUT_DATA_FILE = 'homepage.yaml';
+
 import {input} from '#composite';
 import find from '#find';
 import Thing from '#thing';
@@ -182,4 +184,40 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow {
       'Actions': {property: 'actionLinks'},
     },
   });
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {headerAndEntries}, // Kludge, see below
+    thingConstructors: {
+      HomepageLayout,
+      HomepageLayoutAlbumsRow,
+    },
+  }) => ({
+    title: `Process homepage layout file`,
+
+    // Kludge: This benefits from the same headerAndEntries style messaging as
+    // albums and tracks (for example), but that document mode is designed to
+    // support multiple files, and only one is actually getting processed here.
+    files: [HOMEPAGE_LAYOUT_DATA_FILE],
+
+    documentMode: headerAndEntries,
+    headerDocumentThing: HomepageLayout,
+    entryDocumentThing: document => {
+      switch (document['Type']) {
+        case 'albums':
+          return HomepageLayoutAlbumsRow;
+        default:
+          throw new TypeError(`No processDocument function for row type ${document['Type']}!`);
+      }
+    },
+
+    save(results) {
+      if (!results[0]) {
+        return;
+      }
+
+      const {header: homepageLayout, entries: rows} = results[0];
+      Object.assign(homepageLayout, {rows});
+      return {homepageLayout};
+    },
+  });
 }
diff --git a/src/data/things/news-entry.js b/src/data/things/news-entry.js
index 5a022449..658453b0 100644
--- a/src/data/things/news-entry.js
+++ b/src/data/things/news-entry.js
@@ -1,4 +1,7 @@
+export const NEWS_DATA_FILE = 'news.yaml';
+
 import Thing from '#thing';
+import {sortChronologically} from '#wiki-data';
 import {parseDate} from '#yaml';
 
 import {contentString, directory, name, simpleDate}
@@ -43,4 +46,22 @@ export class NewsEntry extends Thing {
       'Content': {property: 'content'},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {allInOne},
+    thingConstructors: {NewsEntry},
+  }) => ({
+    title: `Process news data file`,
+    file: NEWS_DATA_FILE,
+
+    documentMode: allInOne,
+    documentThing: NewsEntry,
+
+    save(newsData) {
+      sortChronologically(newsData);
+      newsData.reverse();
+
+      return {newsData};
+    },
+  });
 }
diff --git a/src/data/things/static-page.js b/src/data/things/static-page.js
index 2da7312b..1e8cb7c6 100644
--- a/src/data/things/static-page.js
+++ b/src/data/things/static-page.js
@@ -1,5 +1,11 @@
+export const DATA_STATIC_PAGE_DIRECTORY = 'static-page';
+
+import * as path from 'node:path';
+
+import {traverse} from '#node-utils';
 import Thing from '#thing';
 import {isName} from '#validators';
+import {sortAlphabetically} from '#wiki-data';
 
 import {contentString, directory, name, simpleString}
   from '#composite/wiki-properties';
@@ -42,4 +48,26 @@ export class StaticPage extends Thing {
       'Review Points': {ignore: true},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {onePerFile},
+    thingConstructors: {StaticPage},
+  }) => ({
+    title: `Process static page files`,
+
+    files: dataPath =>
+      traverse(path.join(dataPath, DATA_STATIC_PAGE_DIRECTORY), {
+        filterFile: name => path.extname(name) === '.yaml',
+        prefixPath: DATA_STATIC_PAGE_DIRECTORY,
+      }),
+
+    documentMode: onePerFile,
+    documentThing: StaticPage,
+
+    save(staticPageData) {
+      sortAlphabetically(staticPageData);
+
+      return {staticPageData};
+    },
+  });
 }
diff --git a/src/data/things/track.js b/src/data/things/track.js
index dd102683..d1a12aac 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -451,6 +451,9 @@ export class Track extends Thing {
     ],
   };
 
+  // Track YAML loading is handled in album.js.
+  static [Thing.getYamlLoadingSpec] = null;
+
   [inspect.custom](depth) {
     const parts = [];
 
diff --git a/src/data/things/wiki-info.js b/src/data/things/wiki-info.js
index fd6c239c..316bd3bb 100644
--- a/src/data/things/wiki-info.js
+++ b/src/data/things/wiki-info.js
@@ -1,3 +1,5 @@
+export const WIKI_INFO_FILE = 'wiki-info.yaml';
+
 import {input} from '#composite';
 import find from '#find';
 import Thing from '#thing';
@@ -86,4 +88,23 @@ export class WikiInfo extends Thing {
       'Enable Group UI': {property: 'enableGroupUI'},
     },
   };
+
+  static [Thing.getYamlLoadingSpec] = ({
+    documentModes: {oneDocumentTotal},
+    thingConstructors: {WikiInfo},
+  }) => ({
+    title: `Process wiki info file`,
+    file: WIKI_INFO_FILE,
+
+    documentMode: oneDocumentTotal,
+    documentThing: WikiInfo,
+
+    save(wikiInfo) {
+      if (!wikiInfo) {
+        return;
+      }
+
+      return {wikiInfo};
+    },
+  });
 }