function.ts 12.7 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';
Lijiaoa's avatar
Lijiaoa committed
6
import { MetricDataRecord, FinalType, TableObj, 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
    };
};

191
const filterByStatus = (item: TableObj): boolean => {
192
193
194
    return item.status === 'SUCCEEDED';
};

195
// a waittiong trial may havn't start time
196
const filterDuration = (item: TableObj): boolean => {
197
198
199
    return item.status !== 'WAITING';
};

Lijiao's avatar
Lijiao committed
200
const downFile = (content: string, fileName: string): void => {
v-liguo's avatar
v-liguo committed
201
202
203
204
205
206
207
208
209
210
211
    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');
212
        downTag.addEventListener('click', function () {
v-liguo's avatar
v-liguo committed
213
214
215
            downTag.download = fileName;
            downTag.href = URL.createObjectURL(file);
        });
chicm-ms's avatar
chicm-ms committed
216
        const eventMouse = document.createEvent('MouseEvents');
v-liguo's avatar
v-liguo committed
217
218
219
220
221
        eventMouse.initEvent('click', false, false);
        downTag.dispatchEvent(eventMouse);
    }
};

222
223
224
225
226
// function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
function formatTimestamp(timestamp?: number, placeholder?: string): string {
    if (placeholder === undefined) {
        placeholder = 'N/A';
    }
227
228
229
    return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
}

230
231
232
233
234
235
236
237
function expformatTimestamp(timestamp: number | string): string {
    if (typeof timestamp === 'number') {
        return new Date(timestamp).toLocaleString('en-US');
    } else {
        return 'N/A';
    }
}

238
function metricAccuracy(metric: MetricDataRecord): number {
chicm-ms's avatar
chicm-ms committed
239
    const data = parseMetrics(metric.data);
240
241
242
243
244
245
    // return typeof data === 'number' ? data : NaN;
    if (typeof data === 'number') {
        return data;
    } else {
        return data.default;
    }
246
247
248
249
}

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

Lijiaoa's avatar
Lijiaoa committed
253
254
255
256
257
258
259
260
function formatComplexTypeValue(value: any): string | number {
    if (['number', 'string'].includes(typeof value)) {
        return value;
    } else {
        return value.toString();
    }
}

261
262
263
264
function isManagerExperimentPage(): boolean {
    return location.pathname.indexOf('experiment') === -1 ? false : true;
}

265
266
267
268
269
270
271
function caclMonacoEditorHeight(height): number {
    // [Search space 56px] + [marginBottom 18px] +
    // button[height: 32px, marginTop: 45px, marginBottom: 7px]
    // panel own padding-bottom: 20px;
    return height - 178;
}

272
273
function copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): any {
    const key = columnKey as keyof T;
274
    return items.slice(0).sort(function (a: T, b: T): any {
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
        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
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
326
327
328
329
330
331
332
333
334

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;
}
335
336
337
338
339
340
341
342
343
344
345
346
347

// 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;
};

348
349
350
351
352
// retiarii experiment parameters is in field `mutation_summary`
const reformatRetiariiParameter = (parameters: any): {} => {
    return RETIARIIPARAMETERS in parameters ? parameters[RETIARIIPARAMETERS] : parameters;
};

Lijiaoa's avatar
Lijiaoa committed
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
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[] = [];
    if (intermediateDialogTrial!.intermediateMetrics !== undefined && intermediateDialogTrial!.intermediateMetrics[0]) {
        const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediateMetrics[0].data);
        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;
};

401
export {
402
    getPrefix,
403
404
    convertTime,
    convertDuration,
405
    convertTimeAsUnit,
406
407
408
409
410
411
412
413
    getFinalResult,
    getFinal,
    downFile,
    intermediateGraphOption,
    filterByStatus,
    filterDuration,
    formatAccuracy,
    formatTimestamp,
414
    expformatTimestamp,
415
416
417
418
419
    metricAccuracy,
    parseMetrics,
    isArrayType,
    requestAxios,
    isNaNorInfinity,
420
    formatComplexTypeValue,
421
    isManagerExperimentPage,
422
    caclMonacoEditorHeight,
Lijiaoa's avatar
Lijiaoa committed
423
424
    copyAndSort,
    disableTensorboard,
425
    getTensorboardMenu,
426
    parametersType,
Lijiaoa's avatar
Lijiaoa committed
427
428
429
    reformatRetiariiParameter,
    getIntermediateAllKeys,
    _inferColumnTitle
430
};