@sjcrh/proteinpaint-shared 2.177.1-0 → 2.178.1-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/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-shared",
3
- "version": "2.177.1-0",
3
+ "version": "2.178.1-0",
4
4
  "description": "ProteinPaint code that is shared between server and client-side workspaces",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
+ "imports": {
8
+ "#types": "@sjcrh/proteinpaint-types",
9
+ "#types/*": "@sjcrh/proteinpaint-types/*"
10
+ },
7
11
  "exports": {
8
12
  ".": "./src/index.js",
9
13
  "./*.ts": "./src/*.ts_SHOULD_BE_js",
package/src/common.js CHANGED
@@ -22,10 +22,12 @@ export const TermTypeGroups = {
22
22
  GENE_EXPRESSION: 'Gene Expression',
23
23
  GSEA: 'GSEA',
24
24
  METABOLITE_INTENSITY: 'Metabolite Intensity',
25
+ WHOLE_PROTEOME_ABUNDANCE: 'Whole Proteome Abundance',
25
26
  MUTATION_CNV_FUSION: 'Mutation/CNV/Fusion',
26
27
  MUTATION_SIGNATURE: 'Mutation Signature',
27
28
  PROTEIN_EXPRESSION: 'Protein Expression',
28
29
  SINGLECELL_CELLTYPE: 'Single-cell Cell Type',
30
+ SINGLECELL_GENE_EXPRESSION: 'Single-cell Gene Expression',
29
31
  SNP: 'SNP Genotype',
30
32
  SNP_LIST: 'SNP List',
31
33
  SNP_LOCUS: 'SNP Locus',
@@ -66,6 +68,7 @@ export const dtloh = 10
66
68
  export const dtmetaboliteintensity = 11
67
69
  export const dtssgsea = 12
68
70
  export const dtdnamethylation = 13
71
+ export const dtwholeproteomeabundance = 14
69
72
  // add new dt value here. !!!DO NOT change value of existing dt!!!
70
73
 
71
74
  export const dt2label = {
@@ -79,7 +82,8 @@ export const dt2label = {
79
82
  [dtcloss]: 'C-loss',
80
83
  [dtloh]: 'LOH',
81
84
  [dtgeneexpression]: 'Gene Expression',
82
- [dtmetaboliteintensity]: 'Metabolite Intensity'
85
+ [dtmetaboliteintensity]: 'Metabolite Intensity',
86
+ [dtwholeproteomeabundance]: 'Whole Proteome Abundance'
83
87
  }
84
88
 
85
89
  // Maps dt types to UI labels and lesion types for GRIN2
@@ -223,22 +223,27 @@ export async function memFetch(url, init, opts = {}) {
223
223
  dataCache.set(dataKey, { response, exp: Date.now() + cacheLifetime })
224
224
  return response
225
225
  })
226
- : fetch(url, init).then(async r => {
227
- const response = await processResponse(r)
228
- if (!r.ok) {
229
- console.trace(response)
230
- throw (
231
- 'memFetch error ' +
232
- r.status +
233
- ': ' +
234
- (typeof response == 'object' ? response.message || response.error : response)
235
- )
236
- }
237
- // replace the cached promise result with the actual data,
238
- // since persisting a cached promise for a long time is likely not best practice
239
- dataCache.set(dataKey, { response: deepFreeze(response), exp: Date.now() + cacheLifetime })
240
- return response
241
- })
226
+ : fetch(url, init)
227
+ .then(async r => {
228
+ const response = await processResponse(r)
229
+ if (!r.ok) {
230
+ console.trace(response)
231
+ throw (
232
+ 'memFetch error ' +
233
+ r.status +
234
+ ': ' +
235
+ (typeof response == 'object' ? response.message || response.error : response)
236
+ )
237
+ }
238
+ // replace the cached promise result with the actual data,
239
+ // since persisting a cached promise for a long time is likely not best practice
240
+ dataCache.set(dataKey, { response: deepFreeze(response), exp: Date.now() + cacheLifetime })
241
+ return response
242
+ })
243
+ .catch(e => {
244
+ if (dataCache.get(dataKey)) dataCache.delete(dataKey)
245
+ throw e
246
+ })
242
247
 
243
248
  dataCache.set(dataKey, { response: result, exp: Date.now() + cacheLifetime })
244
249
  manageCacheSize(now)
@@ -246,7 +251,7 @@ export async function memFetch(url, init, opts = {}) {
246
251
  } catch (e) {
247
252
  // delete this cache only if it is a promise;
248
253
  // do not delete a valid resolved data cache
249
- if (dataCache.get(dataKey) instanceof Promise) delete dataCache.delete(dataKey)
254
+ if (dataCache.get(dataKey) instanceof Promise) dataCache.delete(dataKey)
250
255
  throw e
251
256
  }
252
257
  }
package/src/filter.js CHANGED
@@ -1,255 +1,193 @@
1
- /*
2
- sampleAnno[anno{}] array of sample annotations
3
- anno.sample
4
- - string or number sample name
5
-
6
- anno.s || anno.data
7
- - the annotation object {[key1]: value1, ...}
8
-
9
- filter: nested filter structure as used in the termdbapp, see docs
10
- */
11
- export function getFilteredSamples(sampleAnno, filter) {
12
- setDatasetAnnotations(filter)
13
-
14
- const samples = new Set()
15
- for (const anno of sampleAnno) {
16
- if (samples.has(anno.sample)) continue
17
- const data = anno.s || anno.data
18
- if (data && sample_match_termvaluesetting(data, filter)) {
19
- samples.add(anno.sample)
20
- }
21
- }
22
- return samples // return as a Set, or maybe as an array later
1
+ function getFilteredSamples(sampleAnno, filter) {
2
+ setDatasetAnnotations(filter);
3
+ const samples = /* @__PURE__ */ new Set();
4
+ for (const anno of sampleAnno) {
5
+ if (samples.has(anno.sample)) continue;
6
+ const data = anno.s || anno.data;
7
+ if (data && sample_match_termvaluesetting(data, filter)) {
8
+ samples.add(anno.sample);
9
+ }
10
+ }
11
+ return samples;
23
12
  }
24
-
25
- /*
26
- given a value from a sample's anno of a term, return true if a value matches the filter
27
- */
28
- export function sample_match_termvaluesetting(row, filter, _term = null, sample = null) {
29
- const lst = filter.type == 'tvslst' ? filter.lst : [filter]
30
- let numberofmatchedterms = 0
31
-
32
- /* for AND, require all terms to match */
33
- for (const item of lst) {
34
- if (item.type == 'tvslst') {
35
- if (sample_match_termvaluesetting(row, item, _term, sample)) {
36
- numberofmatchedterms++
37
- }
38
- } else {
39
- const itemCopy = JSON.parse(JSON.stringify(item))
40
- const t = itemCopy.tvs
41
-
42
- if (_term && t.term) {
43
- if (!(_term.name == t.term.name && _term.type == t.term.type)) {
44
- // for an filter from "this.config.legendValueFilter", if the filter is not for the tw
45
- // (not the same type and name), ignore the filter.
46
- numberofmatchedterms++
47
- continue
48
- }
49
- }
50
-
51
- let samplevalue
52
- if (_term && !t.term) {
53
- if (t.term$type && t.term$type !== _term.type) {
54
- //when the filter is not for the term being tested, ignore the filter
55
- numberofmatchedterms++
56
- continue
57
- }
58
- t.term = _term
59
- samplevalue = typeof row === 'object' && t.term.id in row ? row[t.term.id] : row //'tumorWES'
60
- } else if (sample && t.term.$id) {
61
- samplevalue = sample[t.term.$id].value
62
- } else {
63
- samplevalue = t.term.id in row ? row[t.term.id] : row
64
- }
65
- setDatasetAnnotations(itemCopy)
66
- let thistermmatch
67
-
68
- if (t.term.type == 'categorical') {
69
- if (samplevalue === undefined) continue // this sample has no anno for this term, do not count
70
- thistermmatch = t.valueset.has(samplevalue)
71
- } else if (t.term.type == 'integer' || t.term.type == 'float') {
72
- if (samplevalue === undefined) continue // this sample has no anno for this term, do not count
73
- for (const range of t.ranges) {
74
- if ('value' in range) {
75
- thistermmatch = samplevalue === range.value // || ""+samplevalue == range.value || samplevalue == ""+range.value //; if (thistermmatch) console.log(i++)
76
- if (thistermmatch) break
77
- } else if (samplevalue == range.name) {
78
- thistermmatch = true
79
- break
80
- } else {
81
- // actual range
82
- if (t.term.values) {
83
- const v = t.term.values[samplevalue.toString()]
84
- if (v && v.uncomputable) {
85
- continue
86
- }
87
- }
88
- let left, right
89
- if (range.startunbounded) {
90
- left = true
91
- } else if ('start' in range) {
92
- if (range.startinclusive) {
93
- left = samplevalue >= range.start
94
- } else {
95
- left = samplevalue > range.start
96
- }
97
- }
98
- if (range.stopunbounded) {
99
- right = true
100
- } else if ('stop' in range) {
101
- if (range.stopinclusive) {
102
- right = samplevalue <= range.stop
103
- } else {
104
- right = samplevalue < range.stop
105
- }
106
- }
107
- thistermmatch = left && right
108
- }
109
- if (thistermmatch) break
110
- }
111
- } else if (t.term.type == 'condition') {
112
- const key = getPrecomputedKey(t)
113
- const anno = samplevalue && samplevalue[key]
114
- if (anno) {
115
- thistermmatch = Array.isArray(anno)
116
- ? t.values.find(d => anno.includes(d.key))
117
- : t.values.find(d => d.key == anno)
118
- }
119
- } else if (t.term.type == 'geneVariant') {
120
- /*
121
- samplevalue.values here can be an array or only one of the entries
122
- [
123
- { dt: 1, class: 'WT', _SAMPLEID_: 21, origin: 'germline' },
124
- { dt: 1, class: 'WT', _SAMPLEID_: 21, origin: 'somatic' },
125
- { dt: 2, class: 'Blank', _SAMPLEID_: 21 },
126
- { dt: 4, class: 'WT', _SAMPLEID_: 21 }
127
- ]
128
- */
129
- /* tvs.values is an array that stores classes (for each available dt) that have/haven't been crossed out by the user at this round of edit-and-apply, e.g.
130
- [
131
- {dt: 1, mclassLst: ['WT'], mclassExcludeLst: ['Blank'], origin: 'germline'}
132
- {dt: 1, mclassLst: ['Blank', 'WT', 'M'], mclassExcludeLst:[], origin:'somatic'},
133
- {dt: 2, mclassLst: ['Blank', 'WT'], mclassExcludeLst:[]}
134
- {dt: 4, mclassLst: ['WT', 'CNV_loss'], mclassExcludeLst:[]}
135
- ]
136
- */
137
- const svalues = samplevalue.values || [samplevalue]
138
- for (const sv of svalues) {
139
- thistermmatch =
140
- t.values.find(
141
- v =>
142
- v.dt == sv.dt &&
143
- (!v.origin || sv.origin == v.origin) &&
144
- (!v.mclasslst || v.mclasslst.includes(sv.class))
145
- ) && true //; console.log(114, t.values[0].dt, samplevalue.dt, thistermmatch)
146
- }
147
- } else {
148
- throw 'unknown term type [sample_match_termvaluesetting() shared/utils/src/filter.js]'
149
- }
150
-
151
- if (t.isnot) {
152
- thistermmatch = !thistermmatch
153
- }
154
- if (thistermmatch) numberofmatchedterms++
155
- }
156
-
157
- // if one tvslst is matched with an "or" (Set UNION), then sample is okay
158
- if (filter.join == 'or') {
159
- if (numberofmatchedterms && filter.in) return true
160
- if (!numberofmatchedterms && !filter.in) return true
161
- }
162
- }
163
- // for join="and" (Set intersection)
164
- if (!('in' in filter)) filter.in = true
165
- return filter.in == (numberofmatchedterms == lst.length)
166
- // if (filter.in && numberofmatchedterms == lst.length) return true
167
- // if (!filter.in && numberofmatchedterms != lst.length) return true
13
+ function sample_match_termvaluesetting(row, filter, _term = null, sample = null) {
14
+ const lst = filter.type == "tvslst" ? filter.lst : [filter];
15
+ let numberofmatchedterms = 0;
16
+ for (const item of lst) {
17
+ if ("type" in item && item.type == "tvslst") {
18
+ if (sample_match_termvaluesetting(row, item, _term, sample)) {
19
+ numberofmatchedterms++;
20
+ }
21
+ } else {
22
+ const itemCopy = JSON.parse(JSON.stringify(item));
23
+ const t = itemCopy.tvs;
24
+ if (_term && t.term) {
25
+ if (!(_term.name == t.term.name && _term.type == t.term.type)) {
26
+ numberofmatchedterms++;
27
+ continue;
28
+ }
29
+ }
30
+ let samplevalue;
31
+ if (_term && !t.term) {
32
+ if (t.term$type && t.term$type !== _term.type) {
33
+ numberofmatchedterms++;
34
+ continue;
35
+ }
36
+ t.term = _term;
37
+ samplevalue = typeof row === "object" && t.term.id in row ? row[t.term.id] : row;
38
+ } else if (sample && t.term.$id) {
39
+ samplevalue = sample[t.term.$id].value;
40
+ } else {
41
+ samplevalue = t.term.id in row ? row[t.term.id] : row;
42
+ }
43
+ setDatasetAnnotations(itemCopy);
44
+ let thistermmatch;
45
+ if (t.term.type == "categorical") {
46
+ if (samplevalue === void 0) continue;
47
+ thistermmatch = t.valueset.has(samplevalue);
48
+ } else if (t.term.type == "integer" || t.term.type == "float") {
49
+ if (samplevalue === void 0) continue;
50
+ for (const range of t.ranges) {
51
+ if ("value" in range) {
52
+ thistermmatch = samplevalue === range.value;
53
+ if (thistermmatch) break;
54
+ } else if (samplevalue == range.name) {
55
+ thistermmatch = true;
56
+ break;
57
+ } else {
58
+ if (t.term.values) {
59
+ const v = t.term.values[samplevalue.toString()];
60
+ if (v && v.uncomputable) {
61
+ continue;
62
+ }
63
+ }
64
+ let left, right;
65
+ if (range.startunbounded) {
66
+ left = true;
67
+ } else if ("start" in range) {
68
+ if (range.startinclusive) {
69
+ left = samplevalue >= range.start;
70
+ } else {
71
+ left = samplevalue > range.start;
72
+ }
73
+ }
74
+ if (range.stopunbounded) {
75
+ right = true;
76
+ } else if ("stop" in range) {
77
+ if (range.stopinclusive) {
78
+ right = samplevalue <= range.stop;
79
+ } else {
80
+ right = samplevalue < range.stop;
81
+ }
82
+ }
83
+ thistermmatch = left && right;
84
+ }
85
+ if (thistermmatch) break;
86
+ }
87
+ } else if (t.term.type == "condition") {
88
+ const key = getPrecomputedKey(t);
89
+ const anno = samplevalue && samplevalue[key];
90
+ if (anno) {
91
+ thistermmatch = Array.isArray(anno) ? t.values.find((d) => anno.includes(d.key)) : t.values.find((d) => d.key == anno);
92
+ }
93
+ } else if (t.term.type == "geneVariant") {
94
+ const svalues = samplevalue.values || [samplevalue];
95
+ for (const sv of svalues) {
96
+ thistermmatch = t.values.find(
97
+ (v) => v.dt == sv.dt && (!v.origin || sv.origin == v.origin) && (!v.mclasslst || v.mclasslst.includes(sv.class))
98
+ ) && true;
99
+ if (thistermmatch) break;
100
+ }
101
+ } else {
102
+ throw "unknown term type [sample_match_termvaluesetting() shared/utils/src/filter.ts]";
103
+ }
104
+ if (t.isnot) {
105
+ thistermmatch = !thistermmatch;
106
+ }
107
+ if (thistermmatch) numberofmatchedterms++;
108
+ }
109
+ if (filter.join == "or") {
110
+ if (numberofmatchedterms && filter.in) return true;
111
+ if (!numberofmatchedterms && !filter.in) return true;
112
+ }
113
+ }
114
+ if (!("in" in filter)) filter.in = true;
115
+ return filter.in == (numberofmatchedterms == lst.length);
168
116
  }
169
-
170
- export function setDatasetAnnotations(item, ds = null) {
171
- if (item.type == 'tvslst') {
172
- for (const subitem of item.lst) {
173
- setDatasetAnnotations(subitem, ds)
174
- }
175
- } else {
176
- if (ds && typeof ds.setAnnoByTermId == 'function') {
177
- ds.setAnnoByTermId(item.tvs.term.id)
178
- }
179
- if (item.tvs.term.type == 'categorical') {
180
- item.tvs.valueset = new Set(item.tvs.values.map(i => i.key))
181
- }
182
- }
117
+ function setDatasetAnnotations(item, ds = null) {
118
+ if (item.type == "tvslst") {
119
+ for (const subitem of item.lst) {
120
+ setDatasetAnnotations(subitem, ds);
121
+ }
122
+ } else {
123
+ if (ds && typeof ds.setAnnoByTermId == "function") {
124
+ ds.setAnnoByTermId(item.tvs.term.id);
125
+ }
126
+ if (item.tvs.term.type == "categorical") {
127
+ const tvsAny = item.tvs;
128
+ tvsAny.valueset = new Set(tvsAny.values.map((i) => i.key));
129
+ }
130
+ }
183
131
  }
184
-
185
132
  function getPrecomputedKey(q) {
186
- const precomputedKey =
187
- q.bar_by_children && q.value_by_max_grade
188
- ? 'childrenAtMaxGrade'
189
- : q.bar_by_children && q.value_by_most_recent
190
- ? 'childrenAtMostRecent'
191
- : q.bar_by_children && q.value_by_computable_grade
192
- ? 'children'
193
- : q.bar_by_grade && q.value_by_max_grade
194
- ? 'maxGrade'
195
- : q.bar_by_grade && q.value_by_most_recent
196
- ? 'mostRecentGrades'
197
- : q.bar_by_grade && q.value_by_computable_grade
198
- ? 'computableGrades'
199
- : ''
200
- if (!precomputedKey) throw `unknown condition term bar_by_* and/or value_by_*`
201
- return precomputedKey
133
+ const precomputedKey = q.bar_by_children && q.value_by_max_grade ? "childrenAtMaxGrade" : q.bar_by_children && q.value_by_most_recent ? "childrenAtMostRecent" : q.bar_by_children && q.value_by_computable_grade ? "children" : q.bar_by_grade && q.value_by_max_grade ? "maxGrade" : q.bar_by_grade && q.value_by_most_recent ? "mostRecentGrades" : q.bar_by_grade && q.value_by_computable_grade ? "computableGrades" : "";
134
+ if (!precomputedKey) throw `unknown condition term bar_by_* and/or value_by_*`;
135
+ return precomputedKey;
202
136
  }
203
-
204
- /* join a list of filters into the first filter with "and", return joined filter
205
- to be used by caller app to join hidden filters into a visible filter
206
-
207
- lst:[]
208
- a list of filters
209
- the function returns a (modified) copy of the first filter, and will not modify it
210
- rest of the array will be joined to the first one under "and"
211
- */
212
- export function filterJoin(lst) {
213
- if (!lst || lst.length == 0) return
214
- let f = JSON.parse(JSON.stringify(lst[0]))
215
- if (lst.length == 1) return f
216
- // more than 1 item, will join
217
- if (f.lst.length < 2) {
218
- if (f.join !== '') throw 'filter.join must be an empty string "" when filter.lst.length < 2'
219
- f.join = 'and'
220
- } else if (f.join == 'or') {
221
- // f is "or", wrap it with another root layer of "and"
222
- f = {
223
- type: 'tvslst',
224
- join: 'and',
225
- in: true,
226
- lst: [f]
227
- }
228
- } else if (f.join != 'and') {
229
- throw 'filter.join must be either "and" or "or" when .lst length > 1'
230
- }
231
- // now, f.join should be "and"
232
- // if the argument lst[0].join == "and",
233
- // then the f.in boolean value is reused
234
- for (let i = 1; i < lst.length; i++) {
235
- const f2 = JSON.parse(JSON.stringify(lst[i]))
236
- if (f2.join == 'or') f.lst.push(f2)
237
- else f.lst.push(...f2.lst)
238
- }
239
- // if f ends up single-tvs item (from joining single tvs to empty filter), need to set join to '' per filter spec
240
- if (f.lst.length == 1 && f.lst[0].type == 'tvs') {
241
- f.join = ''
242
- }
243
- return f
137
+ function filterJoin(lst) {
138
+ if (!lst || lst.length == 0) return;
139
+ let f = JSON.parse(JSON.stringify(lst[0]));
140
+ if (lst.length == 1) return f;
141
+ if (f.lst.length < 2) {
142
+ if (f.join !== "") throw 'filter.join must be an empty string "" when filter.lst.length < 2';
143
+ f.join = "and";
144
+ } else if (f.join == "or") {
145
+ f = {
146
+ type: "tvslst",
147
+ join: "and",
148
+ in: true,
149
+ lst: [f]
150
+ };
151
+ } else if (f.join != "and") {
152
+ throw 'filter.join must be either "and" or "or" when .lst length > 1';
153
+ }
154
+ for (let i = 1; i < lst.length; i++) {
155
+ const f2 = JSON.parse(JSON.stringify(lst[i]));
156
+ if (f2.join == "or") f.lst.push(f2);
157
+ else f.lst.push(...f2.lst);
158
+ }
159
+ if (f.lst.length == 1 && f.lst[0].type == "tvs") {
160
+ f.join = "";
161
+ }
162
+ return f;
244
163
  }
245
-
246
- export function getWrappedTvslst(lst = [], join = '', $id = null) {
247
- const filter = {
248
- type: 'tvslst',
249
- in: true,
250
- join,
251
- lst
252
- }
253
- if ($id !== null && filter.$id !== undefined) filter.$id = $id
254
- return filter
164
+ function getWrappedTvslst(lst = [], join = "", $id = null) {
165
+ const filter = {
166
+ type: "tvslst",
167
+ in: true,
168
+ join,
169
+ lst
170
+ };
171
+ if ($id !== null) filter.$id = $id;
172
+ return filter;
255
173
  }
174
+ function validateTermCollectionTvs(lst1, lst2) {
175
+ if (!Array.isArray(lst1)) throw new Error("numerator not array");
176
+ if (!Array.isArray(lst2)) throw new Error("denominator not array");
177
+ if (lst1.length == 0) throw new Error("numerator empty");
178
+ if (lst2.length == 0) throw new Error("denominator empty");
179
+ if (lst1.length > lst2.length) throw new Error("numerator longer than denominator");
180
+ for (const s of lst1) {
181
+ if (typeof s != "string") throw new Error("one of numerator not string");
182
+ if (!s) throw new Error("empty string in numerator");
183
+ if (!lst2.includes(s)) throw new Error("one of numerator not in denominator");
184
+ }
185
+ }
186
+ export {
187
+ filterJoin,
188
+ getFilteredSamples,
189
+ getWrappedTvslst,
190
+ sample_match_termvaluesetting,
191
+ setDatasetAnnotations,
192
+ validateTermCollectionTvs
193
+ };
@@ -19,6 +19,7 @@ export const graphableTypes = new Set([
19
19
  TermTypes.SSGSEA,
20
20
  TermTypes.DNA_METHYLATION,
21
21
  TermTypes.METABOLITE_INTENSITY,
22
+ TermTypes.WHOLE_PROTEOME_ABUNDANCE,
22
23
  TermTypes.SINGLECELL_GENE_EXPRESSION,
23
24
  TermTypes.SINGLECELL_CELLTYPE,
24
25
  TermTypes.SNP,
@@ -67,8 +68,8 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
67
68
  const usecase = _usecase || {}
68
69
 
69
70
  // may apply dataset specific override filter for a use case
70
- if (typeof ds?.usecase?.[use.target] == 'function') {
71
- return ds.usecase[use.target](term, use)
71
+ if (typeof ds?.usecase?.[usecase.target] == 'function') {
72
+ return ds.usecase[usecase.target](term, usecase)
72
73
  }
73
74
 
74
75
  // if (term.isprivate && !user.roleCanUse(term)) return false
@@ -76,7 +77,6 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
76
77
  const uses = new Set()
77
78
  // note: expects term.child_types to be null if term.isleaf == true
78
79
  const child_types = term.child_types || []
79
-
80
80
  // default handling
81
81
  switch (usecase.target) {
82
82
  case 'barchart':
@@ -92,6 +92,10 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
92
92
  if (term.type && term.type !== 'survival') uses.add('plot')
93
93
  if (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')
94
94
  return uses
95
+ } else {
96
+ if (graphableTypes.has(term.type)) uses.add('plot')
97
+ if (!term.isleaf) uses.add('branch')
98
+ return uses
95
99
  }
96
100
 
97
101
  case 'matrix':
@@ -110,6 +114,13 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
110
114
  uses.add('plot')
111
115
  }
112
116
  if (hasNumericChild(child_types)) uses.add('branch')
117
+ } else if (usecase?.vocab?.type == 'singleCell') {
118
+ /** TODO: Revisit this approach. Seems chaotic. */
119
+ if (term.type && term.type.startsWith('singleCell')) {
120
+ if (term.plot && term.plot == usecase.vocab?.config.name) {
121
+ uses.add('plot')
122
+ }
123
+ }
113
124
  } else {
114
125
  if (graphableTypes.has(term.type)) uses.add('plot')
115
126
  if (!term.isleaf) uses.add('branch')
@@ -142,7 +153,7 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
142
153
  }
143
154
  return uses
144
155
 
145
- case 'numericTermCollections':
156
+ case 'termCollections':
146
157
  if (usecase.detail?.termIds?.includes(term.id)) uses.add('plot')
147
158
  if (usecase.detail?.branchIds?.includes(term.id)) uses.add('branch')
148
159
  return uses
@@ -173,6 +184,7 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
173
184
  if (hasAllowedChildTypes(child_types, ['condition', 'survival'])) uses.add('branch')
174
185
  return uses
175
186
  }
187
+ return uses
176
188
 
177
189
  case 'survival':
178
190
  if (usecase.detail == 'term') {
@@ -185,6 +197,7 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
185
197
  if (hasAllowedChildTypes(child_types, ['survival'])) uses.add('branch')
186
198
  return uses
187
199
  }
200
+ return uses
188
201
 
189
202
  case 'regression':
190
203
  if (usecase.detail == 'outcome') {
@@ -210,8 +223,9 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
210
223
  if (hasChildTypes(child_types, ['categorical', 'float', 'integer'])) uses.add('branch')
211
224
  return uses
212
225
  }
226
+ return uses
213
227
 
214
- case 'filter':
228
+ case 'filter': {
215
229
  // apply "exlst" to other targets as needed
216
230
  const exlst = termdbConfig?.excludedTermtypeByTarget?.filter
217
231
  if (exlst) {
@@ -219,7 +233,11 @@ export function isUsableTerm(term, _usecase, termdbConfig, ds) {
219
233
  if (child_types.find(t => !exlst.includes(t))) uses.add('branch') // there's a non-excluded child type, allow branch to show
220
234
  return uses
221
235
  }
222
- // no specific rule for filter. pass and use default rules
236
+ // no specific rule for filter. use default rules
237
+ if (graphableTypes.has(term.type)) uses.add('plot')
238
+ if (!term.isleaf) uses.add('branch')
239
+ return uses
240
+ }
223
241
 
224
242
  case 'correlationVolcano':
225
243
  if (usecase.detail == 'numeric') {
package/src/terms.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  dtssgsea,
4
4
  dtdnamethylation,
5
5
  dtmetaboliteintensity,
6
+ dtwholeproteomeabundance,
6
7
  TermTypeGroups,
7
8
  dtTerms
8
9
  } from './common.js'
@@ -44,6 +45,7 @@ export const TermTypes = {
44
45
  SURVIVAL: 'survival',
45
46
  SAMPLELST: 'samplelst',
46
47
  METABOLITE_INTENSITY: 'metaboliteIntensity',
48
+ WHOLE_PROTEOME_ABUNDANCE: 'wholeProteomeAbundance',
47
49
  SINGLECELL_GENE_EXPRESSION: 'singleCellGeneExpression',
48
50
  SINGLECELL_CELLTYPE: 'singleCellCellType',
49
51
  MULTIVALUE: 'multivalue',
@@ -61,7 +63,8 @@ export const TermTypes2Dt = {
61
63
  [TermTypes.GENE_EXPRESSION]: dtgeneexpression,
62
64
  [TermTypes.SSGSEA]: dtssgsea,
63
65
  [TermTypes.DNA_METHYLATION]: dtdnamethylation,
64
- [TermTypes.METABOLITE_INTENSITY]: dtmetaboliteintensity
66
+ [TermTypes.METABOLITE_INTENSITY]: dtmetaboliteintensity,
67
+ [TermTypes.WHOLE_PROTEOME_ABUNDANCE]: dtwholeproteomeabundance
65
68
  }
66
69
 
67
70
  // maps term type to group (as is shown as toggles in search ui)
@@ -80,8 +83,10 @@ export const typeGroup = {
80
83
  [TermTypes.SSGSEA]: TermTypeGroups.SSGSEA,
81
84
  [TermTypes.DNA_METHYLATION]: TermTypeGroups.DNA_METHYLATION,
82
85
  [TermTypes.METABOLITE_INTENSITY]: TermTypeGroups.METABOLITE_INTENSITY,
86
+ [TermTypes.WHOLE_PROTEOME_ABUNDANCE]: TermTypeGroups.WHOLE_PROTEOME_ABUNDANCE,
83
87
  [TermTypes.TERM_COLLECTION]: TermTypeGroups.TERM_COLLECTION,
84
- [TermTypes.SINGLECELL_CELLTYPE]: TermTypeGroups.SINGLECELL_CELLTYPE
88
+ [TermTypes.SINGLECELL_CELLTYPE]: TermTypeGroups.SINGLECELL_CELLTYPE,
89
+ [TermTypes.SINGLECELL_GENE_EXPRESSION]: TermTypeGroups.SINGLECELL_GENE_EXPRESSION
85
90
  }
86
91
 
87
92
  const nonDictTypes = new Set([
@@ -93,6 +98,7 @@ const nonDictTypes = new Set([
93
98
  TermTypes.DNA_METHYLATION,
94
99
  TermTypes.GENE_VARIANT,
95
100
  TermTypes.METABOLITE_INTENSITY,
101
+ TermTypes.WHOLE_PROTEOME_ABUNDANCE,
96
102
  TermTypes.SINGLECELL_GENE_EXPRESSION,
97
103
  TermTypes.SINGLECELL_CELLTYPE
98
104
  ])
@@ -107,6 +113,7 @@ export const numericTypes = new Set([
107
113
  TermTypes.SSGSEA,
108
114
  TermTypes.DNA_METHYLATION,
109
115
  TermTypes.METABOLITE_INTENSITY,
116
+ TermTypes.WHOLE_PROTEOME_ABUNDANCE,
110
117
  TermTypes.SINGLECELL_GENE_EXPRESSION,
111
118
  TermTypes.DATE
112
119
  ])
@@ -117,7 +124,7 @@ export const annoNumericTypes = new Set([TermTypes.INTEGER, TermTypes.FLOAT, Ter
117
124
 
118
125
  const categoricalTypes = new Set([TermTypes.CATEGORICAL, TermTypes.SNP])
119
126
 
120
- const singleSampleTerms = new Set([TermTypes.SINGLECELL_GENE_EXPRESSION])
127
+ const singleSampleTerms = new Set([TermTypes.SINGLECELL_CELLTYPE, TermTypes.SINGLECELL_GENE_EXPRESSION])
121
128
 
122
129
  export function isSingleSampleTerm(term) {
123
130
  if (!term) return false
@@ -154,6 +161,7 @@ export function equals(t1, t2) {
154
161
  case TermTypes.DNA_METHYLATION:
155
162
  return t1.chr == t2.chr && t1.start == t2.start && t1.stop == t2.stop
156
163
  case TermTypes.METABOLITE_INTENSITY:
164
+ case TermTypes.WHOLE_PROTEOME_ABUNDANCE:
157
165
  return t1.name == t2.name
158
166
  case TermTypes.GENE_VARIANT:
159
167
  return t1.gene == t2.gene || (t1.chr == t2.chr && t1.start == t2.start && t1.stop == t2.stop)
@@ -231,6 +239,7 @@ const typeMap = {
231
239
  dnaMethylation: 'DNA Methylation',
232
240
  geneVariant: 'Gene Variant',
233
241
  metaboliteIntensity: 'Metabolite Intensity',
242
+ wholeProteomeAbundance: 'Whole Proteome Abundance',
234
243
  multiValue: 'Multi Value',
235
244
  singleCellGeneExpression: 'Single Cell, Gene Expression',
236
245
  singleCellCellType: 'Single Cell, Cell Type',