Skip to content

Commit 87b6a6c

Browse files
authored
Merge pull request #259 from easyops-cn/steve/ai-portal-730
fix(): support dashboard comparison
2 parents aeff374 + 13af09c commit 87b6a6c

File tree

3 files changed

+170
-21
lines changed

3 files changed

+170
-21
lines changed

bricks/ai-portal/src/cruise-canvas/NodeView/NodeView.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ export function NodeView({ job, active }: NodeViewProps): JSX.Element {
9090
}
9191
if (component.componentName === "dashboard") {
9292
const widgets = component?.properties?.widgets;
93-
if (Array.isArray(widgets) && widgets.length >= 7) {
93+
if (
94+
Array.isArray(widgets) &&
95+
widgets.length >= (component.properties!.groupField ? 3 : 7)
96+
) {
9497
return true;
9598
}
9699
}

bricks/ai-portal/src/cruise-canvas/utils/converters/convertDashboard.ts

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,23 @@ export default async function convertDashboard(
5555
view: ViewWithInfo,
5656
options: ConvertViewOptions
5757
): Promise<BrickConf> {
58-
const { dataSource, widgets } = properties as {
58+
const {
59+
dataSource,
60+
groupField: _groupField,
61+
widgets,
62+
} = properties as {
5963
dataSource: string;
64+
groupField?: string;
6065
widgets: Array<Widget>;
6166
};
6267

68+
const groupField = _groupField ? "#showKey" : undefined;
69+
6370
const { isString, expression, usedContexts } = parseDataSource(dataSource);
6471

65-
const chartData = isString ? `<%= (${expression}).list %>` : dataSource;
72+
const chartData = isString
73+
? `<%= CTX.__builtin_fn_extractList((${expression})) %>`
74+
: dataSource;
6675

6776
if (options.expanded) {
6877
let mergedWidgets = widgets as MergedWidget[];
@@ -116,7 +125,8 @@ export default async function convertDashboard(
116125
},
117126
children: mergedWidgets.map((widget) => {
118127
const { title, /* type, */ metric, size, precision, min, max } = widget;
119-
const colorCount = widget.relevantMetrics?.length ?? 1;
128+
const colorCount =
129+
widget.relevantMetrics?.length ?? (groupField ? 2 : 1);
120130
const colors = Array.from(
121131
{ length: colorCount },
122132
(_, i) => COLORS[(colorCursor + i) % COLORS.length]
@@ -128,7 +138,19 @@ export default async function convertDashboard(
128138
properties: {
129139
data: chartData,
130140
xField: "time",
131-
yFields: widget.relevantMetrics ?? [metric.id],
141+
...(widget.relevantMetrics
142+
? {
143+
// yFields: widget.relevantMetrics,
144+
yFields: `<% CTX.__builtin_fn_getMetricDisplayNames((${expression}).displayNameList, ${JSON.stringify(
145+
widget.relevantMetrics
146+
)}) %>`,
147+
}
148+
: {
149+
// yField: metric.id,
150+
yField: `<% CTX.__builtin_fn_getMetricDisplayNames((${expression}).displayNameList, [${JSON.stringify(
151+
metric.id
152+
)}])[0] %>`,
153+
}),
132154
...(widget.counterMetric
133155
? {
134156
forceAbsoluteNumbers: true,
@@ -141,7 +163,7 @@ export default async function convertDashboard(
141163
: null),
142164
height: size === "large" ? 230 : 200,
143165
timeFormat: "HH:mm",
144-
...(widget.relevantMetrics
166+
...(widget.relevantMetrics || groupField
145167
? null
146168
: {
147169
areaOptions: {
@@ -164,8 +186,9 @@ export default async function convertDashboard(
164186
shape: "smooth",
165187
},
166188
},
189+
groupField,
167190
areaShape: "smooth",
168-
legends: size === "large",
191+
legends: !!(widget.relevantMetrics || groupField),
169192
colors: colors,
170193
tooltip: {
171194
marker: {
@@ -221,18 +244,82 @@ export default async function convertDashboard(
221244
};
222245
}
223246

247+
if (groupField) {
248+
return {
249+
brick: "div",
250+
properties: {
251+
style: {
252+
display: "flex",
253+
flexDirection: "column",
254+
gap: "8px",
255+
},
256+
},
257+
children: [
258+
{
259+
brick: ":forEach",
260+
dataSource: `<%= CTX.__builtin_fn_groupMetricData(CTX.__builtin_fn_extractList((${expression})), ${JSON.stringify(groupField)}) %>`,
261+
children: [
262+
{
263+
brick: "div",
264+
properties: {
265+
textContent: "<% `${ITEM.group}:` %>",
266+
style: {
267+
fontWeight: "500",
268+
},
269+
},
270+
},
271+
{
272+
brick: "div",
273+
properties: {
274+
style: {
275+
display: "grid",
276+
gridTemplateColumns: "repeat(auto-fill, minmax(205px, 1fr))",
277+
gap: "10px",
278+
marginBottom: "8px",
279+
},
280+
},
281+
children: widgets.map((widget, i) => {
282+
const { title, type, metric, precision } = widget;
283+
return {
284+
brick: "ai-portal.stat-with-mini-chart",
285+
properties: {
286+
size: "small",
287+
label: title || metric.id,
288+
data: "<% ITEM.list %>",
289+
xField: "time",
290+
yField: metric.id,
291+
lineColor: COLORS[i % COLORS.length],
292+
...(metric.unit === "percent(1)"
293+
? { min: 0, max: 1, showArea: true }
294+
: metric.unit === "percent(100)" || metric.unit === "%"
295+
? { min: 0, max: 100, showArea: true }
296+
: { showArea: type === "area" }),
297+
value: `<%= CTX.__builtin_fn_getLatestMetricValue(ITEM.list, ${JSON.stringify(
298+
{
299+
metric,
300+
precision,
301+
}
302+
)}) %>`,
303+
},
304+
};
305+
}),
306+
},
307+
],
308+
},
309+
],
310+
};
311+
}
312+
224313
return {
225314
brick: "div",
226315
properties: {
227316
style: {
228317
display: "grid",
229318
gridTemplateColumns: "repeat(auto-fill, minmax(205px, 1fr))",
230319
gap: "10px",
231-
// gridTemplateColumns: "repeat(auto-fill, minmax(375px, 1fr))",
232-
// gap: "12px",
233320
},
234321
},
235-
children: widgets.map((widget) => {
322+
children: widgets.map((widget, i) => {
236323
const { title, type, metric, precision } = widget;
237324
return {
238325
brick: "ai-portal.stat-with-mini-chart",
@@ -242,14 +329,16 @@ export default async function convertDashboard(
242329
data: chartData,
243330
xField: "time",
244331
yField: metric.id,
245-
lineColor: "#295DFF",
332+
lineColor: COLORS[i % COLORS.length],
246333
...(metric.unit === "percent(1)"
247334
? { min: 0, max: 1, showArea: true }
248335
: metric.unit === "percent(100)" || metric.unit === "%"
249336
? { min: 0, max: 100, showArea: true }
250337
: { showArea: type === "area" }),
251338
value: `<%= CTX.__builtin_fn_getLatestMetricValue((${
252-
isString ? expression : JSON.stringify(dataSource ?? null)
339+
isString
340+
? `CTX.__builtin_fn_extractList((${expression}))`
341+
: JSON.stringify(dataSource ?? null)
253342
}), ${JSON.stringify({
254343
metric,
255344
precision,

bricks/ai-portal/src/cruise-canvas/utils/converters/convertView.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,21 +120,67 @@ export async function convertView(
120120
{
121121
name: "__builtin_fn_getLatestMetricValue",
122122
value: function getLatestMetricValue(
123-
data: {
124-
list?: Record<string, any>[];
125-
},
123+
list: Record<string, any>[] | undefined,
126124
{
127125
metric,
128126
precision,
129127
}: { metric: { id: string; unit: string }; precision?: number }
130128
) {
131-
const value = data?.list?.findLast?.(
132-
(item) => item[metric.id] != null
133-
)?.[metric.id];
129+
const value = list?.findLast?.((item) => item[metric.id] != null)?.[
130+
metric.id
131+
];
134132
const unit = metric.unit === "load" ? "" : metric.unit;
135133
return pipes.unitFormat(value, unit, precision).join("");
136134
},
137135
},
136+
{
137+
name: "__builtin_fn_extractList",
138+
value: function extractList<T = unknown>(data: T[] | { list: T[] }): T[] {
139+
if (Array.isArray(data)) {
140+
return data;
141+
}
142+
return data?.list;
143+
},
144+
},
145+
{
146+
name: "__builtin_fn_groupMetricData",
147+
value: function groupMetricData(
148+
list: Record<string, any>[],
149+
groupField: string
150+
) {
151+
if (!list || !Array.isArray(list) || list.length === 0) {
152+
return [];
153+
}
154+
const grouped = new Map<
155+
string,
156+
{ group: string; list: Record<string, any>[] }
157+
>();
158+
for (const item of list) {
159+
const key = item[groupField];
160+
let groupedList = grouped.get(key);
161+
if (!groupedList) {
162+
grouped.set(key, (groupedList = { group: key, list: [] }));
163+
}
164+
groupedList.list.push(item);
165+
}
166+
return Array.from(grouped.values());
167+
},
168+
},
169+
{
170+
name: "__builtin_fn_getMetricDisplayNames",
171+
value: function getMetricDisplayNames(
172+
displayNameList:
173+
| { metric_name: string; metric_display_name: string }[]
174+
| undefined,
175+
metricNames: string[]
176+
): string[] {
177+
return metricNames.map(
178+
(metricName) =>
179+
displayNameList?.find((item) => item.metric_name === metricName)
180+
?.metric_display_name ?? metricName
181+
);
182+
},
183+
},
138184
{
139185
name: "SIZE",
140186
value: options.expanded ? "medium" : "small",
@@ -162,9 +208,20 @@ function convertDataSourcesToContext(dataSources: DataSource[]): ContextConf[] {
162208
resolve: {
163209
useProvider: `${dataSource.api.name}:${dataSource.api.version}`,
164210
params: dataSource.params,
165-
...(dataSource.transform
166-
? { transform: { value: dataSource.transform } }
167-
: null),
211+
...(dataSource.api.name === "easyops.api.data_exchange.olap@Query"
212+
? {
213+
params: {
214+
...dataSource.params,
215+
translate: ["#showKey"],
216+
limit: undefined,
217+
limitBy: undefined,
218+
order: undefined,
219+
displayName: true,
220+
},
221+
}
222+
: dataSource.transform
223+
? { transform: { value: dataSource.transform } }
224+
: null),
168225
},
169226
track: true,
170227
}));

0 commit comments

Comments
 (0)