Skip to content

Commit

Permalink
Project import simple implementation (#3790)
Browse files Browse the repository at this point in the history
  • Loading branch information
ActiveChooN authored Dec 17, 2021
1 parent cde33ac commit 579bfb3
Show file tree
Hide file tree
Showing 53 changed files with 1,941 additions and 361 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Data sorting option (<https://github.com/openvinotoolkit/cvat/pull/3937>)
- Options to change font size & position of text labels on the canvas (<https://github.com/openvinotoolkit/cvat/pull/3972>)
- Add "tag" return type for automatic annotation in Nuclio (<https://github.com/openvinotoolkit/cvat/pull/3896>)
- Dataset importing to a project (<https://github.com/openvinotoolkit/cvat/pull/3790>)
- User is able to customize information that text labels show (<https://github.com/openvinotoolkit/cvat/pull/4029>)

### Changed
Expand Down
4 changes: 2 additions & 2 deletions cvat-core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "3.21.1",
"version": "3.22.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
17 changes: 17 additions & 0 deletions cvat-core/src/annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,22 @@
return result;
}

function importDataset(instance, format, file, updateStatusCallback = () => {}) {
if (!(typeof format === 'string')) {
throw new ArgumentError('Format must be a string');
}
if (!(instance instanceof Project)) {
throw new ArgumentError('Instance should be a Project instance');
}
if (!(typeof updateStatusCallback === 'function')) {
throw new ArgumentError('Callback should be a function');
}
if (!(['application/zip', 'application/x-zip-compressed'].includes(file.type))) {
throw new ArgumentError('File should be file instance with ZIP extension');
}
return serverProxy.projects.importDataset(instance.id, format, file, updateStatusCallback);
}

function undoActions(session, count) {
const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType);
Expand Down Expand Up @@ -366,6 +382,7 @@
importAnnotations,
exportAnnotations,
exportDataset,
importDataset,
undoActions,
redoActions,
freezeHistory,
Expand Down
13 changes: 11 additions & 2 deletions cvat-core/src/project-implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
const { getPreview } = require('./frames');

const { Project } = require('./project');
const { exportDataset } = require('./annotations');
const { exportDataset, importDataset } = require('./annotations');

function implementProject(projectClass) {
projectClass.prototype.save.implementation = async function () {
Expand Down Expand Up @@ -61,11 +61,20 @@
};

projectClass.prototype.annotations.exportDataset.implementation = async function (
format, saveImages, customName,
format,
saveImages,
customName,
) {
const result = exportDataset(this, format, customName, saveImages);
return result;
};
projectClass.prototype.annotations.importDataset.implementation = async function (
format,
file,
updateStatusCallback,
) {
return importDataset(this, format, file, updateStatusCallback);
};

return projectClass;
}
Expand Down
11 changes: 11 additions & 0 deletions cvat-core/src/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@
// So, we need return it
this.annotations = {
exportDataset: Object.getPrototypeOf(this).annotations.exportDataset.bind(this),
importDataset: Object.getPrototypeOf(this).annotations.importDataset.bind(this),
};
}

Expand Down Expand Up @@ -310,6 +311,16 @@
);
return result;
},
async importDataset(format, file, updateStatusCallback = null) {
const result = await PluginRegistry.apiWrapper.call(
this,
Project.prototype.annotations.importDataset,
format,
file,
updateStatusCallback,
);
return result;
},
},
writable: true,
}),
Expand Down
45 changes: 42 additions & 3 deletions cvat-core/src/server-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,44 @@
};
}

async function importDataset(id, format, file, onUpdate) {
const { backendAPI } = config;
const url = `${backendAPI}/projects/${id}/dataset`;

const formData = new FormData();
formData.append('dataset_file', file);

return new Promise((resolve, reject) => {
async function requestStatus() {
try {
const response = await Axios.get(`${url}?action=import_status`, {
proxy: config.proxy,
});
if (response.status === 202) {
if (onUpdate && response.data.message !== '') {
onUpdate(response.data.message, response.data.progress || 0);
}
setTimeout(requestStatus, 3000);
} else if (response.status === 201) {
resolve();
} else {
reject(generateError(response));
}
} catch (error) {
reject(generateError(error));
}
}

Axios.post(`${url}?format=${format}`, formData, {
proxy: config.proxy,
}).then(() => {
setTimeout(requestStatus, 2000);
}).catch((error) => {
reject(generateError(error));
});
});
}

async function exportTask(id) {
const { backendAPI } = config;
const url = `${backendAPI}/tasks/${id}`;
Expand Down Expand Up @@ -577,7 +615,7 @@
const response = await Axios.get(`${backendAPI}/tasks/${id}/status`);
if (['Queued', 'Started'].includes(response.data.state)) {
if (response.data.message !== '') {
onUpdate(response.data.message);
onUpdate(response.data.message, response.data.progress || 0);
}
setTimeout(checkStatus, 1000);
} else if (response.data.state === 'Finished') {
Expand Down Expand Up @@ -637,7 +675,7 @@

let response = null;

onUpdate('The task is being created on the server..');
onUpdate('The task is being created on the server..', null);
try {
response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskSpec), {
proxy: config.proxy,
Expand All @@ -649,7 +687,7 @@
throw generateError(errorData);
}

onUpdate('The data are being uploaded to the server 0%');
onUpdate('The data are being uploaded to the server..', null);

async function chunkUpload(taskId, file) {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -1438,6 +1476,7 @@
create: createProject,
delete: deleteProject,
exportDataset: exportDataset('projects'),
importDataset,
}),
writable: false,
},
Expand Down
4 changes: 2 additions & 2 deletions cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.29.0",
"version": "1.30.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
59 changes: 59 additions & 0 deletions cvat-ui/src/actions/import-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

import { createAction, ActionUnion, ThunkAction } from 'utils/redux';
import { CombinedState } from 'reducers/interfaces';
import { getProjectsAsync } from './projects-actions';

export enum ImportActionTypes {
OPEN_IMPORT_MODAL = 'OPEN_IMPORT_MODAL',
CLOSE_IMPORT_MODAL = 'CLOSE_IMPORT_MODAL',
IMPORT_DATASET = 'IMPORT_DATASET',
IMPORT_DATASET_SUCCESS = 'IMPORT_DATASET_SUCCESS',
IMPORT_DATASET_FAILED = 'IMPORT_DATASET_FAILED',
IMPORT_DATASET_UPDATE_STATUS = 'IMPORT_DATASET_UPDATE_STATUS',
}

export const importActions = {
openImportModal: (instance: any) => createAction(ImportActionTypes.OPEN_IMPORT_MODAL, { instance }),
closeImportModal: () => createAction(ImportActionTypes.CLOSE_IMPORT_MODAL),
importDataset: (projectId: number) => (
createAction(ImportActionTypes.IMPORT_DATASET, { id: projectId })
),
importDatasetSuccess: () => (
createAction(ImportActionTypes.IMPORT_DATASET_SUCCESS)
),
importDatasetFailed: (instance: any, error: any) => (
createAction(ImportActionTypes.IMPORT_DATASET_FAILED, {
instance,
error,
})
),
importDatasetUpdateStatus: (progress: number, status: string) => (
createAction(ImportActionTypes.IMPORT_DATASET_UPDATE_STATUS, { progress, status })
),
};

export const importDatasetAsync = (instance: any, format: string, file: File): ThunkAction => (
async (dispatch, getState) => {
try {
const state: CombinedState = getState();
if (state.import.importingId !== null) {
throw Error('Only one importing of dataset allowed at the same time');
}
dispatch(importActions.importDataset(instance.id));
await instance.annotations.importDataset(format, file, (message: string, progress: number) => (
dispatch(importActions.importDatasetUpdateStatus(progress * 100, message))
));
} catch (error) {
dispatch(importActions.importDatasetFailed(instance, error));
return;
}

dispatch(importActions.importDatasetSuccess());
dispatch(getProjectsAsync({ id: instance.id }));
}
);

export type ImportActions = ActionUnion<typeof importActions>;
4 changes: 2 additions & 2 deletions cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,8 @@ export function createTaskAsync(data: any): ThunkAction<Promise<void>, {}, {}, A

dispatch(createTask());
try {
const savedTask = await taskInstance.save((status: string): void => {
dispatch(createTaskUpdateStatus(status));
const savedTask = await taskInstance.save((status: string, progress: number): void => {
dispatch(createTaskUpdateStatus(status + (progress !== null ? ` ${Math.floor(progress * 100)}%` : '')));
});
dispatch(createTaskSuccess(savedTask.id));
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function ExportDatasetModal(): JSX.Element {

useEffect(() => {
initActivities();
}, [instance?.id, instance instanceof core.classes.Project]);
}, [instance?.id, instance instanceof core.classes.Project, taskExportActivities, projectExportActivities]);

const closeModal = (): void => {
form.resetFields();
Expand Down
Loading

0 comments on commit 579bfb3

Please sign in to comment.