@abi-software/flatmapvuer 0.4.0 → 0.4.1-beta.1
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/dist/flatmapvuer.common.js +1790 -864
- package/dist/flatmapvuer.common.js.map +1 -1
- package/dist/flatmapvuer.css +1 -1
- package/dist/flatmapvuer.umd.js +1790 -864
- package/dist/flatmapvuer.umd.js.map +1 -1
- package/dist/flatmapvuer.umd.min.js +4 -1
- package/dist/flatmapvuer.umd.min.js.map +1 -1
- package/package-lock.json +4 -4
- package/package.json +2 -2
- package/src/App.vue +10 -6
- package/src/components/ExternalResourceCard.vue +99 -0
- package/src/components/FlatmapVuer.vue +88 -95
- package/src/components/MultiFlatmapVuer.vue +5 -0
- package/src/components/SelectionsGroup.vue +36 -13
- package/src/components/Tooltip.vue +42 -231
- package/src/services/flatmapQueries.js +328 -0
- package/src/components/PubmedViewer.vue +0 -150
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="tooltip-container">
|
|
3
|
-
<el-main v-if="
|
|
4
|
-
<div class="block" v-if="
|
|
5
|
-
<span class="title">{{capitalise(
|
|
3
|
+
<el-main v-if="entry" class="main" v-loading="loading">
|
|
4
|
+
<div class="block" v-if="entry.title">
|
|
5
|
+
<span class="title">{{capitalise(entry.title)}}</span>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="block" v-else>
|
|
8
|
-
<span class="title">{{
|
|
8
|
+
<span class="title">{{entry.featureId}}</span>
|
|
9
9
|
</div>
|
|
10
10
|
<div class="content-container scrollbar">
|
|
11
|
-
{{
|
|
12
|
-
<div v-if="origins && origins.length > 0" class="block">
|
|
11
|
+
{{entry.paths}}
|
|
12
|
+
<div v-if="entry.origins && entry.origins.length > 0" class="block">
|
|
13
13
|
<div>
|
|
14
14
|
<span class="attribute-title">Origin</span>
|
|
15
15
|
<el-popover
|
|
@@ -24,22 +24,22 @@
|
|
|
24
24
|
</span>
|
|
25
25
|
</el-popover>
|
|
26
26
|
</div>
|
|
27
|
-
<div v-for="(origin, i) in origins" class="attribute-content" :key="origin">
|
|
27
|
+
<div v-for="(origin, i) in entry.origins" class="attribute-content" :key="origin">
|
|
28
28
|
{{ capitalise(origin) }}
|
|
29
|
-
<div v-if="i != origins.length - 1" class="seperator"></div>
|
|
29
|
+
<div v-if="i != entry.origins.length - 1" class="seperator"></div>
|
|
30
30
|
</div>
|
|
31
|
-
<el-button v-show="originsWithDatasets.length > 0" class="button" @click="openDendrites">
|
|
31
|
+
<el-button v-show="entry.originsWithDatasets && entry.originsWithDatasets.length > 0" class="button" @click="openDendrites">
|
|
32
32
|
Explore origin data
|
|
33
33
|
</el-button>
|
|
34
34
|
</div>
|
|
35
|
-
<div v-if="components && components.length > 0" class="block">
|
|
35
|
+
<div v-if="entry.components && entry.components.length > 0" class="block">
|
|
36
36
|
<div class="attribute-title">Components</div>
|
|
37
|
-
<div v-for="(component, i) in components" class="attribute-content" :key="component">
|
|
37
|
+
<div v-for="(component, i) in entry.components" class="attribute-content" :key="component">
|
|
38
38
|
{{ capitalise(component) }}
|
|
39
|
-
<div v-if="i != components.length - 1" class="seperator"></div>
|
|
39
|
+
<div v-if="i != entry.components.length - 1" class="seperator"></div>
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|
|
42
|
-
<div v-if="destinations && destinations.length > 0" class="block">
|
|
42
|
+
<div v-if="entry.destinations && entry.destinations.length > 0" class="block">
|
|
43
43
|
<div>
|
|
44
44
|
<span class="attribute-title">Destination</span>
|
|
45
45
|
<el-popover
|
|
@@ -54,24 +54,21 @@
|
|
|
54
54
|
</span>
|
|
55
55
|
</el-popover>
|
|
56
56
|
</div>
|
|
57
|
-
<div v-for="(destination, i) in destinations" class="attribute-content" :key="destination">
|
|
57
|
+
<div v-for="(destination, i) in entry.destinations" class="attribute-content" :key="destination">
|
|
58
58
|
{{ capitalise(destination) }}
|
|
59
|
-
<div v-if="i != destinations.length - 1" class="seperator"></div>
|
|
59
|
+
<div v-if="i != entry.destinations.length - 1" class="seperator"></div>
|
|
60
60
|
</div>
|
|
61
|
-
<el-button v-show="destinationsWithDatasets.length > 0" class="button" @click="openAxons">
|
|
61
|
+
<el-button v-show="entry.destinationsWithDatasets && entry.destinationsWithDatasets.length > 0" class="button" @click="openAxons">
|
|
62
62
|
Explore destination data
|
|
63
63
|
</el-button>
|
|
64
64
|
</div>
|
|
65
65
|
|
|
66
|
-
<el-button v-show="componentsWithDatasets.length > 0" class="button" @click="openAll">
|
|
66
|
+
<el-button v-show="entry.componentsWithDatasets && entry.componentsWithDatasets.length > 0" class="button" @click="openAll">
|
|
67
67
|
Search for data on components
|
|
68
68
|
</el-button>
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<el-button v-if="pubmedSearchUrl != ''" class="button" icon="el-icon-notebook-2" @click="openUrl(pubmedSearchUrl)">
|
|
73
|
-
Open publications in pubmed
|
|
74
|
-
</el-button>
|
|
70
|
+
<external-resource-card :resources="resources"></external-resource-card>
|
|
71
|
+
|
|
75
72
|
</div>
|
|
76
73
|
</el-main>
|
|
77
74
|
</div>
|
|
@@ -97,25 +94,13 @@ Vue.use(Header);
|
|
|
97
94
|
Vue.use(Icon);
|
|
98
95
|
Vue.use(Main);
|
|
99
96
|
|
|
100
|
-
// pubmedviewer is currently not in use, but still under review so not ready to delete yet
|
|
101
|
-
import PubmedViewer from './PubmedViewer.vue'
|
|
102
97
|
import EventBus from './EventBus'
|
|
98
|
+
import ExternalResourceCard from './ExternalResourceCard.vue';
|
|
103
99
|
|
|
104
100
|
const titleCase = (str) => {
|
|
105
101
|
return str.replace(/\w\S*/g, (t) => { return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase() });
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
const inArray = function(ar1, ar2){
|
|
109
|
-
let as1 = JSON.stringify(ar1)
|
|
110
|
-
let as2 = JSON.stringify(ar2)
|
|
111
|
-
return as1.indexOf(as2) !== -1
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// remove duplicates by stringifying the objects
|
|
115
|
-
const removeDuplicates = function(arrayOfAnything){
|
|
116
|
-
return [...new Set(arrayOfAnything.map(e => JSON.stringify(e)))].map(e => JSON.parse(e))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
104
|
const capitalise = function(str){
|
|
120
105
|
if (str)
|
|
121
106
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
@@ -123,24 +108,34 @@ const capitalise = function(str){
|
|
|
123
108
|
}
|
|
124
109
|
|
|
125
110
|
export default {
|
|
126
|
-
components: {
|
|
111
|
+
components: { ExternalResourceCard },
|
|
127
112
|
name: "Tooltip",
|
|
128
113
|
props: {
|
|
129
114
|
visible: {
|
|
130
115
|
type: Boolean,
|
|
131
116
|
default: false
|
|
132
117
|
},
|
|
133
|
-
|
|
118
|
+
entry: {
|
|
134
119
|
type: Object,
|
|
135
|
-
default:
|
|
120
|
+
default: () => ({
|
|
121
|
+
destinations: [],
|
|
122
|
+
origins: [],
|
|
123
|
+
components: [],
|
|
124
|
+
destinationsWithDatasets: [],
|
|
125
|
+
originsWithDatasets: [],
|
|
126
|
+
componentsWithDatasets: [],
|
|
127
|
+
resource: undefined
|
|
128
|
+
})
|
|
136
129
|
},
|
|
137
130
|
},
|
|
138
131
|
data: function() {
|
|
139
132
|
return {
|
|
133
|
+
controller: undefined,
|
|
140
134
|
activeSpecies: undefined,
|
|
141
135
|
appendToBody: false,
|
|
142
136
|
pubmedSearchUrl: '',
|
|
143
137
|
loading: false,
|
|
138
|
+
showToolip: false,
|
|
144
139
|
destinations: [],
|
|
145
140
|
origins: [],
|
|
146
141
|
components: [],
|
|
@@ -154,20 +149,16 @@ export default {
|
|
|
154
149
|
uberons: [{id: undefined, name: undefined}]
|
|
155
150
|
};
|
|
156
151
|
},
|
|
157
|
-
inject: ['sparcAPI', 'flatmapAPI'],
|
|
158
|
-
watch: {
|
|
159
|
-
'content.featureIds': {
|
|
160
|
-
handler: function(){
|
|
161
|
-
this.pathwayQuery(this.content.featureIds)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
mounted: function(){
|
|
166
|
-
this.getOrganCuries()
|
|
167
|
-
},
|
|
168
152
|
computed: {
|
|
153
|
+
resources: function(){
|
|
154
|
+
let resources = []
|
|
155
|
+
if(this.entry && this.entry.hyperlinks){
|
|
156
|
+
resources = this.entry.hyperlinks
|
|
157
|
+
}
|
|
158
|
+
return resources
|
|
159
|
+
},
|
|
169
160
|
originDescription: function(){
|
|
170
|
-
if(this.
|
|
161
|
+
if(this.entry && this.entry.title && this.entry.title.toLowerCase().includes('motor')){
|
|
171
162
|
return this.originDescriptions.motor
|
|
172
163
|
} else {
|
|
173
164
|
return this.originDescriptions.sensory
|
|
@@ -202,186 +193,6 @@ export default {
|
|
|
202
193
|
pubmedSearchUrlUpdate: function (val){
|
|
203
194
|
this.pubmedSearchUrl = val
|
|
204
195
|
},
|
|
205
|
-
findAllIdsFromConnectivity(connectivity){
|
|
206
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
207
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
208
|
-
let found = []
|
|
209
|
-
nodes.forEach(n=>{
|
|
210
|
-
if (Array.isArray(n)){
|
|
211
|
-
found.push(n.flat())
|
|
212
|
-
} else {
|
|
213
|
-
found.push(n)
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
return [... new Set(found.flat())]
|
|
217
|
-
},
|
|
218
|
-
flattenConntectivity(connectivity){
|
|
219
|
-
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
220
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
221
|
-
let found = []
|
|
222
|
-
nodes.forEach(n=>{
|
|
223
|
-
if (Array.isArray(n)){
|
|
224
|
-
found.push(n.flat())
|
|
225
|
-
} else {
|
|
226
|
-
found.push(n)
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
return found.flat()
|
|
230
|
-
},
|
|
231
|
-
findComponents: function(connectivity){
|
|
232
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
233
|
-
let nodes = removeDuplicates(dnodes)
|
|
234
|
-
|
|
235
|
-
let found = []
|
|
236
|
-
let terminal = false
|
|
237
|
-
nodes.forEach(node=>{
|
|
238
|
-
terminal = false
|
|
239
|
-
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
240
|
-
if(inArray(connectivity.axons,node)){
|
|
241
|
-
terminal = true
|
|
242
|
-
}
|
|
243
|
-
if(inArray(connectivity.dendrites, node)){
|
|
244
|
-
terminal = true
|
|
245
|
-
}
|
|
246
|
-
if (!terminal){
|
|
247
|
-
found.push(node)
|
|
248
|
-
}
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
return found
|
|
252
|
-
},
|
|
253
|
-
getOrganCuries: function(){
|
|
254
|
-
fetch(`${this.sparcAPI}get-organ-curies/`)
|
|
255
|
-
.then(response=>response.json())
|
|
256
|
-
.then(data=>{
|
|
257
|
-
this.uberons = data.uberon.array
|
|
258
|
-
})
|
|
259
|
-
},
|
|
260
|
-
buildConnectivitySqlStatement: function(keastIds) {
|
|
261
|
-
let sql = 'select knowledge from knowledge where entity in ('
|
|
262
|
-
if (keastIds.length === 1) {
|
|
263
|
-
sql += `'${keastIds[0]}')`
|
|
264
|
-
} else if (keastIds.length > 1) {
|
|
265
|
-
for (let i in keastIds) {
|
|
266
|
-
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return sql
|
|
270
|
-
},
|
|
271
|
-
buildLabelSqlStatement: function(uberons) {
|
|
272
|
-
let sql = 'select entity, label from labels where entity in ('
|
|
273
|
-
if (uberons.length === 1) {
|
|
274
|
-
sql += `'${uberons[0]}')`
|
|
275
|
-
} else if (uberons.length > 1) {
|
|
276
|
-
for (let i in uberons) {
|
|
277
|
-
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return sql
|
|
281
|
-
},
|
|
282
|
-
createLabelLookup: function(uberons) {
|
|
283
|
-
return new Promise(resolve=> {
|
|
284
|
-
let uberonMap = {}
|
|
285
|
-
const data = { sql: this.buildLabelSqlStatement(uberons)}
|
|
286
|
-
fetch(`${this.flatmapAPI}knowledge/query/`, {
|
|
287
|
-
method: 'POST',
|
|
288
|
-
headers: {
|
|
289
|
-
'Content-Type': 'application/json',
|
|
290
|
-
},
|
|
291
|
-
body: JSON.stringify(data),
|
|
292
|
-
})
|
|
293
|
-
.then(response => response.json())
|
|
294
|
-
.then(payload => {
|
|
295
|
-
const entity = payload.keys.indexOf("entity");
|
|
296
|
-
const label = payload.keys.indexOf("label");
|
|
297
|
-
if (entity > -1 && label > -1) {
|
|
298
|
-
payload.values.forEach(pair => {
|
|
299
|
-
uberonMap[pair[entity]] = pair[label];
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
resolve(uberonMap)
|
|
303
|
-
})
|
|
304
|
-
})
|
|
305
|
-
},
|
|
306
|
-
createComponentsLabelList: function(components, lookUp){
|
|
307
|
-
let labelList = []
|
|
308
|
-
components.forEach(n=>{
|
|
309
|
-
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
|
|
310
|
-
if (n.length === 2){
|
|
311
|
-
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
return labelList
|
|
315
|
-
},
|
|
316
|
-
createLabelFromNeuralNode: function(node, lookUp){
|
|
317
|
-
let label = lookUp[node[0]]
|
|
318
|
-
if (node.length === 2 && node[1].length > 0){
|
|
319
|
-
node[1].forEach(n=>{
|
|
320
|
-
if (lookUp[n] == undefined){
|
|
321
|
-
label += `, ${n}`
|
|
322
|
-
} else {
|
|
323
|
-
label += `, ${lookUp[n]}`
|
|
324
|
-
}
|
|
325
|
-
})
|
|
326
|
-
}
|
|
327
|
-
return label
|
|
328
|
-
},
|
|
329
|
-
processConnectivity(connectivity){
|
|
330
|
-
// Filter the origin and destinations from components
|
|
331
|
-
let components = this.findComponents(connectivity)
|
|
332
|
-
|
|
333
|
-
// Remove duplicates
|
|
334
|
-
let axons = removeDuplicates(connectivity.axons)
|
|
335
|
-
let dendrites = removeDuplicates(connectivity.dendrites)
|
|
336
|
-
|
|
337
|
-
// Create list of ids to get labels for
|
|
338
|
-
let conIds = this.findAllIdsFromConnectivity(connectivity)
|
|
339
|
-
|
|
340
|
-
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
341
|
-
this.createLabelLookup(conIds).then(lookUp=>{
|
|
342
|
-
this.destinations = axons.map(a=>this.createLabelFromNeuralNode(a,lookUp))
|
|
343
|
-
this.origins = dendrites.map(d=>this.createLabelFromNeuralNode(d,lookUp))
|
|
344
|
-
this.components = components.map(c=>this.createLabelFromNeuralNode(c, lookUp))
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
this.flattenAndFindDatasets(components, axons, dendrites)
|
|
348
|
-
},
|
|
349
|
-
flattenAndFindDatasets(components, axons, dendrites){
|
|
350
|
-
|
|
351
|
-
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
352
|
-
let componentsFlat = this.flattenConntectivity(components)
|
|
353
|
-
let axonsFlat = this.flattenConntectivity(axons)
|
|
354
|
-
let dendritesFlat = this.flattenConntectivity(dendrites)
|
|
355
|
-
|
|
356
|
-
// Filter for the anatomy which is annotated on datasets
|
|
357
|
-
this.destinationsWithDatasets = this.uberons.filter(ub => axonsFlat.indexOf(ub.id) !== -1)
|
|
358
|
-
this.originsWithDatasets = this.uberons.filter(ub => dendritesFlat.indexOf(ub.id) !== -1)
|
|
359
|
-
this.componentsWithDatasets = this.uberons.filter(ub => componentsFlat.indexOf(ub.id) !== -1)
|
|
360
|
-
},
|
|
361
|
-
pathwayQuery: function(keastIds){
|
|
362
|
-
this.destinations = []
|
|
363
|
-
this.origins = []
|
|
364
|
-
this.components = []
|
|
365
|
-
this.loading = true
|
|
366
|
-
if (!keastIds || keastIds.length == 0) return
|
|
367
|
-
const data = { sql: this.buildConnectivitySqlStatement(keastIds)};
|
|
368
|
-
fetch(`${this.flatmapAPI}knowledge/query/`, {
|
|
369
|
-
method: 'POST',
|
|
370
|
-
headers: {
|
|
371
|
-
'Content-Type': 'application/json',
|
|
372
|
-
},
|
|
373
|
-
body: JSON.stringify(data),
|
|
374
|
-
})
|
|
375
|
-
.then(response => response.json())
|
|
376
|
-
.then(data => {
|
|
377
|
-
let connectivity = JSON.parse(data.values[0][0])
|
|
378
|
-
this.processConnectivity(connectivity)
|
|
379
|
-
this.loading = false
|
|
380
|
-
})
|
|
381
|
-
.catch((error) => {
|
|
382
|
-
console.error('Error:', error);
|
|
383
|
-
})
|
|
384
|
-
}
|
|
385
196
|
}
|
|
386
197
|
};
|
|
387
198
|
</script>
|
|
@@ -566,7 +377,7 @@ export default {
|
|
|
566
377
|
.content-container {
|
|
567
378
|
overflow-y: scroll;
|
|
568
379
|
scrollbar-width: thin !important;
|
|
569
|
-
height:
|
|
380
|
+
max-height: 240px;
|
|
570
381
|
}
|
|
571
382
|
|
|
572
383
|
.scrollbar::-webkit-scrollbar-track {
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/* eslint-disable no-alert, no-console */
|
|
2
|
+
// remove duplicates by stringifying the objects
|
|
3
|
+
const removeDuplicates = function (arrayOfAnything) {
|
|
4
|
+
return [...new Set(arrayOfAnything.map(e => JSON.stringify(e)))].map(e => JSON.parse(e))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const inArray = function (ar1, ar2) {
|
|
8
|
+
let as1 = JSON.stringify(ar1)
|
|
9
|
+
let as2 = JSON.stringify(ar2)
|
|
10
|
+
return as1.indexOf(as2) !== -1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
exports.FlatmapQueries = function(sparcApi, flatmapApi) {
|
|
14
|
+
this.sparcApi = sparcApi
|
|
15
|
+
this.flatmapApi = flatmapApi
|
|
16
|
+
this.destinations = []
|
|
17
|
+
this.origins = []
|
|
18
|
+
this.components = []
|
|
19
|
+
this.uberons = []
|
|
20
|
+
this.urls = []
|
|
21
|
+
this.controller = undefined
|
|
22
|
+
|
|
23
|
+
this.createTooltipData = (eventData) => {
|
|
24
|
+
let tooltipData = {
|
|
25
|
+
destinations: this.destinations,
|
|
26
|
+
origins: this.origins,
|
|
27
|
+
components: this.components,
|
|
28
|
+
destinationsWithDatasets: this.destinationsWithDatasets,
|
|
29
|
+
originsWithDatasets: this.originsWithDatasets,
|
|
30
|
+
componentsWithDatasets: this.componentsWithDatasets,
|
|
31
|
+
title: eventData.label,
|
|
32
|
+
featureId: eventData.resource,
|
|
33
|
+
hyperlinks: eventData.feature.hyperlinks ? eventData.feature.hyperlinks : this.urls.map(url => ({ url: url, id: "pubmed" })),
|
|
34
|
+
}
|
|
35
|
+
return tooltipData
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const getOrganCuries = () => {
|
|
39
|
+
return new Promise(resolve => {
|
|
40
|
+
fetch(`${this.sparcAPI}get-organ-curies/`)
|
|
41
|
+
.then(response => response.json())
|
|
42
|
+
.then(data => {
|
|
43
|
+
resolve(data.uberon.array)
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const createLabelLookup = (uberons) => {
|
|
49
|
+
return new Promise(resolve => {
|
|
50
|
+
let uberonMap = {}
|
|
51
|
+
const data = { sql: buildLabelSqlStatement(uberons) }
|
|
52
|
+
fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify(data),
|
|
58
|
+
})
|
|
59
|
+
.then(response => response.json())
|
|
60
|
+
.then(payload => {
|
|
61
|
+
const entity = payload.keys.indexOf("entity");
|
|
62
|
+
const label = payload.keys.indexOf("label");
|
|
63
|
+
if (entity > -1 && label > -1) {
|
|
64
|
+
payload.values.forEach(pair => {
|
|
65
|
+
uberonMap[pair[entity]] = pair[label];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
resolve(uberonMap)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
getOrganCuries().then(uberons => {
|
|
75
|
+
this.uberons = uberons
|
|
76
|
+
createLabelLookup(uberons).then(lookUp => {
|
|
77
|
+
this.lookUp = lookUp
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const buildConnectivitySqlStatement = (keastIds) =>{
|
|
82
|
+
let sql = 'select knowledge from knowledge where entity in ('
|
|
83
|
+
if (keastIds.length === 1) {
|
|
84
|
+
sql += `'${keastIds[0]}')`
|
|
85
|
+
} else if (keastIds.length > 1) {
|
|
86
|
+
for (let i in keastIds) {
|
|
87
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return sql
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const buildLabelSqlStatement = (uberons) => {
|
|
94
|
+
let sql = 'select entity, label from labels where entity in ('
|
|
95
|
+
if (uberons.length === 1) {
|
|
96
|
+
sql += `'${uberons[0]}')`
|
|
97
|
+
} else if (uberons.length > 1) {
|
|
98
|
+
for (let i in uberons) {
|
|
99
|
+
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return sql
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const findAllIdsFromConnectivity = (connectivity) => {
|
|
106
|
+
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
107
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
108
|
+
let found = []
|
|
109
|
+
nodes.forEach(n => {
|
|
110
|
+
if (Array.isArray(n)) {
|
|
111
|
+
found.push(n.flat())
|
|
112
|
+
} else {
|
|
113
|
+
found.push(n)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
return [... new Set(found.flat())]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.retrieveFlatmapKnowledgeForEvent = async (eventData) =>{
|
|
120
|
+
// check if there is an existing query
|
|
121
|
+
if (this.controller) this.controller.abort();
|
|
122
|
+
|
|
123
|
+
// set up the abort controller
|
|
124
|
+
this.controller = new AbortController();
|
|
125
|
+
const signal = this.controller.signal;
|
|
126
|
+
|
|
127
|
+
const keastIds = eventData.resource
|
|
128
|
+
this.destinations = []
|
|
129
|
+
this.origins = []
|
|
130
|
+
this.components = []
|
|
131
|
+
if (!keastIds || keastIds.length == 0) return
|
|
132
|
+
const data = { sql: buildConnectivitySqlStatement(keastIds) };
|
|
133
|
+
let prom1 = new Promise(resolve => {
|
|
134
|
+
fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
135
|
+
method: 'POST',
|
|
136
|
+
headers: {
|
|
137
|
+
'Content-Type': 'application/json',
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(data),
|
|
140
|
+
signal: signal
|
|
141
|
+
})
|
|
142
|
+
.then(response => response.json())
|
|
143
|
+
.then(data => {
|
|
144
|
+
if (connectivityExists(data)) {
|
|
145
|
+
let connectivity = JSON.parse(data.values[0][0])
|
|
146
|
+
processConnectivity(connectivity).then(() => {
|
|
147
|
+
resolve(true)
|
|
148
|
+
})
|
|
149
|
+
} else {
|
|
150
|
+
console.log('No connectivity data found')
|
|
151
|
+
resolve(true)
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
.catch((error) => {
|
|
155
|
+
console.error('Error:', error);
|
|
156
|
+
resolve(false)
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
let prom2 = pubmedQueryOnIds(eventData)
|
|
160
|
+
return await Promise.all([prom1, prom2])
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const connectivityExists = (data) => {
|
|
165
|
+
if (data.values && data.values.length > 0 && JSON.parse(data.values[0][0]).connectivity && JSON.parse(data.values[0][0]).connectivity.length > 0) {
|
|
166
|
+
return true
|
|
167
|
+
} else {
|
|
168
|
+
return false
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const createLabelFromNeuralNode = (node, lookUp) => {
|
|
173
|
+
let label = lookUp[node[0]]
|
|
174
|
+
if (node.length === 2 && node[1].length > 0) {
|
|
175
|
+
node[1].forEach(n => {
|
|
176
|
+
if (lookUp[n] == undefined) {
|
|
177
|
+
label += `, ${n}`
|
|
178
|
+
} else {
|
|
179
|
+
label += `, ${lookUp[n]}`
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
return label
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const flattenAndFindDatasets = (components, axons, dendrites) => {
|
|
187
|
+
|
|
188
|
+
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
189
|
+
let componentsFlat = flattenConntectivity(components)
|
|
190
|
+
let axonsFlat = flattenConntectivity(axons)
|
|
191
|
+
let dendritesFlat = flattenConntectivity(dendrites)
|
|
192
|
+
|
|
193
|
+
// Filter for the anatomy which is annotated on datasets
|
|
194
|
+
this.destinationsWithDatasets = this.uberons.filter(ub => axonsFlat.indexOf(ub.id) !== -1)
|
|
195
|
+
this.originsWithDatasets = this.uberons.filter(ub => dendritesFlat.indexOf(ub.id) !== -1)
|
|
196
|
+
this.componentsWithDatasets = this.uberons.filter(ub => componentsFlat.indexOf(ub.id) !== -1)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const processConnectivity = (connectivity) => {
|
|
200
|
+
return new Promise(resolve => {
|
|
201
|
+
|
|
202
|
+
// Filter the origin and destinations from components
|
|
203
|
+
let components = findComponents(connectivity)
|
|
204
|
+
|
|
205
|
+
// Remove duplicates
|
|
206
|
+
let axons = removeDuplicates(connectivity.axons)
|
|
207
|
+
let dendrites = removeDuplicates(connectivity.dendrites)
|
|
208
|
+
|
|
209
|
+
// Create list of ids to get labels for
|
|
210
|
+
let conIds = findAllIdsFromConnectivity(connectivity)
|
|
211
|
+
|
|
212
|
+
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
213
|
+
createLabelLookup(conIds).then(lookUp => {
|
|
214
|
+
this.destinations = axons.map(a => createLabelFromNeuralNode(a, lookUp))
|
|
215
|
+
this.origins = dendrites.map(d => createLabelFromNeuralNode(d, lookUp))
|
|
216
|
+
this.components = components.map(c => createLabelFromNeuralNode(c, lookUp))
|
|
217
|
+
flattenAndFindDatasets(components, axons, dendrites)
|
|
218
|
+
resolve(true)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const flattenConntectivity = (connectivity) => {
|
|
224
|
+
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
225
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
226
|
+
let found = []
|
|
227
|
+
nodes.forEach(n => {
|
|
228
|
+
if (Array.isArray(n)) {
|
|
229
|
+
found.push(n.flat())
|
|
230
|
+
} else {
|
|
231
|
+
found.push(n)
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
return found.flat()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const findComponents = (connectivity) => {
|
|
238
|
+
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
239
|
+
let nodes = removeDuplicates(dnodes)
|
|
240
|
+
|
|
241
|
+
let found = []
|
|
242
|
+
let terminal = false
|
|
243
|
+
nodes.forEach(node => {
|
|
244
|
+
terminal = false
|
|
245
|
+
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
246
|
+
if (inArray(connectivity.axons, node)) {
|
|
247
|
+
terminal = true
|
|
248
|
+
}
|
|
249
|
+
if (inArray(connectivity.dendrites, node)) {
|
|
250
|
+
terminal = true
|
|
251
|
+
}
|
|
252
|
+
if (!terminal) {
|
|
253
|
+
found.push(node)
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
return found
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const stripPMIDPrefix = (pubmedId) => {
|
|
261
|
+
return pubmedId.split(':')[1]
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const buildPubmedSqlStatement = (keastIds) => {
|
|
265
|
+
let sql = 'select distinct publication from publications where entity in ('
|
|
266
|
+
if (keastIds.length === 1) {
|
|
267
|
+
sql += `'${keastIds[0]}')`
|
|
268
|
+
} else if (keastIds.length > 1) {
|
|
269
|
+
for (let i in keastIds) {
|
|
270
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return sql
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const buildPubmedSqlStatementForModels = (model) => {
|
|
277
|
+
return `select distinct publication from publications where entity = '${model}'`
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const flatmapQuery = (sql) => {
|
|
281
|
+
const data = { sql: sql }
|
|
282
|
+
return fetch(`${this.flatmapApi}knowledge/query/`, {
|
|
283
|
+
method: 'POST',
|
|
284
|
+
headers: {
|
|
285
|
+
'Content-Type': 'application/json',
|
|
286
|
+
},
|
|
287
|
+
body: JSON.stringify(data),
|
|
288
|
+
})
|
|
289
|
+
.then(response => response.json())
|
|
290
|
+
.catch((error) => {
|
|
291
|
+
console.error('Error:', error)
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const pubmedQueryOnIds = (eventData) => {
|
|
296
|
+
const keastIds = eventData.resource
|
|
297
|
+
const source = eventData.feature.source
|
|
298
|
+
if (!keastIds || keastIds.length === 0) return
|
|
299
|
+
const sql = buildPubmedSqlStatement(keastIds)
|
|
300
|
+
return flatmapQuery(sql).then(data => {
|
|
301
|
+
// Create pubmed url on paths if we have them
|
|
302
|
+
if (data.values.length > 0) {
|
|
303
|
+
this.urls = [pubmedSearchUrl(data.values.map(id => stripPMIDPrefix(id[0])))]
|
|
304
|
+
return true
|
|
305
|
+
} else { // Create pubmed url on models
|
|
306
|
+
pubmedQueryOnModels(source).then(() => { return true })
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const pubmedQueryOnModels = (source) => {
|
|
312
|
+
return flatmapQuery(buildPubmedSqlStatementForModels(source)).then(data => {
|
|
313
|
+
if (Array.isArray(data.values) && data.values.length > 0) {
|
|
314
|
+
this.urls = [pubmedSearchUrl(data.values.map(id => stripPMIDPrefix(id[0])))]
|
|
315
|
+
} else {
|
|
316
|
+
this.urls = [] // Clears the pubmed search button
|
|
317
|
+
}
|
|
318
|
+
return
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const pubmedSearchUrl = (ids) => {
|
|
323
|
+
let url = 'https://pubmed.ncbi.nlm.nih.gov/?'
|
|
324
|
+
let params = new URLSearchParams()
|
|
325
|
+
params.append('term', ids)
|
|
326
|
+
return url + params.toString()
|
|
327
|
+
}
|
|
328
|
+
}
|