@abi-software/map-side-bar 1.1.2 → 1.1.5-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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/map-side-bar",
3
- "version": "1.1.2",
3
+ "version": "1.1.5-beta-1",
4
4
  "main": "./dist/map-side-bar.common.js",
5
5
  "files": [
6
6
  "dist/*",
@@ -18,6 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@abi-software/svg-sprite": "^0.1.12",
21
+ "algoliasearch": "^4.10.5",
21
22
  "element-ui": "^2.13.0",
22
23
  "vue": "^2.6.10"
23
24
  },
@@ -32,6 +33,7 @@
32
33
  "file-loader": "^5.0.2",
33
34
  "raw-loader": "^0.5.1",
34
35
  "transform-loader": "^0.2.4",
36
+ "typescript": "^4.4.3",
35
37
  "vue-custom-element": "^3.3.0",
36
38
  "vue-template-compiler": "^2.6.10",
37
39
  "webpack-node-externals": "^2.5.2"
package/public/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
6
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
7
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
8
- <title>scaffoldvuer</title>
8
+ <title>Map Sidebar</title>
9
9
  </head>
10
10
  <body>
11
11
  <noscript>
package/src/App.vue CHANGED
@@ -4,9 +4,10 @@
4
4
  href="https://fonts.googleapis.com/css?family=Asap:400,400i,500,600,700&display=swap">
5
5
  Click arrow to open sidebar
6
6
  <el-button @click="openSearch">search 'heart' from refs</el-button>
7
+ <el-button @click="singleFacets">Add to Filter</el-button>
7
8
  <el-button @click="multiFacets">multiple facets</el-button>
8
9
  <el-button @click="neuronSearch">open neuron search</el-button>
9
- <SideBar class="side-bar" ref="sideBar" :apiLocation="apiLocation" :visible="sideBarVisibility"
10
+ <SideBar :envVars="envVars" class="side-bar" ref="sideBar" :visible="sideBarVisibility"
10
11
  :tabs="tabArray" :activeId="activeId" @tabClicked="tabClicked"
11
12
  @search-changed="searchChanged($event)" @actionClick="action"/>
12
13
  </div>
@@ -62,7 +63,13 @@ export default {
62
63
  ]
63
64
  }],
64
65
  sideBarVisibility: true,
65
- apiLocation: process.env.VUE_APP_API_LOCATION,
66
+ envVars: {
67
+ API_LOCATION: process.env.VUE_APP_API_LOCATION,
68
+ ALGOLIA_KEY: process.env.VUE_APP_ALGOLIA_KEY,
69
+ ALGOLIA_ID: process.env.VUE_APP_ALGOLIA_ID,
70
+ ALGOLIA_INDEX: process.env.VUE_APP_ALGOLIA_INDEX,
71
+ PENNSIEVE_API_LOCATION: process.env.VUE_APP_PENNSIEVE_API_LOCATION
72
+ },
66
73
  activeId: 1,
67
74
  }
68
75
  },
@@ -77,10 +84,13 @@ export default {
77
84
  console.log("action fired: ", val)
78
85
  },
79
86
  openSearch: function(){
80
- this.$refs.sideBar.openSearch('heart', [{facet: 'Show all', term:'species'}] )
87
+ this.$refs.sideBar.openSearch('heart', [])
88
+ },
89
+ singleFacets: function(){
90
+ this.$refs.sideBar.addFilter({facet: 'Heart', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name'})
81
91
  },
82
92
  multiFacets: function(){
83
- this.$refs.sideBar.openSearch('', [{facet: 'Rat', term:'species'}, {facet: 'Heart', term:'organ'}])
93
+ this.$refs.sideBar.openSearch('', [{facet: 'Male', term:'Sex', facetPropPath:'attributes.subject.sex.value'}, {facet: 'Heart', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name'}])
84
94
  },
85
95
  neuronSearch: function(){
86
96
  this.$refs.sideBar.openNeuronSearch('neuron-type-keast-10')
@@ -0,0 +1,98 @@
1
+ /* eslint-disable no-alert, no-console */
2
+ import algoliasearch from 'algoliasearch'
3
+
4
+ // export `createAlgoliaClient` to use it in page components
5
+ export class AlgoliaClient {
6
+ constructor(algoliaId, algoliaKey, PENNSIEVE_API_LOCATION='https://api.pennsieve.io') {
7
+ this.client = algoliasearch(
8
+ algoliaId,
9
+ algoliaKey
10
+ )
11
+ this.PENNSIEVE_API_LOCATION = PENNSIEVE_API_LOCATION
12
+ }
13
+ initIndex(ALGOLIA_INDEX) {
14
+ this.index = this.client.initIndex(ALGOLIA_INDEX);
15
+ }
16
+
17
+ getAlgoliaFacets (propPathMapping) {
18
+ const map = new Map(Object.entries(propPathMapping));
19
+ const facetPropPaths = Array.from(map.keys() );
20
+ let facetData = []
21
+ let facetId = 0
22
+ return this.index
23
+ .search('', {
24
+ sortFacetValuesBy: 'alpha',
25
+ facets: facetPropPaths
26
+ })
27
+ .then(response => {
28
+ facetPropPaths.map((facetPropPath) => {
29
+ var children = []
30
+ const responseFacets = response.facets
31
+ if (responseFacets === undefined) {return}
32
+ const responseFacetChildren =
33
+ responseFacets[facetPropPath] == undefined
34
+ ? {}
35
+ : responseFacets[facetPropPath]
36
+ Object.keys(responseFacetChildren).map(facet => {
37
+ children.push({
38
+ label: facet,
39
+ id: facetId++,
40
+ facetPropPath: facetPropPath
41
+ })
42
+ })
43
+ if (children.length > 0) {
44
+ facetData.push({
45
+ label: map.get(facetPropPath),
46
+ id: facetId++,
47
+ children: children,
48
+ key: facetPropPath
49
+ })
50
+ }
51
+ })
52
+ return facetData
53
+ })
54
+ }
55
+
56
+ // Returns all DOIs of all versions for a given discover dataset
57
+ _discoverAllDois (discoverId, PENNSIEVE_API_LOCATION='https://api.pennsieve.io') {
58
+ return new Promise(resolve => {
59
+ fetch(`${PENNSIEVE_API_LOCATION}/discover/datasets/${discoverId}/versions`).then(r=>r.json()).then(dataset => {
60
+ resolve(dataset.map(version => version.doi))
61
+ })
62
+ })
63
+ }
64
+
65
+ // Get all dois given a list of discoverIds
66
+ _expandDois (discoverIds, PENNSIEVE_API_LOCATION='https://api.pennsieve.io') {
67
+ return new Promise(resolve => {
68
+ let promiseList = discoverIds.map(discoverId => this._discoverAllDois(discoverId, PENNSIEVE_API_LOCATION))
69
+ Promise.all(promiseList).then((values) => {
70
+ resolve(values.flat())
71
+ });
72
+ })
73
+ }
74
+
75
+ /**
76
+ * Get Search results
77
+ * This is using fetch from the Algolia API
78
+ */
79
+ search (filter, query='', hitsperPage=10, page=1) {
80
+ return new Promise(resolve => {
81
+ this.index
82
+ .search(query, {
83
+ facets:['*'],
84
+ hitsPerPage: hitsperPage,
85
+ page: page-1,
86
+ filters: filter
87
+ })
88
+ .then(response => {
89
+ let searchData = {
90
+ items: response.hits,
91
+ total: response.nbHits,
92
+ discoverIds: response.hits.map(r=>r.pennsieve.identifier)
93
+ }
94
+ resolve(searchData)
95
+ })
96
+ })
97
+ }
98
+ }
@@ -0,0 +1,52 @@
1
+ /* eslint-disable no-alert, no-console */
2
+
3
+ // Mapping between display categories and their Algolia index property path
4
+ // Used for populating the Dataset Search Results facet menu dynamically
5
+ export const facetPropPathMapping = {
6
+ 'anatomy.organ.name' : 'Anatomical Structure',
7
+ 'organisms.primary.species.name' : 'Species',
8
+ 'item.modalities.keyword' : 'Experimental Approach',
9
+ 'attributes.subject.sex.value' : 'Sex',
10
+ 'attributes.subject.ageCategory.value' : 'Age Categories',
11
+ }
12
+
13
+ /* Returns filter for searching algolia. All facets of the same category are joined with OR,
14
+ * and each of those results is then joined with an AND.
15
+ * i.e. (color:blue OR color:red) AND (shape:circle OR shape:red) */
16
+ export function getFilters(selectedFacetArray=undefined) {
17
+
18
+ // return all datasets if no filter
19
+ if (selectedFacetArray === undefined) {
20
+ return 'NOT item.published.status:embargo'
21
+ }
22
+
23
+ // Switch the 'term' attribute to 'label' if 'label' does not exist
24
+ selectedFacetArray.forEach(f=>f.label=f.facet)
25
+
26
+
27
+ let facets = removeShowAllFacets(selectedFacetArray)
28
+
29
+ let filters = "NOT item.published.status:embargo";
30
+ filters = `(${filters}) AND `;
31
+
32
+ const facetPropPaths = Object.keys(facetPropPathMapping);
33
+ facetPropPaths.map((facetPropPath) => {
34
+ const facetsToOr = facets.filter(
35
+ (facet) => facet.facetPropPath == facetPropPath
36
+ );
37
+ var filter = "";
38
+ facetsToOr.map((facet) => {
39
+ filter += `"${facetPropPath}":"${facet.label}" OR `;
40
+ });
41
+ if (filter == "") {
42
+ return;
43
+ }
44
+ filter = `(${filter.substring(0, filter.lastIndexOf(" OR "))})`;
45
+ filters += `${filter} AND `;
46
+ });
47
+ return filters.substring(0, filters.lastIndexOf(" AND "));
48
+ }
49
+
50
+ function removeShowAllFacets(facetArray){
51
+ return facetArray.filter( f => f.label !== 'Show all')
52
+ }
@@ -1,7 +1,8 @@
1
1
  <template>
2
2
  <div class="dataset-card-container" ref="container">
3
3
  <div v-bind:class=" expanded ? 'dataset-card-expanded' : 'dataset-card'" ref="card">
4
- <div v-if="entry.id !== 0" class="seperator-path"></div>
4
+ <!-- The seperator-path css is set on SidebarContent.vue -->
5
+ <div class="seperator-path"></div>
5
6
  <div class="card" >
6
7
  <span class="card-left">
7
8
  <img svg-inline class="banner-img" :src="thumbnail" @click="openDataset"/>
@@ -26,6 +27,12 @@
26
27
  <div>
27
28
  <el-button v-if="entry.simulation" @click="openSimulation" size="mini" class="button" icon="el-icon-view">View simulation</el-button>
28
29
  </div>
30
+ <div>
31
+ <el-button v-if="entry.segmentation" @click="openSegmentation" size="mini" class="button" icon="el-icon-view">View segmentation</el-button>
32
+ </div>
33
+ <div>
34
+ <el-button v-if="biolucidaData" @click="openImage" size="mini" class="button" icon="el-icon-view">View image</el-button>
35
+ </div>
29
36
  </div>
30
37
 
31
38
  </div>
@@ -61,9 +68,9 @@ export default {
61
68
  * the required viewing.
62
69
  */
63
70
  entry: Object,
64
- apiLocation: {
65
- type: String,
66
- default: ""
71
+ envVars: {
72
+ type: Object,
73
+ default: () => {}
67
74
  },
68
75
  },
69
76
  data: function () {
@@ -72,7 +79,8 @@ export default {
72
79
  dataLocation: this.entry.doi,
73
80
  discoverId: undefined,
74
81
  cardOverflow: false,
75
- expanded: false
82
+ expanded: false,
83
+ biolucidaData: undefined
76
84
  };
77
85
  },
78
86
  computed: {
@@ -94,18 +102,28 @@ export default {
94
102
  },
95
103
  samples: function() {
96
104
  let text = "";
97
- if (this.entry.numberSamples === 1) {
98
- text = this.entry.numberSamples + " sample";
99
- } else if (this.entry.numberSamples > 1) {
100
- text = this.entry.numberSamples + " samples";
101
- }
102
105
  if (this.entry.species) {
103
106
  if (speciesMap[this.entry.species[0].toLowerCase()]){
104
- text += ` (${speciesMap[this.entry.species[0].toLowerCase()]})`
107
+ text = `${speciesMap[this.entry.species[0].toLowerCase()]}`;
105
108
  } else {
106
- text += ` (${this.entry.species})`
109
+ text = `${this.entry.species}`;
107
110
  }
108
111
  }
112
+ if (this.entry.numberSamples > 0) {
113
+ text += " (";
114
+ if (this.entry.numberSamples === 1) {
115
+ text += `${this.entry.numberSamples} sample`;
116
+ } else if (this.entry.numberSamples > 1) {
117
+ text += `${this.entry.numberSamples} samples`;
118
+ }
119
+ if (this.entry.numberSubjects === 1) {
120
+ text += ` from ${this.entry.numberSubjects} subject`;
121
+ } else if (this.entry.numberSamples > 1) {
122
+ text += ` from ${this.entry.numberSubjects} subjects`;
123
+ }
124
+ text += ")";
125
+ }
126
+
109
127
  return text;
110
128
  },
111
129
  label: function(){
@@ -148,7 +166,7 @@ export default {
148
166
  window.open(this.dataLocation,'_blank');
149
167
  },
150
168
  openRepository: function() {
151
- let apiLocation = this.apiLocation;
169
+ let apiLocation = this.envVars.API_LOCATION;
152
170
  this.entry.additionalLinks.forEach(function(el) {
153
171
  if (el.description == "Repository") {
154
172
  let xmlhttp = new XMLHttpRequest();
@@ -193,13 +211,60 @@ export default {
193
211
  }
194
212
  EventBus.$emit("PopoverActionClick", action)
195
213
  },
214
+ openSegmentation: function() {
215
+ if (this.entry.segmentation && this.entry.segmentation[0]) {
216
+ const segmentation = this.entry.segmentation[0];
217
+ const filePath = segmentation.dataset.path;
218
+ const datasetId = this.discoverId;
219
+ const datasetVersion = this.version;
220
+ const prefix = 'https://sparc.biolucida.net:8081';
221
+ const resource = {
222
+ share_link: `${prefix}/dataviewer?datasetId=${datasetId}&version=${datasetVersion}&path=${filePath}`
223
+ };
224
+ let action = {
225
+ label: capitalise(this.label),
226
+ resource: resource,
227
+ dataset: this.dataLocation,
228
+ datasetId: this.discoverId,
229
+ title: "View segmentation",
230
+ name: this.entry.name,
231
+ description: this.entry.description,
232
+ type: "Segmentation"
233
+ };
234
+ EventBus.$emit("PopoverActionClick", action);
235
+ }
236
+ },
237
+ openImage: function() {
238
+ if (this.biolucidaData) {
239
+ const biolucidaData = this.biolucidaData;
240
+ if ('dataset_images' in biolucidaData) {
241
+ const image = biolucidaData['dataset_images'][0];
242
+ const resource = {
243
+ share_link: image.share_link,
244
+ id: image.image_id,
245
+ itemId: image.sourcepkg_id
246
+ }
247
+ let action = {
248
+ label: capitalise(this.label),
249
+ resource: resource,
250
+ dataset: this.dataLocation,
251
+ datasetId: this.discoverId,
252
+ title: "View image",
253
+ name: this.entry.name,
254
+ description: this.entry.description,
255
+ type: "Biolucida"
256
+ };
257
+ EventBus.$emit("PopoverActionClick", action);
258
+ }
259
+ }
260
+ },
196
261
  getScaffoldPath: function(discoverId, version, scaffoldPath){
197
262
  let id = discoverId
198
- let path = `${this.apiLocation}s3-resource/${id}/${version}/files/${scaffoldPath}`
263
+ let path = `${this.envVars.API_LOCATION}s3-resource/${id}/${version}/files/${scaffoldPath}`
199
264
  return path
200
265
  },
201
266
  getFileFromPath: function(discoverId, version, path){
202
- return `${this.apiLocation}s3-resource/${discoverId}/${version}/files/${path}`
267
+ return `${this.envVars.API_LOCATION}s3-resource/${discoverId}/${version}/files/${path}`
203
268
  },
204
269
  isOverflown: function(el){
205
270
  return el.clientHeight < el.scrollHeight
@@ -225,6 +290,7 @@ export default {
225
290
  this.discoverId = data.id
226
291
  this.version = data.version
227
292
  this.dataLocation = `https://sparc.science/datasets/${data.id}?type=dataset`
293
+ this.getBiolucidaInfo(this.discoverId)
228
294
  })
229
295
  .catch(() => {
230
296
  //set defaults if we hit an error
@@ -235,6 +301,17 @@ export default {
235
301
  lastName: function(fullName){
236
302
  return fullName.split(',')[0]
237
303
  },
304
+ getBiolucidaInfo: function(id) {
305
+ let apiLocation = this.envVars.API_LOCATION;
306
+ let endpoint = apiLocation + "image_search/" + id;
307
+ // Add parameters if we are sent them
308
+ fetch(endpoint)
309
+ .then(response => response.json())
310
+ .then(data => {
311
+ if (data.status == "success")
312
+ this.biolucidaData = data;
313
+ });
314
+ }
238
315
  },
239
316
  mounted: function(){
240
317
  this.getBanner()
@@ -259,12 +336,7 @@ export default {
259
336
  padding-left: 16px;
260
337
  position: relative;
261
338
  }
262
- .seperator-path {
263
- width: 486px;
264
- height: 0px;
265
- border: solid 1px #e4e7ed;
266
- background-color: #e4e7ed;
267
- }
339
+
268
340
  .title {
269
341
  padding-bottom: 5px;
270
342
  font-family: Asap;