Skip to content

Commit

Permalink
add a new filter option to the data table context menu which allows c…
Browse files Browse the repository at this point in the history
…olumns to be filtered
  • Loading branch information
rileyajones committed Jul 11, 2023
1 parent 862f9bc commit 3146158
Show file tree
Hide file tree
Showing 19 changed files with 419 additions and 34 deletions.
1 change: 1 addition & 0 deletions tensorboard/webapp/hparams/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tf_ts_library(
],
deps = [
"//tensorboard/webapp/runs/data_source",
"//tensorboard/webapp/widgets/data_table:types",
],
)

Expand Down
22 changes: 2 additions & 20 deletions tensorboard/webapp/hparams/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
import {
DiscreteHparamValues,
DomainType,
HparamSpec,
MetricSpec,
} from '../runs/data_source/runs_data_source_types';

export {DiscreteFilter, IntervalFilter} from '../widgets/data_table/types';

export {
DatasetType,
DiscreteHparamValue,
Expand All @@ -33,21 +33,3 @@ export interface HparamAndMetricSpec {
hparams: HparamSpec[];
metrics: MetricSpec[];
}

export interface DiscreteFilter {
type: DomainType.DISCRETE;
includeUndefined: boolean;
possibleValues: DiscreteHparamValues;
// Subset of `possibleValues`
filterValues: DiscreteHparamValues;
}

export interface IntervalFilter {
type: DomainType.INTERVAL;
includeUndefined: boolean;
minValue: number;
maxValue: number;
// Filter values have to be in between min and max values (inclusive).
filterLowerValue: number;
filterUpperValue: number;
}
22 changes: 22 additions & 0 deletions tensorboard/webapp/metrics/views/main_view/common_selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,24 @@ const getFilteredRenderableRuns = memoize((experimentIds: string[]) => {
);
});

const getCurrentColumnFilters = memoize((experimentIds: string[]) => {
return createSelector(
getHparamFilterMapFromExperimentIds(experimentIds),
getMetricFilterMapFromExperimentIds(experimentIds),
(hparamFilters, metricFilters) => {
return new Map([...hparamFilters, ...metricFilters]);
}
);
});

export const getCurrentColumnFiltersFromRoute = createSelector(
(state) => state,
getExperimentIdsFromRoute,
(state, experimentIds) => {
return getCurrentColumnFilters(experimentIds || [])(state);
}
);

export const getFilteredRenderableRunsFromRoute = createSelector(
(state) => state,
getExperimentIdsFromRoute,
Expand Down Expand Up @@ -272,6 +290,9 @@ export const getPotentialHparamColumns = createSelector(
experimentIds,
});

const hparamFilters =
getHparamFilterMapFromExperimentIds(experimentIds)(state);

return hparams.map((spec) => ({
type: ColumnHeaderType.HPARAM,
name: spec.name,
Expand All @@ -282,6 +303,7 @@ export const getPotentialHparamColumns = createSelector(
removable: true,
sortable: true,
movable: true,
filterable: true,
}));
}
);
Expand Down
3 changes: 2 additions & 1 deletion tensorboard/webapp/runs/data_source/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tf_ng_module(
],
deps = [
":backend_types",
"//tensorboard/webapp/widgets/data_table:types",
"//tensorboard/webapp/webapp_data_source:http_client",
"@npm//@angular/core",
"@npm//rxjs",
Expand All @@ -24,7 +25,7 @@ tf_ts_library(
srcs = [
"runs_backend_types.ts",
],
visibility = ["//visibility:private"],
visibility = ["//tensorboard/webapp/runs/data_source:__subpackages__", "//tensorboard/webapp/widgets/data_table:__subpackages__",],
)

tf_ng_module(
Expand Down
2 changes: 1 addition & 1 deletion tensorboard/webapp/runs/data_source/runs_data_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import {
TBHttpClient,
} from '../../webapp_data_source/tb_http_client';
import * as backendTypes from './runs_backend_types';
import {DomainType} from '../../widgets/data_table/types';
import {
Domain,
DomainType,
HparamsAndMetadata,
HparamSpec,
HparamValue,
Expand Down
8 changes: 3 additions & 5 deletions tensorboard/webapp/runs/data_source/runs_data_source_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import * as backendTypes from './runs_backend_types';

import {DomainType} from '../../widgets/data_table/types';
export {DomainType} from '../../widgets/data_table/types';

export {
BackendHparamsValueType as HparamsValueType,
DatasetType,
Expand All @@ -41,11 +44,6 @@ export interface RunToHparamsAndMetrics {
};
}

export enum DomainType {
DISCRETE,
INTERVAL,
}

interface IntervalDomain {
type: DomainType.INTERVAL;
minValue: number;
Expand Down
1 change: 1 addition & 0 deletions tensorboard/webapp/runs/store/runs_reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ const {initialState: uiInitialState, reducers: uiNamespaceContextedReducers} =
enabled: true,
sortable: true,
movable: true,
filterable: false,
},
],
sortingInfo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
[sortingInfo]="sortingInfo"
[columnCustomizationEnabled]="true"
[selectableColumns]="selectableColumns"
[columnFilters]="columnFilters"
(sortDataBy)="sortDataBy.emit($event)"
(orderColumns)="orderColumns.emit($event)"
(addColumn)="addColumn.emit($event)"
(removeColumn)="removeColumn.emit($event)"
(addFilter)="addFilter.emit($event)"
>
<ng-container header>
<ng-container *ngFor="let header of getHeaders()">
Expand Down
6 changes: 5 additions & 1 deletion tensorboard/webapp/runs/views/runs_table/runs_data_table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import {
TableData,
SortingInfo,
ColumnHeaderType,
FilterAddedEvent,
DiscreteFilter,
IntervalFilter,
} from '../../../widgets/data_table/types';

@Component({
selector: 'runs-data-table',
templateUrl: 'runs_data_table.ng.html',
Expand All @@ -40,6 +42,7 @@ export class RunsDataTable {
@Input() regexFilter!: string;
@Input() isFullScreen!: boolean;
@Input() selectableColumns!: ColumnHeader[];
@Input() columnFilters!: Map<string, DiscreteFilter | IntervalFilter>;

ColumnHeaderType = ColumnHeaderType;

Expand All @@ -59,6 +62,7 @@ export class RunsDataTable {
}>();
@Output() removeColumn = new EventEmitter<ColumnHeader>();
@Output() onSelectionDblClick = new EventEmitter<string>();
@Output() addFilter = new EventEmitter<FilterAddedEvent>();

// These columns must be stored and reused to stop needless re-rendering of
// the content and headers in these columns. This has been known to cause
Expand Down
28 changes: 26 additions & 2 deletions tensorboard/webapp/runs/views/runs_table/runs_table_container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import {matchRunToRegex} from '../../../util/matcher';
import {getEnableHparamsInTimeSeries} from '../../../feature_flag/store/feature_flag_selectors';
import {
ColumnHeader,
ColumnHeaderType,
FilterAddedEvent,
SortingInfo,
SortingOrder,
TableData,
Expand Down Expand Up @@ -96,10 +96,10 @@ import {
} from './runs_table_component';
import {RunsTableColumn, RunTableItem} from './types';
import {
getCurrentColumnFiltersFromRoute,
getFilteredRenderableRunsFromRoute,
getPotentialHparamColumns,
} from '../../../metrics/views/main_view/common_selectors';
import {RunToHParamValues} from '../../data_source/runs_data_source_types';
import {runsTableFullScreenToggled} from '../../../core/actions';

const getRunsLoading = createSelector<
Expand Down Expand Up @@ -271,6 +271,7 @@ function matchFilter(
[headers]="runsColumns$ | async"
[data]="sortedRunsTableData$ | async"
[selectableColumns]="selectableColumns$ | async"
[columnFilters]="columnFilters$ | async"
[sortingInfo]="sortingInfo$ | async"
[experimentIds]="experimentIds"
[regexFilter]="regexFilter$ | async"
Expand All @@ -285,6 +286,7 @@ function matchFilter(
(toggleFullScreen)="toggleFullScreen()"
(addColumn)="addColumn($event)"
(removeColumn)="removeColumn($event)"
(addFilter)="addHparamFilter($event)"
></runs-data-table>
`,
host: {
Expand Down Expand Up @@ -367,6 +369,8 @@ export class RunsTableContainer implements OnInit, OnDestroy {
})
);

columnFilters$ = this.store.select(getCurrentColumnFiltersFromRoute);

allRunsTableData$ = this.store
.select(getFilteredRenderableRunsFromRoute)
.pipe(
Expand Down Expand Up @@ -823,6 +827,26 @@ export class RunsTableContainer implements OnInit, OnDestroy {
useDataTable() {
return this.hparamsEnabled.value && !this.forceLegacyTable;
}

addHparamFilter(event: FilterAddedEvent) {
switch (event.value.type) {
case DomainType.INTERVAL:
this.onHparamIntervalFilterChanged({
name: event.header.name,
includeUndefined: event.value.includeUndefined,
filterLowerValue: (event.value as IntervalFilter).filterLowerValue,
filterUpperValue: (event.value as IntervalFilter).filterUpperValue,
});
break;
case DomainType.DISCRETE:
this.onHparamDiscreteFilterChanged({
hparamName: event.header.name,
includeUndefined: event.value.includeUndefined,
filterValues: (event.value as DiscreteFilter).filterValues,
});
break;
}
}
}

export const TEST_ONLY = {
Expand Down
34 changes: 34 additions & 0 deletions tensorboard/webapp/widgets/data_table/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ tf_sass_binary(
],
)

tf_sass_binary(
name = "filter_dialogue_styles",
src = "filter_dialogue.scss",
strict_deps = False,
deps = [
"//tensorboard/webapp:angular_material_sass_deps",
"//tensorboard/webapp/theme",
],
)

tf_ng_module(
name = "data_table",
srcs = [
Expand All @@ -71,9 +81,11 @@ tf_ng_module(
deps = [
":column_selector",
":data_table_header",
":filter_dialogue",
":types",
"//tensorboard/webapp/angular:expect_angular_material_button",
"//tensorboard/webapp/angular:expect_angular_material_icon",
"//tensorboard/webapp/widgets/range_input:types",
"//tensorboard/webapp/metrics/views/card_renderer:scalar_card_types",
"//tensorboard/webapp/widgets/custom_modal",
"//tensorboard/webapp/widgets/line_chart_v2/lib:formatter",
Expand Down Expand Up @@ -123,6 +135,28 @@ tf_ng_module(
],
)

tf_ng_module(
name = "filter_dialogue",
srcs = [
"filter_dialogue.ts",
"filter_dialogue_module.ts",
],
assets = [
"filter_dialogue.ng.html",
":filter_dialogue_styles",
],
deps = [
":types",
"//tensorboard/webapp/angular:expect_angular_material_button",
"//tensorboard/webapp/angular:expect_angular_material_checkbox",
"//tensorboard/webapp/widgets/range_input",
"//tensorboard/webapp/widgets/range_input:types",
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@angular/forms",
],
)

tf_ts_library(
name = "types",
srcs = [
Expand Down
24 changes: 21 additions & 3 deletions tensorboard/webapp/widgets/data_table/data_table_component.ng.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<custom-modal #contextMenu>
<div class="context-menu">
<div
*ngIf="!contextMenuHeader?.removable && !contextMenuHeader?.sortable && !canContextMenuInsert()"
*ngIf="!contextMenuHeader?.removable && !contextMenuHeader?.sortable && !canContextMenuInsert() && !contextMenuHeader?.filterable"
class="no-actions-message"
>
No Actions Available
Expand Down Expand Up @@ -47,17 +47,25 @@
</ng-template>
</button>
<button
*ngIf="contextMenuHeader?.filterable"
class="context-menu-button"
mat-button
(click)="openFilterMenu($event, contextMenuHeader)"
>
<mat-icon svgIcon="filter_alt_24px"></mat-icon> Filter
</button>
<button
*ngIf="canContextMenuInsert()"
class="context-menu-button"
mat-button
(click)="openColumnSelector($event, Side.LEFT)"
>
<mat-icon svgIcon="add_24px"></mat-icon>Insert Column Left
</button>
<button
mat-button
*ngIf="canContextMenuInsert()"
class="context-menu-button"
mat-button
(click)="openColumnSelector($event, Side.RIGHT)"
>
<mat-icon svgIcon="add_24px"></mat-icon>Insert Column Right
Expand All @@ -69,14 +77,24 @@
#columnSelectorModal
*ngIf="selectableColumns && selectableColumns.length"
(onOpen)="focusColumnSelector()"
onClose="onColumnSelectorClosed()"
(onClose)="onColumnSelectorClosed()"
>
<tb-data-table-column-selector-component
[selectableColumns]="selectableColumns"
(columnSelected)="onColumnAdded($event)"
></tb-data-table-column-selector-component>
</custom-modal>

<custom-modal #filterModal (onClose)="onFilterClosed()">
<tb-data-table-filter
*ngIf="getCurrentColumnFilter()"
[filter]="getCurrentColumnFilter()"
(intervalFilterChanged)="intervalFilterChanged($event)"
(discreteFilterChanged)="discreteFilterChanged($event)"
(includeUndefinedToggled)="includeUndefinedToggled()"
></tb-data-table-filter>
</custom-modal>

<div class="data-table">
<div class="header">
<ng-content select="[header]"></ng-content>
Expand Down
Loading

0 comments on commit 3146158

Please sign in to comment.