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] WIP DF Analytics Map #76952

Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@import 'pages/analytics_exploration/components/regression_exploration/index';
// @import 'pages/analytics_exploration/components/classification_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 @@ -148,7 +148,7 @@ interface LoadEvaluateResult {
}

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

if (keys.length === 1) {
return keys[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { ANALYSIS_CONFIG_TYPE } from '../../common/analytics';
export const Page: FC<{
jobId: string;
analysisType: ANALYSIS_CONFIG_TYPE;
}> = ({ jobId, analysisType }) => (
}> = ({ jobId, analysisType, isMap }) => (
<Fragment>
<NavigationMenu tabId="data_frame_analytics" />
<EuiPage data-test-subj="mlPageDataFrameAnalyticsExploration">
Expand Down Expand Up @@ -65,13 +65,13 @@ export const Page: FC<{
</EuiPageContentHeader>
<EuiPageContentBody style={{ maxWidth: 'calc(100% - 0px)' }}>
<EuiSpacer size="l" />
{analysisType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && (
{!isMap && analysisType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && (
<OutlierExploration jobId={jobId} />
)}
{analysisType === ANALYSIS_CONFIG_TYPE.REGRESSION && (
{!isMap && analysisType === ANALYSIS_CONFIG_TYPE.REGRESSION && (
<RegressionExploration jobId={jobId} />
)}
{analysisType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION && (
{!isMap && analysisType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION && (
<ClassificationExploration jobId={jobId} />
)}
</EuiPageContentBody>
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,50 @@
/*
* 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 toolTipContent = i18n.translate(
'xpack.ml.dataframe.analyticsList.mapActionDisabledTooltipContent',
{
defaultMessage: 'Unknown analysis type.',
}
);
const disabled =
!isRegressionAnalysis(item.config.analysis) &&
!isOutlierAnalysis(item.config.analysis) &&
!isClassificationAnalysis(item.config.analysis);

if (disabled) {
return (
<EuiToolTip position="top" content={toolTipContent}>
<>{mapActionButtonText}</>
</EuiToolTip>
);
}

return <>{mapActionButtonText}</>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 { useNavigateToPath } from '../../../../../contexts/kibana';

import {
getJobMapUrl,
DataFrameAnalyticsListAction,
DataFrameAnalyticsListRow,
} from '../analytics_list/common';

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

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

const clickHandler = useCallback((item: DataFrameAnalyticsListRow) => {
navigateToPath(getJobMapUrl(item.id));
}, []);

const action: DataFrameAnalyticsListAction = useMemo(
() => ({
isPrimary: true,
name: (item: DataFrameAnalyticsListRow) => <MapButton item={item} />,
enabled: () => true,
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 @@ -141,6 +141,10 @@ export function getResultsUrl(jobId: string, analysisType: ANALYSIS_CONFIG_TYPE
return `#/data_frame_analytics/exploration?_g=(ml:(jobId:${jobId},analysisType:${analysisType}))`;
}

export function getJobMapUrl(jobId: string) {
return `#/data_frame_analytics/map?_g=(ml:(jobId:${jobId}))`;
}

// The single Action type is not exported as is
// from EUI so we use that code to get the single
// Action type from the array of actions.
Expand Down
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<{ jobId?: string; selectedTabId?: 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,9 +37,18 @@ export const AnalyticsNavigationBar: FC<{ selectedTabId?: string }> = ({ selecte
}),
path: '/data_frame_analytics/models',
},
],
[]
);
];
if (selectedTabId === 'map') {
navTabs.push({
id: 'map',
name: i18n.translate('xpack.ml.dataframe.mapTabLabel', {
defaultMessage: 'Map',
}),
path: '/data_frame_analytics/map',
});
}
return navTabs;
}, [selectedTabId === 'map']);

const onTabClick = useCallback(async (tab: Tab) => {
await navigateToPath(tab.path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { TIME_FORMAT } from '../../../../../../../common/constants/time_format';
import { DeleteModelsModal } from './delete_models_modal';
import { useMlKibana, useNotifications } from '../../../../../contexts/kibana';
import { ExpandedRow } from './expanded_row';
import { getResultsUrl } from '../analytics_list/common';
import { getResultsUrl, getJobMapUrl } from '../analytics_list/common';
import {
ModelConfigResponse,
ModelPipelines,
Expand Down Expand Up @@ -287,6 +287,21 @@ 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) => {
await navigateToUrl(getJobMapUrl(item.metadata?.analytics_config.id));
},
},
{
name: i18n.translate('xpack.ml.inference.modelsList.deleteModelActionLabel', {
defaultMessage: 'Delete model',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ 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 = () => {
export const Page: FC<{
jobId?: string;
}> = ({ jobId }) => {
const [blockRefresh, setBlockRefresh] = useState(false);

useRefreshInterval(setBlockRefresh);
Expand Down Expand Up @@ -87,8 +90,8 @@ export const Page: FC = () => {
<UpgradeWarning />

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

<AnalyticsNavigationBar selectedTabId={selectedTabId} jobId={jobId} />
{selectedTabId === 'map' && jobId && <JobMap analyticsId={jobId} />}
{selectedTabId === 'data_frame_analytics' && (
<DataFrameAnalyticsList blockRefresh={blockRefresh} />
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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 enum JOB_MAP_NODE_TYPES {
ANALYTICS = 'analytics',
TRANSFORM = 'transform',
INDEX_PATTERN = 'index-pattern',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import 'legend';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.mlJobMapLegend__container {
background-color: $euiColorLightestShade;
}

.mlJobMapLegend__indexPattern {
height: $euiSizeM;
width: $euiSizeM;
background-color: $euiColorVis2;
transform: rotate(45deg);
display: 'inline-block';
}

.mlJobMapLegend__transform {
height: $euiSizeM;
width: $euiSizeM;
background-color: $euiColorVis1;
display: 'inline-block';
}

.mlJobMapLegend__analytics {
height: $euiSizeM;
width: $euiSizeM;
background-color: $euiColorVis0;
border-radius: 50%;
display: 'inline-block';
}
Loading