-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ML] MultiMetric/Population Job creation: Allow model plot enablement…
… via checkbox (#24914) (#25202) * Add route/api-mapping for validateCardinality * Create directive for enableModelPlot checkbox * Ensure model plot enabled prior to cardinality check * Add callout when cardinality high * ensure correct cardinality success check * Population wizard: add enableModelPlot checkbox * Update with suggested changes from review * Remove warning when invalid. Add tests. * Ensure checkbox updated on uncheck
- Loading branch information
1 parent
ccc6b25
commit 4eedf46
Showing
15 changed files
with
358 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
...s/new_job/simple/components/enable_model_plot_checkbox/enable_model_plot_checkbox.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import React from 'react'; | ||
import { mount } from 'enzyme'; | ||
import { EnableModelPlotCheckbox } from './enable_model_plot_checkbox_view.js'; | ||
|
||
const defaultProps = { | ||
checkboxText: 'Enable model plot', | ||
onCheckboxChange: () => {}, | ||
warningStatus: false, | ||
warningContent: 'Test warning content', | ||
}; | ||
|
||
describe('EnableModelPlotCheckbox', () => { | ||
|
||
test('checkbox default is rendered correctly', () => { | ||
const wrapper = mount(<EnableModelPlotCheckbox {...defaultProps} />); | ||
const checkbox = wrapper.find({ type: 'checkbox' }); | ||
const label = wrapper.find('label'); | ||
|
||
expect(checkbox.props().checked).toBe(false); | ||
expect(label.text()).toBe('Enable model plot'); | ||
}); | ||
|
||
test('onCheckboxChange function prop is called when checkbox is toggled', () => { | ||
const mockOnChange = jest.fn(); | ||
defaultProps.onCheckboxChange = mockOnChange; | ||
|
||
const wrapper = mount(<EnableModelPlotCheckbox {...defaultProps} />); | ||
const checkbox = wrapper.find({ type: 'checkbox' }); | ||
|
||
checkbox.simulate('change', { target: { checked: true } }); | ||
expect(mockOnChange).toBeCalled(); | ||
}); | ||
|
||
}); |
154 changes: 154 additions & 0 deletions
154
..._job/simple/components/enable_model_plot_checkbox/enable_model_plot_checkbox_directive.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
|
||
import { EnableModelPlotCheckbox } from './enable_model_plot_checkbox_view.js'; | ||
import { ml } from '../../../../../services/ml_api_service'; | ||
|
||
import { uiModules } from 'ui/modules'; | ||
const module = uiModules.get('apps/ml'); | ||
|
||
module.directive('mlEnableModelPlotCheckbox', function () { | ||
return { | ||
restrict: 'AE', | ||
replace: false, | ||
scope: { | ||
formConfig: '=', | ||
ui: '=ui', | ||
getJobFromConfig: '=' | ||
}, | ||
link: function ($scope, $element) { | ||
const STATUS = { | ||
FAILED: -1, | ||
NOT_RUNNING: 0, | ||
RUNNING: 1, | ||
FINISHED: 2, | ||
WARNING: 3, | ||
}; | ||
|
||
function errorHandler(error) { | ||
console.log('Cardinality could not be validated', error); | ||
$scope.ui.cardinalityValidator.status = STATUS.FAILED; | ||
$scope.ui.cardinalityValidator.message = 'Cardinality could not be validated'; | ||
} | ||
|
||
// Only model plot cardinality relevant | ||
// format:[{id:"cardinality_model_plot_high",modelPlotCardinality:11405}, {id:"cardinality_partition_field",fieldName:"clientip"}] | ||
function checkCardinalitySuccess(data) { | ||
const response = { | ||
success: true, | ||
}; | ||
// There were no fields to run cardinality on. | ||
if (Array.isArray(data) && data.length === 0) { | ||
return response; | ||
} | ||
|
||
for (let i = 0; i < data.length; i++) { | ||
if (data[i].id === 'success_cardinality') { | ||
break; | ||
} | ||
|
||
if (data[i].id === 'cardinality_model_plot_high') { | ||
response.success = false; | ||
response.highCardinality = data[i].modelPlotCardinality; | ||
break; | ||
} | ||
} | ||
|
||
return response; | ||
} | ||
|
||
function validateCardinality() { | ||
$scope.ui.cardinalityValidator.status = STATUS.RUNNING; | ||
$scope.ui.cardinalityValidator.message = ''; | ||
|
||
// create temporary job since cardinality validation expects that format | ||
const tempJob = $scope.getJobFromConfig($scope.formConfig); | ||
|
||
ml.validateCardinality(tempJob) | ||
.then((response) => { | ||
const validationResult = checkCardinalitySuccess(response); | ||
|
||
if (validationResult.success === true) { | ||
$scope.formConfig.enableModelPlot = true; | ||
$scope.ui.cardinalityValidator.status = STATUS.FINISHED; | ||
} else { | ||
$scope.ui.cardinalityValidator.message = `Creating model plots is resource intensive and not recommended | ||
where the cardinality of the selected fields is greater than 100. Estimated cardinality | ||
for this job is ${validationResult.highCardinality}. | ||
If you enable model plot with this configuration we recommend you use a dedicated results index.`; | ||
|
||
$scope.ui.cardinalityValidator.status = STATUS.WARNING; | ||
// Go ahead and check the dedicated index box for them | ||
$scope.formConfig.useDedicatedIndex = true; | ||
// show the advanced section so the warning message is visible since validation failed | ||
$scope.ui.showAdvanced = true; | ||
} | ||
}) | ||
.catch(errorHandler); | ||
} | ||
|
||
// Re-validate cardinality for updated fields/splitField | ||
// when enable model plot is checked and form valid | ||
function revalidateCardinalityOnFieldChange() { | ||
if ($scope.formConfig.enableModelPlot === true && $scope.ui.formValid === true) { | ||
validateCardinality(); | ||
} | ||
} | ||
|
||
$scope.handleCheckboxChange = (isChecked) => { | ||
if (isChecked) { | ||
$scope.formConfig.enableModelPlot = true; | ||
validateCardinality(); | ||
} else { | ||
$scope.formConfig.enableModelPlot = false; | ||
$scope.ui.cardinalityValidator.status = STATUS.FINISHED; | ||
$scope.ui.cardinalityValidator.message = ''; | ||
updateCheckbox(); | ||
} | ||
}; | ||
|
||
// Update checkbox on these changes | ||
$scope.$watch('ui.formValid', updateCheckbox, true); | ||
$scope.$watch('ui.cardinalityValidator.status', updateCheckbox, true); | ||
// MultiMetric: Fire off cardinality validatation when fields and/or split by field is updated | ||
$scope.$watch('formConfig.fields', revalidateCardinalityOnFieldChange, true); | ||
$scope.$watch('formConfig.splitField', revalidateCardinalityOnFieldChange, true); | ||
// Population: Fire off cardinality validatation when overField is updated | ||
$scope.$watch('formConfig.overField', revalidateCardinalityOnFieldChange, true); | ||
|
||
function updateCheckbox() { | ||
// disable if (check is running && checkbox checked) or (form is invalid && checkbox unchecked) | ||
const checkboxDisabled = ( | ||
($scope.ui.cardinalityValidator.status === STATUS.RUNNING && | ||
$scope.formConfig.enableModelPlot === true) || | ||
($scope.ui.formValid !== true && | ||
$scope.formConfig.enableModelPlot === false) | ||
); | ||
const validatorRunning = ($scope.ui.cardinalityValidator.status === STATUS.RUNNING); | ||
const warningStatus = ($scope.ui.cardinalityValidator.status === STATUS.WARNING && $scope.ui.formValid === true); | ||
const checkboxText = (validatorRunning) ? 'Validating cardinality...' : 'Enable model plot'; | ||
|
||
const props = { | ||
checkboxDisabled, | ||
checkboxText, | ||
onCheckboxChange: $scope.handleCheckboxChange, | ||
warningContent: $scope.ui.cardinalityValidator.message, | ||
warningStatus, | ||
}; | ||
|
||
ReactDOM.render( | ||
React.createElement(EnableModelPlotCheckbox, props), | ||
$element[0] | ||
); | ||
} | ||
|
||
updateCheckbox(); | ||
} | ||
}; | ||
}); |
92 changes: 92 additions & 0 deletions
92
...s/new_job/simple/components/enable_model_plot_checkbox/enable_model_plot_checkbox_view.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
|
||
|
||
import PropTypes from 'prop-types'; | ||
import React, { Fragment, Component } from 'react'; | ||
|
||
import { | ||
EuiCallOut, | ||
EuiCheckbox, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
} from '@elastic/eui'; | ||
|
||
import { JsonTooltip } from '../../../../../components/json_tooltip/json_tooltip'; | ||
|
||
|
||
export class EnableModelPlotCheckbox extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.state = { | ||
checked: false, | ||
}; | ||
} | ||
|
||
warningTitle = 'Proceed with caution!'; | ||
|
||
onChange = (e) => { | ||
this.setState({ | ||
checked: e.target.checked, | ||
}); | ||
this.props.onCheckboxChange(e.target.checked); | ||
}; | ||
|
||
renderWarningCallout = () => ( | ||
<Fragment> | ||
<EuiFlexGroup direction="column"> | ||
<EuiFlexItem grow={false}> | ||
<EuiCallOut | ||
title={this.warningTitle} | ||
color="warning" | ||
iconType="help" | ||
> | ||
<p> | ||
{this.props.warningContent} | ||
</p> | ||
</EuiCallOut> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</Fragment> | ||
); | ||
|
||
render() { | ||
return ( | ||
<Fragment> | ||
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}> | ||
<EuiFlexItem grow={false}> | ||
<EuiCheckbox | ||
id="new_job_enable_model_plot" | ||
label={this.props.checkboxText} | ||
onChange={this.onChange} | ||
disabled={this.props.checkboxDisabled} | ||
checked={this.state.checked} | ||
/> | ||
</EuiFlexItem> | ||
|
||
<EuiFlexItem grow={false}> | ||
<JsonTooltip id={'new_job_enable_model_plot'} position="top" /> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
{ this.props.warningStatus && this.renderWarningCallout() } | ||
</Fragment> | ||
); | ||
} | ||
} | ||
|
||
EnableModelPlotCheckbox.propTypes = { | ||
checkboxDisabled: PropTypes.bool, | ||
checkboxText: PropTypes.string.isRequired, | ||
onCheckboxChange: PropTypes.func.isRequired, | ||
warningStatus: PropTypes.bool.isRequired, | ||
warningContent: PropTypes.string.isRequired, | ||
}; | ||
|
||
EnableModelPlotCheckbox.defaultProps = { | ||
checkboxDisabled: false, | ||
}; |
8 changes: 8 additions & 0 deletions
8
x-pack/plugins/ml/public/jobs/new_job/simple/components/enable_model_plot_checkbox/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
|
||
import './enable_model_plot_checkbox_directive.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.