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] Data frame analytics: Adds map view #81666

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions x-pack/plugins/ml/common/constants/data_frame_analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ export const ANALYSIS_CONFIG_TYPE = {
CLASSIFICATION: 'classification',
} as const;
export const DEFAULT_RESULTS_FIELD = 'ml';

export const JOB_MAP_NODE_TYPES = {
ANALYTICS: 'analytics',
TRANSFORM: 'transform',
INDEX: 'index',
INFERENCE_MODEL: 'inferenceModel',
Copy link
Contributor

Choose a reason for hiding this comment

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

'Trained model' is the term we are generally using now, so should probably rename these to be consistent.

} as const;

export type JobMapNodeTypes = typeof JOB_MAP_NODE_TYPES[keyof typeof JOB_MAP_NODE_TYPES];
1 change: 1 addition & 0 deletions x-pack/plugins/ml/common/constants/ml_url_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const ML_PAGES = {
SINGLE_METRIC_VIEWER: 'timeseriesexplorer',
DATA_FRAME_ANALYTICS_JOBS_MANAGE: 'data_frame_analytics',
DATA_FRAME_ANALYTICS_EXPLORATION: 'data_frame_analytics/exploration',
DATA_FRAME_ANALYTICS_MAP: 'data_frame_analytics/map',
/**
* Page: Data Visualizer
*/
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/common/types/ml_url_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export interface DataFrameAnalyticsQueryState {
}

export type DataFrameAnalyticsUrlState = MLPageState<
typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE | typeof ML_PAGES.DATA_FRAME_ANALYTICS_MAP,
DataFrameAnalyticsQueryState | undefined
>;

Expand Down
15 changes: 14 additions & 1 deletion x-pack/plugins/ml/common/util/analytics_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
OutlierAnalysis,
RegressionAnalysis,
} from '../types/data_frame_analytics';
import { ANALYSIS_CONFIG_TYPE } from '../../common/constants/data_frame_analytics';
import { ANALYSIS_CONFIG_TYPE } from '../constants/data_frame_analytics';
import { DataFrameAnalysisConfigType } from '../types/data_frame_analytics';

export const isOutlierAnalysis = (arg: any): arg is OutlierAnalysis => {
if (typeof arg !== 'object' || arg === null) return false;
Expand Down Expand Up @@ -80,3 +81,15 @@ export const getPredictedFieldName = (
}`;
return predictedField;
};

export const getAnalysisType = (
analysis: AnalysisConfig
): DataFrameAnalysisConfigType | 'unknown' => {
const keys = Object.keys(analysis || {});

if (keys.length === 1) {
return keys[0] as DataFrameAnalysisConfigType;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it worth checking the actual key value instead of typecasting?

}

return 'unknown';
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import 'pages/analytics_exploration/components/regression_exploration/index';
@import 'pages/job_map/components/index';
@import 'pages/analytics_management/components/analytics_list/index';
@import 'pages/analytics_management/components/create_analytics_button/index';
@import 'pages/analytics_creation/components/index';
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
getPredictedFieldName,
} from '../../../../common/util/analytics_utils';
import { ANALYSIS_CONFIG_TYPE } from '../../../../common/constants/data_frame_analytics';

export { getAnalysisType } from '../../../../common/util/analytics_utils';
export type IndexPattern = string;

export enum ANALYSIS_ADVANCED_FIELDS {
Expand Down Expand Up @@ -159,18 +161,6 @@ interface LoadEvaluateResult {
error: string | null;
}

export const getAnalysisType = (
analysis: AnalysisConfig
): DataFrameAnalysisConfigType | 'unknown' => {
const keys = Object.keys(analysis);

if (keys.length === 1) {
return keys[0] as DataFrameAnalysisConfigType;
}

return 'unknown';
};

export const getTrainingPercent = (
analysis: AnalysisConfig
):
Expand Down
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.
*/

export { MapButton } from './map_button';
export { useMapAction } from './use_map_action';
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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, { FC } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiToolTip } from '@elastic/eui';

import {
isRegressionAnalysis,
isOutlierAnalysis,
isClassificationAnalysis,
} from '../../../../common/analytics';

import { DataFrameAnalyticsListRow } from '../analytics_list/common';

export const mapActionButtonText = i18n.translate(
'xpack.ml.dataframe.analyticsList.mapActionName',
{
defaultMessage: 'Map',
}
);
interface MapButtonProps {
item: DataFrameAnalyticsListRow;
}

export const MapButton: FC<MapButtonProps> = ({ item }) => {
const disabled =
!isRegressionAnalysis(item.config.analysis) &&
!isOutlierAnalysis(item.config.analysis) &&
!isClassificationAnalysis(item.config.analysis);

if (disabled) {
const toolTipContent = i18n.translate(
'xpack.ml.dataframe.analyticsList.mapActionDisabledTooltipContent',
{
defaultMessage: 'Unknown analysis type.',
}
);

return (
<EuiToolTip position="top" content={toolTipContent}>
<>{mapActionButtonText}</>
darnautov marked this conversation as resolved.
Show resolved Hide resolved
</EuiToolTip>
);
}

return <>{mapActionButtonText}</>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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, { useCallback, useMemo } from 'react';
import { useMlUrlGenerator, useNavigateToPath } from '../../../../../contexts/kibana';
import { DataFrameAnalyticsListAction, DataFrameAnalyticsListRow } from '../analytics_list/common';
import { ML_PAGES } from '../../../../../../../common/constants/ml_url_generator';
import { getViewLinkStatus } from '../action_view/get_view_link_status';

import { mapActionButtonText, MapButton } from './map_button';

export type MapAction = ReturnType<typeof useMapAction>;
export const useMapAction = () => {
const mlUrlGenerator = useMlUrlGenerator();
const navigateToPath = useNavigateToPath();

const clickHandler = useCallback(async (item: DataFrameAnalyticsListRow) => {
const path = await mlUrlGenerator.createUrl({
page: ML_PAGES.DATA_FRAME_ANALYTICS_MAP,
pageState: { jobId: item.id },
});

await navigateToPath(path, false);
}, []);

const action: DataFrameAnalyticsListAction = useMemo(
() => ({
isPrimary: true,
name: (item: DataFrameAnalyticsListRow) => <MapButton item={item} />,
enabled: (item: DataFrameAnalyticsListRow) => !getViewLinkStatus(item).disabled,
description: mapActionButtonText,
icon: 'graphApp',
type: 'icon',
onClick: clickHandler,
'data-test-subj': 'mlAnalyticsJobMapButton',
}),
[clickHandler]
);

return { action };
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { isEditActionFlyoutVisible, useEditAction, EditActionFlyout } from '../a
import { useStartAction, StartActionModal } from '../action_start';
import { useStopAction, StopActionModal } from '../action_stop';
import { useViewAction } from '../action_view';
import { useMapAction } from '../action_map';

import { DataFrameAnalyticsListRow } from './common';

Expand All @@ -30,6 +31,7 @@ export const useActions = (
const canStartStopDataFrameAnalytics: boolean = checkPermission('canStartStopDataFrameAnalytics');

const viewAction = useViewAction();
const mapAction = useMapAction();
const cloneAction = useCloneAction(canCreateDataFrameAnalytics);
const deleteAction = useDeleteAction(canDeleteDataFrameAnalytics);
const editAction = useEditAction(canStartStopDataFrameAnalytics);
Expand All @@ -40,6 +42,7 @@ export const useActions = (

const actions: EuiTableActionsColumnType<DataFrameAnalyticsListRow>['actions'] = [
viewAction.action,
mapAction.action,
];

// isManagementTable will be the same for the lifecycle of the component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ interface Tab {
path: string;
}

export const AnalyticsNavigationBar: FC<{ selectedTabId?: string }> = ({ selectedTabId }) => {
export const AnalyticsNavigationBar: FC<{ selectedTabId?: string; jobId?: string }> = ({
jobId,
selectedTabId,
}) => {
const navigateToPath = useNavigateToPath();

const tabs = useMemo(
() => [
const tabs = useMemo(() => {
const navTabs = [
{
id: 'data_frame_analytics',
name: i18n.translate('xpack.ml.dataframe.jobsTabLabel', {
Expand All @@ -34,12 +37,21 @@ export const AnalyticsNavigationBar: FC<{ selectedTabId?: string }> = ({ selecte
}),
path: '/data_frame_analytics/models',
},
],
[]
);
];
if (jobId !== undefined) {
navTabs.push({
id: 'map',
name: i18n.translate('xpack.ml.dataframe.mapTabLabel', {
defaultMessage: 'Map',
}),
path: '/data_frame_analytics/map',
});
}
return navTabs;
}, [jobId !== undefined]);

const onTabClick = useCallback(async (tab: Tab) => {
await navigateToPath(tab.path);
await navigateToPath(tab.path, true);
}, []);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@ import { StatsBar, ModelsBarStats } from '../../../../../components/stats_bar';
import { useTrainedModelsApiService } from '../../../../../services/ml_api_service/trained_models';
import { ModelsTableToConfigMapping } from './index';
import { DeleteModelsModal } from './delete_models_modal';
import { useMlKibana, useMlUrlGenerator, useNotifications } from '../../../../../contexts/kibana';
import {
useMlKibana,
useMlUrlGenerator,
useNavigateToPath,
useNotifications,
} from '../../../../../contexts/kibana';
import { ExpandedRow } from './expanded_row';

import {
TrainedModelConfigResponse,
ModelPipelines,
Expand Down Expand Up @@ -80,6 +86,9 @@ export const ModelsList: FC = () => {
{}
);

const mlUrlGenerator = useMlUrlGenerator();
const navigateToPath = useNavigateToPath();

const updateFilteredItems = (queryClauses: any) => {
if (queryClauses.length) {
const filtered = filterAnalyticsModels(items, queryClauses);
Expand Down Expand Up @@ -298,6 +307,26 @@ export const ModelsList: FC = () => {
},
isPrimary: true,
},
{
name: i18n.translate('xpack.ml.inference.modelsList.analyticsMapActionLabel', {
defaultMessage: 'Analytics map',
}),
description: i18n.translate('xpack.ml.inference.modelsList.analyticsMapActionLabel', {
defaultMessage: 'Analytics map',
}),
icon: 'graphApp',
type: 'icon',
isPrimary: true,
available: (item) => item.metadata?.analytics_config?.id,
onClick: async (item) => {
const path = await mlUrlGenerator.createUrl({
page: ML_PAGES.DATA_FRAME_ANALYTICS_MAP,
pageState: { jobId: item.metadata?.analytics_config.id },
});

await navigateToPath(path, false);
},
},
{
name: i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', {
defaultMessage: 'Delete model',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from '@elastic/eui';

import { useLocation } from 'react-router-dom';
import { useUrlState } from '../../../util/url_state';
import { NavigationMenu } from '../../../components/navigation_menu';
import { DatePickerWrapper } from '../../../components/navigation_menu/date_picker_wrapper';
import { DataFrameAnalyticsList } from './components/analytics_list';
Expand All @@ -31,14 +32,17 @@ import { NodeAvailableWarning } from '../../../components/node_available_warning
import { UpgradeWarning } from '../../../components/upgrade';
import { AnalyticsNavigationBar } from './components/analytics_navigation_bar';
import { ModelsList } from './components/models_management';
import { JobMap } from '../job_map';

export const Page: FC = () => {
const [blockRefresh, setBlockRefresh] = useState(false);
const [globalState] = useUrlState('_g');

useRefreshInterval(setBlockRefresh);

const location = useLocation();
const selectedTabId = useMemo(() => location.pathname.split('/').pop(), [location]);
const mapJobId = globalState?.ml?.jobId;

return (
<Fragment>
Expand Down Expand Up @@ -73,9 +77,11 @@ export const Page: FC = () => {
</EuiPageHeaderSection>
<EuiPageHeaderSection>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<RefreshAnalyticsListButton />
</EuiFlexItem>
{selectedTabId !== 'map' && (
<EuiFlexItem grow={false}>
<RefreshAnalyticsListButton />
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<DatePickerWrapper />
</EuiFlexItem>
Expand All @@ -87,8 +93,8 @@ export const Page: FC = () => {
<UpgradeWarning />

<EuiPageContent>
<AnalyticsNavigationBar selectedTabId={selectedTabId} />

<AnalyticsNavigationBar selectedTabId={selectedTabId} jobId={mapJobId} />
{selectedTabId === 'map' && mapJobId && <JobMap analyticsId={mapJobId} />}
{selectedTabId === 'data_frame_analytics' && (
<DataFrameAnalyticsList blockRefresh={blockRefresh} />
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import 'legend';
Loading