Skip to content
代码片段 群组 项目
未验证 提交 6bfba418 编辑于 作者: Alper Akgun's avatar Alper Akgun 提交者: GitLab
浏览文件

Merge branch '508887-model-experiments-add-button-to-mlflow-usage-example' into 'master'

Model Experiments: Add menu item to MLflow usage example

See merge request https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175813



Merged-by: default avatarAlper Akgun <aakgun@gitlab.com>
Approved-by: default avatarAndras Herczeg <aherczeg@gitlab.com>
Approved-by: default avatarVitaly Slobodin <vslobodin@gitlab.com>
Approved-by: default avatarEduardo Bonet <ebonet@gitlab.com>
Reviewed-by: default avatarAlper Akgun <aakgun@gitlab.com>
No related branches found
No related tags found
无相关合并请求
显示
140 个添加32 个删除
<script> <script>
import { GlBadge } from '@gitlab/ui'; import {
import { __ } from '~/locale'; GlBadge,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
GlModalDirective,
} from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import PageHeading from '~/vue_shared/components/page_heading.vue'; import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { MLFLOW_USAGE_MODAL_ID } from '../routes/experiments/index/constants';
import MlflowModal from '../routes/experiments/index/components/mlflow_usage_modal.vue';
export default { export default {
components: { components: {
GlBadge, GlBadge,
PageHeading, GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
MlflowModal,
TitleArea,
},
directives: {
GlModal: GlModalDirective,
}, },
props: { props: {
pageTitle: { pageTitle: {
type: String, type: String,
required: true, required: true,
}, },
hideMlflowUsage: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
mlflowUsageModalItem() {
return {
text: this.$options.i18n.importMlflow,
};
},
}, },
i18n: { i18n: {
experimentBadgeLabel: __('Experiment'), experimentBadgeLabel: __('Experiment'),
createTitle: s__('MlModelRegistry|Create'),
importMlflow: s__('MlModelRegistry|Create experiments using MLflow'),
}, },
experimentDocHref: helpPagePath('user/project/ml/experiment_tracking/index.md'), experimentDocHref: helpPagePath('user/project/ml/experiment_tracking/index.md'),
mlflowModalId: MLFLOW_USAGE_MODAL_ID,
}; };
</script> </script>
<template> <template>
<page-heading> <title-area>
<template #heading> <template #title>
<span class="gl-inline-flex gl-items-center gl-gap-3"> <div class="gl-flex gl-grow gl-items-center">
{{ pageTitle }} <span class="gl-inline-flex gl-items-center gl-gap-3" data-testid="page-heading">
<gl-badge variant="info" :href="$options.experimentDocHref"> {{ pageTitle }}
{{ $options.i18n.experimentBadgeLabel }} <gl-badge variant="info" :href="$options.experimentDocHref">
</gl-badge> {{ $options.i18n.experimentBadgeLabel }}
<slot></slot> </gl-badge>
</span> <slot></slot>
</span>
</div>
</template>
<template #right-actions>
<gl-disclosure-dropdown
v-if="!hideMlflowUsage"
:toggle-text="$options.i18n.createTitle"
toggle-class="gl-w-full"
data-testid="create-dropdown"
variant="confirm"
category="primary"
placement="bottom-end"
>
<gl-disclosure-dropdown-group>
<gl-disclosure-dropdown-item
v-gl-modal="$options.mlflowModalId"
data-testid="create-menu-item"
:item="mlflowUsageModalItem"
/>
</gl-disclosure-dropdown-group>
<mlflow-modal />
</gl-disclosure-dropdown>
</template> </template>
</page-heading> </title-area>
</template> </template>
...@@ -35,7 +35,7 @@ export default { ...@@ -35,7 +35,7 @@ export default {
<template> <template>
<div> <div>
<model-experiments-header :page-title="$options.i18n.TITLE_LABEL"> <model-experiments-header :page-title="$options.i18n.TITLE_LABEL" hide-mlflow-usage>
<delete-button <delete-button
:delete-path="info.path" :delete-path="info.path"
:delete-confirmation-text="$options.i18n.DELETE_CANDIDATE_CONFIRMATION_MESSAGE" :delete-confirmation-text="$options.i18n.DELETE_CANDIDATE_CONFIRMATION_MESSAGE"
......
...@@ -6,7 +6,6 @@ import * as translations from '~/ml/experiment_tracking/routes/experiments/index ...@@ -6,7 +6,6 @@ import * as translations from '~/ml/experiment_tracking/routes/experiments/index
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue'; import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import Pagination from '~/ml/experiment_tracking/components/pagination.vue'; import Pagination from '~/ml/experiment_tracking/components/pagination.vue';
import { MLFLOW_USAGE_MODAL_ID } from '../constants'; import { MLFLOW_USAGE_MODAL_ID } from '../constants';
import MlflowModal from './mlflow_usage_modal.vue';
export default { export default {
name: 'MlExperimentsIndexApp', name: 'MlExperimentsIndexApp',
...@@ -17,7 +16,6 @@ export default { ...@@ -17,7 +16,6 @@ export default {
GlEmptyState, GlEmptyState,
GlLink, GlLink,
GlButton, GlButton,
MlflowModal,
}, },
directives: { directives: {
GlModal: GlModalDirective, GlModal: GlModalDirective,
...@@ -93,12 +91,14 @@ export default { ...@@ -93,12 +91,14 @@ export default {
class="gl-py-8" class="gl-py-8"
> >
<template #actions> <template #actions>
<gl-button v-gl-modal="$options.mlflowModalId" class="gl-mx-2 gl-mb-3 gl-mr-3"> <gl-button
v-gl-modal="$options.mlflowModalId"
data-testid="empty-create-using-button"
class="gl-mx-2 gl-mb-3 gl-mr-3"
>
{{ $options.i18n.CREATE_USING_MLFLOW_LABEL }} {{ $options.i18n.CREATE_USING_MLFLOW_LABEL }}
</gl-button> </gl-button>
</template> </template>
</gl-empty-state> </gl-empty-state>
<mlflow-modal />
</div> </div>
</template> </template>
...@@ -31,6 +31,11 @@ export default { ...@@ -31,6 +31,11 @@ export default {
DeleteButton, DeleteButton,
PerformanceGraph, PerformanceGraph,
}, },
provide() {
return {
mlflowTrackingUrl: this.mlflowTrackingUrl,
};
},
props: { props: {
experiment: { experiment: {
type: Object, type: Object,
...@@ -56,6 +61,11 @@ export default { ...@@ -56,6 +61,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
mlflowTrackingUrl: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
const query = queryToObject(window.location.search); const query = queryToObject(window.location.search);
......
...@@ -8,7 +8,15 @@ const initShowExperiment = () => { ...@@ -8,7 +8,15 @@ const initShowExperiment = () => {
return undefined; return undefined;
} }
const { experiment, candidates, metrics, params, pageInfo, emptyStateSvgPath } = element.dataset; const {
experiment,
candidates,
metrics,
params,
pageInfo,
emptyStateSvgPath,
mlflowTrackingUrl,
} = element.dataset;
const props = { const props = {
experiment: JSON.parse(experiment), experiment: JSON.parse(experiment),
...@@ -17,6 +25,7 @@ const initShowExperiment = () => { ...@@ -17,6 +25,7 @@ const initShowExperiment = () => {
paramNames: JSON.parse(params), paramNames: JSON.parse(params),
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)), pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
emptyStateSvgPath, emptyStateSvgPath,
mlflowTrackingUrl,
}; };
return new Vue({ return new Vue({
......
...@@ -16,4 +16,5 @@ ...@@ -16,4 +16,5 @@
params: params, params: params,
page_info: page_info, page_info: page_info,
empty_state_svg_path: image_path('illustrations/status/status-new-md.svg'), empty_state_svg_path: image_path('illustrations/status/status-new-md.svg'),
mlflow_tracking_url: mlflow_tracking_url(@project),
} } } }
...@@ -35352,6 +35352,9 @@ msgstr "" ...@@ -35352,6 +35352,9 @@ msgstr ""
msgid "MlModelRegistry|Create & import" msgid "MlModelRegistry|Create & import"
msgstr "" msgstr ""
   
msgid "MlModelRegistry|Create experiments using MLflow"
msgstr ""
msgid "MlModelRegistry|Create model" msgid "MlModelRegistry|Create model"
msgstr "" msgstr ""
   
import { GlBadge } from '@gitlab/ui'; import { GlBadge } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue'; import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import PageHeading from '~/vue_shared/components/page_heading.vue'; import TitleArea from '~/vue_shared/components/registry/title_area.vue';
describe('ml/experiment_tracking/components/model_experiments_header.vue', () => { describe('ml/experiment_tracking/components/model_experiments_header.vue', () => {
let wrapper; let wrapper;
const createWrapper = () => { const createWrapper = ({ propsData = {} } = {}) => {
wrapper = shallowMountExtended(ModelExperimentsHeader, { wrapper = shallowMountExtended(ModelExperimentsHeader, {
propsData: { pageTitle: 'Some Title' }, propsData: { pageTitle: 'Some Title', ...propsData },
slots: { slots: {
default: 'Slot content', default: 'Slot content',
}, },
stubs: {
PageHeading,
},
}); });
}; };
...@@ -22,11 +19,39 @@ describe('ml/experiment_tracking/components/model_experiments_header.vue', () => ...@@ -22,11 +19,39 @@ describe('ml/experiment_tracking/components/model_experiments_header.vue', () =>
const findBadge = () => wrapper.findComponent(GlBadge); const findBadge = () => wrapper.findComponent(GlBadge);
const findTitle = () => wrapper.findByTestId('page-heading'); const findTitle = () => wrapper.findByTestId('page-heading');
const findTitleArea = () => wrapper.findComponent(TitleArea);
const findDropdown = () => wrapper.findByTestId('create-dropdown');
const findMenuItem = () => wrapper.findByTestId('create-menu-item');
it('title area exists', () => {
expect(findTitleArea().exists()).toBe(true);
});
it('renders title', () => { it('title is set', () => {
expect(findTitle().text()).toContain('Some Title'); expect(findTitle().text()).toContain('Some Title');
}); });
it('dropdown exists', () => {
expect(findDropdown().props()).toMatchObject({
toggleText: 'Create',
variant: 'confirm',
category: 'primary',
});
});
it('dropdown is hidden when hideMlflowUsage is true', () => {
createWrapper({ propsData: { hideMlflowUsage: true } });
expect(findDropdown().exists()).toBe(false);
});
it('a menu item for creating experiments exist', () => {
expect(findMenuItem().props()).toMatchObject({
item: {
text: 'Create experiments using MLflow',
},
});
});
it('link points to documentation', () => { it('link points to documentation', () => {
expect(findBadge().attributes().href).toBe( expect(findBadge().attributes().href).toBe(
'/help/user/project/ml/experiment_tracking/index.md', '/help/user/project/ml/experiment_tracking/index.md',
......
import { GlEmptyState, GlLink, GlTableLite, GlButton } from '@gitlab/ui'; import { GlEmptyState, GlLink, GlTableLite } from '@gitlab/ui';
import MlExperimentsIndexApp from '~/ml/experiment_tracking/routes/experiments/index'; import MlExperimentsIndexApp from '~/ml/experiment_tracking/routes/experiments/index';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue'; import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
...@@ -33,8 +33,7 @@ const findColumnInRow = (row, col) => findNthTableRow(row).findAll('td').at(col) ...@@ -33,8 +33,7 @@ const findColumnInRow = (row, col) => findNthTableRow(row).findAll('td').at(col)
const hrefInRowAndColumn = (row, col) => const hrefInRowAndColumn = (row, col) =>
findColumnInRow(row, col).findComponent(GlLink).attributes().href; findColumnInRow(row, col).findComponent(GlLink).attributes().href;
const findTitleHeader = () => wrapper.findComponent(ModelExperimentsHeader); const findTitleHeader = () => wrapper.findComponent(ModelExperimentsHeader);
const findDocsButton = () => wrapper.findByTestId('empty-create-using-button');
const findDocsButton = () => wrapper.findAllComponents(GlButton).at(0);
describe('MlExperimentsIndex', () => { describe('MlExperimentsIndex', () => {
describe('empty state', () => { describe('empty state', () => {
......
...@@ -26,10 +26,19 @@ describe('MlExperimentsShow', () => { ...@@ -26,10 +26,19 @@ describe('MlExperimentsShow', () => {
pageInfo = MOCK_PAGE_INFO, pageInfo = MOCK_PAGE_INFO,
experiment = MOCK_EXPERIMENT, experiment = MOCK_EXPERIMENT,
emptyStateSvgPath = 'path', emptyStateSvgPath = 'path',
mlflowTrackingUrl = 'mlflow/tracking/url',
// eslint-disable-next-line max-params // eslint-disable-next-line max-params
) => { ) => {
wrapper = mount(MlExperimentsShow, { wrapper = mount(MlExperimentsShow, {
propsData: { experiment, candidates, metricNames, paramNames, pageInfo, emptyStateSvgPath }, propsData: {
experiment,
candidates,
metricNames,
paramNames,
pageInfo,
emptyStateSvgPath,
mlflowTrackingUrl,
},
}); });
}; };
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册