function.ts 12.5 KB
Newer Older
chicm-ms's avatar
chicm-ms committed
1
import * as JSON5 from 'json5';
Lijiao's avatar
Lijiao committed
2
import axios from 'axios';
Lijiaoa's avatar
Lijiaoa committed
3
import { IContextualMenuProps } from '@fluentui/react';
Lijiaoa's avatar
Lijiaoa committed
4
import { RETIARIIPARAMETERS } from './const';
5
import { EXPERIMENT } from './datamodel';
6
import { MetricDataRecord, FinalType, Tensorboard } from './interface';
Lijiao's avatar
Lijiao committed
7

8
9
10
function getPrefix(): string | undefined {
    const pathName = window.location.pathname;
    let newPathName = pathName;
11
12
13
14
15
16
    const pathArr: string[] = ['/oview', '/detail', '/experiment'];
    pathArr.forEach(item => {
        if (pathName.endsWith(item)) {
            newPathName = pathName.replace(item, '');
        }
    });
17
18
19
20
21
22
23
    let result = newPathName === '' || newPathName === '/' ? undefined : newPathName;
    if (result !== undefined) {
        if (result.endsWith('/')) {
            result = result.slice(0, result.length - 1);
        }
    }
    return result;
24
25
}

26
async function requestAxios(url: string): Promise<any> {
Lijiaoa's avatar
Lijiaoa committed
27
28
29
30
31
32
33
34
35
36
37
38
    const response = await axios.get(url);
    if (response.status === 200) {
        if (response.data.error !== undefined) {
            throw new Error(`API ${url} ${response.data.error}`);
        } else {
            return response.data as any;
        }
    } else {
        throw new Error(`API ${url} ${response.status} error`);
    }
}

Lijiao's avatar
Lijiao committed
39
const convertTime = (num: number): string => {
40
41
42
    if (num <= 0) {
        return '0';
    }
Lijiao's avatar
Lijiao committed
43
44
45
46
    if (num % 3600 === 0) {
        return num / 3600 + 'h';
    } else {
        const hour = Math.floor(num / 3600);
47
        const min = Math.floor((num / 60) % 60);
Lijiao's avatar
Lijiao committed
48
49
50
51
52
        return hour > 0 ? `${hour}h ${min}min` : `${min}min`;
    }
};

// trial's duration, accurate to seconds for example 10min 30s
53
54
55
56
57
58
const convertDuration = (seconds: number): string => {
    let str = '';

    const d = Math.floor(seconds / (24 * 3600));
    if (d > 0) {
        str += `${d}d `;
59
    }
60
61
62
63
64
    seconds -= d * 24 * 3600;

    const h = Math.floor(seconds / 3600);
    if (h > 0) {
        str += `${h}h `;
65
    }
66
67
68
69
70
    seconds -= h * 3600;

    const m = Math.floor(seconds / 60);
    if (m > 0) {
        str += `${m}m `;
71
    }
72
73
    seconds -= m * 60;

Lijiaoa's avatar
Lijiaoa committed
74
75
    // don't show `0s`
    if (Math.floor(seconds) > 0) {
76
        str += `${Math.floor(seconds)}s`;
Lijiao's avatar
Lijiao committed
77
    }
78
    return str ? str : '0s';
Lijiao's avatar
Lijiao committed
79
80
};

81
82
83
84
85
86
87
88
89
90
91
92
// according the unit(d,h,m) to convert duration
function convertTimeAsUnit(unit: string, value: number): number {
    let divisor = 1;
    if (unit === 'h') {
        divisor = 3600;
    } else if (unit === 'm') {
        divisor = 60;
    } else {
        divisor = 24 * 3600;
    }
    return value / divisor;
}
93
function parseMetrics(metricData: string): any {
Lijiaoa's avatar
Lijiaoa committed
94
    if (metricData.includes('NaN') || metricData.includes('Infinity')) {
95
96
97
98
99
100
        return JSON5.parse(JSON5.parse(metricData));
    } else {
        return JSON.parse(JSON.parse(metricData));
    }
}

101
102
const isArrayType = (list: any): boolean | undefined => {
    return Array.isArray(list);
103
};
104

Lijiao's avatar
Lijiao committed
105
// get final result value
106
// draw Accuracy point graph
Lijiao's avatar
Lijiao committed
107
const getFinalResult = (final?: MetricDataRecord[]): number => {
Lijiao's avatar
Lijiao committed
108
109
110
    let acc;
    let showDefault = 0;
    if (final) {
111
        acc = parseMetrics(final[final.length - 1].data);
112
        if (typeof acc === 'object' && !isArrayType(acc)) {
Lijiao's avatar
Lijiao committed
113
114
115
            if (acc.default) {
                showDefault = acc.default;
            }
116
        } else if (typeof acc === 'number') {
Lijiao's avatar
Lijiao committed
117
            showDefault = acc;
118
119
        } else {
            showDefault = NaN;
Lijiao's avatar
Lijiao committed
120
121
122
123
124
125
        }
        return showDefault;
    } else {
        return 0;
    }
};
126

Lijiaoa's avatar
Lijiaoa committed
127
128
129
130
function isNaNorInfinity(val: number): boolean {
    return Object.is(val, NaN) || Object.is(val, Infinity);
}

131
// get final result value // acc obj
132
const getFinal = (final?: MetricDataRecord[]): FinalType | undefined => {
133
134
    let showDefault: FinalType;
    if (final) {
135
        showDefault = parseMetrics(final[final.length - 1].data);
136
        if (typeof showDefault === 'number') {
137
            if (!isNaNorInfinity(showDefault)) {
Lijiaoa's avatar
Lijiaoa committed
138
                return { default: showDefault };
139
            }
140
141
142
        } else if (isArrayType(showDefault)) {
            // not support final type
            return undefined;
143
            // eslint-disable-next-line no-prototype-builtins
144
145
        } else if (typeof showDefault === 'object' && showDefault.hasOwnProperty('default')) {
            return showDefault;
146
147
148
149
150
151
        }
    } else {
        return undefined;
    }
};

Lijiao's avatar
Lijiao committed
152
// detail page table intermediate button
Lijiao's avatar
Lijiao committed
153
const intermediateGraphOption = (intermediateArr: number[], id: string): any => {
Lijiao's avatar
Lijiao committed
154
155
156
157
158
159
160
161
162
163
164
    const sequence: number[] = [];
    const lengthInter = intermediateArr.length;
    for (let i = 1; i <= lengthInter; i++) {
        sequence.push(i);
    }
    return {
        title: {
            text: id,
            left: 'center',
            textStyle: {
                fontSize: 16,
165
                color: '#333'
Lijiao's avatar
Lijiao committed
166
167
168
169
170
171
            }
        },
        tooltip: {
            trigger: 'item'
        },
        xAxis: {
172
            // name: '#Intermediate result',
Lijiao's avatar
Lijiao committed
173
174
175
            data: sequence
        },
        yAxis: {
Lijiao's avatar
Lijiao committed
176
            name: 'Default metric',
Lijiao's avatar
Lijiao committed
177
            type: 'value',
Lijiaoa's avatar
Lijiaoa committed
178
179
            data: intermediateArr,
            scale: true
Lijiao's avatar
Lijiao committed
180
        },
181
182
183
184
185
186
187
        series: [
            {
                symbolSize: 6,
                type: 'scatter',
                data: intermediateArr
            }
        ]
Lijiao's avatar
Lijiao committed
188
189
190
    };
};

Lijiao's avatar
Lijiao committed
191
const downFile = (content: string, fileName: string): void => {
v-liguo's avatar
v-liguo committed
192
193
194
195
196
197
198
199
200
201
202
    const aTag = document.createElement('a');
    const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
    const file = new Blob([content], { type: 'application/json' });
    aTag.download = fileName;
    aTag.href = URL.createObjectURL(file);
    aTag.click();
    if (!isEdge) {
        URL.revokeObjectURL(aTag.href);
    }
    if (navigator.userAgent.indexOf('Firefox') > -1) {
        const downTag = document.createElement('a');
203
        downTag.addEventListener('click', function () {
v-liguo's avatar
v-liguo committed
204
205
206
            downTag.download = fileName;
            downTag.href = URL.createObjectURL(file);
        });
chicm-ms's avatar
chicm-ms committed
207
        const eventMouse = document.createEvent('MouseEvents');
v-liguo's avatar
v-liguo committed
208
209
210
211
212
        eventMouse.initEvent('click', false, false);
        downTag.dispatchEvent(eventMouse);
    }
};

213
214
215
216
217
// function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
function formatTimestamp(timestamp?: number, placeholder?: string): string {
    if (placeholder === undefined) {
        placeholder = 'N/A';
    }
218
219
220
    return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
}

221
222
223
224
225
226
227
228
function expformatTimestamp(timestamp: number | string): string {
    if (typeof timestamp === 'number') {
        return new Date(timestamp).toLocaleString('en-US');
    } else {
        return 'N/A';
    }
}

229
function metricAccuracy(metric: MetricDataRecord): number {
chicm-ms's avatar
chicm-ms committed
230
    const data = parseMetrics(metric.data);
231
232
233
234
235
236
    // return typeof data === 'number' ? data : NaN;
    if (typeof data === 'number') {
        return data;
    } else {
        return data.default;
    }
237
238
239
240
}

function formatAccuracy(accuracy: number): string {
    // TODO: how to format NaN?
241
    return accuracy.toFixed(6).replace(/0+$/, '').replace(/\.$/, '');
242
243
}

Lijiaoa's avatar
Lijiaoa committed
244
245
246
247
248
249
250
251
function formatComplexTypeValue(value: any): string | number {
    if (['number', 'string'].includes(typeof value)) {
        return value;
    } else {
        return value.toString();
    }
}

252
253
254
255
function isManagerExperimentPage(): boolean {
    return location.pathname.indexOf('experiment') === -1 ? false : true;
}

256
257
258
259
260
261
262
function caclMonacoEditorHeight(height): number {
    // [Search space 56px] + [marginBottom 18px] +
    // button[height: 32px, marginTop: 45px, marginBottom: 7px]
    // panel own padding-bottom: 20px;
    return height - 178;
}

263
264
function copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): any {
    const key = columnKey as keyof T;
265
    return items.slice(0).sort(function (a: T, b: T): any {
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
        if (
            a[key] === undefined ||
            Object.is(a[key], NaN) ||
            Object.is(a[key], Infinity) ||
            Object.is(a[key], -Infinity) ||
            typeof a[key] === 'object'
        ) {
            return 1;
        }
        if (
            b[key] === undefined ||
            Object.is(b[key], NaN) ||
            Object.is(b[key], Infinity) ||
            Object.is(b[key], -Infinity) ||
            typeof b[key] === 'object'
        ) {
            return -1;
        }
        return (isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1;
    });
}
Lijiaoa's avatar
Lijiaoa committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

function disableTensorboard(selectedRowIds: string[], queryTensorboardList: Tensorboard[]): boolean {
    let flag = true;

    if (selectedRowIds.length !== 0) {
        flag = false;
    }

    if (selectedRowIds.length === 0 && queryTensorboardList.length !== 0) {
        flag = false;
    }

    return flag;
}

function getTensorboardMenu(queryTensorboardList: Tensorboard[], stopFunc, seeDetailFunc): IContextualMenuProps {
    const result: Array<object> = [];
    if (queryTensorboardList.length !== 0) {
        result.push({
            key: 'delete',
            text: 'Stop all tensorBoard',
            className: 'clearAll',
            onClick: stopFunc
        });
        queryTensorboardList.forEach(item => {
            result.push({
                key: item.id,
                text: `${item.id}`,
                className: `CommandBarButton-${item.status}`,
                onClick: (): void => seeDetailFunc(item)
            });
        });
    }
    const tensorboardMenu: IContextualMenuProps = {
        items: result.reverse() as any
    };

    return tensorboardMenu;
}
326
327
328
329
330
331
332
333
334
335
336
337
338

// search space type map list: now get type from search space
const parametersType = (): Map<string, string> => {
    const parametersTypeMap = new Map();
    const trialParameterlist = Object.keys(EXPERIMENT.searchSpace);

    trialParameterlist.forEach(item => {
        parametersTypeMap.set(item, typeof EXPERIMENT.searchSpace[item]._value[0]);
    });

    return parametersTypeMap;
};

339
340
341
342
343
// retiarii experiment parameters is in field `mutation_summary`
const reformatRetiariiParameter = (parameters: any): {} => {
    return RETIARIIPARAMETERS in parameters ? parameters[RETIARIIPARAMETERS] : parameters;
};

Lijiaoa's avatar
Lijiaoa committed
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
function _inferColumnTitle(columnKey: string): string {
    if (columnKey === 'sequenceId') {
        return 'Trial No.';
    } else if (columnKey === 'id') {
        return 'ID';
    } else if (columnKey === 'intermediateCount') {
        return 'Intermediate results (#)';
    } else if (columnKey === 'message') {
        return 'Message';
    } else if (columnKey.startsWith('space/')) {
        return columnKey.split('/', 2)[1] + ' (space)';
    } else if (columnKey === 'latestAccuracy') {
        return 'Default metric'; // to align with the original design
    } else if (columnKey.startsWith('metric/')) {
        return columnKey.split('/', 2)[1] + ' (metric)';
    } else if (columnKey.startsWith('_')) {
        return columnKey;
    } else {
        // camel case to verbose form
        const withSpace = columnKey.replace(/[A-Z]/g, letter => ` ${letter.toLowerCase()}`);
        return withSpace.charAt(0).toUpperCase() + withSpace.slice(1);
    }
}

const getIntermediateAllKeys = (intermediateDialogTrial: any): string[] => {
    let intermediateAllKeysList: string[] = [];
370
371
    if (intermediateDialogTrial!.intermediates !== undefined && intermediateDialogTrial!.intermediates[0]) {
        const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediates[0].data);
Lijiaoa's avatar
Lijiaoa committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
        if (parsedMetric !== undefined && typeof parsedMetric === 'object') {
            const allIntermediateKeys: string[] = [];
            // just add type=number keys
            for (const key in parsedMetric) {
                if (typeof parsedMetric[key] === 'number') {
                    allIntermediateKeys.push(key);
                }
            }
            intermediateAllKeysList = allIntermediateKeys;
        }
    }

    if (intermediateAllKeysList.includes('default') && intermediateAllKeysList[0] !== 'default') {
        intermediateAllKeysList = intermediateAllKeysList.filter(item => item !== 'default');
        intermediateAllKeysList.unshift('default');
    }

    return intermediateAllKeysList;
};

392
export {
393
    getPrefix,
394
395
    convertTime,
    convertDuration,
396
    convertTimeAsUnit,
397
398
399
400
401
402
    getFinalResult,
    getFinal,
    downFile,
    intermediateGraphOption,
    formatAccuracy,
    formatTimestamp,
403
    expformatTimestamp,
404
405
406
407
408
    metricAccuracy,
    parseMetrics,
    isArrayType,
    requestAxios,
    isNaNorInfinity,
409
    formatComplexTypeValue,
410
    isManagerExperimentPage,
411
    caclMonacoEditorHeight,
Lijiaoa's avatar
Lijiaoa committed
412
413
    copyAndSort,
    disableTensorboard,
414
    getTensorboardMenu,
415
    parametersType,
Lijiaoa's avatar
Lijiaoa committed
416
417
418
    reformatRetiariiParameter,
    getIntermediateAllKeys,
    _inferColumnTitle
419
};