@abi-software/map-side-bar 2.5.1 → 2.5.2-isan.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.
@@ -34,18 +34,29 @@
34
34
  ref="searchHistory"
35
35
  @search="searchHistorySearch"
36
36
  ></SearchHistory>
37
+ pmr hits: {{ pmrNumberOfHits }}
37
38
  <div class="content scrollbar" v-loading="loadingCards" ref="content">
38
39
  <div class="error-feedback" v-if="results.length === 0 && !loadingCards">
39
40
  No results found - Please change your search / filter criteria.
40
41
  </div>
41
- <div v-for="result in results" :key="result.doi" class="step-item">
42
+ <div v-for="(result, i) in results" :key="result.doi || i" class="step-item">
42
43
  <DatasetCard
44
+ v-if="result.dataSource === 'SPARC'"
43
45
  class="dataset-card"
44
46
  :entry="result"
45
47
  :envVars="envVars"
46
48
  @mouseenter="hoverChanged(result)"
47
49
  @mouseleave="hoverChanged(undefined)"
48
- />
50
+ ></DatasetCard>
51
+ <PMRDatasetCard
52
+ v-else-if="result.dataSource === 'PMR'"
53
+ class="dataset-card"
54
+ :entry="result"
55
+ :envVars="envVars"
56
+ @pmr-action-click="onPmrActionClick"
57
+ @mouseenter="hoverChanged(result)"
58
+ @mouseleave="hoverChanged(undefined)"
59
+ ></PMRDatasetCard>
49
60
  </div>
50
61
  <el-pagination
51
62
  class="pagination"
@@ -73,12 +84,17 @@ import {
73
84
  } from 'element-plus'
74
85
  import SearchFilters from './SearchFilters.vue'
75
86
  import SearchHistory from './SearchHistory.vue'
76
- import DatasetCard from './DatasetCard.vue'
77
87
  import EventBus from './EventBus.js'
78
88
 
89
+ import DatasetCard from "./DatasetCard.vue";
90
+ import PMRDatasetCard from "./PMRDatasetCard.vue";
91
+
79
92
  import { AlgoliaClient } from '../algolia/algolia.js'
80
93
  import { getFilters, facetPropPathMapping } from '../algolia/utils.js'
94
+ import FlatmapQueries from '../flatmapQueries/flatmapQueries.js'
95
+ import mixedPageCalculation from '../mixins/mixedPageCalculation.vue'
81
96
  import { markRaw } from 'vue'
97
+ const RatioOfPMRResults = 0.2; // Ratio of PMR results to Sparc results
82
98
 
83
99
  // handleErrors: A custom fetch error handler to recieve messages from the server
84
100
  // even when an error is found
@@ -98,21 +114,27 @@ var initial_state = {
98
114
  searchInput: '',
99
115
  lastSearch: '',
100
116
  results: [],
101
- numberOfHits: 0,
117
+ pmrNumberOfHits: 0,
118
+ sparcNumberOfHits: 0,
102
119
  filter: [],
103
120
  loadingCards: false,
104
121
  numberPerPage: 10,
105
122
  page: 1,
106
- pageModel: 1,
107
- start: 0,
123
+ pmrResultsOnlyFlag: false,
124
+ noPMRResultsFlag: false,
108
125
  hasSearched: false,
109
126
  contextCardEnabled: false,
110
- }
127
+ pmrResults: [],
128
+ RatioOfPMRResults: RatioOfPMRResults,
129
+ };
130
+
131
+
111
132
 
112
133
  export default {
113
134
  components: {
114
135
  SearchFilters,
115
136
  DatasetCard,
137
+ PMRDatasetCard,
116
138
  SearchHistory,
117
139
  Button,
118
140
  Card,
@@ -122,6 +144,7 @@ export default {
122
144
  Pagination
123
145
  },
124
146
  name: 'SideBarContent',
147
+ mixins: [mixedPageCalculation],
125
148
  props: {
126
149
  visible: {
127
150
  type: Boolean,
@@ -135,6 +158,13 @@ export default {
135
158
  type: Object,
136
159
  default: () => initial_state,
137
160
  },
161
+ initFilters: {
162
+ type: Object,
163
+ default: {
164
+ filter: [],
165
+ searchInput: '',
166
+ }
167
+ },
138
168
  envVars: {
139
169
  type: Object,
140
170
  default: () => {},
@@ -143,6 +173,7 @@ export default {
143
173
  data: function () {
144
174
  return {
145
175
  ...this.entry,
176
+ ...this.initFilters,
146
177
  algoliaClient: undefined,
147
178
  bodyStyle: {
148
179
  flex: '1 1 auto',
@@ -160,21 +191,69 @@ export default {
160
191
  filterFacets: this.filter,
161
192
  }
162
193
  },
194
+ // npp_SPARC: Number per page for SPARC datasets
195
+ npp_SPARC: function () {
196
+ return Math.round(this.numberPerPage * (1 - RatioOfPMRResults))
197
+ },
198
+ // npp_PMR: Number per page for PMR datasets
199
+ npp_PMR: function () {
200
+ return Math.round(this.numberPerPage * RatioOfPMRResults)
201
+ },
202
+ numberOfHits: function () {
203
+ return this.sparcNumberOfHits + this.pmrNumberOfHits
204
+ },
205
+
163
206
  },
164
207
  methods: {
165
208
  hoverChanged: function (data) {
166
209
  this.$emit('hover-changed', data)
167
210
  },
211
+ onPmrActionClick: function (data) {
212
+ this.$emit('pmr-action-click', data);
213
+ },
214
+ // resetSearch: Resets the results, and page, and variable results ratio
215
+ // Does not: reset filters, search input, or search history
168
216
  resetSearch: function () {
169
- this.numberOfHits = 0
217
+ this.pmrNumberOfHits = 0
218
+ this.sparcNumberOfHits = 0
219
+ this.page = 1
220
+ this.calculateVariableRatio()
170
221
  this.discoverIds = []
171
222
  this._dois = []
172
223
  this.results = []
173
224
  this.loadingCards = false
174
225
  },
175
- openSearch: function (filter, search = '') {
226
+ // openSearch: Resets the results, populates dataset cards and filters. Will use Algolia and SciCrunch data uness pmr mode is set
227
+ openSearch: function(filter, search = '', resetSearch = true) {
228
+ this.updatePMROnlyFlag(filter);
229
+
230
+ if (resetSearch) {
231
+ this.resetSearch();
232
+ this.openFilterSearch(filter, search);
233
+ } else {
234
+ this.searchAlgolia(filter, search);
235
+ this.openPMRSearch(filter, search);
236
+ }
237
+ },
238
+
239
+ // openPMRSearch: Resets the results, populates dataset cards and filters with PMR data.
240
+ openPMRSearch: function (filter, search = '') {
241
+ this.loadingCards = true;
242
+ this.flatmapQueries.updateOffset(this.calculatePMROffest())
243
+ this.flatmapQueries.updateLimit(this.PMRLimit(this.pmrResultsOnlyFlag))
244
+ this.flatmapQueries.pmrSearch(filter, search).then((data) => {
245
+ data.forEach((result) => {
246
+ this.results.push(result)
247
+ })
248
+ this.pmrNumberOfHits = this.flatmapQueries.numberOfHits
249
+ this.loadingCards = false;
250
+ })
251
+ },
252
+
253
+ // previously openAlgoliaSearch:
254
+ // Resets the results, populates dataset cards and filters with Algloia and SciCrunch data.
255
+ openFilterSearch: function (filter, search = '') {
176
256
  this.searchInput = search
177
- this.resetPageNavigation()
178
257
  //Proceed normally if cascader is ready
179
258
  if (this.cascaderIsReady) {
180
259
  this.filter =
@@ -191,7 +270,14 @@ export default {
191
270
  this.$refs.filtersRef.checkShowAllBoxes()
192
271
  this.resetSearch()
193
272
  } else if (this.filter) {
194
- this.searchAlgolia(this.filter, search)
273
+ if (this.pmrResultsOnlyFlag) {
274
+ this.openPMRSearch(this.filter, search);
275
+ } else if (this.noPMRResultsFlag) {
276
+ this.searchAlgolia(this.filter, search);
277
+ } else {
278
+ this.searchAlgolia(this.filter, search);
279
+ this.openPMRSearch(this.filter, search);
280
+ }
195
281
  this.$refs.filtersRef.setCascader(this.filter)
196
282
  }
197
283
  } else {
@@ -199,13 +285,20 @@ export default {
199
285
  //otherwise waith for cascader to be ready
200
286
  this.filter = filter
201
287
  if (!filter || filter.length == 0) {
202
- this.searchAlgolia(this.filter, search)
288
+ if (this.pmrResultsOnlyFlag) {
289
+ this.openPMRSearch(this.filter, search);
290
+ } else if (this.noPMRResultsFlag) {
291
+ this.searchAlgolia(this.filter, search);
292
+ } else {
293
+ this.searchAlgolia(this.filter, search);
294
+ this.openPMRSearch(this.filter, search);
295
+ }
203
296
  }
204
297
  }
205
298
  },
206
299
  addFilter: function (filter) {
207
300
  if (this.cascaderIsReady) {
208
- this.resetPageNavigation()
301
+ this.resetSearch()
209
302
  if (filter) {
210
303
  if (this.$refs.filtersRef.addFilter(filter))
211
304
  this.$refs.filtersRef.initiateSearch()
@@ -224,31 +317,66 @@ export default {
224
317
  },
225
318
  clearSearchClicked: function () {
226
319
  this.searchInput = ''
227
- this.resetPageNavigation()
228
- this.searchAlgolia(this.filters, this.searchInput)
320
+ this.resetSearch()
321
+ this.openSearch(this.filter, this.searchInput)
229
322
  this.$refs.searchHistory.selectValue = 'Full search history'
230
323
  },
231
324
  searchEvent: function (event = false) {
232
325
  if (event.keyCode === 13 || event instanceof MouseEvent) {
233
- this.resetPageNavigation()
234
- this.searchAlgolia(this.filters, this.searchInput)
326
+ this.openSearch(this.filter, this.searchInput)
235
327
  this.$refs.searchHistory.selectValue = 'Full search history'
236
328
  this.$refs.searchHistory.addSearchToHistory(
237
- this.filters,
329
+ this.filter,
238
330
  this.searchInput
239
331
  )
240
332
  }
241
333
  },
334
+ updatePMROnlyFlag: function (filters) {
335
+ const dataTypeFilters = filters.filter((item) => item.facetPropPath === 'item.types.name');
336
+ const pmrFilter = dataTypeFilters.filter((item) => item.facet === 'PMR');
337
+ const showAllFilter = dataTypeFilters.filter((item) => item.facet === 'Show all');
338
+
339
+ this.pmrResultsOnlyFlag = false;
340
+ this.noPMRResultsFlag = false;
341
+
342
+ if (dataTypeFilters.length === 1 && pmrFilter.length === 1) {
343
+ this.pmrResultsOnlyFlag = true;
344
+ }
345
+
346
+ if (dataTypeFilters.length > 0 && pmrFilter.length === 0 && showAllFilter.length === 0) {
347
+ this.noPMRResultsFlag = true;
348
+ }
349
+ },
242
350
  filterUpdate: function (filters) {
243
- this.filters = [...filters]
244
- this.resetPageNavigation()
245
- this.searchAlgolia(filters, this.searchInput)
351
+ this.filter = [...filters]
352
+
353
+ // Check if PMR is in the filters
354
+ this.updatePMROnlyFlag(filters)
355
+
356
+ // Note that we cannot use the openSearch function as that modifies filters
357
+ this.resetSearch()
358
+ if (this.pmrResultsOnlyFlag) {
359
+ this.openPMRSearch(filters, this.searchInput)
360
+ } else if (this.noPMRResultsFlag) {
361
+ this.searchAlgolia(filters, this.searchInput)
362
+ } else {
363
+ this.searchAlgolia(filters, this.searchInput)
364
+ this.openPMRSearch(filters, this.searchInput)
365
+ }
366
+
246
367
  this.$emit('search-changed', {
247
368
  value: filters,
248
369
  type: 'filter-update',
249
370
  })
250
371
  },
251
372
  searchAlgolia(filters, query = '') {
373
+
374
+ // Remove loading if we dont expect any results
375
+ if (this.SPARCLimit() === 0) {
376
+ this.loadingCards = false
377
+ return
378
+ }
379
+
252
380
  // Algolia search
253
381
 
254
382
  this.loadingCards = true
@@ -260,12 +388,17 @@ export default {
260
388
  EventBus.emit('number-of-datasets-for-anatomies', r.forScaffold)
261
389
  })
262
390
  this.algoliaClient
263
- .search(getFilters(filters), query, this.numberPerPage, this.page)
391
+ .search(getFilters(filters), query, this.calculateSPARCOffest(), this.SPARCLimit(this.pmrResultsOnlyFlag) )
264
392
  .then((searchData) => {
265
- this.numberOfHits = searchData.total
393
+ this.sparcNumberOfHits = searchData.total
266
394
  this.discoverIds = searchData.discoverIds
267
395
  this._dois = searchData.dois
268
- this.results = searchData.items
396
+ searchData.items.forEach((item) => {
397
+ item.detailsReady = false
398
+ this.results.push(item)
399
+ })
400
+ // add the items to the results
401
+ this.results.concat(searchData.items)
269
402
  this.loadingCards = false
270
403
  this.scrollToTop()
271
404
  this.$emit('search-changed', {
@@ -287,14 +420,10 @@ export default {
287
420
  this.pageChange(1)
288
421
  },
289
422
  pageChange: function (page) {
290
- this.start = (page - 1) * this.numberPerPage
291
423
  this.page = page
292
- this.searchAlgolia(
293
- this.filters,
294
- this.searchInput,
295
- this.numberPerPage,
296
- this.page
297
- )
424
+ this.results = []
425
+ this.calculateVariableRatio()
426
+ this.openSearch(this.filter, this.searchInput, false)
298
427
  },
299
428
  handleMissingData: function (doi) {
300
429
  let i = this.results.findIndex((res) => res.doi === doi)
@@ -336,7 +465,6 @@ export default {
336
465
  }
337
466
  },
338
467
  resetPageNavigation: function () {
339
- this.start = 0
340
468
  this.page = 1
341
469
  },
342
470
  resultsProcessing: function (data) {
@@ -450,7 +578,13 @@ export default {
450
578
  this.envVars.PENNSIEVE_API_LOCATION
451
579
  ))
452
580
  this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX)
453
- this.openSearch(this.filter, this.searchInput)
581
+
582
+ // initialise flatmap queries
583
+ this.flatmapQueries = new FlatmapQueries()
584
+ this.flatmapQueries.initialise(this.envVars.FLATMAP_API_LOCATION)
585
+
586
+ // open search
587
+ this.openSearch(this.filter, this.searchInput, this.mode )
454
588
  },
455
589
  created: function () {
456
590
  //Create non-reactive local variables
@@ -489,6 +623,17 @@ export default {
489
623
  display: flex;
490
624
  }
491
625
 
626
+ .data-type-select {
627
+ width: 90px;
628
+ margin-left: 10px;
629
+ }
630
+
631
+ .button {
632
+ background-color: $app-primary-color;
633
+ border: $app-primary-color;
634
+ color: white;
635
+ }
636
+
492
637
  .step-item {
493
638
  font-size: 14px;
494
639
  margin-bottom: 18px;
@@ -31,6 +31,7 @@ declare module 'vue' {
31
31
  ElTag: typeof import('element-plus/es')['ElTag']
32
32
  ExternalResourceCard: typeof import('./components/ExternalResourceCard.vue')['default']
33
33
  ImageGallery: typeof import('./components/ImageGallery.vue')['default']
34
+ PMRDatasetCard: typeof import('./components/PMRDatasetCard.vue')['default']
34
35
  SearchFilters: typeof import('./components/SearchFilters.vue')['default']
35
36
  SearchHistory: typeof import('./components/SearchHistory.vue')['default']
36
37
  SideBar: typeof import('./components/SideBar.vue')['default']
@@ -0,0 +1,221 @@
1
+ function transformKeyValueArrayToObject(data) {
2
+ try {
3
+ let result = data.values.map(valueArray =>
4
+ data.keys.reduce((acc, key, index) => {
5
+ acc[key] = valueArray[index];
6
+ return acc;
7
+ }, {})
8
+ )
9
+ return result
10
+ } catch (error) {
11
+ console.error(`Error occured during conversion of Key Value Array to Object: ${error}`)
12
+ return {}
13
+ }
14
+
15
+ }
16
+
17
+ // remove duplicates by stringifying the objects
18
+ const removeDuplicates = function (arrayOfAnything) {
19
+ if (!arrayOfAnything) return []
20
+ return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) =>
21
+ JSON.parse(e)
22
+ )
23
+ }
24
+
25
+
26
+ let FlatmapQueries = function () {
27
+ this.initialise = function (flatmapApi) {
28
+ this.flatmapApi = flatmapApi
29
+ this.features = []
30
+ this.limit = 10
31
+ this.offset = 0
32
+ this.numberOfHits = 0
33
+ this.sqlPreOffset = ''
34
+ this.lookup = {}
35
+ this.createLookup()
36
+ }
37
+
38
+ this.updateOffset = function (offset) {
39
+ this.offset = offset
40
+ }
41
+ this.updateLimit = function (limit) {
42
+ this.limit = limit
43
+ }
44
+
45
+ this.offsetText = function () {
46
+ return 'limit ' + this.limit + ' offset ' + this.offset
47
+ }
48
+
49
+ this.createTermSQL = function (terms) {
50
+ let sql = ''
51
+ let validFilter = false
52
+
53
+
54
+ if (terms && terms.length > 0) {
55
+ sql += 'and '
56
+ terms.forEach((t, i) => {
57
+ if (i == 0) {
58
+ sql += '('
59
+ }
60
+ if (t !== '') {
61
+ sql += `m.term='${t}'`
62
+ validFilter = true
63
+ if (i < terms.length - 1) {
64
+ sql += ' or '
65
+ }
66
+ }
67
+ if (i == terms.length - 1) {
68
+ sql += ') '
69
+ }
70
+ })
71
+ }
72
+ if (!validFilter) {
73
+ sql = ''
74
+ }
75
+ return sql
76
+ }
77
+
78
+
79
+ this.pmrSQL = function (terms=[], search='') {
80
+ let sql = 'select distinct m.term, m.exposure, m.score, m.workspace, d.metadata from pmr_text '
81
+ sql += 'as t left join pmr_metadata as d on t.entity=d.entity left join pmr_models as m on m.exposure=t.entity '
82
+ sql += 'where d.metadata is not null '
83
+
84
+ // add filters for the terms
85
+ const termsSql = this.createTermSQL(terms)
86
+ sql += termsSql
87
+
88
+ // Add the text search
89
+ if (search && search !== '') {
90
+ sql += `and (t.pmr_text match '${search}')`
91
+ }
92
+
93
+ // Add exposure and score filters if we aren't text searching
94
+ if (!search || search === '') {
95
+ sql += 'and m.exposure is not null and score > 0.69'
96
+ }
97
+
98
+ // group by exposure
99
+ sql += ' group by m.exposure'
100
+
101
+ this.sqlPreOffset = sql
102
+
103
+ // add the limit and offset for pagination
104
+ sql += ' ' + this.offsetText() + ';'
105
+
106
+ console.log(sql)
107
+ return sql
108
+ }
109
+
110
+ this.convertTermsToIds = function (terms) {
111
+ return terms.filter(t => this.lookUpId(t))
112
+ }
113
+
114
+ this.labelSQL = function (){
115
+ return "select entity, label from labels"
116
+ }
117
+
118
+ this.countSQL = function (sql) {
119
+ sql = `select count(*) from (${sql})`
120
+ return sql
121
+ }
122
+
123
+
124
+ this.flatmapQuery = function (sql) {
125
+ const data = { sql: sql }
126
+ return fetch(`${this.flatmapApi}knowledge/query/`, {
127
+ method: 'POST',
128
+ headers: {
129
+ 'Content-Type': 'application/json',
130
+ },
131
+ body: JSON.stringify(data),
132
+ })
133
+ .then((response) => response.json())
134
+ .catch((error) => {
135
+ console.error('Error:', error)
136
+ })
137
+ }
138
+
139
+ this.processFilters = function (filters) {
140
+ let featureFacets = []
141
+ filters.forEach((f) => {
142
+ if (f.facet !== 'Show all' && f.facet !== 'PMR')
143
+ featureFacets.push(f.facet)
144
+ })
145
+ return featureFacets
146
+ }
147
+
148
+
149
+
150
+ this.pmrSearch = function (filters=[], search='') {
151
+ let features = this.processFilters(filters)
152
+ let featureIds = this.convertTermsToIds(features)
153
+ return new Promise((resolve, reject) => {
154
+ this.flatmapQuery(this.pmrSQL(featureIds, search))
155
+ .then(data => {
156
+ const pd = this.processPMRData(data, featureIds)
157
+ this.setAvailableFeatures(pd)
158
+
159
+ // get the number of hits for pagination
160
+ this.flatmapQuery(this.countSQL(this.sqlPreOffset)).then(data => {
161
+ this.numberOfHits = data.values[0][0]
162
+ resolve(pd);
163
+ })
164
+ })
165
+ .catch(reject);
166
+ });
167
+ }
168
+
169
+ // setAvailableFeatures returns the available features in the flatmap for filtering
170
+ // pd is the processed data from the flatmap
171
+ this.setAvailableFeatures = function (pd) {
172
+ pd.forEach((d) => {
173
+ Object.keys(d).forEach((key) => {
174
+ if (!this.features.includes(key)) {
175
+ this.features.push(key)
176
+ }
177
+ })
178
+ })
179
+ }
180
+
181
+
182
+ this.processPMRData = function (data, featureIds=[]) {
183
+ // Convert the flatmap data into an array of objects
184
+ let dataObj = transformKeyValueArrayToObject(data)
185
+
186
+ // Only use the results with metadata
187
+ let metadataResults = dataObj.filter(d => d.metadata)
188
+ let metadataOnly = metadataResults.map(d => {
189
+ let md = JSON.parse(d.metadata)
190
+ md.dataSource = 'PMR'
191
+ return md
192
+ })
193
+
194
+ // If there are featureIds, filter the results
195
+ if (featureIds.length > 0) {
196
+ metadataOnly = metadataOnly.filter(d => featureIds.includes(d.term))
197
+ }
198
+
199
+ // Remove duplicates
200
+ let uniqueResults = removeDuplicates(metadataOnly)
201
+ return uniqueResults
202
+ }
203
+
204
+ this.createLookup = function () {
205
+ this.flatmapQuery(this.labelSQL())
206
+ .then(data => {
207
+ data.values.forEach(d => {
208
+ if (d[1] && (typeof d[1] === 'string' || d[1] instanceof String)) {
209
+ this.lookup[d[1].toLowerCase()] = d[0]
210
+ }
211
+ })
212
+ })
213
+ }
214
+
215
+ this.lookUpId = function (label) {
216
+ return this.lookup[label.toLowerCase()]
217
+ }
218
+
219
+ }
220
+
221
+ export default FlatmapQueries