Refactoring: Removed old docs from cvat-core, added more types, fixed import (#5572)
<!-- Raised an issue to propose your change (https://github.com/cvat-ai/cvat/issues). It helps to avoid duplication of efforts from multiple independent contributors. Discuss your ideas with maintainers to be sure that changes will be approved and merged. Read the [CONTRIBUTION](https://github.com/cvat-ai/cvat/blob/develop/CONTRIBUTING.md) guide. --> <!-- Provide a general summary of your changes in the Title above --> ### Motivation and context <!-- Why is this change required? What problem does it solve? If it fixes an open issue, please link to the issue here. Describe your changes in detail, add screenshots. --> ### How has this been tested? <!-- Please describe in detail how you tested your changes. Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc. --> ### Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. If an item isn't applicable by a reason then ~~explicitly strikethrough~~ the whole line. If you don't do that github will show an incorrect process for the pull request. If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [x] I submit my changes into the `develop` branch - [ ] I have added a description of my changes into [CHANGELOG](https://github.com/cvat-ai/cvat/blob/develop/CHANGELOG.md) file - [ ] I have updated the [documentation]( https://github.com/cvat-ai/cvat/blob/develop/README.md#documentation) accordingly - [ ] I have added tests to cover my changes - [ ] I have linked related issues ([read github docs]( https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)) - [ ] I have increased versions of npm packages if it is necessary ([cvat-canvas](https://github.com/cvat-ai/cvat/tree/develop/cvat-canvas#versioning), [cvat-core](https://github.com/cvat-ai/cvat/tree/develop/cvat-core#versioning), [cvat-data](https://github.com/cvat-ai/cvat/tree/develop/cvat-data#versioning) and [cvat-ui](https://github.com/cvat-ai/cvat/tree/develop/cvat-ui#versioning)) ### License - [x] I submit _my code changes_ under the same [MIT License]( https://github.com/cvat-ai/cvat/blob/develop/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern.main
parent
881c1aa5a0
commit
4e425303bb
@ -1,24 +0,0 @@
|
||||
// Copyright (C) 2019-2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
module.exports = {
|
||||
plugins: [],
|
||||
recurseDepth: 10,
|
||||
source: {
|
||||
includePattern: '.+\\.js(doc|x)?$',
|
||||
excludePattern: '(^|\\/|\\\\)_',
|
||||
},
|
||||
sourceType: 'module',
|
||||
tags: {
|
||||
allowUnknownTags: false,
|
||||
dictionaries: ['jsdoc', 'closure'],
|
||||
},
|
||||
templates: {
|
||||
cleverLinks: false,
|
||||
monospaceLinks: false,
|
||||
default: {
|
||||
outputSourceFiles: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,261 +1,264 @@
|
||||
// Copyright (C) 2019-2022 Intel Corporation
|
||||
// Copyright (C) 2022 CVAT.ai Corp
|
||||
// Copyright (C) 2022-2023 CVAT.ai Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
(() => {
|
||||
const serverProxy = require('./server-proxy').default;
|
||||
const { Task } = require('./session');
|
||||
const { ScriptingError } = require('./exceptions');
|
||||
|
||||
class AnnotationsSaver {
|
||||
constructor(version, collection, session) {
|
||||
this.sessionType = session instanceof Task ? 'task' : 'job';
|
||||
this.id = session.id;
|
||||
this.version = version;
|
||||
this.collection = collection;
|
||||
this.initialObjects = {};
|
||||
this.hash = this._getHash();
|
||||
|
||||
// We need use data from export instead of initialData
|
||||
// Otherwise we have differ keys order and JSON comparison code incorrect
|
||||
const exported = this.collection.export();
|
||||
|
||||
this._resetState();
|
||||
for (const shape of exported.shapes) {
|
||||
this.initialObjects.shapes[shape.id] = shape;
|
||||
}
|
||||
|
||||
for (const track of exported.tracks) {
|
||||
this.initialObjects.tracks[track.id] = track;
|
||||
}
|
||||
|
||||
for (const tag of exported.tags) {
|
||||
this.initialObjects.tags[tag.id] = tag;
|
||||
}
|
||||
import serverProxy from './server-proxy';
|
||||
import { Task } from './session';
|
||||
import { ScriptingError } from './exceptions';
|
||||
|
||||
export default class AnnotationsSaver {
|
||||
private sessionType: 'task' | 'job';
|
||||
private id: number;
|
||||
private version: number;
|
||||
private collection: any;
|
||||
private hash: string;
|
||||
private initialObjects: any;
|
||||
|
||||
constructor(version, collection, session) {
|
||||
this.sessionType = session instanceof Task ? 'task' : 'job';
|
||||
this.id = session.id;
|
||||
this.version = version;
|
||||
this.collection = collection;
|
||||
this.initialObjects = {};
|
||||
this.hash = this._getHash();
|
||||
|
||||
// We need use data from export instead of initialData
|
||||
// Otherwise we have differ keys order and JSON comparison code incorrect
|
||||
const exported = this.collection.export();
|
||||
|
||||
this._resetState();
|
||||
for (const shape of exported.shapes) {
|
||||
this.initialObjects.shapes[shape.id] = shape;
|
||||
}
|
||||
|
||||
_resetState() {
|
||||
this.initialObjects = {
|
||||
shapes: {},
|
||||
tracks: {},
|
||||
tags: {},
|
||||
};
|
||||
for (const track of exported.tracks) {
|
||||
this.initialObjects.tracks[track.id] = track;
|
||||
}
|
||||
|
||||
_getHash() {
|
||||
const exported = this.collection.export();
|
||||
return JSON.stringify(exported);
|
||||
for (const tag of exported.tags) {
|
||||
this.initialObjects.tags[tag.id] = tag;
|
||||
}
|
||||
}
|
||||
|
||||
async _request(data, action) {
|
||||
const result = await serverProxy.annotations.updateAnnotations(this.sessionType, this.id, data, action);
|
||||
_resetState() {
|
||||
this.initialObjects = {
|
||||
shapes: {},
|
||||
tracks: {},
|
||||
tags: {},
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
_getHash() {
|
||||
const exported = this.collection.export();
|
||||
return JSON.stringify(exported);
|
||||
}
|
||||
|
||||
async _put(data) {
|
||||
const result = await this._request(data, 'put');
|
||||
return result;
|
||||
}
|
||||
async _request(data, action) {
|
||||
const result = await serverProxy.annotations.updateAnnotations(this.sessionType, this.id, data, action);
|
||||
|
||||
async _create(created) {
|
||||
const result = await this._request(created, 'create');
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async _update(updated) {
|
||||
const result = await this._request(updated, 'update');
|
||||
return result;
|
||||
}
|
||||
async _put(data) {
|
||||
const result = await this._request(data, 'put');
|
||||
return result;
|
||||
}
|
||||
|
||||
async _delete(deleted) {
|
||||
const result = await this._request(deleted, 'delete');
|
||||
return result;
|
||||
}
|
||||
async _create(created) {
|
||||
const result = await this._request(created, 'create');
|
||||
return result;
|
||||
}
|
||||
|
||||
_split(exported) {
|
||||
const splitted = {
|
||||
created: {
|
||||
shapes: [],
|
||||
tracks: [],
|
||||
tags: [],
|
||||
},
|
||||
updated: {
|
||||
shapes: [],
|
||||
tracks: [],
|
||||
tags: [],
|
||||
},
|
||||
deleted: {
|
||||
shapes: [],
|
||||
tracks: [],
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
|
||||
const keys = [
|
||||
'id',
|
||||
'label_id',
|
||||
'group',
|
||||
'frame',
|
||||
'occluded',
|
||||
'z_order',
|
||||
'points',
|
||||
'rotation',
|
||||
'type',
|
||||
'shapes',
|
||||
'elements',
|
||||
'attributes',
|
||||
'value',
|
||||
'spec_id',
|
||||
'source',
|
||||
'outside',
|
||||
];
|
||||
|
||||
// Find created and updated objects
|
||||
for (const type of Object.keys(exported)) {
|
||||
for (const object of exported[type]) {
|
||||
if (object.id in this.initialObjects[type]) {
|
||||
const exportedHash = JSON.stringify(object, keys);
|
||||
const initialHash = JSON.stringify(this.initialObjects[type][object.id], keys);
|
||||
if (exportedHash !== initialHash) {
|
||||
splitted.updated[type].push(object);
|
||||
}
|
||||
} else if (typeof object.id === 'undefined') {
|
||||
splitted.created[type].push(object);
|
||||
} else {
|
||||
throw new ScriptingError(
|
||||
`Id of object is defined "${object.id}" but it absents in initial state`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
async _update(updated) {
|
||||
const result = await this._request(updated, 'update');
|
||||
return result;
|
||||
}
|
||||
|
||||
// Now find deleted objects
|
||||
const indexes = {
|
||||
shapes: exported.shapes.map((object) => +object.id),
|
||||
tracks: exported.tracks.map((object) => +object.id),
|
||||
tags: exported.tags.map((object) => +object.id),
|
||||
};
|
||||
async _delete(deleted) {
|
||||
const result = await this._request(deleted, 'delete');
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const id of Object.keys(this.initialObjects[type])) {
|
||||
if (!indexes[type].includes(+id)) {
|
||||
const object = this.initialObjects[type][id];
|
||||
splitted.deleted[type].push(object);
|
||||
_split(exported) {
|
||||
const splitted = {
|
||||
created: {
|
||||
shapes: [],
|
||||
tracks: [],
|
||||
tags: [],
|
||||
},
|
||||
updated: {
|
||||
shapes: [],
|
||||
tracks: [],
|
||||
tags: [],
|
||||
},
|
||||
deleted: {
|
||||
shapes: [],
|
||||
tracks: [],
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
|
||||
const keys = [
|
||||
'id',
|
||||
'label_id',
|
||||
'group',
|
||||
'frame',
|
||||
'occluded',
|
||||
'z_order',
|
||||
'points',
|
||||
'rotation',
|
||||
'type',
|
||||
'shapes',
|
||||
'elements',
|
||||
'attributes',
|
||||
'value',
|
||||
'spec_id',
|
||||
'source',
|
||||
'outside',
|
||||
];
|
||||
|
||||
// Find created and updated objects
|
||||
for (const type of Object.keys(exported)) {
|
||||
for (const object of exported[type]) {
|
||||
if (object.id in this.initialObjects[type]) {
|
||||
const exportedHash = JSON.stringify(object, keys);
|
||||
const initialHash = JSON.stringify(this.initialObjects[type][object.id], keys);
|
||||
if (exportedHash !== initialHash) {
|
||||
splitted.updated[type].push(object);
|
||||
}
|
||||
} else if (typeof object.id === 'undefined') {
|
||||
splitted.created[type].push(object);
|
||||
} else {
|
||||
throw new ScriptingError(
|
||||
`Id of object is defined "${object.id}" but it absents in initial state`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return splitted;
|
||||
}
|
||||
|
||||
_updateCreatedObjects(saved, indexes) {
|
||||
const savedLength = saved.tracks.length + saved.shapes.length + saved.tags.length;
|
||||
const indexesLength = indexes.tracks.length + indexes.shapes.length + indexes.tags.length;
|
||||
if (indexesLength !== savedLength) {
|
||||
throw new ScriptingError(
|
||||
`Number of indexes is differed by number of saved objects ${indexesLength} vs ${savedLength}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Updated IDs of created objects
|
||||
for (const type of Object.keys(indexes)) {
|
||||
for (let i = 0; i < indexes[type].length; i++) {
|
||||
const clientID = indexes[type][i];
|
||||
this.collection.objects[clientID].updateServerID(saved[type][i]);
|
||||
// Now find deleted objects
|
||||
const indexes = {
|
||||
shapes: exported.shapes.map((object) => +object.id),
|
||||
tracks: exported.tracks.map((object) => +object.id),
|
||||
tags: exported.tags.map((object) => +object.id),
|
||||
};
|
||||
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const id of Object.keys(this.initialObjects[type])) {
|
||||
if (!indexes[type].includes(+id)) {
|
||||
const object = this.initialObjects[type][id];
|
||||
splitted.deleted[type].push(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_receiveIndexes(exported) {
|
||||
// Receive client indexes before saving
|
||||
const indexes = {
|
||||
tracks: exported.tracks.map((track) => track.clientID),
|
||||
shapes: exported.shapes.map((shape) => shape.clientID),
|
||||
tags: exported.tags.map((tag) => tag.clientID),
|
||||
};
|
||||
|
||||
// Remove them from the request body
|
||||
exported.tracks
|
||||
.concat(exported.shapes)
|
||||
.concat(exported.tags)
|
||||
.map((value) => {
|
||||
delete value.clientID;
|
||||
return value;
|
||||
});
|
||||
|
||||
return indexes;
|
||||
return splitted;
|
||||
}
|
||||
|
||||
_updateCreatedObjects(saved, indexes) {
|
||||
const savedLength = saved.tracks.length + saved.shapes.length + saved.tags.length;
|
||||
const indexesLength = indexes.tracks.length + indexes.shapes.length + indexes.tags.length;
|
||||
if (indexesLength !== savedLength) {
|
||||
throw new ScriptingError(
|
||||
`Number of indexes is differed by number of saved objects ${indexesLength} vs ${savedLength}`,
|
||||
);
|
||||
}
|
||||
|
||||
async save(onUpdateArg) {
|
||||
const onUpdate = typeof onUpdateArg === 'function' ? onUpdateArg : (message) => {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
const exported = this.collection.export();
|
||||
const { flush } = this.collection;
|
||||
if (flush) {
|
||||
onUpdate('Created objects are being saved on the server');
|
||||
const indexes = this._receiveIndexes(exported);
|
||||
const savedData = await this._put({ ...exported, version: this.version });
|
||||
this.version = savedData.version;
|
||||
this.collection.flush = false;
|
||||
|
||||
this._updateCreatedObjects(savedData, indexes);
|
||||
|
||||
this._resetState();
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of savedData[type]) {
|
||||
this.initialObjects[type][object.id] = object;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const { created, updated, deleted } = this._split(exported);
|
||||
// Updated IDs of created objects
|
||||
for (const type of Object.keys(indexes)) {
|
||||
for (let i = 0; i < indexes[type].length; i++) {
|
||||
const clientID = indexes[type][i];
|
||||
this.collection.objects[clientID].updateServerID(saved[type][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_receiveIndexes(exported) {
|
||||
// Receive client indexes before saving
|
||||
const indexes = {
|
||||
tracks: exported.tracks.map((track) => track.clientID),
|
||||
shapes: exported.shapes.map((shape) => shape.clientID),
|
||||
tags: exported.tags.map((tag) => tag.clientID),
|
||||
};
|
||||
|
||||
// Remove them from the request body
|
||||
exported.tracks
|
||||
.concat(exported.shapes)
|
||||
.concat(exported.tags)
|
||||
.map((value) => {
|
||||
delete value.clientID;
|
||||
return value;
|
||||
});
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
onUpdate('Created objects are being saved on the server');
|
||||
const indexes = this._receiveIndexes(created);
|
||||
const createdData = await this._create({ ...created, version: this.version });
|
||||
this.version = createdData.version;
|
||||
async save(onUpdateArg) {
|
||||
const onUpdate = typeof onUpdateArg === 'function' ? onUpdateArg : (message) => {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
this._updateCreatedObjects(createdData, indexes);
|
||||
const exported = this.collection.export();
|
||||
const { flush } = this.collection;
|
||||
if (flush) {
|
||||
onUpdate('Created objects are being saved on the server');
|
||||
const indexes = this._receiveIndexes(exported);
|
||||
const savedData = await this._put({ ...exported, version: this.version });
|
||||
this.version = savedData.version;
|
||||
this.collection.flush = false;
|
||||
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of createdData[type]) {
|
||||
this.initialObjects[type][object.id] = object;
|
||||
}
|
||||
this._updateCreatedObjects(savedData, indexes);
|
||||
|
||||
this._resetState();
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of savedData[type]) {
|
||||
this.initialObjects[type][object.id] = object;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const { created, updated, deleted } = this._split(exported);
|
||||
|
||||
onUpdate('Updated objects are being saved on the server');
|
||||
this._receiveIndexes(updated);
|
||||
const updatedData = await this._update({ ...updated, version: this.version });
|
||||
this.version = updatedData.version;
|
||||
onUpdate('Created objects are being saved on the server');
|
||||
const indexes = this._receiveIndexes(created);
|
||||
const createdData = await this._create({ ...created, version: this.version });
|
||||
this.version = createdData.version;
|
||||
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of updatedData[type]) {
|
||||
this.initialObjects[type][object.id] = object;
|
||||
}
|
||||
this._updateCreatedObjects(createdData, indexes);
|
||||
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of createdData[type]) {
|
||||
this.initialObjects[type][object.id] = object;
|
||||
}
|
||||
}
|
||||
|
||||
onUpdate('Deleted objects are being deleted from the server');
|
||||
this._receiveIndexes(deleted);
|
||||
const deletedData = await this._delete({ ...deleted, version: this.version });
|
||||
this._version = deletedData.version;
|
||||
onUpdate('Updated objects are being saved on the server');
|
||||
this._receiveIndexes(updated);
|
||||
const updatedData = await this._update({ ...updated, version: this.version });
|
||||
this.version = updatedData.version;
|
||||
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of deletedData[type]) {
|
||||
delete this.initialObjects[type][object.id];
|
||||
}
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of updatedData[type]) {
|
||||
this.initialObjects[type][object.id] = object;
|
||||
}
|
||||
}
|
||||
|
||||
this.hash = this._getHash();
|
||||
}
|
||||
onUpdate('Deleted objects are being deleted from the server');
|
||||
this._receiveIndexes(deleted);
|
||||
const deletedData = await this._delete({ ...deleted, version: this.version });
|
||||
this.version = deletedData.version;
|
||||
|
||||
hasUnsavedChanges() {
|
||||
return this._getHash() !== this.hash;
|
||||
for (const type of Object.keys(this.initialObjects)) {
|
||||
for (const object of deletedData[type]) {
|
||||
delete this.initialObjects[type][object.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.hash = this._getHash();
|
||||
}
|
||||
|
||||
module.exports = AnnotationsSaver;
|
||||
})();
|
||||
hasUnsavedChanges(): boolean {
|
||||
return this._getHash() !== this.hash;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue