use TableData for timeseries in react

pull/15864/head
ryan 6 years ago
parent 56682cb1eb
commit abf015ace2
  1. 10
      packages/grafana-ui/src/types/data.ts
  2. 9
      packages/grafana-ui/src/types/panel.ts
  3. 20
      packages/grafana-ui/src/utils/processTimeSeries.ts
  4. 12
      public/app/core/table_model.ts
  5. 33
      public/app/features/dashboard/dashgrid/DataPanel.tsx
  6. 10
      public/app/features/dashboard/dashgrid/PanelChrome.tsx
  7. 46
      public/app/features/dashboard/utils/panel.ts
  8. 22
      public/app/plugins/panel/gauge/GaugePanel.tsx
  9. 11
      public/app/plugins/panel/graph2/GraphPanel.tsx

@ -53,12 +53,9 @@ export interface TimeSeriesVMs {
length: number;
}
interface Column {
text: string;
title?: string;
type?: string;
sort?: boolean;
desc?: boolean;
export interface Column {
text: string; // name
type?: 'time' | 'number' | 'string' | 'object';
filterable?: boolean;
unit?: string;
}
@ -67,5 +64,4 @@ export interface TableData {
columns: Column[];
rows: any[];
type: string;
columnMap: any;
}

@ -1,12 +1,12 @@
import { ComponentClass } from 'react';
import { TimeSeries, LoadingState, TableData } from './data';
import { LoadingState, TableData } from './data';
import { TimeRange } from './time';
import { ScopedVars } from './datasource';
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
export interface PanelProps<T = any> {
panelData: PanelData;
data?: TableData[];
timeRange: TimeRange;
loading: LoadingState;
options: T;
@ -16,11 +16,6 @@ export interface PanelProps<T = any> {
replaceVariables: InterpolateFunction;
}
export interface PanelData {
timeSeries?: TimeSeries[];
tableData?: TableData;
}
export interface PanelEditorProps<T = any> {
options: T;
onOptionsChange: (options: T) => void;

@ -4,17 +4,19 @@ import isNumber from 'lodash/isNumber';
import { colors } from './colors';
// Types
import { TimeSeries, TimeSeriesVMs, NullValueMode, TimeSeriesValue } from '../types';
import { TimeSeriesVMs, NullValueMode, TimeSeriesValue, TableData } from '../types';
interface Options {
timeSeries: TimeSeries[];
data: TableData[];
xColumn: number; // Time
yColumn: number; // Value
nullValueMode: NullValueMode;
}
export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeSeriesVMs {
const vmSeries = timeSeries.map((item, index) => {
export function processTimeSeries({ data, xColumn, yColumn, nullValueMode }: Options): TimeSeriesVMs {
const vmSeries = data.map((item, index) => {
const colorIndex = index % colors.length;
const label = item.target;
const label = item.columns[yColumn].text;
const result = [];
// stat defaults
@ -42,9 +44,9 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS
let previousValue = 0;
let previousDeltaUp = true;
for (let i = 0; i < item.datapoints.length; i++) {
currentValue = item.datapoints[i][0];
currentTime = item.datapoints[i][1];
for (let i = 0; i < item.rows.length; i++) {
currentValue = item.rows[i][yColumn];
currentTime = item.rows[i][xColumn];
if (typeof currentTime !== 'number') {
continue;
@ -95,7 +97,7 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS
if (previousValue > currentValue) {
// counter reset
previousDeltaUp = false;
if (i === item.datapoints.length - 1) {
if (i === item.rows.length - 1) {
// reset on last
delta += currentValue;
}

@ -1,17 +1,15 @@
import _ from 'lodash';
import { Column, TableData } from '@grafana/ui';
interface Column {
text: string;
// This class mutates and uses the extra column fields
interface ColumnEX extends Column {
title?: string;
type?: string;
sort?: boolean;
desc?: boolean;
filterable?: boolean;
unit?: string;
}
export default class TableModel {
columns: Column[];
export default class TableModel implements TableData {
columns: ColumnEX[];
rows: any[];
type: string;
columnMap: any;

@ -11,16 +11,16 @@ import {
DataQueryResponse,
DataQueryError,
LoadingState,
PanelData,
TableData,
TimeRange,
TimeSeries,
ScopedVars,
} from '@grafana/ui';
import { toTableData } from '../utils/panel';
interface RenderProps {
loading: LoadingState;
panelData: PanelData;
data: TableData[];
}
export interface Props {
@ -44,7 +44,7 @@ export interface State {
isFirstLoad: boolean;
loading: LoadingState;
response: DataQueryResponse;
panelData: PanelData;
data?: TableData[];
}
export class DataPanel extends Component<Props, State> {
@ -64,7 +64,6 @@ export class DataPanel extends Component<Props, State> {
response: {
data: [],
},
panelData: {},
isFirstLoad: true,
};
}
@ -146,10 +145,12 @@ export class DataPanel extends Component<Props, State> {
onDataResponse(resp);
}
const data = toTableData(resp.data);
console.log('Converted:', data);
this.setState({
loading: LoadingState.Done,
response: resp,
panelData: this.getPanelData(resp),
data,
isFirstLoad: false,
});
} catch (err) {
@ -172,23 +173,9 @@ export class DataPanel extends Component<Props, State> {
}
};
getPanelData(response: DataQueryResponse) {
if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') {
return {
tableData: response.data[0] as TableData,
timeSeries: null,
};
}
return {
timeSeries: response.data as TimeSeries[],
tableData: null,
};
}
render() {
const { queries } = this.props;
const { loading, isFirstLoad, panelData } = this.state;
const { loading, isFirstLoad, data } = this.state;
// do not render component until we have first data
if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
@ -203,10 +190,12 @@ export class DataPanel extends Component<Props, State> {
);
}
console.log('RENDER', data);
return (
<>
{loading === LoadingState.Loading && this.renderLoadingState()}
{this.props.children({ loading, panelData })}
{this.props.children({ loading, data })}
</>
);
}

@ -18,7 +18,7 @@ import { profiler } from 'app/core/profiler';
// Types
import { DashboardModel, PanelModel } from '../state';
import { PanelPlugin } from 'app/types';
import { DataQueryResponse, TimeRange, LoadingState, PanelData, DataQueryError } from '@grafana/ui';
import { DataQueryResponse, TimeRange, LoadingState, TableData, DataQueryError } from '@grafana/ui';
import { ScopedVars } from '@grafana/ui';
import variables from 'sass/_variables.generated.scss';
@ -142,7 +142,7 @@ export class PanelChrome extends PureComponent<Props, State> {
return this.hasPanelSnapshot ? snapshotDataToPanelData(this.props.panel) : null;
}
renderPanelPlugin(loading: LoadingState, panelData: PanelData, width: number, height: number): JSX.Element {
renderPanelPlugin(loading: LoadingState, data: TableData[], width: number, height: number): JSX.Element {
const { panel, plugin } = this.props;
const { timeRange, renderCounter } = this.state;
const PanelComponent = plugin.exports.reactPanel.panel;
@ -157,7 +157,7 @@ export class PanelChrome extends PureComponent<Props, State> {
<div className="panel-content">
<PanelComponent
loading={loading}
panelData={panelData}
data={data}
timeRange={timeRange}
options={panel.getOptions(plugin.exports.reactPanel.defaults)}
width={width - 2 * variables.panelhorizontalpadding}
@ -188,8 +188,8 @@ export class PanelChrome extends PureComponent<Props, State> {
onDataResponse={this.onDataResponse}
onError={this.onDataError}
>
{({ loading, panelData }) => {
return this.renderPanelPlugin(loading, panelData, width, height);
{({ loading, data }) => {
return this.renderPanelPlugin(loading, data, width, height);
}}
</DataPanel>
) : (

@ -4,8 +4,7 @@ import store from 'app/core/store';
// Models
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { PanelData, TimeRange, TimeSeries } from '@grafana/ui';
import { TableData } from '@grafana/ui/src';
import { TableData, TimeRange, TimeSeries } from '@grafana/ui';
// Utils
import { isString as _isString } from 'lodash';
@ -173,16 +172,37 @@ export function getResolution(panel: PanelModel): number {
const isTimeSeries = (data: any): data is TimeSeries => data && data.hasOwnProperty('datapoints');
const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns');
export const snapshotDataToPanelData = (panel: PanelModel): PanelData => {
const snapshotData = panel.snapshotData;
if (isTimeSeries(snapshotData[0])) {
return {
timeSeries: snapshotData,
} as PanelData;
} else if (isTableData(snapshotData[0])) {
return {
tableData: snapshotData[0],
} as PanelData;
export const snapshotDataToPanelData = (panel: PanelModel): TableData[] => {
return toTableData(panel.snapshotData);
};
export const toTableData = (results: any[]): TableData[] => {
if (!results) {
return [];
}
throw new Error('snapshotData is invalid:' + snapshotData.toString());
return results.map(data => {
if (isTableData(data)) {
return data as TableData;
}
if (isTimeSeries(data)) {
const ts = data as TimeSeries;
return {
type: 'timeseries',
columns: [
{
text: ts.target,
unit: ts.unit,
type: 'number', // Is this really true?
},
{
text: 'time',
type: 'time',
},
],
rows: ts.datapoints,
} as TableData;
}
console.warn('Can not convert', data);
throw new Error('Unsupported data format');
});
};

@ -22,29 +22,39 @@ export class GaugePanel extends Component<Props, State> {
this.state = {
value: this.findValue(props),
};
console.log('CONSTRUCTOR!', this.props.data);
}
componentDidUpdate(prevProps: Props) {
if (this.props.panelData !== prevProps.panelData) {
console.log('UPDATE', this.props.data);
if (this.props.data !== prevProps.data) {
this.setState({ value: this.findValue(this.props) });
}
}
findValue(props: Props): number | null {
const { panelData, options } = props;
const { data, options } = props;
const { valueOptions } = options;
if (panelData.timeSeries) {
console.log('FIND VALUE', data);
if (data) {
// For now, assume timeseries defaults
const xColumn = 1; // time
const yColumn = 0; // value
const vmSeries = processTimeSeries({
timeSeries: panelData.timeSeries,
data,
xColumn,
yColumn,
nullValueMode: NullValueMode.Null,
});
console.log('GOT', vmSeries);
if (vmSeries[0]) {
return vmSeries[0].stats[valueOptions.stat];
}
} else if (panelData.tableData) {
return panelData.tableData.rows[0].find(prop => prop > 0);
}
return null;
}

@ -16,13 +16,18 @@ interface Props extends PanelProps<Options> {}
export class GraphPanel extends PureComponent<Props> {
render() {
const { panelData, timeRange, width, height } = this.props;
const { data, timeRange, width, height } = this.props;
const { showLines, showBars, showPoints } = this.props.options;
let vmSeries: TimeSeriesVMs;
if (panelData.timeSeries) {
if (data) {
// For now, assume timeseries defaults
const xColumn = 1; // time
const yColumn = 0; // value
vmSeries = processTimeSeries({
timeSeries: panelData.timeSeries,
data,
xColumn,
yColumn,
nullValueMode: NullValueMode.Ignore,
});
}

Loading…
Cancel
Save