Skip to content

Commit

Permalink
React & Antd UI: Export dataset, refactoring & fixes (cvat-ai#872)
Browse files Browse the repository at this point in the history
* Automatic label matching (by the same name) in model running window
* Improved create task window
* Improved upload model window
* Fixed: error window showed twice
* Updated CONTRIBUTING.md
* Removed token before login, fixed dump submenu (adjustment), fixed case when empty models list displayed
* Export as dataset, better error showing system
* Removed extra requests, improved UI
* Fixed a name of a format
* Show inference progress
* Fixed model loading after a model was uploaded
  • Loading branch information
bsekachev authored and Chris Lee-Messer committed Mar 5, 2020
1 parent 35466c2 commit b36864e
Show file tree
Hide file tree
Showing 56 changed files with 1,473 additions and 688 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ $ python3 -m venv .env
$ . .env/bin/activate
$ pip install -U pip wheel
$ pip install -r cvat/requirements/development.txt
$ pip install -r datumaro/requirements.txt
$ python manage.py migrate
$ python manage.py collectstatic
```
Expand Down
3 changes: 2 additions & 1 deletion cvat-core/src/server-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
`${encodeURIComponent('password')}=${encodeURIComponent(password)}`,
]).join('&').replace(/%20/g, '+');

Axios.defaults.headers.common.Authorization = '';
let authenticationResponse = null;
try {
authenticationResponse = await Axios.post(
Expand Down Expand Up @@ -246,7 +247,7 @@
try {
await Axios.delete(`${backendAPI}/tasks/${id}`);
} catch (errorData) {
throw generateError(errorData, 'Could not delete the task from the server');
throw generateError(errorData, `Could not delete the task ${id} from the server`);
}
}

Expand Down
39 changes: 27 additions & 12 deletions cvat-ui/src/actions/formats-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,53 @@ import getCore from '../core';
const cvat = getCore();

export enum FormatsActionTypes {
GETTING_FORMATS_SUCCESS = 'GETTING_FORMATS_SUCCESS',
GETTING_FORMATS_FAILED = 'GETTING_FORMATS_FAILED',
GET_FORMATS = 'GET_FORMATS',
GET_FORMATS_SUCCESS = 'GET_FORMATS_SUCCESS',
GET_FORMATS_FAILED = 'GET_FORMATS_FAILED',
}

export function gettingFormatsSuccess(formats: any): AnyAction {
function getFormats(): AnyAction {
return {
type: FormatsActionTypes.GETTING_FORMATS_SUCCESS,
type: FormatsActionTypes.GET_FORMATS,
payload: {},
};
}

function getFormatsSuccess(
annotationFormats: any[],
datasetFormats: any[],
): AnyAction {
return {
type: FormatsActionTypes.GET_FORMATS_SUCCESS,
payload: {
formats,
annotationFormats,
datasetFormats,
},
};
}

export function gettingFormatsFailed(error: any): AnyAction {
function getFormatsFailed(error: any): AnyAction {
return {
type: FormatsActionTypes.GETTING_FORMATS_FAILED,
type: FormatsActionTypes.GET_FORMATS_FAILED,
payload: {
error,
},
};
}

export function gettingFormatsAsync(): ThunkAction<Promise<void>, {}, {}, AnyAction> {
export function getFormatsAsync(): ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
let formats = null;
dispatch(getFormats());
let annotationFormats = null;
let datasetFormats = null;
try {
formats = await cvat.server.formats();
annotationFormats = await cvat.server.formats();
datasetFormats = await cvat.server.datasetFormats();
} catch (error) {
dispatch(gettingFormatsFailed(error));
dispatch(getFormatsFailed(error));
return;
}

dispatch(gettingFormatsSuccess(formats));
dispatch(getFormatsSuccess(annotationFormats, datasetFormats));
};
}
202 changes: 201 additions & 1 deletion cvat-ui/src/actions/models-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { ThunkAction } from 'redux-thunk';

import getCore from '../core';
import { getCVATStore } from '../store';
import { Model, ModelFiles, CombinedState } from '../reducers/interfaces';
import {
Model,
ModelFiles,
ActiveInference,
CombinedState,
} from '../reducers/interfaces';

export enum ModelsActionTypes {
GET_MODELS = 'GET_MODELS',
Expand Down Expand Up @@ -324,6 +329,199 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
};
}


function getInferenceStatusSuccess(
taskID: number,
activeInference: ActiveInference,
): AnyAction {
const action = {
type: ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS,
payload: {
taskID,
activeInference,
},
};

return action;
}

function getInferenceStatusFailed(taskID: number, error: any): AnyAction {
const action = {
type: ModelsActionTypes.GET_INFERENCE_STATUS_FAILED,
payload: {
taskID,
error,
},
};

return action;
}

interface InferenceMeta {
active: boolean;
taskID: number;
requestID: string;
}

const timers: any = {};

async function timeoutCallback(
url: string,
taskID: number,
dispatch: ActionCreator<Dispatch>,
): Promise<void> {
try {
delete timers[taskID];

const response = await core.server.request(url, {
method: 'GET',
});

const activeInference: ActiveInference = {
status: response.status,
progress: +response.progress || 0,
error: response.error || response.stderr || '',
};


if (activeInference.status === 'unknown') {
dispatch(getInferenceStatusFailed(
taskID,
new Error(
`Inference status for the task ${taskID} is unknown.`,
),
));

return;
}

if (activeInference.status === 'failed') {
dispatch(getInferenceStatusFailed(
taskID,
new Error(
`Inference status for the task ${taskID} is failed. ${activeInference.error}`,
),
));

return;
}

if (activeInference.status !== 'finished') {
timers[taskID] = setTimeout(
timeoutCallback.bind(
null,
url,
taskID,
dispatch,
), 3000,
);
}

dispatch(getInferenceStatusSuccess(taskID, activeInference));
} catch (error) {
dispatch(getInferenceStatusFailed(taskID, error));
}
}

function subscribe(
urlPath: string,
inferenceMeta: InferenceMeta,
dispatch: ActionCreator<Dispatch>,
): void {
if (!(inferenceMeta.taskID in timers)) {
const requestURL = `${baseURL}/${urlPath}/${inferenceMeta.requestID}`;
timers[inferenceMeta.taskID] = setTimeout(
timeoutCallback.bind(
null,
requestURL,
inferenceMeta.taskID,
dispatch,
),
);
}
}

export function getInferenceStatusAsync(tasks: number[]):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
function parse(response: any): InferenceMeta[] {
return Object.keys(response).map((key: string): InferenceMeta => ({
taskID: +key,
requestID: response[key].rq_id || key,
active: typeof (response[key].active) === 'undefined' ? ['queued', 'started']
.includes(response[key].status.toLowerCase()) : response[key].active,
}));
}

const store = getCVATStore();
const state: CombinedState = store.getState();
const OpenVINO = state.plugins.plugins.AUTO_ANNOTATION;
const RCNN = state.plugins.plugins.TF_ANNOTATION;
const MaskRCNN = state.plugins.plugins.TF_SEGMENTATION;

try {
if (OpenVINO) {
const response = await core.server.request(
`${baseURL}/auto_annotation/meta/get`, {
method: 'POST',
data: JSON.stringify(tasks),
headers: {
'Content-Type': 'application/json',
},
},
);

parse(response.run)
.filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active)
.forEach((inferenceMeta: InferenceMeta): void => {
subscribe('auto_annotation/check', inferenceMeta, dispatch);
});
}

if (RCNN) {
const response = await core.server.request(
`${baseURL}/tensorflow/annotation/meta/get`, {
method: 'POST',
data: JSON.stringify(tasks),
headers: {
'Content-Type': 'application/json',
},
},
);

parse(response)
.filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active)
.forEach((inferenceMeta: InferenceMeta): void => {
subscribe('tensorflow/annotation/check/task', inferenceMeta, dispatch);
});
}

if (MaskRCNN) {
const response = await core.server.request(
`${baseURL}/tensorflow/segmentation/meta/get`, {
method: 'POST',
data: JSON.stringify(tasks),
headers: {
'Content-Type': 'application/json',
},
},
);

parse(response)
.filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active)
.forEach((inferenceMeta: InferenceMeta): void => {
subscribe('tensorflow/segmentation/check/task', inferenceMeta, dispatch);
});
}
} catch (error) {
tasks.forEach((task: number): void => {
dispatch(getInferenceStatusFailed(task, error));
});
}
};
}


function inferModel(): AnyAction {
const action = {
type: ModelsActionTypes.INFER_MODEL,
Expand Down Expand Up @@ -387,6 +585,8 @@ export function inferModelAsync(
},
);
}

dispatch(getInferenceStatusAsync([taskInstance.id]));
} catch (error) {
dispatch(inferModelFailed(error));
return;
Expand Down
24 changes: 24 additions & 0 deletions cvat-ui/src/actions/notification-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { AnyAction } from 'redux';

export enum NotificationsActionType {
RESET_ERRORS = 'RESET_ERRORS',
RESET_MESSAGES = 'RESET_MESSAGES',
}

export function resetErrors(): AnyAction {
const action = {
type: NotificationsActionType.RESET_ERRORS,
payload: {},
};

return action;
}

export function resetMessages(): AnyAction {
const action = {
type: NotificationsActionType.RESET_MESSAGES,
payload: {},
};

return action;
}
11 changes: 11 additions & 0 deletions cvat-ui/src/actions/plugins-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@ import { SupportedPlugins } from '../reducers/interfaces';
import PluginChecker from '../utils/plugin-checker';

export enum PluginsActionTypes {
CHECK_PLUGINS = 'CHECK_PLUGINS',
CHECKED_ALL_PLUGINS = 'CHECKED_ALL_PLUGINS'
}

interface PluginObjects {
[plugin: string]: boolean;
}

function checkPlugins(): AnyAction {
const action = {
type: PluginsActionTypes.CHECK_PLUGINS,
payload: {},
};

return action;
}

function checkedAllPlugins(plugins: PluginObjects): AnyAction {
const action = {
type: PluginsActionTypes.CHECKED_ALL_PLUGINS,
Expand All @@ -25,6 +35,7 @@ function checkedAllPlugins(plugins: PluginObjects): AnyAction {
export function checkPluginsAsync():
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
dispatch(checkPlugins());
const plugins: PluginObjects = {};

const promises: Promise<boolean>[] = [];
Expand Down
Loading

0 comments on commit b36864e

Please sign in to comment.