signoz/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts
Yunus M 2180118094
feat: anomaly detection UI (#5916)
* feat: anamoly detection - initial ui

* feat: anamoly detection - oct 10

* feat: use antd checkbox

* feat: handle multiple series

* feat: handle chart height on switch btwn threshold / anomaly

* feat: do not update url on detection type change

* chore: pr clean up

* feat: remove chart container background
2024-10-14 10:31:02 +05:30

162 lines
4.2 KiB
TypeScript

import getLabelName from 'lib/getLabelName';
import { colors } from 'lib/getRandomColor';
import { cloneDeep, isUndefined } from 'lodash-es';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { QueryData } from 'types/api/widgets/getQuery';
function getXAxisTimestamps(seriesList: QueryData[]): number[] {
const timestamps = new Set();
seriesList.forEach((series: { values: [number, string][] }) => {
series.values.forEach((value) => {
timestamps.add(value[0]);
});
});
const timestampsArr: number[] | unknown[] = Array.from(timestamps) || [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return timestampsArr.sort((a, b) => a - b);
}
function fillMissingXAxisTimestamps(timestampArr: number[], data: any[]): any {
// Generate a set of all timestamps in the range
const allTimestampsSet = new Set(timestampArr);
const processedData = cloneDeep(data);
// Fill missing timestamps with null values
processedData.forEach((entry: { values: (number | null)[][] }) => {
const existingTimestamps = new Set(entry.values.map((value) => value[0]));
const missingTimestamps = Array.from(allTimestampsSet).filter(
(timestamp) => !existingTimestamps.has(timestamp),
);
missingTimestamps.forEach((timestamp) => {
const value = null;
entry.values.push([timestamp, value]);
});
entry.values.forEach((v) => {
if (Number.isNaN(v[1])) {
const replaceValue = null;
// eslint-disable-next-line no-param-reassign
v[1] = replaceValue;
} else if (v[1] !== null) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line no-param-reassign
v[1] = parseFloat(v[1]);
}
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
entry.values.sort((a, b) => a[0] - b[0]);
});
return processedData.map((entry: { values: [number, string][] }) =>
entry.values.map((value) => value[1]),
);
}
function getStackedSeries(val: any): any {
const series = cloneDeep(val) || [];
for (let i = series.length - 2; i >= 0; i--) {
for (let j = 0; j < series[i].length; j++) {
series[i][j] += series[i + 1][j];
}
}
return series;
}
export const getUPlotChartData = (
apiResponse?: MetricRangePayloadProps,
fillSpans?: boolean,
stackedBarChart?: boolean,
hiddenGraph?: {
[key: string]: boolean;
},
): any[] => {
const seriesList = apiResponse?.data?.result || [];
const timestampArr = getXAxisTimestamps(seriesList);
const yAxisValuesArr = fillMissingXAxisTimestamps(timestampArr, seriesList);
return [
timestampArr,
...(stackedBarChart && isUndefined(hiddenGraph)
? getStackedSeries(yAxisValuesArr)
: yAxisValuesArr),
];
};
const processAnomalyDetectionData = (
anomalyDetectionData: any,
): Record<string, { data: number[][]; color: string }> => {
if (!anomalyDetectionData) {
return {};
}
const processedData: Record<
string,
{ data: number[][]; color: string; legendLabel: string }
> = {};
for (
let queryIndex = 0;
queryIndex < anomalyDetectionData.length;
queryIndex++
) {
const {
series,
predictedSeries,
upperBoundSeries,
lowerBoundSeries,
queryName,
legend,
} = anomalyDetectionData[queryIndex];
for (let index = 0; index < series?.length; index++) {
const label = getLabelName(
series[index].labels,
queryName || '', // query
legend || '',
);
const objKey = `${queryName}-${label}`;
processedData[objKey] = {
data: [
series[index].values.map((v: { timestamp: number }) => v.timestamp / 1000),
series[index].values.map((v: { value: number }) => v.value),
predictedSeries[index].values.map((v: { value: number }) => v.value),
upperBoundSeries[index].values.map((v: { value: number }) => v.value),
lowerBoundSeries[index].values.map((v: { value: number }) => v.value),
],
color: colors[index],
legendLabel: label,
};
}
}
return processedData;
};
export const getUplotChartDataForAnomalyDetection = (
apiResponse?: MetricRangePayloadProps,
): Record<
string,
{
[x: string]: any;
data: number[][];
color: string;
}
> => {
const anomalyDetectionData = apiResponse?.data?.newResult?.data?.result;
return processAnomalyDetectionData(anomalyDetectionData);
};