@abi-software/map-side-bar 2.7.2-beta.2 → 2.7.2-beta.4

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,291 @@
1
+ /**
2
+ * Temporary: flatmapQueries from flatmapVuer
3
+ **/
4
+ const cachedTaxonLabels = [];
5
+ let uberons = [];
6
+
7
+ const query = async function (flatmapApi, sql, params) {
8
+ const url = `${flatmapApi}knowledge/query/`;
9
+ const query = { sql, params };
10
+
11
+ try {
12
+ const response = await fetch(url, {
13
+ method: 'POST',
14
+ headers: {
15
+ "Accept": "application/json; charset=utf-8",
16
+ "Cache-Control": "no-store",
17
+ "Content-Type": "application/json"
18
+ },
19
+ body: JSON.stringify(query)
20
+ });
21
+
22
+ if (!response.ok) {
23
+ throw new Error(`Cannot access ${url}`);
24
+ }
25
+ return await response.json();
26
+ } catch {
27
+ return {
28
+ values: []
29
+ };
30
+ }
31
+ }
32
+
33
+ const removeDuplicates = function (arrayOfAnything) {
34
+ if (!arrayOfAnything) return []
35
+ return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) =>
36
+ JSON.parse(e)
37
+ )
38
+ }
39
+
40
+ const queryLabels = async function (flatmapApi, knowledgeSource, entities) {
41
+ const entityLabels = []
42
+ const entityArray = Array.isArray(entities) ? entities
43
+ : entities ? [entities]
44
+ : []
45
+ if (entityArray.length > 0) {
46
+ const rows = await query(
47
+ flatmapApi,
48
+ `select source, entity, knowledge from knowledge
49
+ where (source=? or source is null)
50
+ and entity in (?${', ?'.repeat(entityArray.length-1)})
51
+ order by entity, source desc`,
52
+ [knowledgeSource, ...entityArray]
53
+ )
54
+ let last_entity = null
55
+ for (const row of rows.values) {
56
+ // In entity, source[desc] order; we use the most recent label
57
+ if (row[1] !== last_entity) {
58
+ const knowledge = JSON.parse(row[2])
59
+ entityLabels.push({
60
+ entity: row[1],
61
+ label: knowledge['label'] || row[1]
62
+ })
63
+ last_entity = row[1]
64
+ }
65
+ }
66
+ }
67
+ return entityLabels
68
+ }
69
+
70
+ const inArray = function (ar1, ar2) {
71
+ if (!ar1 || !ar2) return false
72
+ let as1 = JSON.stringify(ar1)
73
+ let as2 = JSON.stringify(ar2)
74
+ return as1.indexOf(as2) !== -1
75
+ }
76
+
77
+ const findComponents = function (connectivity) {
78
+ let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
79
+ let nodes = removeDuplicates(dnodes)
80
+
81
+ let found = []
82
+ let terminal = false
83
+ nodes.forEach((node) => {
84
+ terminal = false
85
+ // Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
86
+ if (inArray(connectivity.axons, node)) {
87
+ terminal = true
88
+ }
89
+ if (connectivity.somas && inArray(connectivity.somas, node)) {
90
+ terminal = true
91
+ }
92
+ if (inArray(connectivity.dendrites, node)) {
93
+ terminal = true
94
+ }
95
+ if (!terminal) {
96
+ found.push(node)
97
+ }
98
+ })
99
+
100
+ return found
101
+ }
102
+
103
+ const findAllIdsFromConnectivity = function (connectivity) {
104
+ let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
105
+ let nodes = [...new Set(dnodes)] // remove duplicates
106
+ let found = []
107
+ nodes.forEach((n) => {
108
+ if (Array.isArray(n)) {
109
+ found.push(n.flat())
110
+ } else {
111
+ found.push(n)
112
+ }
113
+ })
114
+ return [...new Set(found.flat())]
115
+ }
116
+
117
+ const findTaxonomyLabels = async function (flatmapApi, knowledgeSource, taxonomies) {
118
+ const intersectionTaxonomies = taxonomies.filter((taxonomy) =>
119
+ cachedTaxonLabels.some((obj) => obj.taxon === taxonomy)
120
+ );
121
+
122
+ const foundCachedTaxonLabels = cachedTaxonLabels.filter((obj) =>
123
+ intersectionTaxonomies.includes(obj.taxon)
124
+ );
125
+
126
+ const leftoverTaxonomies = taxonomies.filter((taxonomy) =>
127
+ !intersectionTaxonomies.includes(taxonomy)
128
+ );
129
+
130
+ if (!leftoverTaxonomies.length) {
131
+ return foundCachedTaxonLabels;
132
+ } else {
133
+ const entityLabels = await queryLabels(flatmapApi, knowledgeSource, leftoverTaxonomies);
134
+ if (entityLabels.length) {
135
+ entityLabels.forEach((entityLabel) => {
136
+ let { entity: taxon, label } = entityLabel;
137
+ if (label === 'Mammalia') {
138
+ label = 'Mammalia not otherwise specified'
139
+ }
140
+ const item = { taxon, label };
141
+ foundCachedTaxonLabels.push(item);
142
+ cachedTaxonLabels.push(item);
143
+ });
144
+ return foundCachedTaxonLabels;
145
+ }
146
+ }
147
+ }
148
+
149
+ const createLabelLookup = function (flatmapApi, knowledgeSource, _uberons) {
150
+ return new Promise(async (resolve) => {
151
+ let uberonMap = {}
152
+ uberons = []
153
+ const entityLabels = await findTaxonomyLabels(flatmapApi, knowledgeSource, _uberons);
154
+ if (entityLabels.length) {
155
+ entityLabels.forEach((entityLabel) => {
156
+ const { taxon: entity, label } = entityLabel;
157
+ uberonMap[entity] = label;
158
+ uberons.push({
159
+ id: entity,
160
+ name: label,
161
+ })
162
+ });
163
+ resolve(uberonMap)
164
+ }
165
+ })
166
+ }
167
+
168
+ const findIfNodeIsSingle = function (node) {
169
+ if (node.length === 1) { // If the node is in the form [id]
170
+ console.error("Server returns a single node", node)
171
+ return node[0]
172
+ } else {
173
+ if (node.length === 2 && node[1].length === 0) { // If the node is in the form [id, []]
174
+ return node[0]
175
+ } else {
176
+ return false // If the node is in the form [id, [id1, id2]]
177
+ }
178
+ }
179
+ }
180
+
181
+ const createLabelFromNeuralNode = function (node, lookUp) {
182
+
183
+ // Check if the node is a single node or a node with multiple children
184
+ let nodeIsSingle = findIfNodeIsSingle(node)
185
+
186
+ // Case where node is in the form [id]
187
+ if (nodeIsSingle) {
188
+ return lookUp[nodeIsSingle]
189
+ }
190
+
191
+ // Case where node is in the form [id, [id1 (,id2)]]
192
+ let label = lookUp[node[0]]
193
+ if (node.length === 2 && node[1].length > 0) {
194
+ node[1].forEach((n) => {
195
+ if (lookUp[n] == undefined) {
196
+ label += `, ${n}`
197
+ } else {
198
+ label += `, ${lookUp[n]}`
199
+ }
200
+ })
201
+ }
202
+ return label
203
+ }
204
+
205
+ const flattenConntectivity = function (connectivity) {
206
+ let dnodes = 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 found.flat()
217
+ }
218
+
219
+ const flattenAndFindDatasets = function (components, axons, dendrites) {
220
+ // process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
221
+ let componentsFlat = flattenConntectivity(components)
222
+ let axonsFlat = flattenConntectivity(axons)
223
+ let dendritesFlat = flattenConntectivity(dendrites)
224
+
225
+ // Filter for the anatomy which is annotated on datasets
226
+ const destinationsWithDatasets = uberons.filter(
227
+ (ub) => axonsFlat.indexOf(ub.id) !== -1
228
+ )
229
+ const originsWithDatasets = uberons.filter(
230
+ (ub) => dendritesFlat.indexOf(ub.id) !== -1
231
+ )
232
+ const componentsWithDatasets = uberons.filter(
233
+ (ub) => componentsFlat.indexOf(ub.id) !== -1
234
+ )
235
+
236
+ return {
237
+ destinationsWithDatasets,
238
+ originsWithDatasets,
239
+ componentsWithDatasets
240
+ }
241
+ }
242
+
243
+ export const processConnectivity = async function (flatmapApi, knowledgeSource, connectivity) {
244
+ return new Promise((resolve) => {
245
+ // Filter the origin and destinations from components
246
+ let components = findComponents(connectivity)
247
+
248
+ // Remove duplicates
249
+ let axons = removeDuplicates(connectivity.axons)
250
+ //Somas will become part of origins, support this for future proof
251
+ let dendrites = []
252
+ if (connectivity.somas && connectivity.somas.length > 0) {
253
+ dendrites.push(...connectivity.somas)
254
+ }
255
+ if (connectivity.dendrites && connectivity.dendrites.length > 0) {
256
+ dendrites.push(...connectivity.dendrites)
257
+ }
258
+ dendrites = removeDuplicates(dendrites)
259
+
260
+ // Create list of ids to get labels for
261
+ let conIds = findAllIdsFromConnectivity(connectivity)
262
+
263
+ // Create readable labels from the nodes. Setting this to 'this.origins' updates the display
264
+ createLabelLookup(flatmapApi, knowledgeSource, conIds).then((lookUp) => {
265
+ const _destinations = axons.map((a) =>
266
+ createLabelFromNeuralNode(a, lookUp)
267
+ )
268
+ const _origins = dendrites.map((d) =>
269
+ createLabelFromNeuralNode(d, lookUp)
270
+ )
271
+ const _components = components.map((c) =>
272
+ createLabelFromNeuralNode(c, lookUp)
273
+ )
274
+ const withDatasets = flattenAndFindDatasets(components, axons, dendrites)
275
+ const result = {
276
+ ids: {
277
+ axons: axons,
278
+ dendrites: dendrites,
279
+ components: components,
280
+ },
281
+ labels: {
282
+ destinations: _destinations,
283
+ origins: _origins,
284
+ components: _components,
285
+ },
286
+ withDatasets
287
+ }
288
+ resolve(result);
289
+ })
290
+ })
291
+ }
@@ -9,14 +9,32 @@ declare module 'vue' {
9
9
  export interface GlobalComponents {
10
10
  AnnotationTool: typeof import('./components/AnnotationTool.vue')['default']
11
11
  BadgesGroup: typeof import('./components/BadgesGroup.vue')['default']
12
- ConnectivityCard: typeof import('./components/ConnectivityCard.vue')['default']
13
- ConnectivityExplorer: typeof import('./components/ConnectivityExplorer.vue')['default']
14
12
  ConnectivityInfo: typeof import('./components/ConnectivityInfo.vue')['default']
15
13
  DatasetCard: typeof import('./components/DatasetCard.vue')['default']
14
+ ElButton: typeof import('element-plus/es')['ElButton']
15
+ ElCard: typeof import('element-plus/es')['ElCard']
16
+ ElCascader: typeof import('element-plus/es')['ElCascader']
17
+ ElCol: typeof import('element-plus/es')['ElCol']
16
18
  ElDrawer: typeof import('element-plus/es')['ElDrawer']
19
+ ElDropdown: typeof import('element-plus/es')['ElDropdown']
20
+ ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
21
+ ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
17
22
  ElIcon: typeof import('element-plus/es')['ElIcon']
23
+ ElIconArrowDown: typeof import('@element-plus/icons-vue')['ArrowDown']
18
24
  ElIconArrowLeft: typeof import('@element-plus/icons-vue')['ArrowLeft']
19
25
  ElIconArrowRight: typeof import('@element-plus/icons-vue')['ArrowRight']
26
+ ElIconDelete: typeof import('@element-plus/icons-vue')['Delete']
27
+ ElIconLocation: typeof import('@element-plus/icons-vue')['Location']
28
+ ElIconWarnTriangleFilled: typeof import('@element-plus/icons-vue')['WarnTriangleFilled']
29
+ ElInput: typeof import('element-plus/es')['ElInput']
30
+ ElOption: typeof import('element-plus/es')['ElOption']
31
+ ElPagination: typeof import('element-plus/es')['ElPagination']
32
+ ElPopover: typeof import('element-plus/es')['ElPopover']
33
+ ElRadio: typeof import('element-plus/es')['ElRadio']
34
+ ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
35
+ ElRow: typeof import('element-plus/es')['ElRow']
36
+ ElSelect: typeof import('element-plus/es')['ElSelect']
37
+ ElTag: typeof import('element-plus/es')['ElTag']
20
38
  ImageGallery: typeof import('./components/ImageGallery.vue')['default']
21
39
  SearchFilters: typeof import('./components/SearchFilters.vue')['default']
22
40
  SearchHistory: typeof import('./components/SearchHistory.vue')['default']
@@ -24,4 +42,7 @@ declare module 'vue' {
24
42
  SidebarContent: typeof import('./components/SidebarContent.vue')['default']
25
43
  Tabs: typeof import('./components/Tabs.vue')['default']
26
44
  }
45
+ export interface ComponentCustomProperties {
46
+ vLoading: typeof import('element-plus/es')['ElLoadingDirective']
47
+ }
27
48
  }
@@ -287,4 +287,4 @@ export default {
287
287
  "Domestic Pig"
288
288
  ],
289
289
  "knowledge-source": "sckan-2024-03-26-npo",
290
- }
290
+ }
@@ -1,78 +0,0 @@
1
- <template>
2
- <div class="connectivity-card-container" ref="container">
3
- <div class="connectivity-card" ref="card">
4
- <div class="seperator-path"></div>
5
- <div class="card" @click="cardClicked(entry)">
6
- <div class="title">{{ entry.label }}</div>
7
- <template v-for="field in displayFields" :key="field">
8
- <div class="details" v-if="entry[field]">
9
- <strong>{{ field }}:</strong> {{ entry[field] }}
10
- </div>
11
- </template>
12
- </div>
13
- </div>
14
- </div>
15
- </template>
16
-
17
- <script>
18
- export default {
19
- name: "ConnectivityCard",
20
- data() {
21
- return {
22
- displayFields: ["id"],
23
- };
24
- },
25
- props: {
26
- /**
27
- * Object containing information for
28
- * the required viewing.
29
- */
30
- entry: {
31
- type: Object,
32
- default: () => {},
33
- },
34
- },
35
- methods: {
36
- cardClicked: function (data) {
37
- this.$emit("connectivity-explorer-clicked", data);
38
- },
39
- },
40
- };
41
- </script>
42
-
43
- <style lang="scss" scoped>
44
- .connectivity-card {
45
- padding-left: 5px;
46
- position: relative;
47
- min-height: 5rem;
48
- }
49
-
50
- .card {
51
- padding-top: 18px;
52
- padding-left: 6px;
53
- cursor: pointer;
54
- }
55
-
56
- .title {
57
- padding-bottom: 0.75rem;
58
- font-weight: bold;
59
- line-height: 1.5;
60
- letter-spacing: 1.05px;
61
- }
62
-
63
- .details {
64
- line-height: 1.5;
65
- letter-spacing: 1.05px;
66
- }
67
-
68
- .button {
69
- margin-right: 3.5rem;
70
- font-family: Asap;
71
- font-size: 14px;
72
- font-weight: normal;
73
- background-color: $app-primary-color;
74
- border: $app-primary-color;
75
- color: white;
76
- margin-top: 8px;
77
- }
78
- </style>