@abi-software/flatmapvuer 0.4.0 → 0.4.1-beta.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/dist/flatmapvuer.common.js +1634 -1015
- package/dist/flatmapvuer.common.js.map +1 -1
- package/dist/flatmapvuer.css +1 -1
- package/dist/flatmapvuer.umd.js +1634 -1015
- package/dist/flatmapvuer.umd.js.map +1 -1
- package/dist/flatmapvuer.umd.min.js +3 -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 +76 -92
- 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 +381 -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 {
|