Skip to content

Commit 2d12947

Browse files
committed
Optimize Visualization Page load
1 parent bdc00bc commit 2d12947

File tree

5 files changed

+220
-99
lines changed

5 files changed

+220
-99
lines changed

src/api/visualizationBootstrap.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
Datastream,
3+
ObservedProperty,
4+
ProcessingLevel,
5+
Thing,
6+
} from '@hydroserver/client'
7+
8+
const apiHost = import.meta.env.DEV ? 'http://127.0.0.1:8000' : ''
9+
10+
interface VisualizationThingPayload {
11+
id: string
12+
workspaceId: string
13+
name: string
14+
samplingFeatureCode: string
15+
}
16+
17+
interface VisualizationObservedPropertyPayload {
18+
id: string
19+
name: string
20+
code: string
21+
}
22+
23+
interface VisualizationProcessingLevelPayload {
24+
id: string
25+
definition?: string | null
26+
}
27+
28+
interface VisualizationDatastreamPayload {
29+
id: string
30+
name: string
31+
thingId: string
32+
observedPropertyId: string
33+
processingLevelId: string
34+
unitId: string
35+
noDataValue: number
36+
valueCount?: number | null
37+
phenomenonBeginTime?: string | null
38+
phenomenonEndTime?: string | null
39+
intendedTimeSpacing?: number
40+
intendedTimeSpacingUnit?: 'seconds' | 'minutes' | 'hours' | 'days' | null
41+
}
42+
43+
interface VisualizationBootstrapPayload {
44+
things: VisualizationThingPayload[]
45+
datastreams: VisualizationDatastreamPayload[]
46+
observedProperties: VisualizationObservedPropertyPayload[]
47+
processingLevels: VisualizationProcessingLevelPayload[]
48+
}
49+
50+
export async function getVisualizationBootstrap() {
51+
const response = await fetch(`${apiHost}/api/data/datastreams/visualization-bootstrap`, {
52+
credentials: 'include',
53+
headers: {
54+
Accept: 'application/json',
55+
},
56+
})
57+
58+
if (!response.ok) {
59+
throw new Error(`Failed to load visualization bootstrap: ${response.status}`)
60+
}
61+
62+
const payload = (await response.json()) as VisualizationBootstrapPayload
63+
64+
const things = payload.things.map((thingPayload) =>
65+
Object.assign(new Thing(), thingPayload)
66+
)
67+
const thingById = new Map(things.map((thing) => [thing.id, thing]))
68+
69+
const datastreams = payload.datastreams.map((datastreamPayload) =>
70+
Object.assign(new Datastream(), {
71+
...datastreamPayload,
72+
workspaceId: thingById.get(datastreamPayload.thingId)?.workspaceId ?? '',
73+
})
74+
)
75+
const observedProperties = payload.observedProperties.map(
76+
(observedPropertyPayload) =>
77+
Object.assign(new ObservedProperty(), observedPropertyPayload)
78+
)
79+
const processingLevels = payload.processingLevels.map(
80+
(processingLevelPayload) =>
81+
Object.assign(new ProcessingLevel(), processingLevelPayload)
82+
)
83+
84+
return {
85+
things,
86+
datastreams,
87+
observedProperties,
88+
processingLevels,
89+
}
90+
}

src/components/VisualizeData/DataVisDatasetsTable.vue

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,14 +240,12 @@ import DatastreamInformationCard from './DatastreamInformationCard.vue'
240240
import { formatTime } from '@/utils/time'
241241
import { mdiDownload, mdiMagnify, mdiTableColumnWidth } from '@mdi/js'
242242
243+
const dataVisStore = useDataVisStore()
243244
const {
244-
things,
245245
filteredDatastreams,
246246
plottedDatastreams,
247-
observedProperties,
248-
processingLevels,
249247
tableHeaders: headers,
250-
} = storeToRefs(useDataVisStore())
248+
} = storeToRefs(dataVisStore)
251249
252250
const showOnlySelected = ref(false)
253251
const openInfoCard = ref(false)
@@ -272,7 +270,7 @@ const onRowClick = (event: Event, item: any) => {
272270
let targetElement = event.target as HTMLElement
273271
if (targetElement.id && targetElement.id.startsWith('checkbox-')) return
274272
275-
const foundThing = things.value.find((t) => t.id === item.item.thingId)
273+
const foundThing = dataVisStore.thingById.get(item.item.thingId)
276274
if (foundThing) selectedThing.value = foundThing
277275
278276
const selectedDatastreamId = item.item.id
@@ -286,7 +284,7 @@ const onRowClick = (event: Event, item: any) => {
286284
}
287285
288286
const openMetadata = (item: Datastream) => {
289-
const foundThing = things.value.find((t) => t.id === item.thingId)
287+
const foundThing = dataVisStore.thingById.get(item.thingId)
290288
if (foundThing) selectedThing.value = foundThing
291289
292290
const foundDatastream = filteredDatastreams.value.find(
@@ -310,9 +308,9 @@ const displayDatastreams = computed(() => {
310308
311309
const tableItems = computed(() => {
312310
return displayDatastreams.value.map((ds) => {
313-
const thing = things.value.find((t) => t.id === ds.thingId)
314-
const observedProperty = observedProperties.value.find(
315-
(p) => p.id === ds.observedPropertyId
311+
const thing = dataVisStore.thingById.get(ds.thingId)
312+
const observedProperty = dataVisStore.observedPropertyById.get(
313+
ds.observedPropertyId
316314
)
317315
const observedPropertyCode =
318316
typeof observedProperty?.code === 'string'
@@ -325,8 +323,8 @@ const tableItems = computed(() => {
325323
const observedPropertyDisplay = observedPropertyCode
326324
? `${observedPropertyName} (${observedPropertyCode})`
327325
: observedPropertyName
328-
const processingLevel = processingLevels.value.find(
329-
(p) => p.id === ds.processingLevelId
326+
const processingLevel = dataVisStore.processingLevelById.get(
327+
ds.processingLevelId
330328
)
331329
return {
332330
...ds,

src/components/VisualizeData/DataVisFiltersDrawer.vue

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ const {
197197
matchesSelectedThing,
198198
matchesSelectedWorkspace,
199199
} = useDataVisStore()
200+
const dataVisStore = useDataVisStore()
200201
const {
201202
things,
202203
datastreams,
@@ -215,15 +216,11 @@ const searchThing = ref('')
215216
const searchObservedProperty = ref('')
216217
const searchProcessingLevel = ref('')
217218
const totalWorkspacesCount = computed(() => {
218-
const thingIds = new Set<string>()
219-
datastreams.value.forEach((ds) => {
220-
if (ds.thingId) thingIds.add(ds.thingId)
221-
})
222-
223219
const workspaceIds = new Set<string>()
224-
things.value.forEach((thing) => {
225-
if (thingIds.has(thing.id) && thing.workspaceId) {
226-
workspaceIds.add(thing.workspaceId)
220+
datastreams.value.forEach((ds) => {
221+
const workspaceId = dataVisStore.thingById.get(ds.thingId)?.workspaceId
222+
if (workspaceId) {
223+
workspaceIds.add(workspaceId)
227224
}
228225
})
229226
@@ -240,33 +237,24 @@ const totalThingsCount = computed(() => {
240237
})
241238
242239
const totalObservedPropertyNamesCount = computed(() => {
243-
const ids = new Set<string>()
244-
datastreams.value.forEach((ds) => {
245-
if (ds.observedPropertyId) ids.add(ds.observedPropertyId)
246-
})
247240
const names = new Set<string>()
248241
observedProperties.value.forEach((op) => {
249-
if (ids.has(op.id) && op.name) names.add(op.name)
242+
if (op.name) names.add(op.name)
250243
})
251244
return names.size
252245
})
253246
254247
const totalProcessingLevelNamesCount = computed(() => {
255-
const ids = new Set<string>()
256-
datastreams.value.forEach((ds) => {
257-
if (ds.processingLevelId) ids.add(ds.processingLevelId)
258-
})
259248
const names = new Set<string>()
260249
processingLevels.value.forEach((pl) => {
261-
if (ids.has(pl.id) && pl.definition) names.add(pl.definition)
250+
if (pl.definition) names.add(pl.definition)
262251
})
263252
return names.size
264253
})
265254
266255
// Only show list items that are referenced by at least one datastream
267256
// Then mutually filter the lists by selected filters.
268257
const sortedWorkspaces = computed(() => {
269-
const thingById = new Map(things.value.map((thing) => [thing.id, thing]))
270258
const workspaceIds = new Set<string>()
271259
272260
datastreams.value.forEach((ds) => {
@@ -278,7 +266,7 @@ const sortedWorkspaces = computed(() => {
278266
return
279267
}
280268
281-
const workspaceId = thingById.get(ds.thingId)?.workspaceId
269+
const workspaceId = dataVisStore.thingById.get(ds.thingId)?.workspaceId
282270
if (workspaceId) workspaceIds.add(workspaceId)
283271
})
284272
@@ -288,47 +276,65 @@ const sortedWorkspaces = computed(() => {
288276
})
289277
290278
const sortedProcessingLevelNames = computed(() => {
291-
const filteredPLs = processingLevels.value.filter((pl) => {
292-
const definition = pl.definition ?? ''
293-
return datastreams.value.some(
294-
(ds) =>
295-
ds.processingLevelId === pl.id &&
296-
matchesSelectedThing(ds) &&
297-
matchesSelectedObservedProperty(ds) &&
298-
matchesSelectedWorkspace(ds)
299-
)
279+
const names = new Set<string>()
280+
281+
datastreams.value.forEach((ds) => {
282+
if (
283+
!matchesSelectedThing(ds) ||
284+
!matchesSelectedObservedProperty(ds) ||
285+
!matchesSelectedWorkspace(ds)
286+
) {
287+
return
288+
}
289+
290+
const definition = dataVisStore.processingLevelById.get(ds.processingLevelId)?.definition
291+
if (definition) {
292+
names.add(definition)
293+
}
300294
})
301-
const names = filteredPLs.map((pl) => pl.definition)
302-
return [...new Set(names)].sort()
295+
296+
return [...names].sort()
303297
})
304298
305299
const sortedThings = computed(() => {
300+
const thingIds = new Set<string>()
301+
302+
datastreams.value.forEach((ds) => {
303+
if (
304+
!matchesSelectedObservedProperty(ds) ||
305+
!matchesSelectedProcessingLevel(ds) ||
306+
!matchesSelectedWorkspace(ds)
307+
) {
308+
return
309+
}
310+
311+
thingIds.add(ds.thingId)
312+
})
313+
306314
return things.value
307-
.filter((thing) =>
308-
datastreams.value.some(
309-
(ds) =>
310-
ds.thingId === thing.id &&
311-
matchesSelectedObservedProperty(ds) &&
312-
matchesSelectedProcessingLevel(ds) &&
313-
matchesSelectedWorkspace(ds)
314-
)
315-
)
315+
.filter((thing) => thingIds.has(thing.id))
316316
.sort((a, b) => a.name.localeCompare(b.name))
317317
})
318318
319319
const sortedObservedPropertyNames = computed(() => {
320-
const filteredProperties = observedProperties.value.filter((op) =>
321-
datastreams.value.some(
322-
(ds) =>
323-
ds.observedPropertyId === op.id &&
324-
matchesSelectedThing(ds) &&
325-
matchesSelectedProcessingLevel(ds) &&
326-
matchesSelectedWorkspace(ds)
327-
)
328-
)
329-
330-
const names = filteredProperties.map((pl) => pl.name)
331-
return [...new Set(names)].sort()
320+
const names = new Set<string>()
321+
322+
datastreams.value.forEach((ds) => {
323+
if (
324+
!matchesSelectedThing(ds) ||
325+
!matchesSelectedProcessingLevel(ds) ||
326+
!matchesSelectedWorkspace(ds)
327+
) {
328+
return
329+
}
330+
331+
const name = dataVisStore.observedPropertyById.get(ds.observedPropertyId)?.name
332+
if (name) {
333+
names.add(name)
334+
}
335+
})
336+
337+
return [...names].sort()
332338
})
333339
334340
const emit = defineEmits<{

src/pages/VisualizeData.vue

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import DataVisNavRail from '@/components/VisualizeData/DataVisNavRail.vue'
3535
import DataVisDatasetsTable from '@/components/VisualizeData/DataVisDatasetsTable.vue'
3636
import DataVisualizationCard from '@/components/VisualizeData/DataVisualizationCard.vue'
3737
import { onMounted, onUnmounted, ref, watch } from 'vue'
38-
import hs from '@hydroserver/client'
38+
import { getVisualizationBootstrap } from '@/api/visualizationBootstrap'
3939
import { useDataVisStore } from '@/store/dataVisualization'
4040
import { useSidebarStore } from '@/store/useSidebar'
4141
import { storeToRefs } from 'pinia'
@@ -366,22 +366,19 @@ const loading = ref(true)
366366
367367
onMounted(async () => {
368368
try {
369-
const [
370-
thingsResponse,
371-
datastreamsResponse,
372-
processingLevelsResponse,
373-
observedPropertiesResponse,
374-
] = await Promise.all([
375-
hs.things.listAllItems(),
376-
hs.datastreams.listAllItems(),
377-
hs.processingLevels.listAllItems(),
378-
hs.observedProperties.listAllItems(),
379-
])
380-
381-
things.value = thingsResponse
382-
datastreams.value = datastreamsResponse
383-
processingLevels.value = processingLevelsResponse
384-
observedProperties.value = observedPropertiesResponse
369+
const hasBootstrapData =
370+
things.value.length > 0 ||
371+
datastreams.value.length > 0 ||
372+
processingLevels.value.length > 0 ||
373+
observedProperties.value.length > 0
374+
375+
if (!hasBootstrapData) {
376+
const bootstrap = await getVisualizationBootstrap()
377+
things.value = bootstrap.things
378+
datastreams.value = bootstrap.datastreams
379+
processingLevels.value = bootstrap.processingLevels
380+
observedProperties.value = bootstrap.observedProperties
381+
}
385382
} catch (error) {
386383
Snackbar.error('Unable to fetch data from the API.')
387384
console.error('Unable to fetch data from the API:', error)

0 commit comments

Comments
 (0)