function.ts 11.2 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';
Lijiao's avatar
Lijiao committed
4
import { MANAGER_IP } 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
11
12
13
14
15
16
17
18
function getPrefix(): string | undefined {
    const pathName = window.location.pathname;
    let newPathName = pathName;

    if (pathName.endsWith('/oview') || pathName.endsWith('/detail') || pathName.endsWith('/experiment')) {
        newPathName = pathName.replace('/oview' || '/detail' || '/experiment', '');
    }

    return newPathName === '' ? undefined : newPathName;
}

19
async function requestAxios(url: string): Promise<any> {
Lijiaoa's avatar
Lijiaoa committed
20
21
22
23
24
25
26
27
28
29
30
31
    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
32
const convertTime = (num: number): string => {
33
34
35
    if (num <= 0) {
        return '0';
    }
Lijiao's avatar
Lijiao committed
36
37
38
39
    if (num % 3600 === 0) {
        return num / 3600 + 'h';
    } else {
        const hour = Math.floor(num / 3600);
40
        const min = Math.floor((num / 60) % 60);
Lijiao's avatar
Lijiao committed
41
42
43
44
45
        return hour > 0 ? `${hour}h ${min}min` : `${min}min`;
    }
};

// trial's duration, accurate to seconds for example 10min 30s
46
47
48
49
50
51
const convertDuration = (seconds: number): string => {
    let str = '';

    const d = Math.floor(seconds / (24 * 3600));
    if (d > 0) {
        str += `${d}d `;
52
    }
53
54
55
56
57
    seconds -= d * 24 * 3600;

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

    const m = Math.floor(seconds / 60);
    if (m > 0) {
        str += `${m}m `;
64
    }
65
66
67
68
    seconds -= m * 60;

    if (seconds > 0) {
        str += `${Math.floor(seconds)}s`;
Lijiao's avatar
Lijiao committed
69
    }
70
    return str ? str : '0s';
Lijiao's avatar
Lijiao committed
71
72
};

73
74
75
76
77
78
79
80
81
82
83
84
// 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;
}
85
function parseMetrics(metricData: string): any {
Lijiaoa's avatar
Lijiaoa committed
86
    if (metricData.includes('NaN') || metricData.includes('Infinity')) {
87
88
89
90
91
92
        return JSON5.parse(JSON5.parse(metricData));
    } else {
        return JSON.parse(JSON.parse(metricData));
    }
}

93
94
const isArrayType = (list: any): boolean | undefined => {
    return Array.isArray(list);
95
};
96

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

Lijiaoa's avatar
Lijiaoa committed
119
120
121
122
function isNaNorInfinity(val: number): boolean {
    return Object.is(val, NaN) || Object.is(val, Infinity);
}

123
// get final result value // acc obj
124
const getFinal = (final?: MetricDataRecord[]): FinalType | undefined => {
125
126
    let showDefault: FinalType;
    if (final) {
127
        showDefault = parseMetrics(final[final.length - 1].data);
128
        if (typeof showDefault === 'number') {
129
            if (!isNaNorInfinity(showDefault)) {
Lijiaoa's avatar
Lijiaoa committed
130
                return { default: showDefault };
131
            }
132
133
134
135
136
        } else if (isArrayType(showDefault)) {
            // not support final type
            return undefined;
        } else if (typeof showDefault === 'object' && showDefault.hasOwnProperty('default')) {
            return showDefault;
137
138
139
140
141
142
        }
    } else {
        return undefined;
    }
};

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

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

213
const filterByStatus = (item: TableObj): boolean => {
214
215
216
    return item.status === 'SUCCEEDED';
};

217
// a waittiong trial may havn't start time
218
const filterDuration = (item: TableObj): boolean => {
219
220
221
    return item.status !== 'WAITING';
};

Lijiao's avatar
Lijiao committed
222
const downFile = (content: string, fileName: string): void => {
v-liguo's avatar
v-liguo committed
223
224
225
226
227
228
229
230
231
232
233
    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');
234
        downTag.addEventListener('click', function() {
v-liguo's avatar
v-liguo committed
235
236
237
            downTag.download = fileName;
            downTag.href = URL.createObjectURL(file);
        });
chicm-ms's avatar
chicm-ms committed
238
        const eventMouse = document.createEvent('MouseEvents');
v-liguo's avatar
v-liguo committed
239
240
241
242
243
        eventMouse.initEvent('click', false, false);
        downTag.dispatchEvent(eventMouse);
    }
};

244
245
246
247
248
// function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
function formatTimestamp(timestamp?: number, placeholder?: string): string {
    if (placeholder === undefined) {
        placeholder = 'N/A';
    }
249
250
251
    return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
}

252
253
254
255
256
257
258
259
function expformatTimestamp(timestamp: number | string): string {
    if (typeof timestamp === 'number') {
        return new Date(timestamp).toLocaleString('en-US');
    } else {
        return 'N/A';
    }
}

260
function metricAccuracy(metric: MetricDataRecord): number {
chicm-ms's avatar
chicm-ms committed
261
    const data = parseMetrics(metric.data);
262
263
264
265
266
267
    // return typeof data === 'number' ? data : NaN;
    if (typeof data === 'number') {
        return data;
    } else {
        return data.default;
    }
268
269
270
271
}

function formatAccuracy(accuracy: number): string {
    // TODO: how to format NaN?
272
273
274
275
    return accuracy
        .toFixed(6)
        .replace(/0+$/, '')
        .replace(/\.$/, '');
276
277
}

Lijiaoa's avatar
Lijiaoa committed
278
279
280
281
282
283
284
285
function formatComplexTypeValue(value: any): string | number {
    if (['number', 'string'].includes(typeof value)) {
        return value;
    } else {
        return value.toString();
    }
}

286
287
288
289
function isManagerExperimentPage(): boolean {
    return location.pathname.indexOf('experiment') === -1 ? false : true;
}

290
291
292
293
294
295
296
function caclMonacoEditorHeight(height): number {
    // [Search space 56px] + [marginBottom 18px] +
    // button[height: 32px, marginTop: 45px, marginBottom: 7px]
    // panel own padding-bottom: 20px;
    return height - 178;
}

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
function copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): any {
    const key = columnKey as keyof T;
    return items.slice(0).sort(function(a: T, b: T): any {
        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
321
322
323
324
325
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

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;
}
360
361
362
363
364
365
366
367
368
369
370
371
372

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

373
export {
374
    getPrefix,
375
376
    convertTime,
    convertDuration,
377
    convertTimeAsUnit,
378
379
380
381
382
383
384
385
386
    getFinalResult,
    getFinal,
    downFile,
    intermediateGraphOption,
    killJob,
    filterByStatus,
    filterDuration,
    formatAccuracy,
    formatTimestamp,
387
    expformatTimestamp,
388
389
390
391
392
    metricAccuracy,
    parseMetrics,
    isArrayType,
    requestAxios,
    isNaNorInfinity,
393
    formatComplexTypeValue,
394
    isManagerExperimentPage,
395
    caclMonacoEditorHeight,
Lijiaoa's avatar
Lijiaoa committed
396
397
    copyAndSort,
    disableTensorboard,
398
399
    getTensorboardMenu,
    parametersType
400
};