From d706c3742f6566d9993ec9c736d939e175e42213 Mon Sep 17 00:00:00 2001 From: Riley Jones Date: Tue, 27 Aug 2024 00:15:22 +0000 Subject: [PATCH] recreate changes manually --- tensorboard/webapp/metrics/effects/index.ts | 327 ++++++++++---------- 1 file changed, 169 insertions(+), 158 deletions(-) diff --git a/tensorboard/webapp/metrics/effects/index.ts b/tensorboard/webapp/metrics/effects/index.ts index 2ae62d817c..4ee3d22f97 100644 --- a/tensorboard/webapp/metrics/effects/index.ts +++ b/tensorboard/webapp/metrics/effects/index.ts @@ -72,13 +72,142 @@ const initAction = createAction('[Metrics Effects] Init'); @Injectable() export class MetricsEffects implements OnInitEffects { + /** @export */ + ngrxOnInitEffects(): Action { + return initAction(); + } + + /** + * Our effects react when the plugin dashboard is fully "shown" and experiment + * ids are available. The `activePlugin` acts as our proxy to know whether it + * is shown. + * + * [Metrics Effects] Init - the initial `activePlugin` is set. + * [Core] Plugin Changed - subsequent `activePlugin` updates. + * [Core] PluginListing Fetch Successful - list of plugins fetched and the + * first `activePlugin` set. + * [App Routing] Navigated - experiment id updates. + */ + private readonly dashboardShownWithoutData$; + + private readonly reloadRequestedWhileShown$; + + private readonly loadTagMetadata$; + + private getVisibleCardFetchInfos(): Observable { + const visibleCardIds$ = this.store.select(selectors.getVisibleCardIdSet); + return visibleCardIds$.pipe( + switchMap((cardIds) => { + // Explicitly notify subscribers that there are no visible cards, + // since `forkJoin` does not emit when passed an empty array. + if (!cardIds.size) { + return of([]); + } + const observables = [...cardIds].map((cardId) => { + return this.store.select(getCardFetchInfo, cardId).pipe(take(1)); + }); + return forkJoin(observables); + }), + map((fetchInfos) => { + return fetchInfos.filter(Boolean) as CardFetchInfo[]; + }) + ); + } + + private fetchTimeSeries(request: TimeSeriesRequest) { + return this.metricsDataSource.fetchTimeSeries([request]).pipe( + tap((responses: TimeSeriesResponse[]) => { + const errors = responses.filter(isFailedTimeSeriesResponse); + if (errors.length) { + console.error('Time series response contained errors:', errors); + } + this.store.dispatch( + actions.fetchTimeSeriesLoaded({response: responses[0]}) + ); + }), + catchError(() => { + this.store.dispatch(actions.fetchTimeSeriesFailed({request})); + return of(null); + }) + ); + } + + private fetchTimeSeriesForCards( + fetchInfos: CardFetchInfo[], + experimentIds: string[] + ) { + /** + * TODO(psybuzz): if 2 cards require the same data, we should dedupe instead of + * making 2 identical requests. + */ + const requests: TimeSeriesRequest[] = fetchInfos.map((fetchInfo) => { + const {plugin, tag, runId, sample} = fetchInfo; + const partialRequest: TimeSeriesRequest = isSingleRunPlugin(plugin) + ? {plugin, tag, runId: runId!} + : {plugin, tag, experimentIds}; + if (sample !== undefined) { + partialRequest.sample = sample; + } + return partialRequest; + }); + + // Fetch and handle responses. + return of(requests).pipe( + tap((requests) => { + this.store.dispatch(actions.multipleTimeSeriesRequested({requests})); + }), + mergeMap((requests: TimeSeriesRequest[]) => { + const observables = requests.map((request) => + this.fetchTimeSeries(request) + ); + return merge(...observables); + }) + ); + } + + private readonly visibleCardsWithoutDataChanged$; + + private readonly visibleCardsReloaded$; + + private readonly loadTimeSeries$; + + private readonly addOrRemovePin$; + + private readonly loadSavedPins$; + + private readonly removeAllPins$; + + private readonly addOrRemovePinsOnToggle$; + + /** + * In general, this effect dispatch the following actions: + * + * On dashboard shown without data loaded: + * - metricsTagMetadataRequested + * + * On changes to the set of cards that are visible: + * - multipleTimeSeriesRequested + * + * On reloads: + * - metricsTagMetadataRequested + * - multipleTimeSeriesRequested + * + * On data source responses: + * - metricsTagMetadataLoaded + * - metricsTagMetadataFailed + * - fetchTimeSeriesLoaded + * - fetchTimeSeriesFailed + */ + /** @export */ + readonly dataEffects$; + constructor( private readonly actions$: Actions, private readonly store: Store, private readonly metricsDataSource: MetricsDataSource, private readonly savedPinsDataSource: SavedPinsDataSource ) { - this.dashboardShownWithoutData$ = this.actions$.pipe( + this.dashboardShownWithoutData$ = actions$.pipe( ofType( initAction, coreActions.changePlugin, @@ -96,13 +225,15 @@ export class MetricsEffects implements OnInitEffects { ); }) ); - this.reloadRequestedWhileShown$ = this.actions$.pipe( + + this.reloadRequestedWhileShown$ = actions$.pipe( ofType(coreActions.reload, coreActions.manualReload), withLatestFrom(this.store.select(getActivePlugin)), filter(([, activePlugin]) => { return activePlugin === METRICS_PLUGIN_ID; }) ); + this.loadTagMetadata$ = merge( this.dashboardShownWithoutData$, this.reloadRequestedWhileShown$ @@ -138,6 +269,7 @@ export class MetricsEffects implements OnInitEffects { ); }) ); + this.visibleCardsWithoutDataChanged$ = this.actions$.pipe( ofType(actions.cardVisibilityChanged), withLatestFrom(this.getVisibleCardFetchInfos()), @@ -147,6 +279,7 @@ export class MetricsEffects implements OnInitEffects { }); }) ); + this.visibleCardsReloaded$ = this.reloadRequestedWhileShown$.pipe( withLatestFrom(this.getVisibleCardFetchInfos()), map(([, fetchInfos]) => { @@ -155,6 +288,7 @@ export class MetricsEffects implements OnInitEffects { }); }) ); + this.loadTimeSeries$ = merge( this.visibleCardsWithoutDataChanged$, this.visibleCardsReloaded$ @@ -172,6 +306,7 @@ export class MetricsEffects implements OnInitEffects { return this.fetchTimeSeriesForCards(fetchInfos, experimentIds!); }) ); + this.addOrRemovePin$ = this.actions$.pipe( ofType(actions.cardPinStateToggled), withLatestFrom( @@ -205,6 +340,7 @@ export class MetricsEffects implements OnInitEffects { } }) ); + this.loadSavedPins$ = this.actions$.pipe( // Should be dispatch before stateRehydratedFromUrl. ofType(initAction), @@ -240,6 +376,7 @@ export class MetricsEffects implements OnInitEffects { ); }) ); + this.removeAllPins$ = this.actions$.pipe( ofType(actions.metricsClearAllPinnedCards), withLatestFrom( @@ -262,6 +399,7 @@ export class MetricsEffects implements OnInitEffects { this.savedPinsDataSource.removeAllScalarPins(); }) ); + this.addOrRemovePinsOnToggle$ = this.actions$.pipe( ofType(actions.metricsEnableSavingPinsToggled), withLatestFrom( @@ -287,168 +425,41 @@ export class MetricsEffects implements OnInitEffects { } }) ); - } - /** @export */ - ngrxOnInitEffects(): Action { - return initAction(); - } + this.dataEffects$ = createEffect( + () => { + return merge( + /** + * Subscribes to: dashboard shown, route navigation, reloads. + */ + this.loadTagMetadata$, - /** - * Our effects react when the plugin dashboard is fully "shown" and experiment - * ids are available. The `activePlugin` acts as our proxy to know whether it - * is shown. - * - * [Metrics Effects] Init - the initial `activePlugin` is set. - * [Core] Plugin Changed - subsequent `activePlugin` updates. - * [Core] PluginListing Fetch Successful - list of plugins fetched and the - * first `activePlugin` set. - * [App Routing] Navigated - experiment id updates. - */ - private readonly dashboardShownWithoutData$; + /** + * Subscribes to: card visibility, reloads. + */ + this.loadTimeSeries$, - private readonly reloadRequestedWhileShown$; - - private readonly loadTagMetadata$; - - private getVisibleCardFetchInfos(): Observable { - const visibleCardIds$ = this.store.select(selectors.getVisibleCardIdSet); - return visibleCardIds$.pipe( - switchMap((cardIds) => { - // Explicitly notify subscribers that there are no visible cards, - // since `forkJoin` does not emit when passed an empty array. - if (!cardIds.size) { - return of([]); - } - const observables = [...cardIds].map((cardId) => { - return this.store.select(getCardFetchInfo, cardId).pipe(take(1)); - }); - return forkJoin(observables); - }), - map((fetchInfos) => { - return fetchInfos.filter(Boolean) as CardFetchInfo[]; - }) - ); - } - - private fetchTimeSeries(request: TimeSeriesRequest) { - return this.metricsDataSource.fetchTimeSeries([request]).pipe( - tap((responses: TimeSeriesResponse[]) => { - const errors = responses.filter(isFailedTimeSeriesResponse); - if (errors.length) { - console.error('Time series response contained errors:', errors); - } - this.store.dispatch( - actions.fetchTimeSeriesLoaded({response: responses[0]}) + /** + * Subscribes to: cardPinStateToggled. + */ + this.addOrRemovePin$, + /** + * Subscribes to: dashboard shown (initAction). + */ + this.loadSavedPins$, + /** + * Subscribes to: metricsClearAllPinnedCards. + */ + this.removeAllPins$, + /** + * Subscribes to: metricsEnableSavingPinsToggled. + */ + this.addOrRemovePinsOnToggle$ ); - }), - catchError(() => { - this.store.dispatch(actions.fetchTimeSeriesFailed({request})); - return of(null); - }) + }, + {dispatch: false} ); } - - private fetchTimeSeriesForCards( - fetchInfos: CardFetchInfo[], - experimentIds: string[] - ) { - /** - * TODO(psybuzz): if 2 cards require the same data, we should dedupe instead of - * making 2 identical requests. - */ - const requests: TimeSeriesRequest[] = fetchInfos.map((fetchInfo) => { - const {plugin, tag, runId, sample} = fetchInfo; - const partialRequest: TimeSeriesRequest = isSingleRunPlugin(plugin) - ? {plugin, tag, runId: runId!} - : {plugin, tag, experimentIds}; - if (sample !== undefined) { - partialRequest.sample = sample; - } - return partialRequest; - }); - - // Fetch and handle responses. - return of(requests).pipe( - tap((requests) => { - this.store.dispatch(actions.multipleTimeSeriesRequested({requests})); - }), - mergeMap((requests: TimeSeriesRequest[]) => { - const observables = requests.map((request) => - this.fetchTimeSeries(request) - ); - return merge(...observables); - }) - ); - } - - private readonly visibleCardsWithoutDataChanged$; - - private readonly visibleCardsReloaded$; - - private readonly loadTimeSeries$; - - private readonly addOrRemovePin$; - - private readonly loadSavedPins$; - - private readonly removeAllPins$; - - private readonly addOrRemovePinsOnToggle$; - - /** - * In general, this effect dispatch the following actions: - * - * On dashboard shown without data loaded: - * - metricsTagMetadataRequested - * - * On changes to the set of cards that are visible: - * - multipleTimeSeriesRequested - * - * On reloads: - * - metricsTagMetadataRequested - * - multipleTimeSeriesRequested - * - * On data source responses: - * - metricsTagMetadataLoaded - * - metricsTagMetadataFailed - * - fetchTimeSeriesLoaded - * - fetchTimeSeriesFailed - */ - /** @export */ - readonly dataEffects$ = createEffect( - () => { - return merge( - /** - * Subscribes to: dashboard shown, route navigation, reloads. - */ - this.loadTagMetadata$, - - /** - * Subscribes to: card visibility, reloads. - */ - this.loadTimeSeries$, - - /** - * Subscribes to: cardPinStateToggled. - */ - this.addOrRemovePin$, - /** - * Subscribes to: dashboard shown (initAction). - */ - this.loadSavedPins$, - /** - * Subscribes to: metricsClearAllPinnedCards. - */ - this.removeAllPins$, - /** - * Subscribes to: metricsEnableSavingPinsToggled. - */ - this.addOrRemovePinsOnToggle$ - ); - }, - {dispatch: false} - ); } export const TEST_ONLY = {