@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 +5 -1
- package/src/common.js +5 -1
- package/src/fetch-helpers.js +22 -17
- package/src/filter.js +186 -248
- package/src/termdb.usecase.js +24 -6
- package/src/terms.js +12 -3
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-shared",
|
|
3
|
-
"version": "2.
|
|
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
|
package/src/fetch-helpers.js
CHANGED
|
@@ -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)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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)
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
lst
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
};
|
package/src/termdb.usecase.js
CHANGED
|
@@ -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?.[
|
|
71
|
-
return ds.usecase[
|
|
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 '
|
|
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
|
-
|
|
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',
|