8000 Add localStorage support to the tf-storage module. · jbenjos/tensorflow@5b2b591 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5b2b591

Browse files
Dandelion Manétensorflower-gardener
authored andcommitted
Add localStorage support to the tf-storage module.
Now, if you have a setting that is serialized to URIStorage, you can optionally also serialize it to localStorage. The localStorage copy will take precedence over the default value, but is always suboordinate to the URI copy. Settings that are independent of the data in a given TensorBoard (e.g. whether to show data download links, or hide outliers) are good candidates for localStorage. Settings that depend on the specific data (e.g. which runs are toggled, or which tag groups are visible) are not good candidates for localStorage. Also, wire up scalar dashboard ignoreYOutliers to URIStorage, and turn on the localStorage setting for the data_download_links option. Change: 150701322
1 parent cb9d67d commit 5b2b591

File tree

2 files changed

+114
-69
lines changed

2 files changed

+114
-69
lines changed

tensorflow/tensorboard/components/tf_scalar_dashboard/tf-scalar-dashboard.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@
210210
type: Boolean,
211211
notify: true,
212212
value: TF.URIStorage.getBooleanInitializer('_showDownloadLinks',
213-
false),
213+
false, true),
214214
observer: '_showDownloadLinksObserver'
215215
},
216216
_smoothingWeight: {
@@ -225,7 +225,8 @@
225225
},
226226
_ignoreYOutliers: {
227227
type: Boolean,
228-
value: true,
228+
value: TF.URIStorage.getBooleanInitializer('_ignoreYOutliers', true, true),
229+
observer: '_ignoreYOutliersObserver',
229230
},
230231
_xType: {
231232
type: String,
@@ -241,9 +242,11 @@
241242
return this.router.scalars;
242243
},
243244
_showDownloadLinksObserver: TF.URIStorage.getBooleanObserver(
244-
'_showDownloadLinks', false),
245+
'_showDownloadLinks', /*default=*/ false, /*useLocalStorage=*/ true),
245246
_smoothingWeightObserver: TF.URIStorage.getNumberObserver(
246247
'_smoothingWeight', 0.6),
248+
_ignoreYOutliersObserver: TF.URIStorage.getBooleanObserver(
249+
'_ignoreYOutliers', /*default=*/ true, /*useLocalStorage=*/true),
247250
_computeSmoothingEnabled: function(_smoothingWeight) {
248251
return _smoothingWeight > 0;
249252
},

tensorflow/tensorboard/components/tf_storage/storage.ts

Lines changed: 108 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ limitations under the License.
2121
* It generates URI components like: events&runPrefix=train*
2222
* which TensorBoard uses after like localhost:8000/#events&runPrefix=train*
2323
* to store state in the URI.
24+
*
25+
* It also allows saving the values to localStorage for long-term persistance.
2426
*/
2527
module TF.URIStorage {
2628
type StringDict = {[key: string]: string};
@@ -50,76 +52,79 @@ module TF.URIStorage {
5052
export let DISAMBIGUATOR = 'disambiguator';
5153

5254
/**
53-
* Return a boolean stored in the URI, given a corresponding key.
55+
* Return a string stored in URI or localStorage.
5456
* Undefined if not found.
5557
*/
56-
export function getBoolean(key: string): boolean {
57-
let items = _componentToDict(_readComponent());
58-
let item = items[key];
59-
return item === 'true' ? true : item === 'false' ? false : undefined;
58+
export function getString(key: string, useLocalStorage: boolean): string {
59+
if (useLocalStorage) {
60+
return window.localStorage.getItem(key);
61+
} else {
62+
return _componentToDict(_readComponent())[key];
63+
}
6064
}
6165

6266
/**
63-
* Store a boolean in the URI, with a corresponding key.
67+
* Set a string in URI or localStorage.
6468
*/
65-
export function setBoolean(key: string, value: boolean) {
66-
let items = _componentToDict(_readComponent());
67-
items[key] = value.toString();
68-
_writeComponent(_dictToComponent(items));
69+
export function setString(
70+
key: string, value: string, useLocalStorage: boolean) {
71+
if (useLocalStorage) {
72+
window.localStorage.setItem(key, value);
73+
} else {
74+
let items = _componentToDict(_readComponent());
75+
items[key] = value;
76+
_writeComponent(_dictToComponent(items));
77+
}
6978
}
7079

7180
/**
72-
* Return a string stored in the URI, given a corresponding key.
81+
* Return a boolean stored in stored in URI or localStorage.
7382
* Undefined if not found.
7483
*/
75-
export function getString(key: string): string {
76-
let items = _componentToDict(_readComponent());
77-
return items[key];
84+
export function getBoolean(key: string, useLocalStorage: boolean): boolean {
85+
let item = getString(key, useLocalStorage);
86+
return item === 'true' ? true : item === 'false' ? false : undefined;
7887
}
7988

8089
/**
81-
* Store a string in the URI, with a corresponding key.
90+
* Store a boolean in URI or localStorage.
8291
*/
83-
export function setString(key: string, value: string) {
84-
let items = _componentToDict(_readComponent());
85-
items[key] = value;
86-
_writeComponent(_dictToComponent(items));
92+
export function setBoolean(
93+
key: string, value: boolean, useLocalStorage = false) {
94+
setString(key, value.toString(), useLocalStorage);
8795
}
8896

8997
/**
90-
* Return a number stored in the URI, given a corresponding key.
98+
* Return a number stored in stored in URI or localStorage.
9199
* Undefined if not found.
92100
*/
93-
export function getNumber(key: string): number {
94-
let items = _componentToDict(_readComponent());
95-
return items[key] === undefined ? undefined : +items[key];
101+
export function getNumber(key: string, useLocalStorage: boolean): number {
102+
let item = getString(key, useLocalStorage);
103+
return item === undefined ? undefined : +item;
96104
}
97105

98106
/**
99-
* Store a number in the URI, with a corresponding key.
107+
* Store a number in URI or localStorage.
100108
*/
101-
export function setNumber(key: string, value: number) {
102-
let items = _componentToDict(_readComponent());
103-
items[key] = '' + value;
104-
_writeComponent(_dictToComponent(items));
109+
export function setNumber(
110+
key: string, value: number, useLocalStorage: boolean) {
111+
setString(key, '' + value, useLocalStorage);
105112
}
106113

107114
/**
108-
* Return an object stored in the URI, given a corresponding key.
115+
* Return an object stored in stored in URI or localStorage.
109116
* Undefined if not found.
110117
*/
111-
export function getObject(key: string): Object {
112-
let items = _componentToDict(_readComponent());
113-
return items[key] === undefined ? undefined : JSON.parse(atob(items[key]));
118+
export function getObject(key: string, useLocalStorage: boolean): {} {
119+
let item = getString(key, useLocalStorage);
120+
return item === undefined ? undefined : JSON.parse(atob(item));
114121
}
115122

116123
/**
117-
* Store an object in the URI, with a corresponding key.
124+
* Store an object in URI or localStorage.
118125
*/
119-
export function setObject(key: string, value: Object) {
120-
let items = _componentToDict(_readComponent());
121-
items[key] = btoa(JSON.stringify(value));
122-
_writeComponent(_dictToComponent(items));
126+
export function setObject(key: string, value: {}, useLocalStorage: boolean) {
127+
setString(key, btoa(JSON.stringify(value)), useLocalStorage);
123128
}
124129

125130
/**
@@ -129,7 +134,7 @@ module TF.URIStorage {
129134
* same propertyName.
130135
*/
131136
export function getURIStorageName(
132-
component: Object, propertyName: string): string {
137+
component: {}, propertyName: string): string {
133138
let d = component[DISAMBIGUATOR];
134139
let components = d == null ? [propertyName] : [d, propertyName];
135140
return components.join('.');
@@ -142,8 +147,10 @@ module TF.URIStorage {
142147
* (2) Sets up listener that updates Polymer property on hash change.
143148
*/
144149
export function getBooleanInitializer(
145-
propertyName: string, defaultVal: boolean): Function {
146-
return _getInitializer(getBoolean, propertyName, defaultVal);
150+
propertyName: string, defaultVal: boolean,
151+
useLocalStorage = false): Function {
152+
return _getInitializer(
153+
getBoolean, propertyName, defaultVal, useLocalStorage);
147154
}
148155

149156
/**
@@ -153,8 +160,10 @@ module TF.URIStorage {
153160
* (2) Sets up listener that updates Polymer property on hash change.
154161
*/
155162
export function getStringInitializer(
156-
propertyName: string, defaultVal: string): Function {
157-
return _getInitializer(getString, propertyName, defaultVal);
163+
propertyName: string, defaultVal: string,
164+
useLocalStorage = false): Function {
165+
return _getInitializer(
166+
getString, propertyName, defaultVal, useLocalStorage);
158167
}
159168

160169
/**
@@ -164,8 +173,10 @@ module TF.URIStorage {
164173
* (2) Sets up listener that updates Polymer property on hash change.
165174
*/
166175
export function getNumberInitializer(
167-
propertyName: string, defaultVal: number): Function {
168-
return _getInitializer(getNumber, propertyName, defaultVal);
176+
propertyName: string, defaultVal: number,
177+
useLocalStorage = false): Function {
178+
return _getInitializer(
179+
getNumber, propertyName, defaultVal, useLocalStorage);
169180
}
170181

171182
/**
@@ -177,42 +188,50 @@ module TF.URIStorage {
177188
* Generates a deep clone of the defaultVal to avoid mutation issues.
178189
*/
179190
export function getObjectInitializer(
180-
propertyName: string, defaultVal: Object): Function {
181-
return _getInitializer(getObject, propertyName, defaultVal);
191+
propertyName: string, defaultVal: {}, useLocalStorage = false): Function {
192+
return _getInitializer(
193+
getObject, propertyName, defaultVal, useLocalStorage);
182194
}
183195

184196
/**
185197
* Return a function that updates URIStorage when a string property changes.
186198
*/
187199
export function getBooleanObserver(
188-
propertyName: string, defaultVal: boolean): Function {
189-
return _getObserver(getBoolean, setBoolean, propertyName, defaultVal);
200+
propertyName: string, defaultVal: boolean,
201+
useLocalStorage = false): Function {
202+
return _getObserver(
203+
getBoolean, setBoolean, propertyName, defaultVal, useLocalStorage);
190204
}
191205

192206
/**
193207
* Return a function that updates URIStorage when a string property changes.
194208
*/
195209
export function getStringObserver(
196-
propertyName: string, defaultVal: string): Function {
197-
return _getObserver(getString, setString, propertyName, defaultVal);
210+
propertyName: string, defaultVal: string,
211+
useLocalStorage = false): Function {
212+
return _getObserver(
213+
getString, setString, propertyName, defaultVal, useLocalStorage);
198214
}
199215

200216
/**
201217
* Return a function that updates URIStorage when a number property changes.
202218
*/
203219
export function getNumberObserver(
204-
propertyName: string, defaultVal: number): Function {
205-
return _getObserver(getNumber, setNumber, propertyName, defaultVal);
220+
propertyName: string, defaultVal: number,
221+
useLocalStorage = false): Function {
222+
return _getObserver(
223+
getNumber, setNumber, propertyName, defaultVal, useLocalStorage);
206224
}
207225

208226
/**
209227
* Return a function that updates URIStorage when an object property changes.
210228
* Generates a deep clone of the defaultVal to avoid mutation issues.
211229
*/
212230
export function getObjectObserver(
213-
propertyName: string, defaultVal: Object): Function {
231+
propertyName: string, defaultVal: {}, useLocalStorage = false): Function {
214232
let clone = _.cloneDeep(defaultVal);
215-
return _getObserver(getObject, setObject, propertyName, clone);
233+
return _getObserver(
234+
getObject, setObject, propertyName, clone, useLocalStorage);
216235
}
217236

218237
/**
@@ -292,7 +311,8 @@ module TF.URIStorage {
292311
* (2) Sets up listener that updates Polymer property on hash change.
293312
*/
294313
function _getInitializer<T>(
295-
get: (name: string) => T, propertyName: string, defaultVal: T): Function {
314+
get: (name: string, useLocalStorage: boolean) => T, propertyName: string,
315+
defaultVal: T, useLocalStorage): Function {
296316
return function() {
297317
let URIStorageName = getURIStorageName(this, propertyName);
298318
// setComponentValue will be called every time the hash changes, and is
@@ -301,23 +321,39 @@ module TF.URIStorage {
301321
// It is important that this function does not re-assign needlessly,
302322
// to avoid Polymer observer churn.
303323
let setComponentValue = () => {
304-
let uriValue = get(URIStorageName);
324+
let uriValue = get(URIStorageName, false);
305325
let currentValue = this[propertyName];
306326
// if uriValue is undefined, we will ensure that the property has the
307327
// default value
308328
if (uriValue === undefined) {
309-
if (!_.isEqual(currentValue, defaultVal)) {
329+
let valueToSet: T;
330+
// if we are using localStorage, we will set the value to the value
331+
// from localStorage. Then, the corresponding observer will proxy
332+
// the localStorage value into URI storage.
333+
// in this way, localStorage takes precedence over the default val
334+
// but not over the URI value.
335+
if (useLocalStorage) {
336+
let useLocalStorageValue = get(URIStorageName, true);
337+
valueToSet = useLocalStorageValue === undefined ?
338+
defaultVal :
339+
useLocalStorageValue;
340+
} else {
341+
valueToSet = defaultVal;
342+
}
343+
if (!_.isEqual(currentValue, valueToSet)) {
310344
// If we don't have an explicit URI value, then we need to ensure
311345
// the property value is equal to the default value.
312346
// We will assign a clone rather than the canonical default, because
313347
// the component receiving this property may mutate it, and we need
314348
// to keep a pristine copy of the default.
315-
this[propertyName] = _.clone(defaultVal);
349+
this[propertyName] = _.clone(valueToSet);
316350
}
317351
// In this case, we have an explicit URI value, so we will ensure that
318352
// the component has an equivalent value.
319-
} else if (!_.isEqual(uriValue, currentValue)) {
320-
this[propertyName] = uriValue;
353+
} else {
354+
if (!_.isEqual(uriValue, currentValue)) {
355+
this[propertyName] = uriValue;
356+
}
321357
}
322358
};
323359
// Set the value on the property.
@@ -331,16 +367,22 @@ module TF.URIStorage {
331367
* Return a function that updates URIStorage when a property changes.
332368
*/
333369
function _getObserver<T>(
334-
get: (name: string) => T, set: (name: string, newVal: T) => void,
335-
propertyName: string, defaultVal: T): Function {
370+
get: (name: string, useLocalStorage: boolean) => T,
371+
set: (name: string, newVal: T, useLocalStorage: boolean) => void,
372+
propertyName: string, defaultVal: T, useLocalStorage: boolean): Function {
336373
return function() {
337374
let URIStorageName = getURIStorageName(this, propertyName);
338375
let newVal = this[propertyName];
339-
if (!_.isEqual(newVal, get(URIStorageName))) {
376+
// if this is a localStorage property, we always synchronize the value
377+
// in localStorage to match the one currently in the URI.
378+
if (useLocalStorage) {
379+
set(URIStorageName, newVal, true);
380+
}
381+
if (!_.isEqual(newVal, get(URIStorageName, false))) {
340382
if (_.isEqual(newVal, defaultVal)) {
341-
_unset(URIStorageName);
383+
_unsetFromURI(URIStorageName);
342384
} else {
343-
set(URIStorageName, newVal);
385+
set(URIStorageName, newVal, false);
344386
}
345387
}
346388
};
@@ -349,7 +391,7 @@ module TF.URIStorage {
349391
/**
350392
* Delete a key from the URI.
351393
*/
352-
function _unset(key) {
394+
function _unsetFromURI(key) {
353395
let items = _componentToDict(_readComponent());
354396
delete items[key];
355397
_writeComponent(_dictToComponent(items));

0 commit comments

Comments
 (0)
0