function.ts 11.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';
4
import { MANAGER_IP, 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
74
75
    seconds -= m * 60;

    if (seconds > 0) {
        str += `${Math.floor(seconds)}s`;
Lijiao's avatar
Lijiao committed
76
    }
77
    return str ? str : '0s';
Lijiao's avatar
Lijiao committed
78
79
};

80
81
82
83
84
85
86
87
88
89
90
91
// 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;
}
92
function parseMetrics(metricData: string): any {
Lijiaoa's avatar
Lijiaoa committed
93
    if (metricData.includes('NaN') || metricData.includes('Infinity')) {
94
95
96
97
98
99
        return JSON5.parse(JSON5.parse(metricData));
    } else {
        return JSON.parse(JSON.parse(metricData));
    }
}

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

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

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

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

Lijiao's avatar
Lijiao committed
151
// detail page table intermediate button
Lijiao's avatar
Lijiao committed
152
const intermediateGraphOption = (intermediateArr: number[], id: string): any => {
Lijiao's avatar
Lijiao committed
153
154
155
156
157
158
159
160
161
162
163
    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,
164
                color: '#333'
Lijiao's avatar
Lijiao committed
165
166
167
168
169
170
            }
        },
        tooltip: {
            trigger: 'item'
        },
        xAxis: {
171
            // name: '#Intermediate result',
Lijiao's avatar
Lijiao committed
172
173
174
            data: sequence
        },
        yAxis: {
Lijiao's avatar
Lijiao committed
175
            name: 'Default metric',
Lijiao's avatar
Lijiao committed
176
            type: 'value',
Lijiaoa's avatar
Lijiaoa committed
177
178
            data: intermediateArr,
            scale: true
Lijiao's avatar
Lijiao committed
179
        },
180
181
182
183
184
185
186
        series: [
            {
                symbolSize: 6,
                type: 'scatter',
                data: intermediateArr
            }
        ]
Lijiao's avatar
Lijiao committed
187
188
189
190
    };
};

// kill job
Lijiao's avatar
Lijiao committed
191
const killJob = (key: number, id: string, status: string, updateList?: Function): void => {
Lijiao's avatar
Lijiao committed
192
193
194
195
196
197
198
199
    axios(`${MANAGER_IP}/trial-jobs/${id}`, {
        method: 'DELETE',
        headers: {
            'Content-Type': 'application/json;charset=utf-8'
        }
    })
        .then(res => {
            if (res.status === 200) {
200
201
                // TODO: use Message.txt to tooltip
                alert('Cancel the job successfully');
Lijiao's avatar
Lijiao committed
202
                // render the table
203
                if (updateList) {
204
                    updateList(); // FIXME
205
                }
Lijiao's avatar
Lijiao committed
206
            } else {
207
                alert('fail to cancel the job');
Lijiao's avatar
Lijiao committed
208
209
210
211
212
            }
        })
        .catch(error => {
            if (error.response.status === 500) {
                if (error.response.data.error) {
Lijiaoa's avatar
Lijiaoa committed
213
                    alert(error.response.data.error);
Lijiao's avatar
Lijiao committed
214
                } else {
Lijiaoa's avatar
Lijiaoa committed
215
                    alert('500 error, fail to cancel the job');
Lijiao's avatar
Lijiao committed
216
217
218
219
220
                }
            }
        });
};

221
const filterByStatus = (item: TableObj): boolean => {
222
223
224
    return item.status === 'SUCCEEDED';
};

225
// a waittiong trial may havn't start time
226
const filterDuration = (item: TableObj): boolean => {
227
228
229
    return item.status !== 'WAITING';
};

Lijiao's avatar
Lijiao committed
230
const downFile = (content: string, fileName: string): void => {
v-liguo's avatar
v-liguo committed
231
232
233
234
235
236
237
238
239
240
241
    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');
242
        downTag.addEventListener('click', function () {
v-liguo's avatar
v-liguo committed
243
244
245
            downTag.download = fileName;
            downTag.href = URL.createObjectURL(file);
        });
chicm-ms's avatar
chicm-ms committed
246
        const eventMouse = document.createEvent('MouseEvents');
v-liguo's avatar
v-liguo committed
247
248
249
250
251
        eventMouse.initEvent('click', false, false);
        downTag.dispatchEvent(eventMouse);
    }
};

252
253
254
255
256
// function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
function formatTimestamp(timestamp?: number, placeholder?: string): string {
    if (placeholder === undefined) {
        placeholder = 'N/A';
    }
257
258
259
    return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
}

260
261
262
263
264
265
266
267
function expformatTimestamp(timestamp: number | string): string {
    if (typeof timestamp === 'number') {
        return new Date(timestamp).toLocaleString('en-US');
    } else {
        return 'N/A';
    }
}

268
function metricAccuracy(metric: MetricDataRecord): number {
chicm-ms's avatar
chicm-ms committed
269
    const data = parseMetrics(metric.data);
270
271
272
273
274
275
    // return typeof data === 'number' ? data : NaN;
    if (typeof data === 'number') {
        return data;
    } else {
        return data.default;
    }
276
277
278
279
}

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

Lijiaoa's avatar
Lijiaoa committed
283
284
285
286
287
288
289
290
function formatComplexTypeValue(value: any): string | number {
    if (['number', 'string'].includes(typeof value)) {
        return value;
    } else {
        return value.toString();
    }
}

291
292
293
294
function isManagerExperimentPage(): boolean {
    return location.pathname.indexOf('experiment') === -1 ? false : true;
}

295
296
297
298
299
300
301
function caclMonacoEditorHeight(height): number {
    // [Search space 56px] + [marginBottom 18px] +
    // button[height: 32px, marginTop: 45px, marginBottom: 7px]
    // panel own padding-bottom: 20px;
    return height - 178;
}

302
303
function copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): any {
    const key = columnKey as keyof T;
304
    return items.slice(0).sort(function (a: T, b: T): any {
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
        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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

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;
}
365
366
367
368
369
370
371
372
373
374
375
376
377

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

378
379
380
381
382
// retiarii experiment parameters is in field `mutation_summary`
const reformatRetiariiParameter = (parameters: any): {} => {
    return RETIARIIPARAMETERS in parameters ? parameters[RETIARIIPARAMETERS] : parameters;
};

383
export {
384
    getPrefix,
385
386
    convertTime,
    convertDuration,
387
    convertTimeAsUnit,
388
389
390
391
392
393
394
395
396
    getFinalResult,
    getFinal,
    downFile,
    intermediateGraphOption,
    killJob,
    filterByStatus,
    filterDuration,
    formatAccuracy,
    formatTimestamp,
397
    expformatTimestamp,
398
399
400
401
402
    metricAccuracy,
    parseMetrics,
    isArrayType,
    requestAxios,
    isNaNorInfinity,
403
    formatComplexTypeValue,
404
    isManagerExperimentPage,
405
    caclMonacoEditorHeight,
Lijiaoa's avatar
Lijiaoa committed
406
407
    copyAndSort,
    disableTensorboard,
408
    getTensorboardMenu,
409
410
    parametersType,
    reformatRetiariiParameter
411
};