Skip to content

Commit

Permalink
Project task subsets (cvat-ai#2774)
Browse files Browse the repository at this point in the history
* Added task project subsets

* Added components list key

* Added subset field resetting and subset header

* Added CHANGELOG and increased npm package version

* Added replacing camelcase to snake
  • Loading branch information
ActiveChooN authored and kenu committed Feb 23, 2021
1 parent 3f6b5d6 commit 8a8077a
Show file tree
Hide file tree
Showing 24 changed files with 383 additions and 79 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
function for interative segmentation
- Pre-built [cvat_server](https://hub.docker.com/r/openvino/cvat_server) and
[cvat_ui](https://hub.docker.com/r/openvino/cvat_ui) images were published on DockerHub (<https://github.com/openvinotoolkit/cvat/pull/2766>)
- Project task subsets (<https://github.com/openvinotoolkit/cvat/pull/2774>)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion 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.10.0",
"version": "3.11.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
66 changes: 31 additions & 35 deletions cvat-core/src/api-implementation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -7,7 +7,13 @@
const serverProxy = require('./server-proxy');
const lambdaManager = require('./lambda-manager');
const {
isBoolean, isInteger, isEnum, isString, checkFilter,
isBoolean,
isInteger,
isEnum,
isString,
checkFilter,
checkExclusiveFields,
camelToSnake,
} = require('./common');

const { TaskStatus, TaskMode, DimensionType } = require('./enums');
Expand Down Expand Up @@ -179,27 +185,21 @@
dimension: isEnum.bind(DimensionType),
});

if ('search' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) {
throw new ArgumentError('Do not use the filter field "search" with others');
}
}

if ('id' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) {
throw new ArgumentError('Do not use the filter field "id" with others');
}
}

if (
'projectId' in filter
&& (('page' in filter && Object.keys(filter).length > 2) || Object.keys(filter).length > 2)
) {
throw new ArgumentError('Do not use the filter field "projectId" with other');
}
checkExclusiveFields(filter, ['id', 'search', 'projectId'], ['page']);

const searchParams = new URLSearchParams();
for (const field of ['name', 'owner', 'assignee', 'search', 'status', 'mode', 'id', 'page', 'projectId', 'dimension']) {
for (const field of [
'name',
'owner',
'assignee',
'search',
'status',
'mode',
'id',
'page',
'projectId',
'dimension',
]) {
if (Object.prototype.hasOwnProperty.call(filter, field)) {
searchParams.set(field, filter[field]);
}
Expand All @@ -222,30 +222,26 @@
owner: isString,
search: isString,
status: isEnum.bind(TaskStatus),
withoutTasks: isBoolean,
});

if ('search' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) {
throw new ArgumentError('Do not use the filter field "search" with others');
}
}

if ('id' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) {
throw new ArgumentError('Do not use the filter field "id" with others');
}
}
checkExclusiveFields(filter, ['id', 'search'], ['page', 'withoutTasks']);

const searchParams = new URLSearchParams();
for (const field of ['name', 'assignee', 'owner', 'search', 'status', 'id', 'page']) {
for (const field of ['name', 'assignee', 'owner', 'search', 'status', 'id', 'page', 'withoutTasks']) {
if (Object.prototype.hasOwnProperty.call(filter, field)) {
searchParams.set(field, filter[field]);
searchParams.set(camelToSnake(field), filter[field]);
}
}

const projectsData = await serverProxy.projects.get(searchParams.toString());
// prettier-ignore
const projects = projectsData.map((project) => new Project(project));
const projects = projectsData.map((project) => {
if (filter.withoutTasks) {
project.tasks = [];
}
return project;
}).map((project) => new Project(project));

projects.count = projectsData.count;

Expand Down
2 changes: 1 addition & 1 deletion cvat-core/src/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down
33 changes: 32 additions & 1 deletion cvat-core/src/common.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -42,6 +42,25 @@
}
}

function checkExclusiveFields(obj, exclusive, ignore) {
const fields = {
exclusive: [],
other: [],
};
for (const field in Object.keys(obj)) {
if (!(field in ignore)) {
if (field in exclusive) {
if (fields.other.length) {
throw new ArgumentError(`Do not use the filter field "${field}" with others`);
}
fields.exclusive.push(field);
} else {
fields.other.push(field);
}
}
}
}

function checkObjectType(name, value, type, instance) {
if (type) {
if (typeof value !== type) {
Expand All @@ -68,6 +87,16 @@
return true;
}

function camelToSnake(str) {
if (typeof str !== 'string') {
throw new ArgumentError('str is expected to be string');
}

return (
str[0].toLowerCase() + str.slice(1, str.length).replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
);
}

function negativeIDGenerator() {
const value = negativeIDGenerator.start;
negativeIDGenerator.start -= 1;
Expand All @@ -83,5 +112,7 @@
checkFilter,
checkObjectType,
negativeIDGenerator,
checkExclusiveFields,
camelToSnake,
};
})();
21 changes: 20 additions & 1 deletion cvat-core/src/project.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -32,6 +32,7 @@
bug_tracker: undefined,
created_date: undefined,
updated_date: undefined,
task_subsets: undefined,
};

for (const property in data) {
Expand All @@ -56,6 +57,13 @@
data.tasks.push(taskInstance);
}
}
if (!data.task_subsets && data.tasks.length) {
const subsetsSet = new Set();
for (const task in data.tasks) {
if (task.subset) subsetsSet.add(task.subset);
}
data.task_subsets = Array.from(subsetsSet);
}

Object.defineProperties(
this,
Expand Down Expand Up @@ -192,6 +200,17 @@
tasks: {
get: () => [...data.tasks],
},
/**
* Subsets array for linked tasks
* @name subsets
* @type {string[]}
* @memberof module:API.cvat.classes.Project
* @readonly
* @instance
*/
subsets: {
get: () => [...data.task_subsets],
},
}),
);
}
Expand Down
2 changes: 1 addition & 1 deletion cvat-core/src/server-proxy.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down
37 changes: 36 additions & 1 deletion cvat-core/src/session.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -972,6 +972,7 @@
created_date: undefined,
updated_date: undefined,
bug_tracker: undefined,
subset: undefined,
overlap: undefined,
segment_size: undefined,
image_quality: undefined,
Expand All @@ -991,6 +992,7 @@
name: false,
assignee: false,
bug_tracker: false,
subset: false,
labels: false,
};

Expand Down Expand Up @@ -1167,10 +1169,36 @@
bugTracker: {
get: () => data.bug_tracker,
set: (tracker) => {
if (typeof tracker !== 'string') {
throw new ArgumentError(
`Subset value must be a string. But ${typeof tracker} has been got.`,
);
}

updatedFields.bug_tracker = true;
data.bug_tracker = tracker;
},
},
/**
* @name subset
* @type {string}
* @memberof module:API.cvat.classes.Task
* @instance
* @throws {module:API.cvat.exception.ArgumentError}
*/
subset: {
get: () => data.subset,
set: (subset) => {
if (typeof subset !== 'string') {
throw new ArgumentError(
`Subset value must be a string. But ${typeof subset} has been got.`,
);
}

updatedFields.subset = true;
data.subset = subset;
},
},
/**
* @name overlap
* @type {integer}
Expand Down Expand Up @@ -1888,6 +1916,9 @@
case 'bug_tracker':
taskData.bug_tracker = this.bugTracker;
break;
case 'subset':
taskData.subset = this.subset;
break;
case 'labels':
taskData.labels = [...this.labels.map((el) => el.toJSON())];
break;
Expand All @@ -1903,6 +1934,7 @@
assignee: false,
name: false,
bugTracker: false,
subset: false,
labels: false,
};

Expand All @@ -1926,6 +1958,9 @@
if (typeof this.projectId !== 'undefined') {
taskSpec.project_id = this.projectId;
}
if (typeof this.subset !== 'undefined') {
taskSpec.subset = this.subset;
}

const taskDataSpec = {
client_files: this.clientFiles,
Expand Down
2 changes: 1 addition & 1 deletion cvat-ui/src/actions/projects-actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down
5 changes: 4 additions & 1 deletion cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -397,6 +397,9 @@ export function createTaskAsync(data: any): ThunkAction<Promise<void>, {}, {}, A
if (data.advanced.copyData) {
description.copy_data = data.advanced.copyData;
}
if (data.subset) {
description.subset = data.subset;
}

const taskInstance = new cvat.classes.Task(description);
taskInstance.clientFiles = data.files.local;
Expand Down
Loading

0 comments on commit 8a8077a

Please sign in to comment.