Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Overview tab for ML #45864

Merged
merged 19 commits into from
Sep 23, 2019

Conversation

alvarezmelissa87
Copy link
Contributor

@alvarezmelissa87 alvarezmelissa87 commented Sep 17, 2019

Summary

Moved and renamed MlInMemoryTable (was AnalyticsTable) to a shared location in public/components. Updated all imports with new name/location.

Adds Overview main tab with Anomaly Detection jobs section and Analytics job section.

This is version one of the overview tab. In place of max anomaly score, the anomaly detection section will have a mini graph/swimlane representing the anomalies for the last 24hrs across jobs for that group.

image

Checklist

Use strikethroughs to remove checklist items you don't feel are applicable to this PR.

For maintainers

- [ ] This was checked for breaking API changes and was labeled appropriately
- [ ] This includes a feature addition or change that requires a release note and was labeled appropriately

@elasticmachine
Copy link
Contributor

Pinging @elastic/ml-ui

@elasticmachine
Copy link
Contributor

💔 Build Failed

@sophiec20
Copy link
Contributor

sophiec20 commented Sep 17, 2019

We rarely show decimal places for anomaly score. Even though it is not the final graphic, probably worth rounding.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@alvarezmelissa87 alvarezmelissa87 changed the title WIP [ML] Overview tab for ML [ML] Overview tab for ML Sep 17, 2019
@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@alvarezmelissa87
Copy link
Contributor Author

retest

@elasticmachine
Copy link
Contributor

💔 Build Failed

Copy link
Contributor

@walterra walterra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good, the only thing I'd consider a must for this PR are some missing translations.

I made some suggestions for improving types but that could be done in a follow up since I assume the use of some anys stems from consuming/reusing code from the anomaly jobs list which isn't TypeScript yet.

@@ -21,31 +21,33 @@ import { stopAnalytics } from '../../services/analytics_service';
import { StartAction } from './action_start';
import { DeleteAction } from './action_delete';

export const ANALYTICS_VIEW_ACTION = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could be analyticsViewAction, it's not an enum or a simple constant.

@@ -58,6 +58,57 @@ export const getTaskStateBadge = (
);
};

export const PROGRESS_COLUMN = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could be progressColumn, it's not an enum or simple constant.

{isInitialized === true && analytics.length === 0 && (
<EuiEmptyPrompt
iconType="createAdvancedJob"
title={<h2>Create your first analytics job</h2>}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be translated.

body={
<Fragment>
<p>
Data frame analytics enable you to perform different analyses of your data and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be translated.

}
actions={
<EuiButton href="#/data_frame_analytics?" color="primary" fill>
Create job
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be translated.

import { Group, Groups, Jobs, Job } from './anomaly_detection_panel';

export function getGroupsFromJobs(jobs: Jobs): Groups {
const groups: any = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be groups: Groups?

max_anomaly_score: number | null;
}

export interface Job {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have MlJob in ml/common/types/jobs.ts could this be used instead here? This would help to get rid of all the anys when working with this type/interface. If we use that, I'd suggest that we remove the Jobs type from this file, using MlJob[] in the consuming code would be more clear IMHO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not quite the type I need since I'm getting jobs from ml.jobsSummary but I went ahead and added a MlSummaryJob and MlSummaryJobs as we'll need those types when we convert all the job management code to typescript.

import { mlResultsService } from '../../../services/results_service';
import { getGroupsFromJobs, getStatsBarData, getJobsWithTimerange } from './utils';

export interface Groups {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could we name this GroupsDictionary and reuse Dictionary from x-pack/legacy/plugins/ml/common/types/common.ts? It's just that looking at the consuming code I assumed Groups was an array (because Jobs was) which was a bit irritating.


export type Jobs = Job[];

interface MaxScoresByGroup {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use Dictionary from x-pack/legacy/plugins/ml/common/types/common.ts

}

// object to keep track of nodes being used by jobs
const mlNodes: any = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at how this is used later on, could this just be an array of job.nodeNames like const mlNodes: string[] = []; or even just a counter like let activeNodes = 0;?

defaultMessage: 'Jobs in group',
}),
render: (jobIds: Group['jobIds']) => jobIds.length,
sortable: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am seeing some odd sort behavior here. It doesn't seem to sort numerically:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to follow-up meta issue 👍

name: i18n.translate('xpack.ml.overview.anomalyDetection.tableActionLabel', {
defaultMessage: 'Actions',
}),
render: (group: Group) => <ExplorerLink jobsList={getJobsFromGroup(group, jobsList)} />,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seen this strange behavior where the count in the table is different to the count in the tooltip on the link to the Anomaly Explorer (16 is the correct number):

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 59c9808

<Fragment>
<AnalyticsTable items={analytics} />
<EuiSpacer size="m" />
<div className="mlOverviewPanel__buttons">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the Refresh and Manage buttons could do with a bit more padding between them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to meta issue 👍

},
{
name: i18n.translate('xpack.ml.overview.anomalyDetection.tableActionLabel', {
defaultMessage: 'Actions',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two Actions columns have different horizontal alignments. Could they both get the same?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to follow-up meta issue

<EuiPanel className="mlOverviewPanel">
{typeof errorMessage !== 'undefined' && errorDisplay}
{isLoading && <EuiLoadingSpinner />}   
{isLoading === false && typeof errorMessage === 'undefined' && groupsCount === 0 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As previously reported, when there are no Anomaly Detection jobs, rather than the expected EuiEmptyPrompt I am seeing:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 59c9808
(ensures removal of ungrouped when there are no ungrouped jobs)

const resultsIndex = scores[groupId] && scores[groupId].index;
const promiseResult: { success: boolean; results: { [key: string]: number } } =
resultsIndex !== undefined && results[resultsIndex];
if (promiseResult.success === true && promiseResult.results !== undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws an error if there are no ungrouped jobs, with promiseResult being undefined:

image

Copy link
Contributor Author

@alvarezmelissa87 alvarezmelissa87 Sep 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, @peteharverson. This is occurring because we're passing the empty jobIds array. I've created a fix to remove ungrouped when there are no ungrouped job ids. This also fixes the issue where we weren't seeing the empty prompt when there were no Anomaly Detection jobs and were seeing the table with the loading anomaly score for ungrouped.

import { ML_BREADCRUMB } from '../breadcrumbs';

export function getOverviewBreadcrumbs() {
// Whilst top level nav menu with tabs remains,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: While*

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whilst is a real word - means while

// TODO: panels can be smaller when empty
export const AnalyticsPanel: FC = () => {
const [analytics, setAnalytics] = useState<DataFrameAnalyticsListRow[]>([]);
const [errorMessage, setErrorMessage] = useState<any>(undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe provide an error interface or just get rid of any?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will add as a follow up in the meta issue 👍 😄

color="danger"
iconType="alert"
>
<pre>{JSON.stringify(errorMessage)}</pre>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also can be a dedicated component for rendering error messages which cover this logic

import { AnalyticsStatsBar } from './analytics_stats_bar';

interface Props {
items: any[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item interface might be helpful

const scores = getDefaultAnomalyScores(groupsList);

try {
const promises: any[] = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: something like

Suggested change
const promises: any[] = [];
const promises = groupsList
.filter(group => group.jobIds.length > 0)
.map((group) => {
return mlResultsService.getOverallBucketScores(
group.jobIds,
1,
group.earliest_timestamp,
group.latest_timestamp
);
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, great suggestion for preventing the anomaly score fetch error with empty jobIds 👌

});

const results = await Promise.all(promises);
const tempGroups = Object.assign({}, { ...groupsObject });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should only Object.assign or just spread

},
};

jobs.forEach((job: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a Job interface at x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_creator/configs/job.ts

},
};

jobs.forEach((job: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably deserves a function

return groups;
}

export function getStatsBarData(jobsList: any) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would by nice to replace any

}

export function getJobsWithTimerange(jobsList: any) {
const jobs: any = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
const jobs: any = {};
const jobs = jobsList
.filter(job => jobs[job.id] === undefined && job.earliestTimestampMs !== undefined)
.reduce((acc, job: Job) => {
const { earliestTimestampMs, latestResultsTimestampMs } = job;
acc[job.id] = {
id: job.id,
earliestTimestampMs,
latestResultsTimestampMs,
};
return acc;
}, {});

Copy link
Contributor

@peteharverson peteharverson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am seeing errors from the Transforms wizard with the tables failing to load.

@@ -33,7 +33,7 @@ import {
MlInMemoryTable,
SortingPropType,
SORT_DIRECTION,
} from '../../../../../../common/types/eui/in_memory_table';
} from '../../../../../components/ml_in_memory_table';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check if this table works in the Transforms wizard. I am getting errors that the pageIndex property is missing from the pagination part of the EuiBasicTable so the tables fail to load, with this error seen in the console.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed with d981ba0

@@ -25,7 +25,7 @@ import {
ColumnType,
MlInMemoryTable,
SORT_DIRECTION,
} from '../../../../../../common/types/eui/in_memory_table';
} from '../../../../../components/ml_in_memory_table';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, this table fails to load for me, due to the missing pageIndex prop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d981ba0

@darnautov
Copy link
Contributor

@elasticmachine merge upstream

@elasticmachine
Copy link
Contributor

💔 Build Failed

<Fragment>
<p>
Data frame analytics enable you to perform different analyses of your data and
annotate it with the results. As part of its output, data frame analytics appends
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the second sentence should be changed to something like this, since otherwise it seems to imply that the source index is altered: "The analytics job stores the annotated data, as well as a copy of the source data, in a new index."

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@@ -66,7 +66,7 @@ interface TabData {
}

const TAB_DATA: Record<TabId, TabData> = {
// overview: { testSubject: 'mlTabOverview', pathId: 'overview' },
overview: { testSubject: 'mlTabOverview', pathId: 'overview' },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test subject naming has slightly changed with #45793. Would be good to have mlMainTab overview here to match the other tab's attributes.

@@ -50,6 +50,10 @@ export function MachineLearningNavigationProvider({
]);
},

async navigateToOverview() {
await this.navigateToArea('mlTabOverview', 'mlPageOverview');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you follow my suggestion from above, also change the tab data subject to mlMainTab overview here.

Copy link
Member

@pheyos pheyos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analytics summary doesn't count the running job (Started: 0):
overview_analytics_state

Copy link
Member

@pheyos pheyos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clicking the Explore action for an anomaly detection group, puts all jobs of that group into the job selection of the anomaly explorer. This could be a large number of jobs, maybe it's worth to think about just adding the group to the job selection?

Copy link
Member

@pheyos pheyos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refresh of the analytics table works as expected, but clicking the Refresh button in the anomaly detection table, removes the whole table for a second and displays it again. Is this intended?

Copy link
Contributor

@lcawl lcawl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Text LGTM

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@peteharverson peteharverson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and LGTM. Happy for remaining enhancements and fixes listed in #46357 to be done in follow-ups.

Copy link
Contributor

@walterra walterra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest changes LGTM

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@alvarezmelissa87 alvarezmelissa87 merged commit dce008b into elastic:master Sep 23, 2019
alvarezmelissa87 added a commit to alvarezmelissa87/kibana that referenced this pull request Sep 23, 2019
* show overview tab with main tabs

* wip:add overview dir with initial components

* convert and move AnalyticsTable and types to MlInMemoryTable components dir

* create analytics table for overview

* add stats bar to analytics panel

* wip: adds anomaly detection table

* adds actions column to anomaly detection table

* add stats bar to anomaly detection panel

* add max anomaly score to table

* update score display.

* add refresh button to panels

* create scss files for styles

* update functional nav tests

* fix functional test failure

* fix anomalyDetection for when there are no jobs

* add translations and update jobList types

* add maxAnomalyScore endpoint

* fix types and update tab testSub

* fix transforms use of inMemoryTable
alvarezmelissa87 added a commit that referenced this pull request Sep 23, 2019
* show overview tab with main tabs

* wip:add overview dir with initial components

* convert and move AnalyticsTable and types to MlInMemoryTable components dir

* create analytics table for overview

* add stats bar to analytics panel

* wip: adds anomaly detection table

* adds actions column to anomaly detection table

* add stats bar to anomaly detection panel

* add max anomaly score to table

* update score display.

* add refresh button to panels

* create scss files for styles

* update functional nav tests

* fix functional test failure

* fix anomalyDetection for when there are no jobs

* add translations and update jobList types

* add maxAnomalyScore endpoint

* fix types and update tab testSub

* fix transforms use of inMemoryTable
@alvarezmelissa87 alvarezmelissa87 deleted the ml-overview-tab branch September 23, 2019 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants