function.ts 10.3 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';
Lijiaoa's avatar
Lijiaoa committed
5
import { MetricDataRecord, FinalType, TableObj, Tensorboard } from './interface';
Lijiao's avatar
Lijiao committed
6

7
async function requestAxios(url: string): Promise<any> {
Lijiaoa's avatar
Lijiaoa committed
8
9
10
11
12
13
14
15
16
17
18
19
    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
20
const convertTime = (num: number): string => {
21
22
23
    if (num <= 0) {
        return '0';
    }
Lijiao's avatar
Lijiao committed
24
25
26
27
    if (num % 3600 === 0) {
        return num / 3600 + 'h';
    } else {
        const hour = Math.floor(num / 3600);
28
        const min = Math.floor((num / 60) % 60);
Lijiao's avatar
Lijiao committed
29
30
31
32
33
        return hour > 0 ? `${hour}h ${min}min` : `${min}min`;
    }
};

// trial's duration, accurate to seconds for example 10min 30s
34
35
36
37
38
39
const convertDuration = (seconds: number): string => {
    let str = '';

    const d = Math.floor(seconds / (24 * 3600));
    if (d > 0) {
        str += `${d}d `;
40
    }
41
42
43
44
45
    seconds -= d * 24 * 3600;

    const h = Math.floor(seconds / 3600);
    if (h > 0) {
        str += `${h}h `;
46
    }
47
48
49
50
51
    seconds -= h * 3600;

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

    if (seconds > 0) {
        str += `${Math.floor(seconds)}s`;
Lijiao's avatar
Lijiao committed
57
    }
58
    return str ? str : '0s';
Lijiao's avatar
Lijiao committed
59
60
};

61
62
63
64
65
66
67
68
69
70
71
72
// 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;
}
73
function parseMetrics(metricData: string): any {
Lijiaoa's avatar
Lijiaoa committed
74
    if (metricData.includes('NaN') || metricData.includes('Infinity')) {
75
76
77
78
79
80
        return JSON5.parse(JSON5.parse(metricData));
    } else {
        return JSON.parse(JSON.parse(metricData));
    }
}

81
82
const isArrayType = (list: any): boolean | undefined => {
    return Array.isArray(list);
83
};
84

Lijiao's avatar
Lijiao committed
85
// get final result value
86
// draw Accuracy point graph
Lijiao's avatar
Lijiao committed
87
const getFinalResult = (final?: MetricDataRecord[]): number => {
Lijiao's avatar
Lijiao committed
88
89
90
    let acc;
    let showDefault = 0;
    if (final) {
91
        acc = parseMetrics(final[final.length - 1].data);
92
        if (typeof acc === 'object' && !isArrayType(acc)) {
Lijiao's avatar
Lijiao committed
93
94
95
            if (acc.default) {
                showDefault = acc.default;
            }
96
        } else if (typeof acc === 'number') {
Lijiao's avatar
Lijiao committed
97
            showDefault = acc;
98
99
        } else {
            showDefault = NaN;
Lijiao's avatar
Lijiao committed
100
101
102
103
104
105
        }
        return showDefault;
    } else {
        return 0;
    }
};
106

Lijiaoa's avatar
Lijiaoa committed
107
108
109
110
function isNaNorInfinity(val: number): boolean {
    return Object.is(val, NaN) || Object.is(val, Infinity);
}

111
// get final result value // acc obj
112
const getFinal = (final?: MetricDataRecord[]): FinalType | undefined => {
113
114
    let showDefault: FinalType;
    if (final) {
115
        showDefault = parseMetrics(final[final.length - 1].data);
116
        if (typeof showDefault === 'number') {
117
            if (!isNaNorInfinity(showDefault)) {
Lijiaoa's avatar
Lijiaoa committed
118
                return { default: showDefault };
119
            }
120
121
122
123
124
        } else if (isArrayType(showDefault)) {
            // not support final type
            return undefined;
        } else if (typeof showDefault === 'object' && showDefault.hasOwnProperty('default')) {
            return showDefault;
125
126
127
128
129
130
        }
    } else {
        return undefined;
    }
};

Lijiao's avatar
Lijiao committed
131
// detail page table intermediate button
Lijiao's avatar
Lijiao committed
132
const intermediateGraphOption = (intermediateArr: number[], id: string): any => {
Lijiao's avatar
Lijiao committed
133
134
135
136
137
138
139
140
141
142
143
    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,
144
                color: '#333'
Lijiao's avatar
Lijiao committed
145
146
147
148
149
150
            }
        },
        tooltip: {
            trigger: 'item'
        },
        xAxis: {
151
            // name: '#Intermediate result',
Lijiao's avatar
Lijiao committed
152
153
154
            data: sequence
        },
        yAxis: {
Lijiao's avatar
Lijiao committed
155
            name: 'Default metric',
Lijiao's avatar
Lijiao committed
156
            type: 'value',
Lijiaoa's avatar
Lijiaoa committed
157
158
            data: intermediateArr,
            scale: true
Lijiao's avatar
Lijiao committed
159
        },
160
161
162
163
164
165
166
        series: [
            {
                symbolSize: 6,
                type: 'scatter',
                data: intermediateArr
            }
        ]
Lijiao's avatar
Lijiao committed
167
168
169
170
    };
};

// kill job
Lijiao's avatar
Lijiao committed
171
const killJob = (key: number, id: string, status: string, updateList?: Function): void => {
Lijiao's avatar
Lijiao committed
172
173
174
175
176
177
178
179
    axios(`${MANAGER_IP}/trial-jobs/${id}`, {
        method: 'DELETE',
        headers: {
            'Content-Type': 'application/json;charset=utf-8'
        }
    })
        .then(res => {
            if (res.status === 200) {
180
181
                // TODO: use Message.txt to tooltip
                alert('Cancel the job successfully');
Lijiao's avatar
Lijiao committed
182
                // render the table
183
                if (updateList) {
184
                    updateList(); // FIXME
185
                }
Lijiao's avatar
Lijiao committed
186
            } else {
187
                alert('fail to cancel the job');
Lijiao's avatar
Lijiao committed
188
189
190
191
192
            }
        })
        .catch(error => {
            if (error.response.status === 500) {
                if (error.response.data.error) {
Lijiaoa's avatar
Lijiaoa committed
193
                    alert(error.response.data.error);
Lijiao's avatar
Lijiao committed
194
                } else {
Lijiaoa's avatar
Lijiaoa committed
195
                    alert('500 error, fail to cancel the job');
Lijiao's avatar
Lijiao committed
196
197
198
199
200
                }
            }
        });
};

201
const filterByStatus = (item: TableObj): boolean => {
202
203
204
    return item.status === 'SUCCEEDED';
};

205
// a waittiong trial may havn't start time
206
const filterDuration = (item: TableObj): boolean => {
207
208
209
    return item.status !== 'WAITING';
};

Lijiao's avatar
Lijiao committed
210
const downFile = (content: string, fileName: string): void => {
v-liguo's avatar
v-liguo committed
211
212
213
214
215
216
217
218
219
220
221
    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');
222
        downTag.addEventListener('click', function() {
v-liguo's avatar
v-liguo committed
223
224
225
            downTag.download = fileName;
            downTag.href = URL.createObjectURL(file);
        });
chicm-ms's avatar
chicm-ms committed
226
        const eventMouse = document.createEvent('MouseEvents');
v-liguo's avatar
v-liguo committed
227
228
229
230
231
        eventMouse.initEvent('click', false, false);
        downTag.dispatchEvent(eventMouse);
    }
};

232
233
234
235
236
// function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
function formatTimestamp(timestamp?: number, placeholder?: string): string {
    if (placeholder === undefined) {
        placeholder = 'N/A';
    }
237
238
239
    return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
}

240
241
242
243
244
245
246
247
function expformatTimestamp(timestamp: number | string): string {
    if (typeof timestamp === 'number') {
        return new Date(timestamp).toLocaleString('en-US');
    } else {
        return 'N/A';
    }
}

248
function metricAccuracy(metric: MetricDataRecord): number {
chicm-ms's avatar
chicm-ms committed
249
    const data = parseMetrics(metric.data);
250
251
252
253
254
255
    // return typeof data === 'number' ? data : NaN;
    if (typeof data === 'number') {
        return data;
    } else {
        return data.default;
    }
256
257
258
259
}

function formatAccuracy(accuracy: number): string {
    // TODO: how to format NaN?
260
261
262
263
    return accuracy
        .toFixed(6)
        .replace(/0+$/, '')
        .replace(/\.$/, '');
264
265
}

Lijiaoa's avatar
Lijiaoa committed
266
267
268
269
270
271
272
273
function formatComplexTypeValue(value: any): string | number {
    if (['number', 'string'].includes(typeof value)) {
        return value;
    } else {
        return value.toString();
    }
}

274
275
276
277
function isManagerExperimentPage(): boolean {
    return location.pathname.indexOf('experiment') === -1 ? false : true;
}

278
279
280
281
282
283
284
function caclMonacoEditorHeight(height): number {
    // [Search space 56px] + [marginBottom 18px] +
    // button[height: 32px, marginTop: 45px, marginBottom: 7px]
    // panel own padding-bottom: 20px;
    return height - 178;
}

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
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
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
335
336
337
338
339
340
341
342
343
344
345
346
347

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;
}
348
export {
349
350
    convertTime,
    convertDuration,
351
    convertTimeAsUnit,
352
353
354
355
356
357
358
359
360
    getFinalResult,
    getFinal,
    downFile,
    intermediateGraphOption,
    killJob,
    filterByStatus,
    filterDuration,
    formatAccuracy,
    formatTimestamp,
361
    expformatTimestamp,
362
363
364
365
366
    metricAccuracy,
    parseMetrics,
    isArrayType,
    requestAxios,
    isNaNorInfinity,
367
    formatComplexTypeValue,
368
    isManagerExperimentPage,
369
    caclMonacoEditorHeight,
Lijiaoa's avatar
Lijiaoa committed
370
371
372
    copyAndSort,
    disableTensorboard,
    getTensorboardMenu
373
};