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

Commit

Permalink
Phase two result-data-sample migration
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gurbirkalsi committed May 13, 2020
1 parent e3599bc commit a642a16
Show file tree
Hide file tree
Showing 12 changed files with 492 additions and 393 deletions.
1 change: 1 addition & 0 deletions config.json.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"elasticsearch": "{{ elasticsearch_url }}",
"results": "{{ results_url }}",
"graphql": "{{ graphql_url }}",
"result_index": "{{ result_index }}",
"run_index": "{{ run_index }}",
"prefix": "{{ prefix }}"
}
31 changes: 31 additions & 0 deletions mock/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,37 @@ export const mockDataSample = [
},
},
},
{
_source: {
run: {
id: 'test_run_id',
controller: 'test_controller',
name: 'test_run_name',
script: 'test_script',
config: 'test_config',
},
iteration: { name: 'test_iteration_2', number: 2 },
benchmark: {
instances: 1,
max_stddevpct: 1,
message_size_bytes: 1,
primary_metric: 'test_measurement_title',
test_type: 'stream',
},
sample: {
closest_sample: 1,
mean: 0.1,
stddev: 0.1,
stddevpct: 1,
uid: 'test_measurement_id',
measurement_type: 'test_measurement_type',
measurement_idx: 0,
measurement_title: 'diff_measurement_title',
'@idx': 1,
name: 'sample2',
},
},
},
],
},
aggregations: {
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"@antv/g2": "^3.4.10",
"@babel/runtime": "^7.3.1",
"@nivo/bar": "^0.36.0",
"@patternfly/react-charts": "^5.3.19",
"@patternfly/react-core": "^3.153.2",
"@patternfly/react-icons": "^3.15.16",
"@patternfly/react-table": "^2.28.39",
"ant-design-pro": "^2.1.1",
"antd": "^3.16.1",
"classnames": "^2.2.5",
Expand Down
85 changes: 85 additions & 0 deletions src/components/Select/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
Select as PFSelect,
SelectOption,
SelectVariant,
SelectDirection,
} from '@patternfly/react-core';

export default class Select extends PureComponent {
static propTypes = {
options: PropTypes.array.isRequired,
};

constructor(props) {
super(props);

this.state = {
isToggleIcon: false,
isExpanded: false,
isDisabled: false,
direction: SelectDirection.down,
};
}

onToggle = isExpanded => {
this.setState({
isExpanded,
});
};

clearSelection = () => {
this.setState({
isExpanded: false,
});
};

toggleDisabled = checked => {
this.setState({
isDisabled: checked,
});
};

setIcon = checked => {
this.setState({
isToggleIcon: checked,
});
};

toggleDirection = () => {
const { direction } = this.state;

if (direction === SelectDirection.up) {
this.setState({
direction: SelectDirection.down,
});
} else {
this.setState({
direction: SelectDirection.up,
});
}
};

render() {
const { options, onSelect, selected } = this.props;
const { isExpanded, isDisabled, direction, isToggleIcon } = this.state;

return (
<PFSelect
toggleIcon={isToggleIcon}
variant={SelectVariant.single}
onToggle={this.onToggle}
onSelect={onSelect}
selections={selected}
isExpanded={isExpanded}
isDisabled={isDisabled}
direction={direction}
>
{options.map(option => (
<SelectOption key={option} value={parseInt(option, 10) + 1} />
))}
</PFSelect>
);
}
}
48 changes: 39 additions & 9 deletions src/components/TimeseriesGraph/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import jschart from 'jschart';

import Select from '@/components/Select';

export default class TimeseriesGraph extends PureComponent {
static propTypes = {
dataSeriesNames: PropTypes.array.isRequired,
Expand All @@ -21,6 +23,14 @@ export default class TimeseriesGraph extends PureComponent {
yAxisTitle: null,
};

constructor(props) {
super(props);

this.state = {
selectedValue: 1,
};
}

componentDidMount = () => {
const {
xAxisSeries,
Expand All @@ -32,39 +42,59 @@ export default class TimeseriesGraph extends PureComponent {
yAxisTitle,
graphOptions,
} = this.props;
let { selectedValue } = this.state;
selectedValue -= 1;

jschart.create_jschart(0, 'timeseries', graphId, graphName, xAxisTitle, yAxisTitle, {
dynamic_chart: true,
json_object: {
x_axis_series: xAxisSeries,
data_series_names: dataSeriesNames,
data,
data_series_names: data.length > 0 ? data[selectedValue].timeseriesLabels : dataSeriesNames,
data: data.length > 0 ? data[selectedValue].timeseriesAggregation : data,
},
...graphOptions,
});
};

componentDidUpdate = prevProps => {
componentDidUpdate = (prevProps, prevState) => {
const { data, dataSeriesNames, xAxisSeries, graphId } = this.props;
let { selectedValue } = this.state;
selectedValue -= 1;

if (
JSON.stringify(prevProps.data) !== JSON.stringify(data) ||
JSON.stringify(prevProps.dataSeriesNames) !== JSON.stringify(dataSeriesNames) ||
prevProps.xAxisSeries !== xAxisSeries
prevProps.xAxisSeries !== xAxisSeries ||
prevState.selectedValue !== selectedValue
) {
jschart.chart_reload(graphId, {
jschart.chart_reload_options(graphId, {
json_object: {
x_axis_series: xAxisSeries,
data_series_names: dataSeriesNames,
data,
data_series_names:
data.length > 0 ? data[selectedValue].timeseriesLabels : dataSeriesNames,
data: data.length > 0 ? data[selectedValue].timeseriesAggregation : data,
},
});
}
};

onSelect = (event, selection) => {
this.setState({
selectedValue: selection,
});
};

render() {
const { graphId } = this.props;
const { graphId, options } = this.props;
const { selectedValue } = this.state;

return <div id={graphId} />;
return (
<div>
{options && (
<Select onSelect={this.onSelect} options={options} selected={selectedValue.toString()} />
)}
<div id={graphId} />
</div>
);
}
}
2 changes: 1 addition & 1 deletion src/components/TimeseriesGraph/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jest.mock('jschart', () => ({
create_jschart: jest.fn(() => {
return 'true';
}),
chart_reload: jest.fn(() => {
chart_reload_options: jest.fn(() => {
return 'true';
}),
}));
Expand Down
2 changes: 1 addition & 1 deletion src/global.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import './polyfill';
import './global.less';
import 'ant-design-pro/dist/ant-design-pro.css';
import '@patternfly/react-core/dist/styles/base.css';
55 changes: 55 additions & 0 deletions src/models/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/* eslint-disable no-underscore-dangle */
import _ from 'lodash';
import {
queryControllers,
queryResults,
queryResult,
queryTocResult,
queryIterationSamples,
queryTimeseriesData,
} from '../services/dashboard';

import { insertTocTreeData } from '../utils/utils';
Expand Down Expand Up @@ -178,6 +181,58 @@ export default {
payload: parsedSampleData.runs,
});
},
*fetchTimeseriesData({ payload }, { call }) {
const response = yield call(queryTimeseriesData, payload);
const clusteredIterations = payload.clusters.data;

response.forEach(timeseriesResponse => {
const timeseriesCollection = [];
const firstResponse = timeseriesResponse.hits.hits[0]._source;
const runId = firstResponse.run.id;
const primaryMetric = firstResponse.sample.measurement_title;
const iterationName = firstResponse.iteration.name;
const sampleName = firstResponse.sample.name;
timeseriesResponse.hits.hits.forEach(timeseries => {
timeseriesCollection.push({
x: timeseries._source['@timestamp_original'],
[`y-${runId}_${iterationName}_${sampleName}`]: timeseries._source.result.value,
});
});

Object.entries(clusteredIterations[primaryMetric]).forEach(([clusterId, cluster]) => {
const clusterKey = `${runId}_${iterationName}_${sampleName}`;
if (clusterKey in cluster) {
clusteredIterations[primaryMetric][clusterId][
clusterKey
].timeseries = timeseriesCollection;
}
});
});

Object.entries(clusteredIterations).forEach(([primaryMetric, clusters]) => {
Object.entries(clusters).forEach(([clusterKey, cluster]) => {
let timeseriesAggregation = {};
const timeseriesLabels = ['time'];
Object.entries(cluster.clusterKeys).forEach(([keyIndex]) => {
timeseriesAggregation =
Object.keys(timeseriesAggregation).length > 0
? (timeseriesAggregation = _.merge(
timeseriesAggregation,
clusteredIterations[primaryMetric][clusterKey][keyIndex].timeseries
))
: clusteredIterations[primaryMetric][clusterKey][keyIndex].timeseries;
timeseriesLabels.push(keyIndex);
});
timeseriesAggregation = timeseriesAggregation.map(item => Object.values(item));
clusteredIterations[primaryMetric][
clusterKey
].timeseriesAggregation = timeseriesAggregation;
clusteredIterations[primaryMetric][clusterKey].timeseriesLabels = timeseriesLabels;
});
});

return clusteredIterations;
},
*updateConfigCategories({ payload }, { put }) {
yield put({
type: 'modifyConfigCategories',
Expand Down
57 changes: 35 additions & 22 deletions src/pages/ComparisonSelect/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from 'react';
import { connect } from 'dva';
import { routerRedux } from 'dva/router';
import { Card, Spin, Tag } from 'antd';
import { Title, EmptyState, EmptyStateIcon, EmptyStateBody } from '@patternfly/react-core';
import { SearchIcon } from '@patternfly/react-icons';

import TableFilterSelection from '@/components/TableFilterSelection';
import Button from '@/components/Button';
Expand Down Expand Up @@ -104,31 +106,42 @@ class ComparisonSelect extends React.Component {
style={{ marginBottom: 16 }}
name="Compare Iterations"
onClick={this.onCompareIterations}
disabled={Object.values(resultIterations).length === 0}
/>
<TableFilterSelection onFilterTable={this.onFilterTable} filters={iterationParams} />
{Object.values(resultIterations).map(run => {
const rowSelection = {
hideDefaultSelections: true,
onSelect: record => this.onSelectChange(record, run),
};
return (
<div key={run.run_name} style={{ marginTop: 32 }}>
<div style={{ display: 'flex' }}>
<h1>{run.run_name}</h1>
<span style={{ marginLeft: 8 }}>
<Tag color="blue">{run.run_controller}</Tag>
</span>
{Object.values(resultIterations).length > 0 ? (
Object.values(resultIterations).map(run => {
const rowSelection = {
hideDefaultSelections: true,
onSelect: record => this.onSelectChange(record, run),
};
return (
<div key={run.run_name} style={{ marginTop: 32 }}>
<div style={{ display: 'flex' }}>
<h1>{run.run_name}</h1>
<span style={{ marginLeft: 8 }}>
<Tag color="blue">{run.run_controller}</Tag>
</span>
</div>

<Table
rowSelection={rowSelection}
columns={run.columns}
dataSource={Object.values(run.iterations)}
bordered
/>
</div>

<Table
rowSelection={rowSelection}
columns={run.columns}
dataSource={Object.values(run.iterations)}
bordered
/>
</div>
);
})}
);
})
) : (
<EmptyState>
<EmptyStateIcon icon={SearchIcon} />
<Title size="lg">No iterations found</Title>
<EmptyStateBody>
Unable to find iterations for the selected runs. Please try a different run set.
</EmptyStateBody>
</EmptyState>
)}
</Spin>
</Card>
</PageHeaderLayout>
Expand Down
Loading

0 comments on commit a642a16

Please sign in to comment.