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