Skip to content

Commit

Permalink
[ML] Edits to EUI / React anomalies table after review
Browse files Browse the repository at this point in the history
  • Loading branch information
peteharverson committed May 24, 2018
1 parent 0ccfd06 commit bc94853
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,11 @@ function getColumns(
{
field: 'severity',
name: `${(isAggregatedData === true) ? 'max ' : ''}severity`,
render: (score) => {
return (
<EuiHealth color={getSeverityColor(score)} compressed="true">
{score >= 1 ? Math.floor(score) : '< 1'}
</EuiHealth>
);
},
render: (score) => (
<EuiHealth color={getSeverityColor(score)} compressed="true">
{score >= 1 ? Math.floor(score) : '< 1'}
</EuiHealth>
),
sortable: true
},
{
Expand All @@ -111,15 +109,13 @@ function getColumns(
columns.push({
field: 'entityValue',
name: 'found for',
render: (entityValue, item) => {
return (
<EntityCell
entityName={item.entityName}
entityValue={entityValue}
filter={filter}
/>
);
},
render: (entityValue, item) => (
<EntityCell
entityName={item.entityName}
entityValue={entityValue}
filter={filter}
/>
),
sortable: true
});
}
Expand All @@ -128,14 +124,12 @@ function getColumns(
columns.push({
field: 'influencers',
name: 'influenced by',
render: (influencers) => {
return (
<InfluencersCell
limit={INFLUENCERS_LIMIT}
influencers={influencers}
/>
);
},
render: (influencers) => (
<InfluencersCell
limit={INFLUENCERS_LIMIT}
influencers={influencers}
/>
),
sortable: true
});
}
Expand Down Expand Up @@ -176,14 +170,12 @@ function getColumns(
columns.push({
field: 'metricDescriptionSort',
name: 'description',
render: (metricDescriptionSort, item) => {
return (
<DescriptionCell
actual={item.actual}
typical={item.typical}
/>
);
},
render: (metricDescriptionSort, item) => (
<DescriptionCell
actual={item.actual}
typical={item.typical}
/>
),
sortable: true
});
}
Expand All @@ -196,7 +188,7 @@ function getColumns(
});

const showExamples = items.some(item => item.entityName === 'mlcategory');
const showLinks = showViewSeriesLink === true && items.some((item) => showLinksMenuForItem(item));
const showLinks = (showViewSeriesLink === true) || items.some(item => showLinksMenuForItem(item));

if (showLinks === true) {
columns.push({
Expand Down Expand Up @@ -264,7 +256,7 @@ class AnomaliesTable extends Component {
return anomaly.rowId === rowId;
});

return matching === undefined;
return (matching === undefined);
});

if (prevExpandedNotInData !== undefined) {
Expand All @@ -283,14 +275,15 @@ class AnomaliesTable extends Component {
if (itemIdToExpandedRowMap[item.rowId]) {
delete itemIdToExpandedRowMap[item.rowId];
} else {
const examples = item.entityName === 'mlcategory' ?
const examples = (item.entityName === 'mlcategory') ?
_.get(this.props.tableData, ['examplesByJobId', item.jobId, item.entityValue]) : undefined;
itemIdToExpandedRowMap[item.rowId] = (
<AnomalyDetails
anomaly={item}
examples={examples}
isAggregatedData={this.isShowingAggregatedData()}
filter={this.props.filter}
influencersLimit={INFLUENCERS_LIMIT}
/>
);
}
Expand All @@ -310,7 +303,7 @@ class AnomaliesTable extends Component {
if (expandButton.length > 0) {
const rowId = expandButton.attr('data-row-id');
mouseOverRecord = this.props.tableData.anomalies.find((anomaly) => {
return anomaly.rowId === rowId;
return (anomaly.rowId === rowId);
});
}
}
Expand All @@ -325,11 +318,9 @@ class AnomaliesTable extends Component {
mlAnomaliesTableService.anomalyRecordMouseenter.changed(mouseOverRecord);
}
}
} else {
if (mouseOverRecord !== undefined) {
// Mouse is now over a row, fire mouseenter on the record.
mlAnomaliesTableService.anomalyRecordMouseenter.changed(mouseOverRecord);
}
} else if (mouseOverRecord !== undefined) {
// Mouse is now over a row, fire mouseenter on the record.
mlAnomaliesTableService.anomalyRecordMouseenter.changed(mouseOverRecord);
}

this.mouseOverRecord = mouseOverRecord;
Expand All @@ -342,9 +333,10 @@ class AnomaliesTable extends Component {
};

render() {
if (this.props.tableData === undefined ||
this.props.tableData.anomalies === undefined ||
this.props.tableData.anomalies.length === 0) {
const { timefilter, tableData, filter } = this.props;

if (tableData === undefined ||
tableData.anomalies === undefined || tableData.anomalies.length === 0) {
return (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
Expand All @@ -357,15 +349,15 @@ class AnomaliesTable extends Component {
}

const columns = getColumns(
this.props.tableData.anomalies,
this.props.tableData.examplesByJobId,
tableData.anomalies,
tableData.examplesByJobId,
this.isShowingAggregatedData(),
this.props.tableData.interval,
this.props.timefilter,
this.props.tableData.showViewSeriesLink,
tableData.interval,
timefilter,
tableData.showViewSeriesLink,
this.state.itemIdToExpandedRowMap,
this.toggleRow,
this.props.filter);
filter);

const sorting = {
sort: {
Expand All @@ -377,7 +369,7 @@ class AnomaliesTable extends Component {
return (
<EuiInMemoryTable
className="ml-anomalies-table"
items={this.props.tableData.anomalies}
items={tableData.anomalies}
columns={columns}
pagination={{
pageSizeOptions: [10, 25, 100],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
import { formatValue } from 'plugins/ml/formatters/format_value';

const TIME_FIELD_NAME = 'timestamp';
const INFLUENCERS_LIMIT = 5;


function getFilterEntity(entityName, entityValue, filter) {
Expand Down Expand Up @@ -116,14 +115,14 @@ function getDetailsItems(anomaly, examples, filter) {

if (examples !== undefined && examples.length > 0) {
examples.forEach((example, index) => {
const title = index === 0 ? 'category examples' : '';
const title = (index === 0) ? 'category examples' : '';
items.push({ title, description: example });
});
}

items.push({
title: 'function',
description: source.function !== 'metric' ? source.function : source.function_description
description: (source.function !== 'metric') ? source.function : source.function_description
});

if (source.field_name !== undefined) {
Expand Down Expand Up @@ -162,7 +161,7 @@ function getDetailsItems(anomaly, examples, filter) {
// will already have been added for display.
if (causes.length > 1) {
causes.forEach((cause, index) => {
const title = index === 0 ? `${cause.entityName} values` : '';
const title = (index === 0) ? `${cause.entityName} values` : '';
let description = `${cause.entityValue} (actual ${formatValue(cause.actual, source.function)}, `;
description += `typical ${formatValue(cause.typical, source.function)}, probability ${cause.probability})`;
items.push({ title, description });
Expand Down Expand Up @@ -213,7 +212,7 @@ export class AnomalyDetails extends Component {
<h5>Description</h5>
{anomalyDescription}
</EuiText>
{mvDescription !== undefined &&
{(mvDescription !== undefined) &&
<EuiText size="xs">
{mvDescription}
</EuiText>
Expand Down Expand Up @@ -254,8 +253,8 @@ export class AnomalyDetails extends Component {
let othersCount = 0;
let numToDisplay = 0;
if (anomalyInfluencers !== undefined) {
numToDisplay = this.state.showAllInfluencers === true ?
anomalyInfluencers.length : Math.min(INFLUENCERS_LIMIT, anomalyInfluencers.length);
numToDisplay = (this.state.showAllInfluencers === true) ?
anomalyInfluencers.length : Math.min(this.props.influencersLimit, anomalyInfluencers.length);
othersCount = Math.max(anomalyInfluencers.length - numToDisplay, 0);

if (othersCount === 1) {
Expand Down Expand Up @@ -293,7 +292,7 @@ export class AnomalyDetails extends Component {
and {othersCount} more
</EuiLink>
}
{numToDisplay > (INFLUENCERS_LIMIT + 1) &&
{numToDisplay > (this.props.influencersLimit + 1) &&
<EuiLink
onClick={() => this.toggleAllInfluencers()}
>
Expand Down Expand Up @@ -322,5 +321,6 @@ AnomalyDetails.propTypes = {
anomaly: PropTypes.object.isRequired,
examples: PropTypes.array,
isAggregatedData: PropTypes.bool,
filter: PropTypes.func
filter: PropTypes.func,
influencersLimit: PropTypes.number
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
* a filter on this entity.
*/
export function EntityCell({ entityName, entityValue, filter }) {
const valueText = entityName !== 'mlcategory' ? entityValue : `mlcategory ${entityValue}`;
const valueText = (entityName !== 'mlcategory') ? entityValue : `mlcategory ${entityValue}`;
return (
<React.Fragment>
{valueText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class InfluencersCell extends Component {
const recordInfluencers = this.props.influencers || [];

const influencers = [];
_.each(recordInfluencers, (influencer) => {
recordInfluencers.forEach((influencer) => {
_.each(influencer, (influencerFieldValue, influencerFieldName) => {
influencers.push({
influencerFieldName,
Expand Down
37 changes: 22 additions & 15 deletions x-pack/plugins/ml/public/components/anomalies_table/links_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ export class LinksMenu extends Component {
}

openCustomUrl = (customUrl) => {
console.log('Anomalies Table - open customUrl for record:', this.props.anomaly);
const { anomaly, interval, isAggregatedData } = this.props;

console.log('Anomalies Table - open customUrl for record:', anomaly);

// If url_value contains $earliest$ and $latest$ tokens, add in times to the source record.
// Create a copy of the record as we are adding properties into it.
const record = _.cloneDeep(this.props.anomaly.source);
const record = _.cloneDeep(anomaly.source);
const timestamp = record.timestamp;
const interval = this.props.interval;
const configuredUrlValue = customUrl.url_value;
const timeRangeInterval = parseInterval(customUrl.time_range);
if (configuredUrlValue.includes('$earliest$')) {
Expand All @@ -74,7 +75,7 @@ export class LinksMenu extends Component {
if (timeRangeInterval !== null) {
latestMoment.add(timeRangeInterval);
} else {
if (this.props.isAggregatedData === true) {
if (isAggregatedData === true) {
latestMoment = moment(timestamp).endOf(interval);
if (interval === 'hour') {
// Show to the end of the next hour.
Expand All @@ -97,9 +98,9 @@ export class LinksMenu extends Component {

ml.results.getCategoryDefinition(jobId, categoryId)
.then((resp) => {
// Prefix each of the terms with '+' so that the Elasticsearch Query String query
// run in a drilldown Kibana dashboard has to match on all terms.
const termsArray = _.map(resp.terms.split(' '), (term) => { return '+' + term; });
// Prefix each of the terms with '+' so that the Elasticsearch Query String query
// run in a drilldown Kibana dashboard has to match on all terms.
const termsArray = resp.terms.split(' ').map(term => `+${term}`);
record.mlcategoryterms = termsArray.join(' ');
record.mlcategoryregex = resp.regex;

Expand Down Expand Up @@ -188,10 +189,8 @@ export class LinksMenu extends Component {
});

// Need to encode the _a parameter in case any entities contain unsafe characters such as '+'.
let path = chrome.getBasePath();
path += '/app/ml#/timeseriesexplorer';
path += '?_g=' + _g;
path += '&_a=' + encodeURIComponent(_a);
let path = `${chrome.getBasePath()}/app/ml#/timeseriesexplorer`;
path += `?_g=${_g}&_a=${encodeURIComponent(_a)}`;
window.open(path, '_blank');
}

Expand All @@ -201,6 +200,12 @@ export class LinksMenu extends Component {
const indexPatterns = getIndexPatterns();

const job = mlJobService.getJob(this.props.anomaly.jobId);
if (job === undefined) {
console.log(`viewExamples(): no job found with ID: ${this.props.anomaly.jobId}`);
toastNotifications.addDanger(
`Unable to view examples as no details could be found for job ID ${this.props.anomaly.jobId}`);
return;
}
const categorizationFieldName = job.analysis_config.categorization_field_name;
const datafeedIndices = job.datafeed_config.indices;
// Find the type of the categorization field i.e. text (preferred) or keyword.
Expand Down Expand Up @@ -330,6 +335,8 @@ export class LinksMenu extends Component {
};

render() {
const { anomaly, showViewSeriesLink } = this.props;

const button = (
<EuiButtonEmpty
size="s"
Expand All @@ -343,8 +350,8 @@ export class LinksMenu extends Component {
);

const items = [];
if (this.props.anomaly.customUrls !== undefined) {
this.props.anomaly.customUrls.forEach((customUrl, index) => {
if (anomaly.customUrls !== undefined) {
anomaly.customUrls.forEach((customUrl, index) => {
items.push(
<EuiContextMenuItem
key={`custom_url_${index}`}
Expand All @@ -357,7 +364,7 @@ export class LinksMenu extends Component {
});
}

if (this.props.showViewSeriesLink === true && this.props.anomaly.isTimeSeriesViewDetector === true) {
if (showViewSeriesLink === true && anomaly.isTimeSeriesViewDetector === true) {
items.push(
<EuiContextMenuItem
key="view_series"
Expand All @@ -369,7 +376,7 @@ export class LinksMenu extends Component {
);
}

if (this.props.anomaly.entityName === 'mlcategory') {
if (anomaly.entityName === 'mlcategory') {
items.push(
<EuiContextMenuItem
key="view_examples"
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/ml/public/explorer/explorer_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -812,14 +812,14 @@ module.controller('MlExplorerController', function (
}
});

$timeout(() => {
$scope.$evalAsync(() => {
$scope.tableData = {
anomalies,
interval: resp.interval,
examplesByJobId: resp.examplesByJobId,
showViewSeriesLink: true
};
}, 0);
});

}).catch((resp) => {
console.log('Explorer - error loading data for anomalies table:', resp);
Expand Down
Loading

0 comments on commit bc94853

Please sign in to comment.