Skip to content
This repository was archived by the owner on Aug 31, 2022. It is now read-only.

Commit a642a16

Browse files
committed
Phase two result-data-sample migration
Utilize scroll helper for scrolling through query until total objects are found and aggregated Assign clusters to state before querying timeseries data to trigger componentDidUpdate in TimeseriesGraph component Update mock api test with result sample case with differing measurement_title fields
1 parent e3599bc commit a642a16

File tree

12 files changed

+492
-393
lines changed

12 files changed

+492
-393
lines changed

config.json.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"elasticsearch": "{{ elasticsearch_url }}",
33
"results": "{{ results_url }}",
44
"graphql": "{{ graphql_url }}",
5+
"result_index": "{{ result_index }}",
56
"run_index": "{{ run_index }}",
67
"prefix": "{{ prefix }}"
78
}

mock/api.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,37 @@ export const mockDataSample = [
142142
},
143143
},
144144
},
145+
{
146+
_source: {
147+
run: {
148+
id: 'test_run_id',
149+
controller: 'test_controller',
150+
name: 'test_run_name',
151+
script: 'test_script',
152+
config: 'test_config',
153+
},
154+
iteration: { name: 'test_iteration_2', number: 2 },
155+
benchmark: {
156+
instances: 1,
157+
max_stddevpct: 1,
158+
message_size_bytes: 1,
159+
primary_metric: 'test_measurement_title',
160+
test_type: 'stream',
161+
},
162+
sample: {
163+
closest_sample: 1,
164+
mean: 0.1,
165+
stddev: 0.1,
166+
stddevpct: 1,
167+
uid: 'test_measurement_id',
168+
measurement_type: 'test_measurement_type',
169+
measurement_idx: 0,
170+
measurement_title: 'diff_measurement_title',
171+
'@idx': 1,
172+
name: 'sample2',
173+
},
174+
},
175+
},
145176
],
146177
},
147178
aggregations: {

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
"@antv/g2": "^3.4.10",
2626
"@babel/runtime": "^7.3.1",
2727
"@nivo/bar": "^0.36.0",
28+
"@patternfly/react-charts": "^5.3.19",
29+
"@patternfly/react-core": "^3.153.2",
30+
"@patternfly/react-icons": "^3.15.16",
31+
"@patternfly/react-table": "^2.28.39",
2832
"ant-design-pro": "^2.1.1",
2933
"antd": "^3.16.1",
3034
"classnames": "^2.2.5",

src/components/Select/index.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React, { PureComponent } from 'react';
2+
import PropTypes from 'prop-types';
3+
import {
4+
Select as PFSelect,
5+
SelectOption,
6+
SelectVariant,
7+
SelectDirection,
8+
} from '@patternfly/react-core';
9+
10+
export default class Select extends PureComponent {
11+
static propTypes = {
12+
options: PropTypes.array.isRequired,
13+
};
14+
15+
constructor(props) {
16+
super(props);
17+
18+
this.state = {
19+
isToggleIcon: false,
20+
isExpanded: false,
21+
isDisabled: false,
22+
direction: SelectDirection.down,
23+
};
24+
}
25+
26+
onToggle = isExpanded => {
27+
this.setState({
28+
isExpanded,
29+
});
30+
};
31+
32+
clearSelection = () => {
33+
this.setState({
34+
isExpanded: false,
35+
});
36+
};
37+
38+
toggleDisabled = checked => {
39+
this.setState({
40+
isDisabled: checked,
41+
});
42+
};
43+
44+
setIcon = checked => {
45+
this.setState({
46+
isToggleIcon: checked,
47+
});
48+
};
49+
50+
toggleDirection = () => {
51+
const { direction } = this.state;
52+
53+
if (direction === SelectDirection.up) {
54+
this.setState({
55+
direction: SelectDirection.down,
56+
});
57+
} else {
58+
this.setState({
59+
direction: SelectDirection.up,
60+
});
61+
}
62+
};
63+
64+
render() {
65+
const { options, onSelect, selected } = this.props;
66+
const { isExpanded, isDisabled, direction, isToggleIcon } = this.state;
67+
68+
return (
69+
<PFSelect
70+
toggleIcon={isToggleIcon}
71+
variant={SelectVariant.single}
72+
onToggle={this.onToggle}
73+
onSelect={onSelect}
74+
selections={selected}
75+
isExpanded={isExpanded}
76+
isDisabled={isDisabled}
77+
direction={direction}
78+
>
79+
{options.map(option => (
80+
<SelectOption key={option} value={parseInt(option, 10) + 1} />
81+
))}
82+
</PFSelect>
83+
);
84+
}
85+
}

src/components/TimeseriesGraph/index.js

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, { PureComponent } from 'react';
22
import PropTypes from 'prop-types';
33
import jschart from 'jschart';
44

5+
import Select from '@/components/Select';
6+
57
export default class TimeseriesGraph extends PureComponent {
68
static propTypes = {
79
dataSeriesNames: PropTypes.array.isRequired,
@@ -21,6 +23,14 @@ export default class TimeseriesGraph extends PureComponent {
2123
yAxisTitle: null,
2224
};
2325

26+
constructor(props) {
27+
super(props);
28+
29+
this.state = {
30+
selectedValue: 1,
31+
};
32+
}
33+
2434
componentDidMount = () => {
2535
const {
2636
xAxisSeries,
@@ -32,39 +42,59 @@ export default class TimeseriesGraph extends PureComponent {
3242
yAxisTitle,
3343
graphOptions,
3444
} = this.props;
45+
let { selectedValue } = this.state;
46+
selectedValue -= 1;
3547

3648
jschart.create_jschart(0, 'timeseries', graphId, graphName, xAxisTitle, yAxisTitle, {
3749
dynamic_chart: true,
3850
json_object: {
3951
x_axis_series: xAxisSeries,
40-
data_series_names: dataSeriesNames,
41-
data,
52+
data_series_names: data.length > 0 ? data[selectedValue].timeseriesLabels : dataSeriesNames,
53+
data: data.length > 0 ? data[selectedValue].timeseriesAggregation : data,
4254
},
4355
...graphOptions,
4456
});
4557
};
4658

47-
componentDidUpdate = prevProps => {
59+
componentDidUpdate = (prevProps, prevState) => {
4860
const { data, dataSeriesNames, xAxisSeries, graphId } = this.props;
61+
let { selectedValue } = this.state;
62+
selectedValue -= 1;
4963

5064
if (
5165
JSON.stringify(prevProps.data) !== JSON.stringify(data) ||
5266
JSON.stringify(prevProps.dataSeriesNames) !== JSON.stringify(dataSeriesNames) ||
53-
prevProps.xAxisSeries !== xAxisSeries
67+
prevProps.xAxisSeries !== xAxisSeries ||
68+
prevState.selectedValue !== selectedValue
5469
) {
55-
jschart.chart_reload(graphId, {
70+
jschart.chart_reload_options(graphId, {
5671
json_object: {
5772
x_axis_series: xAxisSeries,
58-
data_series_names: dataSeriesNames,
59-
data,
73+
data_series_names:
74+
data.length > 0 ? data[selectedValue].timeseriesLabels : dataSeriesNames,
75+
data: data.length > 0 ? data[selectedValue].timeseriesAggregation : data,
6076
},
6177
});
6278
}
6379
};
6480

81+
onSelect = (event, selection) => {
82+
this.setState({
83+
selectedValue: selection,
84+
});
85+
};
86+
6587
render() {
66-
const { graphId } = this.props;
88+
const { graphId, options } = this.props;
89+
const { selectedValue } = this.state;
6790

68-
return <div id={graphId} />;
91+
return (
92+
<div>
93+
{options && (
94+
<Select onSelect={this.onSelect} options={options} selected={selectedValue.toString()} />
95+
)}
96+
<div id={graphId} />
97+
</div>
98+
);
6999
}
70100
}

src/components/TimeseriesGraph/index.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jest.mock('jschart', () => ({
1919
create_jschart: jest.fn(() => {
2020
return 'true';
2121
}),
22-
chart_reload: jest.fn(() => {
22+
chart_reload_options: jest.fn(() => {
2323
return 'true';
2424
}),
2525
}));

src/global.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import './polyfill';
22
import './global.less';
3-
import 'ant-design-pro/dist/ant-design-pro.css';
3+
import '@patternfly/react-core/dist/styles/base.css';

src/models/dashboard.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
/* eslint-disable no-underscore-dangle */
2+
import _ from 'lodash';
13
import {
24
queryControllers,
35
queryResults,
46
queryResult,
57
queryTocResult,
68
queryIterationSamples,
9+
queryTimeseriesData,
710
} from '../services/dashboard';
811

912
import { insertTocTreeData } from '../utils/utils';
@@ -178,6 +181,58 @@ export default {
178181
payload: parsedSampleData.runs,
179182
});
180183
},
184+
*fetchTimeseriesData({ payload }, { call }) {
185+
const response = yield call(queryTimeseriesData, payload);
186+
const clusteredIterations = payload.clusters.data;
187+
188+
response.forEach(timeseriesResponse => {
189+
const timeseriesCollection = [];
190+
const firstResponse = timeseriesResponse.hits.hits[0]._source;
191+
const runId = firstResponse.run.id;
192+
const primaryMetric = firstResponse.sample.measurement_title;
193+
const iterationName = firstResponse.iteration.name;
194+
const sampleName = firstResponse.sample.name;
195+
timeseriesResponse.hits.hits.forEach(timeseries => {
196+
timeseriesCollection.push({
197+
x: timeseries._source['@timestamp_original'],
198+
[`y-${runId}_${iterationName}_${sampleName}`]: timeseries._source.result.value,
199+
});
200+
});
201+
202+
Object.entries(clusteredIterations[primaryMetric]).forEach(([clusterId, cluster]) => {
203+
const clusterKey = `${runId}_${iterationName}_${sampleName}`;
204+
if (clusterKey in cluster) {
205+
clusteredIterations[primaryMetric][clusterId][
206+
clusterKey
207+
].timeseries = timeseriesCollection;
208+
}
209+
});
210+
});
211+
212+
Object.entries(clusteredIterations).forEach(([primaryMetric, clusters]) => {
213+
Object.entries(clusters).forEach(([clusterKey, cluster]) => {
214+
let timeseriesAggregation = {};
215+
const timeseriesLabels = ['time'];
216+
Object.entries(cluster.clusterKeys).forEach(([keyIndex]) => {
217+
timeseriesAggregation =
218+
Object.keys(timeseriesAggregation).length > 0
219+
? (timeseriesAggregation = _.merge(
220+
timeseriesAggregation,
221+
clusteredIterations[primaryMetric][clusterKey][keyIndex].timeseries
222+
))
223+
: clusteredIterations[primaryMetric][clusterKey][keyIndex].timeseries;
224+
timeseriesLabels.push(keyIndex);
225+
});
226+
timeseriesAggregation = timeseriesAggregation.map(item => Object.values(item));
227+
clusteredIterations[primaryMetric][
228+
clusterKey
229+
].timeseriesAggregation = timeseriesAggregation;
230+
clusteredIterations[primaryMetric][clusterKey].timeseriesLabels = timeseriesLabels;
231+
});
232+
});
233+
234+
return clusteredIterations;
235+
},
181236
*updateConfigCategories({ payload }, { put }) {
182237
yield put({
183238
type: 'modifyConfigCategories',

src/pages/ComparisonSelect/index.js

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import { connect } from 'dva';
33
import { routerRedux } from 'dva/router';
44
import { Card, Spin, Tag } from 'antd';
5+
import { Title, EmptyState, EmptyStateIcon, EmptyStateBody } from '@patternfly/react-core';
6+
import { SearchIcon } from '@patternfly/react-icons';
57

68
import TableFilterSelection from '@/components/TableFilterSelection';
79
import Button from '@/components/Button';
@@ -104,31 +106,42 @@ class ComparisonSelect extends React.Component {
104106
style={{ marginBottom: 16 }}
105107
name="Compare Iterations"
106108
onClick={this.onCompareIterations}
109+
disabled={Object.values(resultIterations).length === 0}
107110
/>
108111
<TableFilterSelection onFilterTable={this.onFilterTable} filters={iterationParams} />
109-
{Object.values(resultIterations).map(run => {
110-
const rowSelection = {
111-
hideDefaultSelections: true,
112-
onSelect: record => this.onSelectChange(record, run),
113-
};
114-
return (
115-
<div key={run.run_name} style={{ marginTop: 32 }}>
116-
<div style={{ display: 'flex' }}>
117-
<h1>{run.run_name}</h1>
118-
<span style={{ marginLeft: 8 }}>
119-
<Tag color="blue">{run.run_controller}</Tag>
120-
</span>
112+
{Object.values(resultIterations).length > 0 ? (
113+
Object.values(resultIterations).map(run => {
114+
const rowSelection = {
115+
hideDefaultSelections: true,
116+
onSelect: record => this.onSelectChange(record, run),
117+
};
118+
return (
119+
<div key={run.run_name} style={{ marginTop: 32 }}>
120+
<div style={{ display: 'flex' }}>
121+
<h1>{run.run_name}</h1>
122+
<span style={{ marginLeft: 8 }}>
123+
<Tag color="blue">{run.run_controller}</Tag>
124+
</span>
125+
</div>
126+
127+
<Table
128+
rowSelection={rowSelection}
129+
columns={run.columns}
130+
dataSource={Object.values(run.iterations)}
131+
bordered
132+
/>
121133
</div>
122-
123-
<Table
124-
rowSelection={rowSelection}
125-
columns={run.columns}
126-
dataSource={Object.values(run.iterations)}
127-
bordered
128-
/>
129-
</div>
130-
);
131-
})}
134+
);
135+
})
136+
) : (
137+
<EmptyState>
138+
<EmptyStateIcon icon={SearchIcon} />
139+
<Title size="lg">No iterations found</Title>
140+
<EmptyStateBody>
141+
Unable to find iterations for the selected runs. Please try a different run set.
142+
</EmptyStateBody>
143+
</EmptyState>
144+
)}
132145
</Spin>
133146
</Card>
134147
</PageHeaderLayout>

0 commit comments

Comments
 (0)