Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jobs page: advanced filtration and implemented sorting #4319

Merged
merged 67 commits into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
1bdcf5f
POC implementation
bsekachev Feb 10, 2022
54e3268
Fixed recently used list
bsekachev Feb 10, 2022
8df5f58
Added handling of filters query parameter for JobViewSet
Feb 10, 2022
2a9c93b
Improved filtering
bsekachev Feb 10, 2022
caefe1b
Fixed styles
bsekachev Feb 10, 2022
0bc4804
Added datetime operators, removed null from recent
bsekachev Feb 11, 2022
0412355
Applied changes for some comments
bsekachev Feb 11, 2022
8f17f10
Added reset button
bsekachev Feb 11, 2022
048c301
Adjusted styles
bsekachev Feb 11, 2022
7ec3f6f
Adjusted styles
bsekachev Feb 11, 2022
0121bd5
updated appearance, using username
bsekachev Feb 11, 2022
320c08f
Added sorting prototype
bsekachev Feb 13, 2022
893e18c
Support different priorities during sort
bsekachev Feb 13, 2022
65a7cc4
Removed todos
bsekachev Feb 13, 2022
cf1993f
Added filtering HOC
bsekachev Feb 13, 2022
1de9f36
Better visibility handling
bsekachev Feb 13, 2022
4346d6f
Adjusted sorting implementation
bsekachev Feb 13, 2022
2d0eddc
Fixed styles
bsekachev Feb 13, 2022
0e241d8
updated default filter
bsekachev Feb 13, 2022
696fa5e
Sorting more fields
bsekachev Feb 13, 2022
9c589a2
Updated version & changelog
bsekachev Feb 13, 2022
db745ae
Merge remote-tracking branch 'origin/develop' into bs/advanced_filtering
Feb 15, 2022
4569e77
Updated dropdowns position
bsekachev Feb 16, 2022
0aeb561
Removed 'close' buttons, added 'reset' button
bsekachev Feb 16, 2022
8d03a0c
Merge branch 'bs/advanced_filtering' of github.com:openvinotoolkit/cv…
bsekachev Feb 16, 2022
44c2f8e
Swap location for apply & reset
bsekachev Feb 16, 2022
d1bd3a6
Recent filters as a menu
bsekachev Feb 16, 2022
12508cd
Not selectable menu, a bit fixed styles
bsekachev Feb 16, 2022
d1df913
Human readable format
bsekachev Feb 16, 2022
a002353
Fixed stylelint issue
bsekachev Feb 16, 2022
7f35882
Implemented common JsonLogicFilter and OrderingFilter.
Feb 17, 2022
11d1a0d
Using ternary operator
bsekachev Feb 17, 2022
14983cd
Adjusted UX: do not jump filters builder
bsekachev Feb 17, 2022
c4d9dcf
Updated sorting component
bsekachev Feb 17, 2022
584a161
Fixed cursor
bsekachev Feb 17, 2022
bef44a9
Fixed several issues with sorting
bsekachev Feb 17, 2022
4c6f73f
Added search field
bsekachev Feb 17, 2022
d4995d3
Fixed styles
bsekachev Feb 17, 2022
318b8a5
Fixed an exception with organization filtering.
Feb 17, 2022
965c633
Fix linter issues
Feb 17, 2022
432382a
Merge remote-tracking branch 'origin/develop' into bs/advanced_filtering
Feb 17, 2022
7e1ae47
Fix search
Marishka17 Feb 21, 2022
bf0298e
Add full description
Marishka17 Feb 21, 2022
01000f4
Updated headers
bsekachev Feb 22, 2022
0cd1879
Merged develop
bsekachev Feb 22, 2022
a3af766
Highlighted header
bsekachev Feb 22, 2022
2e7226b
Keep sorting while SPA is opened
bsekachev Feb 22, 2022
2e1de76
Fixed styles
bsekachev Feb 22, 2022
e6b4e67
Fixed style, added default value for search, disabled test
bsekachev Feb 22, 2022
d2227dd
Removed duplicated selector
bsekachev Feb 22, 2022
91127b6
Fixed sorting fields with _
bsekachev Feb 22, 2022
6bfe471
Added ability to open cards in a new tab
bsekachev Feb 22, 2022
1f9564a
Fixed UI search for tasks and projects
bsekachev Feb 22, 2022
abd7327
Add missed fields
Marishka17 Feb 22, 2022
82dd24a
Merge branch 'bs/advanced_filtering' of https://github.com/openvinoto…
Marishka17 Feb 22, 2022
59ad812
Update assets caused by default sort change
Marishka17 Feb 22, 2022
129b17d
Merge develop
Marishka17 Feb 22, 2022
46fe554
Fix test
Marishka17 Feb 22, 2022
022a489
Fixed cloud storages search, fixed tests
bsekachev Feb 24, 2022
8eba786
Jobs previews
bsekachev Feb 24, 2022
1e1c508
Add validation for filter & fix some bugs
Marishka17 Feb 24, 2022
e2c327b
Renamed resourceName to resource
bsekachev Feb 24, 2022
59a4a32
Fixed core test
bsekachev Feb 24, 2022
c7f26fa
Fix filter for membership
Marishka17 Feb 24, 2022
401fbd0
Merge bs/advanced_filtering
Marishka17 Feb 24, 2022
bcc7008
small fix
Marishka17 Feb 25, 2022
f8c52da
Merge remote-tracking branch 'origin/develop' into bs/advanced_filtering
Feb 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Basic page with jobs list, basic filtration to this list (<https://github.com/openvinotoolkit/cvat/pull/4258>)
- Added OpenCV.js TrackerMIL as tracking tool (<https://github.com/openvinotoolkit/cvat/pull/4200>)
- Ability to continue working from the latest frame where an annotator was before (<https://github.com/openvinotoolkit/cvat/pull/4297>)
- Advanced filtration and sorting for a list of jobs (<https://github.com/openvinotoolkit/cvat/pull/4319>)



### 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": "4.2.0",
"version": "4.2.1",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
57 changes: 11 additions & 46 deletions cvat-core/src/api-implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,13 @@ const config = require('./config');
const {
isBoolean,
isInteger,
isEnum,
isString,
checkFilter,
checkExclusiveFields,
camelToSnake,
checkObjectType,
} = require('./common');

const {
TaskStatus,
TaskMode,
DimensionType,
CloudStorageProviderType,
CloudStorageCredentialsType,
} = require('./enums');

const User = require('./user');
const { AnnotationFormats } = require('./annotation-formats');
const { ArgumentError } = require('./exceptions');
Expand Down Expand Up @@ -153,9 +144,9 @@ const config = require('./config');
cvat.jobs.get.implementation = async (filter) => {
checkFilter(filter, {
page: isInteger,
stage: isString,
state: isString,
assignee: isString,
filter: isString,
sort: isString,
search: isString,
taskID: isInteger,
jobID: isInteger,
});
Expand Down Expand Up @@ -190,32 +181,22 @@ const config = require('./config');
checkFilter(filter, {
page: isInteger,
projectId: isInteger,
name: isString,
id: isInteger,
owner: isString,
assignee: isString,
search: isString,
filter: isString,
ordering: isString,
status: isEnum.bind(TaskStatus),
mode: isEnum.bind(TaskMode),
dimension: isEnum.bind(DimensionType),
});

checkExclusiveFields(filter, ['id', 'search', 'projectId'], ['page']);
checkExclusiveFields(filter, ['id', 'projectId'], ['page']);

const searchParams = {};
for (const field of [
'name',
'owner',
'assignee',
'filter',
'search',
'ordering',
'status',
'mode',
'id',
'page',
'projectId',
'dimension',
]) {
if (Object.prototype.hasOwnProperty.call(filter, field)) {
searchParams[camelToSnake(field)] = filter[field];
Expand All @@ -234,17 +215,14 @@ const config = require('./config');
checkFilter(filter, {
id: isInteger,
page: isInteger,
name: isString,
assignee: isString,
owner: isString,
search: isString,
status: isEnum.bind(TaskStatus),
filter: isString,
});

checkExclusiveFields(filter, ['id', 'search'], ['page']);
checkExclusiveFields(filter, ['id'], ['page']);

const searchParams = {};
for (const field of ['name', 'assignee', 'owner', 'search', 'status', 'id', 'page']) {
for (const field of ['filter', 'search', 'status', 'id', 'page']) {
if (Object.prototype.hasOwnProperty.call(filter, field)) {
searchParams[camelToSnake(field)] = filter[field];
}
Expand All @@ -267,38 +245,25 @@ const config = require('./config');
cvat.cloudStorages.get.implementation = async (filter) => {
checkFilter(filter, {
page: isInteger,
displayName: isString,
resourceName: isString,
description: isString,
filter: isString,
id: isInteger,
owner: isString,
search: isString,
providerType: isEnum.bind(CloudStorageProviderType),
credentialsType: isEnum.bind(CloudStorageCredentialsType),
});

checkExclusiveFields(filter, ['id', 'search'], ['page']);

const searchParams = new URLSearchParams();
for (const field of [
'displayName',
'credentialsType',
'providerType',
'owner',
'filter',
'search',
'id',
'page',
'description',
]) {
if (Object.prototype.hasOwnProperty.call(filter, field)) {
searchParams.set(camelToSnake(field), filter[field]);
}
}

if (Object.prototype.hasOwnProperty.call(filter, 'resourceName')) {
searchParams.set('resource', filter.resourceName);
}

const cloudStoragesData = await serverProxy.cloudStorages.get(searchParams.toString());
const cloudStorages = cloudStoragesData.map((cloudStorage) => new CloudStorage(cloudStorage));
cloudStorages.count = cloudStoragesData.count;
Expand Down
4 changes: 2 additions & 2 deletions cvat-core/src/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2021 Intel Corporation
// Copyright (C) 2019-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -773,7 +773,7 @@ function build() {
/**
* @typedef {Object} CloudStorageFilter
* @property {string} displayName Check if displayName contains this value
* @property {string} resourceName Check if resourceName contains this value
* @property {string} resource Check if resource name contains this value
* @property {module:API.cvat.enums.ProviderType} providerType Check if providerType equal this value
* @property {integer} id Check if id equals this value
* @property {integer} page Get specific page
Expand Down
8 changes: 4 additions & 4 deletions cvat-core/src/cloud-storage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -174,13 +174,13 @@
},
/**
* Unique resource name
* @name resourceName
* @name resource
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
resourceName: {
resource: {
get: () => data.resource,
set: (value) => {
validateNotEmptyString(value);
Expand Down Expand Up @@ -456,7 +456,7 @@
display_name: this.displayName,
credentials_type: this.credentialsType,
provider_type: this.providerType,
resource: this.resourceName,
resource: this.resource,
manifests: this.manifests,
};

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

Expand Down Expand Up @@ -36,7 +36,7 @@
if (!(prop in fields)) {
throw new ArgumentError(`Unsupported filter property has been received: "${prop}"`);
} else if (!fields[prop](filter[prop])) {
throw new ArgumentError(`Received filter property "${prop}" is not satisfied for checker`);
throw new ArgumentError(`Received filter property "${prop}" does not satisfy API`);
}
}
}
Expand Down
10 changes: 8 additions & 2 deletions cvat-core/src/organization.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -360,7 +360,13 @@ Organization.prototype.deleteMembership.implementation = async function (members
Organization.prototype.leave.implementation = async function (user) {
checkObjectType('user', user, null, User);
if (typeof this.id === 'number') {
const result = await serverProxy.organizations.members(this.slug, 1, 10, { user: user.id });
const result = await serverProxy.organizations.members(this.slug, 1, 10, {
filter: JSON.stringify({
and: [{
'==': [{ var: 'user' }, user.id],
}],
}),
});
const [membership] = result.results;
if (!membership) {
throw new ServerError(`Could not find membership for user ${user.username} in organization ${this.slug}`);
Expand Down
2 changes: 1 addition & 1 deletion cvat-core/src/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -1891,7 +1891,7 @@
return '';
}

const frameData = await getPreview(this.taskId, this.jobID);
const frameData = await getPreview(this.taskId, this.id);
return frameData;
};

Expand Down
51 changes: 14 additions & 37 deletions cvat-core/tests/api/cloud-storages.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -36,7 +36,7 @@ describe('Feature: get cloud storages', () => {
expect(cloudStorage.id).toBe(1);
expect(cloudStorage.providerType).toBe('AWS_S3_BUCKET');
expect(cloudStorage.credentialsType).toBe('KEY_SECRET_KEY_PAIR');
expect(cloudStorage.resourceName).toBe('bucket');
expect(cloudStorage.resource).toBe('bucket');
expect(cloudStorage.displayName).toBe('Demonstration bucket');
expect(cloudStorage.manifests).toHaveLength(1);
expect(cloudStorage.manifests[0]).toBe('manifest.jsonl');
Expand All @@ -61,41 +61,18 @@ describe('Feature: get cloud storages', () => {
});

test('get cloud storages by filters', async () => {
const filters = [
new Map([
['providerType', 'AWS_S3_BUCKET'],
['resourceName', 'bucket'],
['displayName', 'Demonstration bucket'],
['credentialsType', 'KEY_SECRET_KEY_PAIR'],
['description', 'It is first bucket'],
]),
new Map([
['providerType', 'AZURE_CONTAINER'],
['resourceName', 'container'],
['displayName', 'Demonstration container'],
['credentialsType', 'ACCOUNT_NAME_TOKEN_PAIR'],
]),
new Map([
['providerType', 'GOOGLE_CLOUD_STORAGE'],
['resourceName', 'gcsbucket'],
['displayName', 'Demo GCS'],
['credentialsType', 'KEY_FILE_PATH'],
]),
];

const ids = [1, 2, 3];

await Promise.all(filters.map(async (_, idx) => {
const result = await window.cvat.cloudStorages.get(Object.fromEntries(filters[idx]));
const [cloudStorage] = result;
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(1);
expect(cloudStorage).toBeInstanceOf(CloudStorage);
expect(cloudStorage.id).toBe(ids[idx]);
filters[idx].forEach((value, key) => {
expect(cloudStorage[key]).toBe(value);
});
}));
const filter = {
and: [
{ '==': [{ var: 'display_name' }, 'Demonstration bucket'] },
{ '==': [{ var: 'resource_name' }, 'bucket'] },
{ '==': [{ var: 'description' }, 'It is first bucket'] },
{ '==': [{ var: 'provider_type' }, 'AWS_S3_BUCKET'] },
{ '==': [{ var: 'credentials_type' }, 'KEY_SECRET_KEY_PAIR'] },
],
};

const result = await window.cvat.cloudStorages.get({ filter: JSON.stringify(filter) });
expect(result).toBeInstanceOf(Array);
});

test('get cloud storage by invalid filters', async () => {
Expand Down
12 changes: 4 additions & 8 deletions cvat-core/tests/api/projects.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2021 Intel Corporation
// Copyright (C) 2019-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -54,16 +54,12 @@ describe('Feature: get projects', () => {

test('get projects by filters', async () => {
const result = await window.cvat.projects.get({
status: 'completed',
filter: '{"and":[{"==":[{"var":"status"},"completed"]}]}',
});
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(Project);
expect(result[0].id).toBe(2);
expect(result[0].status).toBe('completed');
expect(result).toBeInstanceOf(Array);
});

test('get projects by invalid filters', async () => {
test('get projects by invalid query', async () => {
expect(
window.cvat.projects.get({
unknown: '5',
Expand Down
Loading