Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #192 from gky360/feature/download-chart-with-legend
Browse files Browse the repository at this point in the history
Download chart with legend
  • Loading branch information
ofk authored Nov 13, 2018
2 parents 9090f6a + deec6b3 commit 0851fd3
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 39 deletions.
34 changes: 25 additions & 9 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"dependencies": {
"babel-polyfill": "^6.26.0",
"bootstrap": "^4.1.3",
"html2canvas": "^1.0.0-alpha.12",
"jquery": "^3.2.1",
"moment": "^2.22.2",
"open-iconic": "^1.1.1",
Expand All @@ -67,7 +68,6 @@
"redux-persist": "^5.4.0",
"redux-requests": "^1.0.2",
"redux-thunk": "^2.2.0",
"save-svg-as-png": "^1.4.5",
"whatwg-fetch": "^2.0.3"
}
}
9 changes: 9 additions & 0 deletions frontend/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,12 @@ export const updateGlobalResultNameAlignment = (isResultNameAlignRight) => ({
isResultNameAlignRight
});

// download

export const CHART_DOWNLOAD_STATUS_UPDATE = 'CHART_DOWNLOAD_STATUS_UPDATE';

export const updateChartDownloadStatus = (projectId, chartDownloadStatus) => ({
type: CHART_DOWNLOAD_STATUS_UPDATE,
projectId,
chartDownloadStatus
});
79 changes: 73 additions & 6 deletions frontend/src/components/LogVisualizer.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { Button } from 'reactstrap';
import {
LineChart,
Expand All @@ -22,6 +23,7 @@ import {
downloadObjectAsJson,
downloadChartAsPng
} from '../utils';
import { CHART_DOWNLOAD_STATUS } from '../constants';
import LogVisualizerLegend from './LogVisualizerLegend';
import LogVisualizerTooltip from './LogVisualizerTooltip';

Expand Down Expand Up @@ -69,6 +71,18 @@ class LogVisualizer extends React.Component {
this.handleClickDownloadPNG = this.handleClickDownloadPNG.bind(this);
}

componentDidUpdate() {
const { project, projectStatus, onChartDownloadStatusUpdate } = this.props;
if (projectStatus.chartDownloadStatus === CHART_DOWNLOAD_STATUS.REQUESTED) {
onChartDownloadStatusUpdate(project.id, CHART_DOWNLOAD_STATUS.CONVERTING);
const exportName = getUrlSafeProjectNameFull(project);
// eslint-disable-next-line react/no-find-dom-node
downloadChartAsPng(ReactDOM.findDOMNode(this.chart), exportName).then(() => {
onChartDownloadStatusUpdate(project.id, CHART_DOWNLOAD_STATUS.NONE);
});
}
}

chartRef(element) {
this.chart = element;
}
Expand All @@ -81,16 +95,17 @@ class LogVisualizer extends React.Component {
}

handleClickDownloadPNG() {
const { project } = this.props;
const exportName = getUrlSafeProjectNameFull(project);
// eslint-disable-next-line react/no-find-dom-node
downloadChartAsPng(ReactDOM.findDOMNode(this.chart), exportName);
const { project, projectStatus, onChartDownloadStatusUpdate } = this.props;
if (projectStatus.chartDownloadStatus === CHART_DOWNLOAD_STATUS.NONE) {
onChartDownloadStatusUpdate(project.id, CHART_DOWNLOAD_STATUS.REQUESTED);
}
}

render() {
const {
project,
results,
projectStatus,
projectConfig,
globalConfig,
stats
Expand Down Expand Up @@ -134,16 +149,66 @@ class LogVisualizer extends React.Component {
});

const { chartSize, isResultNameAlignRight } = globalConfig;
// TODO: split these components into a separated component
const tempHiddenPlot =
(projectStatus.chartDownloadStatus !== CHART_DOWNLOAD_STATUS.NONE) ? (
<div className="d-flex plot-hidden" ref={this.chartRef}>
<ResponsiveContainer
width={chartSize.width}
height={chartSize.height}
aspect={chartSize.aspect}
>
<LineChart data={data}>
<XAxis
type="number"
dataKey={xAxisKey}
scale={xAxis.scale}
domain={getDomain(xAxis)}
allowDataOverflow
/>
<YAxis
yAxisId="yLeftAxis"
orientation="left"
scale={yLeftAxis.scale}
domain={getDomain(yLeftAxis)}
tickFormatter={formatLogValue()}
allowDataOverflow
/>
<YAxis
yAxisId="yRightAxis"
orientation="right"
scale={yRightAxis.scale}
domain={getDomain(yRightAxis)}
tickFormatter={formatLogValue()}
allowDataOverflow
/>
<CartesianGrid strokeDasharray="3 3" />
{lineElems.yLeftAxis}
{lineElems.yRightAxis}
</LineChart>
</ResponsiveContainer>
<div>
<LogVisualizerLegend
project={project}
results={results}
lines={axisLines}
maxHeight={chartSize.height}
isResultNameAlignRight={isResultNameAlignRight}
/>
</div>
</div>
) : null;

return (
<div className="log-visualizer-root">
{tempHiddenPlot}
<div className="d-flex">
<ResponsiveContainer
width={chartSize.width}
height={chartSize.height}
aspect={chartSize.aspect}
>
<LineChart data={data} ref={this.chartRef}>
<LineChart data={data}>
<XAxis
type="number"
dataKey={xAxisKey}
Expand Down Expand Up @@ -197,9 +262,11 @@ class LogVisualizer extends React.Component {
LogVisualizer.propTypes = {
project: uiPropTypes.project.isRequired,
results: uiPropTypes.results.isRequired,
projectStatus: uiPropTypes.projectStatus.isRequired,
stats: uiPropTypes.stats.isRequired,
projectConfig: uiPropTypes.projectConfig.isRequired,
globalConfig: uiPropTypes.globalConfig.isRequired
globalConfig: uiPropTypes.globalConfig.isRequired,
onChartDownloadStatusUpdate: PropTypes.func.isRequired
};

export default LogVisualizer;
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ export const defaultProjectConfig = {
tableState: {}
};

export const CHART_DOWNLOAD_STATUS = {
NONE: 'NONE',
REQUESTED: 'REQUESTED',
CONVERTING: 'CONVERTING'
};

export const defaultProjectStatus = {
chartDownloadStatus: CHART_DOWNLOAD_STATUS.NONE
};

export const keyOptions = ['epoch', 'iteration', 'episode', 'step', 'elapsed_time'];

export const SCHEDULE_NOW = 'scheduleNow';
Expand Down
18 changes: 14 additions & 4 deletions frontend/src/containers/PlotContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ import {
updateAxisScaleRangeType, updateAxisScaleRangeNumber,
createCommand,
updateTableExpanded,
updateTableColumnsVisibility
updateTableColumnsVisibility,
updateChartDownloadStatus
} from '../actions';
import BreadcrumbLink from '../components/BreadcrumbLink';
import ExperimentsTable from '../components/ExperimentsTable';
import ExperimentsTableConfigurator from '../components/ExperimentsTableConfigurator';
import LogVisualizer from '../components/LogVisualizer';
import NavigationBar from '../components/NavigationBar';
import SideBar from '../components/SideBar';
import { defaultProjectConfig, keyOptions } from '../constants';
import { defaultProjectStatus, defaultProjectConfig, keyOptions } from '../constants';
import { startPolling, stopPolling } from '../utils';


Expand Down Expand Up @@ -66,6 +67,7 @@ class PlotContainer extends React.Component {
const {
project,
results,
projectStatus,
fetchState,
projectConfig,
globalConfig,
Expand Down Expand Up @@ -110,9 +112,11 @@ class PlotContainer extends React.Component {
<LogVisualizer
project={project}
results={results}
projectStatus={projectStatus}
stats={stats}
projectConfig={projectConfig}
globalConfig={globalConfig}
onChartDownloadStatusUpdate={this.props.updateChartDownloadStatus}
/>
<ExperimentsTable
project={project}
Expand Down Expand Up @@ -164,10 +168,12 @@ const mapStateToProps = (state, ownProps) => {
const {
entities,
fetchState,
status,
config
} = state;
const { projects = {}, results = {} } = entities;
const project = projects[projectId] || { id: projectId };
const projectStatus = status.projectsStatus[projectId] || defaultProjectStatus;
const projectConfig = config.projectsConfig[projectId] || defaultProjectConfig;
const globalConfig = config.global;
const stats = mapEntitiesToStats(entities);
Expand All @@ -177,6 +183,7 @@ const mapStateToProps = (state, ownProps) => {
project,
results,
fetchState,
projectStatus,
projectConfig,
globalConfig,
stats
Expand All @@ -188,6 +195,7 @@ PlotContainer.propTypes = {
project: uiPropTypes.project.isRequired,
results: uiPropTypes.results.isRequired,
fetchState: uiPropTypes.fetchState.isRequired,
projectStatus: uiPropTypes.projectStatus.isRequired,
projectConfig: uiPropTypes.projectConfig.isRequired,
globalConfig: uiPropTypes.globalConfig.isRequired,
stats: uiPropTypes.stats.isRequired,
Expand All @@ -209,7 +217,8 @@ PlotContainer.propTypes = {
updateAxisScaleRangeType: PropTypes.func.isRequired,
updateAxisScaleRangeNumber: PropTypes.func.isRequired,
updateTableExpanded: PropTypes.func.isRequired,
updateTableColumnsVisibility: PropTypes.func.isRequired
updateTableColumnsVisibility: PropTypes.func.isRequired,
updateChartDownloadStatus: PropTypes.func.isRequired
};

export default connect(mapStateToProps, {
Expand All @@ -231,5 +240,6 @@ export default connect(mapStateToProps, {
updateAxisScaleRangeType,
updateAxisScaleRangeNumber,
updateTableExpanded,
updateTableColumnsVisibility
updateTableColumnsVisibility,
updateChartDownloadStatus
})(PlotContainer);
Loading

0 comments on commit 0851fd3

Please sign in to comment.