UNPKG

scv-bilara

Version:

SuttaCentral bilara-data library

1,115 lines (1,060 loc) • 35.1 kB
(function (exports) { const fs = require("fs"); const path = require("path"); const json5 = require("json5"); const { readFile } = fs.promises; const { logger } = require("log-instance"); const { execSync } = require("child_process"); const { Memoizer, MemoCache, Files } = require("memo-again"); const { ScApi } = require("suttacentral-api"); const Examples = require("./examples.json"); const SegDoc = require("./seg-doc"); const MLDoc = require("./ml-doc"); const { BilaraPath, Authors, AuthorsV2 } = require("scv-esm"); const { SuttaCentralId, SuttaRef } = require("scv-esm"); const { DBG, DBG_BD_INIT, } = require('./defines.cjs'); const BilaraPathMap = require("./bilara-path-map"); const FuzzyWordSet = require("./fuzzy-word-set"); const Publication = require("./publication"); const Pali = require("./pali"); const English = require("./english"); const ExecGit = require("./exec-git"); const PUB_PREFIX = /^https:.*translation\//; const ROOT_PLI_MS = path.join("root", "pli", "ms"); const STUBFILESIZE = 10; const MAXBUFFER = 10 * 1024 * 1024; class BilaraData { constructor(opts = {}) { const msg = "B8a.ctor()"; const dbg = DBG_BD_INIT; (opts.logger || logger).logInstance(this, opts); this.name = opts.name || "bilara-data"; this.branch = opts.branch || "published"; let root = this.root = opts.root || path.join(Files.LOCAL_DIR, this.name); dbg && console.warn(msg, '[1]root', root, '@', this.branch); this.info(`root:${this.root}`); this.lang = opts.lang || "en"; this.scApi = opts.scApi || new ScApi({ logger: this }); this.gitAccount = opts.gitAccount || "suttacentral"; let repo = (this.repo = opts.repo || `https://github.com/${this.gitAccount}/${this.name}.git`); var includeUnpublished = opts.includeUnpublished == null ? false : opts.includeUnpublished; this.publication = opts.publication || new Publication({ includeUnpublished, name: this.name, root: this.root, }); this.bilaraPathMap = this.publication.bilaraPathMap; this.execGit = opts.execGit || new ExecGit({ repo, repoPath: opts.repoPath || root, logger: this, }); this.languages = opts.languages || ["pli", this.lang]; this.authors = Authors.authors; this.authorsV2 = AuthorsV2.authors; Object.defineProperty(this, "_sources", { writable: true, value: null, }); Object.defineProperty(this, "_suttaIds", { writable: true, value: null, }); this.initialized = false; } static get GITLOG_FNAME() { return ".old" } async syncEbtData() { this.info("syncEbtData()"); let repo = "https://github.com/ebt-site/ebt-data.git"; let execGit = new ExecGit({ repo, logger: this, }); let branches = ["published"]; var res = await execGit.sync(undefined, undefined, branches); return res; } get includeUnpublished() { return this.publication.includeUnpublished; } set includeUnpublished(value) { return (this.publication.includeUnpublished = value); } isBilaraDoc({ suid, lang, author, includeUnpublished = this.includeUnpublished, }) { const msg = "B8a.isBilaraDoc() "; let { bilaraPathMap: bpm, publication } = this; let sp = bpm.suidPaths(suid); let spKeys = (sp && Object.keys(sp)) || []; if (lang) { spKeys = spKeys.filter((p) => p.indexOf(`/${lang}/`) >= 0); } if (author) { spKeys = spKeys.filter((p) => p.endsWith(author)); } let result = spKeys.length > 0; return result; } async isFresh(save = true) { try { if (!fs.existsSync(this.root)) { this.debug(`isFresh() no repository => false`); return false; } let { stdout } = await this.execGit.gitLog(); this.debug(`isFresh() ${stdout}`); let gitlogPath = path.join(this.root, BilaraData.GITLOG_FNAME); let gitlog = fs.existsSync(gitlogPath) && (await fs.promises.readFile(gitlogPath)).toString(); let fresh = stdout === gitlog; if (save && !fresh) { await fs.promises.writeFile(gitlogPath, stdout); } return fresh; } catch (e) { this.warn(`isFresh()`, e.message); throw e; } } async initialize(sync) { try { if (this.initialized) { return this; } sync = sync === undefined ? !(await this.isFresh()) : sync; this.debug(`initialize(sync:${sync})`); sync && (await this.syncEbtData()); var { root } = this; var version = this.version(); var EXPECTED_VERSION = 1; var purge = !fs.existsSync(this.root); if (version.major < EXPECTED_VERSION) { this.warn( `Expected ${this.name} data version ` + `actual:${version.major} ` + `expected:${EXPECTED_VERSION} ` + `(re-cloning repository...)` ); purge = true; } (sync || purge) && (await this.sync({ purge, initializing: true, })); if (this.branch) { try { let res = await this.execGit.branch(this.branch); this.info(`initialize() ${res.stdout} ${res.stderr}`); } catch (e) { this.warn(`initialize() ${this.branch} failed:`, e.message); throw e; } } await this.publication.initialize(); await this.scApi.initialize(); let langPath = path.join(this.root, "_language.json"); this.languageInfo = JSON.parse(await fs.promises.readFile(langPath)); this.examples = Examples; var rootPath = path.join(this.root, "root"); if (!fs.existsSync(rootPath)) { throw new Error(`Root document directory ` + `not found:${rootPath}`); } // The following code must be synchronous this.initialized = true; this.rootFiles = this.dirFiles(rootPath).filter((f) => this.publication.isPublishedPath(f) ); var transPath = path.join(this.root, "translation"); if (!fs.existsSync(transPath)) { throw new Error(`Translation directory not found:${transPath}`); } this.translations = this.dirFiles(transPath) .filter((f) => this.publication.isPublishedPath(f)) .sort(); this.log( `initialize() translations ${transPath}:`, this.translations.length ); this.translations.forEach((f, i) => { var file = f.replace(/.*\/translation\//, "translation/"); var stat = fs.statSync(path.join(root, file)); if (stat.size < STUBFILESIZE) { return; } var parts = file.split("/"); var lang = parts[1]; var author = parts[2]; var category = parts[3]; var nikaya = parts[4]; var suid = parts[parts.length - 1].split("_")[0].toLowerCase(); }); //var uidExpPath = path.join(this.root, '.helpers', 'uid_expansion.json'); var uidExpPath = path.join( __dirname, "..", "src", "auto", "uid_expansion.json" ); this.uid_expansion = json5.parse(fs.readFileSync(uidExpPath)); return this; } catch (e) { this.warn(`initialize()`, e.message); throw e; } } get suttaIds() { if (this._suttaIds == null) { let suttaIds = Object.keys(this.bilaraPathMap.suidMap) .filter((suid) => SuttaCentralId.test(suid)) .sort(SuttaCentralId.compareLow); if (suttaIds.length === 0) { throw new Error(`no suttaIds`); } this._suttaIds = suttaIds; this.info(`suttaIds:${suttaIds.length}`); } return this._suttaIds; } async sync(opts = {}) { try { var purge = opts.purge || false; var initializing = opts.initializing || false; var branches = opts.branches || [this.branch || "unpublished"]; if (purge) { var cmd = `rm -rf ${this.name}`; var execOpts = { cwd: Files.LOCAL_DIR, }; this.warn(`Purging repository: ${cmd}`); var res = execSync(cmd, execOpts).toString(); } else { this.info(`Updating ${this.name}`); } var res = await this.execGit.sync(undefined, undefined, branches); // clear memoizer // TODO: put this in seeker.cjs var mzr = new Memoizer({ writeMem: false }); var mc = mzr.cache; var volumes = mc.volumes(); this.info(`clearing memoizer bytes:`, await mc.fileSize(), volumes); for await (let v of volumes) { mc.clearVolume(v); } if (purge && !initializing) { await this.initialize(); } this.info(`buildSuidMap()`); await this.bilaraPathMap.buildSuidMap(); return this; } catch (e) { this.warn(`sync()`, JSON.stringify(opts), e.message); throw e; } } isSuttaPath(fpath) { console.trace(`DEPRECATED: isSuttaPath => isPublishedPath`); return this.publication.isPublishedPath(fpath); } suttaInfo(suttaRef) { if (!this.initialized) { throw new Error("Expected preceding call to initialize()"); } if (suttaRef == null) { throw new Error("BilaraData.suttaInfo() suid is required"); } var refParts = suttaRef.split("/"); var suid = refParts[0]; var lang = refParts[1]; var author = refParts[2]; var info = this.bilaraPathMap.suidLanguages(suttaRef); if (!info) { // binary search let { suttaIds } = this; var j = suttaIds.length - 1; for (var i = 0; i < j; ) { var k = Math.floor((i + j) / 2); var sk = suttaIds[k]; var cmp = SuttaCentralId.compareLow(suid, sk); if (cmp <= 0) { j = k; } else if (i !== k) { i = k; } else { break; } } var nikaya = new SuttaCentralId(suid).nikaya; var suidMaybe = suttaIds[i]; var nikayaMaybe = new SuttaCentralId(suidMaybe).nikaya; if (nikaya !== nikayaMaybe) { return null; // no information } info = this.bilaraPathMap.suidLanguages(suidMaybe); } info = info.filter( (i) => (!lang || i.lang === lang) && (!author || i.author === author) ); let { authorsV2 } = this; info.forEach((ai) => { let aia = this.authorV2Info(ai.author) || {}; if (ai) { ai.exampleVersion = Number(aia.exampleVersion) || 0; } }); info.sort((a, b) => { // sort by exampleVersion DSC, lang ASC, author ASC let cmp = b.exampleVersion - a.exampleVersion; if (cmp === 0) { cmp = a.lang < b.lang ? -1 : b.lang < a.lang ? 1 : 0; } if (cmp === 0) { cmp = a.author < b.author ? -1 : b.author < a.author ? 1 : 0; } return cmp; }); return info; } dirFiles(root) { var files = []; var prune = `-path '*.git*' -prune`; var cmd = `find ${root} ${prune} -o -type f -print`; var execOpts = { cwd: Files.LOCAL_DIR, maxBuffer: MAXBUFFER, }; var res = execSync(cmd, execOpts).toString(); return res.split("\n"); } loadArgs(args) { if (!this.initialized) { throw new Error("Expected preceding call to initialize()"); } var opts = typeof args[0] !== "string" ? args[0] : { suid: args[0].split("/")[0], lang: args[0].split("/")[1], author: args[0].split("/")[2], }; var { suid, lang, language, author, logLevel, returnNull, languages, types = ["root", "translation" ], } = opts; var thisLang = this.lang; lang = lang || language || thisLang; languages = languages || (this.languages.indexOf(lang) <= 0 ? [...this.languages, lang] : this.languages); logLevel = logLevel === undefined ? this.infoLevel : logLevel; return { suid, lang, author, logLevel, languages, returnNull, types, }; } loadSegDoc(...args) { var that = this; var loadArgs = this.loadArgs(args); var { suid, lang, author, logLevel, returnNull } = loadArgs; this.debug(`loadSegDoc(${JSON.stringify(loadArgs)})`); var info = this.suttaInfo(suid); if (info == null) { if (returnNull) { return null; } throw new Error(`no suttaInfo(suid:${suid})`); } info.sort((a, b) => { try { var cmp = a.lang.localeCompare(b.lang); if (cmp === 0) { cmp = a.author.localeCompare(b.author); } } catch (e) { that.error(`a:${JSON.stringify(a)} `); that.error(`b:${JSON.stringify(b)} `); throw e; } }); if (lang == null && author == null) { lang = info[0].lang; author = info[0].author; } var suttaInfo = info.filter( (i) => (!lang || i.lang === lang) && (!author || i.author === author) )[0]; if (suttaInfo == null || suttaInfo.bilaraPath == null) { this.info(`loadSegDoc(${suid}) info:${JSON.stringify(info)}`); if (returnNull) { return null; } throw new Error( author ? `No information for ${suid}/${lang}` : `No information for ${suid}/${lang}/${author}` ); } suttaInfo.logLevel = logLevel; return new SegDoc(suttaInfo).load(this.root); } loadMLDoc(...args) { const msg = "B8a.loadMLDoc() "; var loadArgs = this.loadArgs(args); var { suid: suidRef, author, lang, logLevel, returnNull, languages, types, } = loadArgs; var { bilaraPathMap: bpm, root } = this; var suidParts = suidRef.split("/"); var suid = suidParts[0]; suidParts.length > 1 && (lang = suidParts[1]); suidParts.length > 2 && (author = suidParts[2]); var info = this.suttaInfo(suid); if (info == null) { if (returnNull) { return null; } var msgData = JSON.stringify({ suidRef, suid }); throw new Error(`no suttaInfo(${msgData})`); } var langMap = languages.reduce((a, l) => ((a[l] = true), a), {}); var bilaraPaths = info .filter((i) => langMap[i.lang]) .filter((i) => this.isBilaraDoc(i)) .filter( (i) => !author || (i.lang === lang && i.author === author) || (i.lang !== lang && this.isBilaraDoc(i)) ) .map((i) => i.bilaraPath); if (types.length > 2) { var xTypes = types.filter((t) => t !== "translation" && t !== "root"); var xbps = bpm.bilaraPaths({ suid, types: xTypes }); bilaraPaths = bilaraPaths.concat(xbps.map((bp) => bp.bilaraPath)); } if (bilaraPaths.length === 0) { if (returnNull) { this.info(`loadMLDoc(${suid}) no info:${languages}`); return null; } throw new Error( author ? `No information for ${suid}/${lang}` : `No information for ${suid}/${lang}/${author}` ); } let authors = author == null ? bilaraPaths.reduce((alist, bp) => { let [s, l, a] = bp.split("/"); if (l === lang && !alist.includes(a)) { alist.push(a); } return alist; }, []) : [author]; let mldOpts = { logLevel, lang, author_uid: authors[0], sutta_uid: suid, bilaraPaths, }; this.debug(`loadMLDoc mldOpts`, { mldOpts, authors }); return new MLDoc(mldOpts).load(this.root); } async trilingualDoc(suttaRef, opts={}) { const msg = "B8a.trilingualDoc() "; const dbg = DBG.TRILINGUALDOC; let { bilaraPathMap: bpm, root } = this; suttaRef = SuttaRef.create(suttaRef); let { rootLang = 'pli', rootAuthor = 'ms', refLang, refAuthor = 'sujato', refAuthorName, docLang = suttaRef.lang, docAuthor = suttaRef.author, docAuthorName, logLevel, } = opts; let suid = suttaRef.sutta_uid; let info = this.suttaInfo(suid); if (info == null) { let msgData = JSON.stringify({ suidRef, suid }); throw new Error(`${msg} no suttaInfo(${msgData})`); } if (docAuthorName == null) { let info = AuthorsV2.authorInfo(docAuthor); docAuthorName = info?.name?.join(', '); } if (refAuthorName == null) { let info = AuthorsV2.authorInfo(refAuthor); refAuthorName = info?.name?.join(', '); } let bilaraPaths = bpm.trilingualPaths({ suid, rootLang, rootAuthor, refLang, refAuthor, docLang, docAuthor, }); if (bilaraPaths.length === 0) { throw new Error(`${msg} ${suttaRef} not found`); } let mldOpts = { lang: docLang || refLang, author_uid: docAuthor || refAuthor, sutta_uid: suid, bilaraPaths, refAuthor, refLang, refAuthorName, docAuthor, docAuthorName, docLang, trilingual:true, }; logLevel && (mldOpts.logLevel = logLevel); dbg>1 && console.log(msg, '[1]mldOpts', mldOpts); let mld = await new MLDoc(mldOpts).load(root); if (dbg) { let dbgOut = mld && { author: mld.author, refLang: mld.refLang, refAuthor: mld.refAuthor, refAuthorName: mld.refAuthorName, docLang: mld.docLang, docAuthor: mld.docAuthor, docAuthorName: mld.docAuthorName, trilingual: mld.trilingual, bilaraPaths: mld.bilaraPaths, langSegs: mld.langSegs, '...': '...', }; console.log(msg, '[2]mld', dbgOut); } return mld; } canonicalSuttaId(id, type="acro") { const msg = 'B8a.canonicalSuttaId()'; if (!this.initialized) { let emsg = `${msg}, initialize() required`; throw new Error(emsg); } var { uid_expansion } = this; id = this.normalizeSuttaId(id.toLowerCase()); let nikaya = id.replace(/[-0-9:.]+/, ''); let [ue] = uid_expansion.filter((ue) => ue.uid === nikaya); switch (type) { case "name": return id.replace(nikaya, ue.name+' '); case "acro": default: return id.replace(nikaya, ue.acro); } } normalizeSuttaId(id) { if (!this.initialized) { throw new Error(`${this.constructor.name}.` + `initialize() required`); } var { uid_expansion } = this; var suttaIds = this.suttaIds; if (typeof id !== "string") { if (id.scid) { id = id.scid; } else { throw new Error(`expected string:${JSON.stringify(id)}`); } } id = id.trim(); var sutta_uid = null; if (/[^0-9][1-9]/.test(id)) { var tokens = id.toLowerCase().split(" "); if (tokens.length > 1) { var matches = uid_expansion.filter( (ue) => 0 === ue.acro.toLowerCase().localeCompare(tokens[0]) ); if (matches.length > 0) { sutta_uid = `${matches[0].uid}${tokens.slice(1).join("")}`; } } sutta_uid = sutta_uid || id; } sutta_uid = (sutta_uid && suttaIds.filter((sid) => { return ( SuttaCentralId.compareLow(sid, sutta_uid) <= 0 && 0 <= SuttaCentralId.compareHigh(sid, sutta_uid) ); })[0]) || sutta_uid; return sutta_uid; } suttaPath(...args) { // DEPRECATED: use docPaths this.info(`DEPRECATED: suttaPath => docPaths`); return this.docPaths.apply(this, args)[0]; } docPaths(...args) { if (!this.initialized) { throw new Error(`${this.constructor.name}.initialize() is required`); } var opts = args[0]; if (typeof opts === "string") { var opts = { scid: args[0], lang: args[1], //language: args[1], // DEPRECATED author_uid: args[2], }; } // TODO mn1/en/sujato sutta_uid var scid = new SuttaCentralId(opts.scid || opts.sutta_uid); var sutta_uid = this.normalizeSuttaId(scid.sutta); if (!sutta_uid) { throw new Error("sutta_uid is required"); } var lang = opts.lang || "en"; var author = opts.author_uid || opts.author; var docs = this.bilaraPathMap.suidLanguages(sutta_uid); if (docs.length === 0) { return []; } docs.sort((a,b)=>AuthorsV2.compare(b.author, a.author)); let authorDocs = docs.filter(doc => doc.author === author); if (authorDocs.length) { docs = authorDocs; } else if (!author) { let langDocs = docs.filter(t => t.lang === lang); docs = langDocs; } else { docs = []; } return docs.map((t) => path.join(this.root, t.bilaraPath)); } nikayaSuttaIds(nikaya, lang = "pli", author) { var that = this; if (nikaya == null) { return Promise.reject(new Error(`nikayaSuttaIds() nikaya is required`)); } var reNikaya = author ? new RegExp(`.*/${lang}/${author}/.*${nikaya}/`) : new RegExp(`.*/${lang}/.*/${nikaya}/`); return new Promise((resolve, reject) => { (async function () { try { await that.initialize(); var srcFiles = lang === "pli" ? that.rootFiles : that.translations; var sutta_uids = srcFiles.reduce((a, t) => { if (reNikaya.test(t)) { var f = path.basename(t, ".json"); var sutta_uid = f.replace(/_.*$/, ""); a.push(sutta_uid); } return a; }, []); var cmp = SuttaCentralId.compareLow; sutta_uids.sort(cmp); resolve(sutta_uids); } catch (e) { reject(e); } })(); }); } suttaIndex(suttaRef, strict = true) { if (!this.initialized) { throw new Error("initialize() is required"); } if (suttaRef == null) { throw new Error("suttaIndex(suttaRef?)"); } var sutta_uid = suttaRef.split("/")[0]; var iEnd = this.suttaIds.length; var i1 = 0; var i2 = iEnd; var cmp; while (i1 <= i2) { var i = Math.trunc((i1 + i2) / 2); var sf = this.suttaIds[i]; cmp = SuttaCentralId.compareLow(sutta_uid, sf); if (cmp === 0) { return i; } if (cmp < 0) { if (i < i2) { i2 = i; } else { break; } } else if (i1 < i) { // cmp > 0 i1 = i; } else { break; } } if (cmp < 0) { return i === 0 ? null : i; } if (strict) { var uidNext = this.sutta_uidSuccessor(this.suttaIds[i], true); var cmpNext = SuttaCentralId.compareLow(sutta_uid, uidNext); if (cmpNext < 0) { return i; } return null; } return i; } sutta_uidSuccessor(sutta_uid, logical) { var prefix = sutta_uid.replace(/[-0-9.:]*$/u, ""); var dotParts = sutta_uid.substring(prefix.length).split("."); var dotLast = dotParts.length - 1; var rangeParts = sutta_uid.substring(prefix.length).split("-"); rangeParts[0] = prefix + rangeParts[0]; var rangeLast = rangeParts.length - 1; if (logical) { // logical dotParts[dotParts.length - 1] = rangeParts.length < 2 ? `${Number(dotParts[dotLast]) + 1}` : `${Number(rangeParts[rangeLast]) + 1}`; var uidEnd = prefix + dotParts.join("."); return uidEnd; } else { // physical dotParts[dotParts.length - 1] = rangeParts.length < 2 ? `${Number(dotParts[dotLast])}` : `${Number(rangeParts[rangeLast])}`; var uidLast = prefix + dotParts.join("."); var iLast = this.suttaIndex(uidLast, false); var uidNext = this.suttaIds[iLast + 1]; return uidNext; } } suttaList(list) { if (typeof list === "string") { list = list.split(","); } try { return list.reduce((acc, item) => { var suttaRef = item.toLowerCase().replace(/ /gu, ""); this.expandRange(suttaRef).forEach((item) => acc.push(item)); return acc; }, []); } catch (e) { throw e; } } expandRange(suttaRef) { const msg = "B8a.expandRange"; const dbg = DBG.SUTTA_UIDSEARCH; suttaRef = suttaRef.split(":")[0]; var reCollection = new RegExp("[0-9].*", "u"); var collName = suttaRef.replace(reCollection, ""); var suffix = suttaRef.replace(/[^/]*([a-z\/]*)$/iu, "$1"); var sref = suttaRef.replace(suffix, ""); var range = sref.substring(collName.length); if (/^[-a-z]+$/.test(range)) { // e.g., kusalagnana-maitrimurti-traetow return [suttaRef]; } var rl = SuttaCentralId.rangeLow(suttaRef); var suttaRefLow = this.suttaIds[this.suttaIndex(rl)]; if (!suttaRefLow) { throw new Error(`Document not found: ${suttaRef} [E5]`); } var coll = this.publication.pubInfo(suttaRefLow); var result = []; if (!coll || !coll.length) { // no collection throw new Error(`Unpublished:${suttaRef} collection:${collName} [E4]`); } var rangeParts = range.split("-"); var dotParts = rangeParts[0].split("."); let dpLast = dotParts.length - 1; if (dpLast > 2) { throw new Error(`Invalid sutta reference: ${suttaRef} [E3]`); } if (dpLast > 0 || coll[0].subchapters) { // SN, AN, KN if (dpLast === 0) { // e.g. SN50 var prefix = `${sref}.`; var first = rangeParts.length === 1 ? 1 : Number(rangeParts[0]); var last = rangeParts.length === 1 ? 999 : Number(rangeParts[1]); dbg && console.log(msg, '[1]dpLast0', {prefix, first, last}); } else { var prefix = `${collName}${dotParts.slice(0,dpLast).join('.')}.`; var first = Number(dotParts[dpLast]); var last = rangeParts.length === 1 ? first : Number(rangeParts[1]); dbg && console.log(msg, '[2]!dpLast0', {prefix, first, last}); } if (isNaN(first) || isNaN(last)) { throw new Error(`Invalid sutta reference: ${suttaRef} [E1]`); } var firstItem = `${prefix}${first}`; var iCur = this.suttaIndex(firstItem, false); if (iCur == null) { throw new Error(`Sutta ${firstItem} not found`); } var endUid = this.sutta_uidSuccessor(`${prefix}${last}`); var iEnd = (endUid && this.suttaIndex(endUid)) || iCur + 1; for (var i = iCur; i < iEnd; i++) { result.push(`${this.suttaIds[i]}${suffix}`); } } else { // e.g., MN, DN if (rangeParts.length === 1) { var first = Number(rangeParts[0]); var last = first; } else { var first = Number(rangeParts[0]); var last = Number(rangeParts[1]); } if (isNaN(first) || isNaN(last)) { throw new Error(`Invalid sutta reference: ${suttaRef} [E2]`); } var firstItem = `${collName}${first}`; var iCur = this.suttaIndex(firstItem, false); if (iCur == null) { throw new Error(`Sutta ${firstItem} not found`); } var lastItem = `${collName}${last}`; var endUid = this.sutta_uidSuccessor(lastItem); var iEnd = endUid ? this.suttaIndex(endUid) : iCur + 1; dbg && console.log(msg, '[3]MN_DN', {prefix, first, last}); for (var i = iCur; i < iEnd; i++) { result.push(`${this.suttaIds[i]}${suffix}`); } } dbg && console.log(msg, '=>', result); return result; } authorInfo(author) { return Authors.authorInfo(author); } authorV2Info(author) { return AuthorsV2.authorInfo(author); } sutta_uidSearch(pattern, maxResults = 5) { const msg = 'B8a.sutta_uidSearch() '; const dbg = DBG.SUTTA_UIDSEARCH; var method = "sutta_uid"; var uids = this.suttaList(pattern).slice(0, maxResults); var lang = undefined; var suttaRefs = uids.map((ref) => { var refParts = ref.split("/"); var refTranslator = refParts[2]; var uid = refParts[0]; var refLang = refParts[1]; lang == null && (lang = refLang); return refTranslator ? `${uid}/${refLang}/${refTranslator}` : refLang ? `${uid}/${refLang}` : uid; }); let result = { method, uids, suttaRefs, lang, }; dbg && console.log(msg, pattern, '=>', result); return result; } version() { var pkgPath = path.join(this.root, "package.json"); var result = { major: 1, minor: 0, patch: 0, }; if (fs.existsSync(pkgPath)) { var json = json5.parse(fs.readFileSync(pkgPath).toString()); var verParts = json.version.split("."); result.major = Number(verParts[0]); result.minor = Number(verParts[1]); result.patch = Number(verParts[2]); } return result; } readBlurb({ suid, lang }) { var that = this; var pbody = (resolve, reject) => (async function () { try { if (!suid) { resolve(null); } suid = suid.toLowerCase(); lang = lang || that.lang; var nikaya = suid.replace(/[1-9].*/, ""); if (lang === "pli" || lang === "en") { var dir = path.join(that.root, "root", lang, "blurb"); var fname = `${nikaya}-blurbs_root-${lang}.json`; var fpath = path.join(dir, fname); } else { var dir = path.join(that.root, "translation", lang, "blurb"); var fname = `${nikaya}-blurbs_translation-${lang}.json`; var fpath = path.join(dir, fname); } if (!fs.existsSync(fpath)) { if (lang !== "en") { var enBlurb = await that.readBlurb({ suid, lang: "en" }); if (enBlurb) { that.info(`Not found: ${fpath}. Using en blurb`); } resolve(enBlurb); } resolve(null); } var json = await fs.promises.readFile(fpath); var blurbs = json5.parse(json); var key = `${nikaya}-blurbs:${suid}`; var blurbKey = blurbs[key]; var blurbSuid = blurbs[suid]; resolve(blurbKey || blurbSuid || null); } catch (e) { reject(e); } })(); return new Promise(pbody); } async loadSuttaplexJson( scid, lang, author_uid, includeUnpublished = this.includeUnpublished ) { try { let suttaplex = await this.scApi.loadSuttaplexJson(scid); var allTranslations = (suttaplex && suttaplex.translations) || []; var translations = allTranslations; if (lang || author_uid) { const ANY_LANGUAGE = "*"; suttaplex.translations = translations = allTranslations.filter( (t) => (!lang || lang === ANY_LANGUAGE || t.lang === lang) && (!author_uid || t.author_uid === author_uid) ); this.debug( `ScApi.loadSuttaplexJson` + `(${scid}, ${lang}, ${author_uid}) ` + `${JSON.stringify(suttaplex, null, 2)}` ); } translations.sort((a, b) => { if (a.segmented === b.segmented) { if (a.lang === "pli") { return 1; } if (b.lang === "pli") { return -1; } return (a.author_uid || "").localeCompare(b.author_uid || ""); } return b.segmented ? 1 : -1; }); if (translations.length === 0 || !translations[0].segmented) { if (!author_uid) { let suidPaths = this.bilaraPathMap.suidPaths(scid); let authors = Object.keys(suidPaths).reduce((a, k) => { let [t, l, aid] = k.split("/"); if (t === "translation" && (!lang || lang === l)) { a.push(aid); } return a; }, []); author_uid = authors[0]; } if ( this.isBilaraDoc({ suid: scid, lang, author: author_uid, includeUnpublished, }) ) { let ainfo = this.authorV2Info(author_uid); let title = allTranslations.reduce((a, t) => { return a ? a : t.lang === "pli" && t.title; }, null); let langInfo = this.languageInfo[lang]; translations.unshift({ author: ainfo.author, author_short: ainfo.name[0], author_uid, is_root: false, lang, lang_name: (langInfo && langInfo.name) || lang, id: `${lang}_${scid}_${author_uid}`, segmented: true, publication_date: null, title, volpage: null, }); } } if (translations.length === 0) { throw new Error(`suttaplex has no translations`); } return suttaplex; } catch (e) { this.warn( `loadSuttaplexJson()`, { scid, lang, author_uid, includeUnpublished }, e.message ); throw e; } } } module.exports = exports.BilaraData = BilaraData; })(typeof exports === "object" ? exports : (exports = {}));