From f6dd1b31cb3c5064b37e013242514ced2ab62406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20Zag=C3=B3rski?= Date: Tue, 12 Sep 2023 14:42:40 +0200 Subject: [PATCH] support metadata from backend --- .../TimeSeriesWidgetUI/TimeSeriesWidgetUI.js | 55 +++++++----------- .../components/TimeSeriesChart.js | 2 +- .../src/models/TimeSeriesModel.js | 57 +++++++++++-------- .../src/widgets/TimeSeriesWidget.js | 16 ++++-- 4 files changed, 66 insertions(+), 64 deletions(-) diff --git a/packages/react-ui/src/widgets/TimeSeriesWidgetUI/TimeSeriesWidgetUI.js b/packages/react-ui/src/widgets/TimeSeriesWidgetUI/TimeSeriesWidgetUI.js index 67bda30e1..5a09f1179 100644 --- a/packages/react-ui/src/widgets/TimeSeriesWidgetUI/TimeSeriesWidgetUI.js +++ b/packages/react-ui/src/widgets/TimeSeriesWidgetUI/TimeSeriesWidgetUI.js @@ -34,6 +34,7 @@ const FORMAT_DATE_BY_STEP_SIZE_FOR_TIME_WINDOW = { function TimeSeriesWidgetUI({ data, + categories, stepSize, chartType, tooltip, @@ -80,6 +81,7 @@ function TimeSeriesWidgetUI({ > { - const series = []; - - const categories = []; + const series = useMemo(() => { const colorMapping = {}; + const series = categories + ? categories.map((category) => ({ + category, + data: [], + color: getColorByCategory(category, { + palette, + fallbackColor, + colorMapping + }) + })) + : [{ data: [], color: theme.palette.secondary.main }]; for (const { name, value, category } of data) { - let dataSeriesIndex = category ? categories.indexOf(category) : 0; - if (dataSeriesIndex === -1) { - dataSeriesIndex = categories.length; - categories.push(category); - } - if (!series[dataSeriesIndex]) { - series[dataSeriesIndex] = { - category, - data: [] - }; - } - series[dataSeriesIndex].data.push([name, value]); - } - // } - - const hasMultipleSeries = data[0]?.category || series.length > 1; - - if (hasMultipleSeries) { - series.forEach(({ category }, i) => { - series[i].color = getColorByCategory(category, { - palette, - fallbackColor, - colorMapping - }); - }); - } else { - series[0].color = theme.palette.secondary.main; + const categoryIndex = categories && category ? categories.indexOf(category) : 0; + if (categoryIndex === -1) continue; + + series[categoryIndex].data.push([name, value]); } - return { series, categories }; - }, [data, palette, fallbackColor, theme.palette.secondary.main]); + return series; + }, [categories, data, palette, fallbackColor, theme.palette.secondary.main]); const currentDate = useMemo(() => { if (!data.length) { diff --git a/packages/react-ui/src/widgets/TimeSeriesWidgetUI/components/TimeSeriesChart.js b/packages/react-ui/src/widgets/TimeSeriesWidgetUI/components/TimeSeriesChart.js index 87df3e83c..da05bc50d 100644 --- a/packages/react-ui/src/widgets/TimeSeriesWidgetUI/components/TimeSeriesChart.js +++ b/packages/react-ui/src/widgets/TimeSeriesWidgetUI/components/TimeSeriesChart.js @@ -189,7 +189,7 @@ export default function TimeSeriesChart({ const handleClick = useCallback( (params) => { - if (onCategoryClick) { + if (categories && onCategoryClick) { const category = categories[params.seriesIndex]; onCategoryClick(category); } diff --git a/packages/react-widgets/src/models/TimeSeriesModel.js b/packages/react-widgets/src/models/TimeSeriesModel.js index a127204c3..046f5d2d4 100644 --- a/packages/react-widgets/src/models/TimeSeriesModel.js +++ b/packages/react-widgets/src/models/TimeSeriesModel.js @@ -3,34 +3,38 @@ import { Methods, executeTask } from '@carto/react-workers'; import { normalizeObjectKeys, wrapModelCall } from './utils'; import { AggregationTypes } from '@carto/react-core'; -export async function getTimeSeries(props) { +export function getTimeSeries(props) { if (props.series) { - const { series, ...propsNoSeries } = props; - const rawSeriesData = await Promise.all( - series.map(async ({ operation, operationColumn }) => { - const category = getCategory({ operation, operationColumn, series }); - return { - data: await wrapModelCall( - { ...propsNoSeries, operation, operationColumn }, - fromLocal, - fromRemote - ), - category - }; - }) - ); - let aggregatedData = []; - for (const { data, category } of rawSeriesData) { - for (const { name, value } of data) { - aggregatedData.push({ name, value, category }); - } - } - return aggregatedData; + return getMultipleSeries(props); } else { return wrapModelCall(props, fromLocal, fromRemote); } } +async function getMultipleSeries(props) { + const { series, ...propsNoSeries } = props; + + const categories = series.map(({ operation, operationColumn }) => + getCategoryForAggregationOperation({ operation, operationColumn, series }) + ); + const rowsByCategory = await Promise.all( + series.map(async (serie, index) => ({ + data: await getTimeSeries({ ...propsNoSeries, ...serie }), + category: categories[index] + })) + ); + const rows = []; + for (const { data, category } of rowsByCategory) { + for (const { name, value } of data) { + rows.push({ name, value, category }); + } + } + return { + rows, + categories + }; +} + // From local function fromLocal({ source, @@ -90,10 +94,13 @@ function fromRemote(props) { splitByCategoryValues }, opts: { abortController } - }).then((res) => normalizeObjectKeys(res.rows)); + }).then((res) => ({ + rows: normalizeObjectKeys(res.rows), + categories: res.metadata?.categories + })); } -function getCategory({ operation, operationColumn, series }) { +function getCategoryForAggregationOperation({ operation, operationColumn, series }) { if (operation === AggregationTypes.COUNT) { return `count of records`; } @@ -103,6 +110,6 @@ function getCategory({ operation, operationColumn, series }) { if (countColumnUsed < 2) { return operationColumn; } else { - return `${operation} of ${operationColumn}`; // todo translate maybe ? + return `${operation} of ${operationColumn}`; } } diff --git a/packages/react-widgets/src/widgets/TimeSeriesWidget.js b/packages/react-widgets/src/widgets/TimeSeriesWidget.js index 17a44cc9d..74e079330 100644 --- a/packages/react-widgets/src/widgets/TimeSeriesWidget.js +++ b/packages/react-widgets/src/widgets/TimeSeriesWidget.js @@ -161,7 +161,7 @@ function TimeSeriesWidget({ }, [stepSize]); const { - data: unsafeData = [], + data: result = [], isLoading, warning, remoteCalculation @@ -186,6 +186,11 @@ function TimeSeriesWidget({ attemptRemoteCalculation: _hasFeatureFlag(_FeatureFlags.REMOTE_WIDGETS) }); + const { data: unsafeData, categories } = Array.isArray(result) + ? { data: result, categories: undefined } + : { data: result.rows, categories: result.categories }; + + // filter nulls, TODO remove when backend already fixes this const data = useMemo(() => unsafeData.filter((row) => row.name !== null), [unsafeData]); const handleTimeWindowUpdate = useCallback( @@ -238,16 +243,16 @@ function TimeSeriesWidget({ ); const handleSelectedCategoriesChange = useCallback( - (categories) => { - if (!splitByCategory || !categories) return; + (newSelectedCategories) => { + if (!splitByCategory || !newSelectedCategories) return; - if (categories.length) { + if (newSelectedCategories.length) { dispatch( addFilter({ id: dataSource, column: splitByCategory, type: FilterTypes.IN, - values: categories, + values: newSelectedCategories, owner: id }) ); @@ -324,6 +329,7 @@ function TimeSeriesWidget({ {(!!data.length || isLoading) && (