forked from tensorflow/tensorboard
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.ts
103 lines (97 loc) · 3.89 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
export interface TagInfo {
displayName: string;
description: string;
}
/**
* Given many occurrences of tag info for a particular tag across
* multiple runs, create a representative info object. This is useful
* for plugins that display just one visualization per tag, instead of
* one per run--tag combination: each run--tag combination can have its
* own display name or description, so there is a dimension mismatch. We
* reconcile this as follows:
*
* - We only show a display name if all runs agree. Otherwise, or if
* there are no runs, we use the provided `defaultDisplayName`.
*
* - If all runs agree on a description, we use it. Otherwise,
* we concatenate all descriptions, annotating which ones
* came from which run, and display them in a list.
*
* NOTE: Per TensorBoard convention, we assume that the provided
* `description`s have sanitized HTML and are safe to render into the
* DOM, while the `displayName` may be an arbitrary string. The output
* of this function respects this convention as well.
*/
export function aggregateTagInfo(
runToTagInfo: {
[run: string]: TagInfo;
},
defaultDisplayName: string
): TagInfo {
let unanimousDisplayName: string | null | undefined = undefined;
const descriptionToRuns: {
[description: string]: string[];
} = {};
Object.keys(runToTagInfo).forEach((run) => {
const info = runToTagInfo[run];
if (unanimousDisplayName === undefined) {
unanimousDisplayName = info.displayName;
}
if (unanimousDisplayName !== info.displayName) {
unanimousDisplayName = null;
}
if (descriptionToRuns[info.description] === undefined) {
descriptionToRuns[info.description] = [];
}
descriptionToRuns[info.description].push(run);
});
const displayName =
unanimousDisplayName != null ? unanimousDisplayName : defaultDisplayName;
const description = (() => {
const descriptions = Object.keys(descriptionToRuns);
if (descriptions.length === 0) {
return '';
} else if (descriptions.length === 1) {
return descriptions[0];
} else {
const items = descriptions.map((description) => {
const runs = descriptionToRuns[description].map((run) => {
// We're splicing potentially unsafe display names into
// sanitized descriptions, so we need to sanitize them.
const safeRun = run
.replace(/</g, '<')
.replace(/>/g, '>') // for symmetry :-)
.replace(/&/g, '&');
return `<code>${safeRun}</code>`;
});
const joined =
runs.length > 2
? runs.slice(0, runs.length - 1).join(', ') +
', and ' +
runs[runs.length - 1]
: runs.join(' and ');
const runNoun = ngettext(runs.length, 'run', 'runs');
return `<li><p>For ${runNoun} ${joined}:</p>${description}</li>`;
});
const prefix = '<p><strong>Multiple descriptions:</strong></p>';
return `${prefix}<ul>${items.join('')}</ul>`;
}
})();
return {displayName, description};
}
function ngettext(k: number, enSingular: string, enPlural: string): string {
// Potential extension point for proper i18n infrastructure, if we
// ever implement it.
return k === 1 ? enSingular : enPlural;
}