Skip to content

Commit

Permalink
Add push experiment(s) to the command palette (#3793)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattseddon authored May 1, 2023
1 parent 651c2f6 commit cad154d
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 4 deletions.
9 changes: 9 additions & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@
"light": "resources/light/queue-experiment.svg"
}
},
{
"title": "Push Experiment(s)",
"command": "dvc.pushExperiments",
"category": "DVC"
},
{
"title": "Remove Experiment(s)",
"command": "dvc.removeExperiments",
Expand Down Expand Up @@ -761,6 +766,10 @@
"command": "dvc.modifyExperimentParamsResetAndRun",
"when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running && dvc.experiment.checkpoints"
},
{
"command": "dvc.pushExperiments",
"when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running"
},
{
"command": "dvc.queueExperiment",
"when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running"
Expand Down
1 change: 1 addition & 0 deletions extension/src/commands/external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum RegisteredCliCommands {
EXPERIMENT_APPLY = 'dvc.applyExperiment',
EXPERIMENT_BRANCH = 'dvc.branchExperiment',
EXPERIMENT_GARBAGE_COLLECT = 'dvc.experimentGarbageCollect',
EXPERIMENT_PUSH = 'dvc.pushExperiments',
EXPERIMENT_REMOVE = 'dvc.removeExperiments',
EXPERIMENT_REMOVE_QUEUE = 'dvc.removeExperimentQueue',
EXPERIMENT_RESET_AND_RUN = 'dvc.resetAndRunCheckpointExperiment',
Expand Down
10 changes: 8 additions & 2 deletions extension/src/experiments/commands/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ const registerExperimentInputCommands = (

const registerExperimentQuickPickCommands = (
experiments: WorkspaceExperiments,
internalCommands: InternalCommands
internalCommands: InternalCommands,
setup: Setup
): void => {
internalCommands.registerExternalCliCommand(
RegisteredCliCommands.EXPERIMENT_GARBAGE_COLLECT,
Expand Down Expand Up @@ -220,6 +221,11 @@ const registerExperimentQuickPickCommands = (
() => experiments.selectQueueTasksToKill()
)

internalCommands.registerExternalCliCommand(
RegisteredCliCommands.EXPERIMENT_PUSH,
() => experiments.selectExperimentsToPush(setup)
)

internalCommands.registerExternalCliCommand(
RegisteredCliCommands.EXPERIMENT_REMOVE,
() => experiments.selectExperimentsToRemove()
Expand Down Expand Up @@ -280,7 +286,7 @@ export const registerExperimentCommands = (
registerExperimentCwdCommands(experiments, internalCommands)
registerExperimentNameCommands(experiments, internalCommands)
registerExperimentInputCommands(experiments, internalCommands)
registerExperimentQuickPickCommands(experiments, internalCommands)
registerExperimentQuickPickCommands(experiments, internalCommands, setup)
registerExperimentRunCommands(experiments, internalCommands, setup)

internalCommands.registerExternalCommand(
Expand Down
8 changes: 8 additions & 0 deletions extension/src/experiments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,14 @@ export class Experiments extends BaseRepository<TableData> {
)
}

public pickExperimentsToPush() {
return pickExperiments(
this.experiments.getExperiments(),
this.getFirstThreeColumnOrder(),
Title.SELECT_EXPERIMENTS_PUSH
)
}

public async pickAndModifyParams(overrideId?: string) {
const id = await this.getExperimentId(overrideId)
if (!id) {
Expand Down
18 changes: 18 additions & 0 deletions extension/src/experiments/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EventEmitter, Memento } from 'vscode'
import isEmpty from 'lodash.isempty'
import { Experiments, ModifiedExperimentAndRunCommandId } from '.'
import { getPushExperimentCommand } from './commands'
import { TableData } from './webview/contract'
import { Args } from '../cli/dvc/constants'
import {
Expand All @@ -9,6 +10,7 @@ import {
InternalCommands
} from '../commands/internal'
import { ResourceLocator } from '../resourceLocator'
import { Setup } from '../setup'
import { Toast } from '../vscode/toast'
import {
getInput,
Expand Down Expand Up @@ -172,6 +174,22 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews<
return this.runCommand(AvailableCommands.QUEUE_KILL, cwd, ...taskIds)
}

public async selectExperimentsToPush(setup: Setup) {
const dvcRoot = await this.getFocusedOrOnlyOrPickProject()
if (!dvcRoot) {
return
}

const ids = await this.getRepository(dvcRoot).pickExperimentsToPush()
if (!ids || isEmpty(ids)) {
return
}

const pushCommand = getPushExperimentCommand(this.internalCommands, setup)

return pushCommand({ dvcRoot, ids })
}

public async selectExperimentsToRemove() {
const cwd = await this.getFocusedOrOnlyOrPickProject()
if (!cwd) {
Expand Down
1 change: 1 addition & 0 deletions extension/src/telemetry/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export interface IEventNamePropertyMapping {
[EventName.EXPERIMENT_FILTERS_REMOVE_ALL]: undefined
[EventName.EXPERIMENT_GARBAGE_COLLECT]: undefined
[EventName.EXPERIMENT_METRICS_AND_PARAMS_TOGGLE]: undefined
[EventName.EXPERIMENT_PUSH]: undefined
[EventName.EXPERIMENT_REMOVE]: undefined
[EventName.EXPERIMENT_REMOVE_QUEUE]: undefined
[EventName.EXPERIMENT_RESUME]: undefined
Expand Down
109 changes: 108 additions & 1 deletion extension/src/test/suite/experiments/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Experiments } from '../../../experiments'
import * as QuickPick from '../../../vscode/quickPick'
import { DvcExecutor } from '../../../cli/dvc/executor'
import {
bypassProgressCloseDelay,
closeAllEditors,
getInputBoxEvent,
getTimeSafeDisposer,
Expand Down Expand Up @@ -657,6 +658,112 @@ suite('Workspace Experiments Test Suite', () => {
})
})

describe('dvc.pushExperiments', () => {
it('should ask the user to pick experiment(s) and then push selected ones to the remote', async () => {
bypassProgressCloseDelay()
const mockExperimentId = 'exp-e7a67'
const secondMockExperimentId = 'exp-83425'
type QuickPickReturnValue = QuickPickItemWithValue<string>[]
stub(Setup.prototype, 'getStudioAccessToken').returns('isat_token')

const { experiments } = buildExperiments(disposable)

await experiments.isReady()

stubWorkspaceExperimentsGetters(dvcDemoPath, experiments)

const mockShowQuickPick = stub(window, 'showQuickPick') as SinonStub<
[items: readonly QuickPickItem[], options: QuickPickOptionsWithTitle],
Thenable<QuickPickReturnValue | undefined>
>

mockShowQuickPick
.onFirstCall()
.resolves([
{
value: mockExperimentId
}
] as QuickPickReturnValue)
.onSecondCall()
.resolves([
{
value: mockExperimentId
},
{
value: secondMockExperimentId
}
] as QuickPickReturnValue)
const mockExpPush = stub(DvcExecutor.prototype, 'expPush')

await commands.executeCommand(RegisteredCliCommands.EXPERIMENT_PUSH)
expect(mockShowQuickPick).calledWithExactly(
[
{
description: '[exp-e7a67]',
detail: `Created:${formatDate(
'2020-12-29T15:31:52'
)}, loss:2.0205045, accuracy:0.37241668`,
label: '4fb124a',
value: 'exp-e7a67'
},
{
description: '[test-branch]',
detail: `Created:${formatDate(
'2020-12-29T15:28:59'
)}, loss:1.9293040, accuracy:0.46680000`,
label: '42b8736',
value: 'test-branch'
},
{
description: '[exp-83425]',
detail: `Created:${formatDate(
'2020-12-29T15:27:02'
)}, loss:1.7750162, accuracy:0.59265000`,
label: EXPERIMENT_WORKSPACE_ID,
value: 'exp-83425'
},
{
description: undefined,
detail: 'Created:-, loss:-, accuracy:-',
label: '489fd8b',
value: '489fd8b'
},
{
description: '[exp-f13bca]',
detail: `Created:${formatDate(
'2020-12-29T15:26:36'
)}, loss:-, accuracy:-`,
label: 'f0f9186',
value: 'exp-f13bca'
},
{
description: undefined,
detail: `Created:${formatDate(
'2020-12-29T15:25:27'
)}, loss:-, accuracy:-`,
label: '55d492c',
value: '55d492c'
}
],
{
canPickMany: true,
matchOnDescription: true,
matchOnDetail: true,
title: 'Select Experiment(s) to Push'
}
)
expect(mockExpPush).to.be.calledWith(dvcDemoPath, mockExperimentId)

await commands.executeCommand(RegisteredCliCommands.EXPERIMENT_PUSH)

expect(mockExpPush).to.be.calledWith(
dvcDemoPath,
mockExperimentId,
secondMockExperimentId
)
})
})

describe('dvc.removeExperiments', () => {
it('should ask the user to pick experiment(s) and then remove selected ones from the workspace', async () => {
const mockExperimentId = 'exp-e7a67'
Expand Down Expand Up @@ -754,7 +861,7 @@ suite('Workspace Experiments Test Suite', () => {
canPickMany: true,
matchOnDescription: true,
matchOnDetail: true,
title: 'Select Experiments to Remove'
title: 'Select Experiment(s) to Remove'
}
)
expect(mockExperimentRemove).to.be.calledWith(
Expand Down
3 changes: 2 additions & 1 deletion extension/src/vscode/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export enum Title {
SELECT_COLUMNS = 'Select Columns to Display in the Experiments Table',
SELECT_EXPERIMENT = 'Select an Experiment',
SELECT_EXPERIMENTS = 'Select Experiments',
SELECT_EXPERIMENTS_REMOVE = 'Select Experiments to Remove',
SELECT_EXPERIMENTS_PUSH = 'Select Experiment(s) to Push',
SELECT_EXPERIMENTS_REMOVE = 'Select Experiment(s) to Remove',
SELECT_EXPERIMENTS_TO_PLOT = 'Select up to 7 Experiments to Display in Plots',
SELECT_FILTERS_TO_REMOVE = 'Select Filter(s) to Remove',
SELECT_FOCUSED_PROJECTS = 'Select Project(s) to Focus (set dvc.focusedProjects)',
Expand Down

0 comments on commit cad154d

Please sign in to comment.