function.ts 10.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';
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
8
9
10
11
12
13
14
15
16
17
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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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
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
348
349
350
351
352
353
354
355
356
357
358

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;
}
359
export {
360
    getPrefix,
361
362
    convertTime,
    convertDuration,
363
    convertTimeAsUnit,
364
365
366
367
368
369
370
371
372
    getFinalResult,
    getFinal,
    downFile,
    intermediateGraphOption,
    killJob,
    filterByStatus,
    filterDuration,
    formatAccuracy,
    formatTimestamp,
373
    expformatTimestamp,
374
375
376
377
378
    metricAccuracy,
    parseMetrics,
    isArrayType,
    requestAxios,
    isNaNorInfinity,
379
    formatComplexTypeValue,
380
    isManagerExperimentPage,
381
    caclMonacoEditorHeight,
Lijiaoa's avatar
Lijiaoa committed
382
383
384
    copyAndSort,
    disableTensorboard,
    getTensorboardMenu
385
};