Suggestions: Update all suggestions suppliers to be functions (#113986)

* Suggestions: Convert panels to use function supplier

* rework deaggregation

* BarGauge

* cleanup and make consistent the deaggregation in suggestions

* Candlestick

* Implement timeseries and clean up some things that can already be deleted

* spotted some typos in self-review

* restore PanelDataSummary deprecated fields, we wont delete till Grafana 13

* change deprecation message

* remove some unused imports

* run prettier

* update radialbar defaults logic

* update tests and logic to DRY up the reduceOptions a bit and more thoroughly test the output

* Trend: Improve suggestions

* updates from review

* add unique DataFrameType list to PanelDataSummary

* add histogram suggestions

* rework panelDataSummary to be a class, change some things

* further boil down PanelDataSummary

* Improve FlameGgraph suggestions

* geomap and other defaults

* reorder the single frame with string and number test
This commit is contained in:
Paul Marbach
2025-11-26 08:30:38 -08:00
committed by GitHub
parent ca8cad68c8
commit ebaccc781b
53 changed files with 1573 additions and 1275 deletions
@@ -381,6 +381,11 @@ export class PanelPlugin<
const appender = builder.getListAppender<TOptions, TFieldConfigOptions>({
pluginId: this.meta.id,
name: this.meta.name,
options: {},
fieldConfig: {
defaults: {},
overrides: [],
},
});
const result = supplier(builder.dataSummary);
@@ -1,19 +1,27 @@
import { PreferredVisualisationType } from '../../types/data';
import { DataFrame, FieldType } from '../../types/dataFrame';
import { DataFrameType } from '../../types/dataFrameTypes';
/**
* @alpha
*/
export interface PanelDataSummary {
hasData?: boolean;
rowCountTotal: number;
/** max number of rows in any given dataframe in the panel data */
rowCountMax: number;
frameCount: number;
fieldCount: number;
/** max number of fields in any given dataframe in the panel data */
fieldCountMax: number;
/** given a field type, return the number of fields across all dataframes which match this type */
fieldCountByType: (type: FieldType) => number;
/** returns true if any fields in any frames match the field type */
hasFieldType: (type: FieldType) => boolean;
/** The first frame that set's this value */
preferredVisualisationType?: PreferredVisualisationType;
/* returns true if any of the frames in this panel data summary have the type */
hasDataFrameType: (type: DataFrameType) => boolean;
/* returns true if any of the frames in this panel data summary have the type */
hasPreferredVisualisationType: (type: PreferredVisualisationType) => boolean;
/** pass along a reference to the DataFrame array in case it's needed by the plugin */
rawFrames?: DataFrame[];
/* --- DEPRECATED FIELDS BELOW --- */
/** @deprecated use PanelDataSummary.fieldCountByType(FieldType.number) */
@@ -23,60 +31,114 @@ export interface PanelDataSummary {
/** @deprecated use PanelDataSummary.fieldCountByType(FieldType.string) */
stringFieldCount: number;
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.number) */
hasNumberField?: boolean;
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.time) */
hasTimeField?: boolean;
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.time) */
hasNumberField?: boolean;
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.string) */
hasStringField?: boolean;
}
/**
* @alpha
*/
class PanelDataSummaryImpl implements PanelDataSummary {
public rowCountTotal = 0;
/** max number of rows in any single dataframe in the panel data */
public rowCountMax = 0;
public fieldCount = 0;
/** max number of fields in any single dataframe in the panel data */
public fieldCountMax = 0;
private countByType: Partial<Record<FieldType, number>> = {};
private preferredVisualisationTypes: Set<PreferredVisualisationType> = new Set<PreferredVisualisationType>();
private dataFrameTypes: Set<DataFrameType> = new Set<DataFrameType>();
public get hasData(): boolean {
return this.rowCountTotal > 0;
}
public get frameCount(): number {
return this.rawFrames?.length ?? 0;
}
constructor(public rawFrames?: DataFrame[]) {
this._processFrames();
}
private _processFrames() {
for (const frame of this.rawFrames ?? []) {
this.rowCountTotal += frame.length;
if (frame.meta?.preferredVisualisationType) {
this.preferredVisualisationTypes.add(frame.meta.preferredVisualisationType);
}
if (frame.meta?.type) {
this.dataFrameTypes.add(frame.meta.type);
}
for (const field of frame.fields) {
this.fieldCount++;
this.countByType[field.type] = (this.countByType[field.type] || 0) + 1;
}
if (frame.length > this.rowCountMax) {
this.rowCountMax = frame.length;
}
if (frame.fields.length > this.fieldCountMax) {
this.fieldCountMax = frame.fields.length;
}
}
}
public fieldCountByType(type: FieldType): number {
return this.countByType[type] ?? 0;
}
public hasFieldType(type: FieldType): boolean {
return this.fieldCountByType(type) > 0;
}
public hasPreferredVisualisationType(type: PreferredVisualisationType): boolean {
return this.preferredVisualisationTypes.has(type);
}
public hasDataFrameType(type: DataFrameType): boolean {
return this.dataFrameTypes.has(type);
}
/**** DEPRECATED ****/
/** @deprecated use PanelDataSummary.fieldCountByType(FieldType.number) */
public get numberFieldCount(): number {
return this.fieldCountByType(FieldType.number);
}
/** @deprecated use PanelDataSummary.fieldCountByType(FieldType.time) */
public get timeFieldCount(): number {
return this.fieldCountByType(FieldType.time);
}
/** @deprecated use PanelDataSummary.fieldCountByType(FieldType.string) */
public get stringFieldCount() {
return this.fieldCountByType(FieldType.string);
}
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.number) */
public get hasTimeField() {
return this.fieldCountByType(FieldType.time) > 0;
}
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.time) */
public get hasNumberField() {
return this.fieldCountByType(FieldType.number) > 0;
}
/** @deprecated use PanelDataSummary.hasFieldType(FieldType.string) */
public get hasStringField() {
return this.fieldCountByType(FieldType.string) > 0;
}
}
/**
* @alpha
* given a list of dataframes, summarize attributes of those frames for features like suggestions.
* @param frames - dataframes to summarize
* @returns summary of the dataframes
*/
export function getPanelDataSummary(frames: DataFrame[] = []): PanelDataSummary {
let rowCountTotal = 0;
let rowCountMax = 0;
let fieldCount = 0;
const countByType: Partial<Record<FieldType, number>> = {};
let preferredVisualisationType: PreferredVisualisationType | undefined;
for (const frame of frames) {
rowCountTotal += frame.length;
if (frame.meta?.preferredVisualisationType) {
preferredVisualisationType = frame.meta.preferredVisualisationType;
}
for (const field of frame.fields) {
fieldCount++;
countByType[field.type] = (countByType[field.type] || 0) + 1;
}
if (frame.length > rowCountMax) {
rowCountMax = frame.length;
}
}
const fieldCountByType = (f: FieldType) => countByType[f] ?? 0;
return {
rowCountTotal,
rowCountMax,
fieldCount,
preferredVisualisationType,
frameCount: frames.length,
hasData: rowCountTotal > 0,
hasFieldType: (f: FieldType) => fieldCountByType(f) > 0,
fieldCountByType,
// deprecated
numberFieldCount: fieldCountByType(FieldType.number),
timeFieldCount: fieldCountByType(FieldType.time),
stringFieldCount: fieldCountByType(FieldType.string),
hasTimeField: fieldCountByType(FieldType.time) > 0,
hasNumberField: fieldCountByType(FieldType.number) > 0,
hasStringField: fieldCountByType(FieldType.string) > 0,
};
export function getPanelDataSummary(frames?: DataFrame[]): PanelDataSummary {
return new PanelDataSummaryImpl(frames);
}
@@ -72,7 +72,7 @@ interface IndexOptions {
asPercentile: boolean;
}
const defaultReduceOptions: ReduceOptions = {
const defaultNumericVizOptions: ReduceOptions = {
reducer: ReducerID.sum,
};
@@ -149,10 +149,10 @@ export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransf
switch (mode) {
case CalculateFieldMode.ReduceRow:
creator = getReduceRowCreator(defaults(options.reduce, defaultReduceOptions), data);
creator = getReduceRowCreator(defaults(options.reduce, defaultNumericVizOptions), data);
break;
case CalculateFieldMode.CumulativeFunctions:
creator = getCumulativeCreator(defaults(options.cumulative, defaultReduceOptions), data);
creator = getCumulativeCreator(defaults(options.cumulative, defaultNumericVizOptions), data);
break;
case CalculateFieldMode.WindowFunctions:
creator = getWindowCreator(defaults(options.window, defaultWindowOptions), data);
@@ -75,7 +75,9 @@ export interface VisualizationSuggestion<TOptions extends unknown = {}, TFieldCo
* mutate the suggestion object which is passed in as the first argument.
*/
previewModifier?: (suggestion: VisualizationSuggestion<TOptions, TFieldConfig>) => void;
/** @deprecated this will no longer be supported in the new Suggestions UI. */
icon?: string;
/** @deprecated this will no longer be supported in the new Suggestions UI. */
imgSrc?: string;
};
}