Skip to content

Commit 695fc37

Browse files
bsekachevnmanovic
authored andcommitted
User interface with react and antd (#755)
* Login page, router * Registration * Tasks view
1 parent 7e214f8 commit 695fc37

File tree

151 files changed

+15813
-12566
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+15813
-12566
lines changed

.vscode/settings.json

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
"directory": "./cvat-canvas",
1515
"changeProcessCWD": true
1616
},
17+
{
18+
"directory": "./cvat-ui",
19+
"changeProcessCWD": true
20+
},
1721
{
1822
"directory": ".",
1923
"changeProcessCWD": true

cvat-canvas/package-lock.json

+8,581
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cvat-core/.dockerignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dist
2+
docs
3+
node_modules
4+
reports
5+

cvat-core/.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@
5050
"func-names": [0],
5151
"valid-typeof": [0],
5252
"no-console": [0], // this rule deprecates console.log, console.warn etc. because "it is not good in production code"
53+
"max-classes-per-file": [0],
5354
},
5455
};

cvat-core/src/annotation-format.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@
138138
handler_file: initialData.handler_file,
139139
};
140140

141-
data.dumpers = initialData.dumpers.map(el => new Dumper(el));
142-
data.loaders = initialData.loaders.map(el => new Loader(el));
141+
data.dumpers = initialData.dumpers.map((el) => new Dumper(el));
142+
data.loaders = initialData.loaders.map((el) => new Loader(el));
143143

144144
// Now all fields are readonly
145145
Object.defineProperties(this, {

cvat-core/src/annotations-collection.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -177,19 +177,19 @@
177177

178178
export() {
179179
const data = {
180-
tracks: this.tracks.filter(track => !track.removed)
181-
.map(track => track.toJSON()),
180+
tracks: this.tracks.filter((track) => !track.removed)
181+
.map((track) => track.toJSON()),
182182
shapes: Object.values(this.shapes)
183183
.reduce((accumulator, value) => {
184184
accumulator.push(...value);
185185
return accumulator;
186-
}, []).filter(shape => !shape.removed)
187-
.map(shape => shape.toJSON()),
186+
}, []).filter((shape) => !shape.removed)
187+
.map((shape) => shape.toJSON()),
188188
tags: Object.values(this.tags).reduce((accumulator, value) => {
189189
accumulator.push(...value);
190190
return accumulator;
191-
}, []).filter(tag => !tag.removed)
192-
.map(tag => tag.toJSON()),
191+
}, []).filter((tag) => !tag.removed)
192+
.map((tag) => tag.toJSON()),
193193
};
194194

195195
return data;
@@ -200,7 +200,7 @@
200200
const shapes = this.shapes[frame] || [];
201201
const tags = this.tags[frame] || [];
202202

203-
const objects = tracks.concat(shapes).concat(tags).filter(object => !object.removed);
203+
const objects = tracks.concat(shapes).concat(tags).filter((object) => !object.removed);
204204
// filtering here
205205

206206
const objectStates = [];
@@ -370,7 +370,7 @@
370370

371371
const clientID = ++this.count;
372372
const track = {
373-
frame: Math.min.apply(null, Object.keys(keyframes).map(frame => +frame)),
373+
frame: Math.min.apply(null, Object.keys(keyframes).map((frame) => +frame)),
374374
shapes: Object.values(keyframes),
375375
group: 0,
376376
label_id: label.id,
@@ -577,7 +577,7 @@
577577

578578
if (objectType === 'track') {
579579
const keyframes = Object.keys(object.shapes)
580-
.sort((a, b) => +a - +b).map(el => +el);
580+
.sort((a, b) => +a - +b).map((el) => +el);
581581

582582
let prevKeyframe = keyframes[0];
583583
let visible = false;
@@ -699,13 +699,13 @@
699699
} else if (state.objectType === 'track') {
700700
constructed.tracks.push({
701701
attributes: attributes
702-
.filter(attr => !labelAttributes[attr.spec_id].mutable),
702+
.filter((attr) => !labelAttributes[attr.spec_id].mutable),
703703
frame: state.frame,
704704
group: 0,
705705
label_id: state.label.id,
706706
shapes: [{
707707
attributes: attributes
708-
.filter(attr => labelAttributes[attr.spec_id].mutable),
708+
.filter((attr) => labelAttributes[attr.spec_id].mutable),
709709
frame: state.frame,
710710
occluded: state.occluded || false,
711711
outside: false,

cvat-core/src/annotations-objects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@
273273
lock: this.lock,
274274
zOrder: this.zOrder,
275275
points: [...this.points],
276-
attributes: Object.assign({}, this.attributes),
276+
attributes: { ...this.attributes },
277277
label: this.label,
278278
group: this.group,
279279
color: this.color,

cvat-core/src/api-implementation.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
cvat.server.formats.implementation = async () => {
4949
const result = await serverProxy.server.formats();
50-
return result.map(el => new AnnotationFormat(el));
50+
return result.map((el) => new AnnotationFormat(el));
5151
};
5252

5353
cvat.server.register.implementation = async (username, firstName, lastName,
@@ -82,7 +82,7 @@
8282
users = await serverProxy.users.getUsers();
8383
}
8484

85-
users = users.map(user => new User(user));
85+
users = users.map((user) => new User(user));
8686
return users;
8787
};
8888

@@ -116,8 +116,11 @@
116116

117117
// If task was found by its id, then create task instance and get Job instance from it
118118
if (tasks !== null && tasks.length) {
119+
tasks[0].owner = await serverProxy.users.getUsers(tasks[0].owner);
120+
tasks[0].assignee = await serverProxy.users.getUsers(tasks[0].assignee);
119121
const task = new Task(tasks[0]);
120-
return filter.jobID ? task.jobs.filter(job => job.id === filter.jobID) : task.jobs;
122+
return filter.jobID ? task.jobs
123+
.filter((job) => job.id === filter.jobID) : task.jobs;
121124
}
122125

123126
return [];
@@ -158,8 +161,13 @@
158161
}
159162
}
160163

164+
const users = await serverProxy.users.getUsers();
161165
const tasksData = await serverProxy.tasks.getTasks(searchParams.toString());
162-
const tasks = tasksData.map(task => new Task(task));
166+
const tasks = tasksData.map((task) => {
167+
[task.owner] = users.filter((user) => user.id === task.owner);
168+
[task.assignee] = users.filter((user) => user.id === task.assignee);
169+
return new Task(task);
170+
});
163171
tasks.count = tasksData.count;
164172

165173
return tasks;

cvat-core/src/frames.js

+21
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@
105105
});
106106
};
107107

108+
async function getPreview(taskID) {
109+
return new Promise(async (resolve, reject) => {
110+
try {
111+
// Just go to server and get preview (no any cache)
112+
const result = await serverProxy.frames.getPreview(taskID);
113+
if (isNode) {
114+
resolve(global.Buffer.from(result, 'binary').toString('base64'));
115+
} else if (isBrowser) {
116+
const reader = new FileReader();
117+
reader.onload = () => {
118+
resolve(reader.result);
119+
};
120+
reader.readAsDataURL(result);
121+
}
122+
} catch (error) {
123+
reject(error);
124+
}
125+
});
126+
}
127+
108128
async function getFrame(taskID, mode, frame) {
109129
if (!(taskID in frameDataCache)) {
110130
frameDataCache[taskID] = {};
@@ -140,5 +160,6 @@
140160
module.exports = {
141161
FrameData,
142162
getFrame,
163+
getPreview,
143164
};
144165
})();

cvat-core/src/server-proxy.js

+32-4
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,20 @@
342342
}
343343
}
344344

345-
async function getUsers() {
345+
async function getUsers(id = null) {
346346
const { backendAPI } = config;
347347

348348
let response = null;
349349
try {
350-
response = await Axios.get(`${backendAPI}/users`, {
351-
proxy: config.proxy,
352-
});
350+
if (id === null) {
351+
response = await Axios.get(`${backendAPI}/users`, {
352+
proxy: config.proxy,
353+
});
354+
} else {
355+
response = await Axios.get(`${backendAPI}/users/${id}`, {
356+
proxy: config.proxy,
357+
});
358+
}
353359
} catch (errorData) {
354360
throw generateError(errorData, 'Could not get users from the server');
355361
}
@@ -372,6 +378,27 @@
372378
return response.data;
373379
}
374380

381+
async function getPreview(tid) {
382+
const { backendAPI } = config;
383+
384+
let response = null;
385+
try {
386+
// TODO: change 0 frame to preview
387+
response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/0`, {
388+
proxy: config.proxy,
389+
responseType: 'blob',
390+
});
391+
} catch (errorData) {
392+
const code = errorData.response ? errorData.response.status : errorData.code;
393+
throw new ServerError(
394+
`Could not get preview frame for the task ${tid} from the server`,
395+
code,
396+
);
397+
}
398+
399+
return response.data;
400+
}
401+
375402
async function getData(tid, frame) {
376403
const { backendAPI } = config;
377404

@@ -567,6 +594,7 @@
567594
value: Object.freeze({
568595
getData,
569596
getMeta,
597+
getPreview,
570598
}),
571599
writable: false,
572600
},

cvat-core/src/session.js

+35-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
(() => {
1111
const PluginRegistry = require('./plugins');
1212
const serverProxy = require('./server-proxy');
13-
const { getFrame } = require('./frames');
13+
const { getFrame, getPreview } = require('./frames');
1414
const { ArgumentError } = require('./exceptions');
1515
const { TaskStatus } = require('./enums');
1616
const { Label } = require('./labels');
@@ -109,6 +109,11 @@
109109
.apiWrapper.call(this, prototype.frames.get, frame);
110110
return result;
111111
},
112+
async preview() {
113+
const result = await PluginRegistry
114+
.apiWrapper.call(this, prototype.frames.preview);
115+
return result;
116+
},
112117
},
113118
writable: true,
114119
}),
@@ -380,6 +385,17 @@
380385
* @throws {module:API.cvat.exceptions.ServerError}
381386
* @throws {module:API.cvat.exceptions.ArgumentError}
382387
*/
388+
/**
389+
* Get the first frame of a task for preview
390+
* @method preview
391+
* @memberof Session.frames
392+
* @returns {string} - jpeg encoded image
393+
* @instance
394+
* @async
395+
* @throws {module:API.cvat.exceptions.PluginError}
396+
* @throws {module:API.cvat.exceptions.ServerError}
397+
* @throws {module:API.cvat.exceptions.ArgumentError}
398+
*/
383399

384400
/**
385401
* Namespace is used for an interaction with logs
@@ -619,6 +635,7 @@
619635

620636
this.frames = {
621637
get: Object.getPrototypeOf(this).frames.get.bind(this),
638+
preview: Object.getPrototypeOf(this).frames.preview.bind(this),
622639
};
623640
}
624641

@@ -780,9 +797,9 @@
780797
get: () => data.mode,
781798
},
782799
/**
783-
* Identificator of a user who has created the task
800+
* Instance of a user who has created the task
784801
* @name owner
785-
* @type {integer}
802+
* @type {module:API.cvat.classes.User}
786803
* @memberof module:API.cvat.classes.Task
787804
* @readonly
788805
* @instance
@@ -791,9 +808,9 @@
791808
get: () => data.owner,
792809
},
793810
/**
794-
* Identificator of a user who is responsible for the task
811+
* Instance of a user who is responsible for the task
795812
* @name assignee
796-
* @type {integer}
813+
* @type {module:API.cvat.classes.User}
797814
* @memberof module:API.cvat.classes.Task
798815
* @instance
799816
* @throws {module:API.cvat.exceptions.ArgumentError}
@@ -1122,6 +1139,7 @@
11221139

11231140
this.frames = {
11241141
get: Object.getPrototypeOf(this).frames.get.bind(this),
1142+
preview: Object.getPrototypeOf(this).frames.preview.bind(this),
11251143
};
11261144
}
11271145

@@ -1218,6 +1236,11 @@
12181236
return frameData;
12191237
};
12201238

1239+
Job.prototype.frames.preview.implementation = async function () {
1240+
const frameData = await getPreview(this.task.id);
1241+
return frameData;
1242+
};
1243+
12211244
// TODO: Check filter for annotations
12221245
Job.prototype.annotations.get.implementation = async function (frame, filter) {
12231246
if (frame < this.startFrame || frame > this.stopFrame) {
@@ -1293,7 +1316,7 @@
12931316
name: this.name,
12941317
bug_tracker: this.bugTracker,
12951318
z_order: this.zOrder,
1296-
labels: [...this.labels.map(el => el.toJSON())],
1319+
labels: [...this.labels.map((el) => el.toJSON())],
12971320
};
12981321

12991322
await serverProxy.tasks.saveTask(this.id, taskData);
@@ -1302,7 +1325,7 @@
13021325

13031326
const taskData = {
13041327
name: this.name,
1305-
labels: this.labels.map(el => el.toJSON()),
1328+
labels: this.labels.map((el) => el.toJSON()),
13061329
image_quality: this.imageQuality,
13071330
z_order: Boolean(this.zOrder),
13081331
};
@@ -1358,6 +1381,11 @@
13581381
return result;
13591382
};
13601383

1384+
Task.prototype.frames.preview.implementation = async function () {
1385+
const frameData = await getPreview(this.id);
1386+
return frameData;
1387+
};
1388+
13611389
// TODO: Check filter for annotations
13621390
Task.prototype.annotations.get.implementation = async function (frame, filter) {
13631391
if (!Number.isInteger(frame) || frame < 0) {

cvat-core/tests/api/frames.js

+14
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,17 @@ describe('Feature: get frame data', () => {
6969
expect(typeof (frameData)).toBe('string');
7070
});
7171
});
72+
73+
describe('Feature: get frame preview', () => {
74+
test('get frame preview for a task', async () => {
75+
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
76+
const frame = await task.frames.preview();
77+
expect(typeof (frame)).toBe('string');
78+
});
79+
80+
test('get frame preview for a job', async () => {
81+
const job = (await window.cvat.jobs.get({ jobID: 100 }))[0];
82+
const frame = await job.frames.preview();
83+
expect(typeof (frame)).toBe('string');
84+
});
85+
});

0 commit comments

Comments
 (0)