mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-13 13:10:29 +00:00
fix(web): sort line chart data according to time order (#1639)
This commit is contained in:
@@ -73,8 +73,8 @@ export const getVisAdvices = (props: { data: Datum[]; myChartAdvisor: Advisor; d
|
|||||||
*/
|
*/
|
||||||
const customDataProps = dataMetaMap
|
const customDataProps = dataMetaMap
|
||||||
? Object.keys(dataMetaMap).map((item) => {
|
? Object.keys(dataMetaMap).map((item) => {
|
||||||
return { name: item, ...dataMetaMap[item] };
|
return { name: item, ...dataMetaMap[item] };
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// 可根据需要选择是否使用全部 fields 进行推荐
|
// 可根据需要选择是否使用全部 fields 进行推荐
|
||||||
@@ -84,11 +84,11 @@ export const getVisAdvices = (props: { data: Datum[]; myChartAdvisor: Advisor; d
|
|||||||
const selectedFields =
|
const selectedFields =
|
||||||
size(allFieldsInfo) > 2
|
size(allFieldsInfo) > 2
|
||||||
? allFieldsInfo?.filter((field) => {
|
? allFieldsInfo?.filter((field) => {
|
||||||
if (field.recommendation === 'string' || field.recommendation === 'date') {
|
if (field.recommendation === 'string' || field.recommendation === 'date') {
|
||||||
return field.distinct && field.distinct > 1;
|
return field.distinct && field.distinct > 1;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
: allFieldsInfo;
|
: allFieldsInfo;
|
||||||
|
|
||||||
const allAdvices = myChartAdvisor?.adviseWithLog({
|
const allAdvices = myChartAdvisor?.adviseWithLog({
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import { hasSubset, intersects } from '../advisor/utils';
|
import { hasSubset } from '../advisor/utils';
|
||||||
import { findOrdinalField, processDateEncode, findNominalField, isUniqueXValue, getLineSize } from './util';
|
import { findOrdinalField, processDateEncode, findNominalField, getLineSize, sortData } from './util';
|
||||||
import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';
|
import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';
|
||||||
import type { Datum } from '@antv/ava';
|
import type { Datum } from '@antv/ava';
|
||||||
|
|
||||||
|
const MULTI_LINE_CHART = 'multi_line_chart'
|
||||||
const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {
|
const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {
|
||||||
const ordinalField = findOrdinalField(dataProps);
|
const ordinalField = findOrdinalField(dataProps);
|
||||||
const nominalField = findNominalField(dataProps);
|
const nominalField = findNominalField(dataProps);
|
||||||
@@ -18,7 +19,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf
|
|||||||
const spec: Specification = {
|
const spec: Specification = {
|
||||||
type: 'view',
|
type: 'view',
|
||||||
autoFit: true,
|
autoFit: true,
|
||||||
data,
|
data: sortData({ data, chartType: MULTI_LINE_CHART, xField: field4X }),
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ckb: ChartKnowledge = {
|
const ckb: ChartKnowledge = {
|
||||||
id: 'multi_line_chart',
|
id: MULTI_LINE_CHART,
|
||||||
name: 'multi_line_chart',
|
name: 'multi_line_chart',
|
||||||
alias: ['multi_line_chart'],
|
alias: ['multi_line_chart'],
|
||||||
family: ['LineCharts'],
|
family: ['LineCharts'],
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import { hasSubset } from '../advisor/utils';
|
import { hasSubset } from '../advisor/utils';
|
||||||
import { findNominalField, findOrdinalField, getLineSize, processDateEncode } from './util';
|
import { findNominalField, findOrdinalField, getLineSize, processDateEncode, sortData } from './util';
|
||||||
import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';
|
import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';
|
||||||
import { Datum } from '@antv/ava';
|
import { Datum } from '@antv/ava';
|
||||||
|
|
||||||
|
const MULTI_MEASURE_LINE_CHART = 'multi_measure_line_chart'
|
||||||
const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {
|
const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -12,7 +13,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf
|
|||||||
|
|
||||||
const spec: Specification = {
|
const spec: Specification = {
|
||||||
type: 'view',
|
type: 'view',
|
||||||
data,
|
data: sortData({ data, chartType: MULTI_MEASURE_LINE_CHART, xField: field4Nominal }),
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ckb: ChartKnowledge = {
|
const ckb: ChartKnowledge = {
|
||||||
id: 'multi_measure_line_chart',
|
id: MULTI_MEASURE_LINE_CHART,
|
||||||
name: 'multi_measure_line_chart',
|
name: 'multi_measure_line_chart',
|
||||||
alias: ['multi_measure_line_chart'],
|
alias: ['multi_measure_line_chart'],
|
||||||
family: ['LineCharts'],
|
family: ['LineCharts'],
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import { Datum, FieldInfo } from '@antv/ava';
|
import type { Datum, FieldInfo } from "@antv/ava";
|
||||||
import { hasSubset, intersects } from '../advisor/utils';
|
import { hasSubset, intersects } from "../advisor/utils";
|
||||||
import { uniq } from 'lodash';
|
import { cloneDeep, uniq } from "lodash";
|
||||||
|
|
||||||
type BasicDataPropertyForAdvice = any;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process date column to new Date().
|
* Process date column to new Date().
|
||||||
@@ -10,7 +8,7 @@ type BasicDataPropertyForAdvice = any;
|
|||||||
* @param dataProps
|
* @param dataProps
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function processDateEncode(field: string, dataProps: BasicDataPropertyForAdvice[]) {
|
export function processDateEncode(field: string, dataProps: FieldInfo[]) {
|
||||||
const dp = dataProps.find((dataProp) => dataProp.name === field);
|
const dp = dataProps.find((dataProp) => dataProp.name === field);
|
||||||
|
|
||||||
if (dp?.recommendation === 'date') {
|
if (dp?.recommendation === 'date') {
|
||||||
@@ -50,3 +48,21 @@ export const getLineSize = (
|
|||||||
}
|
}
|
||||||
return field4X?.name && isUniqueXValue({ data: allData, xField: field4X.name }) ? 5 : undefined;
|
return field4X?.name && isUniqueXValue({ data: allData, xField: field4X.name }) ? 5 : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sortData = ({ data, chartType, xField }: {
|
||||||
|
data: Datum[];
|
||||||
|
xField?: FieldInfo;
|
||||||
|
chartType: string;
|
||||||
|
}) => {
|
||||||
|
const sortedData = cloneDeep(data)
|
||||||
|
try {
|
||||||
|
// 折线图绘制需要将数据点按照日期从小到大的顺序排序和连线
|
||||||
|
if (chartType.includes('line') && xField?.name && xField.recommendation === 'date') {
|
||||||
|
sortedData.sort((datum1, datum2) => new Date(datum1[xField.name as string]).getTime() - new Date(datum2[xField.name as string]).getTime())
|
||||||
|
return sortedData
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
return sortedData
|
||||||
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import { AutoChartProps, ChartType, CustomAdvisorConfig, CustomChart, Specificat
|
|||||||
import { customCharts } from './charts';
|
import { customCharts } from './charts';
|
||||||
import { ChatContext } from '@/app/chat-context';
|
import { ChatContext } from '@/app/chat-context';
|
||||||
import { compact, concat, uniq } from 'lodash';
|
import { compact, concat, uniq } from 'lodash';
|
||||||
|
import { sortData } from './charts/util';
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ export const AutoChart = (props: AutoChartProps) => {
|
|||||||
setAdvices(allAdvices);
|
setAdvices(allAdvices);
|
||||||
setRenderChartType(allAdvices[0]?.type as ChartType);
|
setRenderChartType(allAdvices[0]?.type as ChartType);
|
||||||
}
|
}
|
||||||
}, [data, advisor, chartType]);
|
}, [JSON.stringify(data), advisor, chartType]);
|
||||||
|
|
||||||
const visComponent = useMemo(() => {
|
const visComponent = useMemo(() => {
|
||||||
/* Advices exist, render the chart. */
|
/* Advices exist, render the chart. */
|
||||||
@@ -89,6 +90,13 @@ export const AutoChart = (props: AutoChartProps) => {
|
|||||||
const chartTypeInput = renderChartType ?? advices[0].type;
|
const chartTypeInput = renderChartType ?? advices[0].type;
|
||||||
const spec: Specification = advices?.find((item: Advice) => item.type === chartTypeInput)?.spec ?? undefined;
|
const spec: Specification = advices?.find((item: Advice) => item.type === chartTypeInput)?.spec ?? undefined;
|
||||||
if (spec) {
|
if (spec) {
|
||||||
|
if (spec.data && ['line_chart', 'step_line_chart'].includes(chartTypeInput)) {
|
||||||
|
// 处理 ava 内置折线图的排序问题
|
||||||
|
const dataAnalyzerOutput = advisor?.dataAnalyzer.execute({ data })
|
||||||
|
if (dataAnalyzerOutput && 'dataProps' in dataAnalyzerOutput) {
|
||||||
|
spec.data = sortData({ data: spec.data, xField: dataAnalyzerOutput.dataProps?.find(field => field.recommendation === 'date'), chartType: chartTypeInput });
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Chart
|
<Chart
|
||||||
key={chartTypeInput}
|
key={chartTypeInput}
|
||||||
|
Reference in New Issue
Block a user