Merge branch 'develop' into do/cypress_test_pr_2203_error_сannot_read_property_at_saving_job

main
Dmitriy Oparin 5 years ago
commit f4a6764b7c

@ -26,6 +26,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Django templates for email and user guide (<https://github.com/openvinotoolkit/cvat/pull/2412>)
- Saving relative paths in dummy chunks instead of absolute (<https://github.com/openvinotoolkit/cvat/pull/2424>)
- Objects with a specific label cannot be displayed if at least one tag with the label exist (<https://github.com/openvinotoolkit/cvat/pull/2435>)
- Wrong attribute can be removed in labels editor (<https://github.com/openvinotoolkit/cvat/pull/2436>)
- UI fails with the error "Cannot read property 'label' of undefined" (<https://github.com/openvinotoolkit/cvat/pull/2442>)
- Exception: "Value must be a user instance" (<https://github.com/openvinotoolkit/cvat/pull/2441>)
- Reset zoom option doesn't work in tag annotation mode (<https://github.com/openvinotoolkit/cvat/pull/2443>)
- Canvas is busy error (<https://github.com/openvinotoolkit/cvat/pull/2437>)
### Security

@ -21,8 +21,6 @@ COPY cvat-canvas/package*.json /tmp/cvat-canvas/
COPY cvat-ui/package*.json /tmp/cvat-ui/
COPY cvat-data/package*.json /tmp/cvat-data/
RUN npm config set loglevel info
# Install cvat-data dependencies
WORKDIR /tmp/cvat-data/
RUN npm ci

@ -1867,9 +1867,9 @@
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
},
"axios": {
"version": "0.20.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz",
"integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==",
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
"integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
"requires": {
"follow-redirects": "^1.10.0"
}

@ -33,7 +33,7 @@
"webpack-cli": "^3.3.2"
},
"dependencies": {
"axios": "^0.20.0",
"axios": "^0.21.0",
"browser-or-node": "^1.2.1",
"cvat-data": "../cvat-data",
"detect-browser": "^5.2.0",

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.10.0",
"version": "1.10.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1213,9 +1213,9 @@
"dev": true
},
"@types/react": {
"version": "16.9.53",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.53.tgz",
"integrity": "sha512-4nW60Sd4L7+WMXH1D6jCdVftuW7j4Za6zdp6tJ33Rqv0nk1ZAmQKML9ZLD4H0dehA3FZxXR/GM8gXplf82oNGw==",
"version": "16.9.54",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.54.tgz",
"integrity": "sha512-GhawhYraQZpGFO2hVMArjPrYbnA/6+DS8SubK8IPhhVClmKqANihsRenOm5E0mvqK0m/BKoqVktA1O1+Xvlz9w==",
"requires": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@ -1231,9 +1231,9 @@
}
},
"@types/react-dom": {
"version": "16.9.8",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
"integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
"version": "16.9.9",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz",
"integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==",
"requires": {
"@types/react": "*"
}
@ -1854,9 +1854,9 @@
}
},
"antd": {
"version": "3.26.18",
"resolved": "https://registry.npmjs.org/antd/-/antd-3.26.18.tgz",
"integrity": "sha512-TPuacNJJNPji+LnapU46uWGqi+6JlyH75paMNs95IH0F7gGYtp4oSkua88gGsoAaUbDxTIF+cWI9mdIsr7ywlw==",
"version": "3.26.20",
"resolved": "https://registry.npmjs.org/antd/-/antd-3.26.20.tgz",
"integrity": "sha512-VIous4ofZfxFtd9K1h9MpRX2sDDpj3QcOFi3YgIc9B/uyDli/GlLb8SWKfQfJaMkaxwatIv503dag2Tog+hiEg==",
"requires": {
"@ant-design/create-react-context": "^0.2.4",
"@ant-design/icons": "~2.1.1",
@ -3779,11 +3779,10 @@
}
},
"create-react-class": {
"version": "15.6.3",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz",
"integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==",
"version": "15.7.0",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz",
"integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==",
"requires": {
"fbjs": "^0.8.9",
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
@ -3957,9 +3956,9 @@
}
},
"csstype": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz",
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA=="
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz",
"integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ=="
},
"currently-unhandled": {
"version": "0.4.1",
@ -17052,7 +17051,7 @@
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "",
"resolved": false,
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"optional": true,
"requires": {
@ -17067,7 +17066,7 @@
},
"gauge": {
"version": "2.7.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true,
"requires": {
@ -17083,7 +17082,7 @@
},
"glob": {
"version": "7.1.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"optional": true,
"requires": {
@ -17121,7 +17120,7 @@
},
"inflight": {
"version": "1.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"optional": true,
"requires": {
@ -17173,7 +17172,7 @@
},
"minipass": {
"version": "2.9.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"optional": true,
"requires": {
@ -17183,7 +17182,7 @@
},
"minizlib": {
"version": "1.3.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"optional": true,
"requires": {
@ -17218,7 +17217,7 @@
},
"node-pre-gyp": {
"version": "0.14.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
"optional": true,
"requires": {
@ -17272,7 +17271,7 @@
},
"npmlog": {
"version": "4.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true,
"requires": {
@ -17296,7 +17295,7 @@
},
"once": {
"version": "1.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": {
@ -17366,7 +17365,7 @@
},
"rimraf": {
"version": "2.7.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"optional": true,
"requires": {
@ -17446,7 +17445,7 @@
},
"tar": {
"version": "4.4.13",
"resolved": "",
"resolved": false,
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"optional": true,
"requires": {
@ -17476,13 +17475,13 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
},
"yallist": {
"version": "3.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"optional": true
}
@ -28684,9 +28683,9 @@
}
},
"rc-switch": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.0.tgz",
"integrity": "sha512-Isas+egaK6qSk64jaEw4GgPStY4umYDbT7ZY93bZF1Af+b/JEsKsJdNOU2qG3WI0Z6tXo2DDq0kJCv8Yhu0zww==",
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.2.tgz",
"integrity": "sha512-qaK7mY4FLDKy99Hq3A1tf8CcqfzKtHp9LPX8WTnZ0MzdHCTneSARb1XD7Eqeu8BactasYGsi2bF9p18Q+/5JEw==",
"requires": {
"classnames": "^2.2.1",
"prop-types": "^15.5.6",
@ -28928,15 +28927,43 @@
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-redux": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz",
"integrity": "sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==",
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz",
"integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==",
"requires": {
"@babel/runtime": "^7.5.5",
"hoist-non-react-statics": "^3.3.0",
"@babel/runtime": "^7.12.1",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^16.9.0"
"react-is": "^16.13.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
}
}
},
"react-router": {
@ -32254,9 +32281,9 @@
"dev": true
},
"whatwg-fetch": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz",
"integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ=="
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz",
"integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A=="
},
"which": {
"version": "1.3.1",

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.10.0",
"version": "1.10.6",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
@ -49,15 +49,15 @@
"dependencies": {
"@types/lodash": "^4.14.165",
"@types/platform": "^1.3.3",
"@types/react": "^16.9.53",
"@types/react": "^16.9.54",
"@types/react-color": "^3.0.4",
"@types/react-dom": "^16.9.0",
"@types/react-dom": "^16.9.9",
"@types/react-redux": "^7.1.2",
"@types/react-router": "^5.0.5",
"@types/react-router-dom": "^5.1.6",
"@types/react-share": "^3.0.3",
"@types/redux-logger": "^3.0.8",
"antd": "^3.26.18",
"antd": "^3.26.20",
"copy-to-clipboard": "^3.3.1",
"cvat-canvas": "file:../cvat-canvas",
"cvat-core": "file:../cvat-core",
@ -72,7 +72,7 @@
"react-cookie": "^4.0.3",
"react-dom": "^16.14.0",
"react-hotkeys": "^2.0.0",
"react-redux": "^7.1.1",
"react-redux": "^7.2.2",
"react-router": "^5.1.0",
"react-router-dom": "^5.1.0",
"react-share": "^3.0.1",

@ -10,7 +10,9 @@ import Icon from 'antd/lib/icon';
import Layout from 'antd/lib/layout/layout';
import Slider, { SliderValue } from 'antd/lib/slider';
import { ColorBy, GridColor, ObjectType, ContextMenuType, Workspace, ShapeType } from 'reducers/interfaces';
import {
ColorBy, GridColor, ObjectType, ContextMenuType, Workspace, ShapeType,
} from 'reducers/interfaces';
import { LogType } from 'cvat-logger';
import { Canvas } from 'cvat-canvas-wrapper';
import getCore from 'cvat-core-wrapper';
@ -217,10 +219,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
this.updateCanvas();
}
if (
prevProps.frame !== frameData.number &&
((resetZoom && workspace !== Workspace.ATTRIBUTE_ANNOTATION) || workspace === Workspace.TAG_ANNOTATION)
) {
if (prevProps.frame !== frameData.number && resetZoom && workspace !== Workspace.ATTRIBUTE_ANNOTATION) {
canvasInstance.html().addEventListener(
'canvas.setup',
() => {
@ -304,7 +303,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
}
private onCanvasShapeDrawn = (event: any): void => {
const { jobInstance, activeLabelID, activeObjectType, frame, onShapeDrawn, onCreateAnnotations } = this.props;
const {
jobInstance, activeLabelID, activeObjectType, frame, onShapeDrawn, onCreateAnnotations,
} = this.props;
if (!event.detail.continue) {
onShapeDrawn();
@ -327,7 +328,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
};
private onCanvasObjectsMerged = (event: any): void => {
const { jobInstance, frame, onMergeAnnotations, onMergeObjects } = this.props;
const {
jobInstance, frame, onMergeAnnotations, onMergeObjects,
} = this.props;
onMergeObjects(false);
@ -340,7 +343,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
};
private onCanvasObjectsGroupped = (event: any): void => {
const { jobInstance, frame, onGroupAnnotations, onGroupObjects } = this.props;
const {
jobInstance, frame, onGroupAnnotations, onGroupObjects,
} = this.props;
onGroupObjects(false);
@ -349,7 +354,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
};
private onCanvasTrackSplitted = (event: any): void => {
const { jobInstance, frame, onSplitAnnotations, onSplitTrack } = this.props;
const {
jobInstance, frame, onSplitAnnotations, onSplitTrack,
} = this.props;
onSplitTrack(false);
@ -429,7 +436,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
};
private onCanvasCursorMoved = async (event: any): Promise<void> => {
const { jobInstance, activatedStateID, workspace, onActivateObject } = this.props;
const {
jobInstance, activatedStateID, workspace, onActivateObject,
} = this.props;
if (workspace !== Workspace.STANDARD) {
return;
@ -560,7 +569,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
}
private updateShapesView(): void {
const { annotations, opacity, colorBy, outlined, outlineColor } = this.props;
const {
annotations, opacity, colorBy, outlined, outlineColor,
} = this.props;
for (const state of annotations) {
let shapeColor = '';
@ -588,7 +599,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
}
private updateCanvas(): void {
const { curZLayer, annotations, frameData, canvasInstance } = this.props;
const {
curZLayer, annotations, frameData, canvasInstance,
} = this.props;
if (frameData !== null) {
canvasInstance.setup(

@ -109,7 +109,7 @@ function PlayerButtons(props: Props): JSX.Element {
<Popover
trigger='contextMenu'
placement='bottom'
content={
content={(
<>
<Tooltip title={`${prevRegularText}`} mouseLeaveDelay={0}>
<Icon
@ -139,7 +139,7 @@ function PlayerButtons(props: Props): JSX.Element {
/>
</Tooltip>
</>
}
)}
>
<Tooltip
placement='top'
@ -163,7 +163,7 @@ function PlayerButtons(props: Props): JSX.Element {
<Popover
trigger='contextMenu'
placement='bottom'
content={
content={(
<>
<Tooltip title={`${nextRegularText}`} mouseLeaveDelay={0}>
<Icon
@ -193,7 +193,7 @@ function PlayerButtons(props: Props): JSX.Element {
/>
</Tooltip>
</>
}
)}
>
<Tooltip placement='top' mouseLeaveDelay={0} title={`${nextButtonTooltipMessage} ${nextFrameShortcut}`}>
{nextButton}

@ -18,7 +18,9 @@ import ColorPicker from 'components/annotation-page/standard-workspace/objects-s
import { ColorizeIcon } from 'icons';
import patterns from 'utils/validation-patterns';
import consts from 'consts';
import { equalArrayHead, idGenerator, Label, Attribute } from './common';
import {
equalArrayHead, idGenerator, Label, Attribute,
} from './common';
export enum AttributeType {
SELECT = 'SELECT',
@ -318,9 +320,9 @@ class LabelForm extends React.PureComponent<Props, {}> {
);
}
private renderAttribute = (key: number, index: number): JSX.Element => {
private renderAttribute = (key: number): JSX.Element => {
const { label, form } = this.props;
const attr = label && index < label.attributes.length ? label.attributes[index] : null;
const attr = label ? label.attributes.filter((_attr: any): boolean => _attr.id === key)[0] : null;
return (
<Form.Item key={key}>

@ -83,13 +83,7 @@ export default function UserSelector(props: Props): JSX.Element {
if (value && !users.filter((user) => user.id === value.id).length) {
core.users.get({ id: value.id }).then((result: User[]) => {
const [user] = result;
setUsers([
...users,
{
id: user.id,
username: user.username,
},
]);
setUsers([...users, user]);
setSearchPhrase(user.username);
});
}

@ -24,7 +24,10 @@ function mapStateToProps(state: CombinedState): StateToProps {
annotation: {
annotations: { activatedStateID, collapsed, states: objectStates },
canvas: {
contextMenu: { visible, top, left, type },
contextMenu: {
visible, top, left, type,
},
ready,
},
},
} = state;
@ -33,7 +36,11 @@ function mapStateToProps(state: CombinedState): StateToProps {
activatedStateID,
collapsed: activatedStateID !== null ? collapsed[activatedStateID] : undefined,
objectStates,
visible,
visible:
activatedStateID !== null &&
visible &&
ready &&
objectStates.map((_state: any): number => _state.clientID).includes(activatedStateID),
left,
top,
type,
@ -166,7 +173,9 @@ class CanvasContextMenuContainer extends React.PureComponent<Props, State> {
public render(): JSX.Element {
const { left, top } = this.state;
const { visible, activatedStateID, objectStates, type } = this.props;
const {
visible, activatedStateID, objectStates, type,
} = this.props;
return (
<>

@ -8,7 +8,7 @@ import { connect } from 'react-redux';
import { updateAnnotationsAsync } from 'actions/annotation-actions';
import LabelItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/label-item';
import { CombinedState } from 'reducers/interfaces';
import { CombinedState, ObjectType } from 'reducers/interfaces';
interface OwnProps {
labelID: number;
@ -92,8 +92,8 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
let statesLocked = true;
ownObjectStates.forEach((objectState: any) => {
const { lock } = objectState;
if (!lock) {
const { lock, objectType } = objectState;
if (!lock && objectType !== ObjectType.TAG) {
statesHidden = statesHidden && objectState.hidden;
statesLocked = statesLocked && objectState.lock;
}

@ -7,7 +7,9 @@ import copy from 'copy-to-clipboard';
import { connect } from 'react-redux';
import { LogType } from 'cvat-logger';
import { ActiveControl, CombinedState, ColorBy, ShapeType } from 'reducers/interfaces';
import {
ActiveControl, CombinedState, ColorBy, ShapeType,
} from 'reducers/interfaces';
import {
collapseObjectItems,
updateAnnotationsAsync,
@ -83,40 +85,25 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
const stateIDs = states.map((_state: any): number => _state.clientID);
const index = stateIDs.indexOf(clientID);
try {
const collapsedState =
typeof statesCollapsed[clientID] === 'undefined' ? initialCollapsed : statesCollapsed[clientID];
return {
objectState: states[index],
collapsed: collapsedState,
attributes: jobAttributes[states[index].label.id],
labels,
ready,
activeControl,
colorBy,
jobInstance,
frameNumber,
activated: activatedStateID === clientID,
minZLayer,
maxZLayer,
normalizedKeyMap,
aiToolsRef,
};
} catch (exception) {
// we have an exception here sometimes
// but I cannot understand when it happens and what is the root reason
// maybe this temporary hack helps us
const dataObject = {
index,
frameNumber,
clientID: own.clientID,
stateIDs,
};
throw new Error(
`${exception.toString()} in mapStateToProps of ObjectItemContainer. Data are ${JSON.stringify(dataObject)}`,
);
}
const collapsedState =
typeof statesCollapsed[clientID] === 'undefined' ? initialCollapsed : statesCollapsed[clientID];
return {
objectState: states[index],
collapsed: collapsedState,
attributes: jobAttributes[states[index].label.id],
labels,
ready,
activeControl,
colorBy,
jobInstance,
frameNumber,
activated: activatedStateID === clientID,
minZLayer,
maxZLayer,
normalizedKeyMap,
aiToolsRef,
};
}
function mapDispatchToProps(dispatch: any): DispatchToProps {
@ -219,7 +206,9 @@ class ObjectItemContainer extends React.PureComponent<Props> {
};
private activate = (): void => {
const { activateObject, objectState, ready, activeControl } = this.props;
const {
activateObject, objectState, ready, activeControl,
} = this.props;
if (ready && activeControl === ActiveControl.CURSOR) {
activateObject(objectState.clientID);
@ -324,7 +313,9 @@ class ObjectItemContainer extends React.PureComponent<Props> {
}
public render(): JSX.Element {
const { objectState, collapsed, labels, attributes, activated, colorBy, normalizedKeyMap } = this.props;
const {
objectState, collapsed, labels, attributes, activated, colorBy, normalizedKeyMap,
} = this.props;
let stateColor = '';
if (colorBy === ColorBy.INSTANCE) {

@ -231,7 +231,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
}
private undo = (): void => {
const { undo, jobInstance, frameNumber, canvasInstance } = this.props;
const {
undo, jobInstance, frameNumber, canvasInstance,
} = this.props;
if (canvasInstance.isAbleToChangeFrame()) {
undo(jobInstance, frameNumber);
@ -239,7 +241,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
};
private redo = (): void => {
const { redo, jobInstance, frameNumber, canvasInstance } = this.props;
const {
redo, jobInstance, frameNumber, canvasInstance,
} = this.props;
if (canvasInstance.isAbleToChangeFrame()) {
redo(jobInstance, frameNumber);
@ -253,7 +257,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
};
private onSwitchPlay = (): void => {
const { frameNumber, jobInstance, onSwitchPlay, playing } = this.props;
const {
frameNumber, jobInstance, onSwitchPlay, playing,
} = this.props;
if (playing) {
onSwitchPlay(false);
@ -263,7 +269,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
};
private onFirstFrame = (): void => {
const { frameNumber, jobInstance, playing, onSwitchPlay } = this.props;
const {
frameNumber, jobInstance, playing, onSwitchPlay,
} = this.props;
const newFrame = jobInstance.startFrame;
if (newFrame !== frameNumber) {
@ -275,7 +283,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
};
private onBackward = (): void => {
const { frameNumber, frameStep, jobInstance, playing, onSwitchPlay } = this.props;
const {
frameNumber, frameStep, jobInstance, playing, onSwitchPlay,
} = this.props;
const newFrame = Math.max(jobInstance.startFrame, frameNumber - frameStep);
if (newFrame !== frameNumber) {
@ -288,7 +298,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
private onPrevFrame = (): void => {
const { prevButtonType } = this.state;
const { frameNumber, jobInstance, playing, onSwitchPlay, searchAnnotations, searchEmptyFrame } = this.props;
const {
frameNumber, jobInstance, playing, onSwitchPlay,
} = this.props;
const { startFrame } = jobInstance;
const newFrame = Math.max(jobInstance.startFrame, frameNumber - 1);
@ -296,19 +308,22 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
if (playing) {
onSwitchPlay(false);
}
if (prevButtonType === 'regular') {
this.changeFrame(newFrame);
} else if (prevButtonType === 'filtered') {
searchAnnotations(jobInstance, frameNumber - 1, startFrame);
this.searchAnnotations(frameNumber - 1, startFrame);
} else {
searchEmptyFrame(jobInstance, frameNumber - 1, startFrame);
this.searchEmptyFrame(frameNumber - 1, startFrame);
}
}
};
private onNextFrame = (): void => {
const { nextButtonType } = this.state;
const { frameNumber, jobInstance, playing, onSwitchPlay, searchAnnotations, searchEmptyFrame } = this.props;
const {
frameNumber, jobInstance, playing, onSwitchPlay,
} = this.props;
const { stopFrame } = jobInstance;
const newFrame = Math.min(jobInstance.stopFrame, frameNumber + 1);
@ -316,18 +331,21 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
if (playing) {
onSwitchPlay(false);
}
if (nextButtonType === 'regular') {
this.changeFrame(newFrame);
} else if (nextButtonType === 'filtered') {
searchAnnotations(jobInstance, frameNumber + 1, stopFrame);
this.searchAnnotations(frameNumber + 1, stopFrame);
} else {
searchEmptyFrame(jobInstance, frameNumber + 1, stopFrame);
this.searchEmptyFrame(frameNumber + 1, stopFrame);
}
}
};
private onForward = (): void => {
const { frameNumber, frameStep, jobInstance, playing, onSwitchPlay } = this.props;
const {
frameNumber, frameStep, jobInstance, playing, onSwitchPlay,
} = this.props;
const newFrame = Math.min(jobInstance.stopFrame, frameNumber + frameStep);
if (newFrame !== frameNumber) {
@ -339,7 +357,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
};
private onLastFrame = (): void => {
const { frameNumber, jobInstance, playing, onSwitchPlay } = this.props;
const {
frameNumber, jobInstance, playing, onSwitchPlay,
} = this.props;
const newFrame = jobInstance.stopFrame;
if (newFrame !== frameNumber) {
@ -418,6 +438,20 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
}
}
private searchAnnotations(start: number, stop: number): void {
const { canvasInstance, jobInstance, searchAnnotations } = this.props;
if (canvasInstance.isAbleToChangeFrame()) {
searchAnnotations(jobInstance, start, stop);
}
}
private searchEmptyFrame(start: number, stop: number): void {
const { canvasInstance, jobInstance, searchAnnotations } = this.props;
if (canvasInstance.isAbleToChangeFrame()) {
searchAnnotations(jobInstance, start, stop);
}
}
public render(): JSX.Element {
const { nextButtonType, prevButtonType } = this.state;
const {

@ -354,7 +354,7 @@ def _create_thread(tid, data):
img_sizes = []
with open(db_data.get_dummy_chunk_path(chunk_number), 'w') as dummy_chunk:
for path, frame_id in chunk_paths:
dummy_chunk.write(path + '\n')
dummy_chunk.write(os.path.relpath(path, upload_dir) + '\n')
img_sizes.append(extractor.get_image_size(frame_id))
db_images.extend([

@ -0,0 +1,37 @@
# REST API design principles
## REST API scheme
Common scheme for our REST API is `<VERB> [namespace] <objects> <id> <action>`.
- `VERB` can be `POST`, `GET`, `PATCH`, `PUT`, `DELETE`.
- `namespace` should scope some specific functionality like `auth`, `lambda`.
It is optional in the scheme.
- Typical `objects` are `tasks`, `projects`, `jobs`.
- When you want to extract a specific object from a collection, just specify its `id`.
- An `action` can be used to simplify REST API or provide an endpoint for entities
without `objects` endpoint like `annotations`, `data`, `data/meta`. Note: action
should not duplicate other endpoints without a reason.
## Design principles
- Use nouns instead of verbs in endpoint paths. For example,
`POST /api/v1/tasks` instead of `POST /api/v1/tasks/create`.
- Accept and respond with JSON whenever it is possible
- Name collections with plural nouns (e.g. `/tasks`, `/projects`)
- Try to keep the API structure flat. Prefer two separate endpoints
for `/projects` and `/tasks` instead of `/projects/:id1/tasks/:id2`. Use
filters to extract necessary information like `/tasks/:id2?project=:id1`.
In some cases it is useful to get all `tasks`. If the structure is
hierarchical, it cannot be done easily. Also you have to know both `:id1`
and `:id2` to get information about the task.
_Note: for now we accept `GET /tasks/:id2/jobs` but it should be replaced
by `/jobs?task=:id2` in the future_.
- Handle errors gracefully and return standard error codes (e.g. `201`, `400`)
- Allow filtering, sorting, and pagination
- Maintain good security practices
- Cache data to improve performance
- Versioning our APIs (e.g. `/api/v1`, `/api/v2`). It should be done when you
delete an endpoint or modify its behaviors.
## Links
- [Best practices for REST API design](https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/)
- [Flat vs. nested resources](https://stackoverflow.com/questions/20951419/what-are-best-practices-for-rest-nested-resources)

@ -1,5 +1,5 @@
metadata:
name: openvino.dextr
name: openvino-dextr
namespace: cvat
annotations:
name: DEXTR

@ -1,5 +1,5 @@
metadata:
name: openvino.omz.intel.person-reidentification-retail-0300
name: openvino-omz-intel-person-reidentification-retail-0300
namespace: cvat
annotations:
name: Person reidentification

@ -1,5 +1,5 @@
metadata:
name: openvino.omz.semantic-segmentation-adas-0001
name: openvino-omz-semantic-segmentation-adas-0001
namespace: cvat
annotations:
name: Semantic segmentation for ADAS

@ -1,5 +1,5 @@
metadata:
name: openvino.omz.intel.text-detection-0004
name: openvino-omz-intel-text-detection-0004
namespace: cvat
annotations:
name: Text detection v4

@ -1,5 +1,5 @@
metadata:
name: openvino.omz.public.faster_rcnn_inception_v2_coco
name: openvino-omz-public-faster_rcnn_inception_v2_coco
namespace: cvat
annotations:
name: Faster RCNN

@ -2,7 +2,7 @@
# have enough memory (more than 4GB). Look here how to do that
# https://stackoverflow.com/questions/44417159/docker-process-killed-with-cryptic-killed-message
metadata:
name: openvino.omz.public.mask_rcnn_inception_resnet_v2_atrous_coco
name: openvino-mask-rcnn-inception-resnet-v2-atrous-coco
namespace: cvat
annotations:
name: Mask RCNN

@ -1,5 +1,5 @@
metadata:
name: openvino.omz.public.yolo-v3-tf
name: openvino-omz-public-yolo-v3-tf
namespace: cvat
annotations:
name: YOLO v3

@ -1,5 +1,5 @@
metadata:
name: pth.foolwood.siammask
name: pth-foolwood-siammask
namespace: cvat
annotations:
name: SiamMask

@ -1,5 +1,5 @@
metadata:
name: pth.saic-vul.fbrs
name: pth-saic-vul-fbrs
namespace: cvat
annotations:
name: f-BRS

@ -1,5 +1,5 @@
metadata:
name: tf.faster_rcnn_inception_v2_coco
name: tf-faster-rcnn-inception-v2-coco
namespace: cvat
annotations:
name: Faster RCNN via Tensorflow

@ -1,5 +1,5 @@
metadata:
name: tf.matterport.mask_rcnn
name: tf-matterport-mask-rcnn
namespace: cvat
annotations:
name: Mask RCNN via Tensorflow

Loading…
Cancel
Save