Skip to content

Commit 7b6cf4c

Browse files
Merge pull request #99 from hydroserver2/159-mutual-filtering
159 mutual filtering
2 parents 43108e4 + 6dac145 commit 7b6cf4c

File tree

9 files changed

+244
-122
lines changed

9 files changed

+244
-122
lines changed

src/components/TimeSeriesAnalyst/DatastreamInformationCard.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ const addToPlot = (datastream: Datastream) => {
118118
}
119119
120120
const clearAndPlot = (datastream: Datastream) => {
121+
emit('close')
121122
selectedDatastreams.value = []
122123
selectedDatastreams.value.push(datastream)
123-
emit('close')
124124
}
125125
126126
const unit = ref<Unit | null>(null)

src/components/TimeSeriesAnalyst/DatePickerField.vue

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,27 @@
55
readonly
66
@click="showDatePicker = true"
77
append-inner-icon="mdi-calendar"
8-
class="mb-2"
98
hide-details
109
density="compact"
1110
/>
1211

13-
<v-date-picker
14-
v-if="showDatePicker"
15-
v-model="localDate"
16-
@update:modelValue="dateSelected"
17-
/>
12+
<v-dialog v-model="showDatePicker" max-width="22rem">
13+
<v-card>
14+
<v-card-title class="d-flex pt-4">
15+
Select {{ placeholder }}
16+
<v-spacer />
17+
<v-icon color="grey-darken-1" @click="showDatePicker = false"
18+
>mdi-close</v-icon
19+
>
20+
</v-card-title>
21+
<v-divider />
22+
<v-date-picker
23+
hide-header
24+
v-model="localDate"
25+
@update:modelValue="dateSelected"
26+
/>
27+
</v-card>
28+
</v-dialog>
1829
</template>
1930

2031
<script setup lang="ts">

src/components/TimeSeriesAnalyst/TSADatasetsTable.vue

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,7 @@
11
<template>
2-
<h5 class="text-h5">Datasets</h5>
3-
42
<v-row class="my-2" align="center">
5-
<v-col cols="12" sm="auto">
6-
<v-btn
7-
:color="showOnlySelected ? 'blue-grey-lighten-4' : 'primary'"
8-
block
9-
@click="showOnlySelected = false"
10-
>
11-
Show All
12-
</v-btn>
13-
</v-col>
14-
15-
<v-col cols="12" sm="auto">
16-
<v-btn
17-
:color="!showOnlySelected ? 'blue-grey-lighten-4' : 'primary'"
18-
block
19-
@click="showOnlySelected = true"
20-
>
21-
Show Selected
22-
</v-btn>
23-
</v-col>
24-
25-
<v-col cols="12" sm="auto">
26-
<v-btn color="blue-grey-lighten-4" block @click="clearSelected">
27-
Clear Selected
28-
</v-btn>
3+
<v-col>
4+
<h5 class="text-h5">Datasets</h5>
295
</v-col>
306

317
<v-col cols="12" sm="auto">
@@ -70,6 +46,12 @@
7046

7147
<v-spacer />
7248

49+
<v-btn @click="clearSelected"> Clear Selected </v-btn>
50+
51+
<v-btn variant="outlined" @click="showOnlySelected = !showOnlySelected">
52+
Showing: {{ showOnlySelected ? 'Selected' : 'All' }}
53+
</v-btn>
54+
7355
<v-btn
7456
prepend-icon="mdi-download"
7557
@click="downloadSelectedDatastreamsCSVs(selectedDatastreams)"
@@ -109,11 +91,10 @@
10991
</template>
11092

11193
<script setup lang="ts">
112-
import { api } from '@/services/api'
11394
import { useTSAStore } from '@/store/timeSeriesAnalyst'
114-
import { Datastream, ProcessingLevel } from '@/types'
95+
import { Datastream } from '@/types'
11596
import { storeToRefs } from 'pinia'
116-
import { computed, onMounted, reactive, ref } from 'vue'
97+
import { computed, reactive, ref } from 'vue'
11798
import DatastreamInformationCard from './DatastreamInformationCard.vue'
11899
import { downloadSelectedDatastreamsCSVs } from '@/utils/CSVDownloadUtils'
119100

src/components/TimeSeriesAnalyst/TSAFiltersDrawer.vue

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
<v-expansion-panels multiple v-model="panels">
1717
<v-expansion-panel title="Sites">
1818
<v-expansion-panel-text>
19+
<v-text-field
20+
v-model="searchThing"
21+
prepend-inner-icon="mdi-magnify"
22+
label="Search"
23+
dense
24+
hide-details
25+
/>
26+
1927
<v-virtual-scroll
2028
:items="sortedThings"
2129
:height="sortedThings.length < 6 ? sortedThings.length * 40 : 250"
@@ -34,36 +42,16 @@
3442
</v-expansion-panel-text>
3543
</v-expansion-panel>
3644

37-
<v-expansion-panel title="Time">
45+
<v-expansion-panel title="Observed Properties">
3846
<v-expansion-panel-text>
39-
<div class="d-flex justify-center mb-3">
40-
<v-btn
41-
v-for="option in dateOptions"
42-
:key="option.id"
43-
:color="
44-
selectedDateBtnId === option.id ? 'blue' : 'blue-grey-lighten-4'
45-
"
46-
@click="setDateRange(option.id)"
47-
>
48-
{{ option.label }}
49-
</v-btn>
50-
</div>
51-
52-
<DatePickerField
53-
:model-value="beginDate"
54-
placeholder="Begin Date"
55-
@update:model-value="handleCustomDateSelection('begin', $event)"
47+
<v-text-field
48+
v-model="searchObservedProperty"
49+
prepend-inner-icon="mdi-magnify"
50+
label="Search"
51+
dense
52+
hide-details
5653
/>
57-
<DatePickerField
58-
:model-value="endDate"
59-
placeholder="End Date"
60-
@update:model-value="handleCustomDateSelection('end', $event)"
61-
/>
62-
</v-expansion-panel-text>
63-
</v-expansion-panel>
6454

65-
<v-expansion-panel title="Observed Properties">
66-
<v-expansion-panel-text>
6755
<v-virtual-scroll
6856
:items="sortedObservedPropertyNames"
6957
:height="
@@ -87,6 +75,14 @@
8775

8876
<v-expansion-panel title="Processing Levels">
8977
<v-expansion-panel-text>
78+
<v-text-field
79+
v-model="searchProcessingLevel"
80+
prepend-inner-icon="mdi-magnify"
81+
label="Search"
82+
dense
83+
hide-details
84+
/>
85+
9086
<v-virtual-scroll
9187
:items="sortedProcessingLevelNames"
9288
:height="
@@ -118,47 +114,91 @@
118114
<script setup lang="ts">
119115
import { computed, ref } from 'vue'
120116
import { useDisplay } from 'vuetify/lib/framework.mjs'
121-
import DatePickerField from '@/components/TimeSeriesAnalyst/DatePickerField.vue'
122117
import { useTSAStore } from '@/store/timeSeriesAnalyst'
123118
import { storeToRefs } from 'pinia'
124119
125-
const { clearFilters, setDateRange } = useTSAStore()
120+
const {
121+
matchesSelectedObservedProperty,
122+
matchesSelectedProcessingLevel,
123+
matchesSelectedThing,
124+
} = useTSAStore()
126125
const {
127126
things,
127+
datastreams,
128128
processingLevels,
129129
observedProperties,
130130
selectedThings,
131131
selectedObservedPropertyNames,
132132
selectedProcessingLevelNames,
133-
beginDate,
134-
endDate,
135-
dateOptions,
136-
selectedDateBtnId,
137133
} = storeToRefs(useTSAStore())
138134
135+
const searchThing = ref('')
136+
const searchObservedProperty = ref('')
137+
const searchProcessingLevel = ref('')
138+
139+
// Only show list items that are referenced by at least one datastream
140+
// Then mutually filter the lists by selected filters.
139141
const sortedProcessingLevelNames = computed(() => {
140-
const names = processingLevels.value.map((pl) => pl.definition)
142+
const filteredPLs = processingLevels.value.filter(
143+
(pl) =>
144+
pl.definition
145+
.toLowerCase()
146+
.includes(searchProcessingLevel.value.toLowerCase()) &&
147+
datastreams.value.some(
148+
(ds) =>
149+
ds.processingLevelId === pl.id &&
150+
matchesSelectedThing(ds) &&
151+
matchesSelectedObservedProperty(ds)
152+
)
153+
)
154+
const names = filteredPLs.map((pl) => pl.definition)
141155
return [...new Set(names)].sort()
142156
})
143157
144158
const sortedThings = computed(() => {
145-
return things.value.slice().sort((a, b) => {
146-
return a.name.localeCompare(b.name)
147-
})
159+
return things.value
160+
.filter(
161+
(thing) =>
162+
thing.name.toLowerCase().includes(searchThing.value.toLowerCase()) &&
163+
datastreams.value.some(
164+
(ds) =>
165+
ds.thingId === thing.id &&
166+
matchesSelectedObservedProperty(ds) &&
167+
matchesSelectedProcessingLevel(ds)
168+
)
169+
)
170+
.sort((a, b) => a.name.localeCompare(b.name))
148171
})
149172
150173
const sortedObservedPropertyNames = computed(() => {
151-
const names = observedProperties.value.map((pl) => pl.name)
174+
const filteredProperties = observedProperties.value.filter(
175+
(op) =>
176+
op.name
177+
.toLowerCase()
178+
.includes(searchObservedProperty.value.toLowerCase()) &&
179+
datastreams.value.some(
180+
(ds) =>
181+
ds.observedPropertyId === op.id &&
182+
matchesSelectedThing(ds) &&
183+
matchesSelectedProcessingLevel(ds)
184+
)
185+
)
186+
187+
const names = filteredProperties.map((pl) => pl.name)
152188
return [...new Set(names)].sort()
153189
})
154190
191+
const clearFilters = () => {
192+
selectedThings.value = []
193+
selectedObservedPropertyNames.value = []
194+
selectedProcessingLevelNames.value = []
195+
196+
searchThing.value = ''
197+
searchObservedProperty.value = ''
198+
searchProcessingLevel.value = ''
199+
}
200+
155201
const { smAndDown } = useDisplay()
156202
const panels = ref([0, 1, 2, 3])
157203
const drawer = ref(!!smAndDown)
158-
159-
const handleCustomDateSelection = (type: 'begin' | 'end', date: Date) => {
160-
if (type === 'begin') beginDate.value = date
161-
else endDate.value = date
162-
selectedDateBtnId.value = -1
163-
}
164204
</script>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<template>
2+
<v-row align="center" class="my-1">
3+
<v-col col="auto">
4+
<v-btn
5+
v-for="option in dateOptions"
6+
:key="option.id"
7+
:color="
8+
selectedDateBtnId === option.id ? 'blue' : 'blue-grey-lighten-4'
9+
"
10+
@click="setDateRange(option.id)"
11+
>
12+
{{ option.label }}
13+
</v-btn>
14+
</v-col>
15+
16+
<v-col>
17+
<DatePickerField
18+
:model-value="beginDate"
19+
placeholder="Begin Date"
20+
@update:model-value="handleCustomDateSelection('begin', $event)"
21+
/>
22+
</v-col>
23+
<v-col>
24+
<DatePickerField
25+
:model-value="endDate"
26+
placeholder="End Date"
27+
@update:model-value="handleCustomDateSelection('end', $event)"
28+
/>
29+
</v-col>
30+
</v-row>
31+
</template>
32+
33+
<script setup lang="ts">
34+
import DatePickerField from '@/components/TimeSeriesAnalyst/DatePickerField.vue'
35+
import { useTSAStore } from '@/store/timeSeriesAnalyst'
36+
import { storeToRefs } from 'pinia'
37+
38+
const { setDateRange } = useTSAStore()
39+
40+
const { dateOptions, beginDate, endDate, selectedDateBtnId } = storeToRefs(
41+
useTSAStore()
42+
)
43+
44+
const handleCustomDateSelection = (type: 'begin' | 'end', date: Date) => {
45+
if (type === 'begin') beginDate.value = date
46+
else endDate.value = date
47+
selectedDateBtnId.value = -1
48+
}
49+
</script>

src/components/TimeSeriesAnalyst/TSAVisualizationCard.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ const fetchGraphSeries = async (
168168
const prevDatastreamIds = ref<string[]>([])
169169
const prevBeginDate = ref<string>('')
170170
const prevEndDate = ref<string>('')
171+
const isStateChanged = ref(true)
171172
172173
const updateState = async (
173174
datastreams: Datastream[],
@@ -191,6 +192,9 @@ const updateState = async (
191192
(id) => !currentIds.includes(id)
192193
)
193194
195+
isStateChanged.value =
196+
!!newIds.length || !!removedIds.length || isDateRangeChanged
197+
194198
// Directly remove graph series for datastreams that have been removed
195199
if (removedIds.length > 0) {
196200
graphSeriesArray.value = graphSeriesArray.value.filter(
@@ -240,6 +244,7 @@ const updateState = async (
240244
241245
const clearState = () => {
242246
graphSeriesArray.value = []
247+
prevDatastreamIds.value = []
243248
showSummaryStatistics.value = false
244249
option.value = undefined
245250
}
@@ -251,11 +256,9 @@ watch(
251256
async ([newDatastreams, newBeginDate, newEndDate]) => {
252257
if (!newBeginDate || !newEndDate || !newDatastreams.length) {
253258
clearState()
254-
return
255-
}
256-
if (!updating.value) {
259+
} else if (!updating.value) {
257260
await updateState(newDatastreams, newBeginDate, newEndDate)
258-
renderPlot()
261+
if (isStateChanged.value) renderPlot()
259262
}
260263
},
261264
{ deep: true, immediate: true }

src/pages/SiteDetails.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
<h5 class="text-h5 my-6">Datastreams Available at this Site</h5>
8787

8888
<v-row class="pb-4" v-if="isOwner">
89-
<v-col>
89+
<v-col cols="auto">
9090
<v-btn-secondary
9191
prependIcon="mdi-plus"
9292
variant="elevated"

0 commit comments

Comments
 (0)