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
e0ecbc4c
Commit
e0ecbc4c
authored
Mar 22, 2019
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'table-reducer' of
https://github.com/ryantxu/grafana
into ryantxu-table-reducer
parents
a0d2e586
78a92437
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
720 additions
and
37 deletions
+720
-37
packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx
+79
-0
packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx
+98
-0
packages/grafana-ui/src/components/index.ts
+1
-0
packages/grafana-ui/src/utils/index.ts
+1
-0
packages/grafana-ui/src/utils/statsCalculator.test.ts
+92
-0
packages/grafana-ui/src/utils/statsCalculator.ts
+403
-0
public/app/features/dashboard/dashgrid/DashboardPanel.tsx
+5
-0
public/app/plugins/panel/bargauge/types.ts
+2
-2
public/app/plugins/panel/gauge/types.ts
+2
-2
public/app/plugins/panel/singlestat2/SingleStatPanel.tsx
+15
-10
public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx
+12
-20
public/app/plugins/panel/singlestat2/module.tsx
+8
-1
public/app/plugins/panel/singlestat2/types.ts
+2
-2
No files found.
packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx
0 → 100644
View file @
e0ecbc4c
import
React
,
{
PureComponent
}
from
'react'
;
import
{
storiesOf
}
from
'@storybook/react'
;
import
{
action
}
from
'@storybook/addon-actions'
;
import
{
withCenteredStory
}
from
'../../utils/storybook/withCenteredStory'
;
import
{
StatsPicker
}
from
'./StatsPicker'
;
import
{
text
,
boolean
}
from
'@storybook/addon-knobs'
;
const
getKnobs
=
()
=>
{
return
{
placeholder
:
text
(
'Placeholder Text'
,
''
),
defaultStat
:
text
(
'Default Stat'
,
''
),
allowMultiple
:
boolean
(
'Allow Multiple'
,
false
),
initialStats
:
text
(
'Initial Stats'
,
''
),
};
};
interface
State
{
stats
:
string
[];
}
export
class
WrapperWithState
extends
PureComponent
<
any
,
State
>
{
constructor
(
props
:
any
)
{
super
(
props
);
this
.
state
=
{
stats
:
this
.
toStatsArray
(
props
.
initialReducers
),
};
}
toStatsArray
=
(
txt
:
string
):
string
[]
=>
{
if
(
!
txt
)
{
return
[];
}
return
txt
.
split
(
','
).
map
(
v
=>
v
.
trim
());
};
componentDidUpdate
(
prevProps
:
any
)
{
const
{
initialReducers
}
=
this
.
props
;
if
(
initialReducers
!==
prevProps
.
initialReducers
)
{
console
.
log
(
'Changing initial reducers'
);
this
.
setState
({
stats
:
this
.
toStatsArray
(
initialReducers
)
});
}
}
render
()
{
const
{
placeholder
,
defaultStat
,
allowMultiple
}
=
this
.
props
;
const
{
stats
}
=
this
.
state
;
return
(
<
StatsPicker
placeholder=
{
placeholder
}
defaultStat=
{
defaultStat
}
allowMultiple=
{
allowMultiple
}
stats=
{
stats
}
onChange=
{
(
stats
:
string
[])
=>
{
action
(
'Picked:'
)(
stats
);
this
.
setState
({
stats
});
}
}
/>
);
}
}
const
story
=
storiesOf
(
'UI/StatsPicker'
,
module
);
story
.
addDecorator
(
withCenteredStory
);
story
.
add
(
'picker'
,
()
=>
{
const
{
placeholder
,
defaultStat
,
allowMultiple
,
initialStats
}
=
getKnobs
();
return
(
<
div
>
<
WrapperWithState
placeholder=
{
placeholder
}
defaultStat=
{
defaultStat
}
allowMultiple=
{
allowMultiple
}
initialStats=
{
initialStats
}
/>
</
div
>
);
});
packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx
0 → 100644
View file @
e0ecbc4c
import
React
,
{
PureComponent
}
from
'react'
;
import
isArray
from
'lodash/isArray'
;
import
difference
from
'lodash/difference'
;
import
{
Select
}
from
'../index'
;
import
{
getStatsCalculators
}
from
'../../utils/statsCalculator'
;
import
{
SelectOptionItem
}
from
'../Select/Select'
;
interface
Props
{
placeholder
?:
string
;
onChange
:
(
stats
:
string
[])
=>
void
;
stats
:
string
[];
width
?:
number
;
allowMultiple
?:
boolean
;
defaultStat
?:
string
;
}
export
class
StatsPicker
extends
PureComponent
<
Props
>
{
static
defaultProps
=
{
width
:
12
,
allowMultiple
:
false
,
};
componentDidMount
()
{
this
.
checkInput
();
}
componentDidUpdate
(
prevProps
:
Props
)
{
this
.
checkInput
();
}
checkInput
=
()
=>
{
const
{
stats
,
allowMultiple
,
defaultStat
,
onChange
}
=
this
.
props
;
const
current
=
getStatsCalculators
(
stats
);
if
(
current
.
length
!==
stats
.
length
)
{
const
found
=
current
.
map
(
v
=>
v
.
id
);
const
notFound
=
difference
(
stats
,
found
);
console
.
warn
(
'Unknown stats'
,
notFound
,
stats
);
onChange
(
current
.
map
(
stat
=>
stat
.
id
));
}
// Make sure there is only one
if
(
!
allowMultiple
&&
stats
.
length
>
1
)
{
console
.
warn
(
'Removing extra stat'
,
stats
);
onChange
([
stats
[
0
]]);
}
// Set the reducer from callback
if
(
defaultStat
&&
stats
.
length
<
1
)
{
onChange
([
defaultStat
]);
}
};
onSelectionChange
=
(
item
:
SelectOptionItem
)
=>
{
const
{
onChange
}
=
this
.
props
;
if
(
isArray
(
item
))
{
onChange
(
item
.
map
(
v
=>
v
.
value
));
}
else
{
onChange
([
item
.
value
]);
}
};
render
()
{
const
{
width
,
stats
,
allowMultiple
,
defaultStat
,
placeholder
}
=
this
.
props
;
const
options
=
getStatsCalculators
().
map
(
s
=>
{
return
{
value
:
s
.
id
,
label
:
s
.
name
,
description
:
s
.
description
,
};
});
const
value
:
SelectOptionItem
[]
=
[];
stats
.
forEach
(
s
=>
{
const
o
=
options
.
find
(
v
=>
v
.
value
===
s
);
if
(
o
)
{
value
.
push
(
o
);
}
});
//getStatsCalculators(stats);
return
(
<
Select
width=
{
width
}
value=
{
value
}
isClearable=
{
!
defaultStat
}
isMulti=
{
allowMultiple
}
isSearchable=
{
true
}
options=
{
options
}
placeholder=
{
placeholder
}
onChange=
{
this
.
onSelectionChange
}
/>
);
}
}
packages/grafana-ui/src/components/index.ts
View file @
e0ecbc4c
...
...
@@ -27,6 +27,7 @@ export { Switch } from './Switch/Switch';
export
{
EmptySearchResult
}
from
'./EmptySearchResult/EmptySearchResult'
;
export
{
PieChart
,
PieChartDataPoint
,
PieChartType
}
from
'./PieChart/PieChart'
;
export
{
UnitPicker
}
from
'./UnitPicker/UnitPicker'
;
export
{
StatsPicker
}
from
'./StatsPicker/StatsPicker'
;
export
{
Input
,
InputStatus
}
from
'./Input/Input'
;
// Visualizations
...
...
packages/grafana-ui/src/utils/index.ts
View file @
e0ecbc4c
...
...
@@ -5,6 +5,7 @@ export * from './colors';
export
*
from
'./namedColorsPalette'
;
export
*
from
'./thresholds'
;
export
*
from
'./string'
;
export
*
from
'./statsCalculator'
;
export
*
from
'./displayValue'
;
export
*
from
'./deprecationWarning'
;
export
{
getMappedValue
}
from
'./valueMappings'
;
...
...
packages/grafana-ui/src/utils/statsCalculator.test.ts
0 → 100644
View file @
e0ecbc4c
import
{
parseCSV
}
from
'./processTableData'
;
import
{
getStatsCalculators
,
StatID
,
calculateStats
}
from
'./statsCalculator'
;
import
_
from
'lodash'
;
describe
(
'Stats Calculators'
,
()
=>
{
const
basicTable
=
parseCSV
(
'a,b,c
\
n10,20,30
\
n20,30,40'
);
it
(
'should load all standard stats'
,
()
=>
{
const
names
=
[
StatID
.
sum
,
StatID
.
max
,
StatID
.
min
,
StatID
.
logmin
,
StatID
.
mean
,
StatID
.
last
,
StatID
.
first
,
StatID
.
count
,
StatID
.
range
,
StatID
.
diff
,
StatID
.
step
,
StatID
.
delta
,
// StatID.allIsZero,
// StatID.allIsNull,
];
const
stats
=
getStatsCalculators
(
names
);
expect
(
stats
.
length
).
toBe
(
names
.
length
);
});
it
(
'should fail to load unknown stats'
,
()
=>
{
const
names
=
[
'not a stat'
,
StatID
.
max
,
StatID
.
min
,
'also not a stat'
];
const
stats
=
getStatsCalculators
(
names
);
expect
(
stats
.
length
).
toBe
(
2
);
const
found
=
stats
.
map
(
v
=>
v
.
id
);
const
notFound
=
_
.
difference
(
names
,
found
);
expect
(
notFound
.
length
).
toBe
(
2
);
expect
(
notFound
[
0
]).
toBe
(
'not a stat'
);
});
it
(
'should calculate basic stats'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
columnIndex
:
0
,
stats
:
[
'first'
,
'last'
,
'mean'
],
});
// First
expect
(
stats
.
first
).
toEqual
(
10
);
// Last
expect
(
stats
.
last
).
toEqual
(
20
);
// Mean
expect
(
stats
.
mean
).
toEqual
(
15
);
});
it
(
'should support a single stat also'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
columnIndex
:
0
,
stats
:
[
'first'
],
});
// Should do the simple version that just looks up value
expect
(
Object
.
keys
(
stats
).
length
).
toEqual
(
1
);
expect
(
stats
.
first
).
toEqual
(
10
);
});
it
(
'should get non standard stats'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
columnIndex
:
0
,
stats
:
[
StatID
.
distinctCount
,
StatID
.
changeCount
],
});
expect
(
stats
.
distinctCount
).
toEqual
(
2
);
expect
(
stats
.
changeCount
).
toEqual
(
1
);
});
it
(
'should calculate step'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
{
columns
:
[{
text
:
'A'
}],
rows
:
[[
100
],
[
200
],
[
300
],
[
400
]]
},
columnIndex
:
0
,
stats
:
[
StatID
.
step
,
StatID
.
delta
],
});
expect
(
stats
.
step
).
toEqual
(
100
);
expect
(
stats
.
delta
).
toEqual
(
300
);
});
});
packages/grafana-ui/src/utils/statsCalculator.ts
0 → 100644
View file @
e0ecbc4c
// Libraries
import
isNumber
from
'lodash/isNumber'
;
import
{
TableData
,
NullValueMode
}
from
'../types/index'
;
export
enum
StatID
{
sum
=
'sum'
,
max
=
'max'
,
min
=
'min'
,
logmin
=
'logmin'
,
mean
=
'mean'
,
last
=
'last'
,
first
=
'first'
,
count
=
'count'
,
range
=
'range'
,
diff
=
'diff'
,
delta
=
'delta'
,
step
=
'step'
,
changeCount
=
'changeCount'
,
distinctCount
=
'distinctCount'
,
allIsZero
=
'allIsZero'
,
allIsNull
=
'allIsNull'
,
}
export
interface
ColumnStats
{
[
key
:
string
]:
any
;
}
// Internal function
type
StatCalculator
=
(
table
:
TableData
,
columnIndex
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
)
=>
ColumnStats
;
export
interface
StatCalculatorInfo
{
id
:
string
;
name
:
string
;
description
:
string
;
alias
?:
string
;
// optional secondary key. 'avg' vs 'mean', 'total' vs 'sum'
// Internal details
emptyInputResult
?:
any
;
// typically null, but some things like 'count' & 'sum' should be zero
standard
:
boolean
;
// The most common stats can all be calculated in a single pass
calculator
?:
StatCalculator
;
}
/**
* @param ids list of stat names or null to get all of them
*/
export
function
getStatsCalculators
(
ids
?:
string
[]):
StatCalculatorInfo
[]
{
if
(
ids
===
null
||
ids
===
undefined
)
{
if
(
!
hasBuiltIndex
)
{
getById
(
StatID
.
mean
);
}
return
listOfStats
;
}
return
ids
.
reduce
((
list
,
id
)
=>
{
const
stat
=
getById
(
id
);
if
(
stat
)
{
list
.
push
(
stat
);
}
return
list
;
},
new
Array
<
StatCalculatorInfo
>
());
}
export
interface
CalculateStatsOptions
{
table
:
TableData
;
columnIndex
:
number
;
stats
:
string
[];
// The stats to calculate
nullValueMode
?:
NullValueMode
;
}
/**
* @returns an object with a key for each selected stat
*/
export
function
calculateStats
(
options
:
CalculateStatsOptions
):
ColumnStats
{
const
{
table
,
columnIndex
,
stats
,
nullValueMode
}
=
options
;
if
(
!
stats
||
stats
.
length
<
1
)
{
return
{};
}
const
queue
=
getStatsCalculators
(
stats
);
// Return early for empty tables
// This lets the concrete implementations assume at least one row
if
(
!
table
.
rows
||
table
.
rows
.
length
<
1
)
{
const
stats
=
{}
as
ColumnStats
;
for
(
const
stat
of
queue
)
{
stats
[
stat
.
id
]
=
stat
.
emptyInputResult
!==
null
?
stat
.
emptyInputResult
:
null
;
}
return
stats
;
}
const
ignoreNulls
=
nullValueMode
===
NullValueMode
.
Ignore
;
const
nullAsZero
=
nullValueMode
===
NullValueMode
.
AsZero
;
// Avoid calculating all the standard stats if possible
if
(
queue
.
length
===
1
&&
queue
[
0
].
calculator
)
{
return
queue
[
0
].
calculator
(
table
,
columnIndex
,
ignoreNulls
,
nullAsZero
);
}
// For now everything can use the standard stats
let
values
=
standardStatsStat
(
table
,
columnIndex
,
ignoreNulls
,
nullAsZero
);
for
(
const
calc
of
queue
)
{
if
(
!
values
.
hasOwnProperty
(
calc
.
id
)
&&
calc
.
calculator
)
{
values
=
{
...
values
,
...
calc
.
calculator
(
table
,
columnIndex
,
ignoreNulls
,
nullAsZero
),
};
}
}
return
values
;
}
// ------------------------------------------------------------------------------
//
// No Exported symbols below here.
//
// ------------------------------------------------------------------------------
// private registry of all stats
interface
TableStatIndex
{
[
id
:
string
]:
StatCalculatorInfo
;
}
const
listOfStats
:
StatCalculatorInfo
[]
=
[];
const
index
:
TableStatIndex
=
{};
let
hasBuiltIndex
=
false
;
function
getById
(
id
:
string
):
StatCalculatorInfo
|
undefined
{
if
(
!
hasBuiltIndex
)
{
[
{
id
:
StatID
.
last
,
name
:
'Last'
,
description
:
'Last Value (current)'
,
standard
:
true
,
alias
:
'current'
,
calculator
:
calculateLast
,
},
{
id
:
StatID
.
first
,
name
:
'First'
,
description
:
'First Value'
,
standard
:
true
,
calculator
:
calculateFirst
},
{
id
:
StatID
.
min
,
name
:
'Min'
,
description
:
'Minimum Value'
,
standard
:
true
},
{
id
:
StatID
.
max
,
name
:
'Max'
,
description
:
'Maximum Value'
,
standard
:
true
},
{
id
:
StatID
.
mean
,
name
:
'Mean'
,
description
:
'Average Value'
,
standard
:
true
,
alias
:
'avg'
},
{
id
:
StatID
.
sum
,
name
:
'Total'
,
description
:
'The sum of all values'
,
emptyInputResult
:
0
,
standard
:
true
,
alias
:
'total'
,
},
{
id
:
StatID
.
count
,
name
:
'Count'
,
description
:
'Number of values in response'
,
emptyInputResult
:
0
,
standard
:
true
,
},
{
id
:
StatID
.
range
,
name
:
'Range'
,
description
:
'Difference between minimum and maximum values'
,
standard
:
true
,
},
{
id
:
StatID
.
delta
,
name
:
'Delta'
,
description
:
'Cumulative change in value (??? help not really sure ???)'
,
standard
:
true
,
},
{
id
:
StatID
.
step
,
name
:
'Step'
,
description
:
'Minimum interval between values'
,
standard
:
true
,
},
{
id
:
StatID
.
diff
,
name
:
'Difference'
,
description
:
'Difference between first and last values'
,
standard
:
true
,
},
{
id
:
StatID
.
logmin
,
name
:
'Min (above zero)'
,
description
:
'Used for log min scale'
,
standard
:
true
,
},
{
id
:
StatID
.
changeCount
,
name
:
'Change Count'
,
description
:
'Number of times the value changes'
,
standard
:
false
,
calculator
:
calculateChangeCount
,
},
{
id
:
StatID
.
distinctCount
,
name
:
'Distinct Count'
,
description
:
'Number of distinct values'
,
standard
:
false
,
calculator
:
calculateDistinctCount
,
},
].
forEach
(
info
=>
{
const
{
id
,
alias
}
=
info
;
if
(
index
.
hasOwnProperty
(
id
))
{
console
.
warn
(
'Duplicate Stat'
,
id
,
info
,
index
);
}
index
[
id
]
=
info
;
if
(
alias
)
{
if
(
index
.
hasOwnProperty
(
alias
))
{
console
.
warn
(
'Duplicate Stat (alias)'
,
alias
,
info
,
index
);
}
index
[
alias
]
=
info
;
}
listOfStats
.
push
(
info
);
});
hasBuiltIndex
=
true
;
}
return
index
[
id
];
}
function
standardStatsStat
(
data
:
TableData
,
columnIndex
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
const
stats
=
{
sum
:
0
,
max
:
-
Number
.
MAX_VALUE
,
min
:
Number
.
MAX_VALUE
,
logmin
:
Number
.
MAX_VALUE
,
mean
:
null
,
last
:
null
,
first
:
null
,
count
:
0
,
nonNullCount
:
0
,
allIsNull
:
true
,
allIsZero
:
false
,
range
:
null
,
diff
:
null
,
delta
:
0
,
step
:
Number
.
MAX_VALUE
,
// Just used for calcutations -- not exposed as a stat
previousDeltaUp
:
true
,
}
as
ColumnStats
;
for
(
let
i
=
0
;
i
<
data
.
rows
.
length
;
i
++
)
{
let
currentValue
=
data
.
rows
[
i
][
columnIndex
];
if
(
currentValue
===
null
)
{
if
(
ignoreNulls
)
{
continue
;
}
if
(
nullAsZero
)
{
currentValue
=
0
;
}
}
if
(
currentValue
!==
null
)
{
const
isFirst
=
stats
.
first
===
null
;
if
(
isFirst
)
{
stats
.
first
=
currentValue
;
}
if
(
isNumber
(
currentValue
))
{
stats
.
sum
+=
currentValue
;
stats
.
allIsNull
=
false
;
stats
.
nonNullCount
++
;
if
(
!
isFirst
)
{
const
step
=
currentValue
-
stats
.
last
!
;
if
(
stats
.
step
>
step
)
{
stats
.
step
=
step
;
// the minimum interval
}
if
(
stats
.
last
!
>
currentValue
)
{
// counter reset
stats
.
previousDeltaUp
=
false
;
if
(
i
===
data
.
rows
.
length
-
1
)
{
// reset on last
stats
.
delta
+=
currentValue
;
}
}
else
{
if
(
stats
.
previousDeltaUp
)
{
stats
.
delta
+=
step
;
// normal increment
}
else
{
stats
.
delta
+=
currentValue
;
// account for counter reset
}
stats
.
previousDeltaUp
=
true
;
}
}
if
(
currentValue
>
stats
.
max
)
{
stats
.
max
=
currentValue
;
}
if
(
currentValue
<
stats
.
min
)
{
stats
.
min
=
currentValue
;
}
if
(
currentValue
<
stats
.
logmin
&&
currentValue
>
0
)
{
stats
.
logmin
=
currentValue
;
}
}
if
(
currentValue
!==
0
)
{
stats
.
allIsZero
=
false
;
}
stats
.
last
=
currentValue
;
}
}
if
(
stats
.
max
===
-
Number
.
MAX_VALUE
)
{
stats
.
max
=
null
;
}
if
(
stats
.
min
===
Number
.
MAX_VALUE
)
{
stats
.
min
=
null
;
}
if
(
stats
.
step
===
Number
.
MAX_VALUE
)
{
stats
.
step
=
null
;
}
if
(
stats
.
nonNullCount
>
0
)
{
stats
.
mean
=
stats
.
sum
!
/ stats.nonNullCount
;
}
if
(
stats
.
max
!==
null
&&
stats
.
min
!==
null
)
{
stats
.
range
=
stats
.
max
-
stats
.
min
;
}
if
(
stats
.
first
!==
null
&&
stats
.
last
!==
null
)
{
if
(
isNumber
(
stats
.
first
)
&&
isNumber
(
stats
.
last
))
{
stats
.
diff
=
stats
.
last
-
stats
.
first
;
}
}
return
stats
;
}
function
calculateFirst
(
data
:
TableData
,
columnIndex
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
return
{
first
:
data
.
rows
[
0
][
columnIndex
]
};
}
function
calculateLast
(
data
:
TableData
,
columnIndex
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
return
{
last
:
data
.
rows
[
data
.
rows
.
length
-
1
][
columnIndex
]
};
}
function
calculateChangeCount
(
data
:
TableData
,
columnIndex
:
number
,
ignoreNulls
:
boolean
,
nullAsZero
:
boolean
):
ColumnStats
{
let
count
=
0
;
let
first
=
true
;
let
last
:
any
=
null
;
for
(
let
i
=
0
;
i
<
data
.
rows
.
length
;
i
++
)
{
let
currentValue
=
data
.
rows
[
i
][
columnIndex
];
if
(
currentValue
===
null
)
{
if
(
ignoreNulls
)
{
continue
;
}
if
(
nullAsZero
)
{
currentValue
=
0
;
}
}
if
(
!
first
&&
last
!==
currentValue
)
{
count
++
;
}
first
=
false
;
last
=
currentValue
;
}
return
{
changeCount
:
count
};
}
function
calculateDistinctCount
(
data
:
TableData
,
columnIndex
:
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
][
columnIndex
];
if
(
currentValue
===
null
)
{
if
(
ignoreNulls
)
{
continue
;
}
if
(
nullAsZero
)
{
currentValue
=
0
;
}
}
distinct
.
add
(
currentValue
);
}
return
{
distinctCount
:
distinct
.
size
};
}
public/app/features/dashboard/dashgrid/DashboardPanel.tsx
View file @
e0ecbc4c
...
...
@@ -98,6 +98,11 @@ export class DashboardPanel extends PureComponent<Props, State> {
}
panel
.
changeType
(
pluginId
,
hook
);
}
}
else
if
(
plugin
.
exports
&&
plugin
.
exports
.
reactPanel
)
{
const
hook
=
plugin
.
exports
.
reactPanel
.
panelTypeChangedHook
;
if
(
hook
)
{
panel
.
options
=
hook
(
panel
.
options
||
{},
null
,
null
);
}
}
this
.
setState
({
plugin
,
angularPanel
:
null
});
...
...
public/app/plugins/panel/bargauge/types.ts
View file @
e0ecbc4c
import
{
VizOrientation
,
SelectOptionItem
}
from
'@grafana/ui'
;
import
{
VizOrientation
,
SelectOptionItem
,
StatID
}
from
'@grafana/ui'
;
import
{
SingleStatBaseOptions
}
from
'../singlestat2/types'
;
export
interface
BarGaugeOptions
extends
SingleStatBaseOptions
{
...
...
@@ -25,7 +25,7 @@ export const defaults: BarGaugeOptions = {
orientation
:
VizOrientation
.
Horizontal
,
valueOptions
:
{
unit
:
'none'
,
stat
:
'avg'
,
stat
:
StatID
.
mean
,
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
...
...
public/app/plugins/panel/gauge/types.ts
View file @
e0ecbc4c
import
{
SingleStatBaseOptions
}
from
'../singlestat2/types'
;
import
{
VizOrientation
}
from
'@grafana/ui'
;
import
{
VizOrientation
,
StatID
}
from
'@grafana/ui'
;
export
interface
GaugeOptions
extends
SingleStatBaseOptions
{
maxValue
:
number
;
...
...
@@ -17,7 +17,7 @@ export const defaults: GaugeOptions = {
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
stat
:
'avg'
,
stat
:
StatID
.
mean
,
unit
:
'none'
,
},
valueMappings
:
[],
...
...
public/app/plugins/panel/singlestat2/SingleStatPanel.tsx
View file @
e0ecbc4c
...
...
@@ -4,7 +4,7 @@ import React, { PureComponent, CSSProperties } from 'react';
// Types
import
{
SingleStatOptions
,
SingleStatBaseOptions
}
from
'./types'
;
import
{
DisplayValue
,
PanelProps
,
processTimeSeries
,
NullValueMode
,
ColumnType
}
from
'@grafana/ui'
;
import
{
DisplayValue
,
PanelProps
,
NullValueMode
,
ColumnType
,
calculateStats
}
from
'@grafana/ui'
;
import
{
config
}
from
'app/core/config'
;
import
{
getDisplayProcessor
}
from
'@grafana/ui'
;
import
{
ProcessedValuesRepeater
}
from
'./ProcessedValuesRepeater'
;
...
...
@@ -14,7 +14,7 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
const
{
valueOptions
,
valueMappings
}
=
options
;
const
{
unit
,
decimals
,
stat
}
=
valueOptions
;
const
processor
=
getDisplayProcessor
({
const
display
=
getDisplayProcessor
({
unit
,
decimals
,
mappings
:
valueMappings
,
...
...
@@ -25,21 +25,25 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
});
const
values
:
DisplayValue
[]
=
[];
for
(
const
table
of
data
)
{
if
(
stat
===
'name'
)
{
values
.
push
(
display
(
table
.
name
));
}
for
(
let
i
=
0
;
i
<
table
.
columns
.
length
;
i
++
)
{
const
column
=
table
.
columns
[
i
];
// Show all columns that are not 'time'
if
(
column
.
type
===
ColumnType
.
number
)
{
const
s
eries
=
processTimeSerie
s
({
data
:
[
table
]
,
xColumn
:
i
,
yColumn
:
i
,
const
s
tats
=
calculateStat
s
({
table
,
columnIndex
:
i
,
// Hardcoded for now!
stats
:
[
stat
],
// The stats to calculate
nullValueMode
:
NullValueMode
.
Null
,
})[
0
];
const
value
=
stat
!==
'name'
?
series
.
stats
[
stat
]
:
series
.
label
;
values
.
push
(
processor
(
value
));
});
const
displayValue
=
display
(
stats
[
stat
]);
values
.
push
(
displayValue
);
}
}
}
...
...
@@ -47,6 +51,7 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
if
(
values
.
length
===
0
)
{
throw
{
message
:
'Could not find numeric data'
};
}
return
values
;
};
...
...
public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx
View file @
e0ecbc4c
...
...
@@ -2,25 +2,11 @@
import
React
,
{
PureComponent
}
from
'react'
;
// Components
import
{
FormField
,
FormLabel
,
PanelOptionsGroup
,
S
elect
,
UnitPicker
}
from
'@grafana/ui'
;
import
{
FormField
,
FormLabel
,
PanelOptionsGroup
,
S
tatsPicker
,
UnitPicker
,
StatID
}
from
'@grafana/ui'
;
// Types
import
{
SingleStatValueOptions
}
from
'./types'
;
const
statOptions
=
[
{
value
:
'min'
,
label
:
'Min'
},
{
value
:
'max'
,
label
:
'Max'
},
{
value
:
'avg'
,
label
:
'Average'
},
{
value
:
'current'
,
label
:
'Current'
},
{
value
:
'total'
,
label
:
'Total'
},
{
value
:
'name'
,
label
:
'Name'
},
{
value
:
'first'
,
label
:
'First'
},
{
value
:
'delta'
,
label
:
'Delta'
},
{
value
:
'diff'
,
label
:
'Difference'
},
{
value
:
'range'
,
label
:
'Range'
},
{
value
:
'last_time'
,
label
:
'Time of last point'
},
];
const
labelWidth
=
6
;
export
interface
Props
{
...
...
@@ -30,7 +16,11 @@ export interface Props {
export
class
SingleStatValueEditor
extends
PureComponent
<
Props
>
{
onUnitChange
=
unit
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
unit
:
unit
.
value
});
onStatChange
=
stat
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
stat
:
stat
.
value
});
onStatsChange
=
stats
=>
{
const
stat
=
stats
[
0
]
||
StatID
.
mean
;
this
.
props
.
onChange
({
...
this
.
props
.
options
,
stat
});
};
onDecimalChange
=
event
=>
{
if
(
!
isNaN
(
event
.
target
.
value
))
{
...
...
@@ -61,11 +51,13 @@ export class SingleStatValueEditor extends PureComponent<Props> {
<
PanelOptionsGroup
title=
"Value"
>
<
div
className=
"gf-form"
>
<
FormLabel
width=
{
labelWidth
}
>
Stat
</
FormLabel
>
<
S
elect
<
S
tatsPicker
width=
{
12
}
options=
{
statOptions
}
onChange=
{
this
.
onStatChange
}
value=
{
statOptions
.
find
(
option
=>
option
.
value
===
stat
)
}
placeholder=
"Choose Stat"
defaultStat=
{
StatID
.
mean
}
allowMultiple=
{
false
}
stats=
{
[
stat
]
}
onChange=
{
this
.
onStatsChange
}
/>
</
div
>
<
div
className=
"gf-form"
>
...
...
public/app/plugins/panel/singlestat2/module.tsx
View file @
e0ecbc4c
import
{
ReactPanelPlugin
}
from
'@grafana/ui'
;
import
{
ReactPanelPlugin
,
getStatsCalculators
}
from
'@grafana/ui'
;
import
{
SingleStatOptions
,
defaults
,
SingleStatBaseOptions
}
from
'./types'
;
import
{
SingleStatPanel
}
from
'./SingleStatPanel'
;
import
cloneDeep
from
'lodash/cloneDeep'
;
...
...
@@ -21,6 +21,13 @@ export const singleStatBaseOptionsCheck = (
});
}
// 6.1 renamed some stats, This makes sure they are up to date
// avg -> mean, current -> last, total -> sum
const
{
valueOptions
}
=
options
;
if
(
valueOptions
&&
valueOptions
.
stat
)
{
valueOptions
.
stat
=
getStatsCalculators
([
valueOptions
.
stat
]).
map
(
s
=>
s
.
id
)[
0
];
console
.
log
(
'CHANGED'
,
valueOptions
);
}
return
options
;
};
...
...
public/app/plugins/panel/singlestat2/types.ts
View file @
e0ecbc4c
import
{
VizOrientation
,
ValueMapping
,
Threshold
}
from
'@grafana/ui'
;
import
{
VizOrientation
,
ValueMapping
,
Threshold
,
StatID
}
from
'@grafana/ui'
;
export
interface
SingleStatBaseOptions
{
valueMappings
:
ValueMapping
[];
...
...
@@ -24,7 +24,7 @@ export const defaults: SingleStatOptions = {
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
stat
:
'avg'
,
stat
:
StatID
.
mean
,
unit
:
'none'
,
},
valueMappings
:
[],
...
...
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