@abi-software/flatmapvuer 1.1.4 → 1.2.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/LICENSE +201 -201
- package/README.md +120 -120
- package/cypress.config.js +23 -23
- package/dist/flatmapvuer.js +43570 -38544
- package/dist/flatmapvuer.umd.cjs +182 -182
- package/dist/index.html +17 -17
- package/dist/style.css +1 -1
- package/package.json +95 -95
- package/public/index.html +17 -17
- package/reporter-config.json +9 -9
- package/src/App.vue +379 -379
- package/src/assets/_variables.scss +43 -43
- package/src/assets/styles.scss +5 -5
- package/src/components/AnnotationTool.vue +501 -501
- package/src/components/ConnectionDialog.vue +134 -134
- package/src/components/DrawTool.vue +502 -502
- package/src/components/EventBus.js +3 -3
- package/src/components/ExternalResourceCard.vue +109 -109
- package/src/components/FlatmapVuer.vue +3515 -3461
- package/src/components/HelpModeDialog.vue +360 -360
- package/src/components/MultiFlatmapVuer.vue +814 -814
- package/src/components/ProvenancePopup.vue +530 -530
- package/src/components/SelectionsGroup.vue +363 -363
- package/src/components/Tooltip.vue +50 -50
- package/src/components/TreeControls.vue +236 -236
- package/src/components/index.js +8 -8
- package/src/components/legends/DynamicLegends.vue +106 -106
- package/src/components/legends/SvgLegends.vue +112 -112
- package/src/icons/flatmap-marker.js +9 -9
- package/src/icons/fonts/mapicon-species.svg +14 -14
- package/src/icons/fonts/mapicon-species.ttf +0 -0
- package/src/icons/fonts/mapicon-species.woff +0 -0
- package/src/icons/mapicon-species-style.css +42 -42
- package/src/icons/yellowstar.js +5 -5
- package/src/legends/legend.svg +25 -25
- package/src/main.js +19 -19
- package/src/services/flatmapQueries.js +475 -475
- package/src/store/index.js +23 -23
- package/vite.config.js +73 -73
- package/vite.static-build.js +12 -12
- package/vuese-generator.js +64 -64
|
@@ -1,475 +1,475 @@
|
|
|
1
|
-
/* eslint-disable no-alert, no-console */
|
|
2
|
-
// remove duplicates by stringifying the objects
|
|
3
|
-
const removeDuplicates = function (arrayOfAnything) {
|
|
4
|
-
if (!arrayOfAnything) return []
|
|
5
|
-
return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) =>
|
|
6
|
-
JSON.parse(e)
|
|
7
|
-
)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const cachedLabels = {}
|
|
11
|
-
|
|
12
|
-
const findTaxonomyLabel = async function (flatmapAPI, taxonomy) {
|
|
13
|
-
if (cachedLabels && cachedLabels.hasOwnProperty(taxonomy)) {
|
|
14
|
-
return cachedLabels[taxonomy]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return new Promise((resolve) => {
|
|
18
|
-
fetch(`${flatmapAPI}knowledge/label/${taxonomy}`, {
|
|
19
|
-
method: 'GET',
|
|
20
|
-
})
|
|
21
|
-
.then((response) => response.json())
|
|
22
|
-
.then((data) => {
|
|
23
|
-
let label = data.label
|
|
24
|
-
if (label === 'Mammalia') {
|
|
25
|
-
label = 'Mammalia not otherwise specified'
|
|
26
|
-
}
|
|
27
|
-
cachedLabels[taxonomy] = label
|
|
28
|
-
resolve(label)
|
|
29
|
-
})
|
|
30
|
-
.catch((error) => {
|
|
31
|
-
console.error('Error:', error)
|
|
32
|
-
cachedLabels[taxonomy] = taxonomy
|
|
33
|
-
resolve(taxonomy)
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const inArray = function (ar1, ar2) {
|
|
39
|
-
if (!ar1 || !ar2) return false
|
|
40
|
-
let as1 = JSON.stringify(ar1)
|
|
41
|
-
let as2 = JSON.stringify(ar2)
|
|
42
|
-
return as1.indexOf(as2) !== -1
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let FlatmapQueries = function () {
|
|
46
|
-
this.initialise = function (flatmapApi) {
|
|
47
|
-
this.flatmapApi = flatmapApi
|
|
48
|
-
this.destinations = []
|
|
49
|
-
this.origins = []
|
|
50
|
-
this.components = []
|
|
51
|
-
this.urls = []
|
|
52
|
-
this.controller = undefined
|
|
53
|
-
this.uberons = []
|
|
54
|
-
this.lookUp = []
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
this.createTooltipData = async function (eventData) {
|
|
58
|
-
let hyperlinks = []
|
|
59
|
-
if (
|
|
60
|
-
eventData.feature.hyperlinks &&
|
|
61
|
-
eventData.feature.hyperlinks.length > 0
|
|
62
|
-
) {
|
|
63
|
-
hyperlinks = eventData.feature.hyperlinks
|
|
64
|
-
} else {
|
|
65
|
-
hyperlinks = this.urls.map((url) => ({ url: url, id: 'pubmed' }))
|
|
66
|
-
}
|
|
67
|
-
let taxonomyLabel = undefined
|
|
68
|
-
if (eventData.provenanceTaxonomy) {
|
|
69
|
-
taxonomyLabel = []
|
|
70
|
-
for (let i = 0; eventData.provenanceTaxonomy.length > i; i++) {
|
|
71
|
-
taxonomyLabel.push(
|
|
72
|
-
await findTaxonomyLabel(
|
|
73
|
-
this.flatmapAPI,
|
|
74
|
-
eventData.provenanceTaxonomy[i]
|
|
75
|
-
)
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let tooltipData = {
|
|
81
|
-
destinations: this.destinations,
|
|
82
|
-
origins: this.origins,
|
|
83
|
-
components: this.components,
|
|
84
|
-
destinationsWithDatasets: this.destinationsWithDatasets,
|
|
85
|
-
originsWithDatasets: this.originsWithDatasets,
|
|
86
|
-
componentsWithDatasets: this.componentsWithDatasets,
|
|
87
|
-
title: eventData.label,
|
|
88
|
-
featureId: eventData.resource,
|
|
89
|
-
hyperlinks: hyperlinks,
|
|
90
|
-
provenanceTaxonomy: eventData.provenanceTaxonomy,
|
|
91
|
-
provenanceTaxonomyLabel: taxonomyLabel,
|
|
92
|
-
}
|
|
93
|
-
return tooltipData
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
this.createComponentsLabelList = function (components, lookUp) {
|
|
97
|
-
let labelList = []
|
|
98
|
-
components.forEach((n) => {
|
|
99
|
-
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
|
|
100
|
-
if (n.length === 2) {
|
|
101
|
-
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
return labelList
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this.createLabelLookup = function (uberons) {
|
|
108
|
-
return new Promise((resolve) => {
|
|
109
|
-
let uberonMap = {}
|
|
110
|
-
this.uberons = []
|
|
111
|
-
const data = { sql: this.buildLabelSqlStatement(uberons) }
|
|
112
|
-
fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
113
|
-
method: 'POST',
|
|
114
|
-
headers: {
|
|
115
|
-
'Content-Type': 'application/json',
|
|
116
|
-
},
|
|
117
|
-
body: JSON.stringify(data),
|
|
118
|
-
})
|
|
119
|
-
.then((response) => response.json())
|
|
120
|
-
.then((payload) => {
|
|
121
|
-
const entity = payload.keys.indexOf('entity')
|
|
122
|
-
const label = payload.keys.indexOf('label')
|
|
123
|
-
if (entity > -1 && label > -1) {
|
|
124
|
-
payload.values.forEach((pair) => {
|
|
125
|
-
uberonMap[pair[entity]] = pair[label]
|
|
126
|
-
this.uberons.push({
|
|
127
|
-
id: pair[entity],
|
|
128
|
-
name: pair[label],
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
resolve(uberonMap)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this.buildConnectivitySqlStatement = function (keastIds) {
|
|
138
|
-
let sql = 'select knowledge from knowledge where entity in ('
|
|
139
|
-
if (keastIds.length === 1) {
|
|
140
|
-
sql += `'${keastIds[0]}')`
|
|
141
|
-
} else if (keastIds.length > 1) {
|
|
142
|
-
for (let i in keastIds) {
|
|
143
|
-
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return sql
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
this.buildLabelSqlStatement = function (uberons) {
|
|
150
|
-
let sql = 'select entity, label from labels where entity in ('
|
|
151
|
-
if (uberons.length === 1) {
|
|
152
|
-
sql += `'${uberons[0]}')`
|
|
153
|
-
} else if (uberons.length > 1) {
|
|
154
|
-
for (let i in uberons) {
|
|
155
|
-
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return sql
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
this.findAllIdsFromConnectivity = function (connectivity) {
|
|
162
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
163
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
164
|
-
let found = []
|
|
165
|
-
nodes.forEach((n) => {
|
|
166
|
-
if (Array.isArray(n)) {
|
|
167
|
-
found.push(n.flat())
|
|
168
|
-
} else {
|
|
169
|
-
found.push(n)
|
|
170
|
-
}
|
|
171
|
-
})
|
|
172
|
-
return [...new Set(found.flat())]
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
this.flattenConntectivity = function (connectivity) {
|
|
176
|
-
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
177
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
178
|
-
let found = []
|
|
179
|
-
nodes.forEach((n) => {
|
|
180
|
-
if (Array.isArray(n)) {
|
|
181
|
-
found.push(n.flat())
|
|
182
|
-
} else {
|
|
183
|
-
found.push(n)
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
return found.flat()
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
this.findComponents = function (connectivity) {
|
|
190
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
191
|
-
let nodes = removeDuplicates(dnodes)
|
|
192
|
-
|
|
193
|
-
let found = []
|
|
194
|
-
let terminal = false
|
|
195
|
-
nodes.forEach((node) => {
|
|
196
|
-
terminal = false
|
|
197
|
-
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
198
|
-
if (inArray(connectivity.axons, node)) {
|
|
199
|
-
terminal = true
|
|
200
|
-
}
|
|
201
|
-
if (inArray(connectivity.dendrites, node)) {
|
|
202
|
-
terminal = true
|
|
203
|
-
}
|
|
204
|
-
if (!terminal) {
|
|
205
|
-
found.push(node)
|
|
206
|
-
}
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
return found
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
this.retrieveFlatmapKnowledgeForEvent = async function (eventData) {
|
|
213
|
-
// check if there is an existing query
|
|
214
|
-
if (this.controller) this.controller.abort()
|
|
215
|
-
|
|
216
|
-
// set up the abort controller
|
|
217
|
-
this.controller = new AbortController()
|
|
218
|
-
const signal = this.controller.signal
|
|
219
|
-
|
|
220
|
-
const keastIds = eventData.resource
|
|
221
|
-
this.destinations = []
|
|
222
|
-
this.origins = []
|
|
223
|
-
this.components = []
|
|
224
|
-
if (!keastIds || keastIds.length == 0) return
|
|
225
|
-
|
|
226
|
-
let prom1 = this.queryForConnectivity(keastIds, signal) // This on returns a promise so dont need 'await'
|
|
227
|
-
let prom2 = await this.pubmedQueryOnIds(eventData)
|
|
228
|
-
let results = await Promise.all([prom1, prom2])
|
|
229
|
-
return results
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
this.queryForConnectivity = function (keastIds, signal, processConnectivity=true) {
|
|
233
|
-
const data = { sql: this.buildConnectivitySqlStatement(keastIds) }
|
|
234
|
-
const headers = {
|
|
235
|
-
method: 'POST',
|
|
236
|
-
headers: {
|
|
237
|
-
'Content-Type': 'application/json',
|
|
238
|
-
},
|
|
239
|
-
body: JSON.stringify(data),
|
|
240
|
-
...(signal ? { signal: signal } : {}), // add signal to header if it exists
|
|
241
|
-
}
|
|
242
|
-
return new Promise((resolve) => {
|
|
243
|
-
fetch(`${this.flatmapApi}knowledge/query/`, headers)
|
|
244
|
-
.then((response) => response.json())
|
|
245
|
-
.then((data) => {
|
|
246
|
-
if (this.connectivityExists(data)) {
|
|
247
|
-
let connectivity = JSON.parse(data.values[0][0])
|
|
248
|
-
if (processConnectivity) {
|
|
249
|
-
this.processConnectivity(connectivity).then((processedConnectivity) => {
|
|
250
|
-
resolve(processedConnectivity)
|
|
251
|
-
})
|
|
252
|
-
}
|
|
253
|
-
else resolve(connectivity)
|
|
254
|
-
} else {
|
|
255
|
-
resolve(false)
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
.catch((error) => {
|
|
259
|
-
console.error('Error:', error)
|
|
260
|
-
resolve(false)
|
|
261
|
-
})
|
|
262
|
-
})
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
this.connectivityExists = function (data) {
|
|
266
|
-
if (
|
|
267
|
-
data.values &&
|
|
268
|
-
data.values.length > 0 &&
|
|
269
|
-
JSON.parse(data.values[0][0]).connectivity &&
|
|
270
|
-
JSON.parse(data.values[0][0]).connectivity.length > 0
|
|
271
|
-
) {
|
|
272
|
-
return true
|
|
273
|
-
} else {
|
|
274
|
-
return false
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// This function is used to determine if a node is a single node or a node with multiple children
|
|
279
|
-
// Returns the id of the node if it is a single node, otherwise returns false
|
|
280
|
-
this.findIfNodeIsSingle = function (node) {
|
|
281
|
-
if (node.length === 1) { // If the node is in the form [id]
|
|
282
|
-
console.error("Server returns a single node", node)
|
|
283
|
-
return node[0]
|
|
284
|
-
} else {
|
|
285
|
-
if (node.length === 2 && node[1].length === 0) { // If the node is in the form [id, []]
|
|
286
|
-
return node[0]
|
|
287
|
-
} else {
|
|
288
|
-
return false // If the node is in the form [id, [id1, id2]]
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
this.createLabelFromNeuralNode = function (node, lookUp) {
|
|
294
|
-
|
|
295
|
-
// Check if the node is a single node or a node with multiple children
|
|
296
|
-
let nodeIsSingle = this.findIfNodeIsSingle(node)
|
|
297
|
-
|
|
298
|
-
// Case where node is in the form [id]
|
|
299
|
-
if (nodeIsSingle) {
|
|
300
|
-
return lookUp[nodeIsSingle]
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Case where node is in the form [id, [id1 (,id2)]]
|
|
304
|
-
let label = lookUp[node[0]]
|
|
305
|
-
if (node.length === 2 && node[1].length > 0) {
|
|
306
|
-
node[1].forEach((n) => {
|
|
307
|
-
if (lookUp[n] == undefined) {
|
|
308
|
-
label += `, ${n}`
|
|
309
|
-
} else {
|
|
310
|
-
label += `, ${lookUp[n]}`
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
return label
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
this.flattenAndFindDatasets = function (components, axons, dendrites) {
|
|
318
|
-
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
319
|
-
let componentsFlat = this.flattenConntectivity(components)
|
|
320
|
-
let axonsFlat = this.flattenConntectivity(axons)
|
|
321
|
-
let dendritesFlat = this.flattenConntectivity(dendrites)
|
|
322
|
-
|
|
323
|
-
// Filter for the anatomy which is annotated on datasets
|
|
324
|
-
this.destinationsWithDatasets = this.uberons.filter(
|
|
325
|
-
(ub) => axonsFlat.indexOf(ub.id) !== -1
|
|
326
|
-
)
|
|
327
|
-
this.originsWithDatasets = this.uberons.filter(
|
|
328
|
-
(ub) => dendritesFlat.indexOf(ub.id) !== -1
|
|
329
|
-
)
|
|
330
|
-
this.componentsWithDatasets = this.uberons.filter(
|
|
331
|
-
(ub) => componentsFlat.indexOf(ub.id) !== -1
|
|
332
|
-
)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
this.processConnectivity = function (connectivity) {
|
|
336
|
-
return new Promise((resolve) => {
|
|
337
|
-
// Filter the origin and destinations from components
|
|
338
|
-
let components = this.findComponents(connectivity)
|
|
339
|
-
|
|
340
|
-
// Remove duplicates
|
|
341
|
-
let axons = removeDuplicates(connectivity.axons)
|
|
342
|
-
let dendrites = removeDuplicates(connectivity.dendrites)
|
|
343
|
-
|
|
344
|
-
// Create list of ids to get labels for
|
|
345
|
-
let conIds = this.findAllIdsFromConnectivity(connectivity)
|
|
346
|
-
|
|
347
|
-
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
348
|
-
this.createLabelLookup(conIds).then((lookUp) => {
|
|
349
|
-
this.destinations = axons.map((a) =>
|
|
350
|
-
this.createLabelFromNeuralNode(a, lookUp)
|
|
351
|
-
)
|
|
352
|
-
this.origins = dendrites.map((d) =>
|
|
353
|
-
this.createLabelFromNeuralNode(d, lookUp)
|
|
354
|
-
)
|
|
355
|
-
this.components = components.map((c) =>
|
|
356
|
-
this.createLabelFromNeuralNode(c, lookUp)
|
|
357
|
-
)
|
|
358
|
-
this.flattenAndFindDatasets(components, axons, dendrites)
|
|
359
|
-
resolve({
|
|
360
|
-
ids: {
|
|
361
|
-
axons: axons,
|
|
362
|
-
dendrites: dendrites,
|
|
363
|
-
components: components,
|
|
364
|
-
},
|
|
365
|
-
labels: {
|
|
366
|
-
destinations: this.destinations,
|
|
367
|
-
origins: this.origins,
|
|
368
|
-
components: this.components,
|
|
369
|
-
}
|
|
370
|
-
})
|
|
371
|
-
})
|
|
372
|
-
})
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
this.flattenConntectivity = function (connectivity) {
|
|
376
|
-
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
377
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
378
|
-
let found = []
|
|
379
|
-
nodes.forEach((n) => {
|
|
380
|
-
if (Array.isArray(n)) {
|
|
381
|
-
found.push(n.flat())
|
|
382
|
-
} else {
|
|
383
|
-
found.push(n)
|
|
384
|
-
}
|
|
385
|
-
})
|
|
386
|
-
return found.flat()
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
this.stripPMIDPrefix = function (pubmedId) {
|
|
390
|
-
return pubmedId.split(':')[1]
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
this.buildPubmedSqlStatement = function (keastIds) {
|
|
394
|
-
let sql = 'select distinct publication from publications where entity in ('
|
|
395
|
-
if (keastIds.length === 1) {
|
|
396
|
-
sql += `'${keastIds[0]}')`
|
|
397
|
-
} else if (keastIds.length > 1) {
|
|
398
|
-
for (let i in keastIds) {
|
|
399
|
-
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return sql
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
this.buildPubmedSqlStatementForModels = function (model) {
|
|
406
|
-
return `select distinct publication from publications where entity = '${model}'`
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
this.flatmapQuery = function (sql) {
|
|
410
|
-
const data = { sql: sql }
|
|
411
|
-
return fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
412
|
-
method: 'POST',
|
|
413
|
-
headers: {
|
|
414
|
-
'Content-Type': 'application/json',
|
|
415
|
-
},
|
|
416
|
-
body: JSON.stringify(data),
|
|
417
|
-
})
|
|
418
|
-
.then((response) => response.json())
|
|
419
|
-
.catch((error) => {
|
|
420
|
-
console.error('Error:', error)
|
|
421
|
-
})
|
|
422
|
-
}
|
|
423
|
-
// Note that this functin WILL run to the end, as it doesn not catch the second level of promises
|
|
424
|
-
this.pubmedQueryOnIds = function (eventData) {
|
|
425
|
-
return new Promise((resolve) => {
|
|
426
|
-
const keastIds = eventData.resource
|
|
427
|
-
const source = eventData.feature.source
|
|
428
|
-
if (!keastIds || keastIds.length === 0) return
|
|
429
|
-
const sql = this.buildPubmedSqlStatement(keastIds)
|
|
430
|
-
this.flatmapQuery(sql).then((data) => {
|
|
431
|
-
// Create pubmed url on paths if we have them
|
|
432
|
-
if (data.values.length > 0) {
|
|
433
|
-
this.urls = [
|
|
434
|
-
this.pubmedSearchUrl(
|
|
435
|
-
data.values.map((id) => this.stripPMIDPrefix(id[0]))
|
|
436
|
-
),
|
|
437
|
-
]
|
|
438
|
-
resolve(true)
|
|
439
|
-
} else {
|
|
440
|
-
// Create pubmed url on models
|
|
441
|
-
this.pubmedQueryOnModels(source).then((result) => {
|
|
442
|
-
resolve(result)
|
|
443
|
-
})
|
|
444
|
-
}
|
|
445
|
-
})
|
|
446
|
-
})
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
this.pubmedQueryOnModels = function (source) {
|
|
450
|
-
return this.flatmapQuery(
|
|
451
|
-
this.buildPubmedSqlStatementForModels(source)
|
|
452
|
-
).then((data) => {
|
|
453
|
-
if (Array.isArray(data.values) && data.values.length > 0) {
|
|
454
|
-
this.urls = [
|
|
455
|
-
this.pubmedSearchUrl(
|
|
456
|
-
data.values.map((id) => this.stripPMIDPrefix(id[0]))
|
|
457
|
-
),
|
|
458
|
-
]
|
|
459
|
-
return true
|
|
460
|
-
} else {
|
|
461
|
-
this.urls = [] // Clears the pubmed search button
|
|
462
|
-
}
|
|
463
|
-
return false
|
|
464
|
-
})
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
this.pubmedSearchUrl = function (ids) {
|
|
468
|
-
let url = 'https://pubmed.ncbi.nlm.nih.gov/?'
|
|
469
|
-
let params = new URLSearchParams()
|
|
470
|
-
params.append('term', ids)
|
|
471
|
-
return url + params.toString()
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
export { FlatmapQueries, findTaxonomyLabel }
|
|
1
|
+
/* eslint-disable no-alert, no-console */
|
|
2
|
+
// remove duplicates by stringifying the objects
|
|
3
|
+
const removeDuplicates = function (arrayOfAnything) {
|
|
4
|
+
if (!arrayOfAnything) return []
|
|
5
|
+
return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) =>
|
|
6
|
+
JSON.parse(e)
|
|
7
|
+
)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const cachedLabels = {}
|
|
11
|
+
|
|
12
|
+
const findTaxonomyLabel = async function (flatmapAPI, taxonomy) {
|
|
13
|
+
if (cachedLabels && cachedLabels.hasOwnProperty(taxonomy)) {
|
|
14
|
+
return cachedLabels[taxonomy]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
fetch(`${flatmapAPI}knowledge/label/${taxonomy}`, {
|
|
19
|
+
method: 'GET',
|
|
20
|
+
})
|
|
21
|
+
.then((response) => response.json())
|
|
22
|
+
.then((data) => {
|
|
23
|
+
let label = data.label
|
|
24
|
+
if (label === 'Mammalia') {
|
|
25
|
+
label = 'Mammalia not otherwise specified'
|
|
26
|
+
}
|
|
27
|
+
cachedLabels[taxonomy] = label
|
|
28
|
+
resolve(label)
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
console.error('Error:', error)
|
|
32
|
+
cachedLabels[taxonomy] = taxonomy
|
|
33
|
+
resolve(taxonomy)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const inArray = function (ar1, ar2) {
|
|
39
|
+
if (!ar1 || !ar2) return false
|
|
40
|
+
let as1 = JSON.stringify(ar1)
|
|
41
|
+
let as2 = JSON.stringify(ar2)
|
|
42
|
+
return as1.indexOf(as2) !== -1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let FlatmapQueries = function () {
|
|
46
|
+
this.initialise = function (flatmapApi) {
|
|
47
|
+
this.flatmapApi = flatmapApi
|
|
48
|
+
this.destinations = []
|
|
49
|
+
this.origins = []
|
|
50
|
+
this.components = []
|
|
51
|
+
this.urls = []
|
|
52
|
+
this.controller = undefined
|
|
53
|
+
this.uberons = []
|
|
54
|
+
this.lookUp = []
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.createTooltipData = async function (eventData) {
|
|
58
|
+
let hyperlinks = []
|
|
59
|
+
if (
|
|
60
|
+
eventData.feature.hyperlinks &&
|
|
61
|
+
eventData.feature.hyperlinks.length > 0
|
|
62
|
+
) {
|
|
63
|
+
hyperlinks = eventData.feature.hyperlinks
|
|
64
|
+
} else {
|
|
65
|
+
hyperlinks = this.urls.map((url) => ({ url: url, id: 'pubmed' }))
|
|
66
|
+
}
|
|
67
|
+
let taxonomyLabel = undefined
|
|
68
|
+
if (eventData.provenanceTaxonomy) {
|
|
69
|
+
taxonomyLabel = []
|
|
70
|
+
for (let i = 0; eventData.provenanceTaxonomy.length > i; i++) {
|
|
71
|
+
taxonomyLabel.push(
|
|
72
|
+
await findTaxonomyLabel(
|
|
73
|
+
this.flatmapAPI,
|
|
74
|
+
eventData.provenanceTaxonomy[i]
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let tooltipData = {
|
|
81
|
+
destinations: this.destinations,
|
|
82
|
+
origins: this.origins,
|
|
83
|
+
components: this.components,
|
|
84
|
+
destinationsWithDatasets: this.destinationsWithDatasets,
|
|
85
|
+
originsWithDatasets: this.originsWithDatasets,
|
|
86
|
+
componentsWithDatasets: this.componentsWithDatasets,
|
|
87
|
+
title: eventData.label,
|
|
88
|
+
featureId: eventData.resource,
|
|
89
|
+
hyperlinks: hyperlinks,
|
|
90
|
+
provenanceTaxonomy: eventData.provenanceTaxonomy,
|
|
91
|
+
provenanceTaxonomyLabel: taxonomyLabel,
|
|
92
|
+
}
|
|
93
|
+
return tooltipData
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.createComponentsLabelList = function (components, lookUp) {
|
|
97
|
+
let labelList = []
|
|
98
|
+
components.forEach((n) => {
|
|
99
|
+
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
|
|
100
|
+
if (n.length === 2) {
|
|
101
|
+
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
return labelList
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.createLabelLookup = function (uberons) {
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
let uberonMap = {}
|
|
110
|
+
this.uberons = []
|
|
111
|
+
const data = { sql: this.buildLabelSqlStatement(uberons) }
|
|
112
|
+
fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify(data),
|
|
118
|
+
})
|
|
119
|
+
.then((response) => response.json())
|
|
120
|
+
.then((payload) => {
|
|
121
|
+
const entity = payload.keys.indexOf('entity')
|
|
122
|
+
const label = payload.keys.indexOf('label')
|
|
123
|
+
if (entity > -1 && label > -1) {
|
|
124
|
+
payload.values.forEach((pair) => {
|
|
125
|
+
uberonMap[pair[entity]] = pair[label]
|
|
126
|
+
this.uberons.push({
|
|
127
|
+
id: pair[entity],
|
|
128
|
+
name: pair[label],
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
resolve(uberonMap)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.buildConnectivitySqlStatement = function (keastIds) {
|
|
138
|
+
let sql = 'select knowledge from knowledge where entity in ('
|
|
139
|
+
if (keastIds.length === 1) {
|
|
140
|
+
sql += `'${keastIds[0]}')`
|
|
141
|
+
} else if (keastIds.length > 1) {
|
|
142
|
+
for (let i in keastIds) {
|
|
143
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return sql
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.buildLabelSqlStatement = function (uberons) {
|
|
150
|
+
let sql = 'select entity, label from labels where entity in ('
|
|
151
|
+
if (uberons.length === 1) {
|
|
152
|
+
sql += `'${uberons[0]}')`
|
|
153
|
+
} else if (uberons.length > 1) {
|
|
154
|
+
for (let i in uberons) {
|
|
155
|
+
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return sql
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.findAllIdsFromConnectivity = function (connectivity) {
|
|
162
|
+
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
163
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
164
|
+
let found = []
|
|
165
|
+
nodes.forEach((n) => {
|
|
166
|
+
if (Array.isArray(n)) {
|
|
167
|
+
found.push(n.flat())
|
|
168
|
+
} else {
|
|
169
|
+
found.push(n)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
return [...new Set(found.flat())]
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.flattenConntectivity = function (connectivity) {
|
|
176
|
+
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
177
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
178
|
+
let found = []
|
|
179
|
+
nodes.forEach((n) => {
|
|
180
|
+
if (Array.isArray(n)) {
|
|
181
|
+
found.push(n.flat())
|
|
182
|
+
} else {
|
|
183
|
+
found.push(n)
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
return found.flat()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.findComponents = function (connectivity) {
|
|
190
|
+
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
191
|
+
let nodes = removeDuplicates(dnodes)
|
|
192
|
+
|
|
193
|
+
let found = []
|
|
194
|
+
let terminal = false
|
|
195
|
+
nodes.forEach((node) => {
|
|
196
|
+
terminal = false
|
|
197
|
+
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
198
|
+
if (inArray(connectivity.axons, node)) {
|
|
199
|
+
terminal = true
|
|
200
|
+
}
|
|
201
|
+
if (inArray(connectivity.dendrites, node)) {
|
|
202
|
+
terminal = true
|
|
203
|
+
}
|
|
204
|
+
if (!terminal) {
|
|
205
|
+
found.push(node)
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
return found
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.retrieveFlatmapKnowledgeForEvent = async function (eventData) {
|
|
213
|
+
// check if there is an existing query
|
|
214
|
+
if (this.controller) this.controller.abort()
|
|
215
|
+
|
|
216
|
+
// set up the abort controller
|
|
217
|
+
this.controller = new AbortController()
|
|
218
|
+
const signal = this.controller.signal
|
|
219
|
+
|
|
220
|
+
const keastIds = eventData.resource
|
|
221
|
+
this.destinations = []
|
|
222
|
+
this.origins = []
|
|
223
|
+
this.components = []
|
|
224
|
+
if (!keastIds || keastIds.length == 0) return
|
|
225
|
+
|
|
226
|
+
let prom1 = this.queryForConnectivity(keastIds, signal) // This on returns a promise so dont need 'await'
|
|
227
|
+
let prom2 = await this.pubmedQueryOnIds(eventData)
|
|
228
|
+
let results = await Promise.all([prom1, prom2])
|
|
229
|
+
return results
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this.queryForConnectivity = function (keastIds, signal, processConnectivity=true) {
|
|
233
|
+
const data = { sql: this.buildConnectivitySqlStatement(keastIds) }
|
|
234
|
+
const headers = {
|
|
235
|
+
method: 'POST',
|
|
236
|
+
headers: {
|
|
237
|
+
'Content-Type': 'application/json',
|
|
238
|
+
},
|
|
239
|
+
body: JSON.stringify(data),
|
|
240
|
+
...(signal ? { signal: signal } : {}), // add signal to header if it exists
|
|
241
|
+
}
|
|
242
|
+
return new Promise((resolve) => {
|
|
243
|
+
fetch(`${this.flatmapApi}knowledge/query/`, headers)
|
|
244
|
+
.then((response) => response.json())
|
|
245
|
+
.then((data) => {
|
|
246
|
+
if (this.connectivityExists(data)) {
|
|
247
|
+
let connectivity = JSON.parse(data.values[0][0])
|
|
248
|
+
if (processConnectivity) {
|
|
249
|
+
this.processConnectivity(connectivity).then((processedConnectivity) => {
|
|
250
|
+
resolve(processedConnectivity)
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
else resolve(connectivity)
|
|
254
|
+
} else {
|
|
255
|
+
resolve(false)
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
.catch((error) => {
|
|
259
|
+
console.error('Error:', error)
|
|
260
|
+
resolve(false)
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.connectivityExists = function (data) {
|
|
266
|
+
if (
|
|
267
|
+
data.values &&
|
|
268
|
+
data.values.length > 0 &&
|
|
269
|
+
JSON.parse(data.values[0][0]).connectivity &&
|
|
270
|
+
JSON.parse(data.values[0][0]).connectivity.length > 0
|
|
271
|
+
) {
|
|
272
|
+
return true
|
|
273
|
+
} else {
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// This function is used to determine if a node is a single node or a node with multiple children
|
|
279
|
+
// Returns the id of the node if it is a single node, otherwise returns false
|
|
280
|
+
this.findIfNodeIsSingle = function (node) {
|
|
281
|
+
if (node.length === 1) { // If the node is in the form [id]
|
|
282
|
+
console.error("Server returns a single node", node)
|
|
283
|
+
return node[0]
|
|
284
|
+
} else {
|
|
285
|
+
if (node.length === 2 && node[1].length === 0) { // If the node is in the form [id, []]
|
|
286
|
+
return node[0]
|
|
287
|
+
} else {
|
|
288
|
+
return false // If the node is in the form [id, [id1, id2]]
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.createLabelFromNeuralNode = function (node, lookUp) {
|
|
294
|
+
|
|
295
|
+
// Check if the node is a single node or a node with multiple children
|
|
296
|
+
let nodeIsSingle = this.findIfNodeIsSingle(node)
|
|
297
|
+
|
|
298
|
+
// Case where node is in the form [id]
|
|
299
|
+
if (nodeIsSingle) {
|
|
300
|
+
return lookUp[nodeIsSingle]
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Case where node is in the form [id, [id1 (,id2)]]
|
|
304
|
+
let label = lookUp[node[0]]
|
|
305
|
+
if (node.length === 2 && node[1].length > 0) {
|
|
306
|
+
node[1].forEach((n) => {
|
|
307
|
+
if (lookUp[n] == undefined) {
|
|
308
|
+
label += `, ${n}`
|
|
309
|
+
} else {
|
|
310
|
+
label += `, ${lookUp[n]}`
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
return label
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this.flattenAndFindDatasets = function (components, axons, dendrites) {
|
|
318
|
+
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
319
|
+
let componentsFlat = this.flattenConntectivity(components)
|
|
320
|
+
let axonsFlat = this.flattenConntectivity(axons)
|
|
321
|
+
let dendritesFlat = this.flattenConntectivity(dendrites)
|
|
322
|
+
|
|
323
|
+
// Filter for the anatomy which is annotated on datasets
|
|
324
|
+
this.destinationsWithDatasets = this.uberons.filter(
|
|
325
|
+
(ub) => axonsFlat.indexOf(ub.id) !== -1
|
|
326
|
+
)
|
|
327
|
+
this.originsWithDatasets = this.uberons.filter(
|
|
328
|
+
(ub) => dendritesFlat.indexOf(ub.id) !== -1
|
|
329
|
+
)
|
|
330
|
+
this.componentsWithDatasets = this.uberons.filter(
|
|
331
|
+
(ub) => componentsFlat.indexOf(ub.id) !== -1
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
this.processConnectivity = function (connectivity) {
|
|
336
|
+
return new Promise((resolve) => {
|
|
337
|
+
// Filter the origin and destinations from components
|
|
338
|
+
let components = this.findComponents(connectivity)
|
|
339
|
+
|
|
340
|
+
// Remove duplicates
|
|
341
|
+
let axons = removeDuplicates(connectivity.axons)
|
|
342
|
+
let dendrites = removeDuplicates(connectivity.dendrites)
|
|
343
|
+
|
|
344
|
+
// Create list of ids to get labels for
|
|
345
|
+
let conIds = this.findAllIdsFromConnectivity(connectivity)
|
|
346
|
+
|
|
347
|
+
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
348
|
+
this.createLabelLookup(conIds).then((lookUp) => {
|
|
349
|
+
this.destinations = axons.map((a) =>
|
|
350
|
+
this.createLabelFromNeuralNode(a, lookUp)
|
|
351
|
+
)
|
|
352
|
+
this.origins = dendrites.map((d) =>
|
|
353
|
+
this.createLabelFromNeuralNode(d, lookUp)
|
|
354
|
+
)
|
|
355
|
+
this.components = components.map((c) =>
|
|
356
|
+
this.createLabelFromNeuralNode(c, lookUp)
|
|
357
|
+
)
|
|
358
|
+
this.flattenAndFindDatasets(components, axons, dendrites)
|
|
359
|
+
resolve({
|
|
360
|
+
ids: {
|
|
361
|
+
axons: axons,
|
|
362
|
+
dendrites: dendrites,
|
|
363
|
+
components: components,
|
|
364
|
+
},
|
|
365
|
+
labels: {
|
|
366
|
+
destinations: this.destinations,
|
|
367
|
+
origins: this.origins,
|
|
368
|
+
components: this.components,
|
|
369
|
+
}
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
this.flattenConntectivity = function (connectivity) {
|
|
376
|
+
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
377
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
378
|
+
let found = []
|
|
379
|
+
nodes.forEach((n) => {
|
|
380
|
+
if (Array.isArray(n)) {
|
|
381
|
+
found.push(n.flat())
|
|
382
|
+
} else {
|
|
383
|
+
found.push(n)
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
return found.flat()
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
this.stripPMIDPrefix = function (pubmedId) {
|
|
390
|
+
return pubmedId.split(':')[1]
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
this.buildPubmedSqlStatement = function (keastIds) {
|
|
394
|
+
let sql = 'select distinct publication from publications where entity in ('
|
|
395
|
+
if (keastIds.length === 1) {
|
|
396
|
+
sql += `'${keastIds[0]}')`
|
|
397
|
+
} else if (keastIds.length > 1) {
|
|
398
|
+
for (let i in keastIds) {
|
|
399
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return sql
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
this.buildPubmedSqlStatementForModels = function (model) {
|
|
406
|
+
return `select distinct publication from publications where entity = '${model}'`
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this.flatmapQuery = function (sql) {
|
|
410
|
+
const data = { sql: sql }
|
|
411
|
+
return fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
412
|
+
method: 'POST',
|
|
413
|
+
headers: {
|
|
414
|
+
'Content-Type': 'application/json',
|
|
415
|
+
},
|
|
416
|
+
body: JSON.stringify(data),
|
|
417
|
+
})
|
|
418
|
+
.then((response) => response.json())
|
|
419
|
+
.catch((error) => {
|
|
420
|
+
console.error('Error:', error)
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
// Note that this functin WILL run to the end, as it doesn not catch the second level of promises
|
|
424
|
+
this.pubmedQueryOnIds = function (eventData) {
|
|
425
|
+
return new Promise((resolve) => {
|
|
426
|
+
const keastIds = eventData.resource
|
|
427
|
+
const source = eventData.feature.source
|
|
428
|
+
if (!keastIds || keastIds.length === 0) return
|
|
429
|
+
const sql = this.buildPubmedSqlStatement(keastIds)
|
|
430
|
+
this.flatmapQuery(sql).then((data) => {
|
|
431
|
+
// Create pubmed url on paths if we have them
|
|
432
|
+
if (data.values.length > 0) {
|
|
433
|
+
this.urls = [
|
|
434
|
+
this.pubmedSearchUrl(
|
|
435
|
+
data.values.map((id) => this.stripPMIDPrefix(id[0]))
|
|
436
|
+
),
|
|
437
|
+
]
|
|
438
|
+
resolve(true)
|
|
439
|
+
} else {
|
|
440
|
+
// Create pubmed url on models
|
|
441
|
+
this.pubmedQueryOnModels(source).then((result) => {
|
|
442
|
+
resolve(result)
|
|
443
|
+
})
|
|
444
|
+
}
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
this.pubmedQueryOnModels = function (source) {
|
|
450
|
+
return this.flatmapQuery(
|
|
451
|
+
this.buildPubmedSqlStatementForModels(source)
|
|
452
|
+
).then((data) => {
|
|
453
|
+
if (Array.isArray(data.values) && data.values.length > 0) {
|
|
454
|
+
this.urls = [
|
|
455
|
+
this.pubmedSearchUrl(
|
|
456
|
+
data.values.map((id) => this.stripPMIDPrefix(id[0]))
|
|
457
|
+
),
|
|
458
|
+
]
|
|
459
|
+
return true
|
|
460
|
+
} else {
|
|
461
|
+
this.urls = [] // Clears the pubmed search button
|
|
462
|
+
}
|
|
463
|
+
return false
|
|
464
|
+
})
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
this.pubmedSearchUrl = function (ids) {
|
|
468
|
+
let url = 'https://pubmed.ncbi.nlm.nih.gov/?'
|
|
469
|
+
let params = new URLSearchParams()
|
|
470
|
+
params.append('term', ids)
|
|
471
|
+
return url + params.toString()
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export { FlatmapQueries, findTaxonomyLabel }
|