@abi-software/map-side-bar 1.3.17 → 1.3.20

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.3.17",
3
+ "version": "1.3.20",
4
4
  "main": "./dist/map-side-bar.common.js",
5
5
  "files": [
6
6
  "dist/*",
package/src/App.vue CHANGED
@@ -3,9 +3,10 @@
3
3
  <link rel="stylesheet"
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
- <el-button @click="openSearch">search 'heart' from refs</el-button>
6
+ <el-button @click="openSearch">search Uberon from refs</el-button>
7
7
  <el-button @click="singleFacets">Add heart to Filter</el-button>
8
- <el-button @click="addStomach">Add stomach to Filter</el-button>
8
+ <el-button @click="addStomach">Add stomach to Filter</el-button>
9
+ <el-button @click="AddInvalidTerm">Add invalid term to Filter</el-button>
9
10
  <el-button @click="multiFacets">multiple facets</el-button>
10
11
  <el-button @click="neuronSearch">open neuron search</el-button>
11
12
  <el-button @click="keywordSearch">keyword search</el-button>
@@ -113,10 +114,14 @@ export default {
113
114
  this.$refs.sideBar.addFilter({facet: 'Heart', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name', AND: true})
114
115
  },
115
116
  addStomach: function(){
116
- this.$refs.sideBar.addFilter({facet: 'Stomach', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name', AND: true})
117
+ this.$refs.sideBar.addFilter({facet: 'Stomach', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name', AND: false})
118
+ },
119
+ addInvalidTerm: function(){
120
+ this.$refs.sideBar.addFilter({facet: 'Invalid', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name', AND: true})
117
121
  },
118
122
  multiFacets: function(){
119
- this.$refs.sideBar.openSearch([{facet: 'Male', term:'Sex', facetPropPath:'attributes.subject.sex.value'}, {facet: 'Heart', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name'}], '')
123
+ this.$refs.sideBar.openSearch([{facet: 'Male', term:'Sex', facetPropPath:'attributes.subject.sex.value'}, {facet: 'Heart', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name'},
124
+ {facet: 'Not correct', term:'Anatomical structure', facetPropPath: 'anatomy.organ.name'}], '')
120
125
  },
121
126
  keywordSearch: function(){
122
127
  this.$refs.sideBar.addFilter({type: 'Facet', label: undefined, facet: '3d model', facetPropPath: 'item.keywords.keyword', term: 'Keywords', AND: true})
@@ -48,7 +48,7 @@
48
48
  <div class="view-description">{{view.description}}<i class="el-icon-warning-outline info"></i> </div>
49
49
  </span>
50
50
  <div v-if="sampleDetails[i]" v-html="samplesMatching(view.id).description" :key="i+'_2'"/>
51
- <a v-bind:key="i+'_5'" v-if="sampleDetails[i]" :href="generateFileLink(samplesMatching(view.id).path)" target="_blank">View Source</a>
51
+ <a v-bind:key="i+'_5'" v-if="sampleDetails[i]" :href="generateFileLink(samplesMatching(view.id))" target="_blank">View Source</a>
52
52
  <div :key="i" class="padding"/>
53
53
 
54
54
  <!-- Extra padding if sample details is open -->
@@ -79,6 +79,26 @@ Vue.use(Button);
79
79
  Vue.use(Select);
80
80
  Vue.use(Input);
81
81
 
82
+ const addFilesToPathIfMissing = function(path){
83
+ if (!path.includes('files')){
84
+ return 'files/' + path
85
+ } else {
86
+ return path
87
+ }
88
+ }
89
+
90
+ const convertBackslashToForwardSlash = function(path){
91
+ path = path.replaceAll('\\','/')
92
+ path = path.replaceAll('\\\\', '/')
93
+ return path
94
+ }
95
+
96
+ // const switchPathToDirectory = function(path){
97
+ // let newPath = path.split('/')
98
+ // newPath.pop()
99
+ // return newPath.join('/')
100
+ // }
101
+
82
102
 
83
103
  export default {
84
104
  name: "contextCard",
@@ -88,6 +108,7 @@ export default {
88
108
  * the required viewing.
89
109
  */
90
110
  entry: Object,
111
+ envVars: Object,
91
112
  },
92
113
  data: function () {
93
114
  return {
@@ -162,9 +183,11 @@ export default {
162
183
  .then((data) => {
163
184
  this.contextData = data
164
185
  this.loading = false
186
+ this.addDiscoverIdsToContextData()
165
187
  })
166
- .catch(() => {
188
+ .catch((err) => {
167
189
  //set defaults if we hit an error
190
+ console.error('caught error!', err)
168
191
  this.thumbnail = require('@/../assets/missing-image.svg')
169
192
  this.discoverId = undefined
170
193
  this.loading = false
@@ -192,15 +215,39 @@ export default {
192
215
  return path
193
216
  }
194
217
  path = this.removeDoubleFilesPath(path)
195
- return `${this.entry.apiLocation}s3-resource/${this.entry.discoverId}/${this.entry.version}/files/${path}`
218
+ return `${this.envVars.API_LOCATION}s3-resource/${this.entry.discoverId}/${this.entry.version}/files/${path}`
196
219
  },
197
- generateFileLink(path){
198
- return `https://sparc.science/file/${this.entry.discoverId}/${this.entry.version}?path=${encodeURI(path)}`
220
+ // This is used later when generateing links to the resource on sparc.science (see generateFileLink)
221
+ addDiscoverIdsToContextData(){
222
+ this.contextData.samples.forEach((sample, i)=>{
223
+ if (sample && sample.doi && sample.doi !== ""){
224
+ fetch(`${this.envVars.PENNSIEVE_API_LOCATION}/discover/datasets/doi/${this.splitDoiFromUrl(sample.doi)}`)
225
+ .then((response) => response.json())
226
+ .then((data) => {
227
+ this.contextData.samples[i].discoverId = data.id
228
+ this.contextData.samples[i].version = data.version
229
+ })
230
+ } else {
231
+ this.contextData.samples[i].discoverId = this.entry.discoverId
232
+ this.contextData.samples[i].version = this.entry.version
233
+ }
234
+ })
235
+ },
236
+ processPathForUrl(path){
237
+ path = convertBackslashToForwardSlash(path)
238
+ path = addFilesToPathIfMissing(path)
239
+ return encodeURI(path)
240
+ },
241
+ splitDoiFromUrl(url){
242
+ return url.split('https://doi.org/').pop()
243
+ },
244
+ generateFileLink(sample){
245
+ return `${this.envVars.ROOT_URL}/file/${sample.discoverId}/${sample.version}?path=${this.processPathForUrl(sample.path)}`
199
246
 
200
247
  },
201
248
  openViewFile: function(view){
202
249
  // note that we assume that the view file is in the same directory as the scaffold (viewUrls take relative paths)
203
- this.entry.viewUrl = `${this.entry.apiLocation}s3-resource/${this.entry.discoverId}/${this.entry.version}/${view.path}`
250
+ this.entry.viewUrl = `${this.envVars.API_LOCATION}s3-resource/${this.entry.discoverId}/${this.entry.version}/${view.path}`
204
251
  this.entry.type = 'Scaffold View'
205
252
  EventBus.$emit("PopoverActionClick", this.entry)
206
253
  }
@@ -237,7 +284,7 @@ export default {
237
284
 
238
285
  .view-image {
239
286
  width: 34px;
240
- height: auto;
287
+ height: 34px;
241
288
  flex: 1;
242
289
  margin-right: 4px;
243
290
  }
@@ -178,7 +178,7 @@ export default {
178
178
  this.createCascaderItemValue(facet.label, facetItem.label);
179
179
  });
180
180
  });
181
- this.createDataTypeFacet()
181
+ this.createDataTypeFacet();
182
182
  })
183
183
  .finally(() => {
184
184
  resolve();
@@ -225,7 +225,7 @@ export default {
225
225
  this.makeCascadeLabelsClickable();
226
226
  }
227
227
  },
228
- // showAllEventModifier: Modifies a cascade event to unlick all selections in category if "show all" is clicked. Also unchecks "Show all" if any secection is clicked
228
+ // showAllEventModifier: Modifies a cascade event to unclick all selections in category if "show all" is clicked. Also unchecks "Show all" if any secection is clicked
229
229
  // *NOTE* Does NOT remove 'Show all' selections from showing in 'cascadeSelected'
230
230
  showAllEventModifier: function (event) {
231
231
  // check if show all is in the cascader checked option list
@@ -363,13 +363,13 @@ export default {
363
363
  addFilter: function (filter) {
364
364
  //Do not set the value unless it is ready
365
365
  if (this.cascaderIsReady && filter) {
366
- this.cascadeSelected.filter(f=>f.term != filter.term)
367
- this.cascadeSelected.push([filter.facetPropPath, this.createCascaderItemValue(filter.term, filter.facet), filter.AND])
368
-
369
- this.cascadeSelectedWithBoolean.push([filter.facetPropPath, this.createCascaderItemValue(filter.term, filter.facet), filter.AND])
370
- // The 'AND' her is to set the boolean value when we search on the filters. It can be undefined without breaking anything
371
-
372
-
366
+ if (this.validateFilter(filter)) {
367
+ this.cascadeSelected.filter(f=>f.term != filter.term)
368
+ this.cascadeSelected.push([filter.facetPropPath, this.createCascaderItemValue(filter.term, filter.facet), filter.AND])
369
+ this.cascadeSelectedWithBoolean.push([filter.facetPropPath, this.createCascaderItemValue(filter.term, filter.facet), filter.AND])
370
+ // The 'AND' her is to set the boolean value when we search on the filters. It can be undefined without breaking anything
371
+ return true;
372
+ }
373
373
  }
374
374
  },
375
375
  initiateSearch: function() {
@@ -406,15 +406,48 @@ export default {
406
406
  });
407
407
  });
408
408
  },
409
+ /**
410
+ * Validate ther filter term to make sure the term is correct
411
+ */
412
+ validateFilter: function(filter) {
413
+ if (filter && filter.facet && filter.term) {
414
+ const item = this.createCascaderItemValue(filter.term, filter.facet);
415
+ const facet = this.options.find(element => element.value === filter.facetPropPath);
416
+ if (facet) {
417
+ const filter = facet.children.find(element => element.value === item);
418
+ if (filter)
419
+ return true;
420
+ }
421
+ }
422
+ return false;
423
+ },
424
+ /**
425
+ * Return a list of valid filers given a list of filters,
426
+ */
427
+ getValidatedFilters: function (filters) {
428
+ if (filters) {
429
+ if (this.cascaderIsReady) {
430
+ const result = [];
431
+ filters.forEach(filter => {
432
+ if (this.validateFilter(filter)) {
433
+ result.push(filter);
434
+ }
435
+ });
436
+ return result;
437
+ } else return filters;
438
+ }
439
+ return [];
440
+ },
409
441
  },
410
442
  mounted: function () {
411
443
  this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
412
444
  this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
413
445
  this.populateCascader().then(() => {
414
446
  this.cascaderIsReady = true;
415
- this.checkShowAllBoxes()
447
+ this.checkShowAllBoxes();
416
448
  this.setCascader(this.entry.filterFacets);
417
449
  this.makeCascadeLabelsClickable();
450
+ this.$emit("cascaderReady");
418
451
  });
419
452
  },
420
453
  };
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <el-card :body-style="bodyStyle" class="content-card">
3
3
  <div slot="header" class="header">
4
- <context-card v-if="contextCardEntry && contextCardEnabled" :entry="contextCardEntry" />
4
+ <context-card v-if="contextCardEntry && contextCardEnabled" :entry="contextCardEntry" :envVars="envVars"/>
5
5
  <el-input
6
6
  class="search-input"
7
7
  placeholder="Search"
@@ -20,6 +20,7 @@
20
20
  @filterResults="filterUpdate"
21
21
  @numberPerPage="numberPerPageUpdate"
22
22
  @loading="filtersLoading"
23
+ @cascaderReady="cascaderReady"
23
24
  ></SearchFilters>
24
25
  <div class="content scrollbar" v-loading="loadingCards" ref="content">
25
26
  <div
@@ -125,10 +126,6 @@ export default {
125
126
  type: Object,
126
127
  default: () => {}
127
128
  },
128
- firstSearch: {
129
- type: String,
130
- default: ""
131
- }
132
129
  },
133
130
  data: function() {
134
131
  return {
@@ -137,7 +134,8 @@ export default {
137
134
  flex: "1 1 auto",
138
135
  "flex-flow": "column",
139
136
  display: "flex"
140
- }
137
+ },
138
+ cascaderIsReady: false,
141
139
  };
142
140
  },
143
141
  computed: {
@@ -153,22 +151,58 @@ export default {
153
151
  contextCardUpdate: function(val){
154
152
  this.contextCardEntry = val
155
153
  },
154
+ resetSearch: function() {
155
+ this.numberOfHits = 0
156
+ this.discoverIds = []
157
+ this._dois = []
158
+ this.results = []
159
+ this.loadingCards = false
160
+ },
156
161
  openSearch: function(filter, search='') {
157
162
  this.searchInput = search;
158
163
  this.resetPageNavigation();
159
- this.searchAlgolia(filter, search);
160
- if (filter) {
161
- this.filter = [...filter];
162
- this.$refs.filtersRef.setCascader(this.filter);
164
+ //Proceed normally if cascader is ready
165
+ if (this.cascaderIsReady) {
166
+ this.filter = this.$refs.filtersRef.getValidatedFilters(filter);
167
+ //Facets provided but cannot find at least one valid
168
+ //facet. Tell the users the search is invalid and reset
169
+ //facets check boxes.
170
+ if ((filter && filter.length > 0) &&
171
+ (this.filter && this.filter.length === 0)) {
172
+ this.$refs.filtersRef.checkShowAllBoxes();
173
+ this.resetSearch();
174
+ } else if (this.filter) {
175
+ this.searchAlgolia(this.filter, search);
176
+ this.$refs.filtersRef.setCascader(this.filter);
177
+ }
178
+ } else {
179
+ //cascader is not ready, perform search if no filter is set,
180
+ //otherwise waith for cascader to be ready
181
+ this.filter = filter;
182
+ if (!filter || filter.length == 0) {
183
+ this.searchAlgolia(this.filter, search);
184
+ }
163
185
  }
164
186
  },
165
187
  addFilter: function(filter) {
166
- this.resetPageNavigation();
167
- if (filter) {
168
- this.$refs.filtersRef.addFilter(filter);
169
- this.$refs.filtersRef.initiateSearch()
188
+ if (this.cascaderIsReady) {
189
+ this.resetPageNavigation();
190
+ if (filter) {
191
+ if (this.$refs.filtersRef.addFilter(filter))
192
+ this.$refs.filtersRef.initiateSearch();
193
+ }
194
+ } else {
195
+ if (Array.isArray(this.filter)) {
196
+ this.filter.push(filter);
197
+ } else {
198
+ this.filter = [filter];
199
+ }
170
200
  }
171
201
  },
202
+ cascaderReady: function() {
203
+ this.cascaderIsReady = true;
204
+ this.openSearch(this.filter, this.searchInput);
205
+ },
172
206
  clearSearchClicked: function() {
173
207
  this.searchInput = "";
174
208
  this.resetPageNavigation();
@@ -346,13 +380,7 @@ export default {
346
380
  // initialise algolia
347
381
  this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
348
382
  this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
349
-
350
- // temporarily disable flatmap search since there are no datasets
351
- if (this.firstSearch === "Flatmap" || this.firstSearch === "flatmap") {
352
- this.openSearch(undefined, '')
353
- } else {
354
- this.openSearch(undefined, '');
355
- }
383
+ this.openSearch(undefined, '');
356
384
  },
357
385
  created: function() {
358
386
  //Create non-reactive local variables
@@ -0,0 +1,27 @@
1
+ {
2
+ "description": "Mean luminal pressure (represented by a color field with red as high pressure and blue as low pressure) recorded from the proximal, transverse and distal sections of the pig colon are mapped on the scaffold. \n\nBaseline data was collected for 30min, followed by 15min of stimulation and 30min of post-stimulation.",
3
+ "heading": "Direct proximal colon stimulation",
4
+ "id": "sparc.science.context_data",
5
+ "samples": [
6
+ {
7
+ "annotation": "",
8
+ "description": "Manometry data recorded from pigs under direct proximal colon stimulation.",
9
+ "doi": "https://doi.org/10.26275/up27-ibcr",
10
+ "heading": "Proximal direct stimulation samples",
11
+ "id": "Sample 1",
12
+ "path": "derivative\\stim_proximal-colon_manometry.csv",
13
+ "view": "View 1"
14
+ }
15
+ ],
16
+ "version": "0.1.0",
17
+ "views": [
18
+ {
19
+ "annotation": "--",
20
+ "description": "Sections of pig colon scaffold with mapped manometry data where the data are collected from. ",
21
+ "id": "View 1",
22
+ "path": "colon_Layout1_view.json",
23
+ "sample": "Sample 1",
24
+ "thumbnail": "derivative\\pig_colon_main_thumbnail.jpeg"
25
+ }
26
+ ]
27
+ }