Skip to content

Commit 4597dc5

Browse files
committed
add redux logic
1 parent 56c51b0 commit 4597dc5

16 files changed

+907
-6
lines changed

tensorboard/webapp/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ tf_ng_web_test_suite(
269269
"//tensorboard/webapp/metrics:integration_test",
270270
"//tensorboard/webapp/metrics:test_lib",
271271
"//tensorboard/webapp/metrics:utils_test",
272+
"//tensorboard/webapp/metrics/data_source:card_interactions_data_source_test",
272273
"//tensorboard/webapp/metrics/data_source:metrics_data_source_test",
273274
"//tensorboard/webapp/metrics/effects:effects_test",
274275
"//tensorboard/webapp/metrics/store:store_test",

tensorboard/webapp/metrics/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ tf_ng_module(
1515
"//tensorboard/webapp/core",
1616
"//tensorboard/webapp/metrics/actions",
1717
"//tensorboard/webapp/metrics/data_source",
18+
"//tensorboard/webapp/metrics/data_source:card_interactions_data_source",
1819
"//tensorboard/webapp/metrics/effects",
1920
"//tensorboard/webapp/metrics/store",
2021
"//tensorboard/webapp/metrics/store:metrics_initial_state_provider",

tensorboard/webapp/metrics/actions/index.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ import {
2323
TimeSeriesRequest,
2424
TimeSeriesResponse,
2525
} from '../data_source';
26-
import {CardState} from '../store/metrics_types';
26+
import {CardInteractions, CardState} from '../store/metrics_types';
2727
import {
2828
CardId,
2929
HeaderEditInfo,
3030
HeaderToggleInfo,
3131
HistogramMode,
32-
MinMaxStep,
3332
PluginType,
3433
TooltipSort,
3534
XAxisType,
@@ -272,5 +271,15 @@ export const metricsHideEmptyCardsToggled = createAction(
272271
'[Metrics] Hide Empty Cards Changed'
273272
);
274273

274+
export const metricsPreviousCardInteractionsChanged = createAction(
275+
'[Metrics] Card Interactions Changed',
276+
props<{cardInteractions: CardInteractions}>()
277+
);
278+
279+
export const metricsCardClicked = createAction(
280+
'[Metrics] Card Clicked',
281+
props<{cardId: string}>()
282+
);
283+
275284
// TODO(jieweiwu): Delete after internal code is updated.
276285
export const stepSelectorTimeSelectionChanged = timeSelectionChanged;

tensorboard/webapp/metrics/data_source/BUILD

+26
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ tf_ng_module(
2525
],
2626
)
2727

28+
tf_ng_module(
29+
name = "card_interactions_data_source",
30+
srcs = [
31+
"card_interactions_data_source.ts",
32+
"card_interactions_data_source_module.ts",
33+
],
34+
deps = [
35+
"//tensorboard/webapp/metrics/store:types",
36+
"@npm//@angular/core",
37+
],
38+
)
39+
2840
tf_ts_library(
2941
name = "types",
3042
srcs = [
@@ -70,3 +82,17 @@ tf_ts_library(
7082
"@npm//rxjs",
7183
],
7284
)
85+
86+
tf_ts_library(
87+
name = "card_interactions_data_source_test",
88+
testonly = True,
89+
srcs = [
90+
"card_interactions_data_source_test.ts",
91+
],
92+
deps = [
93+
":card_interactions_data_source",
94+
"//tensorboard/webapp/angular:expect_angular_core_testing",
95+
"//tensorboard/webapp/metrics:internal_types",
96+
"@npm//@types/jasmine",
97+
],
98+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
import {Injectable} from '@angular/core';
16+
import {CardInteractions} from '../store/metrics_types';
17+
18+
const CARD_INTERACTIONS_KEY = 'tb-card-interactions';
19+
20+
const MAX_RECORDS: Record<keyof CardInteractions, number> = {
21+
pins: 10,
22+
clicks: 10,
23+
tagFilters: 10,
24+
};
25+
26+
@Injectable()
27+
export class CardInteractionsDataSource {
28+
saveCardInteractions(cardInteractions: CardInteractions) {
29+
const trimmedInteractions: CardInteractions = {
30+
pins: cardInteractions.pins.slice(
31+
cardInteractions.pins.length - MAX_RECORDS.pins
32+
),
33+
clicks: cardInteractions.clicks.slice(
34+
cardInteractions.clicks.length - MAX_RECORDS.clicks
35+
),
36+
tagFilters: cardInteractions.tagFilters.slice(
37+
cardInteractions.tagFilters.length - MAX_RECORDS.tagFilters
38+
),
39+
};
40+
localStorage.setItem(
41+
CARD_INTERACTIONS_KEY,
42+
JSON.stringify(trimmedInteractions)
43+
);
44+
}
45+
46+
getCardInteractions(): CardInteractions {
47+
const existingInteractions = localStorage.getItem(CARD_INTERACTIONS_KEY);
48+
if (existingInteractions) {
49+
return JSON.parse(existingInteractions) as CardInteractions;
50+
}
51+
return {
52+
tagFilters: [],
53+
pins: [],
54+
clicks: [],
55+
};
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
import {NgModule} from '@angular/core';
16+
import {CardInteractionsDataSource} from './card_interactions_data_source';
17+
18+
@NgModule({
19+
imports: [],
20+
providers: [CardInteractionsDataSource],
21+
})
22+
export class MetricsCardInteractionsDataSourceModule {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
import {TestBed} from '@angular/core/testing';
16+
import {CardInteractionsDataSource} from './card_interactions_data_source';
17+
import {PluginType} from '../internal_types';
18+
19+
describe('CardInteractionsDataSource Test', () => {
20+
let mockStorage: Record<string, string>;
21+
let dataSource: CardInteractionsDataSource;
22+
23+
beforeEach(async () => {
24+
await TestBed.configureTestingModule({
25+
providers: [CardInteractionsDataSource],
26+
});
27+
28+
dataSource = TestBed.inject(CardInteractionsDataSource);
29+
30+
mockStorage = {};
31+
spyOn(window.localStorage, 'setItem').and.callFake(
32+
(key: string, value: string) => {
33+
if (key !== 'tb-card-interactions') {
34+
throw new Error('incorrect key used');
35+
}
36+
37+
mockStorage[key] = value;
38+
}
39+
);
40+
41+
spyOn(window.localStorage, 'getItem').and.callFake((key: string) => {
42+
if (key !== 'tb-card-interactions') {
43+
throw new Error('incorrect key used');
44+
}
45+
46+
return mockStorage[key];
47+
});
48+
});
49+
50+
describe('saveCardInteractions', () => {
51+
it('only saves 10 pins', () => {
52+
dataSource.saveCardInteractions({
53+
clicks: [],
54+
tagFilters: [],
55+
pins: Array.from({length: 12}).map((_, index) => ({
56+
cardId: `card-${index}`,
57+
runId: null,
58+
tag: 'foo',
59+
plugin: PluginType.SCALARS,
60+
})),
61+
});
62+
63+
expect(dataSource.getCardInteractions().pins.length).toEqual(10);
64+
});
65+
66+
it('only saves 10 clicks', () => {
67+
dataSource.saveCardInteractions({
68+
pins: [],
69+
tagFilters: [],
70+
clicks: Array.from({length: 12}).map((_, index) => ({
71+
cardId: `card-${index}`,
72+
runId: null,
73+
tag: 'foo',
74+
plugin: PluginType.SCALARS,
75+
})),
76+
});
77+
78+
expect(dataSource.getCardInteractions().clicks.length).toEqual(10);
79+
});
80+
81+
it('only saves 10 tagFilgers', () => {
82+
dataSource.saveCardInteractions({
83+
clicks: [],
84+
tagFilters: Array.from({length: 12}).map((_, index) =>
85+
index.toString()
86+
),
87+
pins: [],
88+
});
89+
90+
expect(dataSource.getCardInteractions().tagFilters.length).toEqual(10);
91+
});
92+
});
93+
94+
describe('getCardInteractions', () => {
95+
it('returns all default state when key is not set', () => {
96+
expect(dataSource.getCardInteractions()).toEqual({
97+
tagFilters: [],
98+
pins: [],
99+
clicks: [],
100+
});
101+
});
102+
103+
it('returns previously written value', () => {
104+
dataSource.saveCardInteractions({
105+
tagFilters: ['foo'],
106+
clicks: [
107+
{cardId: '1', runId: null, tag: 'foo', plugin: PluginType.SCALARS},
108+
],
109+
pins: [
110+
{cardId: '2', runId: null, tag: 'bar', plugin: PluginType.SCALARS},
111+
],
112+
});
113+
114+
expect(dataSource.getCardInteractions()).toEqual({
115+
tagFilters: ['foo'],
116+
clicks: [
117+
{cardId: '1', runId: null, tag: 'foo', plugin: PluginType.SCALARS},
118+
],
119+
pins: [
120+
{cardId: '2', runId: null, tag: 'bar', plugin: PluginType.SCALARS},
121+
],
122+
});
123+
});
124+
});
125+
});

tensorboard/webapp/metrics/effects/BUILD

+14-2
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ package(default_visibility = ["//tensorboard:internal"])
44

55
tf_ng_module(
66
name = "effects",
7-
srcs = ["index.ts"],
7+
srcs = [
8+
"card_interaction_effects.ts",
9+
"index.ts",
10+
],
811
deps = [
912
"//tensorboard/webapp:app_state",
1013
"//tensorboard/webapp:selectors",
1114
"//tensorboard/webapp/app_routing:types",
1215
"//tensorboard/webapp/app_routing/actions",
16+
"//tensorboard/webapp/app_routing/store",
1317
"//tensorboard/webapp/core/actions",
1418
"//tensorboard/webapp/core/store",
1519
"//tensorboard/webapp/metrics:types",
1620
"//tensorboard/webapp/metrics/actions",
1721
"//tensorboard/webapp/metrics/data_source",
22+
"//tensorboard/webapp/metrics/data_source:card_interactions_data_source",
1823
"//tensorboard/webapp/metrics/store",
1924
"//tensorboard/webapp/types",
2025
"@npm//@angular/core",
@@ -27,7 +32,10 @@ tf_ng_module(
2732
tf_ts_library(
2833
name = "effects_test",
2934
testonly = True,
30-
srcs = ["metrics_effects_test.ts"],
35+
srcs = [
36+
"card_interactions_effects_test.ts",
37+
"metrics_effects_test.ts",
38+
],
3139
deps = [
3240
":effects",
3341
"//tensorboard/webapp:app_state",
@@ -37,14 +45,18 @@ tf_ts_library(
3745
"//tensorboard/webapp/app_routing:testing",
3846
"//tensorboard/webapp/app_routing:types",
3947
"//tensorboard/webapp/app_routing/actions",
48+
"//tensorboard/webapp/app_routing/store",
4049
"//tensorboard/webapp/core/actions",
4150
"//tensorboard/webapp/core/store",
4251
"//tensorboard/webapp/core/testing",
52+
"//tensorboard/webapp/metrics:internal_types",
4353
"//tensorboard/webapp/metrics:test_lib",
4454
"//tensorboard/webapp/metrics:types",
4555
"//tensorboard/webapp/metrics/actions",
4656
"//tensorboard/webapp/metrics/data_source",
57+
"//tensorboard/webapp/metrics/data_source:card_interactions_data_source",
4758
"//tensorboard/webapp/metrics/store",
59+
"//tensorboard/webapp/testing:utils",
4860
"//tensorboard/webapp/types",
4961
"//tensorboard/webapp/util:dom",
5062
"//tensorboard/webapp/webapp_data_source:http_client_testing",

0 commit comments

Comments
 (0)