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) 2019-2022 Intel Corporation
|
||||||
// Copyright (C) 2022 CVAT.ai Corp
|
// Copyright (C) 2022-2023 CVAT.ai Corporation
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
(() => {
|
import serverProxy from './server-proxy';
|
||||||
const serverProxy = require('./server-proxy').default;
|
import { Task } from './session';
|
||||||
const { Task } = require('./session');
|
import { ScriptingError } from './exceptions';
|
||||||
const { ScriptingError } = require('./exceptions');
|
|
||||||
|
export default class AnnotationsSaver {
|
||||||
class AnnotationsSaver {
|
private sessionType: 'task' | 'job';
|
||||||
constructor(version, collection, session) {
|
private id: number;
|
||||||
this.sessionType = session instanceof Task ? 'task' : 'job';
|
private version: number;
|
||||||
this.id = session.id;
|
private collection: any;
|
||||||
this.version = version;
|
private hash: string;
|
||||||
this.collection = collection;
|
private initialObjects: any;
|
||||||
this.initialObjects = {};
|
|
||||||
this.hash = this._getHash();
|
constructor(version, collection, session) {
|
||||||
|
this.sessionType = session instanceof Task ? 'task' : 'job';
|
||||||
// We need use data from export instead of initialData
|
this.id = session.id;
|
||||||
// Otherwise we have differ keys order and JSON comparison code incorrect
|
this.version = version;
|
||||||
const exported = this.collection.export();
|
this.collection = collection;
|
||||||
|
this.initialObjects = {};
|
||||||
this._resetState();
|
this.hash = this._getHash();
|
||||||
for (const shape of exported.shapes) {
|
|
||||||
this.initialObjects.shapes[shape.id] = shape;
|
// 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();
|
||||||
for (const track of exported.tracks) {
|
|
||||||
this.initialObjects.tracks[track.id] = track;
|
this._resetState();
|
||||||
}
|
for (const shape of exported.shapes) {
|
||||||
|
this.initialObjects.shapes[shape.id] = shape;
|
||||||
for (const tag of exported.tags) {
|
|
||||||
this.initialObjects.tags[tag.id] = tag;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_resetState() {
|
for (const track of exported.tracks) {
|
||||||
this.initialObjects = {
|
this.initialObjects.tracks[track.id] = track;
|
||||||
shapes: {},
|
|
||||||
tracks: {},
|
|
||||||
tags: {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getHash() {
|
for (const tag of exported.tags) {
|
||||||
const exported = this.collection.export();
|
this.initialObjects.tags[tag.id] = tag;
|
||||||
return JSON.stringify(exported);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async _request(data, action) {
|
_resetState() {
|
||||||
const result = await serverProxy.annotations.updateAnnotations(this.sessionType, this.id, data, action);
|
this.initialObjects = {
|
||||||
|
shapes: {},
|
||||||
|
tracks: {},
|
||||||
|
tags: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
_getHash() {
|
||||||
}
|
const exported = this.collection.export();
|
||||||
|
return JSON.stringify(exported);
|
||||||
|
}
|
||||||
|
|
||||||
async _put(data) {
|
async _request(data, action) {
|
||||||
const result = await this._request(data, 'put');
|
const result = await serverProxy.annotations.updateAnnotations(this.sessionType, this.id, data, action);
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _create(created) {
|
return result;
|
||||||
const result = await this._request(created, 'create');
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _update(updated) {
|
async _put(data) {
|
||||||
const result = await this._request(updated, 'update');
|
const result = await this._request(data, 'put');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _delete(deleted) {
|
async _create(created) {
|
||||||
const result = await this._request(deleted, 'delete');
|
const result = await this._request(created, 'create');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_split(exported) {
|
async _update(updated) {
|
||||||
const splitted = {
|
const result = await this._request(updated, 'update');
|
||||||
created: {
|
return result;
|
||||||
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`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now find deleted objects
|
async _delete(deleted) {
|
||||||
const indexes = {
|
const result = await this._request(deleted, 'delete');
|
||||||
shapes: exported.shapes.map((object) => +object.id),
|
return result;
|
||||||
tracks: exported.tracks.map((object) => +object.id),
|
}
|
||||||
tags: exported.tags.map((object) => +object.id),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const type of Object.keys(this.initialObjects)) {
|
_split(exported) {
|
||||||
for (const id of Object.keys(this.initialObjects[type])) {
|
const splitted = {
|
||||||
if (!indexes[type].includes(+id)) {
|
created: {
|
||||||
const object = this.initialObjects[type][id];
|
shapes: [],
|
||||||
splitted.deleted[type].push(object);
|
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) {
|
// Now find deleted objects
|
||||||
const savedLength = saved.tracks.length + saved.shapes.length + saved.tags.length;
|
const indexes = {
|
||||||
const indexesLength = indexes.tracks.length + indexes.shapes.length + indexes.tags.length;
|
shapes: exported.shapes.map((object) => +object.id),
|
||||||
if (indexesLength !== savedLength) {
|
tracks: exported.tracks.map((object) => +object.id),
|
||||||
throw new ScriptingError(
|
tags: exported.tags.map((object) => +object.id),
|
||||||
`Number of indexes is differed by number of saved objects ${indexesLength} vs ${savedLength}`,
|
};
|
||||||
);
|
|
||||||
}
|
for (const type of Object.keys(this.initialObjects)) {
|
||||||
|
for (const id of Object.keys(this.initialObjects[type])) {
|
||||||
// Updated IDs of created objects
|
if (!indexes[type].includes(+id)) {
|
||||||
for (const type of Object.keys(indexes)) {
|
const object = this.initialObjects[type][id];
|
||||||
for (let i = 0; i < indexes[type].length; i++) {
|
splitted.deleted[type].push(object);
|
||||||
const clientID = indexes[type][i];
|
|
||||||
this.collection.objects[clientID].updateServerID(saved[type][i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_receiveIndexes(exported) {
|
return splitted;
|
||||||
// Receive client indexes before saving
|
}
|
||||||
const indexes = {
|
|
||||||
tracks: exported.tracks.map((track) => track.clientID),
|
_updateCreatedObjects(saved, indexes) {
|
||||||
shapes: exported.shapes.map((shape) => shape.clientID),
|
const savedLength = saved.tracks.length + saved.shapes.length + saved.tags.length;
|
||||||
tags: exported.tags.map((tag) => tag.clientID),
|
const indexesLength = indexes.tracks.length + indexes.shapes.length + indexes.tags.length;
|
||||||
};
|
if (indexesLength !== savedLength) {
|
||||||
|
throw new ScriptingError(
|
||||||
// Remove them from the request body
|
`Number of indexes is differed by number of saved objects ${indexesLength} vs ${savedLength}`,
|
||||||
exported.tracks
|
);
|
||||||
.concat(exported.shapes)
|
|
||||||
.concat(exported.tags)
|
|
||||||
.map((value) => {
|
|
||||||
delete value.clientID;
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
return indexes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(onUpdateArg) {
|
// Updated IDs of created objects
|
||||||
const onUpdate = typeof onUpdateArg === 'function' ? onUpdateArg : (message) => {
|
for (const type of Object.keys(indexes)) {
|
||||||
console.log(message);
|
for (let i = 0; i < indexes[type].length; i++) {
|
||||||
};
|
const clientID = indexes[type][i];
|
||||||
|
this.collection.objects[clientID].updateServerID(saved[type][i]);
|
||||||
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);
|
_receiveIndexes(exported) {
|
||||||
const savedData = await this._put({ ...exported, version: this.version });
|
// Receive client indexes before saving
|
||||||
this.version = savedData.version;
|
const indexes = {
|
||||||
this.collection.flush = false;
|
tracks: exported.tracks.map((track) => track.clientID),
|
||||||
|
shapes: exported.shapes.map((shape) => shape.clientID),
|
||||||
this._updateCreatedObjects(savedData, indexes);
|
tags: exported.tags.map((tag) => tag.clientID),
|
||||||
|
};
|
||||||
this._resetState();
|
|
||||||
for (const type of Object.keys(this.initialObjects)) {
|
// Remove them from the request body
|
||||||
for (const object of savedData[type]) {
|
exported.tracks
|
||||||
this.initialObjects[type][object.id] = object;
|
.concat(exported.shapes)
|
||||||
}
|
.concat(exported.tags)
|
||||||
}
|
.map((value) => {
|
||||||
} else {
|
delete value.clientID;
|
||||||
const { created, updated, deleted } = this._split(exported);
|
return value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return indexes;
|
||||||
|
}
|
||||||
|
|
||||||
onUpdate('Created objects are being saved on the server');
|
async save(onUpdateArg) {
|
||||||
const indexes = this._receiveIndexes(created);
|
const onUpdate = typeof onUpdateArg === 'function' ? onUpdateArg : (message) => {
|
||||||
const createdData = await this._create({ ...created, version: this.version });
|
console.log(message);
|
||||||
this.version = createdData.version;
|
};
|
||||||
|
|
||||||
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)) {
|
this._updateCreatedObjects(savedData, indexes);
|
||||||
for (const object of createdData[type]) {
|
|
||||||
this.initialObjects[type][object.id] = object;
|
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');
|
onUpdate('Created objects are being saved on the server');
|
||||||
this._receiveIndexes(updated);
|
const indexes = this._receiveIndexes(created);
|
||||||
const updatedData = await this._update({ ...updated, version: this.version });
|
const createdData = await this._create({ ...created, version: this.version });
|
||||||
this.version = updatedData.version;
|
this.version = createdData.version;
|
||||||
|
|
||||||
for (const type of Object.keys(this.initialObjects)) {
|
this._updateCreatedObjects(createdData, indexes);
|
||||||
for (const object of updatedData[type]) {
|
|
||||||
this.initialObjects[type][object.id] = object;
|
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');
|
onUpdate('Updated objects are being saved on the server');
|
||||||
this._receiveIndexes(deleted);
|
this._receiveIndexes(updated);
|
||||||
const deletedData = await this._delete({ ...deleted, version: this.version });
|
const updatedData = await this._update({ ...updated, version: this.version });
|
||||||
this._version = deletedData.version;
|
this.version = updatedData.version;
|
||||||
|
|
||||||
for (const type of Object.keys(this.initialObjects)) {
|
for (const type of Object.keys(this.initialObjects)) {
|
||||||
for (const object of deletedData[type]) {
|
for (const object of updatedData[type]) {
|
||||||
delete this.initialObjects[type][object.id];
|
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() {
|
for (const type of Object.keys(this.initialObjects)) {
|
||||||
return this._getHash() !== this.hash;
|
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