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 #301 from gky360/feature/filter-results
Browse files Browse the repository at this point in the history
Add simple filtering UI for experiments table
  • Loading branch information
ofk authored Oct 4, 2019
2 parents d72c385 + 3ab70e6 commit 162ac57
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 2 deletions.
11 changes: 11 additions & 0 deletions frontend/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,17 @@ export const updateResultSelect = (projectId, resultId, selected) => ({
selected,
});

// result filter

export const RESULT_FILTER_UPDATE = 'RESULT_FILTER_UPDATE';

export const updateResultFilter = (projectId, filterKey, filterText) => ({
type: RESULT_FILTER_UPDATE,
projectId,
filterKey,
filterText,
});

// lines config

export const LINES_CONFIG_LINE_UPDATE = 'LINES_CONFIG_LINE_UPDATE';
Expand Down
26 changes: 26 additions & 0 deletions frontend/src/components/ExperimentsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import ResultName from './experiments_table_cell/ResultName';
import ToggleResult from './experiments_table_cell/ToggleResult';
import SubComponent from './experiments_table_cell/SubComponent';
import ResultFilter from './experiments_table_cell/ResultFilter';
import VisibilityCheckbox from './VisibilityCheckbox';
import TableConfigurator from './TableConfigurator';

Expand All @@ -24,12 +25,14 @@ const ExperimentsTable = (props) => {
project,
results,
resultsStatus,
resultFilter,
stats,
projectConfig,
globalConfig,
onResultsConfigSelectUpdate,
onResultUpdate,
onResultSelect,
onResultFilterUpdate,
onCommandSubmit,
onTableExpandedUpdate,
onTableColumnsVisibilityUpdate,
Expand Down Expand Up @@ -90,6 +93,7 @@ const ExperimentsTable = (props) => {
},
className: 'text-center',
sortable: false,
filterable: false,
minWidth: 40,
Aggregated: (row) => {
const groupedResultKeys = row.subRows.map((r) => {
Expand Down Expand Up @@ -129,13 +133,29 @@ const ExperimentsTable = (props) => {
return null;
},
minWidth: 250,
Filter: (
<ResultFilter
projectId={project.id}
filterKey="name"
filterText={resultFilter.name}
onResultFilterUpdate={onResultFilterUpdate}
/>
),
},
];
if (isGrouped) {
nameColumns.unshift({
Header: '',
id: 'group',
accessor: (p) => getGrandParentDirectoryName(p),
Filter: (
<ResultFilter
projectId={project.id}
filterKey="group"
filterText={resultFilter.group}
onResultFilterUpdate={onResultFilterUpdate}
/>
),
});
}
const groupedKey = isGrouped ? ['group'] : [];
Expand All @@ -153,6 +173,7 @@ const ExperimentsTable = (props) => {
style: defaultStyle,
show: !(knownLogKeysConfig[logKey] || {}).hidden,
aggregate: () => '',
filterable: false,
}));

const argsList = sortKeys(argKeys, knownArgKeysConfig).map((argKey) => ({
Expand All @@ -169,6 +190,7 @@ const ExperimentsTable = (props) => {
style: defaultStyle,
show: !(knownArgKeysConfig[argKey] || {}).hidden,
aggregate: () => '',
filterable: false,
}));

const columns = [
Expand Down Expand Up @@ -203,6 +225,7 @@ const ExperimentsTable = (props) => {
expanded={expanded}
onExpandedChange={(nextExpanded) => onTableExpandedUpdate(project.id, nextExpanded)}
pageSize={resultList.length}
filterable
defaultSortMethod={sortMethod}
defaultSorted={[
{
Expand Down Expand Up @@ -254,19 +277,22 @@ ExperimentsTable.propTypes = {
project: uiPropTypes.project.isRequired,
results: uiPropTypes.results.isRequired,
resultsStatus: uiPropTypes.resultsStatus,
resultFilter: uiPropTypes.resultFilter,
projectConfig: uiPropTypes.projectConfig.isRequired,
globalConfig: uiPropTypes.globalConfig.isRequired,
stats: uiPropTypes.stats.isRequired,
onResultsConfigSelectUpdate: PropTypes.func.isRequired,
onResultUpdate: PropTypes.func.isRequired,
onResultSelect: PropTypes.func.isRequired,
onResultFilterUpdate: PropTypes.func.isRequired,
onCommandSubmit: PropTypes.func.isRequired,
onTableExpandedUpdate: PropTypes.func.isRequired,
onTableColumnsVisibilityUpdate: PropTypes.func.isRequired,
};

ExperimentsTable.defaultProps = {
resultsStatus: {},
resultFilter: {},
};

export default ExperimentsTable;
35 changes: 35 additions & 0 deletions frontend/src/components/experiments_table_cell/ResultFilter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';

const ResultFilter = ({ projectId, filterKey, filterText, onResultFilterUpdate }) => {
const onChange = useCallback(
(e) => {
onResultFilterUpdate(projectId, filterKey, e.target.value);
},
[projectId, filterKey, onResultFilterUpdate]
);

return (
<div>
<input
type="text"
placeholder={`filter ${filterKey}`}
value={filterText}
onChange={onChange}
/>
</div>
);
};

ResultFilter.propTypes = {
projectId: PropTypes.number.isRequired,
filterKey: PropTypes.string.isRequired,
filterText: PropTypes.string,
onResultFilterUpdate: PropTypes.func.isRequired,
};

ResultFilter.defaultProps = {
filterText: '',
};

export default ResultFilter;
45 changes: 43 additions & 2 deletions frontend/src/containers/PlotContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
updateAxisScale,
toggleLogKeySelect,
updateResultSelect,
updateResultFilter,
updateResultsConfigSelect,
updateXAxisKey,
updateAxisScaleRangeType,
Expand All @@ -32,7 +33,12 @@ import LogVisualizer from '../components/LogVisualizer';
import SideBar from '../components/SideBar';
import ResultTypeSelector from '../components/ResultTypeSelector';
import { defaultProjectStatus, defaultProjectConfig } from '../constants';
import { startPolling, stopPolling } from '../utils';
import {
startPolling,
stopPolling,
getGrandParentDirectoryName,
displayResultNameFull,
} from '../utils';

class PlotContainer extends React.Component {
componentDidMount() {
Expand Down Expand Up @@ -145,12 +151,14 @@ class PlotContainer extends React.Component {
project={project}
results={results}
resultsStatus={projectStatus.resultsStatus}
resultFilter={projectStatus.resultFilter}
stats={stats}
projectConfig={projectConfig}
globalConfig={globalConfig}
onResultsConfigSelectUpdate={this.props.updateResultsConfigSelect}
onResultUpdate={this.props.updateResult}
onResultSelect={this.props.updateResultSelect}
onResultFilterUpdate={this.props.updateResultFilter}
onCommandSubmit={this.props.createCommand}
onTableExpandedUpdate={this.props.updateTableExpanded}
onTableColumnsVisibilityUpdate={this.handleExperimentsTableColumnsVisibilityUpdate}
Expand Down Expand Up @@ -187,6 +195,7 @@ PlotContainer.propTypes = {
updateAxisScale: PropTypes.func.isRequired,
toggleLogKeySelect: PropTypes.func.isRequired,
updateResultSelect: PropTypes.func.isRequired,
updateResultFilter: PropTypes.func.isRequired,
updateResultsConfigSelect: PropTypes.func.isRequired,
updateXAxisKey: PropTypes.func.isRequired,
updateAxisScaleRangeType: PropTypes.func.isRequired,
Expand All @@ -197,6 +206,33 @@ PlotContainer.propTypes = {
updateTargetResultType: PropTypes.func.isRequired,
};

const getTargetTextForFilter = (project, result, filterKey) => {
switch (filterKey) {
case 'group':
return getGrandParentDirectoryName(result);
case 'name':
return displayResultNameFull(project, result);
default:
return result[filterKey];
}
};

const filterResults = (project, results, resultFilter) => {
const filteredResults = Object.keys(results).reduce((pre, resultId) => {
const result = results[resultId];
const isMatched = Object.keys(resultFilter).every((filterKey) => {
const filterText = resultFilter[filterKey];
const targetText = getTargetTextForFilter(project, result, filterKey);
return !targetText || targetText.includes(filterText);
});
if (isMatched) {
return { ...pre, [resultId]: result };
}
return pre;
}, {});
return filteredResults;
};

const mapStateToProps = (state, ownProps) => {
const projectId = Number(ownProps.params.projectId);
const { entities, status, config } = state;
Expand All @@ -205,11 +241,15 @@ const mapStateToProps = (state, ownProps) => {
const projectStatus = status.projectsStatus[projectId] || defaultProjectStatus;
const projectConfig = config.projectsConfig[projectId] || defaultProjectConfig;
const globalConfig = config.global;
const { resultFilter = {} } = projectStatus;
const { stats } = status;

const filteredResults = filterResults(project, results, resultFilter);

return {
projectId,
project,
results,
results: filteredResults,
projectStatus,
projectConfig,
globalConfig,
Expand All @@ -230,6 +270,7 @@ export default connect(
updateAxisScale,
toggleLogKeySelect,
updateResultSelect,
updateResultFilter,
updateResultsConfigSelect,
updateXAxisKey,
updateAxisScaleRangeType,
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/reducers/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,24 @@ const resultsStatusReducer = (state = {}, action) => {
return state;
};

const resultFilterReducer = (state = {}, action) => {
switch (action.type) {
case ActionTypes.RESULT_FILTER_UPDATE: {
const { filterKey, filterText } = action;
return {
...state,
[filterKey]: filterText,
};
}
default:
return state;
}
};

const projectStatusReducer = combineReducers({
chartDownloadStatus: chartDownloadStatusReducer,
resultsStatus: resultsStatusReducer,
resultFilter: resultFilterReducer,
});

const projectsStatusReducer = (state = {}, action) => {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/store/uiPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ export const resultStatus = PropTypes.shape({

export const resultsStatus = PropTypes.objectOf(resultStatus);

export const resultFilter = PropTypes.objectOf(PropTypes.string);

export const projectStatus = PropTypes.shape({
chartDownloadStatus: PropTypes.oneOf(Object.values(CHART_DOWNLOAD_STATUS)),
resultsStatus,
Expand Down

0 comments on commit 162ac57

Please sign in to comment.