@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.
@@ -0,0 +1,381 @@
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
+ export class FlatmapQueries {
14
+ constructor(sparcApi, flatmapApi) {
15
+ this.sparcApi = sparcApi
16
+ this.flatmapApi = flatmapApi
17
+ this.destinations = []
18
+ this.origins = []
19
+ this.components = []
20
+ this.uberons = []
21
+ this.urls = []
22
+ this.controller = undefined
23
+ this.getOrganCuries().then(uberons=>{
24
+ this.uberons = uberons
25
+ this.createLabelLookup(uberons).then(lookUp=>{
26
+ this.lookUp = lookUp
27
+ })
28
+ })
29
+ }
30
+
31
+ createTooltipData = function (eventData) {
32
+ let tooltipData = {
33
+ destinations: this.destinations,
34
+ origins: this.origins,
35
+ components: this.components,
36
+ destinationsWithDatasets: this.destinationsWithDatasets,
37
+ originsWithDatasets: this.originsWithDatasets,
38
+ componentsWithDatasets: this.componentsWithDatasets,
39
+ title: eventData.label,
40
+ featureId: eventData.resource,
41
+ hyperlinks: eventData.feature.hyperlinks ? eventData.feature.hyperlinks : this.urls.map(url=>({url: url, id: "pubmed"})),
42
+ }
43
+ return tooltipData
44
+ }
45
+
46
+ getOrganCuries = function(){
47
+ return new Promise(resolve=> {
48
+ fetch(`${this.sparcAPI}get-organ-curies/`)
49
+ .then(response=>response.json())
50
+ .then(data=>{
51
+ resolve(data.uberon.array)
52
+ })
53
+ })
54
+ }
55
+
56
+ createComponentsLabelList = function(components, lookUp){
57
+ let labelList = []
58
+ components.forEach(n=>{
59
+ labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
60
+ if (n.length === 2){
61
+ labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
62
+ }
63
+ })
64
+ return labelList
65
+ }
66
+
67
+ createLabelLookup = function(uberons) {
68
+ return new Promise(resolve=> {
69
+ let uberonMap = {}
70
+ const data = { sql: this.buildLabelSqlStatement(uberons)}
71
+ fetch(`${this.flatmapApi}knowledge/query/`, {
72
+ method: 'POST',
73
+ headers: {
74
+ 'Content-Type': 'application/json',
75
+ },
76
+ body: JSON.stringify(data),
77
+ })
78
+ .then(response => response.json())
79
+ .then(payload => {
80
+ const entity = payload.keys.indexOf("entity");
81
+ const label = payload.keys.indexOf("label");
82
+ if (entity > -1 && label > -1) {
83
+ payload.values.forEach(pair => {
84
+ uberonMap[pair[entity]] = pair[label];
85
+ });
86
+ }
87
+ resolve(uberonMap)
88
+ })
89
+ })
90
+ }
91
+
92
+ buildConnectivitySqlStatement = function (keastIds) {
93
+ let sql = 'select knowledge from knowledge where entity in ('
94
+ if (keastIds.length === 1) {
95
+ sql += `'${keastIds[0]}')`
96
+ } else if (keastIds.length > 1) {
97
+ for (let i in keastIds) {
98
+ sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
99
+ }
100
+ }
101
+ return sql
102
+ }
103
+ buildLabelSqlStatement = function (uberons) {
104
+ let sql = 'select entity, label from labels where entity in ('
105
+ if (uberons.length === 1) {
106
+ sql += `'${uberons[0]}')`
107
+ } else if (uberons.length > 1) {
108
+ for (let i in uberons) {
109
+ sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
110
+ }
111
+ }
112
+ return sql
113
+ }
114
+
115
+ findAllIdsFromConnectivity = function (connectivity) {
116
+ let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
117
+ let nodes = [...new Set(dnodes)] // remove duplicates
118
+ let found = []
119
+ nodes.forEach(n => {
120
+ if (Array.isArray(n)) {
121
+ found.push(n.flat())
122
+ } else {
123
+ found.push(n)
124
+ }
125
+ })
126
+ return [... new Set(found.flat())]
127
+ }
128
+
129
+ flattenConntectivity = function (connectivity) {
130
+ let dnodes = connectivity.flat() // get nodes from edgelist
131
+ let nodes = [...new Set(dnodes)] // remove duplicates
132
+ let found = []
133
+ nodes.forEach(n => {
134
+ if (Array.isArray(n)) {
135
+ found.push(n.flat())
136
+ } else {
137
+ found.push(n)
138
+ }
139
+ })
140
+ return found.flat()
141
+ }
142
+
143
+ findComponents = function (connectivity) {
144
+ let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
145
+ let nodes = removeDuplicates(dnodes)
146
+
147
+ let found = []
148
+ let terminal = false
149
+ nodes.forEach(node => {
150
+ terminal = false
151
+ // Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
152
+ if (inArray(connectivity.axons, node)) {
153
+ terminal = true
154
+ }
155
+ if (inArray(connectivity.dendrites, node)) {
156
+ terminal = true
157
+ }
158
+ if (!terminal) {
159
+ found.push(node)
160
+ }
161
+ })
162
+
163
+ return found
164
+ }
165
+
166
+ retrieveFlatmapKnowledgeForEvent = async function(eventData){
167
+ // check if there is an existing query
168
+ if (this.controller) this.controller.abort();
169
+
170
+ // set up the abort controller
171
+ this.controller = new AbortController();
172
+ const signal = this.controller.signal;
173
+
174
+ const keastIds = eventData.resource
175
+ this.destinations = []
176
+ this.origins = []
177
+ this.components = []
178
+ if (!keastIds || keastIds.length == 0) return
179
+ const data = { sql: this.buildConnectivitySqlStatement(keastIds)};
180
+ let prom1 = new Promise(resolve=>{
181
+ fetch(`${this.flatmapApi}knowledge/query/`, {
182
+ method: 'POST',
183
+ headers: {
184
+ 'Content-Type': 'application/json',
185
+ },
186
+ body: JSON.stringify(data),
187
+ signal: signal
188
+ })
189
+ .then(response => response.json())
190
+ .then(data => {
191
+ if(this.connectivityExists(data)){
192
+ let connectivity = JSON.parse(data.values[0][0])
193
+ this.processConnectivity(connectivity).then(()=>{
194
+ resolve(true)
195
+ })
196
+ } else {
197
+ console.log('No connectivity data found')
198
+ resolve(true)
199
+ }
200
+ })
201
+ .catch((error) => {
202
+ console.error('Error:', error);
203
+ resolve(false)
204
+ })
205
+ })
206
+ let prom2 = this.pubmedQueryOnIds(eventData)
207
+ return await Promise.all([prom1, prom2])
208
+
209
+ }
210
+
211
+ connectivityExists = function(data){
212
+ if (data.values && data.values.length > 0 && JSON.parse(data.values[0][0]).connectivity && JSON.parse(data.values[0][0]).connectivity.length > 0) {
213
+ return true
214
+ } else {
215
+ return false
216
+ }
217
+ }
218
+
219
+ createLabelFromNeuralNode = function(node, lookUp){
220
+ let label = lookUp[node[0]]
221
+ if (node.length === 2 && node[1].length > 0){
222
+ node[1].forEach(n=>{
223
+ if (lookUp[n] == undefined){
224
+ label += `, ${n}`
225
+ } else {
226
+ label += `, ${lookUp[n]}`
227
+ }
228
+ })
229
+ }
230
+ return label
231
+ }
232
+
233
+ flattenAndFindDatasets(components, axons, dendrites){
234
+
235
+ // process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
236
+ let componentsFlat = this.flattenConntectivity(components)
237
+ let axonsFlat = this.flattenConntectivity(axons)
238
+ let dendritesFlat = this.flattenConntectivity(dendrites)
239
+
240
+ // Filter for the anatomy which is annotated on datasets
241
+ this.destinationsWithDatasets = this.uberons.filter(ub => axonsFlat.indexOf(ub.id) !== -1)
242
+ this.originsWithDatasets = this.uberons.filter(ub => dendritesFlat.indexOf(ub.id) !== -1)
243
+ this.componentsWithDatasets = this.uberons.filter(ub => componentsFlat.indexOf(ub.id) !== -1)
244
+ }
245
+
246
+ processConnectivity(connectivity){
247
+ return new Promise (resolve=>{
248
+
249
+ // Filter the origin and destinations from components
250
+ let components = this.findComponents(connectivity)
251
+
252
+ // Remove duplicates
253
+ let axons = removeDuplicates(connectivity.axons)
254
+ let dendrites = removeDuplicates(connectivity.dendrites)
255
+
256
+ // Create list of ids to get labels for
257
+ let conIds = this.findAllIdsFromConnectivity(connectivity)
258
+
259
+ // Create readable labels from the nodes. Setting this to 'this.origins' updates the display
260
+ this.createLabelLookup(conIds).then(lookUp=>{
261
+ this.destinations = axons.map(a=>this.createLabelFromNeuralNode(a,lookUp))
262
+ this.origins = dendrites.map(d=>this.createLabelFromNeuralNode(d,lookUp))
263
+ this.components = components.map(c=>this.createLabelFromNeuralNode(c, lookUp))
264
+ this.flattenAndFindDatasets(components, axons, dendrites)
265
+ resolve(true)
266
+ })
267
+ })
268
+ }
269
+
270
+ flattenConntectivity(connectivity){
271
+ let dnodes = connectivity.flat() // get nodes from edgelist
272
+ let nodes = [...new Set(dnodes)] // remove duplicates
273
+ let found = []
274
+ nodes.forEach(n=>{
275
+ if (Array.isArray(n)){
276
+ found.push(n.flat())
277
+ } else {
278
+ found.push(n)
279
+ }
280
+ })
281
+ return found.flat()
282
+ }
283
+
284
+ findComponents = function(connectivity){
285
+ let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
286
+ let nodes = removeDuplicates(dnodes)
287
+
288
+ let found = []
289
+ let terminal = false
290
+ nodes.forEach(node=>{
291
+ terminal = false
292
+ // Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
293
+ if(inArray(connectivity.axons,node)){
294
+ terminal = true
295
+ }
296
+ if(inArray(connectivity.dendrites, node)){
297
+ terminal = true
298
+ }
299
+ if (!terminal){
300
+ found.push(node)
301
+ }
302
+ })
303
+
304
+ return found
305
+ }
306
+
307
+ stripPMIDPrefix = function (pubmedId){
308
+ return pubmedId.split(':')[1]
309
+ }
310
+
311
+ buildPubmedSqlStatement = function(keastIds) {
312
+ let sql = 'select distinct publication from publications where entity in ('
313
+ if (keastIds.length === 1) {
314
+ sql += `'${keastIds[0]}')`
315
+ } else if (keastIds.length > 1) {
316
+ for (let i in keastIds) {
317
+ sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
318
+ }
319
+ }
320
+ return sql
321
+ }
322
+
323
+ buildPubmedSqlStatementForModels = function(model) {
324
+ return `select distinct publication from publications where entity = '${model}'`
325
+ }
326
+
327
+ flatmapQuery = function(sql){
328
+ const data = { sql: sql}
329
+ return fetch(`${this.flatmapApi}knowledge/query/`, {
330
+ method: 'POST',
331
+ headers: {
332
+ 'Content-Type': 'application/json',
333
+ },
334
+ body: JSON.stringify(data),
335
+ })
336
+ .then(response => response.json())
337
+ .catch((error) => {
338
+ console.error('Error:', error)
339
+ })
340
+ }
341
+
342
+ pubmedQueryOnIds = function(eventData){
343
+ const keastIds = eventData.resource
344
+ const source = eventData.feature.source
345
+ if(!keastIds || keastIds.length === 0) return
346
+ const sql = this.buildPubmedSqlStatement(keastIds)
347
+ return this.flatmapQuery(sql).then(data=>{
348
+ // Create pubmed url on paths if we have them
349
+ if (data.values.length > 0){
350
+ this.urls = [this.pubmedSearchUrl(data.values.map(id=>this.stripPMIDPrefix(id[0])))]
351
+ return true
352
+ } else { // Create pubmed url on models
353
+ this.pubmedQueryOnModels(source).then(()=>{return true})
354
+ }
355
+ })
356
+ }
357
+
358
+ pubmedQueryOnModels = function(source){
359
+ return this.flatmapQuery(this.buildPubmedSqlStatementForModels(source)).then(data=>{
360
+ if (Array.isArray(data.values) && data.values.length > 0){
361
+ this.urls = [this.pubmedSearchUrl(data.values.map(id=>this.stripPMIDPrefix(id[0])))]
362
+ } else {
363
+ this.urls = [] // Clears the pubmed search button
364
+ }
365
+ return
366
+ })
367
+ }
368
+
369
+ pubmedSearchUrl = function(ids) {
370
+ let url = 'https://pubmed.ncbi.nlm.nih.gov/?'
371
+ let params = new URLSearchParams()
372
+ params.append('term', ids)
373
+ return url + params.toString()
374
+ }
375
+ }
376
+
377
+
378
+
379
+
380
+
381
+ export default FlatmapQueries
@@ -1,150 +0,0 @@
1
- <template>
2
- <div class="pubmed-container">
3
- <!-- To view old pubmed display go to: https://github.com/Tehsurfer/flatmapvuer/commit/eca131f8d32cdcac4d136d1722d7fe4df25f6c3a -->
4
- </div>
5
- </template>
6
-
7
-
8
- <script>
9
- /* eslint-disable no-alert, no-console */
10
-
11
- export default {
12
- name: "Tooltip",
13
- props: {
14
- entry: {
15
- type: Object,
16
- default: () => {}
17
- },
18
- },
19
- watch: {
20
- 'entry.featureIds': {
21
- handler: function(ids) {
22
- this.pubmedQueryOnIds(ids)
23
- }
24
- }
25
- },
26
- inject: ['flatmapAPI'],
27
- data: function() {
28
- return {
29
- pubmeds: [],
30
- pubmedIds: [],
31
- };
32
- },
33
- mounted: function() {
34
- if (this.entry.featureIds)
35
- this.pubmedQueryOnIds(this.entry.featureIds)
36
- },
37
- methods: {
38
- stripPMIDPrefix: function (pubmedId){
39
- return pubmedId.split(':')[1]
40
- },
41
- buildPubmedSqlStatement: function(keastIds) {
42
- let sql = 'select distinct publication from publications where entity in ('
43
- if (keastIds.length === 1) {
44
- sql += `'${keastIds[0]}')`
45
- } else if (keastIds.length > 1) {
46
- for (let i in keastIds) {
47
- sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
48
- }
49
- }
50
- return sql
51
- },
52
- buildPubmedSqlStatementForModels: function(model) {
53
- return `select distinct publication from publications where entity = '${model}'`
54
- },
55
- flatmapQuery: function(sql){
56
- const data = { sql: sql}
57
- return fetch(`${this.flatmapAPI}knowledge/query/`, {
58
- method: 'POST',
59
- headers: {
60
- 'Content-Type': 'application/json',
61
- },
62
- body: JSON.stringify(data),
63
- })
64
- .then(response => response.json())
65
- .catch((error) => {
66
- console.error('Error:', error)
67
- })
68
- },
69
- pubmedQueryOnIds: function(keastIds){
70
- if(!keastIds || keastIds.length === 0) return
71
- const sql = this.buildPubmedSqlStatement(keastIds)
72
- this.flatmapQuery(sql).then(data=>{
73
- this.responseData = data
74
- // Create pubmed url on paths if we have them
75
- if (data.values.length > 0){
76
- this.$emit('pubmedSearchUrl', this.pubmedSearchUrl(data.values.map(id=>this.stripPMIDPrefix(id[0]))))
77
- } else { // Create pubmed url on models
78
- this.pubmedQueryOnModels(this.entry.source)
79
- }
80
- })
81
- },
82
- pubmedQueryOnModels(source){
83
- this.flatmapQuery(this.buildPubmedSqlStatementForModels(source)).then(data=>{
84
- if (Array.isArray(data.values) && data.values.length > 0){
85
- this.$emit('pubmedSearchUrl', this.pubmedSearchUrl(data.values.map(id=>this.stripPMIDPrefix(id[0]))))
86
- } else {
87
- this.$emit('pubmedSearchUrl', '') // Clears the pubmed search button
88
- }
89
- })
90
- },
91
- pubmedSearchUrl: function(ids) {
92
- let url = 'https://pubmed.ncbi.nlm.nih.gov/?'
93
- let params = new URLSearchParams()
94
- params.append('term', ids)
95
- return url + params.toString()
96
- }
97
- }
98
- };
99
- </script>
100
-
101
- <style scoped lang="scss">
102
- @import "~element-ui/packages/theme-chalk/src/link";
103
- @import "~element-ui/packages/theme-chalk/src/carousel";
104
- @import "~element-ui/packages/theme-chalk/src/carousel-item";
105
-
106
- .attribute-title{
107
- font-size: 16px;
108
- font-weight: 600;
109
- /* font-weight: bold; */
110
- text-transform: uppercase;
111
- }
112
-
113
- .attribute-content{
114
- font-size: 14px;
115
- font-weight: 400;
116
- }
117
-
118
- .el-link {
119
- color: $app-primary-color;
120
- text-decoration: none;
121
- word-wrap: break-word;
122
- &:hover, &:focus{
123
- color: $app-primary-color;
124
- text-decoration: underline;
125
- }
126
- }
127
-
128
- ::v-deep .el-carousel__button {
129
- background-color: $app-primary-color;
130
- }
131
-
132
- .button {
133
- margin-left: 0px !important;
134
- margin-top: 0px !important;
135
- font-size: 14px !important;
136
- background-color: $app-primary-color;
137
- color: #fff;
138
- &:hover{
139
- color: #fff !important;
140
- background: #ac76c5 !important;
141
- border: 1px solid #ac76c5 !important;
142
- }
143
- &+.button {
144
- margin-top: 10px !important;
145
- background-color: $app-primary-color;
146
- color: #fff;
147
- }
148
- }
149
-
150
- </style>