@sjcrh/proteinpaint-shared 2.188.1 → 2.189.0
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/constants/AiHisto.ts +17 -13
- package/dist/constants/AiHisto.d.ts +14 -12
- package/dist/constants/AiHisto.js +15 -17
- package/dist/constants/AiHisto.js.map +3 -3
- package/dist/src/aiHisto.d.ts +2 -2
- package/dist/src/aiHisto.js.map +2 -2
- package/dist/src/bulk.cnv.d.ts +2 -0
- package/dist/src/bulk.cnv.js +1 -1
- package/dist/src/bulk.cnv.js.map +3 -3
- package/dist/src/bulk.d.ts +56 -0
- package/dist/src/bulk.del.d.ts +2 -0
- package/dist/src/bulk.del.js +2 -2
- package/dist/src/bulk.del.js.map +3 -3
- package/dist/src/bulk.itd.d.ts +2 -0
- package/dist/src/bulk.itd.js +2 -2
- package/dist/src/bulk.itd.js.map +3 -3
- package/dist/src/bulk.js +1 -2
- package/dist/src/bulk.js.map +3 -3
- package/dist/src/bulk.snv.d.ts +2 -0
- package/dist/src/bulk.snv.js +3 -3
- package/dist/src/bulk.snv.js.map +3 -3
- package/dist/src/bulk.sv.d.ts +3 -0
- package/dist/src/bulk.sv.js +1 -1
- package/dist/src/bulk.sv.js.map +3 -3
- package/dist/src/bulk.svjson.d.ts +2 -0
- package/dist/src/bulk.svjson.js +3 -3
- package/dist/src/bulk.svjson.js.map +3 -3
- package/dist/src/bulk.trunc.d.ts +2 -0
- package/dist/src/bulk.trunc.js +1 -1
- package/dist/src/bulk.trunc.js.map +3 -3
- package/dist/src/clustering.d.ts +10 -0
- package/dist/src/clustering.js.map +1 -1
- package/dist/src/common.d.ts +431 -0
- package/dist/src/common.js +62 -22
- package/dist/src/common.js.map +3 -3
- package/dist/src/compute.percentile.d.ts +1 -0
- package/dist/src/compute.percentile.js.map +1 -1
- package/dist/src/fetch-helpers.d.ts +8 -0
- package/dist/src/fetch-helpers.js +17 -18
- package/dist/src/fetch-helpers.js.map +3 -3
- package/dist/src/fileSize.d.ts +1 -0
- package/dist/src/fileSize.js.map +1 -1
- package/dist/src/hash.d.ts +1 -0
- package/dist/src/hash.js.map +1 -1
- package/dist/src/helpers.d.ts +14 -0
- package/dist/src/helpers.js +3 -4
- package/dist/src/helpers.js.map +3 -3
- package/dist/src/mds3tk.d.ts +6 -0
- package/dist/src/mds3tk.js.map +3 -3
- package/dist/src/roundValue.d.ts +14 -0
- package/dist/src/roundValue.js.map +1 -1
- package/dist/src/termdb.bins.d.ts +5 -0
- package/dist/src/termdb.bins.js +1 -1
- package/dist/src/termdb.bins.js.map +3 -3
- package/dist/src/termdb.initbinconfig.d.ts +1 -0
- package/dist/src/termdb.initbinconfig.js +1 -1
- package/dist/src/termdb.initbinconfig.js.map +3 -3
- package/dist/src/termdb.usecase.d.ts +2 -0
- package/dist/src/termdb.usecase.js.map +3 -3
- package/dist/src/terms.d.ts +26 -26
- package/dist/src/tree.d.ts +1 -0
- package/dist/src/tree.js.map +3 -3
- package/dist/src/vcf.ann.d.ts +1 -0
- package/dist/src/vcf.ann.js +1 -2
- package/dist/src/vcf.ann.js.map +3 -3
- package/dist/src/vcf.csq.d.ts +1 -0
- package/dist/src/vcf.csq.js +1 -2
- package/dist/src/vcf.csq.js.map +3 -3
- package/dist/src/vcf.d.ts +2 -0
- package/dist/src/vcf.info.d.ts +1 -0
- package/dist/src/vcf.info.js.map +1 -1
- package/dist/src/vcf.js +47 -42
- package/dist/src/vcf.js.map +4 -4
- package/dist/src/vcf.type.d.ts +1 -0
- package/dist/src/vcf.type.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/fetch-helpers.
|
|
4
|
-
"sourcesContent": ["import { hash } from './hash.js'\nimport { encode } from './urljson.js'\nimport { deepFreeze } from './helpers.js'\n\n/*\n\tezFetch()\n\tfetch wrapper with automatic response content-type detection and handling\n\n\t- this addresses issues with ky.json() or got.json(), where a HTTP 404 NOT FOUND\n\t\tresponse with text/html can break error handling/logging, making it harder to debug\n\n\t- this also automatically handles multipart responses\n\n - NOTE: for backend, use xfetch() instead, it uses ky() and its built-in retry support\n\n\targuments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n*/\nexport async function ezFetch(_url, init = {}, opts = {}) {\n\tconst url = opts.autoMethod ? mayAdjustRequest(_url, init) : _url\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\n\treturn fetch(url, init).then(async r => {\n\t\tconst response = await processResponse(r)\n\t\tif (!r.ok) {\n\t\t\tconsole.log('ezFetch error ' + r.status)\n\t\t\tconsole.log(response)\n\t\t\tthrow response\n\t\t}\n\t\treturn response\n\t})\n}\n\nfunction mayAdjustRequest(url, init) {\n\tconst method = init.method?.toUpperCase() || 'GET'\n\tif (method == 'POST') {\n\t\tif (!init.headers) init.headers = {}\n\t\tif (init.body) {\n\t\t\tif (!init.headers['content-type']) init.headers['content-type'] = 'application/json'\n\t\t\tif (init.headers['content-type'].toLowerCase() == 'application/json') {\n\t\t\t\t// if consumer code has pre-encoded the body, parse to verify correctness\n\t\t\t\tif (typeof init.body == 'string') init.body = JSON.parse(init.body)\n\t\t\t\tinit.body = JSON.stringify(init.body)\n\t\t\t}\n\t\t}\n\t\treturn url\n\t}\n\t// default to GET method per native fetch\n\tif (init.body) {\n\t\tif (typeof init.body != 'object') throw `init.body should be an object`\n\t\t// init.body should be an object, to be converted to GET URL search parameter strings\n\t\tif (!url.includes('?')) url += '?'\n\t\treturn `${url}${encode(init.body)}`\n\t}\n}\n\n/* \nr: a native fetch response argument\n\npotentially allow \"application/octet-stream\" as response type\nin such case it will not try to parse it as json\nalso the caller should just call dofetch2() without a serverData{}\nrather than dofetch3\n*/\nexport async function processResponse(r) {\n\t// if (!r.ok) {\n\t// throw new Error(`HTTP error! status: ${r.status}`)\n\t// }\n\tconst ct = r.headers.get('content-type') // content type is always present\n\tif (!ct) throw `missing response.header['content-type']`\n\tif (ct.includes('/json')) {\n\t\tconst payload = await r.json()\n\t\t// server should use a standard HTTP response status 400+, 500+\n\t\t// so that !r.ok will already be caught when wrapping fetch with try-catch\n\t\t// if (payload.error || payload.status == '') throw payload\n\t\t// if (payload.status === 'error') throw payload.message || payload\n\t\treturn payload\n\t}\n\tif (ct.includes('/text') || ct.includes('text/')) {\n\t\treturn r.text()\n\t}\n\tif (ct.includes('multipart')) {\n\t\tif (ct.startsWith('multipart/form-data')) return processFormData(r)\n\t\telse throw `cannot handle response content-type: '${ct}'`\n\t}\n\tif (ct == 'application/x-ndjson-nestedkey') {\n\t\treturn processNDJSON_nestedKey(r)\n\t}\n\t// call blob() as catch-all\n\t// https://developer.mozilla.org/en-US/docs/Web/API/Response\n\treturn r.blob()\n}\n\n/*\n\texpected response format\n\t--boundary-text-from-HTTP-headers-content-type\n\theader-key1: header-value1\n\theader-key2: header-value2\n\n\t...json, text, blob, value, etc...\n\t--boundary-text-from-HTTP-headers-content-type\n\t... same format as previous chunk ...\n\n\t--boundary-text-from-HTTP-headers-content-type--\n*/\n\nexport async function processFormData(res) {\n\tconst decoder = new TextDecoder()\n\tconst data = {}\n\ttry {\n\t\tconst form = await res.formData()\n\t\t// The key of each form entry is a string, and the value is either a string or a Blob.\n\t\t// see https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries\n\t\tfor (const [key, value] of form.entries()) {\n\t\t\tif (value.type) {\n\t\t\t\t// value is a Blob\n\t\t\t\tdata[key] = { headers: { 'content-type': value.type }, body: value }\n\t\t\t} else {\n\t\t\t\t// value is a string, assume to be application/x-jsonlines (one json encoded value per line)\n\t\t\t\t// and convert into an array of json-decoded values\n\t\t\t\tconst body = !value ? [] : value.trim().split('\\n').map(JSON.parse)\n\t\t\t\tdata[key] = { headers: { 'content-type': 'application/json' }, body }\n\t\t\t}\n\t\t}\n\t\treturn data\n\t} catch (e) {\n\t\tthrow e\n\t}\n}\n\nasync function processNDJSON_nestedKey(r) {\n\t// 1. Pipe through TextDecoder to convert bytes to text\n\tconst stream = r.body.pipeThrough(new TextDecoderStream())\n\tconst reader = stream.getReader()\n\tlet rootObj = {}\n\n\tlet buffer = ''\n\n\twhile (true) {\n\t\tconst { value, done } = await reader.read()\n\t\tif (done) break\n\n\t\t// 2. Add new chunk to buffer\n\t\tbuffer += value\n\n\t\t// 3. Split by newline\n\t\tlet parts = buffer.split('\\n')\n\n\t\t// 4. Keep the last partial line in the buffer\n\t\tbuffer = parts.pop()\n\n\t\t// 5. Process complete lines\n\t\tfor (const line of parts) {\n\t\t\tif (line.trim()) {\n\t\t\t\tconst [keys, data] = JSON.parse(line) //; console.log(143, keys, data) // Process JSON data\n\t\t\t\tif (!keys.length) rootObj = data\n\t\t\t\telse {\n\t\t\t\t\tconst lastKey = keys.pop()\n\t\t\t\t\tlet target = rootObj\n\t\t\t\t\tfor (const k of keys) target = target[k]\n\t\t\t\t\ttarget[lastKey] = data\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn rootObj\n}\n\n// key: request object reference or computed string dataName\n// value: {\n// response: fetch promise or response,\n// exp: expiration timestamp\n// }\nconst dataCache = new Map()\n// maximum number of cached dataNames, oldest will be deleted if this is exceeded\nconst maxNumOfDataKeys = 10\nconst cacheLifetime = 1000 * 60 * 5\n/*\n\tmemFetch()\n\t- fetch wrapper that saves cached responses into memory and recovers them for matching subsequent requests\n\t- recommended for caching responses in the backend, with the opts.q argument to cache per expressjs request object\n\t- should call deleteCache(request) at the end of request handling, to free unneeded cache\n\t\n\tSee the usage note for getDataName() to avoid non-unique request/response.\n\t\n\tArguments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n - when passing opts.client, may include other applicable options inside the init{} object, such as retry\n\n\topts{client}\n\n client: use this http client instead of native fetch\n - since fetch-helpers is shared between server and frontend workspaces, \n cannot directly import non-native modules at the beginning of this code file \n - for server side usage, client may be `xfetch()`, `ky` or other libraries \n*/\nexport async function memFetch(url, init, opts = {}) {\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\tconst dataKey = opts.q || (await getDataName(url, init))\n\tconst { response, exp } = dataCache.get(dataKey) || {}\n\tconst now = Date.now()\n\tlet result = response // either a Promise or actual data\n\n\tif (result) {\n\t\t// extend the expiration, since exp is more about managing the cache size\n\t\t// and not the validity of the cached response. A response for the current\n\t\t// dataName req.url + body + headers is technically valid until a new data version\n\t\t// gets published.\n\t\tdataCache.set(dataKey, { response, exp: now + cacheLifetime })\n\t\treturn result\n\t} else {\n\t\ttry {\n\t\t\t// IMPORTANT: do not await so that this same promise may be reused\n\t\t\t// by subsequent requests with the same dataKey\n\t\t\tresult = opts.client\n\t\t\t\t? opts.client(url, init, Object.assign(opts, { client: undefined })).then(response => {\n\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\tdataCache.set(dataKey, { response, exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\treturn response\n\t\t\t\t })\n\t\t\t\t: fetch(url, init)\n\t\t\t\t\t\t.then(async r => {\n\t\t\t\t\t\t\tconst response = await processResponse(r)\n\t\t\t\t\t\t\tif (!r.ok) {\n\t\t\t\t\t\t\t\tconsole.trace(response)\n\t\t\t\t\t\t\t\tthrow (\n\t\t\t\t\t\t\t\t\t'memFetch error ' +\n\t\t\t\t\t\t\t\t\tr.status +\n\t\t\t\t\t\t\t\t\t': ' +\n\t\t\t\t\t\t\t\t\t(typeof response == 'object' ? response.message || response.error : response)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\t\tdataCache.set(dataKey, { response: deepFreeze(response), exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\t\treturn response\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(e => {\n\t\t\t\t\t\t\tif (dataCache.get(dataKey)) dataCache.delete(dataKey)\n\t\t\t\t\t\t\tthrow e\n\t\t\t\t\t\t})\n\n\t\t\tdataCache.set(dataKey, { response: result, exp: Date.now() + cacheLifetime })\n\t\t\tmanageCacheSize(now)\n\t\t\treturn result\n\t\t} catch (e) {\n\t\t\t// delete this cache only if it is a promise;\n\t\t\t// do not delete a valid resolved data cache\n\t\t\tif (dataCache.get(dataKey) instanceof Promise) dataCache.delete(dataKey)\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport function deleteCache(key) {\n\tdataCache.delete(key)\n}\n\nexport function manageCacheSize(_now) {\n\tconst now = _now || Date.now()\n\tconst keyExp = []\n\tfor (const [key, result] of dataCache.entries()) {\n\t\tif (result.exp < now) dataCache.delete(key)\n\t\telse keyExp.push({ key, exp: result.exp })\n\t}\n\tif (dataCache.size > maxNumOfDataKeys) {\n\t\tconst oldestEntries = keyExp.sort((a, b) => a.exp - b.exp).slice(maxNumOfDataKeys)\n\t\tfor (const entry of oldestEntries) dataCache.delete(entry.key)\n\t}\n}\n\n/*\n\tNOTE: When used in client-side code, an HttpOnly cookie for a logged in user will not be\n\ttracked in init.headers below. \n*/\nexport async function getDataName(url, init) {\n\t// IMPORTANT: must ensure dataName is unique to either public or logged-in user\n\tconst dataName = url + ' | ' + init.method + ' | ' + init.body + ' | ' + JSON.stringify(init.headers)\n\treturn await hash(dataName)\n}\n\n//\nexport function clearMemFetchDataCache(opts = {}) {\n\tif (!opts.serverData) {\n\t\tdataCache.clear()\n\t\treturn\n\t}\n\tif (typeof opts.serverData != 'object') throw `opts.serverData is not an object`\n\tfor (const k of Object.keys(opts.serverData)) {\n\t\tdelete opts.serverData[k]\n\t}\n\tif (optsServerDataNames.has(opts.serverData)) optsServerDataNames.delete(opts.serverData)\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,kBAAkB;
|
|
3
|
+
"sources": ["../../src/fetch-helpers.ts"],
|
|
4
|
+
"sourcesContent": ["import { hash } from './hash.js'\nimport { encode } from './urljson.js'\nimport { deepFreeze } from './helpers.js'\n\n// tracks serverData objects so their associated cache entries can be cleared; uses WeakMap to avoid memory leaks\nconst optsServerDataNames = new WeakMap()\n\n/*\n\tezFetch()\n\tfetch wrapper with automatic response content-type detection and handling\n\n\t- this addresses issues with ky.json() or got.json(), where a HTTP 404 NOT FOUND\n\t\tresponse with text/html can break error handling/logging, making it harder to debug\n\n\t- this also automatically handles multipart responses\n\n - NOTE: for backend, use xfetch() instead, it uses ky() and its built-in retry support\n\n\targuments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n*/\nexport async function ezFetch(_url, init: Record<string, any> = {}, opts: Record<string, any> = {}) {\n\tconst url = opts.autoMethod ? mayAdjustRequest(_url, init) : _url\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\n\treturn fetch(url, init).then(async r => {\n\t\tconst response = await processResponse(r)\n\t\tif (!r.ok) {\n\t\t\tconsole.log('ezFetch error ' + r.status)\n\t\t\tconsole.log(response)\n\t\t\tthrow response\n\t\t}\n\t\treturn response\n\t})\n}\n\nfunction mayAdjustRequest(url, init: Record<string, any>) {\n\tconst method = init.method?.toUpperCase() || 'GET'\n\tif (method == 'POST') {\n\t\tif (!init.headers) init.headers = {}\n\t\tif (init.body) {\n\t\t\tif (!init.headers['content-type']) init.headers['content-type'] = 'application/json'\n\t\t\tif (init.headers['content-type'].toLowerCase() == 'application/json') {\n\t\t\t\t// if consumer code has pre-encoded the body, parse to verify correctness\n\t\t\t\tif (typeof init.body == 'string') init.body = JSON.parse(init.body)\n\t\t\t\tinit.body = JSON.stringify(init.body)\n\t\t\t}\n\t\t}\n\t\treturn url\n\t}\n\t// default to GET method per native fetch\n\tif (init.body) {\n\t\tif (typeof init.body != 'object') throw `init.body should be an object`\n\t\t// init.body should be an object, to be converted to GET URL search parameter strings\n\t\tif (!url.includes('?')) url += '?'\n\t\treturn `${url}${encode(init.body)}`\n\t}\n}\n\n/* \nr: a native fetch response argument\n\npotentially allow \"application/octet-stream\" as response type\nin such case it will not try to parse it as json\nalso the caller should just call dofetch2() without a serverData{}\nrather than dofetch3\n*/\nexport async function processResponse(r) {\n\t// if (!r.ok) {\n\t// throw new Error(`HTTP error! status: ${r.status}`)\n\t// }\n\tconst ct = r.headers.get('content-type') // content type is always present\n\tif (!ct) throw `missing response.header['content-type']`\n\tif (ct.includes('/json')) {\n\t\tconst payload = await r.json()\n\t\t// server should use a standard HTTP response status 400+, 500+\n\t\t// so that !r.ok will already be caught when wrapping fetch with try-catch\n\t\t// if (payload.error || payload.status == '') throw payload\n\t\t// if (payload.status === 'error') throw payload.message || payload\n\t\treturn payload\n\t}\n\tif (ct.includes('/text') || ct.includes('text/')) {\n\t\treturn r.text()\n\t}\n\tif (ct.includes('multipart')) {\n\t\tif (ct.startsWith('multipart/form-data')) return processFormData(r)\n\t\telse throw `cannot handle response content-type: '${ct}'`\n\t}\n\tif (ct == 'application/x-ndjson-nestedkey') {\n\t\treturn processNDJSON_nestedKey(r)\n\t}\n\t// call blob() as catch-all\n\t// https://developer.mozilla.org/en-US/docs/Web/API/Response\n\treturn r.blob()\n}\n\n/*\n\texpected response format\n\t--boundary-text-from-HTTP-headers-content-type\n\theader-key1: header-value1\n\theader-key2: header-value2\n\n\t...json, text, blob, value, etc...\n\t--boundary-text-from-HTTP-headers-content-type\n\t... same format as previous chunk ...\n\n\t--boundary-text-from-HTTP-headers-content-type--\n*/\n\nexport async function processFormData(res) {\n\tconst data = {}\n\tconst form = await res.formData()\n\t// The key of each form entry is a string, and the value is either a string or a Blob.\n\t// see https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries\n\tfor (const [key, value] of form.entries()) {\n\t\tif (value.type) {\n\t\t\t// value is a Blob\n\t\t\tdata[key] = { headers: { 'content-type': value.type }, body: value }\n\t\t} else {\n\t\t\t// value is a string, assume to be application/x-jsonlines (one json encoded value per line)\n\t\t\t// and convert into an array of json-decoded values\n\t\t\tconst body = !value ? [] : value.trim().split('\\n').map(JSON.parse)\n\t\t\tdata[key] = { headers: { 'content-type': 'application/json' }, body }\n\t\t}\n\t}\n\treturn data\n}\n\nasync function processNDJSON_nestedKey(r) {\n\t// 1. Pipe through TextDecoder to convert bytes to text\n\tconst stream = r.body.pipeThrough(new TextDecoderStream())\n\tconst reader = stream.getReader()\n\tlet rootObj = {}\n\n\tlet buffer = ''\n\n\tlet done = false\n\twhile (!done) {\n\t\tconst readResult = await reader.read()\n\t\tdone = readResult.done\n\t\tif (done) break\n\t\tconst value = readResult.value\n\n\t\t// 2. Add new chunk to buffer\n\t\tbuffer += value\n\n\t\t// 3. Split by newline\n\t\tconst parts = buffer.split('\\n')\n\n\t\t// 4. Keep the last partial line in the buffer\n\t\tbuffer = parts.pop() ?? ''\n\n\t\t// 5. Process complete lines\n\t\tfor (const line of parts) {\n\t\t\tif (line.trim()) {\n\t\t\t\tconst [keys, data] = JSON.parse(line) //; console.log(143, keys, data) // Process JSON data\n\t\t\t\tif (!keys.length) rootObj = data\n\t\t\t\telse {\n\t\t\t\t\tconst lastKey = keys.pop()\n\t\t\t\t\tlet target = rootObj\n\t\t\t\t\tfor (const k of keys) target = target[k]\n\t\t\t\t\ttarget[lastKey] = data\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn rootObj\n}\n\n// key: request object reference or computed string dataName\n// value: {\n// response: fetch promise or response,\n// exp: expiration timestamp\n// }\nconst dataCache = new Map()\n// maximum number of cached dataNames, oldest will be deleted if this is exceeded\nconst maxNumOfDataKeys = 10\nconst cacheLifetime = 1000 * 60 * 5\n/*\n\tmemFetch()\n\t- fetch wrapper that saves cached responses into memory and recovers them for matching subsequent requests\n\t- recommended for caching responses in the backend, with the opts.q argument to cache per expressjs request object\n\t- should call deleteCache(request) at the end of request handling, to free unneeded cache\n\t\n\tSee the usage note for getDataName() to avoid non-unique request/response.\n\t\n\tArguments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n - when passing opts.client, may include other applicable options inside the init{} object, such as retry\n\n\topts{client}\n\n client: use this http client instead of native fetch\n - since fetch-helpers is shared between server and frontend workspaces, \n cannot directly import non-native modules at the beginning of this code file \n - for server side usage, client may be `xfetch()`, `ky` or other libraries \n*/\nexport async function memFetch(url, init: Record<string, any>, opts: Record<string, any> = {}) {\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\tconst dataKey = opts.q || (await getDataName(url, init))\n\tconst { response } = dataCache.get(dataKey) || {}\n\tconst now = Date.now()\n\tlet result = response // either a Promise or actual data\n\n\tif (result) {\n\t\t// extend the expiration, since exp is more about managing the cache size\n\t\t// and not the validity of the cached response. A response for the current\n\t\t// dataName req.url + body + headers is technically valid until a new data version\n\t\t// gets published.\n\t\tdataCache.set(dataKey, { response, exp: now + cacheLifetime })\n\t\treturn result\n\t} else {\n\t\ttry {\n\t\t\t// IMPORTANT: do not await so that this same promise may be reused\n\t\t\t// by subsequent requests with the same dataKey\n\t\t\tresult = opts.client\n\t\t\t\t? opts.client(url, init, Object.assign(opts, { client: undefined })).then(response => {\n\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\tdataCache.set(dataKey, { response, exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\treturn response\n\t\t\t\t })\n\t\t\t\t: fetch(url, init)\n\t\t\t\t\t\t.then(async r => {\n\t\t\t\t\t\t\tconst response = await processResponse(r)\n\t\t\t\t\t\t\tif (!r.ok) {\n\t\t\t\t\t\t\t\tconsole.trace(response)\n\t\t\t\t\t\t\t\tthrow (\n\t\t\t\t\t\t\t\t\t'memFetch error ' +\n\t\t\t\t\t\t\t\t\tr.status +\n\t\t\t\t\t\t\t\t\t': ' +\n\t\t\t\t\t\t\t\t\t(typeof response == 'object' ? response.message || response.error : response)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\t\tdataCache.set(dataKey, { response: deepFreeze(response), exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\t\treturn response\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(e => {\n\t\t\t\t\t\t\tif (dataCache.get(dataKey)) dataCache.delete(dataKey)\n\t\t\t\t\t\t\tthrow e\n\t\t\t\t\t\t})\n\n\t\t\tdataCache.set(dataKey, { response: result, exp: Date.now() + cacheLifetime })\n\t\t\tmanageCacheSize(now)\n\t\t\treturn result\n\t\t} catch (e) {\n\t\t\t// delete this cache only if it is a promise;\n\t\t\t// do not delete a valid resolved data cache\n\t\t\tif (dataCache.get(dataKey) instanceof Promise) dataCache.delete(dataKey)\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport function deleteCache(key) {\n\tdataCache.delete(key)\n}\n\nexport function manageCacheSize(_now) {\n\tconst now = _now || Date.now()\n\tconst keyExp: { key: any; exp: number }[] = []\n\tfor (const [key, result] of dataCache.entries()) {\n\t\tif (result.exp < now) dataCache.delete(key)\n\t\telse keyExp.push({ key, exp: result.exp })\n\t}\n\tif (dataCache.size > maxNumOfDataKeys) {\n\t\tconst oldestEntries = keyExp.sort((a, b) => a.exp - b.exp).slice(maxNumOfDataKeys)\n\t\tfor (const entry of oldestEntries) dataCache.delete(entry.key)\n\t}\n}\n\n/*\n\tNOTE: When used in client-side code, an HttpOnly cookie for a logged in user will not be\n\ttracked in init.headers below. \n*/\nexport async function getDataName(url, init) {\n\t// IMPORTANT: must ensure dataName is unique to either public or logged-in user\n\tconst dataName = url + ' | ' + init.method + ' | ' + init.body + ' | ' + JSON.stringify(init.headers)\n\treturn await hash(dataName)\n}\n\n//\nexport function clearMemFetchDataCache(opts: Record<string, any> = {}) {\n\tif (!opts.serverData) {\n\t\tdataCache.clear()\n\t\treturn\n\t}\n\tif (typeof opts.serverData != 'object') throw `opts.serverData is not an object`\n\tfor (const k of Object.keys(opts.serverData)) {\n\t\tdelete opts.serverData[k]\n\t}\n\tif (optsServerDataNames.has(opts.serverData)) optsServerDataNames.delete(opts.serverData)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["response"]
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fileSize(v: any): string;
|
package/dist/src/fileSize.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/fileSize.
|
|
3
|
+
"sources": ["../../src/fileSize.ts"],
|
|
4
4
|
"sourcesContent": ["export function fileSize(v) {\n\tif (v > 1e9) return (v / 1e9).toFixed(2) + ' GB'\n\tif (v > 1e6) return (v / 1e6).toFixed(2) + ' MB'\n\tif (v > 1e3) return (v / 1e3).toFixed(2) + ' KB'\n\treturn v + ' Bytes'\n}\n"],
|
|
5
5
|
"mappings": "AAAO,SAAS,SAAS,GAAG;AAC3B,MAAI,IAAI,IAAK,SAAQ,IAAI,KAAK,QAAQ,CAAC,IAAI;AAC3C,MAAI,IAAI,IAAK,SAAQ,IAAI,KAAK,QAAQ,CAAC,IAAI;AAC3C,MAAI,IAAI,IAAK,SAAQ,IAAI,KAAK,QAAQ,CAAC,IAAI;AAC3C,SAAO,IAAI;AACZ;",
|
|
6
6
|
"names": []
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hash(message: any): Promise<string>;
|
package/dist/src/hash.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/hash.
|
|
3
|
+
"sources": ["../../src/hash.ts"],
|
|
4
4
|
"sourcesContent": ["const encoder = new TextEncoder()\n\nexport async function hash(message) {\n\tconst msgUint8 = encoder.encode(message) // encode as (utf-8) Uint8Array\n\tconst hashBuffer = await crypto.subtle.digest('SHA-1', msgUint8) // hash the message\n\tconst hashArray = Array.from(new Uint8Array(hashBuffer)) // convert buffer to byte array\n\tconst hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string\n\treturn hexToBase64(hashHex).replace('=', '-') // shorten from 40 to 28 chars\n}\n\nfunction hexToBase64(hexStr) {\n\treturn btoa(\n\t\t[...hexStr].reduce(\n\t\t\t(acc, _, i) => (acc += !((i - 1) & 1) ? String.fromCharCode(parseInt(hexStr.substring(i - 1, i + 1), 16)) : ''),\n\t\t\t''\n\t\t)\n\t)\n}\n"],
|
|
5
5
|
"mappings": "AAAA,MAAM,UAAU,IAAI,YAAY;AAEhC,eAAsB,KAAK,SAAS;AACnC,QAAM,WAAW,QAAQ,OAAO,OAAO;AACvC,QAAM,aAAa,MAAM,OAAO,OAAO,OAAO,SAAS,QAAQ;AAC/D,QAAM,YAAY,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACvD,QAAM,UAAU,UAAU,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC3E,SAAO,YAAY,OAAO,EAAE,QAAQ,KAAK,GAAG;AAC7C;AAEA,SAAS,YAAY,QAAQ;AAC5B,SAAO;AAAA,IACN,CAAC,GAAG,MAAM,EAAE;AAAA,MACX,CAAC,KAAK,GAAG,MAAO,OAAO,EAAG,IAAI,IAAK,KAAK,OAAO,aAAa,SAAS,OAAO,UAAU,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI;AAAA,MAC5G;AAAA,IACD;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function isNumeric(n: any): boolean;
|
|
2
|
+
export declare function isStrictNumeric(n: any): boolean;
|
|
3
|
+
export declare function convertUnits(v: any, fromUnit: any, toUnit: any, scaleFactor: any, compact?: boolean): string;
|
|
4
|
+
export declare function deepEqual(x: any, y: any): boolean;
|
|
5
|
+
export declare function deepFreeze(obj: any): any;
|
|
6
|
+
export declare class CustomError extends Error {
|
|
7
|
+
level: string;
|
|
8
|
+
code?: string;
|
|
9
|
+
constructor(message: any, opts?: {
|
|
10
|
+
name?: string;
|
|
11
|
+
code?: string;
|
|
12
|
+
level?: string;
|
|
13
|
+
});
|
|
14
|
+
}
|
package/dist/src/helpers.js
CHANGED
|
@@ -28,8 +28,8 @@ function deepEqual(x, y) {
|
|
|
28
28
|
if (Object.keys(x).length != Object.keys(y).length) {
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
|
-
for (
|
|
32
|
-
if (
|
|
31
|
+
for (const prop of Object.keys(x)) {
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(y, prop)) {
|
|
33
33
|
if (!deepEqual(x[prop], y[prop])) return false;
|
|
34
34
|
} else {
|
|
35
35
|
return false;
|
|
@@ -46,10 +46,9 @@ function deepFreeze(obj) {
|
|
|
46
46
|
return obj;
|
|
47
47
|
}
|
|
48
48
|
class CustomError extends Error {
|
|
49
|
-
level = "";
|
|
50
|
-
// '' | 'warn'
|
|
51
49
|
constructor(message, opts = {}) {
|
|
52
50
|
super(message);
|
|
51
|
+
this.level = "";
|
|
53
52
|
if (opts.name) this.name = opts.name;
|
|
54
53
|
if (opts.code) this.code = opts.code;
|
|
55
54
|
if (opts.level) this.level = opts.level;
|
package/dist/src/helpers.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/helpers.
|
|
4
|
-
"sourcesContent": ["/*\nthis is a helper file with a collection of functions to be used in backend and client side code. Here is a list.\n\n1. isNumeric(n)\n2. strictNumeric(n) \n2. convertUnits\n3. TODO - move computepercentile, roundValue, etc here?\n*/\n\n// checks whether given argument n is Numeric, with option to cast from string\nexport function isNumeric(n) {\n\tconst v = typeof n != 'string' || n === '' ? n : Number(n)\n\tconst f = parseFloat(n)\n\treturn !isNaN(f) && Number.isFinite(v) && v === f\n}\n\n// like isNumeric but does not cast from string\nexport function isStrictNumeric(n) {\n\treturn typeof n === 'number' && Number.isFinite(n)\n}\n\n// converts a value from a unit to another unit\nexport function convertUnits(v, fromUnit, toUnit, scaleFactor, compact) {\n\t// do floor() on toUnit\n\t// do ceil() on fromUnit, in case v is decimal (from violin range selection) and to keep showing integer fromUnit\n\tif (scaleFactor >= 1) {\n\t\tconst toUnitV = Math.floor(v * scaleFactor)\n\t\tif (compact) return `${toUnitV}${toUnit.charAt(0)}`\n\t\treturn `${toUnitV} ${toUnitV > 1 ? toUnit + 's' : ''}`\n\t}\n\tconst toUnitV = Math.floor(v * scaleFactor)\n\tconst fromUnitV = Math.ceil(v % (1 / scaleFactor))\n\n\tif (fromUnitV == 0) {\n\t\tif (compact) return `${toUnitV}${toUnit.charAt(0)}`\n\t\treturn `${toUnitV} ${toUnitV > 1 ? toUnit + 's' : ''}`\n\t}\n\n\tif (compact) return `${toUnitV}${toUnit.charAt(0)}${fromUnitV}${fromUnit.charAt(0)}`\n\treturn `${toUnitV} ${toUnitV > 1 ? toUnit + 's' : toUnit} ${fromUnitV} ${fromUnitV > 1 ? fromUnit + 's' : fromUnit}`\n}\n\nexport function deepEqual(x, y) {\n\tif (x === y) {\n\t\treturn true\n\t} else if (typeof x == 'object' && x != null && typeof y == 'object' && y != null) {\n\t\tif (Object.keys(x).length != Object.keys(y).length) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor (
|
|
5
|
-
"mappings": "AAUO,SAAS,UAAU,GAAG;AAC5B,QAAM,IAAI,OAAO,KAAK,YAAY,MAAM,KAAK,IAAI,OAAO,CAAC;AACzD,QAAM,IAAI,WAAW,CAAC;AACtB,SAAO,CAAC,MAAM,CAAC,KAAK,OAAO,SAAS,CAAC,KAAK,MAAM;AACjD;AAGO,SAAS,gBAAgB,GAAG;AAClC,SAAO,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC;AAClD;AAGO,SAAS,aAAa,GAAG,UAAU,QAAQ,aAAa,
|
|
3
|
+
"sources": ["../../src/helpers.ts"],
|
|
4
|
+
"sourcesContent": ["/*\nthis is a helper file with a collection of functions to be used in backend and client side code. Here is a list.\n\n1. isNumeric(n)\n2. strictNumeric(n) \n2. convertUnits\n3. TODO - move computepercentile, roundValue, etc here?\n*/\n\n// checks whether given argument n is Numeric, with option to cast from string\nexport function isNumeric(n) {\n\tconst v = typeof n != 'string' || n === '' ? n : Number(n)\n\tconst f = parseFloat(n)\n\treturn !isNaN(f) && Number.isFinite(v) && v === f\n}\n\n// like isNumeric but does not cast from string\nexport function isStrictNumeric(n) {\n\treturn typeof n === 'number' && Number.isFinite(n)\n}\n\n// converts a value from a unit to another unit\nexport function convertUnits(v, fromUnit, toUnit, scaleFactor, compact?: boolean) {\n\t// do floor() on toUnit\n\t// do ceil() on fromUnit, in case v is decimal (from violin range selection) and to keep showing integer fromUnit\n\tif (scaleFactor >= 1) {\n\t\tconst toUnitV = Math.floor(v * scaleFactor)\n\t\tif (compact) return `${toUnitV}${toUnit.charAt(0)}`\n\t\treturn `${toUnitV} ${toUnitV > 1 ? toUnit + 's' : ''}`\n\t}\n\tconst toUnitV = Math.floor(v * scaleFactor)\n\tconst fromUnitV = Math.ceil(v % (1 / scaleFactor))\n\n\tif (fromUnitV == 0) {\n\t\tif (compact) return `${toUnitV}${toUnit.charAt(0)}`\n\t\treturn `${toUnitV} ${toUnitV > 1 ? toUnit + 's' : ''}`\n\t}\n\n\tif (compact) return `${toUnitV}${toUnit.charAt(0)}${fromUnitV}${fromUnit.charAt(0)}`\n\treturn `${toUnitV} ${toUnitV > 1 ? toUnit + 's' : toUnit} ${fromUnitV} ${fromUnitV > 1 ? fromUnit + 's' : fromUnit}`\n}\n\nexport function deepEqual(x, y) {\n\tif (x === y) {\n\t\treturn true\n\t} else if (typeof x == 'object' && x != null && typeof y == 'object' && y != null) {\n\t\tif (Object.keys(x).length != Object.keys(y).length) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor (const prop of Object.keys(x)) {\n\t\t\tif (Object.prototype.hasOwnProperty.call(y, prop)) {\n\t\t\t\tif (!deepEqual(x[prop], y[prop])) return false\n\t\t\t} else {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t} else return false\n}\n\nexport function deepFreeze(obj) {\n\tObject.freeze(obj)\n\t// not using for..in loop, in order to not descend into inherited props/methods\n\tfor (const value of Object.values(obj)) {\n\t\tif (value !== null && typeof value == 'object') deepFreeze(value)\n\t}\n\treturn obj\n}\n\nexport class CustomError extends Error {\n\tlevel = '' // '' | 'warn'\n\tcode?: string\n\n\tconstructor(message, opts: { name?: string; code?: string; level?: string } = {}) {\n\t\tsuper(message)\n\t\tif (opts.name) this.name = opts.name\n\t\tif (opts.code) this.code = opts.code\n\t\tif (opts.level) this.level = opts.level\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AAUO,SAAS,UAAU,GAAG;AAC5B,QAAM,IAAI,OAAO,KAAK,YAAY,MAAM,KAAK,IAAI,OAAO,CAAC;AACzD,QAAM,IAAI,WAAW,CAAC;AACtB,SAAO,CAAC,MAAM,CAAC,KAAK,OAAO,SAAS,CAAC,KAAK,MAAM;AACjD;AAGO,SAAS,gBAAgB,GAAG;AAClC,SAAO,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC;AAClD;AAGO,SAAS,aAAa,GAAG,UAAU,QAAQ,aAAa,SAAmB;AAGjF,MAAI,eAAe,GAAG;AACrB,UAAMA,WAAU,KAAK,MAAM,IAAI,WAAW;AAC1C,QAAI,QAAS,QAAO,GAAGA,QAAO,GAAG,OAAO,OAAO,CAAC,CAAC;AACjD,WAAO,GAAGA,QAAO,IAAIA,WAAU,IAAI,SAAS,MAAM,EAAE;AAAA,EACrD;AACA,QAAM,UAAU,KAAK,MAAM,IAAI,WAAW;AAC1C,QAAM,YAAY,KAAK,KAAK,KAAK,IAAI,YAAY;AAEjD,MAAI,aAAa,GAAG;AACnB,QAAI,QAAS,QAAO,GAAG,OAAO,GAAG,OAAO,OAAO,CAAC,CAAC;AACjD,WAAO,GAAG,OAAO,IAAI,UAAU,IAAI,SAAS,MAAM,EAAE;AAAA,EACrD;AAEA,MAAI,QAAS,QAAO,GAAG,OAAO,GAAG,OAAO,OAAO,CAAC,CAAC,GAAG,SAAS,GAAG,SAAS,OAAO,CAAC,CAAC;AAClF,SAAO,GAAG,OAAO,IAAI,UAAU,IAAI,SAAS,MAAM,MAAM,IAAI,SAAS,IAAI,YAAY,IAAI,WAAW,MAAM,QAAQ;AACnH;AAEO,SAAS,UAAU,GAAG,GAAG;AAC/B,MAAI,MAAM,GAAG;AACZ,WAAO;AAAA,EACR,WAAW,OAAO,KAAK,YAAY,KAAK,QAAQ,OAAO,KAAK,YAAY,KAAK,MAAM;AAClF,QAAI,OAAO,KAAK,CAAC,EAAE,UAAU,OAAO,KAAK,CAAC,EAAE,QAAQ;AACnD,aAAO;AAAA,IACR;AAEA,eAAW,QAAQ,OAAO,KAAK,CAAC,GAAG;AAClC,UAAI,OAAO,UAAU,eAAe,KAAK,GAAG,IAAI,GAAG;AAClD,YAAI,CAAC,UAAU,EAAE,IAAI,GAAG,EAAE,IAAI,CAAC,EAAG,QAAO;AAAA,MAC1C,OAAO;AACN,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR,MAAO,QAAO;AACf;AAEO,SAAS,WAAW,KAAK;AAC/B,SAAO,OAAO,GAAG;AAEjB,aAAW,SAAS,OAAO,OAAO,GAAG,GAAG;AACvC,QAAI,UAAU,QAAQ,OAAO,SAAS,SAAU,YAAW,KAAK;AAAA,EACjE;AACA,SAAO;AACR;AAEO,MAAM,oBAAoB,MAAM;AAAA,EAItC,YAAY,SAAS,OAAyD,CAAC,GAAG;AACjF,UAAM,OAAO;AAJd,iBAAQ;AAKP,QAAI,KAAK,KAAM,MAAK,OAAO,KAAK;AAChC,QAAI,KAAK,KAAM,MAAK,OAAO,KAAK;AAChC,QAAI,KAAK,MAAO,MAAK,QAAQ,KAAK;AAAA,EACnC;AACD;",
|
|
6
6
|
"names": ["toUnitV"]
|
|
7
7
|
}
|
package/dist/src/mds3tk.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/mds3tk.
|
|
4
|
-
"sourcesContent": ["import { dtcnv, dtsnvindel, dtsv, dtfusionrna } from './common.js'\n\n// this script should contain mds3 track-related stuff shared between client and backend\n\n/*\nthe separator is used to join essential bits of a variant obj into a string as the \"ssm_id\", aims to uniquely identify a variant irrespective of sample\nthis is to mimic the GDC \"ssm_id\" which is a random id, with below benefits:\n- consistent way to pass request body for both gdc and non-gdc\n- uniform identification of ssm/cnv/sv in non-gdc backend code\n- uniform identification of all variants in client\n\nssm: chr + pos + ref + alt\ncnv: chr + start + stop + class\nsvfusion: dt + chr + pos + strand + pairlstidx + mname\n\nthe separator must avoid conflicting with characters from gene names, and can be changed based on needs\n*/\nexport const ssmIdFieldsSeparator = '__'\n\n/*\ninput: array of mixture of ssm, svfusion and cnv\n\noutput: sorted array. each element: [ class/dt, count of m ]\n*/\nexport function summarize_mclass(mlst) {\n\tconst m2c = new Map() // k: mclass, v: {}\n\tconst cnvs = []\n\tfor (const m of mlst) {\n\t\tif (m.dt == dtcnv) {\n\t\t\tcnvs.push(m)\n\t\t\tcontinue // process cnv later\n\t\t}\n\t\t// snvindel has m.class=str, svfusion has only dt=int\n\t\tconst key = m.class || m.dt\n\t\tm2c.set(key, 1 + (m2c.get(key) || 0))\n\t}\n\n\tif (cnvs.length) {\n\t\tif (Number.isFinite(cnvs[0].value)) {\n\t\t\t// first cnv uses numeric value (assumes all the same). record by dt\n\t\t\tm2c.set(dtcnv, cnvs.length)\n\t\t} else {\n\t\t\t// cnv not numeric and uses class; record by each class\n\t\t\tfor (const c of cnvs) {\n\t\t\t\tif (!c.class) continue // should not happen\n\t\t\t\tm2c.set(c.class, 1 + (m2c.get(c.class) || 0))\n\t\t\t}\n\t\t}\n\t}\n\treturn [...m2c].sort((i, j) => j[1] - i[1])\n}\n\n/*\nssmid is not specific for ssm, it covers all alterations\ngdc ssm are identified by a specific uuid, thus the design\n*/\nexport function guessSsmid(ssmid) {\n\tconst l = ssmid.split(ssmIdFieldsSeparator)\n\tif (l.length == 4) {\n\t\tconst [chr, tmp, ref, alt] = l\n\t\tconst pos = Number(tmp)\n\t\tif (Number.isNaN(pos)) throw 'ssmid snvindel pos not integer'\n\t\treturn { dt: dtsnvindel, l: [chr, pos, ref, alt] }\n\t}\n\tif (l.length == 5) {\n\t\t// cnv. if type=cat, _value is blank string\n\t\tconst [chr, _start, _stop, _class, _value] = l\n\t\tconst start = Number(_start),\n\t\t\tstop = Number(_stop),\n\t\t\tvalue = _value == '' ? null : Number(_value)\n\t\tif (Number.isNaN(start) || Number.isNaN(stop)) throw 'ssmid cnv start/stop not integer'\n\t\treturn { dt: dtcnv, l: [chr, start, stop, _class, value] }\n\t}\n\tif (l.length == 6) {\n\t\tif (l[3] == '+' || l[3] == '-') {\n\t\t\t// sv/fusion\n\t\t\tconst [_dt, chr, _pos, strand, _pi, _mname] = l\n\n\t\t\t// mname is encoded in case it contains comma (and is same as ssmIdFieldsSeparator)\n\t\t\tconst mname = decodeURIComponent(_mname)\n\t\t\tconst dt = Number(_dt)\n\t\t\tif (dt != dtsv && dt != dtfusionrna) throw 'ssmid dt not sv/fusion'\n\t\t\tconst pos = Number(_pos)\n\t\t\tif (Number.isNaN(pos)) throw 'ssmid svfusion position not integer'\n\t\t\tconst pairlstIdx = Number(_pi)\n\t\t\tif (Number.isNaN(pairlstIdx)) throw 'ssmid pairlstIdx not integer'\n\t\t\treturn { dt, l: [dt, chr, pos, strand, pairlstIdx, mname] }\n\t\t}\n\t\t// cnv with sample\n\t\tconst [chr, _start, _stop, _class, _value, sample] = l\n\t\tconst start = Number(_start),\n\t\t\tstop = Number(_stop),\n\t\t\tvalue = _value == '' ? null : Number(_value) // if cnv not using value, must avoid `Number('')=0`\n\t\tif (Number.isNaN(start) || Number.isNaN(stop)) throw 'ssmid cnv start/stop not integer'\n\t\treturn { dt: dtcnv, l: [chr, start, stop, _class, value, sample] }\n\t}\n\tthrow 'unknown ssmid'\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,OAAO,YAAY,MAAM,mBAAmB;AAiB9C,MAAM,uBAAuB;AAO7B,SAAS,iBAAiB,MAAM;AACtC,QAAM,MAAM,oBAAI,IAAI;AACpB,QAAM,
|
|
3
|
+
"sources": ["../../src/mds3tk.ts"],
|
|
4
|
+
"sourcesContent": ["import { dtcnv, dtsnvindel, dtsv, dtfusionrna } from './common.js'\n\n// this script should contain mds3 track-related stuff shared between client and backend\n\n/*\nthe separator is used to join essential bits of a variant obj into a string as the \"ssm_id\", aims to uniquely identify a variant irrespective of sample\nthis is to mimic the GDC \"ssm_id\" which is a random id, with below benefits:\n- consistent way to pass request body for both gdc and non-gdc\n- uniform identification of ssm/cnv/sv in non-gdc backend code\n- uniform identification of all variants in client\n\nssm: chr + pos + ref + alt\ncnv: chr + start + stop + class\nsvfusion: dt + chr + pos + strand + pairlstidx + mname\n\nthe separator must avoid conflicting with characters from gene names, and can be changed based on needs\n*/\nexport const ssmIdFieldsSeparator = '__'\n\n/*\ninput: array of mixture of ssm, svfusion and cnv\n\noutput: sorted array. each element: [ class/dt, count of m ]\n*/\nexport function summarize_mclass(mlst) {\n\tconst m2c = new Map() // k: mclass, v: {}\n\tconst cnvs: any[] = []\n\tfor (const m of mlst) {\n\t\tif (m.dt == dtcnv) {\n\t\t\tcnvs.push(m)\n\t\t\tcontinue // process cnv later\n\t\t}\n\t\t// snvindel has m.class=str, svfusion has only dt=int\n\t\tconst key = m.class || m.dt\n\t\tm2c.set(key, 1 + (m2c.get(key) || 0))\n\t}\n\n\tif (cnvs.length) {\n\t\tif (Number.isFinite(cnvs[0].value)) {\n\t\t\t// first cnv uses numeric value (assumes all the same). record by dt\n\t\t\tm2c.set(dtcnv, cnvs.length)\n\t\t} else {\n\t\t\t// cnv not numeric and uses class; record by each class\n\t\t\tfor (const c of cnvs) {\n\t\t\t\tif (!c.class) continue // should not happen\n\t\t\t\tm2c.set(c.class, 1 + (m2c.get(c.class) || 0))\n\t\t\t}\n\t\t}\n\t}\n\treturn [...m2c].sort((i, j) => j[1] - i[1])\n}\n\n/*\nssmid is not specific for ssm, it covers all alterations\ngdc ssm are identified by a specific uuid, thus the design\n*/\nexport function guessSsmid(ssmid) {\n\tconst l = ssmid.split(ssmIdFieldsSeparator)\n\tif (l.length == 4) {\n\t\tconst [chr, tmp, ref, alt] = l\n\t\tconst pos = Number(tmp)\n\t\tif (Number.isNaN(pos)) throw 'ssmid snvindel pos not integer'\n\t\treturn { dt: dtsnvindel, l: [chr, pos, ref, alt] }\n\t}\n\tif (l.length == 5) {\n\t\t// cnv. if type=cat, _value is blank string\n\t\tconst [chr, _start, _stop, _class, _value] = l\n\t\tconst start = Number(_start),\n\t\t\tstop = Number(_stop),\n\t\t\tvalue = _value == '' ? null : Number(_value)\n\t\tif (Number.isNaN(start) || Number.isNaN(stop)) throw 'ssmid cnv start/stop not integer'\n\t\treturn { dt: dtcnv, l: [chr, start, stop, _class, value] }\n\t}\n\tif (l.length == 6) {\n\t\tif (l[3] == '+' || l[3] == '-') {\n\t\t\t// sv/fusion\n\t\t\tconst [_dt, chr, _pos, strand, _pi, _mname] = l\n\n\t\t\t// mname is encoded in case it contains comma (and is same as ssmIdFieldsSeparator)\n\t\t\tconst mname = decodeURIComponent(_mname)\n\t\t\tconst dt = Number(_dt)\n\t\t\tif (dt != dtsv && dt != dtfusionrna) throw 'ssmid dt not sv/fusion'\n\t\t\tconst pos = Number(_pos)\n\t\t\tif (Number.isNaN(pos)) throw 'ssmid svfusion position not integer'\n\t\t\tconst pairlstIdx = Number(_pi)\n\t\t\tif (Number.isNaN(pairlstIdx)) throw 'ssmid pairlstIdx not integer'\n\t\t\treturn { dt, l: [dt, chr, pos, strand, pairlstIdx, mname] }\n\t\t}\n\t\t// cnv with sample\n\t\tconst [chr, _start, _stop, _class, _value, sample] = l\n\t\tconst start = Number(_start),\n\t\t\tstop = Number(_stop),\n\t\t\tvalue = _value == '' ? null : Number(_value) // if cnv not using value, must avoid `Number('')=0`\n\t\tif (Number.isNaN(start) || Number.isNaN(stop)) throw 'ssmid cnv start/stop not integer'\n\t\treturn { dt: dtcnv, l: [chr, start, stop, _class, value, sample] }\n\t}\n\tthrow 'unknown ssmid'\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,OAAO,YAAY,MAAM,mBAAmB;AAiB9C,MAAM,uBAAuB;AAO7B,SAAS,iBAAiB,MAAM;AACtC,QAAM,MAAM,oBAAI,IAAI;AACpB,QAAM,OAAc,CAAC;AACrB,aAAW,KAAK,MAAM;AACrB,QAAI,EAAE,MAAM,OAAO;AAClB,WAAK,KAAK,CAAC;AACX;AAAA,IACD;AAEA,UAAM,MAAM,EAAE,SAAS,EAAE;AACzB,QAAI,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE;AAAA,EACrC;AAEA,MAAI,KAAK,QAAQ;AAChB,QAAI,OAAO,SAAS,KAAK,CAAC,EAAE,KAAK,GAAG;AAEnC,UAAI,IAAI,OAAO,KAAK,MAAM;AAAA,IAC3B,OAAO;AAEN,iBAAW,KAAK,MAAM;AACrB,YAAI,CAAC,EAAE,MAAO;AACd,YAAI,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,EAAE,KAAK,KAAK,EAAE;AAAA,MAC7C;AAAA,IACD;AAAA,EACD;AACA,SAAO,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3C;AAMO,SAAS,WAAW,OAAO;AACjC,QAAM,IAAI,MAAM,MAAM,oBAAoB;AAC1C,MAAI,EAAE,UAAU,GAAG;AAClB,UAAM,CAAC,KAAK,KAAK,KAAK,GAAG,IAAI;AAC7B,UAAM,MAAM,OAAO,GAAG;AACtB,QAAI,OAAO,MAAM,GAAG,EAAG,OAAM;AAC7B,WAAO,EAAE,IAAI,YAAY,GAAG,CAAC,KAAK,KAAK,KAAK,GAAG,EAAE;AAAA,EAClD;AACA,MAAI,EAAE,UAAU,GAAG;AAElB,UAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,MAAM,IAAI;AAC7C,UAAM,QAAQ,OAAO,MAAM,GAC1B,OAAO,OAAO,KAAK,GACnB,QAAQ,UAAU,KAAK,OAAO,OAAO,MAAM;AAC5C,QAAI,OAAO,MAAM,KAAK,KAAK,OAAO,MAAM,IAAI,EAAG,OAAM;AACrD,WAAO,EAAE,IAAI,OAAO,GAAG,CAAC,KAAK,OAAO,MAAM,QAAQ,KAAK,EAAE;AAAA,EAC1D;AACA,MAAI,EAAE,UAAU,GAAG;AAClB,QAAI,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC,KAAK,KAAK;AAE/B,YAAM,CAAC,KAAKA,MAAK,MAAM,QAAQ,KAAK,MAAM,IAAI;AAG9C,YAAM,QAAQ,mBAAmB,MAAM;AACvC,YAAM,KAAK,OAAO,GAAG;AACrB,UAAI,MAAM,QAAQ,MAAM,YAAa,OAAM;AAC3C,YAAM,MAAM,OAAO,IAAI;AACvB,UAAI,OAAO,MAAM,GAAG,EAAG,OAAM;AAC7B,YAAM,aAAa,OAAO,GAAG;AAC7B,UAAI,OAAO,MAAM,UAAU,EAAG,OAAM;AACpC,aAAO,EAAE,IAAI,GAAG,CAAC,IAAIA,MAAK,KAAK,QAAQ,YAAY,KAAK,EAAE;AAAA,IAC3D;AAEA,UAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,MAAM,IAAI;AACrD,UAAM,QAAQ,OAAO,MAAM,GAC1B,OAAO,OAAO,KAAK,GACnB,QAAQ,UAAU,KAAK,OAAO,OAAO,MAAM;AAC5C,QAAI,OAAO,MAAM,KAAK,KAAK,OAAO,MAAM,IAAI,EAAG,OAAM;AACrD,WAAO,EAAE,IAAI,OAAO,GAAG,CAAC,KAAK,OAAO,MAAM,QAAQ,OAAO,MAAM,EAAE;AAAA,EAClE;AACA,QAAM;AACP;",
|
|
6
6
|
"names": ["chr"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function roundValue(value: any, digits: any): number;
|
|
2
|
+
/** Rounds numbers to the appropriate decimal point
|
|
3
|
+
* if format is true, returns either a number or string in
|
|
4
|
+
* scientific notation.
|
|
5
|
+
*
|
|
6
|
+
* TODO: Review digit logic.
|
|
7
|
+
*/
|
|
8
|
+
export declare function roundValueAuto(value: any, format?: boolean, defaultDigits?: number): any;
|
|
9
|
+
export declare function decimalPlacesUntilFirstNonZero(number: any): number;
|
|
10
|
+
export declare function roundValue2(value: any): any;
|
|
11
|
+
/** Use to return displayed values in scientific notation
|
|
12
|
+
* Do not use for values intended for calculation later.
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatValue(value: any, digits: any): string | number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/roundValue.
|
|
3
|
+
"sources": ["../../src/roundValue.ts"],
|
|
4
4
|
"sourcesContent": ["/*\nround a value to specified digits\n - if value is integer, value is returned\n - if value is a fractional float, round to precision\n - if value is not a fractional float, round to decimal point\n - TODO: handle scientific notation\nvalue: given value\ndigits: number of digits to round to\n*/\n\nexport function roundValue(value, digits) {\n\tconst v = Number(value)\n\tif (Number.isInteger(v)) return v\n\tif (Math.abs(v) < 1) return Number(v.toPrecision(digits))\n\treturn Number(v.toFixed(digits))\n}\n\n/** Rounds numbers to the appropriate decimal point\n * if format is true, returns either a number or string in\n * scientific notation.\n *\n * TODO: Review digit logic.\n */\n\nexport function roundValueAuto(value, format = false, defaultDigits = 2) {\n\tif (!value && value != 0) return value\n\tconst dp = decimalPlacesUntilFirstNonZero(value)\n\tconst digits = Math.abs(value) > 1 ? defaultDigits : dp > 0 ? dp + 1 : defaultDigits\n\tif (format) return formatValue(value, digits)\n\treturn roundValue(value, digits)\n}\n\nexport function decimalPlacesUntilFirstNonZero(number) {\n\t// Convert number to string\n\tconst numberStr = number.toString()\n\n\t// Find the position of the decimal point\n\tconst decimalIndex = numberStr.indexOf('.')\n\n\t// If decimal point is not found or number is an integer, return 0\n\tif (decimalIndex === -1 || decimalIndex === numberStr.length - 1) {\n\t\treturn 0\n\t}\n\n\t// Iterate through characters after the decimal point\n\tlet decimalPlaces = 0\n\tfor (let i = decimalIndex + 1; i < numberStr.length; i++) {\n\t\t// Increment the count of decimal places until a non-zero digit is found\n\t\tif (numberStr[i] === '0') {\n\t\t\tdecimalPlaces++\n\t\t} else if (numberStr[i] >= '1' && numberStr[i] <= '9') {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn decimalPlaces\n}\n\n/* \nsimple logic to return a number close to original while rounding up decimals.\nsupplements roundValueAuto which rounds 12345 to 1.2e4 which is only suitable for human quick glance but not subsequent computing\n\nTODO:\n10000 and 10001 to 1e4\n0.00001 to 1e-5\n1.00001 to 1\n*/\nexport function roundValue2(value) {\n\tif (!Number.isFinite(value)) return value // not a number\n\tif (Number.isInteger(value)) return value // is integer, do not convert\n\tconst abs = Math.abs(value)\n\tif (abs > 100) return Math.round(value) // 12345.1234 to 12345 (compared to 1.2e4 from roundValueAuto)\n\tif (abs > 10) return Number(value.toFixed(1)) // 99.1234 to 99.1\n\tif (abs > 1) return Number(value.toFixed(2)) // 9.1234 to 9.12\n\tif (abs > 0.1) return Number(value.toFixed(3)) // 0.12345 to 0.123\n\tif (abs > 0.01) return Number(value.toFixed(4)) // 0.012345 to 0.0123\n\treturn value // as is\n}\n\n/** Use to return displayed values in scientific notation\n * Do not use for values intended for calculation later.\n */\nexport function formatValue(value, digits) {\n\tconst v = Number(value)\n\tif (Number.isInteger(v)) return v\n\tconst abs = Math.abs(v)\n\tif (abs < 1 || abs > 9999) {\n\t\t//Number() reverts positive values less than 10^21 to a whole number\n\t\t//To return the value in scientific notation, use toPrecision without Number()\n\t\treturn abs > 9999 ? v.toPrecision(digits) : Number(v.toPrecision(digits))\n\t}\n\treturn Number(v.toFixed(digits))\n}\n"],
|
|
5
5
|
"mappings": "AAUO,SAAS,WAAW,OAAO,QAAQ;AACzC,QAAM,IAAI,OAAO,KAAK;AACtB,MAAI,OAAO,UAAU,CAAC,EAAG,QAAO;AAChC,MAAI,KAAK,IAAI,CAAC,IAAI,EAAG,QAAO,OAAO,EAAE,YAAY,MAAM,CAAC;AACxD,SAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AAChC;AASO,SAAS,eAAe,OAAO,SAAS,OAAO,gBAAgB,GAAG;AACxE,MAAI,CAAC,SAAS,SAAS,EAAG,QAAO;AACjC,QAAM,KAAK,+BAA+B,KAAK;AAC/C,QAAM,SAAS,KAAK,IAAI,KAAK,IAAI,IAAI,gBAAgB,KAAK,IAAI,KAAK,IAAI;AACvE,MAAI,OAAQ,QAAO,YAAY,OAAO,MAAM;AAC5C,SAAO,WAAW,OAAO,MAAM;AAChC;AAEO,SAAS,+BAA+B,QAAQ;AAEtD,QAAM,YAAY,OAAO,SAAS;AAGlC,QAAM,eAAe,UAAU,QAAQ,GAAG;AAG1C,MAAI,iBAAiB,MAAM,iBAAiB,UAAU,SAAS,GAAG;AACjE,WAAO;AAAA,EACR;AAGA,MAAI,gBAAgB;AACpB,WAAS,IAAI,eAAe,GAAG,IAAI,UAAU,QAAQ,KAAK;AAEzD,QAAI,UAAU,CAAC,MAAM,KAAK;AACzB;AAAA,IACD,WAAW,UAAU,CAAC,KAAK,OAAO,UAAU,CAAC,KAAK,KAAK;AACtD;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAWO,SAAS,YAAY,OAAO;AAClC,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,OAAO,UAAU,KAAK,EAAG,QAAO;AACpC,QAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,MAAI,MAAM,IAAK,QAAO,KAAK,MAAM,KAAK;AACtC,MAAI,MAAM,GAAI,QAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC5C,MAAI,MAAM,EAAG,QAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC3C,MAAI,MAAM,IAAK,QAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC7C,MAAI,MAAM,KAAM,QAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC9C,SAAO;AACR;AAKO,SAAS,YAAY,OAAO,QAAQ;AAC1C,QAAM,IAAI,OAAO,KAAK;AACtB,MAAI,OAAO,UAAU,CAAC,EAAG,QAAO;AAChC,QAAM,MAAM,KAAK,IAAI,CAAC;AACtB,MAAI,MAAM,KAAK,MAAM,MAAM;AAG1B,WAAO,MAAM,OAAO,EAAE,YAAY,MAAM,IAAI,OAAO,EAAE,YAAY,MAAM,CAAC;AAAA,EACzE;AACA,SAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AAChC;",
|
|
6
6
|
"names": []
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export default function validate_bins(binconfig: any): void;
|
|
2
|
+
export declare function compute_bins(binconfig: any, summaryfxn: any, valueConversion: any): any;
|
|
3
|
+
export declare function get_bin_label(bin: any, binconfig: any, valueConversion?: any): any;
|
|
4
|
+
export declare function get_bin_range_equation(bin: any, binconfig: any): any;
|
|
5
|
+
export declare function target_percentiles(binconfig: any): any[];
|
package/dist/src/termdb.bins.js
CHANGED
|
@@ -242,7 +242,7 @@ function get_bin_range_equation(bin, binconfig) {
|
|
|
242
242
|
let range_eq;
|
|
243
243
|
const copy = structuredClone(bin);
|
|
244
244
|
copy.label = "";
|
|
245
|
-
const bin_label = get_bin_label(copy, binconfig);
|
|
245
|
+
const bin_label = get_bin_label(copy, binconfig, void 0);
|
|
246
246
|
if (bin.startunbounded || bin.stopunbounded) {
|
|
247
247
|
range_eq = x + " " + bin_label;
|
|
248
248
|
} else if (bin.startinclusive) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/termdb.bins.
|
|
4
|
-
"sourcesContent": ["import { format } from 'd3-format'\nimport { getColors } from './common.js'\nimport { isNumeric, isStrictNumeric, convertUnits } from './helpers.js'\n\nexport default function validate_bins(binconfig) {\n\t// Number.isFinite('1') returns false, which is desired\n\n\tconst bc = binconfig\n\tif (!bc || typeof bc !== 'object') throw 'bin schema must be an object'\n\t// assign default type\n\tif (!('type' in bc)) bc.type = 'regular-bin'\n\n\tif (bc.type == 'custom-bin') {\n\t\tif (!Array.isArray(bc.lst)) throw 'binconfig.lst must be an array'\n\t\tif (!bc.lst.length) throw 'binconfig.lst must have entries'\n\t\tconst first_bin = bc.lst[0]\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\n\t\tfor (const bin of bc.lst) {\n\t\t\tif (!('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\t\t\tthrow 'custom bin.startinclusive and/or bin.stopinclusive must be defined'\n\t\t\t}\n\n\t\t\tif (bin == first_bin) {\n\t\t\t\tif ('startunbounded' in bin && !bin.startunbounded) {\n\t\t\t\t\tthrow `a custom first bin must not set bin.startunbounded to false`\n\t\t\t\t}\n\t\t\t\tbin.startunbounded = true\n\t\t\t\tif ('start' in bin) {\n\t\t\t\t\tthrow 'a custom first bin must not set a bin.start value'\n\t\t\t\t}\n\t\t\t\tif ('start_percentile' in bin) {\n\t\t\t\t\tthrow 'the first bin must not set a bin.start_percentile value'\n\t\t\t\t}\n\t\t\t\tif (!('stop' in bin)) {\n\t\t\t\t\tthrow `a custom first bin must define a bin.stop value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.stop)) {\n\t\t\t\t\tthrow `a custom first bin.stop value should be numeric`\n\t\t\t\t}\n\t\t\t} else if (bin == last_bin) {\n\t\t\t\tif (!('start' in bin)) {\n\t\t\t\t\tthrow `a custom last bin must define a bin.start value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) {\n\t\t\t\t\tthrow `a custom last bin.start must be numeric`\n\t\t\t\t}\n\t\t\t\tif ('stopunbounded' in bin && !bin.stopunbounded) {\n\t\t\t\t\tthrow 'a custom last bin must not set bin.stopunbounded to false'\n\t\t\t\t}\n\t\t\t\tbin.stopunbounded = true\n\t\t\t\tif ('stop' in bin) {\n\t\t\t\t\tthrow 'a custom last bin must not set a bin.stop value'\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t\t\t\tthrow 'bin.startunbounded and bin.stopunbounded must not be set for non-first/non-last bins'\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) throw 'bin.start must be numeric for a non-first bin'\n\t\t\t\tif (!isStrictNumeric(bin.stop)) throw 'bin.stop must be numeric for a non-last bin'\n\t\t\t}\n\t\t}\n\t} else if (bc.type == 'regular-bin') {\n\t\t// required custom_bin parameter\n\t\tif (!Number.isFinite(bc.bin_size)) throw 'non-numeric bin_size'\n\t\tif (bc.bin_size <= 0) throw 'bin_size must be greater than 0'\n\n\t\tif (!bc.startinclusive && !bc.stopinclusive) {\n\t\t\tbc.startinclusive = 1\n\t\t\tbc.stopinclusive = 0\n\t\t}\n\n\t\tif (!bc.first_bin) throw 'first_bin{} missing'\n\t\tif (typeof bc.first_bin != 'object') throw 'first_bin{} is not an object'\n\t\tif (!Object.keys(bc.first_bin).length) throw 'first_bin is an empty object'\n\n\t\t{\n\t\t\tconst b = bc.first_bin\n\t\t\tb.startunbounded = true\n\t\t\t// requires stop_percentile, or stop\n\t\t\tif (b.stop_percentile) {\n\t\t\t\tif (!Number.isInteger(b.stop_percentile)) throw 'first_bin.stop_percentile should be integer'\n\t\t\t\tif (b.stop_percentile <= 0 || b.stop_percentile >= 100) throw 'first_bin.stop_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.stop)) {\n\t\t\t\tthrow 'first_bin.stop not a number when stop_percentile is not set'\n\t\t\t}\n\t\t}\n\n\t\tif (bc.last_bin) {\n\t\t\tconst b = bc.last_bin\n\t\t\t// requires start_percentile or start\n\t\t\tif (b.start_percentile) {\n\t\t\t\tif (!Number.isInteger(b.start_percentile)) throw 'last_bin.start_percentile should be integer'\n\t\t\t\tif (b.start_percentile <= 0 || b.start_percentile >= 100) throw 'last_bin.start_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.start)) {\n\t\t\t\tthrow 'last_bin.start not a number when start_percentile is not set'\n\t\t\t}\n\n\t\t\tb.stopunbounded = true\n\t\t\tif ('stop' in b) {\n\t\t\t\tthrow 'a regular last bin must not set a bin.stop value'\n\t\t\t}\n\t\t}\n\t} else {\n\t\tthrow `invalid binconfig.type=\"${bc.type}\"`\n\t}\n}\n\nexport function compute_bins(binconfig, summaryfxn, valueConversion) {\n\t/*\n Bins generator\n \nbinconfig \n configuration of bins per the Numerical Binning Scheme\n\nsummaryfxn (percentiles)=> return {min, max, pX, pY, ...}\n - required function\n\n - must accept an array of desired percentile values\n and returns an object of computed properties\n {\n min: minimum value\n max: maximum value\n pX: percentile at X value, so p10 will be 10th percentile value\n pY: .. corresponding to the desired percentile values \n }\n*/\n\tconst bc = binconfig\n\n\tvalidate_bins(bc)\n\tif (bc.lst) {\n\t\tconst k2c = getColors(bc.lst.length) //to color bins\n\t\tfor (const bin of bc.lst) bin.color = k2c(bin.label)\n\t}\n\tif (bc.type == 'custom-bin') return JSON.parse(JSON.stringify(bc.lst))\n\tif (typeof summaryfxn != 'function') throw 'summaryfxn required for modules/termdb.bins.js compute_bins()'\n\tconst percentiles = target_percentiles(bc)\n\tconst summary = summaryfxn(percentiles)\n\tif (!summary || typeof summary !== 'object') throw 'invalid returned value by summaryfxn'\n\tbc.results = { summary }\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\n\t// round the min and max values for use as bin start and stop\n\t// in the first and last bins, respectively\n\tconst minFloor = Math.floor(summary.min * 100) / 100\n\tconst maxCeil = Math.ceil(summary.max * 100) / 100\n\tconst min = bc.first_bin.startunbounded\n\t\t? minFloor\n\t\t: bc.first_bin.start_percentile\n\t\t? summary['p' + bc.first_bin.start_percentile]\n\t\t: bc.first_bin.start\n\tlet max = maxCeil, // in order to include the max value in the last bin\n\t\tlast_start,\n\t\tlast_stop\n\n\tif (bc.last_bin) {\n\t\tmax = bc.last_bin.stopunbounded\n\t\t\t? maxCeil // in order to include the max value in the last bin\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isNumeric(bc.last_bin.stop) && bc.last_bin.stop <= summary.max // '0.0088' < 0.0088\n\t\t\t? bc.last_bin.stop\n\t\t\t: maxCeil // in order to include the max value in the last bin\n\t\tlast_start = isStrictNumeric(bc.last_bin.start_percentile)\n\t\t\t? summary['p' + bc.last_bin.start_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.start)\n\t\t\t? bc.last_bin.start\n\t\t\t: undefined\n\t\tlast_stop = bc.last_bin.stopunbounded\n\t\t\t? null\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.stop)\n\t\t\t? bc.last_bin.stop\n\t\t\t: null\n\t} else if (bc.lst) {\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\t\tlast_start = last_bin.start\n\t\tlast_stop = 'stop' in last_bin && !last_bin.stopunbounded ? last_bin.stop : maxCeil\n\t\tmax = last_stop\n\t} else {\n\t\tlast_start = maxCeil\n\t\tlast_stop = maxCeil\n\t}\n\n\tconst numericMax = isStrictNumeric(max)\n\tconst numericLastStart = isStrictNumeric(last_start)\n\tconst numericLastStop = isStrictNumeric(last_stop)\n\n\tif (!numericMax && !numericLastStart) return [] //throw 'unable to compute the last bin start or stop'\n\n\tconst bins = []\n\tlet currBin = {\n\t\tstartunbounded: bc.first_bin.startunbounded,\n\t\tstart: bc.first_bin.startunbounded ? undefined : min,\n\t\tstop: isStrictNumeric(bc.first_bin.stop_percentile)\n\t\t\t? +summary['p' + bc.first_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.first_bin.stop)\n\t\t\t? +bc.first_bin.stop\n\t\t\t: min + bc.bin_size,\n\t\tstartinclusive: bc.startinclusive,\n\t\tstopinclusive: bc.stopinclusive\n\t}\n\n\tif (!isStrictNumeric(currBin.stop)) throw 'the computed first_bin.stop is non-numeric' + currBin.stop\n\tconst maxNumBins = 100 // harcoded limit for now to not stress sqlite\n\n\twhile ((numericMax && currBin.stop <= max) || (currBin.startunbounded && !bins.length) || currBin.stopunbounded) {\n\t\tbins.push(currBin)\n\t\t// force a computed last bin to have stopunbounded true\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stopunbounded = true\n\t\t\tif (bins.length > 1) {\n\t\t\t\tdelete currBin.stop\n\t\t\t}\n\t\t}\n\t\tcurrBin.label = get_bin_label(currBin, bc, valueConversion)\n\t\tif (currBin.stopunbounded) break\n\n\t\tconst upper = currBin.stop + bc.bin_size\n\t\tconst previousStop = currBin.stop\n\t\tcurrBin = {\n\t\t\tstartinclusive: bc.startinclusive,\n\t\t\tstopinclusive: bc.stopinclusive,\n\t\t\tstart: previousStop,\n\t\t\tstop:\n\t\t\t\tnumericLastStop && (previousStop == last_start || upper > last_stop)\n\t\t\t\t\t? last_stop\n\t\t\t\t\t: numericLastStart && upper > last_start && previousStop != last_start\n\t\t\t\t\t? last_start\n\t\t\t\t\t: upper\n\t\t}\n\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stop = max\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t\tif (bc.last_bin && bc.last_bin.stopinclusive) currBin.stopinclusive = 1\n\t\t}\n\t\tif (numericLastStart && currBin.start == last_start) {\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t}\n\t\tif (currBin.start > currBin.stop) {\n\t\t\tif (numericLastStart && currBin.stop == last_start && bc.last_bin && bc.last_bin.stopunbounded)\n\t\t\t\tcurrBin.stopunbounded = true\n\t\t\telse break\n\t\t}\n\t\tif (bins.length + 1 >= maxNumBins) {\n\t\t\tbc.error = 'max_num_bins_reached'\n\t\t\tbreak\n\t\t}\n\t}\n\tdelete bc.binLabelFormatter\n\tif (bins.length > 1) {\n\t\tdelete bins[bins.length - 1].stop\n\t}\n\tconst k2c = getColors(bins.length) //to color bins\n\tfor (const bin of bins) bin.color = k2c(bin.label)\n\treturn bins\n}\n\nfunction getNumDecimalsFormatter(bc) {\n\t//return format('rounding' in bc ? bc.rounding : '')\n\treturn 'rounding' in bc ? format(bc.rounding) : d => d // default to labeling using the start/stop value as-is\n}\n\nexport function get_bin_label(bin, binconfig, valueConversion) {\n\t/*\n Generate a numeric bin label given a bin configuration and an optional term valueConversion object\n*/\n\tif (!bin) return 'missing bin.label'\n\tif (bin.label) return bin.label\n\n\tconst bc = binconfig\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\tif (!bin.startunbounded && !bin.stopunbounded && !('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\tif (bc.startinclusive) bin.startinclusive = true\n\t\telse if (bc.stopinclusive) bin.stopinclusive = true\n\t}\n\n\tconst start = bc.use_as == 'bins' || bin.start\n\tconst stop = bc.use_as == 'bins' || bin.stop\n\n\tlet label_offset = 0\n\tif ('label_offset' in bc) {\n\t\tbc.label_offset_ignored = 'bin_size' in bc && bc.bin_size < bc.label_offset\n\t\tif (!bc.label_offset_ignored) label_offset = bc.label_offset\n\t} else if (bc.bin_size === 1 && bc.termtype == 'integer') {\n\t\tlabel_offset = 1\n\t}\n\n\t// one side-unbounded bins\n\t// label will be \">v\" or \"<v\"\n\tif (bin.startunbounded) {\n\t\tconst oper = bin.stopinclusive ? '\u2264' : '<' // \\u2264\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(stop) //bin.startinclusive && label_offset ? stop - label_offset : stop)\n\t\treturn oper + v1\n\t}\n\t// a data value may coincide with the last bin's start\n\tif (bin.stopunbounded || start === stop) {\n\t\tconst oper = bin.startinclusive /*|| label_offset*/ ? '\u2265' : '>' // \\u2265\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(start) //bin.startinclusive || start == min ? start : start + label_offset)\n\t\treturn oper + v0\n\t}\n\n\t// two-sided bins\n\tif (label_offset && bin.startinclusive && !bin.stopinclusive) {\n\t\tif (Number.isInteger(bc.bin_size) && Math.abs(start - stop) === label_offset) {\n\t\t\t// make a simpler label when the range simply spans the bin_size\n\t\t\treturn (\n\t\t\t\t'' +\n\t\t\t\t(valueConversion\n\t\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t\t: bc.binLabelFormatter(start))\n\t\t\t)\n\t\t} else {\n\t\t\tconst v0 = valueConversion\n\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t: bc.binLabelFormatter(start)\n\t\t\tconst v1 = valueConversion\n\t\t\t\t? convertUnits(\n\t\t\t\t\t\tstop - label_offset,\n\t\t\t\t\t\tvalueConversion.fromUnit,\n\t\t\t\t\t\tvalueConversion.toUnit,\n\t\t\t\t\t\tvalueConversion.scaleFactor,\n\t\t\t\t\t\ttrue\n\t\t\t\t )\n\t\t\t\t: bc.binLabelFormatter(stop - label_offset)\n\t\t\t// ensure that last two bin labels make sense (the last is stopunbounded)\n\t\t\treturn +v0 >= +v1 ? v0.toString() : v0 + ' to ' + v1\n\t\t}\n\t} else {\n\t\t// stop_inclusive || label_offset == 0\n\t\tconst oper0 = bin.startinclusive ? '' : '>'\n\t\tconst oper1 = bin.stopinclusive ? '' : '<'\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(start)\n\t\t\t? start\n\t\t\t: bc.binLabelFormatter(start)\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(stop)\n\t\t\t? stop\n\t\t\t: bc.binLabelFormatter(stop)\n\t\t// after rounding the bin labels, the bin start may equal the last bin stop as derived from actual data\n\t\tif (+v0 >= +v1) {\n\t\t\tconst oper = bin.startinclusive ? '\u2265' : '>' // \\u2265\n\t\t\treturn oper + v0\n\t\t} else {\n\t\t\treturn oper0 + v0 + ' to ' + oper1 + v1\n\t\t}\n\t}\n}\n\n// get bin range equation from bin label and bin properties\nexport function get_bin_range_equation(bin, binconfig) {\n\tconst x = '<span style=\"font-family:Times;font-style:italic;\">x</span>'\n\tlet range_eq\n\t// should always use computed (not user-customized) bin label to determine bin range text\n\tconst copy = structuredClone(bin)\n\tcopy.label = '' // mutate only the copy, and not the original bin argument\n\tconst bin_label = get_bin_label(copy, binconfig)\n\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t// first or last bins, e.g. x \u2264 14 and x > 16\n\t\trange_eq = x + ' ' + bin_label\n\t} else if (bin.startinclusive) {\n\t\t// bins with startinclusive, e.g. 14 \u2264 x < 16\n\t\trange_eq = bin_label.replace('to <', '\u2264 ' + x + ' <')\n\t} else if (bin.stopinclusive) {\n\t\t// bins with stopinclusive, e.g. 14 < x \u2264 16\n\t\trange_eq = bin_label.replace('>', '').replace('to', '< ' + x + ' \u2264')\n\t}\n\treturn range_eq\n}\n\nexport function target_percentiles(binconfig) {\n\tconst percentiles = []\n\tconst f = binconfig.first_bin\n\tif (f && isStrictNumeric(f.start_percentile)) percentiles.push(f.start_percentile)\n\tif (f && isStrictNumeric(f.stop_percentile)) percentiles.push(f.stop_percentile)\n\tconst l = binconfig.last_bin\n\tif (l && isStrictNumeric(l.start_percentile)) percentiles.push(l.start_percentile)\n\tif (l && isStrictNumeric(l.stop_percentile)) percentiles.push(l.stop_percentile)\n\treturn percentiles\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
3
|
+
"sources": ["../../src/termdb.bins.ts"],
|
|
4
|
+
"sourcesContent": ["import { format } from 'd3-format'\nimport { getColors } from './common.js'\nimport { isNumeric, isStrictNumeric, convertUnits } from './helpers.js'\n\nexport default function validate_bins(binconfig) {\n\t// Number.isFinite('1') returns false, which is desired\n\n\tconst bc = binconfig\n\tif (!bc || typeof bc !== 'object') throw 'bin schema must be an object'\n\t// assign default type\n\tif (!('type' in bc)) bc.type = 'regular-bin'\n\n\tif (bc.type == 'custom-bin') {\n\t\tif (!Array.isArray(bc.lst)) throw 'binconfig.lst must be an array'\n\t\tif (!bc.lst.length) throw 'binconfig.lst must have entries'\n\t\tconst first_bin = bc.lst[0]\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\n\t\tfor (const bin of bc.lst) {\n\t\t\tif (!('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\t\t\tthrow 'custom bin.startinclusive and/or bin.stopinclusive must be defined'\n\t\t\t}\n\n\t\t\tif (bin == first_bin) {\n\t\t\t\tif ('startunbounded' in bin && !bin.startunbounded) {\n\t\t\t\t\tthrow `a custom first bin must not set bin.startunbounded to false`\n\t\t\t\t}\n\t\t\t\tbin.startunbounded = true\n\t\t\t\tif ('start' in bin) {\n\t\t\t\t\tthrow 'a custom first bin must not set a bin.start value'\n\t\t\t\t}\n\t\t\t\tif ('start_percentile' in bin) {\n\t\t\t\t\tthrow 'the first bin must not set a bin.start_percentile value'\n\t\t\t\t}\n\t\t\t\tif (!('stop' in bin)) {\n\t\t\t\t\tthrow `a custom first bin must define a bin.stop value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.stop)) {\n\t\t\t\t\tthrow `a custom first bin.stop value should be numeric`\n\t\t\t\t}\n\t\t\t} else if (bin == last_bin) {\n\t\t\t\tif (!('start' in bin)) {\n\t\t\t\t\tthrow `a custom last bin must define a bin.start value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) {\n\t\t\t\t\tthrow `a custom last bin.start must be numeric`\n\t\t\t\t}\n\t\t\t\tif ('stopunbounded' in bin && !bin.stopunbounded) {\n\t\t\t\t\tthrow 'a custom last bin must not set bin.stopunbounded to false'\n\t\t\t\t}\n\t\t\t\tbin.stopunbounded = true\n\t\t\t\tif ('stop' in bin) {\n\t\t\t\t\tthrow 'a custom last bin must not set a bin.stop value'\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t\t\t\tthrow 'bin.startunbounded and bin.stopunbounded must not be set for non-first/non-last bins'\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) throw 'bin.start must be numeric for a non-first bin'\n\t\t\t\tif (!isStrictNumeric(bin.stop)) throw 'bin.stop must be numeric for a non-last bin'\n\t\t\t}\n\t\t}\n\t} else if (bc.type == 'regular-bin') {\n\t\t// required custom_bin parameter\n\t\tif (!Number.isFinite(bc.bin_size)) throw 'non-numeric bin_size'\n\t\tif (bc.bin_size <= 0) throw 'bin_size must be greater than 0'\n\n\t\tif (!bc.startinclusive && !bc.stopinclusive) {\n\t\t\tbc.startinclusive = 1\n\t\t\tbc.stopinclusive = 0\n\t\t}\n\n\t\tif (!bc.first_bin) throw 'first_bin{} missing'\n\t\tif (typeof bc.first_bin != 'object') throw 'first_bin{} is not an object'\n\t\tif (!Object.keys(bc.first_bin).length) throw 'first_bin is an empty object'\n\n\t\t{\n\t\t\tconst b = bc.first_bin\n\t\t\tb.startunbounded = true\n\t\t\t// requires stop_percentile, or stop\n\t\t\tif (b.stop_percentile) {\n\t\t\t\tif (!Number.isInteger(b.stop_percentile)) throw 'first_bin.stop_percentile should be integer'\n\t\t\t\tif (b.stop_percentile <= 0 || b.stop_percentile >= 100) throw 'first_bin.stop_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.stop)) {\n\t\t\t\tthrow 'first_bin.stop not a number when stop_percentile is not set'\n\t\t\t}\n\t\t}\n\n\t\tif (bc.last_bin) {\n\t\t\tconst b = bc.last_bin\n\t\t\t// requires start_percentile or start\n\t\t\tif (b.start_percentile) {\n\t\t\t\tif (!Number.isInteger(b.start_percentile)) throw 'last_bin.start_percentile should be integer'\n\t\t\t\tif (b.start_percentile <= 0 || b.start_percentile >= 100) throw 'last_bin.start_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.start)) {\n\t\t\t\tthrow 'last_bin.start not a number when start_percentile is not set'\n\t\t\t}\n\n\t\t\tb.stopunbounded = true\n\t\t\tif ('stop' in b) {\n\t\t\t\tthrow 'a regular last bin must not set a bin.stop value'\n\t\t\t}\n\t\t}\n\t} else {\n\t\tthrow `invalid binconfig.type=\"${bc.type}\"`\n\t}\n}\n\nexport function compute_bins(binconfig, summaryfxn, valueConversion) {\n\t/*\n\t Bins generator\n\t \n\tbinconfig \n\t configuration of bins per the Numerical Binning Scheme\n\n\tsummaryfxn (percentiles)=> return {min, max, pX, pY, ...}\n\t - required function\n\n\t - must accept an array of desired percentile values\n\t and returns an object of computed properties\n\t {\n\t min: minimum value\n\t max: maximum value\n\t pX: percentile at X value, so p10 will be 10th percentile value\n\t pY: .. corresponding to the desired percentile values \n\t }\n\t*/\n\tconst bc = binconfig\n\n\tvalidate_bins(bc)\n\tif (bc.lst) {\n\t\tconst k2c = getColors(bc.lst.length) //to color bins\n\t\tfor (const bin of bc.lst) bin.color = k2c(bin.label)\n\t}\n\tif (bc.type == 'custom-bin') return JSON.parse(JSON.stringify(bc.lst))\n\tif (typeof summaryfxn != 'function') throw 'summaryfxn required for modules/termdb.bins.js compute_bins()'\n\tconst percentiles = target_percentiles(bc)\n\tconst summary = summaryfxn(percentiles)\n\tif (!summary || typeof summary !== 'object') throw 'invalid returned value by summaryfxn'\n\tbc.results = { summary }\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\n\t// round the min and max values for use as bin start and stop\n\t// in the first and last bins, respectively\n\tconst minFloor = Math.floor(summary.min * 100) / 100\n\tconst maxCeil = Math.ceil(summary.max * 100) / 100\n\tconst min = bc.first_bin.startunbounded\n\t\t? minFloor\n\t\t: bc.first_bin.start_percentile\n\t\t? summary['p' + bc.first_bin.start_percentile]\n\t\t: bc.first_bin.start\n\tlet max = maxCeil, // in order to include the max value in the last bin\n\t\tlast_start,\n\t\tlast_stop\n\n\tif (bc.last_bin) {\n\t\tmax = bc.last_bin.stopunbounded\n\t\t\t? maxCeil // in order to include the max value in the last bin\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isNumeric(bc.last_bin.stop) && bc.last_bin.stop <= summary.max // '0.0088' < 0.0088\n\t\t\t? bc.last_bin.stop\n\t\t\t: maxCeil // in order to include the max value in the last bin\n\t\tlast_start = isStrictNumeric(bc.last_bin.start_percentile)\n\t\t\t? summary['p' + bc.last_bin.start_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.start)\n\t\t\t? bc.last_bin.start\n\t\t\t: undefined\n\t\tlast_stop = bc.last_bin.stopunbounded\n\t\t\t? null\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.stop)\n\t\t\t? bc.last_bin.stop\n\t\t\t: null\n\t} else if (bc.lst) {\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\t\tlast_start = last_bin.start\n\t\tlast_stop = 'stop' in last_bin && !last_bin.stopunbounded ? last_bin.stop : maxCeil\n\t\tmax = last_stop\n\t} else {\n\t\tlast_start = maxCeil\n\t\tlast_stop = maxCeil\n\t}\n\n\tconst numericMax = isStrictNumeric(max)\n\tconst numericLastStart = isStrictNumeric(last_start)\n\tconst numericLastStop = isStrictNumeric(last_stop)\n\n\tif (!numericMax && !numericLastStart) return [] //throw 'unable to compute the last bin start or stop'\n\n\tconst bins: any[] = []\n\tlet currBin: Record<string, any> = {\n\t\tstartunbounded: bc.first_bin.startunbounded,\n\t\tstart: bc.first_bin.startunbounded ? undefined : min,\n\t\tstop: isStrictNumeric(bc.first_bin.stop_percentile)\n\t\t\t? +summary['p' + bc.first_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.first_bin.stop)\n\t\t\t? +bc.first_bin.stop\n\t\t\t: min + bc.bin_size,\n\t\tstartinclusive: bc.startinclusive,\n\t\tstopinclusive: bc.stopinclusive\n\t}\n\n\tif (!isStrictNumeric(currBin.stop)) throw 'the computed first_bin.stop is non-numeric' + currBin.stop\n\tconst maxNumBins = 100 // harcoded limit for now to not stress sqlite\n\n\twhile ((numericMax && currBin.stop <= max) || (currBin.startunbounded && !bins.length) || currBin.stopunbounded) {\n\t\tbins.push(currBin)\n\t\t// force a computed last bin to have stopunbounded true\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stopunbounded = true\n\t\t\tif (bins.length > 1) {\n\t\t\t\tdelete currBin.stop\n\t\t\t}\n\t\t}\n\t\tcurrBin.label = get_bin_label(currBin, bc, valueConversion)\n\t\tif (currBin.stopunbounded) break\n\n\t\tconst upper = currBin.stop + bc.bin_size\n\t\tconst previousStop = currBin.stop\n\t\tcurrBin = {\n\t\t\tstartinclusive: bc.startinclusive,\n\t\t\tstopinclusive: bc.stopinclusive,\n\t\t\tstart: previousStop,\n\t\t\tstop:\n\t\t\t\tnumericLastStop && (previousStop == last_start || upper > last_stop)\n\t\t\t\t\t? last_stop\n\t\t\t\t\t: numericLastStart && upper > last_start && previousStop != last_start\n\t\t\t\t\t? last_start\n\t\t\t\t\t: upper\n\t\t}\n\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stop = max\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t\tif (bc.last_bin && bc.last_bin.stopinclusive) currBin.stopinclusive = 1\n\t\t}\n\t\tif (numericLastStart && currBin.start == last_start) {\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t}\n\t\tif (currBin.start > currBin.stop) {\n\t\t\tif (numericLastStart && currBin.stop == last_start && bc.last_bin && bc.last_bin.stopunbounded)\n\t\t\t\tcurrBin.stopunbounded = true\n\t\t\telse break\n\t\t}\n\t\tif (bins.length + 1 >= maxNumBins) {\n\t\t\tbc.error = 'max_num_bins_reached'\n\t\t\tbreak\n\t\t}\n\t}\n\tdelete bc.binLabelFormatter\n\tif (bins.length > 1) {\n\t\tdelete bins[bins.length - 1].stop\n\t}\n\tconst k2c = getColors(bins.length) //to color bins\n\tfor (const bin of bins) bin.color = k2c(bin.label)\n\treturn bins\n}\n\nfunction getNumDecimalsFormatter(bc) {\n\t//return format('rounding' in bc ? bc.rounding : '')\n\treturn 'rounding' in bc ? format(bc.rounding) : d => d // default to labeling using the start/stop value as-is\n}\n\nexport function get_bin_label(bin, binconfig, valueConversion?: any) {\n\t/*\n\t Generate a numeric bin label given a bin configuration and an optional term valueConversion object\n\t*/\n\tif (!bin) return 'missing bin.label'\n\tif (bin.label) return bin.label\n\n\tconst bc = binconfig\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\tif (!bin.startunbounded && !bin.stopunbounded && !('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\tif (bc.startinclusive) bin.startinclusive = true\n\t\telse if (bc.stopinclusive) bin.stopinclusive = true\n\t}\n\n\tconst start = bc.use_as == 'bins' || bin.start\n\tconst stop = bc.use_as == 'bins' || bin.stop\n\n\tlet label_offset = 0\n\tif ('label_offset' in bc) {\n\t\tbc.label_offset_ignored = 'bin_size' in bc && bc.bin_size < bc.label_offset\n\t\tif (!bc.label_offset_ignored) label_offset = bc.label_offset\n\t} else if (bc.bin_size === 1 && bc.termtype == 'integer') {\n\t\tlabel_offset = 1\n\t}\n\n\t// one side-unbounded bins\n\t// label will be \">v\" or \"<v\"\n\tif (bin.startunbounded) {\n\t\tconst oper = bin.stopinclusive ? '\u2264' : '<' // \\u2264\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(stop) //bin.startinclusive && label_offset ? stop - label_offset : stop)\n\t\treturn oper + v1\n\t}\n\t// a data value may coincide with the last bin's start\n\tif (bin.stopunbounded || start === stop) {\n\t\tconst oper = bin.startinclusive /*|| label_offset*/ ? '\u2265' : '>' // \\u2265\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(start) //bin.startinclusive || start == min ? start : start + label_offset)\n\t\treturn oper + v0\n\t}\n\n\t// two-sided bins\n\tif (label_offset && bin.startinclusive && !bin.stopinclusive) {\n\t\tif (Number.isInteger(bc.bin_size) && Math.abs(start - stop) === label_offset) {\n\t\t\t// make a simpler label when the range simply spans the bin_size\n\t\t\treturn (\n\t\t\t\t'' +\n\t\t\t\t(valueConversion\n\t\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t\t: bc.binLabelFormatter(start))\n\t\t\t)\n\t\t} else {\n\t\t\tconst v0 = valueConversion\n\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t: bc.binLabelFormatter(start)\n\t\t\tconst v1 = valueConversion\n\t\t\t\t? convertUnits(\n\t\t\t\t\t\tstop - label_offset,\n\t\t\t\t\t\tvalueConversion.fromUnit,\n\t\t\t\t\t\tvalueConversion.toUnit,\n\t\t\t\t\t\tvalueConversion.scaleFactor,\n\t\t\t\t\t\ttrue\n\t\t\t\t )\n\t\t\t\t: bc.binLabelFormatter(stop - label_offset)\n\t\t\t// ensure that last two bin labels make sense (the last is stopunbounded)\n\t\t\treturn +v0 >= +v1 ? v0.toString() : v0 + ' to ' + v1\n\t\t}\n\t} else {\n\t\t// stop_inclusive || label_offset == 0\n\t\tconst oper0 = bin.startinclusive ? '' : '>'\n\t\tconst oper1 = bin.stopinclusive ? '' : '<'\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(start)\n\t\t\t? start\n\t\t\t: bc.binLabelFormatter(start)\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(stop)\n\t\t\t? stop\n\t\t\t: bc.binLabelFormatter(stop)\n\t\t// after rounding the bin labels, the bin start may equal the last bin stop as derived from actual data\n\t\tif (+v0 >= +v1) {\n\t\t\tconst oper = bin.startinclusive ? '\u2265' : '>' // \\u2265\n\t\t\treturn oper + v0\n\t\t} else {\n\t\t\treturn oper0 + v0 + ' to ' + oper1 + v1\n\t\t}\n\t}\n}\n\n// get bin range equation from bin label and bin properties\nexport function get_bin_range_equation(bin, binconfig) {\n\tconst x = '<span style=\"font-family:Times;font-style:italic;\">x</span>'\n\tlet range_eq\n\t// should always use computed (not user-customized) bin label to determine bin range text\n\tconst copy = structuredClone(bin)\n\tcopy.label = '' // mutate only the copy, and not the original bin argument\n\tconst bin_label = get_bin_label(copy, binconfig, undefined)\n\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t// first or last bins, e.g. x \u2264 14 and x > 16\n\t\trange_eq = x + ' ' + bin_label\n\t} else if (bin.startinclusive) {\n\t\t// bins with startinclusive, e.g. 14 \u2264 x < 16\n\t\trange_eq = bin_label.replace('to <', '\u2264 ' + x + ' <')\n\t} else if (bin.stopinclusive) {\n\t\t// bins with stopinclusive, e.g. 14 < x \u2264 16\n\t\trange_eq = bin_label.replace('>', '').replace('to', '< ' + x + ' \u2264')\n\t}\n\treturn range_eq\n}\n\nexport function target_percentiles(binconfig) {\n\tconst percentiles: any[] = []\n\tconst f = binconfig.first_bin\n\tif (f && isStrictNumeric(f.start_percentile)) percentiles.push(f.start_percentile)\n\tif (f && isStrictNumeric(f.stop_percentile)) percentiles.push(f.stop_percentile)\n\tconst l = binconfig.last_bin\n\tif (l && isStrictNumeric(l.start_percentile)) percentiles.push(l.start_percentile)\n\tif (l && isStrictNumeric(l.stop_percentile)) percentiles.push(l.stop_percentile)\n\treturn percentiles\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["k2c"]
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function initBinConfig(data: any, opts?: {}): any;
|
|
@@ -25,7 +25,7 @@ function initBinConfig(data, opts = {}) {
|
|
|
25
25
|
const p98 = data[p98idx];
|
|
26
26
|
const binSize = p98 != p5 ? (p98 - p5) / 8 : (max - min) / 8;
|
|
27
27
|
const firstBinStop = Math.max(min + binSize, p5);
|
|
28
|
-
|
|
28
|
+
const [binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding] = roundBinVals(binSize, firstBinStop, max, min);
|
|
29
29
|
binConfig = {
|
|
30
30
|
type: "regular-bin",
|
|
31
31
|
startinclusive: true,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/termdb.initbinconfig.
|
|
4
|
-
"sourcesContent": ["/*\nInitialize a bin configuration for a numeric dataset\n<data>: array of numeric data values\n<opts> (optional): object of options\n {}: output bin config as JavaScript object (default)\n {format: 'string'}: output bin config as JSON string\n*/\nexport default function initBinConfig(data, opts = {}) {\n\tif (!data.length)\n\t\treturn { mode: 'discrete', type: 'regular-bin', startinclusive: true, bin_size: null, first_bin: { stop: null } }\n\tif (data.find(d => !Number.isFinite(d))) throw new Error('non-numeric values found')\n\n\tlet binConfig\n\tconst s = new Set(data)\n\tif (s.size === 1) {\n\t\t// single unique value in data array\n\t\t// prepare custom bin config for 3 bins: first bin\n\t\t// for values less than the value, second bin for values\n\t\t// equal to the value, and third bin one for values\n\t\t// greater than the value\n\t\t// all data values will fall into the second bin\n\t\tconst value = [...s][0]\n\t\tbinConfig = {\n\t\t\ttype: 'custom-bin',\n\t\t\tlst: [\n\t\t\t\t{ stop: value, stopinclusive: false, startunbounded: true, label: '<' + value },\n\t\t\t\t{ start: value, stop: value, startinclusive: true, stopinclusive: true, label: '=' + value },\n\t\t\t\t{ start: value, startinclusive: false, stopunbounded: true, label: '>' + value }\n\t\t\t]\n\t\t}\n\t} else {\n\t\t// multiple unique values in data array\n\t\t// prepare regular bin config\n\n\t\t// compute the bin size for a maximum bin number of 8\n\t\tdata.sort((a, b) => a - b)\n\t\tconst l = data.length\n\t\tconst min = data[0]\n\t\tconst max = data[l - 1]\n\t\tconst p5idx = Math.ceil(l * 0.05) - 1\n\t\tconst p98idx = Math.ceil(l * 0.98) - 1\n\t\tconst p5 = data[p5idx]\n\t\tconst p98 = data[p98idx]\n\t\t// use 98th and 5th percentiles to compute bin size to reduce outlier influence\n\t\t// if 98th = 5th, use max and min instead\n\t\tconst binSize = p98 != p5 ? (p98 - p5) / 8 : (max - min) / 8\n\t\t// first bin stop will equal either (minimum + bin size) or (5th percentile), whichever is larger.\n\t\tconst firstBinStop = Math.max(min + binSize, p5)\n\t\t// round the bin values\n\t\
|
|
5
|
-
"mappings": "AAOe,SAAR,cAA+B,MAAM,OAAO,CAAC,GAAG;AACtD,MAAI,CAAC,KAAK;AACT,WAAO,EAAE,MAAM,YAAY,MAAM,eAAe,gBAAgB,MAAM,UAAU,MAAM,WAAW,EAAE,MAAM,KAAK,EAAE;AACjH,MAAI,KAAK,KAAK,OAAK,CAAC,OAAO,SAAS,CAAC,CAAC,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAEnF,MAAI;AACJ,QAAM,IAAI,IAAI,IAAI,IAAI;AACtB,MAAI,EAAE,SAAS,GAAG;AAOjB,UAAM,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AACtB,gBAAY;AAAA,MACX,MAAM;AAAA,MACN,KAAK;AAAA,QACJ,EAAE,MAAM,OAAO,eAAe,OAAO,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAAA,QAC9E,EAAE,OAAO,OAAO,MAAM,OAAO,gBAAgB,MAAM,eAAe,MAAM,OAAO,MAAM,MAAM;AAAA,QAC3F,EAAE,OAAO,OAAO,gBAAgB,OAAO,eAAe,MAAM,OAAO,MAAM,MAAM;AAAA,MAChF;AAAA,IACD;AAAA,EACD,OAAO;AAKN,SAAK,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACzB,UAAM,IAAI,KAAK;AACf,UAAM,MAAM,KAAK,CAAC;AAClB,UAAM,MAAM,KAAK,IAAI,CAAC;AACtB,UAAM,QAAQ,KAAK,KAAK,IAAI,IAAI,IAAI;AACpC,UAAM,SAAS,KAAK,KAAK,IAAI,IAAI,IAAI;AACrC,UAAM,KAAK,KAAK,KAAK;AACrB,UAAM,MAAM,KAAK,MAAM;AAGvB,UAAM,UAAU,OAAO,MAAM,MAAM,MAAM,KAAK,MAAM,OAAO;AAE3D,UAAM,eAAe,KAAK,IAAI,MAAM,SAAS,EAAE;AAE/C,
|
|
3
|
+
"sources": ["../../src/termdb.initbinconfig.ts"],
|
|
4
|
+
"sourcesContent": ["/*\nInitialize a bin configuration for a numeric dataset\n<data>: array of numeric data values\n<opts> (optional): object of options\n {}: output bin config as JavaScript object (default)\n {format: 'string'}: output bin config as JSON string\n*/\nexport default function initBinConfig(data, opts = {}) {\n\tif (!data.length)\n\t\treturn { mode: 'discrete', type: 'regular-bin', startinclusive: true, bin_size: null, first_bin: { stop: null } }\n\tif (data.find(d => !Number.isFinite(d))) throw new Error('non-numeric values found')\n\n\tlet binConfig\n\tconst s = new Set(data)\n\tif (s.size === 1) {\n\t\t// single unique value in data array\n\t\t// prepare custom bin config for 3 bins: first bin\n\t\t// for values less than the value, second bin for values\n\t\t// equal to the value, and third bin one for values\n\t\t// greater than the value\n\t\t// all data values will fall into the second bin\n\t\tconst value = [...s][0]\n\t\tbinConfig = {\n\t\t\ttype: 'custom-bin',\n\t\t\tlst: [\n\t\t\t\t{ stop: value, stopinclusive: false, startunbounded: true, label: '<' + value },\n\t\t\t\t{ start: value, stop: value, startinclusive: true, stopinclusive: true, label: '=' + value },\n\t\t\t\t{ start: value, startinclusive: false, stopunbounded: true, label: '>' + value }\n\t\t\t]\n\t\t}\n\t} else {\n\t\t// multiple unique values in data array\n\t\t// prepare regular bin config\n\n\t\t// compute the bin size for a maximum bin number of 8\n\t\tdata.sort((a, b) => a - b)\n\t\tconst l = data.length\n\t\tconst min = data[0]\n\t\tconst max = data[l - 1]\n\t\tconst p5idx = Math.ceil(l * 0.05) - 1\n\t\tconst p98idx = Math.ceil(l * 0.98) - 1\n\t\tconst p5 = data[p5idx]\n\t\tconst p98 = data[p98idx]\n\t\t// use 98th and 5th percentiles to compute bin size to reduce outlier influence\n\t\t// if 98th = 5th, use max and min instead\n\t\tconst binSize = p98 != p5 ? (p98 - p5) / 8 : (max - min) / 8\n\t\t// first bin stop will equal either (minimum + bin size) or (5th percentile), whichever is larger.\n\t\tconst firstBinStop = Math.max(min + binSize, p5)\n\t\t// round the bin values\n\t\tconst [binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding] = roundBinVals(binSize, firstBinStop, max, min)\n\t\t// generate the bin configuration\n\t\tbinConfig = {\n\t\t\ttype: 'regular-bin',\n\t\t\tstartinclusive: true,\n\t\t\tbin_size: binSize_rnd,\n\t\t\tfirst_bin: { stop: firstBinStop_rnd }\n\t\t}\n\t\tif (lastBinStart_rnd) binConfig.last_bin = { start: lastBinStart_rnd }\n\t\tif (rounding) binConfig.rounding = rounding\n\t}\n\tif ('format' in opts) {\n\t\tif (opts.format === 'string') {\n\t\t\treturn JSON.stringify(binConfig)\n\t\t} else {\n\t\t\tthrow 'options are not in the correct format'\n\t\t}\n\t} else {\n\t\treturn binConfig\n\t}\n}\n\nfunction roundBinVals(binSize, firstBinStop, max, min) {\n\tlet binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding\n\tconst log = Math.floor(Math.log10(binSize))\n\tif (binSize >= 0.1 && binSize <= 2) {\n\t\t// Round to the nearest one for small bin sizes\n\t\tbinSize_rnd = Math.round(binSize / (1 * 10 ** log)) * (1 * 10 ** log)\n\t\tfirstBinStop_rnd = Math.round(firstBinStop / (1 * 10 ** log)) * (1 * 10 ** log)\n\t} else {\n\t\t// Round to the nearest five for large bin sizes\n\t\tbinSize_rnd = Math.round(binSize / (5 * 10 ** log)) * (5 * 10 ** log)\n\t\tfirstBinStop_rnd = Math.round(firstBinStop / (5 * 10 ** log)) * (5 * 10 ** log)\n\t\tif (binSize_rnd === 0) binSize_rnd = 1 * 10 ** log\n\t\tif (firstBinStop_rnd === 0) firstBinStop_rnd = 1 * 10 ** log\n\t\tif (binSize_rnd === 5 * 10 ** log && firstBinStop_rnd === 1 * 10 ** log) firstBinStop_rnd = 5 * 10 ** log\n\t}\n\tif (firstBinStop_rnd < min) firstBinStop_rnd = firstBinStop_rnd * 2\n\t// if the number of bins is above 8 after rounding, then set the last bin start to restrict the number of bins to 8\n\tconst eighthBinStop_rnd = firstBinStop_rnd + binSize_rnd * 7\n\tif (max > eighthBinStop_rnd) {\n\t\tlastBinStart_rnd = firstBinStop_rnd + binSize_rnd * 6\n\t}\n\tif (binSize < 1) {\n\t\tconst digits = Math.abs(log)\n\t\tbinSize_rnd = Number(binSize_rnd.toFixed(digits))\n\t\tfirstBinStop_rnd = Number(firstBinStop_rnd.toFixed(digits))\n\t\tif (lastBinStart_rnd) lastBinStart_rnd = Number(lastBinStart_rnd.toFixed(digits))\n\t\trounding = '.' + digits + 'f'\n\t}\n\tif (Object.is(firstBinStop_rnd, -0)) firstBinStop_rnd = 0\n\treturn [binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding]\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/termdb.usecase.
|
|
4
|
-
"sourcesContent": ["import {\n\tTermTypes,\n\tisNumericTerm,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tISOFORM_EXPRESSION\n} from './terms.js'\n\nexport const graphableTypes = new Set([\n\t'categorical',\n\t'integer',\n\t'float',\n\t'condition',\n\t'survival',\n\t'snplst',\n\t'snplocus',\n\t'geneVariant',\n\t'samplelst',\n\t'geneExpression',\n\tISOFORM_EXPRESSION,\n\t'dtcnv',\n\t'dtsnvindel',\n\t'dtfusion',\n\t'dtsv',\n\t'date',\n\tTermTypes.SSGSEA,\n\tTermTypes.DNA_METHYLATION,\n\tTermTypes.METABOLITE_INTENSITY,\n\tTermTypes.PROTEOME_ABUNDANCE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tSINGLECELL_CELLTYPE,\n\tTermTypes.SNP,\n\tTermTypes.TERM_COLLECTION\n])\n\n/*\nisUsableTerm() will\n\n\t- centralize the \"allowed term\" logic\n\twhich can be intricate or dataset-specific \n\tfor certain terms or contexts\n\t- make it easy to handle new term types\n\nArguments:\n\n\tterm {}\n\t\t.type: 'categorical', etc.\n\t\t.child_types: []\n\t\n\t_usecase {}\n\t\t.target (REQUIRED): 'barchart', 'regression', etc\n\t\t\t- used as a switch-case \"router\" for additional use-specific logic\n\t\t\t- other parameters, if applicable, are described in the route \"handler\" \n\t\t.detail \n\t\t - a more specific detailed use case\n\t\n\n\ttermdbConfig\n\t\toptional. provides ds overrides on default rules via excludedTermtypeByTarget. for use on client\n\n\tds\n\t\toptional. provides ds overrides when the function runs on backend\n\t\tserver-side dataset object that can supply overrides (in the form of functions) to the use case logic,\n\t\tfor example, to apply role-based allowed term uses or performance-related restrictions\n\t\tto ancestor terms when a use case aggregates too many data points for a given chart type\n\nReturns\n\n\ta Set{} with zero or more of the following strings:\n\t- 'plot' if the term can be used in a plot chartType\n\t- 'branch' if the term can be used only as an expandable tree branch, but not in a plot\n\t- an empty Set means that the term has no valid uses, i.e, it cannot be used either for plotting or as a tree branch\n*/\nexport function isUsableTerm(term, _usecase, termdbConfig, ds) {\n\tconst usecase = _usecase || {}\n\n\t// may apply dataset specific override filter for a use case\n\tif (typeof ds?.usecase?.[usecase.target] == 'function') {\n\t\treturn ds.usecase[usecase.target](term, usecase)\n\t}\n\n\t// if (term.isprivate && !user.roleCanUse(term)) return false\n\n\tconst uses = new Set()\n\t// note: expects term.child_types to be null if term.isleaf == true\n\tconst child_types = term.child_types || []\n\t// default handling\n\tswitch (usecase.target) {\n\t\tcase 'barchart':\n\t\tcase 'violin':\n\t\tcase 'boxplot':\n\t\tcase 'summary':\n\t\t\tif (term.type && term.type !== 'survival') uses.add('plot')\n\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'summaryInput':\n\t\t\tif (usecase.detail === 'term2' || usecase.detail == 'term0') {\n\t\t\t\tif (term.type && term.type !== 'survival') uses.add('plot')\n\t\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t} else {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\n\t\tcase 'matrix':\n\t\t\tif (term.type) uses.add('plot')\n\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'table':\n\t\t\tif (usecase.detail == 'term') uses.add('plot')\n\t\t\tif (child_types.length > 1) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'sampleScatter':\n\t\t\tif (usecase.detail == 'numeric') {\n\t\t\t\tif (isNumericTerm(term)) {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t}\n\t\t\t// Commenting out for now. May need later for another single\n\t\t\t// cell term. Revisit logic at that time.\n\t\t\t// else if (usecase?.specialCase?.type == 'singleCell') {\n\t\t\t// \t\tif (term.type && term.type.startsWith('singleCell')) {\n\t\t\t// \t\t\tif (term.plot && term.plot == usecase.specialCase?.config.name) {\n\t\t\t// \t\t\t\tuses.add('plot')\n\t\t\t// \t\t\t}\n\t\t\t// \t\t}\n\t\t\t// }\n\t\t\telse {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\t\tcase 'runChart2':\n\t\t\tif (usecase.detail == 'date' || usecase.detail == 'xtw') {\n\t\t\t\tif (term.type == 'date') {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (child_types.includes('date')) uses.add('branch')\n\t\t\t} else if (usecase.detail == 'numeric') {\n\t\t\t\tif (isNumericTerm(term) && term.type != 'date') {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t} else {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'numericDictTermCluster':\n\t\t\tif (!usecase.detail?.exclude?.includes(term.id)) {\n\t\t\t\tif (isNumericTerm(term)) {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) {\n\t\t\t\t\tuses.add('branch')\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'termCollections':\n\t\t\tif (usecase.detail?.termIds?.includes(term.id)) uses.add('plot')\n\t\t\tif (usecase.detail?.branchIds?.includes(term.id)) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'profileForms':\n\t\t\tif (!term.isleaf) {\n\t\t\t\tconst ancestors = term.id.split('__').length //depends on using the __ naming convension!\n\t\t\t\tif (ancestors == 3) {\n\t\t\t\t\t// 3rd level term is a domain, we show the templates associated to this domain\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t} else if (ancestors < 3) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'profileForms2': {\n\t\t\t// Picker (forms2.ts makeChartBtnMenu) sets usecase.cohort + usecase.subtype.\n\t\t\t// Reads the per-cohort domains list directly from the dataset config at\n\t\t\t// plotConfigByCohort[cohort].profileForms2.domains \u2014 each entry is { id, plotTypes }.\n\t\t\tif (term.isleaf) return uses\n\t\t\tconst allowed = termdbConfig?.plotConfigByCohort?.[usecase.cohort]?.profileForms2?.domains\n\t\t\tif (!allowed) return uses\n\t\t\tconst ancestors = term.id.split('__').length\n\t\t\tconst subtype = usecase.subtype\n\t\t\tif (ancestors == 3) {\n\t\t\t\tif (allowed.find(d => d.id === term.id)?.plotTypes?.includes(subtype)) uses.add('plot')\n\t\t\t} else if (ancestors < 3) {\n\t\t\t\tconst prefix = term.id + '__'\n\t\t\t\tif (allowed.some(d => d.id.startsWith(prefix) && d.plotTypes.includes(subtype))) {\n\t\t\t\t\tuses.add('branch')\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn uses\n\t\t}\n\n\t\t// case 'boxplot':\n\t\t// \tif (term.type == 'float' || term.type == 'integer') uses.add('plot')\n\t\t// \tif (usecase.detail === 'term2' && hasNumericChild(child_types)) uses.add('branch')\n\t\t// \treturn uses\n\n\t\tcase 'cuminc':\n\t\t\tif (usecase.detail == 'term') {\n\t\t\t\tif (term.type == 'condition') uses.add('plot')\n\t\t\t\tif (child_types.includes('condition')) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\tif (usecase.detail === 'term2' || usecase.detail == 'term0') {\n\t\t\t\tif (term.type && term.type != 'condition' && term.type != 'survival') uses.add('plot')\n\t\t\t\tif (hasAllowedChildTypes(child_types, ['condition', 'survival'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'survival':\n\t\t\tif (usecase.detail == 'term') {\n\t\t\t\tif (term.type == 'survival') uses.add('plot')\n\t\t\t\tif (child_types.includes('survival')) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\tif (usecase.detail === 'term2' || usecase.detail == 'term0') {\n\t\t\t\tif (term.type && term.type != 'survival') uses.add('plot')\n\t\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'regression':\n\t\t\tif (usecase.detail == 'outcome') {\n\t\t\t\tif (usecase.regressionType == 'linear') {\n\t\t\t\t\tif (term.type == 'float' || term.type == 'integer') uses.add('plot')\n\t\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t\t\treturn uses\n\t\t\t\t}\n\t\t\t\tif (usecase.regressionType == 'logistic') {\n\t\t\t\t\tif (term.type && term.type != 'survival') uses.add('plot')\n\t\t\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\t\t\treturn uses\n\t\t\t\t} else if (usecase.regressionType == 'cox') {\n\t\t\t\t\tif (term.type == 'condition' || term.type == 'survival') uses.add('plot')\n\t\t\t\t\tif (child_types.includes('condition') || child_types.includes('survival')) uses.add('branch')\n\t\t\t\t\treturn uses\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (usecase.detail == 'independent') {\n\t\t\t\tif (term.type == 'float' || term.type == 'integer' || term.type == 'categorical' || term.type == 'samplelst')\n\t\t\t\t\tuses.add('plot')\n\t\t\t\tif (hasChildTypes(child_types, ['categorical', 'float', 'integer'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'filter': {\n\t\t\t// apply \"exlst\" to other targets as needed\n\t\t\tconst exlst = termdbConfig?.excludedTermtypeByTarget?.filter\n\t\t\tif (exlst) {\n\t\t\t\tif (graphableTypes.has(term.type) && !exlst.includes(term.type)) uses.add('plot')\n\t\t\t\tif (child_types.find(t => !exlst.includes(t))) uses.add('branch') // there's a non-excluded child type, allow branch to show\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\t// no specific rule for filter. use default rules\n\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\treturn uses\n\t\t}\n\n\t\tcase 'correlationVolcano':\n\t\t\tif (usecase.detail == 'numeric') {\n\t\t\t\tif (isNumericTerm(term)) {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t} else {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'proteinView':\n\t\t\tif (term.type == TermTypes.PROTEOME_ABUNDANCE) uses.add('plot')\n\t\t\tif (child_types.includes(TermTypes.PROTEOME_ABUNDANCE)) uses.add('branch')\n\t\t\treturn uses\n\n\t\tdefault:\n\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\treturn uses\n\t}\n}\n\n// determine if the term has at least one child type that\n// is not excluded\nfunction hasAllowedChildTypes(child_types, excluded_types) {\n\tif (!child_types.length) {\n\t\t// term does not have children\n\t\treturn false\n\t}\n\tif (!excluded_types?.length) {\n\t\t// no excluded types\n\t\treturn true\n\t}\n\tif (child_types.some(type => !excluded_types.includes(type))) {\n\t\t// at least one child type is not excluded\n\t\treturn true\n\t}\n}\n\nfunction hasNumericChild(child_types) {\n\treturn child_types.includes('float') || child_types.includes('integer')\n}\n\nfunction hasChildTypes(child_types, expected_types) {\n\tfor (const a of expected_types) {\n\t\tif (child_types.includes(a)) return true\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEA,MAAM,iBAAiB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AACX,CAAC;AAwCM,SAAS,aAAa,MAAM,UAAU,
|
|
3
|
+
"sources": ["../../src/termdb.usecase.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n\tTermTypes,\n\tisNumericTerm,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tISOFORM_EXPRESSION\n} from './terms.js'\n\nexport const graphableTypes = new Set([\n\t'categorical',\n\t'integer',\n\t'float',\n\t'condition',\n\t'survival',\n\t'snplst',\n\t'snplocus',\n\t'geneVariant',\n\t'samplelst',\n\t'geneExpression',\n\tISOFORM_EXPRESSION,\n\t'dtcnv',\n\t'dtsnvindel',\n\t'dtfusion',\n\t'dtsv',\n\t'date',\n\tTermTypes.SSGSEA,\n\tTermTypes.DNA_METHYLATION,\n\tTermTypes.METABOLITE_INTENSITY,\n\tTermTypes.PROTEOME_ABUNDANCE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tSINGLECELL_CELLTYPE,\n\tTermTypes.SNP,\n\tTermTypes.TERM_COLLECTION\n])\n\n/*\nisUsableTerm() will\n\n\t- centralize the \"allowed term\" logic\n\twhich can be intricate or dataset-specific \n\tfor certain terms or contexts\n\t- make it easy to handle new term types\n\nArguments:\n\n\tterm {}\n\t\t.type: 'categorical', etc.\n\t\t.child_types: []\n\t\n\t_usecase {}\n\t\t.target (REQUIRED): 'barchart', 'regression', etc\n\t\t\t- used as a switch-case \"router\" for additional use-specific logic\n\t\t\t- other parameters, if applicable, are described in the route \"handler\" \n\t\t.detail \n\t\t - a more specific detailed use case\n\t\n\n\ttermdbConfig\n\t\toptional. provides ds overrides on default rules via excludedTermtypeByTarget. for use on client\n\n\tds\n\t\toptional. provides ds overrides when the function runs on backend\n\t\tserver-side dataset object that can supply overrides (in the form of functions) to the use case logic,\n\t\tfor example, to apply role-based allowed term uses or performance-related restrictions\n\t\tto ancestor terms when a use case aggregates too many data points for a given chart type\n\nReturns\n\n\ta Set{} with zero or more of the following strings:\n\t- 'plot' if the term can be used in a plot chartType\n\t- 'branch' if the term can be used only as an expandable tree branch, but not in a plot\n\t- an empty Set means that the term has no valid uses, i.e, it cannot be used either for plotting or as a tree branch\n*/\nexport function isUsableTerm(term, _usecase, termdbConfig?: any, ds?: any) {\n\tconst usecase = _usecase || {}\n\n\t// may apply dataset specific override filter for a use case\n\tif (typeof ds?.usecase?.[usecase.target] == 'function') {\n\t\treturn ds.usecase[usecase.target](term, usecase)\n\t}\n\n\t// if (term.isprivate && !user.roleCanUse(term)) return false\n\n\tconst uses = new Set()\n\t// note: expects term.child_types to be null if term.isleaf == true\n\tconst child_types = term.child_types || []\n\t// default handling\n\tswitch (usecase.target) {\n\t\tcase 'barchart':\n\t\tcase 'violin':\n\t\tcase 'boxplot':\n\t\tcase 'summary':\n\t\t\tif (term.type && term.type !== 'survival') uses.add('plot')\n\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'summaryInput':\n\t\t\tif (usecase.detail === 'term2' || usecase.detail == 'term0') {\n\t\t\t\tif (term.type && term.type !== 'survival') uses.add('plot')\n\t\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t} else {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\n\t\tcase 'matrix':\n\t\t\tif (term.type) uses.add('plot')\n\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'table':\n\t\t\tif (usecase.detail == 'term') uses.add('plot')\n\t\t\tif (child_types.length > 1) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'sampleScatter':\n\t\t\tif (usecase.detail == 'numeric') {\n\t\t\t\tif (isNumericTerm(term)) {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t}\n\t\t\t// Commenting out for now. May need later for another single\n\t\t\t// cell term. Revisit logic at that time.\n\t\t\t// else if (usecase?.specialCase?.type == 'singleCell') {\n\t\t\t// \t\tif (term.type && term.type.startsWith('singleCell')) {\n\t\t\t// \t\t\tif (term.plot && term.plot == usecase.specialCase?.config.name) {\n\t\t\t// \t\t\t\tuses.add('plot')\n\t\t\t// \t\t\t}\n\t\t\t// \t\t}\n\t\t\t// }\n\t\t\telse {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\t\tcase 'runChart2':\n\t\t\tif (usecase.detail == 'date' || usecase.detail == 'xtw') {\n\t\t\t\tif (term.type == 'date') {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (child_types.includes('date')) uses.add('branch')\n\t\t\t} else if (usecase.detail == 'numeric') {\n\t\t\t\tif (isNumericTerm(term) && term.type != 'date') {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t} else {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'numericDictTermCluster':\n\t\t\tif (!usecase.detail?.exclude?.includes(term.id)) {\n\t\t\t\tif (isNumericTerm(term)) {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) {\n\t\t\t\t\tuses.add('branch')\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'termCollections':\n\t\t\tif (usecase.detail?.termIds?.includes(term.id)) uses.add('plot')\n\t\t\tif (usecase.detail?.branchIds?.includes(term.id)) uses.add('branch')\n\t\t\treturn uses\n\n\t\tcase 'profileForms':\n\t\t\tif (!term.isleaf) {\n\t\t\t\tconst ancestors = term.id.split('__').length //depends on using the __ naming convension!\n\t\t\t\tif (ancestors == 3) {\n\t\t\t\t\t// 3rd level term is a domain, we show the templates associated to this domain\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t} else if (ancestors < 3) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'profileForms2': {\n\t\t\t// Picker (forms2.ts makeChartBtnMenu) sets usecase.cohort + usecase.subtype.\n\t\t\t// Reads the per-cohort domains list directly from the dataset config at\n\t\t\t// plotConfigByCohort[cohort].profileForms2.domains \u2014 each entry is { id, plotTypes }.\n\t\t\tif (term.isleaf) return uses\n\t\t\tconst allowed = termdbConfig?.plotConfigByCohort?.[usecase.cohort]?.profileForms2?.domains\n\t\t\tif (!allowed) return uses\n\t\t\tconst ancestors = term.id.split('__').length\n\t\t\tconst subtype = usecase.subtype\n\t\t\tif (ancestors == 3) {\n\t\t\t\tif (allowed.find(d => d.id === term.id)?.plotTypes?.includes(subtype)) uses.add('plot')\n\t\t\t} else if (ancestors < 3) {\n\t\t\t\tconst prefix = term.id + '__'\n\t\t\t\tif (allowed.some(d => d.id.startsWith(prefix) && d.plotTypes.includes(subtype))) {\n\t\t\t\t\tuses.add('branch')\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn uses\n\t\t}\n\n\t\t// case 'boxplot':\n\t\t// \tif (term.type == 'float' || term.type == 'integer') uses.add('plot')\n\t\t// \tif (usecase.detail === 'term2' && hasNumericChild(child_types)) uses.add('branch')\n\t\t// \treturn uses\n\n\t\tcase 'cuminc':\n\t\t\tif (usecase.detail == 'term') {\n\t\t\t\tif (term.type == 'condition') uses.add('plot')\n\t\t\t\tif (child_types.includes('condition')) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\tif (usecase.detail === 'term2' || usecase.detail == 'term0') {\n\t\t\t\tif (term.type && term.type != 'condition' && term.type != 'survival') uses.add('plot')\n\t\t\t\tif (hasAllowedChildTypes(child_types, ['condition', 'survival'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'survival':\n\t\t\tif (usecase.detail == 'term') {\n\t\t\t\tif (term.type == 'survival') uses.add('plot')\n\t\t\t\tif (child_types.includes('survival')) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\tif (usecase.detail === 'term2' || usecase.detail == 'term0') {\n\t\t\t\tif (term.type && term.type != 'survival') uses.add('plot')\n\t\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'regression':\n\t\t\tif (usecase.detail == 'outcome') {\n\t\t\t\tif (usecase.regressionType == 'linear') {\n\t\t\t\t\tif (term.type == 'float' || term.type == 'integer') uses.add('plot')\n\t\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t\t\treturn uses\n\t\t\t\t}\n\t\t\t\tif (usecase.regressionType == 'logistic') {\n\t\t\t\t\tif (term.type && term.type != 'survival') uses.add('plot')\n\t\t\t\t\tif (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')\n\t\t\t\t\treturn uses\n\t\t\t\t} else if (usecase.regressionType == 'cox') {\n\t\t\t\t\tif (term.type == 'condition' || term.type == 'survival') uses.add('plot')\n\t\t\t\t\tif (child_types.includes('condition') || child_types.includes('survival')) uses.add('branch')\n\t\t\t\t\treturn uses\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (usecase.detail == 'independent') {\n\t\t\t\tif (term.type == 'float' || term.type == 'integer' || term.type == 'categorical' || term.type == 'samplelst')\n\t\t\t\t\tuses.add('plot')\n\t\t\t\tif (hasChildTypes(child_types, ['categorical', 'float', 'integer'])) uses.add('branch')\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'filter': {\n\t\t\t// apply \"exlst\" to other targets as needed\n\t\t\tconst exlst = termdbConfig?.excludedTermtypeByTarget?.filter\n\t\t\tif (exlst) {\n\t\t\t\tif (graphableTypes.has(term.type) && !exlst.includes(term.type)) uses.add('plot')\n\t\t\t\tif (child_types.find(t => !exlst.includes(t))) uses.add('branch') // there's a non-excluded child type, allow branch to show\n\t\t\t\treturn uses\n\t\t\t}\n\t\t\t// no specific rule for filter. use default rules\n\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\treturn uses\n\t\t}\n\n\t\tcase 'correlationVolcano':\n\t\t\tif (usecase.detail == 'numeric') {\n\t\t\t\tif (isNumericTerm(term)) {\n\t\t\t\t\tuses.add('plot')\n\t\t\t\t}\n\t\t\t\tif (hasNumericChild(child_types)) uses.add('branch')\n\t\t\t} else {\n\t\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\t}\n\t\t\treturn uses\n\n\t\tcase 'proteinView':\n\t\t\tif (term.type == TermTypes.PROTEOME_ABUNDANCE) uses.add('plot')\n\t\t\tif (child_types.includes(TermTypes.PROTEOME_ABUNDANCE)) uses.add('branch')\n\t\t\treturn uses\n\n\t\tdefault:\n\t\t\tif (graphableTypes.has(term.type)) uses.add('plot')\n\t\t\tif (!term.isleaf) uses.add('branch')\n\t\t\treturn uses\n\t}\n}\n\n// determine if the term has at least one child type that\n// is not excluded\nfunction hasAllowedChildTypes(child_types, excluded_types) {\n\tif (!child_types.length) {\n\t\t// term does not have children\n\t\treturn false\n\t}\n\tif (!excluded_types?.length) {\n\t\t// no excluded types\n\t\treturn true\n\t}\n\tif (child_types.some(type => !excluded_types.includes(type))) {\n\t\t// at least one child type is not excluded\n\t\treturn true\n\t}\n}\n\nfunction hasNumericChild(child_types) {\n\treturn child_types.includes('float') || child_types.includes('integer')\n}\n\nfunction hasChildTypes(child_types, expected_types) {\n\tfor (const a of expected_types) {\n\t\tif (child_types.includes(a)) return true\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|