Skip to content

Commit fd75a84

Browse files
authored
HParams: Update the runs table to read columns from redux state (#6404)
## Motivation for features / changes As part the effort to add hparams to time series we have updated the runs table to use the data table in #6394. Now I am updating the runs data table to read its columns from the redux state. ## Screenshots of UI changes (or N/A) N/A (but there will be soon!) ## Alternate designs / implementations considered (or N/A) We could have reused the existing sorting property in redux and added keys to it, but I prefered to create a seperate one and remove the existing property when we remove the old runs table.
1 parent 656d22c commit fd75a84

File tree

14 files changed

+373
-30
lines changed

14 files changed

+373
-30
lines changed

tensorboard/webapp/runs/actions/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ tf_ts_library(
1414
"//tensorboard/webapp/runs:types",
1515
"//tensorboard/webapp/runs/data_source",
1616
"//tensorboard/webapp/types:ui",
17+
"//tensorboard/webapp/widgets/data_table:types",
1718
"@npm//@ngrx/store",
1819
],
1920
)

tensorboard/webapp/runs/actions/runs_actions.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {createAction, props} from '@ngrx/store';
2020
import {SortDirection} from '../../types/ui';
2121
import {Run} from '../data_source/runs_data_source_types';
2222
import {ExperimentIdToRunsAndMetadata, GroupBy, SortKey} from '../types';
23+
import {ColumnHeader, SortingInfo} from '../../widgets/data_table/types';
2324

2425
/**
2526
* The action can fire when no requests are actually made (i.e., an empty
@@ -96,3 +97,19 @@ export const runGroupByChanged = createAction(
9697
'[Runs] Run Group By Changed',
9798
props<{experimentIds: string[]; groupBy: GroupBy}>()
9899
);
100+
101+
/**
102+
* Inserts the provided column header at the specified index.
103+
*/
104+
export const runsTableHeaderAdded = createAction(
105+
'[Runs] Runs Table Header Added',
106+
props<{header: ColumnHeader; index?: number}>()
107+
);
108+
109+
/**
110+
* Updates the sorting logic used by the runs data tabe.
111+
*/
112+
export const runsTableSortingInfoChanged = createAction(
113+
'[Runs] Runs Table Sorting Info Changed',
114+
props<{sortingInfo: SortingInfo}>()
115+
);

tensorboard/webapp/runs/data_source/runs_data_source_types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,8 @@ export abstract class RunsDataSource {
8686
experimentId: string
8787
): Observable<HparamsAndMetadata>;
8888
}
89+
90+
export type RunToHParamValues = Record<
91+
string,
92+
Map<string, HparamValue['value']>
93+
>;

tensorboard/webapp/runs/store/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ tf_ts_library(
2222
"//tensorboard/webapp/types",
2323
"//tensorboard/webapp/types:ui",
2424
"//tensorboard/webapp/util:ngrx",
25+
"//tensorboard/webapp/widgets/data_table:types",
2526
"@npm//@ngrx/store",
2627
],
2728
)
@@ -47,6 +48,7 @@ tf_ts_library(
4748
"//tensorboard/webapp/runs:types",
4849
"//tensorboard/webapp/types",
4950
"//tensorboard/webapp/types:ui",
51+
"//tensorboard/webapp/widgets/data_table:types",
5052
"@npm//@ngrx/store",
5153
],
5254
)
@@ -62,6 +64,7 @@ tf_ts_library(
6264
"//tensorboard/webapp/runs/data_source",
6365
"//tensorboard/webapp/types",
6466
"//tensorboard/webapp/types:ui",
67+
"//tensorboard/webapp/widgets/data_table:types",
6568
],
6669
)
6770

@@ -74,6 +77,7 @@ tf_ts_library(
7477
"//tensorboard/webapp/runs:types",
7578
"//tensorboard/webapp/runs/data_source",
7679
"//tensorboard/webapp/types:ui",
80+
"//tensorboard/webapp/widgets/data_table:types",
7781
],
7882
)
7983

@@ -101,6 +105,7 @@ tf_ts_library(
101105
"//tensorboard/webapp/testing:lang",
102106
"//tensorboard/webapp/types",
103107
"//tensorboard/webapp/types:ui",
108+
"//tensorboard/webapp/widgets/data_table:types",
104109
"@npm//@types/jasmine",
105110
],
106111
)

tensorboard/webapp/runs/store/runs_reducers.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ import {
3535
RunsDataState,
3636
RunsState,
3737
RunsUiNamespacedState,
38+
RunsUiNonNamespacedState,
3839
RunsUiState,
3940
} from './runs_types';
4041
import {createGroupBy, groupRuns} from './utils';
42+
import {ColumnHeaderType, SortingOrder} from '../../widgets/data_table/types';
4143

4244
const {
4345
initialState: dataInitialState,
@@ -309,14 +311,30 @@ const initialSort: RunsUiNamespacedState['sort'] = {
309311
direction: SortDirection.UNSET,
310312
};
311313
const {initialState: uiInitialState, reducers: uiNamespaceContextedReducers} =
312-
createNamespaceContextedState(
314+
createNamespaceContextedState<
315+
RunsUiNamespacedState,
316+
RunsUiNonNamespacedState
317+
>(
313318
{
314319
paginationOption: {
315320
pageIndex: 0,
316321
pageSize: 10,
317322
},
318323
sort: initialSort,
319324
selectionState: new Map<string, boolean>(),
325+
runsTableHeaders: [
326+
{
327+
type: ColumnHeaderType.RUN,
328+
name: 'run',
329+
displayName: 'Run',
330+
enabled: true,
331+
},
332+
],
333+
sortingInfo: {
334+
header: ColumnHeaderType.RUN,
335+
name: 'run',
336+
order: SortingOrder.DESCENDING,
337+
},
320338
},
321339
{}
322340
);
@@ -407,6 +425,25 @@ const uiReducer: ActionReducer<RunsUiState, Action> = createReducer(
407425
...state,
408426
selectionState: nextSelectionState,
409427
};
428+
}),
429+
on(runsActions.runsTableHeaderAdded, (state, {header, index}) => {
430+
const newRunsTableHeaders = [...state.runsTableHeaders];
431+
if (index === undefined) {
432+
newRunsTableHeaders.push(header);
433+
} else {
434+
newRunsTableHeaders.splice(index, 0, header);
435+
}
436+
437+
return {
438+
...state,
439+
runsTableHeaders: newRunsTableHeaders,
440+
};
441+
}),
442+
on(runsActions.runsTableSortingInfoChanged, (state, {sortingInfo}) => {
443+
return {
444+
...state,
445+
sortingInfo,
446+
};
410447
})
411448
);
412449

tensorboard/webapp/runs/store/runs_reducers_test.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ import {RouteKind} from '../../app_routing/types';
2222
import {deepFreeze} from '../../testing/lang';
2323
import {DataLoadState} from '../../types/data';
2424
import {SortDirection} from '../../types/ui';
25+
import {ColumnHeaderType, SortingOrder} from '../../widgets/data_table/types';
2526
import * as actions from '../actions';
2627
import {buildHparamsAndMetadata} from '../data_source/testing';
2728
import {GroupByKey, SortType, URLDeserializedState} from '../types';
2829
import * as runsReducers from './runs_reducers';
29-
import {MAX_NUM_RUNS_TO_ENABLE_BY_DEFAULT, Run} from './runs_types';
30+
import {MAX_NUM_RUNS_TO_ENABLE_BY_DEFAULT, Run, RunsState} from './runs_types';
3031
import {buildRun, buildRunsState} from './testing';
3132

3233
describe('runs_reducers', () => {
@@ -1353,4 +1354,103 @@ describe('runs_reducers', () => {
13531354
expect(nextState.data.initialGroupBy.key).toBe(GroupByKey.RUN);
13541355
});
13551356
});
1357+
1358+
describe('runsTableHeaderAdded', () => {
1359+
let state: RunsState;
1360+
1361+
beforeEach(() => {
1362+
state = buildRunsState(
1363+
{},
1364+
{
1365+
runsTableHeaders: [
1366+
{
1367+
type: ColumnHeaderType.RUN,
1368+
name: 'run',
1369+
displayName: 'Run',
1370+
enabled: true,
1371+
},
1372+
{
1373+
type: ColumnHeaderType.VALUE,
1374+
name: 'value',
1375+
displayName: 'Value',
1376+
enabled: true,
1377+
},
1378+
],
1379+
}
1380+
);
1381+
});
1382+
1383+
it('adds new column to end of list when no index is provided', () => {
1384+
const nextState = runsReducers.reducers(
1385+
state,
1386+
actions.runsTableHeaderAdded({
1387+
header: {
1388+
type: ColumnHeaderType.COLOR,
1389+
name: 'color',
1390+
displayName: 'Color',
1391+
enabled: true,
1392+
},
1393+
})
1394+
);
1395+
expect(
1396+
nextState.ui.runsTableHeaders.map((header) => header.type)
1397+
).toEqual([
1398+
ColumnHeaderType.RUN,
1399+
ColumnHeaderType.VALUE,
1400+
ColumnHeaderType.COLOR,
1401+
]);
1402+
});
1403+
1404+
it('adds new column at the specified index', () => {
1405+
const nextState = runsReducers.reducers(
1406+
state,
1407+
actions.runsTableHeaderAdded({
1408+
header: {
1409+
type: ColumnHeaderType.COLOR,
1410+
name: 'color',
1411+
displayName: 'Color',
1412+
enabled: true,
1413+
},
1414+
index: 1,
1415+
})
1416+
);
1417+
expect(
1418+
nextState.ui.runsTableHeaders.map((header) => header.type)
1419+
).toEqual([
1420+
ColumnHeaderType.RUN,
1421+
ColumnHeaderType.COLOR,
1422+
ColumnHeaderType.VALUE,
1423+
]);
1424+
});
1425+
});
1426+
1427+
describe('runsTableSortingInfoChanged', () => {
1428+
it('returns the current runs table sorting info', () => {
1429+
const state = buildRunsState(
1430+
{},
1431+
{
1432+
sortingInfo: {
1433+
header: ColumnHeaderType.RUN,
1434+
name: 'run',
1435+
order: SortingOrder.ASCENDING,
1436+
},
1437+
}
1438+
);
1439+
const nextState = runsReducers.reducers(
1440+
state,
1441+
actions.runsTableSortingInfoChanged({
1442+
sortingInfo: {
1443+
header: ColumnHeaderType.HPARAM,
1444+
name: 'lr',
1445+
order: SortingOrder.DESCENDING,
1446+
},
1447+
})
1448+
);
1449+
expect(nextState.ui.sortingInfo).toEqual({
1450+
header: ColumnHeaderType.HPARAM,
1451+
name: 'lr',
1452+
order: SortingOrder.DESCENDING,
1453+
});
1454+
});
1455+
});
13561456
});

tensorboard/webapp/runs/store/runs_selectors.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
RUNS_FEATURE_KEY,
2727
} from './runs_types';
2828
import {createGroupBy} from './utils';
29+
import {ColumnHeader, SortingInfo} from '../../widgets/data_table/types';
2930

3031
const getRunsState = createFeatureSelector<RunsState>(RUNS_FEATURE_KEY);
3132

@@ -236,3 +237,23 @@ export const getColorGroupRegexString = createSelector(
236237
return state.colorGroupRegexString;
237238
}
238239
);
240+
241+
/**
242+
* Gets the columns to be displayed by the runs table.
243+
*/
244+
export const getRunsTableHeaders = createSelector(
245+
getUiState,
246+
(state: RunsUiState): ColumnHeader[] => {
247+
return state.runsTableHeaders;
248+
}
249+
);
250+
251+
/**
252+
* Gets the information needed to sort the runs data table.
253+
*/
254+
export const getRunsTableSortingInfo = createSelector(
255+
getUiState,
256+
(state: RunsUiState): SortingInfo => {
257+
return state.sortingInfo;
258+
}
259+
);

tensorboard/webapp/runs/store/runs_selectors_test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ limitations under the License.
1414
==============================================================================*/
1515
import {DataLoadState} from '../../types/data';
1616
import {SortDirection} from '../../types/ui';
17+
import {ColumnHeaderType, SortingOrder} from '../../widgets/data_table/types';
1718
import {GroupByKey, SortType} from '../types';
1819
import * as selectors from './runs_selectors';
1920
import {buildRun, buildRunsState, buildStateFromRunsState} from './testing';
@@ -584,4 +585,66 @@ describe('runs_selectors', () => {
584585
expect(selectors.getColorGroupRegexString(state)).toEqual('foo(\\d+)');
585586
});
586587
});
588+
589+
describe('#getRunsTableHeaders', () => {
590+
it('returns the runs table headers', () => {
591+
const state = buildStateFromRunsState(
592+
buildRunsState(
593+
{},
594+
{
595+
runsTableHeaders: [
596+
{
597+
type: ColumnHeaderType.RUN,
598+
name: 'run',
599+
displayName: 'Run',
600+
enabled: true,
601+
},
602+
{
603+
type: ColumnHeaderType.COLOR,
604+
name: 'color',
605+
displayName: 'Color',
606+
enabled: false,
607+
},
608+
],
609+
}
610+
)
611+
);
612+
expect(selectors.getRunsTableHeaders(state)).toEqual([
613+
{
614+
type: ColumnHeaderType.RUN,
615+
name: 'run',
616+
displayName: 'Run',
617+
enabled: true,
618+
},
619+
{
620+
type: ColumnHeaderType.COLOR,
621+
name: 'color',
622+
displayName: 'Color',
623+
enabled: false,
624+
},
625+
]);
626+
});
627+
});
628+
629+
describe('#getRunsTableSortingInfo', () => {
630+
it('returns the runs data table sorting info', () => {
631+
const state = buildStateFromRunsState(
632+
buildRunsState(
633+
{},
634+
{
635+
sortingInfo: {
636+
header: ColumnHeaderType.RUN,
637+
name: 'run',
638+
order: SortingOrder.ASCENDING,
639+
},
640+
}
641+
)
642+
);
643+
expect(selectors.getRunsTableSortingInfo(state)).toEqual({
644+
header: ColumnHeaderType.RUN,
645+
name: 'run',
646+
order: SortingOrder.ASCENDING,
647+
});
648+
});
649+
});
587650
});

tensorboard/webapp/runs/store/runs_types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ limitations under the License.
1919
import {NamespaceContextedState} from '../../app_routing/namespaced_state_reducer_helper';
2020
import {LoadState} from '../../types/data';
2121
import {SortDirection} from '../../types/ui';
22+
import {ColumnHeader, SortingInfo} from '../../widgets/data_table/types';
2223
import {HparamValue} from '../data_source/runs_data_source_types';
2324
import {GroupBy, GroupByKey, SortKey} from '../types';
2425

@@ -79,6 +80,8 @@ export interface RunsUiNamespacedState {
7980
* Indicates whether the run is selected.
8081
*/
8182
selectionState: Map<RunId, boolean>;
83+
runsTableHeaders: ColumnHeader[];
84+
sortingInfo: SortingInfo;
8285
}
8386

8487
export interface RunsUiNonNamespacedState {}

0 commit comments

Comments
 (0)