Skip to content

Commit 771652c

Browse files
authored
Watch metric files to update plots (#3321)
* collect and watch metrics files for plots updates * test change * remove original patch * unit test collect function * self review
1 parent 379566e commit 771652c

File tree

5 files changed

+98
-28
lines changed

5 files changed

+98
-28
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { collectMetricsFiles } from './collect'
2+
import expShowFixture from '../../test/fixtures/expShow/base/output'
3+
import { ExperimentsOutput } from '../../cli/dvc/contract'
4+
5+
describe('collectMetricsFiles', () => {
6+
it('should return the expected metrics files from the test fixture', () => {
7+
expect(collectMetricsFiles(expShowFixture, [])).toStrictEqual([
8+
'summary.json'
9+
])
10+
})
11+
12+
it('should persist existing files', () => {
13+
const existingFiles = ['file.json', 'file.py', 'file.tsv', 'file.txt']
14+
15+
expect(
16+
collectMetricsFiles({ workspace: { baseline: {} } }, existingFiles)
17+
).toStrictEqual(existingFiles)
18+
})
19+
20+
it('should not fail when given an error', () => {
21+
const existingFile = ['metrics.json']
22+
23+
expect(
24+
collectMetricsFiles(
25+
{
26+
workspace: {
27+
baseline: { error: { msg: 'I broke', type: 'not important' } }
28+
}
29+
},
30+
existingFile
31+
)
32+
).toStrictEqual(existingFile)
33+
})
34+
35+
it('should not fail when given empty output', () => {
36+
const existingFiles: string[] = []
37+
38+
expect(
39+
collectMetricsFiles({} as ExperimentsOutput, existingFiles)
40+
).toStrictEqual(existingFiles)
41+
})
42+
})

extension/src/plots/data/collect.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PlotsOutputOrError } from '../../cli/dvc/contract'
1+
import { ExperimentsOutput, PlotsOutputOrError } from '../../cli/dvc/contract'
22
import { isDvcError } from '../../cli/dvc/reader'
33
import { uniqueValues } from '../../util/array'
44
import { isImagePlot, Plot, TemplatePlot } from '../webview/contract'
@@ -56,3 +56,14 @@ export const collectFiles = (
5656

5757
return uniqueValues(acc)
5858
}
59+
60+
export const collectMetricsFiles = (
61+
data: ExperimentsOutput,
62+
existingFiles: string[]
63+
): string[] =>
64+
uniqueValues([
65+
...Object.keys({
66+
...data?.workspace?.baseline?.data?.metrics
67+
}).filter(Boolean),
68+
...existingFiles
69+
]).sort()

extension/src/plots/data/index.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { EventEmitter } from 'vscode'
2-
import { collectFiles } from './collect'
2+
import { collectMetricsFiles, collectFiles } from './collect'
33
import {
44
EXPERIMENT_WORKSPACE_ID,
5+
ExperimentsOutput,
56
PlotsOutputOrError
67
} from '../../cli/dvc/contract'
78
import { AvailableCommands, InternalCommands } from '../../commands/internal'
89
import { BaseData } from '../../data'
9-
import { flattenUnique, sameContents } from '../../util/array'
10+
import { flattenUnique, sameContents, uniqueValues } from '../../util/array'
1011
import { PlotsModel } from '../model'
1112

1213
export class PlotsData extends BaseData<{
@@ -15,6 +16,8 @@ export class PlotsData extends BaseData<{
1516
}> {
1617
private readonly model: PlotsModel
1718

19+
private metricFiles: string[] = []
20+
1821
constructor(
1922
dvcRoot: string,
2023
internalCommands: InternalCommands,
@@ -31,7 +34,7 @@ export class PlotsData extends BaseData<{
3134
process: () => this.update()
3235
}
3336
],
34-
['dvc.yaml', 'dvc.lock', 'metrics.json']
37+
['dvc.yaml', 'dvc.lock']
3538
)
3639
this.model = model
3740
}
@@ -62,6 +65,17 @@ export class PlotsData extends BaseData<{
6265
return this.processManager.run('update')
6366
}
6467

68+
public setMetricFiles(data: ExperimentsOutput) {
69+
const metricsFiles = collectMetricsFiles(data, this.metricFiles)
70+
if (!sameContents(metricsFiles, this.metricFiles)) {
71+
this.metricFiles = metricsFiles
72+
this.collectedFiles = uniqueValues([
73+
...this.collectedFiles,
74+
...metricsFiles
75+
])
76+
}
77+
}
78+
6579
protected collectFiles({ data }: { data: PlotsOutputOrError }) {
6680
this.collectedFiles = collectFiles(data, this.collectedFiles)
6781
}

extension/src/plots/index.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export class Plots extends BaseRepository<TPlotsData> {
2828

2929
private readonly pathsChanged = this.dispose.track(new EventEmitter<void>())
3030

31-
private readonly experiments: Experiments
3231
private readonly plots: PlotsModel
3332
private readonly paths: PathsModel
3433
private readonly data: PlotsData
@@ -45,8 +44,6 @@ export class Plots extends BaseRepository<TPlotsData> {
4544
) {
4645
super(dvcRoot, webviewIcon)
4746

48-
this.experiments = experiments
49-
5047
this.plots = this.dispose.track(
5148
new PlotsModel(this.dvcRoot, experiments, workspaceState)
5249
)
@@ -57,7 +54,7 @@ export class Plots extends BaseRepository<TPlotsData> {
5754
this.webviewMessages = this.createWebviewMessageHandler(
5855
this.paths,
5956
this.plots,
60-
this.experiments
57+
experiments
6158
)
6259

6360
this.data = this.dispose.track(
@@ -174,6 +171,7 @@ export class Plots extends BaseRepository<TPlotsData> {
174171
if (data) {
175172
this.dispose.untrack(waitForInitialExpData)
176173
waitForInitialExpData.dispose()
174+
this.data.setMetricFiles(data)
177175
this.setupExperimentsListener(experiments)
178176
void this.initializeData(data)
179177
}
@@ -185,7 +183,10 @@ export class Plots extends BaseRepository<TPlotsData> {
185183
this.dispose.track(
186184
experiments.onDidChangeExperiments(async data => {
187185
if (data) {
188-
await this.plots.transformAndSetExperiments(data)
186+
await Promise.all([
187+
this.plots.transformAndSetExperiments(data),
188+
this.data.setMetricFiles(data)
189+
])
189190
}
190191

191192
this.notifyChanged()

extension/src/test/suite/plots/data/index.test.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,14 @@ suite('Plots Data Test Suite', () => {
134134
it('should collect files and watch them for updates', async () => {
135135
const mockNow = getMockNow()
136136
const parentDirectory = 'training'
137-
const watchedFile = join(parentDirectory, 'metrics.json')
137+
const metricsFile = join(parentDirectory, 'metrics.json')
138+
const collectedFile = join(
139+
'training',
140+
'plots',
141+
'metrics',
142+
'train',
143+
'acc.tsv'
144+
)
138145

139146
const mockExecuteCommand = (command: CommandId) => {
140147
if (command === AvailableCommands.PLOTS_DIFF) {
@@ -146,28 +153,12 @@ suite('Plots Data Test Suite', () => {
146153
{
147154
dvc_data_version_info: {
148155
field: join('train', 'acc'),
149-
filename: join(
150-
'training',
151-
'plots',
152-
'metrics',
153-
'train',
154-
'acc.tsv'
155-
),
156+
filename: collectedFile,
156157
revision: EXPERIMENT_WORKSPACE_ID
157158
},
158159
dvc_inferred_y_value: '0.2707333333333333',
159160
step: '0',
160161
[join('train', 'acc')]: '0.2707333333333333'
161-
},
162-
{
163-
dvc_data_version_info: {
164-
field: join('test', 'acc'),
165-
filename: watchedFile,
166-
revision: EXPERIMENT_WORKSPACE_ID
167-
},
168-
dvc_inferred_y_value: '0.2712',
169-
step: '0',
170-
[join('test', 'acc')]: '0.2712'
171162
}
172163
]
173164
},
@@ -196,6 +187,17 @@ suite('Plots Data Test Suite', () => {
196187

197188
void data.update()
198189
await data.isReady()
190+
data.setMetricFiles({
191+
workspace: {
192+
baseline: { data: { metrics: { [metricsFile]: { data: {} } } } }
193+
}
194+
})
195+
196+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
197+
expect((data as any).collectedFiles).to.deep.equal([
198+
collectedFile,
199+
metricsFile
200+
])
199201

200202
bypassProcessManagerDebounce(mockNow)
201203

@@ -204,7 +206,7 @@ suite('Plots Data Test Suite', () => {
204206
disposable.track(data.onDidUpdate(() => resolve(undefined)))
205207
)
206208

207-
await fireWatcher(join(dvcDemoPath, watchedFile))
209+
await fireWatcher(join(dvcDemoPath, metricsFile))
208210
await dataUpdatedEvent
209211

210212
expect(

0 commit comments

Comments
 (0)