Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
77b3da3e
Commit
77b3da3e
authored
Mar 25, 2019
by
Ryan McKinley
Committed by
Torkel Ödegaard
Mar 25, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor(data models): Renamed TableData to SeriesData (#16185)
parent
c7d10826
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
283 additions
and
253 deletions
+283
-253
packages/grafana-ui/src/components/Table/Table.story.tsx
+4
-4
packages/grafana-ui/src/components/Table/Table.tsx
+15
-15
packages/grafana-ui/src/components/Table/TableCellBuilder.tsx
+5
-5
packages/grafana-ui/src/components/Table/TableInputCSV.story.tsx
+2
-2
packages/grafana-ui/src/components/Table/TableInputCSV.test.tsx
+2
-2
packages/grafana-ui/src/components/Table/TableInputCSV.tsx
+4
-4
packages/grafana-ui/src/components/Table/examples.ts
+27
-27
packages/grafana-ui/src/types/data.ts
+38
-27
packages/grafana-ui/src/types/panel.ts
+2
-2
packages/grafana-ui/src/utils/__snapshots__/processTableData.test.ts.snap
+10
-10
packages/grafana-ui/src/utils/processTableData.test.ts
+30
-30
packages/grafana-ui/src/utils/processTableData.ts
+70
-52
packages/grafana-ui/src/utils/statsCalculator.test.ts
+8
-8
packages/grafana-ui/src/utils/statsCalculator.ts
+23
-23
public/app/core/table_model.ts
+1
-0
public/app/features/dashboard/dashgrid/DataPanel.test.tsx
+10
-10
public/app/features/dashboard/dashgrid/DataPanel.tsx
+10
-10
public/app/features/dashboard/dashgrid/PanelChrome.tsx
+4
-4
public/app/plugins/datasource/influxdb/influx_series.ts
+2
-2
public/app/plugins/datasource/prometheus/result_transformer.ts
+2
-2
public/app/plugins/panel/graph2/GraphPanel.tsx
+6
-6
public/app/plugins/panel/singlestat2/SingleStatPanel.tsx
+8
-8
No files found.
packages/grafana-ui/src/components/Table/Table.story.tsx
View file @
77b3da3e
...
...
@@ -4,7 +4,7 @@ import { Table } from './Table';
import
{
getTheme
}
from
'../../themes'
;
import
{
migratedTestTable
,
migratedTestStyles
,
simpleTable
}
from
'./examples'
;
import
{
ScopedVars
,
Table
Data
,
GrafanaThemeType
}
from
'../../types/index'
;
import
{
ScopedVars
,
Series
Data
,
GrafanaThemeType
}
from
'../../types/index'
;
import
{
withFullSizeStory
}
from
'../../utils/storybook/withFullSizeStory'
;
import
{
number
,
boolean
}
from
'@storybook/addon-knobs'
;
...
...
@@ -29,11 +29,11 @@ export function columnIndexToLeter(column: number) {
return
String
.
fromCharCode
(
A
+
c2
);
}
export
function
makeDummyTable
(
columnCount
:
number
,
rowCount
:
number
):
Table
Data
{
export
function
makeDummyTable
(
columnCount
:
number
,
rowCount
:
number
):
Series
Data
{
return
{
column
s
:
Array
.
from
(
new
Array
(
columnCount
),
(
x
,
i
)
=>
{
field
s
:
Array
.
from
(
new
Array
(
columnCount
),
(
x
,
i
)
=>
{
return
{
text
:
columnIndexToLeter
(
i
),
name
:
columnIndexToLeter
(
i
),
};
}),
rows
:
Array
.
from
(
new
Array
(
rowCount
),
(
x
,
rowId
)
=>
{
...
...
packages/grafana-ui/src/components/Table/Table.tsx
View file @
77b3da3e
...
...
@@ -12,9 +12,9 @@ import {
}
from
'react-virtualized'
;
import
{
Themeable
}
from
'../../types/theme'
;
import
{
sort
Table
Data
}
from
'../../utils/processTableData'
;
import
{
sort
Series
Data
}
from
'../../utils/processTableData'
;
import
{
Table
Data
,
InterpolateFunction
}
from
'@grafana/ui'
;
import
{
Series
Data
,
InterpolateFunction
}
from
'@grafana/ui'
;
import
{
TableCellBuilder
,
ColumnStyle
,
...
...
@@ -25,7 +25,7 @@ import {
import
{
stringToJsRegex
}
from
'../../utils/index'
;
export
interface
Props
extends
Themeable
{
data
:
Table
Data
;
data
:
Series
Data
;
minColumnWidth
:
number
;
showHeader
:
boolean
;
...
...
@@ -43,7 +43,7 @@ export interface Props extends Themeable {
interface
State
{
sortBy
?:
number
;
sortDirection
?:
SortDirectionType
;
data
:
Table
Data
;
data
:
Series
Data
;
}
interface
ColumnRenderInfo
{
...
...
@@ -108,17 +108,17 @@ export class Table extends Component<Props, State> {
// Update the data when data or sort changes
if
(
dataChanged
||
sortBy
!==
prevState
.
sortBy
||
sortDirection
!==
prevState
.
sortDirection
)
{
this
.
scrollToTop
=
true
;
this
.
setState
({
data
:
sort
Table
Data
(
data
,
sortBy
,
sortDirection
===
'DESC'
)
});
this
.
setState
({
data
:
sort
Series
Data
(
data
,
sortBy
,
sortDirection
===
'DESC'
)
});
}
}
/** Given the configuration, setup how each column gets rendered */
initColumns
(
props
:
Props
):
ColumnRenderInfo
[]
{
const
{
styles
,
data
,
width
,
minColumnWidth
}
=
props
;
const
columnWidth
=
Math
.
max
(
width
/
data
.
column
s
.
length
,
minColumnWidth
);
const
columnWidth
=
Math
.
max
(
width
/
data
.
field
s
.
length
,
minColumnWidth
);
return
data
.
column
s
.
map
((
col
,
index
)
=>
{
let
title
=
col
.
text
;
return
data
.
field
s
.
map
((
col
,
index
)
=>
{
let
title
=
col
.
name
;
let
style
:
ColumnStyle
|
null
=
null
;
// ColumnStyle
// Find the style based on the text
...
...
@@ -159,7 +159,7 @@ export class Table extends Component<Props, State> {
this
.
setState
({
sortBy
:
sort
,
sortDirection
:
dir
});
};
/** Converts the grid coordinates to
Table
Data coordinates */
/** Converts the grid coordinates to
Series
Data coordinates */
getCellRef
=
(
rowIndex
:
number
,
columnIndex
:
number
):
DataIndex
=>
{
const
{
showHeader
,
rotate
}
=
this
.
props
;
const
rowOffset
=
showHeader
?
-
1
:
0
;
...
...
@@ -187,17 +187,17 @@ export class Table extends Component<Props, State> {
const
{
columnIndex
,
rowIndex
,
style
}
=
cell
.
props
;
const
{
column
}
=
this
.
getCellRef
(
rowIndex
,
columnIndex
);
let
col
=
data
.
column
s
[
column
];
let
col
=
data
.
field
s
[
column
];
const
sorting
=
sortBy
===
column
;
if
(
!
col
)
{
col
=
{
text
:
'??'
+
columnIndex
+
'???'
,
name
:
'??'
+
columnIndex
+
'???'
,
};
}
return
(
<
div
className=
"gf-table-header"
style=
{
style
}
onClick=
{
()
=>
this
.
onCellClick
(
rowIndex
,
columnIndex
)
}
>
{
col
.
text
}
{
col
.
name
}
{
sorting
&&
<
SortIndicator
sortDirection=
{
sortDirection
}
/>
}
</
div
>
);
...
...
@@ -217,7 +217,7 @@ export class Table extends Component<Props, State> {
const
{
data
}
=
this
.
state
;
const
isHeader
=
row
<
0
;
const
rowData
=
isHeader
?
data
.
column
s
:
data
.
rows
[
row
];
const
rowData
=
isHeader
?
data
.
field
s
:
data
.
rows
[
row
];
const
value
=
rowData
?
rowData
[
column
]
:
''
;
const
builder
=
isHeader
?
this
.
headerBuilder
:
this
.
getTableCellBuilder
(
column
);
...
...
@@ -226,7 +226,7 @@ export class Table extends Component<Props, State> {
{
builder
({
value
,
row
:
rowData
,
column
:
data
.
column
s
[
column
],
column
:
data
.
field
s
[
column
],
table
:
this
,
props
,
})
}
...
...
@@ -242,7 +242,7 @@ export class Table extends Component<Props, State> {
const
{
showHeader
,
fixedHeader
,
fixedColumns
,
rotate
,
width
,
height
}
=
this
.
props
;
const
{
data
}
=
this
.
state
;
let
columnCount
=
data
.
column
s
.
length
;
let
columnCount
=
data
.
field
s
.
length
;
let
rowCount
=
data
.
rows
.
length
+
(
showHeader
?
1
:
0
);
let
fixedColumnCount
=
Math
.
min
(
fixedColumns
,
columnCount
);
...
...
packages/grafana-ui/src/components/Table/TableCellBuilder.tsx
View file @
77b3da3e
...
...
@@ -6,12 +6,12 @@ import { Table, Props } from './Table';
import
moment
from
'moment'
;
import
{
ValueFormatter
}
from
'../../utils/index'
;
import
{
GrafanaTheme
}
from
'../../types/theme'
;
import
{
getValueFormat
,
getColorFromHexRgbOrName
,
Column
}
from
'@grafana/ui'
;
import
{
getValueFormat
,
getColorFromHexRgbOrName
,
Field
}
from
'@grafana/ui'
;
import
{
InterpolateFunction
}
from
'../../types/panel'
;
export
interface
TableCellBuilderOptions
{
value
:
any
;
column
?:
Column
;
column
?:
Field
;
row
?:
any
[];
table
?:
Table
;
className
?:
string
;
...
...
@@ -74,7 +74,7 @@ export interface ColumnStyle {
// private replaceVariables: InterpolateFunction,
// private fmt?:ValueFormatter) {
export
function
getCellBuilder
(
schema
:
Column
,
style
:
ColumnStyle
|
null
,
props
:
Props
):
TableCellBuilder
{
export
function
getCellBuilder
(
schema
:
Field
,
style
:
ColumnStyle
|
null
,
props
:
Props
):
TableCellBuilder
{
if
(
!
style
)
{
return
simpleCellBuilder
;
}
...
...
@@ -154,12 +154,12 @@ class CellBuilderWithStyle {
private
mapper
:
ValueMapper
,
private
style
:
ColumnStyle
,
private
theme
:
GrafanaTheme
,
private
column
:
Column
,
private
column
:
Field
,
private
replaceVariables
:
InterpolateFunction
,
private
fmt
?:
ValueFormatter
)
{
//
console
.
log
(
'COLUMN'
,
column
.
text
,
theme
);
console
.
log
(
'COLUMN'
,
column
.
name
,
theme
);
}
getColorForValue
=
(
value
:
any
):
string
|
null
=>
{
...
...
packages/grafana-ui/src/components/Table/TableInputCSV.story.tsx
View file @
77b3da3e
...
...
@@ -3,7 +3,7 @@ import React from 'react';
import
{
storiesOf
}
from
'@storybook/react'
;
import
TableInputCSV
from
'./TableInputCSV'
;
import
{
action
}
from
'@storybook/addon-actions'
;
import
{
Table
Data
}
from
'../../types/data'
;
import
{
Series
Data
}
from
'../../types/data'
;
import
{
withCenteredStory
}
from
'../../utils/storybook/withCenteredStory'
;
const
TableInputStories
=
storiesOf
(
'UI/Table/Input'
,
module
);
...
...
@@ -15,7 +15,7 @@ TableInputStories.add('default', () => {
<
div
style=
{
{
width
:
'90%'
,
height
:
'90vh'
}
}
>
<
TableInputCSV
text=
{
'a,b,c
\
n1,2,3'
}
onTableParsed=
{
(
table
:
Table
Data
,
text
:
string
)
=>
{
onTableParsed=
{
(
table
:
Series
Data
,
text
:
string
)
=>
{
console
.
log
(
'Table'
,
table
,
text
);
action
(
'Table'
)(
table
,
text
);
}
}
...
...
packages/grafana-ui/src/components/Table/TableInputCSV.test.tsx
View file @
77b3da3e
...
...
@@ -2,7 +2,7 @@ import React from 'react';
import
renderer
from
'react-test-renderer'
;
import
TableInputCSV
from
'./TableInputCSV'
;
import
{
Table
Data
}
from
'../../types/data'
;
import
{
Series
Data
}
from
'../../types/data'
;
describe
(
'TableInputCSV'
,
()
=>
{
it
(
'renders correctly'
,
()
=>
{
...
...
@@ -10,7 +10,7 @@ describe('TableInputCSV', () => {
.
create
(
<
TableInputCSV
text=
{
'a,b,c
\
n1,2,3'
}
onTableParsed=
{
(
table
:
Table
Data
,
text
:
string
)
=>
{
onTableParsed=
{
(
table
:
Series
Data
,
text
:
string
)
=>
{
// console.log('Table:', table, 'from:', text);
}
}
/>
...
...
packages/grafana-ui/src/components/Table/TableInputCSV.tsx
View file @
77b3da3e
import
React
from
'react'
;
import
debounce
from
'lodash/debounce'
;
import
{
parseCSV
,
TableParseOptions
,
TableParseDetails
}
from
'../../utils/processTableData'
;
import
{
Table
Data
}
from
'../../types/data'
;
import
{
Series
Data
}
from
'../../types/data'
;
import
{
AutoSizer
}
from
'react-virtualized'
;
interface
Props
{
options
?:
TableParseOptions
;
text
:
string
;
onTableParsed
:
(
table
:
Table
Data
,
text
:
string
)
=>
void
;
onTableParsed
:
(
table
:
Series
Data
,
text
:
string
)
=>
void
;
}
interface
State
{
text
:
string
;
table
:
Table
Data
;
table
:
Series
Data
;
details
:
TableParseDetails
;
}
...
...
@@ -82,7 +82,7 @@ class TableInputCSV extends React.PureComponent<Props, State> {
<
div
className=
"gf-table-input-csv"
style=
{
{
width
,
height
}
}
>
<
textarea
placeholder=
"Enter CSV here..."
value=
{
this
.
state
.
text
}
onChange=
{
this
.
onTextChange
}
/>
<
footer
onClick=
{
this
.
onFooterClicked
}
className=
{
footerClassNames
}
>
Rows:
{
table
.
rows
.
length
}
, Columns:
{
table
.
column
s
.
length
}
Rows:
{
table
.
rows
.
length
}
, Columns:
{
table
.
field
s
.
length
}
{
hasErrors
?
<
i
className=
"fa fa-exclamation-triangle"
/>
:
<
i
className=
"fa fa-check-circle"
/>
}
</
footer
>
</
div
>
...
...
packages/grafana-ui/src/components/Table/examples.ts
View file @
77b3da3e
import
{
Table
Data
}
from
'../../types/data'
;
import
{
Series
Data
}
from
'../../types/data'
;
import
{
ColumnStyle
}
from
'./TableCellBuilder'
;
import
{
getColorDefinitionByName
}
from
'@grafana/ui'
;
...
...
@@ -7,23 +7,23 @@ const SemiDarkOrange = getColorDefinitionByName('semi-dark-orange');
export
const
migratedTestTable
=
{
type
:
'table'
,
column
s
:
[
{
text
:
'Time'
},
{
text
:
'Value'
},
{
text
:
'Colored'
},
{
text
:
'Undefined'
},
{
text
:
'String'
},
{
text
:
'United'
,
unit
:
'bps'
},
{
text
:
'Sanitized'
},
{
text
:
'Link'
},
{
text
:
'Array'
},
{
text
:
'Mapping'
},
{
text
:
'RangeMapping'
},
{
text
:
'MappingColored'
},
{
text
:
'RangeMappingColored'
},
field
s
:
[
{
name
:
'Time'
},
{
name
:
'Value'
},
{
name
:
'Colored'
},
{
name
:
'Undefined'
},
{
name
:
'String'
},
{
name
:
'United'
,
unit
:
'bps'
},
{
name
:
'Sanitized'
},
{
name
:
'Link'
},
{
name
:
'Array'
},
{
name
:
'Mapping'
},
{
name
:
'RangeMapping'
},
{
name
:
'MappingColored'
},
{
name
:
'RangeMappingColored'
},
],
rows
:
[[
1388556366666
,
1230
,
40
,
undefined
,
''
,
''
,
'my.host.com'
,
'host1'
,
[
'value1'
,
'value2'
],
1
,
2
,
1
,
2
]],
}
as
Table
Data
;
}
as
Series
Data
;
export
const
migratedTestStyles
:
ColumnStyle
[]
=
[
{
...
...
@@ -87,19 +87,19 @@ export const migratedTestStyles: ColumnStyle[] = [
valueMaps
:
[
{
value
:
'1'
,
text
:
'on'
,
name
:
'on'
,
},
{
value
:
'0'
,
text
:
'off'
,
name
:
'off'
,
},
{
value
:
'HELLO WORLD'
,
text
:
'HELLO GRAFANA'
,
name
:
'HELLO GRAFANA'
,
},
{
value
:
'value1, value2'
,
text
:
'value3, value4'
,
name
:
'value3, value4'
,
},
],
},
...
...
@@ -111,12 +111,12 @@ export const migratedTestStyles: ColumnStyle[] = [
{
from
:
'1'
,
to
:
'3'
,
text
:
'on'
,
name
:
'on'
,
},
{
from
:
'3'
,
to
:
'6'
,
text
:
'off'
,
name
:
'off'
,
},
],
},
...
...
@@ -127,11 +127,11 @@ export const migratedTestStyles: ColumnStyle[] = [
valueMaps
:
[
{
value
:
'1'
,
text
:
'on'
,
name
:
'on'
,
},
{
value
:
'0'
,
text
:
'off'
,
name
:
'off'
,
},
],
colorMode
:
'value'
,
...
...
@@ -146,12 +146,12 @@ export const migratedTestStyles: ColumnStyle[] = [
{
from
:
'1'
,
to
:
'3'
,
text
:
'on'
,
name
:
'on'
,
},
{
from
:
'3'
,
to
:
'6'
,
text
:
'off'
,
name
:
'off'
,
},
],
colorMode
:
'value'
,
...
...
@@ -162,6 +162,6 @@ export const migratedTestStyles: ColumnStyle[] = [
export
const
simpleTable
=
{
type
:
'table'
,
columns
:
[{
text
:
'First'
},
{
text
:
'Second'
},
{
text
:
'Third'
}],
columns
:
[{
name
:
'First'
},
{
name
:
'Second'
},
{
name
:
'Third'
}],
rows
:
[[
701
,
205
,
305
],
[
702
,
206
,
301
],
[
703
,
207
,
304
]],
};
packages/grafana-ui/src/types/data.ts
View file @
77b3da3e
...
...
@@ -5,6 +5,44 @@ export enum LoadingState {
Error
=
'Error'
,
}
export
enum
FieldType
{
time
=
'time'
,
// or date
number
=
'number'
,
string
=
'string'
,
boolean
=
'boolean'
,
other
=
'other'
,
// Object, Array, etc
}
export
interface
Field
{
name
:
string
;
// The column name
type
?:
FieldType
;
filterable
?:
boolean
;
unit
?:
string
;
dateFormat
?:
string
;
// Source data format
}
export
interface
Tags
{
[
key
:
string
]:
string
;
}
export
interface
SeriesData
{
name
?:
string
;
fields
:
Field
[];
rows
:
any
[][];
tags
?:
Tags
;
}
export
interface
Column
{
text
:
string
;
// For a Column, the 'text' is the field name
filterable
?:
boolean
;
unit
?:
string
;
}
export
interface
TableData
{
columns
:
Column
[];
rows
:
any
[][];
}
export
type
TimeSeriesValue
=
number
|
null
;
export
type
TimeSeriesPoints
=
TimeSeriesValue
[][];
...
...
@@ -33,33 +71,6 @@ export enum NullValueMode {
/** View model projection of many time series */
export
type
TimeSeriesVMs
=
TimeSeriesVM
[];
export
enum
ColumnType
{
time
=
'time'
,
// or date
number
=
'number'
,
string
=
'string'
,
boolean
=
'boolean'
,
other
=
'other'
,
// Object, Array, etc
}
export
interface
Column
{
text
:
string
;
// The column name
type
?:
ColumnType
;
filterable
?:
boolean
;
unit
?:
string
;
dateFormat
?:
string
;
// Source data format
}
export
interface
Tags
{
[
key
:
string
]:
string
;
}
export
interface
TableData
{
name
?:
string
;
columns
:
Column
[];
rows
:
any
[][];
tags
?:
Tags
;
}
export
interface
AnnotationEvent
{
annotation
?:
any
;
dashboardId
?:
number
;
...
...
packages/grafana-ui/src/types/panel.ts
View file @
77b3da3e
import
{
ComponentClass
}
from
'react'
;
import
{
LoadingState
,
Table
Data
}
from
'./data'
;
import
{
LoadingState
,
Series
Data
}
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
>
{
data
?:
Table
Data
[];
data
?:
Series
Data
[];
timeRange
:
TimeRange
;
loading
:
LoadingState
;
options
:
T
;
...
...
packages/grafana-ui/src/utils/__snapshots__/processTableData.test.ts.snap
View file @
77b3da3e
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`process
Table
Data basic processing should generate a header and fix widths 1`] = `
exports[`process
Series
Data basic processing should generate a header and fix widths 1`] = `
Object {
"
column
s": Array [
"
field
s": Array [
Object {
"
text": "Column
1",
"
name": "Field
1",
},
Object {
"
text": "Column
2",
"
name": "Field
2",
},
Object {
"
text": "Column
3",
"
name": "Field
3",
},
],
"rows": Array [
...
...
@@ -33,17 +33,17 @@ Object {
}
`;
exports[`process
Table
Data basic processing should read header and two rows 1`] = `
exports[`process
Series
Data basic processing should read header and two rows 1`] = `
Object {
"
column
s": Array [
"
field
s": Array [
Object {
"
text
": "a",
"
name
": "a",
},
Object {
"
text
": "b",
"
name
": "b",
},
Object {
"
text
": "c",
"
name
": "c",
},
],
"rows": Array [
...
...
packages/grafana-ui/src/utils/processTableData.test.ts
View file @
77b3da3e
import
{
parseCSV
,
to
TableData
,
guessColumnTypes
,
guessColumn
TypeFromValue
}
from
'./processTableData'
;
import
{
Column
Type
}
from
'../types/data'
;
import
{
parseCSV
,
to
SeriesData
,
guessFieldTypes
,
guessField
TypeFromValue
}
from
'./processTableData'
;
import
{
Field
Type
}
from
'../types/data'
;
import
moment
from
'moment'
;
describe
(
'process
Table
Data'
,
()
=>
{
describe
(
'process
Series
Data'
,
()
=>
{
describe
(
'basic processing'
,
()
=>
{
it
(
'should read header and two rows'
,
()
=>
{
const
text
=
'a,b,c
\
n1,2,3
\
n4,5,6'
;
...
...
@@ -21,14 +21,14 @@ describe('processTableData', () => {
});
});
describe
(
'to
Table
Data'
,
()
=>
{
describe
(
'to
Series
Data'
,
()
=>
{
it
(
'converts timeseries to table '
,
()
=>
{
const
input1
=
{
target
:
'Field Name'
,
datapoints
:
[[
100
,
1
],
[
200
,
2
]],
};
let
table
=
to
Table
Data
(
input1
);
expect
(
table
.
columns
[
0
].
text
).
toBe
(
input1
.
target
);
let
table
=
to
Series
Data
(
input1
);
expect
(
table
.
fields
[
0
].
name
).
toBe
(
input1
.
target
);
expect
(
table
.
rows
).
toBe
(
input1
.
datapoints
);
// Should fill a default name if target is empty
...
...
@@ -37,48 +37,48 @@ describe('toTableData', () => {
target
:
''
,
datapoints
:
[[
100
,
1
],
[
200
,
2
]],
};
table
=
to
Table
Data
(
input2
);
expect
(
table
.
columns
[
0
].
text
).
toEqual
(
'Value'
);
table
=
to
Series
Data
(
input2
);
expect
(
table
.
fields
[
0
].
name
).
toEqual
(
'Value'
);
});
it
(
'keeps tableData unchanged'
,
()
=>
{
const
input
=
{
column
s
:
[{
text
:
'A'
},
{
text
:
'B'
},
{
text
:
'C'
}],
field
s
:
[{
text
:
'A'
},
{
text
:
'B'
},
{
text
:
'C'
}],
rows
:
[[
100
,
'A'
,
1
],
[
200
,
'B'
,
2
],
[
300
,
'C'
,
3
]],
};
const
table
=
to
Table
Data
(
input
);
const
table
=
to
Series
Data
(
input
);
expect
(
table
).
toBe
(
input
);
});
it
(
'Guess Colum Types from value'
,
()
=>
{
expect
(
guess
ColumnTypeFromValue
(
1
)).
toBe
(
Column
Type
.
number
);
expect
(
guess
ColumnTypeFromValue
(
1.234
)).
toBe
(
Column
Type
.
number
);
expect
(
guess
ColumnTypeFromValue
(
3.125e7
)).
toBe
(
Column
Type
.
number
);
expect
(
guess
ColumnTypeFromValue
(
true
)).
toBe
(
Column
Type
.
boolean
);
expect
(
guess
ColumnTypeFromValue
(
false
)).
toBe
(
Column
Type
.
boolean
);
expect
(
guess
ColumnTypeFromValue
(
new
Date
())).
toBe
(
Column
Type
.
time
);
expect
(
guess
ColumnTypeFromValue
(
moment
())).
toBe
(
Column
Type
.
time
);
expect
(
guess
FieldTypeFromValue
(
1
)).
toBe
(
Field
Type
.
number
);
expect
(
guess
FieldTypeFromValue
(
1.234
)).
toBe
(
Field
Type
.
number
);
expect
(
guess
FieldTypeFromValue
(
3.125e7
)).
toBe
(
Field
Type
.
number
);
expect
(
guess
FieldTypeFromValue
(
true
)).
toBe
(
Field
Type
.
boolean
);
expect
(
guess
FieldTypeFromValue
(
false
)).
toBe
(
Field
Type
.
boolean
);
expect
(
guess
FieldTypeFromValue
(
new
Date
())).
toBe
(
Field
Type
.
time
);
expect
(
guess
FieldTypeFromValue
(
moment
())).
toBe
(
Field
Type
.
time
);
});
it
(
'Guess Colum Types from strings'
,
()
=>
{
expect
(
guess
ColumnTypeFromValue
(
'1'
)).
toBe
(
Column
Type
.
number
);
expect
(
guess
ColumnTypeFromValue
(
'1.234'
)).
toBe
(
Column
Type
.
number
);
expect
(
guess
ColumnTypeFromValue
(
'3.125e7'
)).
toBe
(
Column
Type
.
number
);
expect
(
guess
ColumnTypeFromValue
(
'True'
)).
toBe
(
Column
Type
.
boolean
);
expect
(
guess
ColumnTypeFromValue
(
'FALSE'
)).
toBe
(
Column
Type
.
boolean
);
expect
(
guess
ColumnTypeFromValue
(
'true'
)).
toBe
(
Column
Type
.
boolean
);
expect
(
guess
ColumnTypeFromValue
(
'xxxx'
)).
toBe
(
Column
Type
.
string
);
expect
(
guess
FieldTypeFromValue
(
'1'
)).
toBe
(
Field
Type
.
number
);
expect
(
guess
FieldTypeFromValue
(
'1.234'
)).
toBe
(
Field
Type
.
number
);
expect
(
guess
FieldTypeFromValue
(
'3.125e7'
)).
toBe
(
Field
Type
.
number
);
expect
(
guess
FieldTypeFromValue
(
'True'
)).
toBe
(
Field
Type
.
boolean
);
expect
(
guess
FieldTypeFromValue
(
'FALSE'
)).
toBe
(
Field
Type
.
boolean
);
expect
(
guess
FieldTypeFromValue
(
'true'
)).
toBe
(
Field
Type
.
boolean
);
expect
(
guess
FieldTypeFromValue
(
'xxxx'
)).
toBe
(
Field
Type
.
string
);
});
it
(
'Guess Colum Types from table'
,
()
=>
{
const
table
=
{
columns
:
[{
text
:
'A (number)'
},
{
text
:
'B (strings)'
},
{
text
:
'C (nulls)'
},
{
text
:
'Time'
}],
fields
:
[{
name
:
'A (number)'
},
{
name
:
'B (strings)'
},
{
name
:
'C (nulls)'
},
{
name
:
'Time'
}],
rows
:
[[
123
,
null
,
null
,
'2000'
],
[
null
,
'Hello'
,
null
,
'XXX'
]],
};
const
norm
=
guess
Column
Types
(
table
);
expect
(
norm
.
columns
[
0
].
type
).
toBe
(
Column
Type
.
number
);
expect
(
norm
.
columns
[
1
].
type
).
toBe
(
Column
Type
.
string
);
expect
(
norm
.
column
s
[
2
].
type
).
toBeUndefined
();
expect
(
norm
.
columns
[
3
].
type
).
toBe
(
Column
Type
.
time
);
// based on name
const
norm
=
guess
Field
Types
(
table
);
expect
(
norm
.
fields
[
0
].
type
).
toBe
(
Field
Type
.
number
);
expect
(
norm
.
fields
[
1
].
type
).
toBe
(
Field
Type
.
string
);
expect
(
norm
.
field
s
[
2
].
type
).
toBeUndefined
();
expect
(
norm
.
fields
[
3
].
type
).
toBe
(
Field
Type
.
time
);
// based on name
});
});
packages/grafana-ui/src/utils/processTableData.ts
View file @
77b3da3e
...
...
@@ -7,7 +7,7 @@ import moment from 'moment';
import
Papa
,
{
ParseError
,
ParseMeta
}
from
'papaparse'
;
// Types
import
{
TableData
,
Column
,
TimeSeries
,
ColumnType
}
from
'../types'
;
import
{
SeriesData
,
Field
,
TimeSeries
,
FieldType
,
TableData
}
from
'../types'
;
// Subset of all parse options
export
interface
TableParseOptions
{
...
...
@@ -31,12 +31,12 @@ export interface TableParseDetails {
* @returns a new table that has equal length rows, or the same
* table if no changes were needed
*/
export
function
matchRowSizes
(
table
:
TableData
):
Table
Data
{
export
function
matchRowSizes
(
table
:
SeriesData
):
Series
Data
{
const
{
rows
}
=
table
;
let
{
column
s
}
=
table
;
let
{
field
s
}
=
table
;
let
sameSize
=
true
;
let
size
=
column
s
.
length
;
let
size
=
field
s
.
length
;
rows
.
forEach
(
row
=>
{
if
(
size
!==
row
.
length
)
{
sameSize
=
false
;
...
...
@@ -47,13 +47,13 @@ export function matchRowSizes(table: TableData): TableData {
return
table
;
}
// Pad
Column
s
if
(
size
!==
column
s
.
length
)
{
const
diff
=
size
-
column
s
.
length
;
columns
=
[...
column
s
];
// Pad
Field
s
if
(
size
!==
field
s
.
length
)
{
const
diff
=
size
-
field
s
.
length
;
fields
=
[...
field
s
];
for
(
let
i
=
0
;
i
<
diff
;
i
++
)
{
column
s
.
push
({
text
:
'Column '
+
(
column
s
.
length
+
1
),
field
s
.
push
({
name
:
'Field '
+
(
field
s
.
length
+
1
),
});
}
}
...
...
@@ -72,30 +72,30 @@ export function matchRowSizes(table: TableData): TableData {
});
return
{
column
s
,
field
s
,
rows
:
fixedRows
,
};
}
function
make
Columns
(
values
:
any
[]):
Column
[]
{
function
make
Fields
(
values
:
any
[]):
Field
[]
{
return
values
.
map
((
value
,
index
)
=>
{
if
(
!
value
)
{
value
=
'
Column
'
+
(
index
+
1
);
value
=
'
Field
'
+
(
index
+
1
);
}
return
{
text
:
value
.
toString
().
trim
(),
name
:
value
.
toString
().
trim
(),
};
});
}
/**
* Convert CSV text into a valid
Table
Data object
* Convert CSV text into a valid
Series
Data object
*
* @param text
* @param options
* @param details, if exists the result will be filled with debugging details
*/
export
function
parseCSV
(
text
:
string
,
options
?:
TableParseOptions
,
details
?:
TableParseDetails
):
Table
Data
{
export
function
parseCSV
(
text
:
string
,
options
?:
TableParseOptions
,
details
?:
TableParseDetails
):
Series
Data
{
const
results
=
Papa
.
parse
(
text
,
{
...
options
,
dynamicTyping
:
true
,
skipEmptyLines
:
true
});
const
{
data
,
meta
,
errors
}
=
results
;
...
...
@@ -118,7 +118,7 @@ export function parseCSV(text: string, options?: TableParseOptions, details?: Ta
details
.
errors
=
errors
;
}
return
{
column
s
:
[],
field
s
:
[],
rows
:
[],
};
}
...
...
@@ -128,22 +128,35 @@ export function parseCSV(text: string, options?: TableParseOptions, details?: Ta
const
header
=
headerIsNotFirstLine
?
[]
:
results
.
data
.
shift
();
return
matchRowSizes
({
columns
:
makeColumn
s
(
header
),
fields
:
makeField
s
(
header
),
rows
:
results
.
data
,
});
}
function
convertTimeSeriesToTableData
(
timeSeries
:
TimeSeries
):
TableData
{
function
convertTableToSeriesData
(
table
:
TableData
):
SeriesData
{
return
{
// rename the 'text' to 'name' field
fields
:
table
.
columns
.
map
(
c
=>
{
const
{
text
,
...
field
}
=
c
;
const
f
=
field
as
Field
;
f
.
name
=
text
;
return
f
;
}),
rows
:
table
.
rows
,
};
}
function
convertTimeSeriesToSeriesData
(
timeSeries
:
TimeSeries
):
SeriesData
{
return
{
name
:
timeSeries
.
target
,
column
s
:
[
field
s
:
[
{
text
:
timeSeries
.
target
||
'Value'
,
name
:
timeSeries
.
target
||
'Value'
,
unit
:
timeSeries
.
unit
,
},
{
text
:
'Time'
,
type
:
Column
Type
.
time
,
name
:
'Time'
,
type
:
Field
Type
.
time
,
unit
:
'dateTimeAsIso'
,
},
],
...
...
@@ -151,10 +164,10 @@ function convertTimeSeriesToTableData(timeSeries: TimeSeries): TableData {
};
}
export
const
getFirstTime
Column
=
(
table
:
Table
Data
):
number
=>
{
const
{
column
s
}
=
table
;
for
(
let
i
=
0
;
i
<
column
s
.
length
;
i
++
)
{
if
(
columns
[
i
].
type
===
Column
Type
.
time
)
{
export
const
getFirstTime
Field
=
(
table
:
Series
Data
):
number
=>
{
const
{
field
s
}
=
table
;
for
(
let
i
=
0
;
i
<
field
s
.
length
;
i
++
)
{
if
(
fields
[
i
].
type
===
Field
Type
.
time
)
{
return
i
;
}
}
...
...
@@ -170,45 +183,45 @@ const NUMBER = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i;
*
* TODO: better Date/Time support! Look for standard date strings?
*/
export
function
guess
ColumnTypeFromValue
(
v
:
any
):
Column
Type
{
export
function
guess
FieldTypeFromValue
(
v
:
any
):
Field
Type
{
if
(
isNumber
(
v
))
{
return
Column
Type
.
number
;
return
Field
Type
.
number
;
}
if
(
isString
(
v
))
{
if
(
NUMBER
.
test
(
v
))
{
return
Column
Type
.
number
;
return
Field
Type
.
number
;
}
if
(
v
===
'true'
||
v
===
'TRUE'
||
v
===
'True'
||
v
===
'false'
||
v
===
'FALSE'
||
v
===
'False'
)
{
return
Column
Type
.
boolean
;
return
Field
Type
.
boolean
;
}
return
Column
Type
.
string
;
return
Field
Type
.
string
;
}
if
(
isBoolean
(
v
))
{
return
Column
Type
.
boolean
;
return
Field
Type
.
boolean
;
}
if
(
v
instanceof
Date
||
v
instanceof
moment
)
{
return
Column
Type
.
time
;
return
Field
Type
.
time
;
}
return
Column
Type
.
other
;
return
Field
Type
.
other
;
}
/**
* Looks at the data to guess the column type. This ignores any existing setting
*/
function
guess
ColumnTypeFromTable
(
table
:
TableData
,
index
:
number
):
Column
Type
|
undefined
{
const
column
=
table
.
column
s
[
index
];
function
guess
FieldTypeFromTable
(
table
:
SeriesData
,
index
:
number
):
Field
Type
|
undefined
{
const
column
=
table
.
field
s
[
index
];
// 1. Use the column name to guess
if
(
column
.
text
)
{
const
name
=
column
.
text
.
toLowerCase
();
if
(
column
.
name
)
{
const
name
=
column
.
name
.
toLowerCase
();
if
(
name
===
'date'
||
name
===
'time'
)
{
return
Column
Type
.
time
;
return
Field
Type
.
time
;
}
}
...
...
@@ -216,7 +229,7 @@ function guessColumnTypeFromTable(table: TableData, index: number): ColumnType |
for
(
let
i
=
0
;
i
<
table
.
rows
.
length
;
i
++
)
{
const
v
=
table
.
rows
[
i
][
index
];
if
(
v
!==
null
)
{
return
guess
Column
TypeFromValue
(
v
);
return
guess
Field
TypeFromValue
(
v
);
}
}
...
...
@@ -228,20 +241,20 @@ function guessColumnTypeFromTable(table: TableData, index: number): ColumnType |
* @returns a table Returns a copy of the table with the best guess for each column type
* If the table already has column types defined, they will be used
*/
export
const
guess
ColumnTypes
=
(
table
:
TableData
):
Table
Data
=>
{
for
(
let
i
=
0
;
i
<
table
.
column
s
.
length
;
i
++
)
{
if
(
!
table
.
column
s
[
i
].
type
)
{
export
const
guess
FieldTypes
=
(
table
:
SeriesData
):
Series
Data
=>
{
for
(
let
i
=
0
;
i
<
table
.
field
s
.
length
;
i
++
)
{
if
(
!
table
.
field
s
[
i
].
type
)
{
// Somethign is missing a type return a modified copy
return
{
...
table
,
columns
:
table
.
column
s
.
map
((
column
,
index
)
=>
{
fields
:
table
.
field
s
.
map
((
column
,
index
)
=>
{
if
(
column
.
type
)
{
return
column
;
}
// Replace it with a calculated version
return
{
...
column
,
type
:
guess
Column
TypeFromTable
(
table
,
index
),
type
:
guess
Field
TypeFromTable
(
table
,
index
),
};
}),
};
...
...
@@ -251,21 +264,26 @@ export const guessColumnTypes = (table: TableData): TableData => {
return
table
;
};
export
const
isTableData
=
(
data
:
any
):
data
is
Table
Data
=>
data
&&
data
.
hasOwnProperty
(
'columns'
);
export
const
isTableData
=
(
data
:
any
):
data
is
Series
Data
=>
data
&&
data
.
hasOwnProperty
(
'columns'
);
export
const
toTableData
=
(
data
:
any
):
TableData
=>
{
if
(
data
.
hasOwnProperty
(
'columns'
))
{
return
data
as
TableData
;
export
const
isSeriesData
=
(
data
:
any
):
data
is
SeriesData
=>
data
&&
data
.
hasOwnProperty
(
'fields'
);
export
const
toSeriesData
=
(
data
:
any
):
SeriesData
=>
{
if
(
data
.
hasOwnProperty
(
'fields'
))
{
return
data
as
SeriesData
;
}
if
(
data
.
hasOwnProperty
(
'datapoints'
))
{
return
convertTimeSeriesToTableData
(
data
);
return
convertTimeSeriesToSeriesData
(
data
);
}
if
(
data
.
hasOwnProperty
(
'columns'
))
{
return
convertTableToSeriesData
(
data
);
}
// TODO, try to convert JSON/Array to table?
console
.
warn
(
'Can not convert'
,
data
);
throw
new
Error
(
'Unsupported data format'
);
};
export
function
sort
TableData
(
data
:
TableData
,
sortIndex
?:
number
,
reverse
=
false
):
Table
Data
{
export
function
sort
SeriesData
(
data
:
SeriesData
,
sortIndex
?:
number
,
reverse
=
false
):
Series
Data
{
if
(
isNumber
(
sortIndex
))
{
const
copy
=
{
...
data
,
...
...
packages/grafana-ui/src/utils/statsCalculator.test.ts
View file @
77b3da3e
...
...
@@ -41,8 +41,8 @@ describe('Stats Calculators', () => {
it
(
'should calculate basic stats'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
column
Index
:
0
,
series
:
basicTable
,
field
Index
:
0
,
stats
:
[
'first'
,
'last'
,
'mean'
],
});
...
...
@@ -58,8 +58,8 @@ describe('Stats Calculators', () => {
it
(
'should support a single stat also'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
column
Index
:
0
,
series
:
basicTable
,
field
Index
:
0
,
stats
:
[
'first'
],
});
...
...
@@ -70,8 +70,8 @@ describe('Stats Calculators', () => {
it
(
'should get non standard stats'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
column
Index
:
0
,
series
:
basicTable
,
field
Index
:
0
,
stats
:
[
StatID
.
distinctCount
,
StatID
.
changeCount
],
});
...
...
@@ -81,8 +81,8 @@ describe('Stats Calculators', () => {
it
(
'should calculate step'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
{
columns
:
[{
text
:
'A'
}],
rows
:
[[
100
],
[
200
],
[
300
],
[
400
]]
},
column
Index
:
0
,
series
:
{
fields
:
[{
name
:
'A'
}],
rows
:
[[
100
],
[
200
],
[
300
],
[
400
]]
},
field
Index
:
0
,
stats
:
[
StatID
.
step
,
StatID
.
delta
],
});
...
...
packages/grafana-ui/src/utils/statsCalculator.ts
View file @
77b3da3e
// Libraries
import
isNumber
from
'lodash/isNumber'
;
import
{
Table
Data
,
NullValueMode
}
from
'../types/index'
;
import
{
Series
Data
,
NullValueMode
}
from
'../types/index'
;
export
enum
StatID
{
sum
=
'sum'
,
...
...
@@ -29,7 +29,7 @@ export interface ColumnStats {
}
// Internal function
type
StatCalculator
=
(
table
:
TableData
,
column
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
)
=>
ColumnStats
;
type
StatCalculator
=
(
data
:
SeriesData
,
field
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
)
=>
ColumnStats
;
export
interface
StatCalculatorInfo
{
id
:
string
;
...
...
@@ -64,8 +64,8 @@ export function getStatsCalculators(ids?: string[]): StatCalculatorInfo[] {
}
export
interface
CalculateStatsOptions
{
table
:
Table
Data
;
column
Index
:
number
;
series
:
Series
Data
;
field
Index
:
number
;
stats
:
string
[];
// The stats to calculate
nullValueMode
?:
NullValueMode
;
}
...
...
@@ -74,7 +74,7 @@ export interface CalculateStatsOptions {
* @returns an object with a key for each selected stat
*/
export
function
calculateStats
(
options
:
CalculateStatsOptions
):
ColumnStats
{
const
{
table
,
column
Index
,
stats
,
nullValueMode
}
=
options
;
const
{
series
,
field
Index
,
stats
,
nullValueMode
}
=
options
;
if
(
!
stats
||
stats
.
length
<
1
)
{
return
{};
...
...
@@ -82,9 +82,9 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats {
const
queue
=
getStatsCalculators
(
stats
);
// Return early for empty
tabl
es
// Return early for empty
seri
es
// This lets the concrete implementations assume at least one row
if
(
!
table
.
rows
||
table
.
rows
.
length
<
1
)
{
if
(
!
series
.
rows
||
series
.
rows
.
length
<
1
)
{
const
stats
=
{}
as
ColumnStats
;
for
(
const
stat
of
queue
)
{
stats
[
stat
.
id
]
=
stat
.
emptyInputResult
!==
null
?
stat
.
emptyInputResult
:
null
;
...
...
@@ -97,16 +97,16 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats {
// Avoid calculating all the standard stats if possible
if
(
queue
.
length
===
1
&&
queue
[
0
].
calculator
)
{
return
queue
[
0
].
calculator
(
table
,
column
Index
,
ignoreNulls
,
nullAsZero
);
return
queue
[
0
].
calculator
(
series
,
field
Index
,
ignoreNulls
,
nullAsZero
);
}
// For now everything can use the standard stats
let
values
=
standardStatsStat
(
table
,
column
Index
,
ignoreNulls
,
nullAsZero
);
let
values
=
standardStatsStat
(
series
,
field
Index
,
ignoreNulls
,
nullAsZero
);
for
(
const
calc
of
queue
)
{
if
(
!
values
.
hasOwnProperty
(
calc
.
id
)
&&
calc
.
calculator
)
{
values
=
{
...
values
,
...
calc
.
calculator
(
table
,
column
Index
,
ignoreNulls
,
nullAsZero
),
...
calc
.
calculator
(
series
,
field
Index
,
ignoreNulls
,
nullAsZero
),
};
}
}
...
...
@@ -223,8 +223,8 @@ function getById(id: string): StatCalculatorInfo | undefined {
}
function
standardStatsStat
(
data
:
Table
Data
,
column
Index
:
number
,
data
:
Series
Data
,
field
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
...
...
@@ -250,7 +250,7 @@ function standardStatsStat(
}
as
ColumnStats
;
for
(
let
i
=
0
;
i
<
data
.
rows
.
length
;
i
++
)
{
let
currentValue
=
data
.
rows
[
i
][
column
Index
];
let
currentValue
=
data
.
rows
[
i
][
field
Index
];
if
(
currentValue
===
null
)
{
if
(
ignoreNulls
)
{
...
...
@@ -345,17 +345,17 @@ function standardStatsStat(
return
stats
;
}
function
calculateFirst
(
data
:
TableData
,
column
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
return
{
first
:
data
.
rows
[
0
][
column
Index
]
};
function
calculateFirst
(
data
:
SeriesData
,
field
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
return
{
first
:
data
.
rows
[
0
][
field
Index
]
};
}
function
calculateLast
(
data
:
TableData
,
column
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
return
{
last
:
data
.
rows
[
data
.
rows
.
length
-
1
][
column
Index
]
};
function
calculateLast
(
data
:
SeriesData
,
field
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
return
{
last
:
data
.
rows
[
data
.
rows
.
length
-
1
][
field
Index
]
};
}
function
calculateChangeCount
(
data
:
Table
Data
,
column
Index
:
number
,
data
:
Series
Data
,
field
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
...
...
@@ -363,7 +363,7 @@ function calculateChangeCount(
let
first
=
true
;
let
last
:
any
=
null
;
for
(
let
i
=
0
;
i
<
data
.
rows
.
length
;
i
++
)
{
let
currentValue
=
data
.
rows
[
i
][
column
Index
];
let
currentValue
=
data
.
rows
[
i
][
field
Index
];
if
(
currentValue
===
null
)
{
if
(
ignoreNulls
)
{
continue
;
...
...
@@ -383,14 +383,14 @@ function calculateChangeCount(
}
function
calculateDistinctCount
(
data
:
Table
Data
,
column
Index
:
number
,
data
:
Series
Data
,
field
Index
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
const
distinct
=
new
Set
<
any
>
();
for
(
let
i
=
0
;
i
<
data
.
rows
.
length
;
i
++
)
{
let
currentValue
=
data
.
rows
[
i
][
column
Index
];
let
currentValue
=
data
.
rows
[
i
][
field
Index
];
if
(
currentValue
===
null
)
{
if
(
ignoreNulls
)
{
continue
;
...
...
public/app/core/table_model.ts
View file @
77b3da3e
...
...
@@ -9,6 +9,7 @@ interface MutableColumn extends Column {
title
?:
string
;
sort
?:
boolean
;
desc
?:
boolean
;
type
?:
string
;
}
export
default
class
TableModel
implements
TableData
{
...
...
public/app/features/dashboard/dashgrid/DataPanel.test.tsx
View file @
77b3da3e
// Library
import
React
from
'react'
;
import
{
DataPanel
,
getProcessed
Table
Data
}
from
'./DataPanel'
;
import
{
DataPanel
,
getProcessed
Series
Data
}
from
'./DataPanel'
;
describe
(
'DataPanel'
,
()
=>
{
let
dataPanel
:
DataPanel
;
...
...
@@ -34,27 +34,27 @@ describe('DataPanel', () => {
target
:
''
,
datapoints
:
[[
100
,
1
],
[
200
,
2
]],
};
const
data
=
getProcessed
Table
Data
([
null
,
input1
,
input2
,
null
,
null
]);
const
data
=
getProcessed
Series
Data
([
null
,
input1
,
input2
,
null
,
null
]);
expect
(
data
.
length
).
toBe
(
2
);
expect
(
data
[
0
].
columns
[
0
].
text
).
toBe
(
input1
.
target
);
expect
(
data
[
0
].
fields
[
0
].
name
).
toBe
(
input1
.
target
);
expect
(
data
[
0
].
rows
).
toBe
(
input1
.
datapoints
);
// Default name
expect
(
data
[
1
].
columns
[
0
].
text
).
toEqual
(
'Value'
);
expect
(
data
[
1
].
fields
[
0
].
name
).
toEqual
(
'Value'
);
// Every colun should have a name and a type
for
(
const
table
of
data
)
{
for
(
const
column
of
table
.
column
s
)
{
expect
(
column
.
text
).
toBeDefined
();
for
(
const
column
of
table
.
field
s
)
{
expect
(
column
.
name
).
toBeDefined
();
expect
(
column
.
type
).
toBeDefined
();
}
}
});
it
(
'supports null values from query OK'
,
()
=>
{
expect
(
getProcessed
Table
Data
([
null
,
null
,
null
,
null
])).
toEqual
([]);
expect
(
getProcessed
Table
Data
(
undefined
)).
toEqual
([]);
expect
(
getProcessed
Table
Data
((
null
as
unknown
)
as
any
[])).
toEqual
([]);
expect
(
getProcessed
Table
Data
([])).
toEqual
([]);
expect
(
getProcessed
Series
Data
([
null
,
null
,
null
,
null
])).
toEqual
([]);
expect
(
getProcessed
Series
Data
(
undefined
)).
toEqual
([]);
expect
(
getProcessed
Series
Data
((
null
as
unknown
)
as
any
[])).
toEqual
([]);
expect
(
getProcessed
Series
Data
([])).
toEqual
([]);
});
});
public/app/features/dashboard/dashgrid/DataPanel.tsx
View file @
77b3da3e
...
...
@@ -11,16 +11,16 @@ import {
DataQueryResponse
,
DataQueryError
,
LoadingState
,
Table
Data
,
Series
Data
,
TimeRange
,
ScopedVars
,
to
Table
Data
,
guess
Column
Types
,
to
Series
Data
,
guess
Field
Types
,
}
from
'@grafana/ui'
;
interface
RenderProps
{
loading
:
LoadingState
;
data
:
Table
Data
[];
data
:
Series
Data
[];
}
export
interface
Props
{
...
...
@@ -44,7 +44,7 @@ export interface State {
isFirstLoad
:
boolean
;
loading
:
LoadingState
;
response
:
DataQueryResponse
;
data
?:
Table
Data
[];
data
?:
Series
Data
[];
}
/**
...
...
@@ -52,18 +52,18 @@ export interface State {
*
* This is also used by PanelChrome for snapshot support
*/
export
function
getProcessed
TableData
(
results
?:
any
[]):
Table
Data
[]
{
export
function
getProcessed
SeriesData
(
results
?:
any
[]):
Series
Data
[]
{
if
(
!
results
)
{
return
[];
}
const
tables
:
Table
Data
[]
=
[];
const
series
:
Series
Data
[]
=
[];
for
(
const
r
of
results
)
{
if
(
r
)
{
tables
.
push
(
guessColumnTypes
(
toTable
Data
(
r
)));
series
.
push
(
guessFieldTypes
(
toSeries
Data
(
r
)));
}
}
return
tabl
es
;
return
seri
es
;
}
export
class
DataPanel
extends
Component
<
Props
,
State
>
{
...
...
@@ -167,7 +167,7 @@ export class DataPanel extends Component<Props, State> {
this
.
setState
({
loading
:
LoadingState
.
Done
,
response
:
resp
,
data
:
getProcessed
Table
Data
(
resp
.
data
),
data
:
getProcessed
Series
Data
(
resp
.
data
),
isFirstLoad
:
false
,
});
}
catch
(
err
)
{
...
...
public/app/features/dashboard/dashgrid/PanelChrome.tsx
View file @
77b3da3e
...
...
@@ -19,12 +19,12 @@ import config from 'app/core/config';
// Types
import
{
DashboardModel
,
PanelModel
}
from
'../state'
;
import
{
PanelPlugin
}
from
'app/types'
;
import
{
DataQueryResponse
,
TimeRange
,
LoadingState
,
TableData
,
DataQueryError
}
from
'@grafana/ui'
;
import
{
DataQueryResponse
,
TimeRange
,
LoadingState
,
DataQueryError
,
SeriesData
}
from
'@grafana/ui'
;
import
{
ScopedVars
}
from
'@grafana/ui'
;
import
templateSrv
from
'app/features/templating/template_srv'
;
import
{
getProcessed
Table
Data
}
from
'./DataPanel'
;
import
{
getProcessed
Series
Data
}
from
'./DataPanel'
;
const
DEFAULT_PLUGIN_ERROR
=
'Error in plugin'
;
...
...
@@ -141,10 +141,10 @@ export class PanelChrome extends PureComponent<Props, State> {
}
get
getDataForPanel
()
{
return
this
.
hasPanelSnapshot
?
getProcessed
Table
Data
(
this
.
props
.
panel
.
snapshotData
)
:
null
;
return
this
.
hasPanelSnapshot
?
getProcessed
Series
Data
(
this
.
props
.
panel
.
snapshotData
)
:
null
;
}
renderPanelPlugin
(
loading
:
LoadingState
,
data
:
Table
Data
[],
width
:
number
,
height
:
number
):
JSX
.
Element
{
renderPanelPlugin
(
loading
:
LoadingState
,
data
:
Series
Data
[],
width
:
number
,
height
:
number
):
JSX
.
Element
{
const
{
panel
,
plugin
}
=
this
.
props
;
const
{
timeRange
,
renderCounter
}
=
this
.
state
;
const
PanelComponent
=
plugin
.
exports
.
reactPanel
.
panel
;
...
...
public/app/plugins/datasource/influxdb/influx_series.ts
View file @
77b3da3e
import
_
from
'lodash'
;
import
TableModel
from
'app/core/table_model'
;
import
{
Column
Type
}
from
'@grafana/ui'
;
import
{
Field
Type
}
from
'@grafana/ui'
;
export
default
class
InfluxSeries
{
series
:
any
;
...
...
@@ -157,7 +157,7 @@ export default class InfluxSeries {
// Check that the first column is indeed 'time'
if
(
series
.
columns
[
0
]
===
'time'
)
{
// Push this now before the tags and with the right type
table
.
columns
.
push
({
text
:
'Time'
,
type
:
Column
Type
.
time
});
table
.
columns
.
push
({
text
:
'Time'
,
type
:
Field
Type
.
time
});
j
++
;
}
_
.
each
(
_
.
keys
(
series
.
tags
),
key
=>
{
...
...
public/app/plugins/datasource/prometheus/result_transformer.ts
View file @
77b3da3e
import
_
from
'lodash'
;
import
TableModel
from
'app/core/table_model'
;
import
{
TimeSeries
,
Column
Type
}
from
'@grafana/ui'
;
import
{
TimeSeries
,
Field
Type
}
from
'@grafana/ui'
;
export
class
ResultTransformer
{
constructor
(
private
templateSrv
)
{}
...
...
@@ -98,7 +98,7 @@ export class ResultTransformer {
// Sort metric labels, create columns for them and record their index
const
sortedLabels
=
_
.
keys
(
metricLabels
).
sort
();
table
.
columns
.
push
({
text
:
'Time'
,
type
:
Column
Type
.
time
});
table
.
columns
.
push
({
text
:
'Time'
,
type
:
Field
Type
.
time
});
_
.
each
(
sortedLabels
,
(
label
,
labelIndex
)
=>
{
metricLabels
[
label
]
=
labelIndex
+
1
;
table
.
columns
.
push
({
text
:
label
,
filterable
:
true
});
...
...
public/app/plugins/panel/graph2/GraphPanel.tsx
View file @
77b3da3e
...
...
@@ -2,7 +2,7 @@
import
_
from
'lodash'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
{
Graph
,
PanelProps
,
NullValueMode
,
colors
,
TimeSeriesVMs
,
ColumnType
,
getFirstTimeColumn
}
from
'@grafana/ui'
;
import
{
Graph
,
PanelProps
,
NullValueMode
,
colors
,
TimeSeriesVMs
,
FieldType
,
getFirstTimeField
}
from
'@grafana/ui'
;
import
{
Options
}
from
'./types'
;
import
{
getFlotPairs
}
from
'@grafana/ui/src/utils/flotPairs'
;
...
...
@@ -15,16 +15,16 @@ export class GraphPanel extends PureComponent<Props> {
const
vmSeries
:
TimeSeriesVMs
=
[];
for
(
const
table
of
data
)
{
const
timeColumn
=
getFirstTime
Column
(
table
);
const
timeColumn
=
getFirstTime
Field
(
table
);
if
(
timeColumn
<
0
)
{
continue
;
}
for
(
let
i
=
0
;
i
<
table
.
column
s
.
length
;
i
++
)
{
const
column
=
table
.
column
s
[
i
];
for
(
let
i
=
0
;
i
<
table
.
field
s
.
length
;
i
++
)
{
const
column
=
table
.
field
s
[
i
];
// Show all numeric columns
if
(
column
.
type
===
Column
Type
.
number
)
{
if
(
column
.
type
===
Field
Type
.
number
)
{
// Use external calculator just to make sure it works :)
const
points
=
getFlotPairs
({
rows
:
table
.
rows
,
...
...
@@ -34,7 +34,7 @@ export class GraphPanel extends PureComponent<Props> {
});
vmSeries
.
push
({
label
:
column
.
text
,
label
:
column
.
name
,
data
:
points
,
color
:
colors
[
vmSeries
.
length
%
colors
.
length
],
...
...
public/app/plugins/panel/singlestat2/SingleStatPanel.tsx
View file @
77b3da3e
...
...
@@ -4,7 +4,7 @@ import React, { PureComponent, CSSProperties } from 'react';
// Types
import
{
SingleStatOptions
,
SingleStatBaseOptions
}
from
'./types'
;
import
{
DisplayValue
,
PanelProps
,
NullValueMode
,
Column
Type
,
calculateStats
}
from
'@grafana/ui'
;
import
{
DisplayValue
,
PanelProps
,
NullValueMode
,
Field
Type
,
calculateStats
}
from
'@grafana/ui'
;
import
{
config
}
from
'app/core/config'
;
import
{
getDisplayProcessor
}
from
'@grafana/ui'
;
import
{
ProcessedValuesRepeater
}
from
'./ProcessedValuesRepeater'
;
...
...
@@ -26,19 +26,19 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
const
values
:
DisplayValue
[]
=
[];
for
(
const
table
of
data
)
{
for
(
const
series
of
data
)
{
if
(
stat
===
'name'
)
{
values
.
push
(
display
(
table
.
name
));
values
.
push
(
display
(
series
.
name
));
}
for
(
let
i
=
0
;
i
<
table
.
column
s
.
length
;
i
++
)
{
const
column
=
table
.
column
s
[
i
];
for
(
let
i
=
0
;
i
<
series
.
field
s
.
length
;
i
++
)
{
const
column
=
series
.
field
s
[
i
];
// Show all columns that are not 'time'
if
(
column
.
type
===
Column
Type
.
number
)
{
if
(
column
.
type
===
Field
Type
.
number
)
{
const
stats
=
calculateStats
({
table
,
column
Index
:
i
,
series
,
field
Index
:
i
,
stats
:
[
stat
],
// The stats to calculate
nullValueMode
:
NullValueMode
.
Null
,
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment