Skip to content

Commit 7960447

Browse files
authored
React UI: Better exception handling (#1297)
1 parent 9ef89c7 commit 7960447

26 files changed

+578
-90
lines changed

cvat-core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"dependencies": {
3535
"axios": "^0.18.0",
3636
"browser-or-node": "^1.2.1",
37+
"detect-browser": "^5.0.0",
3738
"error-stack-parser": "^2.0.2",
3839
"form-data": "^2.5.0",
3940
"jest-config": "^24.8.0",

cvat-core/src/enums.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
* @memberof module:API.cvat.enums
110110
* @property {string} loadJob Load job
111111
* @property {string} saveJob Save job
112+
* @property {string} restoreJob Restore job
112113
* @property {string} uploadAnnotations Upload annotations
113114
* @property {string} sendUserActivity Send user activity
114115
* @property {string} sendException Send exception
@@ -142,6 +143,7 @@
142143
const LogType = Object.freeze({
143144
loadJob: 'Load job',
144145
saveJob: 'Save job',
146+
restoreJob: 'Restore job',
145147
uploadAnnotations: 'Upload annotations',
146148
sendUserActivity: 'Send user activity',
147149
sendException: 'Send exception',

cvat-core/src/log.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require:false
77
*/
88

9+
const { detect } = require('detect-browser');
910
const PluginRegistry = require('./plugins');
1011
const { ArgumentError } = require('./exceptions');
1112
const { LogType } = require('./enums');
@@ -179,6 +180,48 @@ class LogWithExceptionInfo extends Log {
179180
+ 'It must be a number';
180181
throw new ArgumentError(message);
181182
}
183+
184+
if (typeof (this.payload.column) !== 'number') {
185+
const message = `The field "column" is required for ${this.type} log. `
186+
+ 'It must be a number';
187+
throw new ArgumentError(message);
188+
}
189+
190+
if (typeof (this.payload.stack) !== 'string') {
191+
const message = `The field "stack" is required for ${this.type} log. `
192+
+ 'It must be a string';
193+
throw new ArgumentError(message);
194+
}
195+
}
196+
197+
dump() {
198+
const payload = { ...this.payload };
199+
const client = detect();
200+
const body = {
201+
client_id: payload.client_id,
202+
name: this.type,
203+
time: this.time.toISOString(),
204+
message: payload.message,
205+
filename: payload.filename,
206+
line: payload.line,
207+
column: payload.column,
208+
stack: payload.stack,
209+
system: client.os,
210+
client: client.name,
211+
version: client.version,
212+
};
213+
214+
delete payload.client_id;
215+
delete payload.message;
216+
delete payload.filename;
217+
delete payload.line;
218+
delete payload.column;
219+
delete payload.stack;
220+
221+
return {
222+
...body,
223+
payload,
224+
};
182225
}
183226
}
184227

cvat-core/src/logger-storage.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
const PluginRegistry = require('./plugins');
10-
const server = require('./server-proxy');
10+
const serverProxy = require('./server-proxy');
1111
const logFactory = require('./log');
1212
const { ArgumentError } = require('./exceptions');
1313
const { LogType } = require('./enums');
@@ -128,6 +128,14 @@ LoggerStorage.prototype.log.implementation = function (logType, payload, wait) {
128128
this.collection.push(log);
129129
};
130130

131+
if (log.type === LogType.sendException) {
132+
serverProxy.server.exception(log.dump()).catch(() => {
133+
pushEvent();
134+
});
135+
136+
return log;
137+
}
138+
131139
if (wait) {
132140
log.onClose(pushEvent);
133141
} else {
@@ -156,7 +164,7 @@ LoggerStorage.prototype.save.implementation = async function () {
156164
const userActivityLog = logFactory(LogType.sendUserActivity, logPayload);
157165
collectionToSend.push(userActivityLog);
158166

159-
await server.logs.save(collectionToSend.map((log) => log.dump()));
167+
await serverProxy.logs.save(collectionToSend.map((log) => log.dump()));
160168

161169
for (const rule of Object.values(this.ignoreRules)) {
162170
rule.lastLog = null;

cvat-ui/package-lock.json

Lines changed: 16 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cvat-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"antd": "^3.25.2",
5858
"copy-to-clipboard": "^3.2.0",
5959
"dotenv-webpack": "^1.7.0",
60+
"error-stack-parser": "^2.0.6",
6061
"moment": "^2.24.0",
6162
"prop-types": "^15.7.2",
6263
"react": "^16.9.0",

cvat-ui/src/actions/annotation-actions.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,14 @@ function receiveAnnotationsParameters(): AnnotationsParameters {
7878
};
7979
}
8080

81-
function computeZRange(states: any[]): number[] {
81+
export function computeZRange(states: any[]): number[] {
8282
let minZ = states.length ? states[0].zOrder : 0;
8383
let maxZ = states.length ? states[0].zOrder : 0;
8484
states.forEach((state: any): void => {
85+
if (state.objectType === ObjectType.TAG) {
86+
return;
87+
}
88+
8589
minZ = Math.min(minZ, state.zOrder);
8690
maxZ = Math.max(maxZ, state.zOrder);
8791
});
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (C) 2020 Intel Corporation
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
import {
6+
ActionUnion,
7+
createAction,
8+
ThunkAction,
9+
ThunkDispatch,
10+
} from 'utils/redux';
11+
import getCore from 'cvat-core';
12+
import { LogType } from 'cvat-logger';
13+
import { computeZRange } from './annotation-actions';
14+
15+
const cvat = getCore();
16+
17+
export enum BoundariesActionTypes {
18+
RESET_AFTER_ERROR = 'RESET_AFTER_ERROR',
19+
THROW_RESET_ERROR = 'THROW_RESET_ERROR',
20+
}
21+
22+
export const boundariesActions = {
23+
resetAfterError: (
24+
job: any,
25+
states: any[],
26+
frameNumber: number,
27+
frameData: any | null,
28+
minZ: number,
29+
maxZ: number,
30+
colors: string[],
31+
) => createAction(BoundariesActionTypes.RESET_AFTER_ERROR, {
32+
job,
33+
states,
34+
frameNumber,
35+
frameData,
36+
minZ,
37+
maxZ,
38+
colors,
39+
}),
40+
throwResetError: () => createAction(BoundariesActionTypes.THROW_RESET_ERROR),
41+
};
42+
43+
export function resetAfterErrorAsync(): ThunkAction {
44+
return async (dispatch: ThunkDispatch, getState): Promise<void> => {
45+
try {
46+
const state = getState();
47+
const job = state.annotation.job.instance;
48+
49+
if (job) {
50+
const currentFrame = state.annotation.player.frame.number;
51+
const { showAllInterpolationTracks } = state.settings.workspace;
52+
const frameNumber = Math.max(Math.min(job.stopFrame, currentFrame), job.startFrame);
53+
54+
const states = await job.annotations
55+
.get(frameNumber, showAllInterpolationTracks, []);
56+
const frameData = await job.frames.get(frameNumber);
57+
const [minZ, maxZ] = computeZRange(states);
58+
const colors = [...cvat.enums.colors];
59+
60+
await job.logger.log(LogType.restoreJob);
61+
62+
dispatch(boundariesActions.resetAfterError(
63+
job,
64+
states,
65+
frameNumber,
66+
frameData,
67+
minZ,
68+
maxZ,
69+
colors,
70+
));
71+
} else {
72+
dispatch(boundariesActions.resetAfterError(
73+
null,
74+
[],
75+
0,
76+
null,
77+
0,
78+
0,
79+
[],
80+
));
81+
}
82+
} catch (error) {
83+
dispatch(boundariesActions.throwResetError());
84+
}
85+
};
86+
}
87+
88+
export type boundariesActions = ActionUnion<typeof boundariesActions>;

cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import React, { useState, useEffect } from 'react';
66
import { GlobalHotKeys, KeyMap } from 'react-hotkeys';
77
import { connect } from 'react-redux';
8+
import { Action } from 'redux';
9+
import { ThunkDispatch } from 'redux-thunk';
810
import Layout, { SiderProps } from 'antd/lib/layout';
911
import { SelectValue } from 'antd/lib/select';
1012
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
@@ -65,7 +67,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
6567
};
6668
}
6769

68-
function mapDispatchToProps(dispatch: any): DispatchToProps {
70+
function mapDispatchToProps(dispatch: ThunkDispatch<CombinedState, {}, Action>): DispatchToProps {
6971
return {
7072
activateObject(clientID: number, attrID: number): void {
7173
dispatch(activateObjectAction(clientID, attrID));

0 commit comments

Comments
 (0)