diff --git a/tensorboard/webapp/metrics/internal_types.ts b/tensorboard/webapp/metrics/internal_types.ts index 43b6fc2ba5..3825cb7532 100644 --- a/tensorboard/webapp/metrics/internal_types.ts +++ b/tensorboard/webapp/metrics/internal_types.ts @@ -43,6 +43,7 @@ export interface CardMetadata { plugin: PluginType; tag: string; sample?: number; + numSample?: number; /** * A `null` runId indicates all runs. diff --git a/tensorboard/webapp/metrics/store/metrics_reducers.ts b/tensorboard/webapp/metrics/store/metrics_reducers.ts index a91e21c82f..2466f3e91e 100644 --- a/tensorboard/webapp/metrics/store/metrics_reducers.ts +++ b/tensorboard/webapp/metrics/store/metrics_reducers.ts @@ -82,7 +82,13 @@ function buildCardMetadataList(tagMetadata: TagMetadata): CardMetadata[] { for (const runId of Object.keys(tagRunSampleInfo[tag])) { const {maxSamplesPerStep} = tagRunSampleInfo[tag][runId]; for (let i = 0; i < maxSamplesPerStep; i++) { - results.push({plugin, tag, runId, sample: i}); + results.push({ + plugin, + tag, + runId, + sample: i, + numSample: maxSamplesPerStep, + }); } } } diff --git a/tensorboard/webapp/metrics/store/metrics_reducers_test.ts b/tensorboard/webapp/metrics/store/metrics_reducers_test.ts index 104aae109e..536746c369 100644 --- a/tensorboard/webapp/metrics/store/metrics_reducers_test.ts +++ b/tensorboard/webapp/metrics/store/metrics_reducers_test.ts @@ -201,9 +201,27 @@ describe('metrics reducers', () => { const expectedCardMetadataList = [ {plugin: PluginType.SCALARS, tag: 'tagA', runId: null}, {plugin: PluginType.HISTOGRAMS, tag: 'tagB', runId: 'run2'}, - {plugin: PluginType.IMAGES, tag: 'tagC', runId: 'run3', sample: 0}, - {plugin: PluginType.IMAGES, tag: 'tagC', runId: 'run3', sample: 1}, - {plugin: PluginType.IMAGES, tag: 'tagC', runId: 'run3', sample: 2}, + { + plugin: PluginType.IMAGES, + tag: 'tagC', + runId: 'run3', + sample: 0, + numSample: 3, + }, + { + plugin: PluginType.IMAGES, + tag: 'tagC', + runId: 'run3', + sample: 1, + numSample: 3, + }, + { + plugin: PluginType.IMAGES, + tag: 'tagC', + runId: 'run3', + sample: 2, + numSample: 3, + }, ]; const expectedCardMetadataMap: CardMetadataMap = {}; for (const cardMetadata of expectedCardMetadataList) { diff --git a/tensorboard/webapp/metrics/views/card_renderer/image_card_component.ng.html b/tensorboard/webapp/metrics/views/card_renderer/image_card_component.ng.html index 2397c70d03..8cc58a5041 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/image_card_component.ng.html +++ b/tensorboard/webapp/metrics/views/card_renderer/image_card_component.ng.html @@ -15,49 +15,61 @@ limitations under the License. -->
- -
- - +
+ + + + +
-
- Step {{ stepValues[stepIndex] }} - +
+ + + + +
- - - -
; runId$?: Observable; sample$?: Observable; + numSample$?: Observable; imageUrl$?: Observable; stepIndex$?: Observable; stepValues$?: Observable; @@ -230,6 +233,10 @@ export class ImageCardContainer implements CardRenderer, OnInit, OnDestroy { }) ); + this.numSample$ = cardMetadata$.pipe( + map((cardMetadata) => cardMetadata.numSample) + ); + this.imageUrl$ = stepDatum$.pipe( map((stepDatum: ImageStepDatum | null) => { if (!stepDatum) { diff --git a/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts b/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts index 3ea402594a..bfa968783a 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts @@ -272,6 +272,26 @@ describe('image card', () => { expect(slider).not.toBeTruthy(); }); + it('renders sample when numSample is larger than 1', () => { + const timeSeries = [{wallTime: 100, imageId: 'ImageId1', step: 10}]; + provideMockCardSeriesData( + selectSpy, + PluginType.IMAGES, + 'card1', + {sample: 5, numSample: 1.2e4}, + timeSeries, + 0 /* stepIndex */ + ); + + const fixture = createImageCardContainer('card1'); + fixture.detectChanges(); + + const metadata = fixture.debugElement.query(By.css('.metadata')); + expect(metadata.nativeElement.textContent.trim()).toBe( + 'Step 10Sample 6/12,000' + ); + }); + it('dispatches event when step slider changes', () => { const timeSeries = [ {wallTime: 100, imageId: 'ImageId1', step: 10},