@abi-software/map-side-bar 2.7.0 → 2.7.1-isan.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.
@@ -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
@@ -99,21 +115,27 @@ var initial_state = {
99
115
  searchInput: '',
100
116
  lastSearch: '',
101
117
  results: [],
102
- numberOfHits: 0,
118
+ pmrNumberOfHits: 0,
119
+ sparcNumberOfHits: 0,
103
120
  filter: [],
104
121
  loadingCards: false,
105
122
  numberPerPage: 10,
106
123
  page: 1,
107
- pageModel: 1,
108
- start: 0,
124
+ pmrResultsOnlyFlag: false,
125
+ noPMRResultsFlag: false,
109
126
  hasSearched: false,
110
127
  contextCardEnabled: false,
111
- }
128
+ pmrResults: [],
129
+ RatioOfPMRResults: RatioOfPMRResults,
130
+ };
131
+
132
+
112
133
 
113
134
  export default {
114
135
  components: {
115
136
  SearchFilters,
116
137
  DatasetCard,
138
+ PMRDatasetCard,
117
139
  SearchHistory,
118
140
  Button,
119
141
  Card,
@@ -123,6 +145,7 @@ export default {
123
145
  Pagination
124
146
  },
125
147
  name: 'SideBarContent',
148
+ mixins: [mixedPageCalculation],
126
149
  props: {
127
150
  visible: {
128
151
  type: Boolean,
@@ -136,6 +159,13 @@ export default {
136
159
  type: Object,
137
160
  default: () => initial_state,
138
161
  },
162
+ initFilters: {
163
+ type: Object,
164
+ default: {
165
+ filter: [],
166
+ searchInput: '',
167
+ }
168
+ },
139
169
  envVars: {
140
170
  type: Object,
141
171
  default: () => {},
@@ -144,6 +174,7 @@ export default {
144
174
  data: function () {
145
175
  return {
146
176
  ...this.entry,
177
+ ...this.initFilters,
147
178
  algoliaClient: undefined,
148
179
  bodyStyle: {
149
180
  flex: '1 1 auto',
@@ -161,21 +192,54 @@ export default {
161
192
  filterFacets: this.filter,
162
193
  }
163
194
  },
195
+ // npp_SPARC: Number per page for SPARC datasets
196
+ npp_SPARC: function () {
197
+ return Math.round(this.numberPerPage * (1 - RatioOfPMRResults))
198
+ },
199
+ // npp_PMR: Number per page for PMR datasets
200
+ npp_PMR: function () {
201
+ return Math.round(this.numberPerPage * RatioOfPMRResults)
202
+ },
203
+ numberOfHits: function () {
204
+ return this.sparcNumberOfHits + this.pmrNumberOfHits
205
+ },
206
+
164
207
  },
165
208
  methods: {
166
209
  hoverChanged: function (data) {
167
210
  this.$emit('hover-changed', data)
168
211
  },
212
+ onPmrActionClick: function (data) {
213
+ this.$emit('pmr-action-click', data);
214
+ },
215
+ // resetSearch: Resets the results, and page, and variable results ratio
216
+ // Does not: reset filters, search input, or search history
169
217
  resetSearch: function () {
170
- this.numberOfHits = 0
218
+ this.pmrNumberOfHits = 0
219
+ this.sparcNumberOfHits = 0
220
+ this.page = 1
221
+ this.calculateVariableRatio()
171
222
  this.discoverIds = []
172
223
  this._dois = []
173
224
  this.results = []
174
225
  this.loadingCards = false
175
226
  },
227
+ // openPMRSearch: Resets the results, populates dataset cards and filters with PMR data.
228
+ openPMRSearch: function (filter, search = '') {
229
+ this.resetSearch()
230
+ this.loadingCards = true;
231
+ this.flatmapQueries.updateOffset(this.calculatePMROffest())
232
+ this.flatmapQueries.updateLimit(this.PMRLimit(this.pmrResultsOnlyFlag))
233
+ this.flatmapQueries.pmrSearch(filter, search).then((data) => {
234
+ data.forEach((result) => {
235
+ this.results.push(result)
236
+ })
237
+ this.pmrNumberOfHits = this.flatmapQueries.numberOfHits
238
+ this.loadingCards = false;
239
+ })
240
+ },
176
241
  openSearch: function (filter, search = '', option = { withSearch: true }) {
177
242
  this.searchInput = search
178
- this.resetPageNavigation()
179
243
  //Proceed normally if cascader is ready
180
244
  if (this.cascaderIsReady) {
181
245
  this.filter =
@@ -193,7 +257,14 @@ export default {
193
257
  this.resetSearch()
194
258
  } else if (this.filter) {
195
259
  if (option.withSearch) {
196
- this.searchAlgolia(this.filter, search)
260
+ if (this.pmrResultsOnlyFlag) {
261
+ this.openPMRSearch(this.filter, search);
262
+ } else if (this.noPMRResultsFlag) {
263
+ this.searchAlgolia(this.filter, search);
264
+ } else {
265
+ this.searchAlgolia(this.filter, search);
266
+ this.openPMRSearch(this.filter, search);
267
+ }
197
268
  }
198
269
  this.$refs.filtersRef.setCascader(this.filter)
199
270
  }
@@ -202,13 +273,20 @@ export default {
202
273
  //otherwise waith for cascader to be ready
203
274
  this.filter = filter
204
275
  if ((!filter || filter.length == 0) && option.withSearch) {
205
- this.searchAlgolia(this.filter, search)
276
+ if (this.pmrResultsOnlyFlag) {
277
+ this.openPMRSearch(this.filter, search);
278
+ } else if (this.noPMRResultsFlag) {
279
+ this.searchAlgolia(this.filter, search);
280
+ } else {
281
+ this.searchAlgolia(this.filter, search);
282
+ this.openPMRSearch(this.filter, search);
283
+ }
206
284
  }
207
285
  }
208
286
  },
209
287
  addFilter: function (filter) {
210
288
  if (this.cascaderIsReady) {
211
- this.resetPageNavigation()
289
+ this.resetSearch()
212
290
  if (filter) {
213
291
  if (this.$refs.filtersRef.addFilter(filter))
214
292
  this.$refs.filtersRef.initiateSearch()
@@ -226,18 +304,52 @@ export default {
226
304
  this.openSearch(this.filter, this.searchInput)
227
305
  },
228
306
  clearSearchClicked: function () {
229
- this.searchInput = '';
230
- this.searchAndFilterUpdate();
307
+ this.searchInput = ''
308
+ this.resetSearch()
309
+ this.openSearch(this.filter, this.searchInput)
310
+ this.$refs.searchHistory.selectValue = 'Full search history'
231
311
  },
232
312
  searchEvent: function (event = false) {
233
313
  if (event.keyCode === 13 || event instanceof MouseEvent) {
234
- this.searchInput = this.searchInput.trim();
235
- this.searchAndFilterUpdate();
314
+ this.openSearch(this.filter, this.searchInput)
315
+ this.$refs.searchHistory.selectValue = 'Full search history'
316
+ this.$refs.searchHistory.addSearchToHistory(
317
+ this.filter,
318
+ this.searchInput
319
+ )
320
+ }
321
+ },
322
+ updatePMROnlyFlag: function (filters) {
323
+ const dataTypeFilters = filters.filter((item) => item.facetPropPath === 'item.types.name');
324
+ const pmrFilter = dataTypeFilters.filter((item) => item.facet === 'PMR');
325
+ const showAllFilter = dataTypeFilters.filter((item) => item.facet === 'Show all');
326
+
327
+ this.pmrResultsOnlyFlag = false;
328
+ this.noPMRResultsFlag = false;
329
+
330
+ if (dataTypeFilters.length === 1 && pmrFilter.length === 1) {
331
+ this.pmrResultsOnlyFlag = true;
332
+ }
333
+
334
+ if (dataTypeFilters.length > 0 && pmrFilter.length === 0 && showAllFilter.length === 0) {
335
+ this.noPMRResultsFlag = true;
236
336
  }
237
337
  },
238
338
  filterUpdate: function (filters) {
239
- this.filters = [...filters]
339
+ this.filter = [...filters]
240
340
  this.searchAndFilterUpdate();
341
+ // Check if PMR is in the filters
342
+ this.updatePMROnlyFlag(filters)
343
+ // Note that we cannot use the openSearch function as that modifies filters
344
+ this.resetSearch()
345
+ if (this.pmrResultsOnlyFlag) {
346
+ this.openPMRSearch(filters, this.searchInput)
347
+ } else if (this.noPMRResultsFlag) {
348
+ this.searchAlgolia(filters, this.searchInput)
349
+ } else {
350
+ this.searchAlgolia(filters, this.searchInput)
351
+ this.openPMRSearch(filters, this.searchInput)
352
+ }
241
353
  this.$emit('search-changed', {
242
354
  value: filters,
243
355
  type: 'filter-update',
@@ -270,6 +382,13 @@ export default {
270
382
  }
271
383
  },
272
384
  searchAlgolia(filters, query = '') {
385
+
386
+ // Remove loading if we dont expect any results
387
+ if (this.SPARCLimit() === 0) {
388
+ this.loadingCards = false
389
+ return
390
+ }
391
+
273
392
  // Algolia search
274
393
 
275
394
  this.loadingCards = true
@@ -281,12 +400,17 @@ export default {
281
400
  EventBus.emit('number-of-datasets-for-anatomies', r.forScaffold)
282
401
  })
283
402
  this.algoliaClient
284
- .search(getFilters(filters), query, this.numberPerPage, this.page)
403
+ .search(getFilters(filters), query, this.calculateSPARCOffest(), this.SPARCLimit(this.pmrResultsOnlyFlag) )
285
404
  .then((searchData) => {
286
- this.numberOfHits = searchData.total
405
+ this.sparcNumberOfHits = searchData.total
287
406
  this.discoverIds = searchData.discoverIds
288
407
  this._dois = searchData.dois
289
- this.results = searchData.items
408
+ searchData.items.forEach((item) => {
409
+ item.detailsReady = false
410
+ this.results.push(item)
411
+ })
412
+ // add the items to the results
413
+ this.results.concat(searchData.items)
290
414
  this.loadingCards = false
291
415
  this.scrollToTop()
292
416
  this.$emit('search-changed', {
@@ -308,14 +432,10 @@ export default {
308
432
  this.pageChange(1)
309
433
  },
310
434
  pageChange: function (page) {
311
- this.start = (page - 1) * this.numberPerPage
312
435
  this.page = page
313
- this.searchAlgolia(
314
- this.filters,
315
- this.searchInput,
316
- this.numberPerPage,
317
- this.page
318
- )
436
+ this.results = []
437
+ this.calculateVariableRatio()
438
+ this.openSearch(this.filter, this.searchInput, false)
319
439
  },
320
440
  handleMissingData: function (doi) {
321
441
  let i = this.results.findIndex((res) => res.doi === doi)
@@ -357,7 +477,6 @@ export default {
357
477
  }
358
478
  },
359
479
  resetPageNavigation: function () {
360
- this.start = 0
361
480
  this.page = 1
362
481
  },
363
482
  resultsProcessing: function (data) {
@@ -473,7 +592,13 @@ export default {
473
592
  this.envVars.PENNSIEVE_API_LOCATION
474
593
  ))
475
594
  this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX)
476
- this.openSearch(this.filter, this.searchInput)
595
+
596
+ // initialise flatmap queries
597
+ this.flatmapQueries = new FlatmapQueries()
598
+ this.flatmapQueries.initialise(this.envVars.FLATMAP_API_LOCATION)
599
+
600
+ // open search
601
+ this.openSearch(this.filter, this.searchInput, this.mode )
477
602
  },
478
603
  created: function () {
479
604
  //Create non-reactive local variables
@@ -515,6 +640,17 @@ export default {
515
640
  border-bottom-right-radius: 0;
516
641
  }
517
642
 
643
+ .data-type-select {
644
+ width: 90px;
645
+ margin-left: 10px;
646
+ }
647
+
648
+ .button {
649
+ background-color: $app-primary-color;
650
+ border: $app-primary-color;
651
+ color: white;
652
+ }
653
+
518
654
  .step-item {
519
655
  font-size: 14px;
520
656
  margin-bottom: 18px;
@@ -9,8 +9,6 @@ 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']
16
14
  ElButton: typeof import('element-plus/es')['ElButton']
@@ -37,6 +35,7 @@ declare module 'vue' {
37
35
  ElSelect: typeof import('element-plus/es')['ElSelect']
38
36
  ElTag: typeof import('element-plus/es')['ElTag']
39
37
  ImageGallery: typeof import('./components/ImageGallery.vue')['default']
38
+ PMRDatasetCard: typeof import('./components/PMRDatasetCard.vue')['default']
40
39
  SearchFilters: typeof import('./components/SearchFilters.vue')['default']
41
40
  SearchHistory: typeof import('./components/SearchHistory.vue')['default']
42
41
  SideBar: typeof import('./components/SideBar.vue')['default']
@@ -0,0 +1,227 @@
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 params = []
52
+ let validFilter = false
53
+
54
+
55
+ if (terms && terms.length > 0) {
56
+ sql += 'and '
57
+ terms.forEach((t, i) => {
58
+ if (i == 0) {
59
+ sql += '('
60
+ }
61
+ if (t !== '') {
62
+ params.push(terms)
63
+ sql += `m.term=?`
64
+ validFilter = true
65
+ if (i < terms.length - 1) {
66
+ sql += ' or '
67
+ }
68
+ }
69
+ if (i == terms.length - 1) {
70
+ sql += ') '
71
+ }
72
+ })
73
+ }
74
+ if (!validFilter) {
75
+ sql = ''
76
+ params = []
77
+ }
78
+ return {sql, params}
79
+ }
80
+
81
+
82
+ this.pmrSQL = function (terms=[], search='') {
83
+ let sql = 'select distinct m.term, m.exposure, m.score, m.workspace, d.metadata from pmr_text '
84
+ 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 '
85
+ sql += 'where d.metadata is not null '
86
+
87
+ // add filters for the terms
88
+ const requestDetails = this.createTermSQL(terms)
89
+ sql += requestDetails.sql
90
+ const params = [...requestDetails.params]
91
+
92
+ // Add the text search
93
+ if (search && search !== '') {
94
+ sql += `and (t.pmr_text match ?)`
95
+ params.push(search)
96
+ }
97
+
98
+ // Add exposure and score filters if we aren't text searching
99
+ if (!search || search === '') {
100
+ sql += 'and m.exposure is not null and score > 0.69'
101
+ }
102
+
103
+ // group by exposure
104
+ sql += ' group by m.exposure'
105
+
106
+ this.sqlPreOffset = sql
107
+
108
+ // add the limit and offset for pagination
109
+ sql += ' ' + this.offsetText() + ';'
110
+
111
+ console.log(sql)
112
+ return {sql, params}
113
+ }
114
+
115
+ this.convertTermsToIds = function (terms) {
116
+ return terms.filter(t => this.lookUpId(t))
117
+ }
118
+
119
+ this.labelSQL = function (){
120
+ return "select entity, label from labels"
121
+ }
122
+
123
+ this.countSQL = function (sql) {
124
+ sql = `select count(*) from (${sql})`
125
+ return sql
126
+ }
127
+
128
+
129
+ this.flatmapQuery = function (sql, params = [] ) {
130
+ const data = { sql, params }
131
+ return fetch(`${this.flatmapApi}knowledge/query/`, {
132
+ method: 'POST',
133
+ headers: {
134
+ 'Content-Type': 'application/json',
135
+ },
136
+ body: JSON.stringify(data),
137
+ })
138
+ .then((response) => response.json())
139
+ .catch((error) => {
140
+ console.error('Error:', error)
141
+ })
142
+ }
143
+
144
+ this.processFilters = function (filters) {
145
+ let featureFacets = []
146
+ filters.forEach((f) => {
147
+ if (f.facet !== 'Show all' && f.facet !== 'PMR')
148
+ featureFacets.push(f.facet)
149
+ })
150
+ return featureFacets
151
+ }
152
+
153
+
154
+
155
+ this.pmrSearch = function (filters=[], search='') {
156
+ let features = this.processFilters(filters)
157
+ let featureIds = this.convertTermsToIds(features)
158
+ return new Promise((resolve, reject) => {
159
+ const {sql, params} = this.pmrSQL(featureIds, search)
160
+ this.flatmapQuery(sql, params)
161
+ .then(data => {
162
+ const pd = this.processPMRData(data, featureIds)
163
+ this.setAvailableFeatures(pd)
164
+
165
+ // get the number of hits for pagination
166
+ this.flatmapQuery(this.countSQL(this.sqlPreOffset), params).then(data => {
167
+ this.numberOfHits = data.values[0][0]
168
+ resolve(pd);
169
+ })
170
+ })
171
+ .catch(reject);
172
+ });
173
+ }
174
+
175
+ // setAvailableFeatures returns the available features in the flatmap for filtering
176
+ // pd is the processed data from the flatmap
177
+ this.setAvailableFeatures = function (pd) {
178
+ pd.forEach((d) => {
179
+ Object.keys(d).forEach((key) => {
180
+ if (!this.features.includes(key)) {
181
+ this.features.push(key)
182
+ }
183
+ })
184
+ })
185
+ }
186
+
187
+
188
+ this.processPMRData = function (data, featureIds=[]) {
189
+ // Convert the flatmap data into an array of objects
190
+ let dataObj = transformKeyValueArrayToObject(data)
191
+ console.log(dataObj)
192
+ // Only use the results with metadata
193
+ let metadataResults = dataObj.filter(d => d.metadata)
194
+ let metadataOnly = metadataResults.map(d => {
195
+ let md = JSON.parse(d.metadata)
196
+ md.dataSource = 'PMR'
197
+ return md
198
+ })
199
+
200
+ // If there are featureIds, filter the results
201
+ if (featureIds.length > 0) {
202
+ metadataOnly = metadataOnly.filter(d => featureIds.includes(d.term))
203
+ }
204
+
205
+ // Remove duplicates
206
+ let uniqueResults = removeDuplicates(metadataOnly)
207
+ return uniqueResults
208
+ }
209
+
210
+ this.createLookup = function () {
211
+ this.flatmapQuery(this.labelSQL())
212
+ .then(data => {
213
+ data.values.forEach(d => {
214
+ if (d[1] && (typeof d[1] === 'string' || d[1] instanceof String)) {
215
+ this.lookup[d[1].toLowerCase()] = d[0]
216
+ }
217
+ })
218
+ })
219
+ }
220
+
221
+ this.lookUpId = function (label) {
222
+ return this.lookup[label.toLowerCase()]
223
+ }
224
+
225
+ }
226
+
227
+ export default FlatmapQueries