diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml index a7f25b2bca3..96d1c52f2ea 100644 --- a/.github/workflows/bandit.yml +++ b/.github/workflows/bandit.yml @@ -9,7 +9,7 @@ jobs: - name: Run checks run: | URL="https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" - PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | .filename') + PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | select(.status != "removed") | .filename') for files in $PR_FILES; do extension="${files##*.}" if [[ $extension == 'py' ]]; then diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 66709d05209..012b5d7a9a3 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -12,7 +12,7 @@ jobs: - name: Run checks run: | URL="https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" - PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | .filename') + PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | select(.status != "removed") | .filename') for files in $PR_FILES; do extension="${files##*.}" if [[ $extension == 'js' || $extension == 'ts' || $extension == 'jsx' || $extension == 'tsx' ]]; then diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 612e16a1eb0..c796214e172 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -9,7 +9,7 @@ jobs: - name: Run checks run: | URL="https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" - PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | .filename') + PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | select(.status != "removed") | .filename') for files in $PR_FILES; do extension="${files##*.}" if [[ $extension == 'py' ]]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c5ed8e1bf..175ea308367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Persistent queue added to logstash () - Improved maintanance of popups visibility () - Image visualizations settings on canvas for faster access () +- Better scale management of left panel when screen is too small () ### Deprecated @@ -58,6 +59,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated the path to python for DL models inside automatic annotation documentation () - Fixed of receiving function variable () - Shortcuts with CAPSLOCK enabled and with non-US languages activated () +- Prevented creating several issues for the same object () +- Fixed label editor name field validator () +- An error about track shapes outside of the task frames during export () +- Fixed project search field updating () - Fixed export error when invalid polygons are present in overlapping frames () ### Security diff --git a/cvat-data/package-lock.json b/cvat-data/package-lock.json index 75279ba3c41..32dfd645e30 100644 --- a/cvat-data/package-lock.json +++ b/cvat-data/package-lock.json @@ -4709,9 +4709,9 @@ "dev": true }, "jszip": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", - "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz", + "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==", "requires": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -5423,9 +5423,9 @@ } }, "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "parent-module": { "version": "1.0.1", diff --git a/cvat-data/package.json b/cvat-data/package.json index d57e36c9986..cd792695614 100644 --- a/cvat-data/package.json +++ b/cvat-data/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "async-mutex": "^0.3.0", - "jszip": "3.5.0" + "jszip": "3.6.0" }, "scripts": { "patch": "cd src/js && patch --dry-run --forward -p0 < 3rdparty_patch.diff >> /dev/null && patch -p0 < 3rdparty_patch.diff; true", diff --git a/cvat-ui/.eslintrc.js b/cvat-ui/.eslintrc.js index 28d9241e9e3..18dd267ca35 100644 --- a/cvat-ui/.eslintrc.js +++ b/cvat-ui/.eslintrc.js @@ -55,6 +55,12 @@ module.exports = { }, }, ], + 'import/order': [ + 'error', + { + 'groups': ['builtin', 'external', 'internal'], + } + ] }, settings: { 'import/resolver': { diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index ce87ebabe05..a9d2a5e9591 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.15.0", + "version": "1.15.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,11 +13,11 @@ } }, "@ant-design/icons": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.4.0.tgz", - "integrity": "sha512-+X44IouK56JbP3r7zM+Zoykv5wQlXBlxY0NTaFXGpiyYSS/Bh6HIo9aTF62QkSuDTqA3UpeNVTRFioKKRmkWDQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.5.0.tgz", + "integrity": "sha512-ZAKJcmr4DBV3NWr8wm2dCxNKN4eFrX+qCaPsuFejP6FRsf+m5OKxvCVi9bSp1lmKWeOI5yECAx5s0uFm4QHuPw==", "requires": { - "@ant-design/colors": "^5.0.0", + "@ant-design/colors": "^6.0.0", "@ant-design/icons-svg": "^4.0.0", "@babel/runtime": "^7.11.2", "classnames": "^2.2.6", @@ -25,14 +25,27 @@ "rc-util": "^5.0.1" }, "dependencies": { + "@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "requires": { + "@ctrl/tinycolor": "^3.4.0" + } + }, "@babel/runtime": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz", - "integrity": "sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } }, + "@ctrl/tinycolor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz", + "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==" + }, "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", @@ -58,9 +71,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -1335,6 +1348,11 @@ "redux": "^4.0.0" } }, + "@types/resize-observer-browser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz", + "integrity": "sha512-8k/67Z95Goa6Lznuykxkfhq9YU3l1Qe6LNZmwde1u7802a3x8v44oq0j91DICclxatTr0rNnhXx7+VTIetSrSQ==" + }, "@typescript-eslint/eslint-plugin": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.5.0.tgz", @@ -1881,12 +1899,12 @@ } }, "antd": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/antd/-/antd-4.12.2.tgz", - "integrity": "sha512-xB7sGg2qM/Sl3azjbc2RbJQ6cTr2Fos0AYZw2gTLLWtKhOyO3FUH7EBsL17GOkVnEDwMmBYtVXLhMgPM+e4gbA==", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/antd/-/antd-4.12.3.tgz", + "integrity": "sha512-opzbxm6jZB+Pc9M0Kuo6+4WmniB59NJ4i/qBr6ExyMtl9hMgsGNH8GuDXsp2xgTzfq5hyobdLci2DAuPMrf0Zg==", "requires": { - "@ant-design/colors": "^5.0.0", - "@ant-design/icons": "^4.4.0", + "@ant-design/colors": "^6.0.0", + "@ant-design/icons": "^4.5.0", "@ant-design/react-slick": "~0.28.1", "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", @@ -1929,14 +1947,40 @@ "warning": "^4.0.3" }, "dependencies": { + "@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "requires": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "@ant-design/icons": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.5.0.tgz", + "integrity": "sha512-ZAKJcmr4DBV3NWr8wm2dCxNKN4eFrX+qCaPsuFejP6FRsf+m5OKxvCVi9bSp1lmKWeOI5yECAx5s0uFm4QHuPw==", + "requires": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.0.0", + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "insert-css": "^2.0.0", + "rc-util": "^5.0.1" + } + }, "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } }, + "@ctrl/tinycolor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz", + "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==" + }, "rc-util": { "version": "5.8.1", "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.8.1.tgz", @@ -15392,7 +15436,6 @@ "cvat-data": { "version": "file:../cvat-data", "requires": { - "async-mutex": "^0.2.6", "jszip": "3.5.0" }, "dependencies": { @@ -21327,9 +21370,9 @@ } }, "date-fns": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.17.0.tgz", - "integrity": "sha512-ZEhqxUtEZeGgg9eHNSOAJ8O9xqSgiJdrL0lzSSfMF54x6KXWJiOH/xntSJ9YomJPrYH/p08t6gWjGWq1SDJlSA==" + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.18.0.tgz", + "integrity": "sha512-NYyAg4wRmGVU4miKq5ivRACOODdZRY3q5WLmOJSq8djyzftYphU7dTHLcEtLqEvfqMKQ0jVv91P4BAwIjsXIcw==" }, "debug": { "version": "4.1.1", @@ -27893,9 +27936,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -27920,9 +27963,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -27944,9 +27987,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -27971,9 +28014,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -27997,9 +28040,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28037,9 +28080,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28077,9 +28120,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28102,9 +28145,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28128,9 +28171,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28153,9 +28196,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28181,9 +28224,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28211,9 +28254,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28251,9 +28294,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28266,9 +28309,9 @@ } }, "rc-notification": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-4.5.4.tgz", - "integrity": "sha512-VsN0ouF4uglE5g3C9oDsXLNYX0Sz++ZNUFYCswkxhpImYJ9u6nJOpyA71uOYDVCu6bAF54Y5Hi/b+EcnMzkepg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-4.5.5.tgz", + "integrity": "sha512-YIfhTSw+h5GsSdgMnuMx24wqiPlg3FeamuOlkh9RkyHx+SeZVAKzQ0juy2NGvPEF2hDWi5xTqxUqLdo0L2AmGg==", "requires": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -28277,9 +28320,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28303,9 +28346,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28342,9 +28385,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28371,9 +28414,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28395,9 +28438,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28420,9 +28463,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28446,9 +28489,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28461,9 +28504,9 @@ } }, "rc-select": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-12.1.3.tgz", - "integrity": "sha512-pMJ27VQRh5QbyGLSE+by4tORYucNFbZxON+Ywj81qjXAGMjvhMcOOvlv1RZRNdnZxaMwH//3iDPOf80b0AJxZg==", + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-12.1.5.tgz", + "integrity": "sha512-UElTMw0+XvYJmVfsHTWvLR42RKNf5qyN3Ed/JfuZQceIPK1/3ugGRjdEOKBsPmPyNB5389NAROCV4tQd9fmqwg==", "requires": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -28475,9 +28518,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28502,9 +28545,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28527,9 +28570,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28552,9 +28595,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28579,9 +28622,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28607,9 +28650,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28633,9 +28676,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28672,9 +28715,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28687,9 +28730,9 @@ } }, "rc-tree": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-4.1.1.tgz", - "integrity": "sha512-ufq7CkWfvTQa+xMPzEWYfOjTfsEALlPr0/IyujEG4+4d8NdaR3e+0dc8LkkVWoe1VCcXV2FQqAsgr2z/ThFUrQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-4.1.2.tgz", + "integrity": "sha512-9yhhDqHxG8gOZfkZeHYT6oarzarzi37lDe5c2r72tq5dflvoayGqD2bMkL2KC7GQJPLknZrtCwAbewqvD/T6NQ==", "requires": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -28699,9 +28742,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28726,9 +28769,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28753,9 +28796,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -28778,9 +28821,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.13.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", - "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 49d93b64f86..9f1c1460624 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.15.0", + "version": "1.15.3", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { @@ -48,7 +48,7 @@ "worker-loader": "^2.0.0" }, "dependencies": { - "@ant-design/icons": "^4.4.0", + "@ant-design/icons": "^4.5.0", "@types/lodash": "^4.14.168", "@types/platform": "^1.3.3", "@types/react": "^16.14.3", @@ -59,7 +59,8 @@ "@types/react-router-dom": "^5.1.7", "@types/react-share": "^3.0.3", "@types/redux-logger": "^3.0.8", - "antd": "^4.12.2", + "@types/resize-observer-browser": "^0.1.5", + "antd": "^4.12.3", "copy-to-clipboard": "^3.3.1", "cvat-canvas": "file:../cvat-canvas", "cvat-canvas3d": "file:../cvat-canvas3d", diff --git a/cvat-ui/src/components/actions-menu/actions-menu.tsx b/cvat-ui/src/components/actions-menu/actions-menu.tsx index ad7b6d55c88..52e0f05bcb1 100644 --- a/cvat-ui/src/components/actions-menu/actions-menu.tsx +++ b/cvat-ui/src/components/actions-menu/actions-menu.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -66,6 +66,7 @@ export default function ActionsMenuComponent(props: Props): JSX.Element { Modal.confirm({ title: 'Current annotation will be lost', content: 'You are going to upload new annotations to this task. Continue?', + className: 'cvat-modal-content-load-task-annotation', onOk: () => { onClickMenu(copyParams, file); }, diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index feb6f7b6d72..48499859758 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -39,7 +39,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { saveLogs(); const root = window.document.getElementById('root'); if (root) { - root.style.minHeight = '768px'; + root.style.minHeight = '600px'; } return () => { diff --git a/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx b/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx index 579bec3129f..1be37564d95 100644 --- a/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx +++ b/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -import React, { ReactPortal, useEffect } from 'react'; +import React, { ReactPortal, useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; import Tag from 'antd/lib/tag'; import { CheckOutlined, CloseCircleOutlined } from '@ant-design/icons'; @@ -25,6 +25,8 @@ export default function HiddenIssueLabel(props: Props): ReactPortal { id, message, top, left, resolved, onClick, highlight, blur, } = props; + const ref = useRef(null); + useEffect(() => { if (!resolved) { setTimeout(highlight); @@ -37,10 +39,21 @@ export default function HiddenIssueLabel(props: Props): ReactPortal { return ReactDOM.createPortal( { + if (ref.current !== null) { + const selfElement = ref.current; + if (event.deltaX > 0) { + selfElement.parentElement?.appendChild(selfElement); + } else { + selfElement.parentElement?.prepend(selfElement); + } + } + }} style={{ top, left }} className='cvat-hidden-issue-label' > diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx new file mode 100644 index 00000000000..c900c306666 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx @@ -0,0 +1,114 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { SmallDashOutlined } from '@ant-design/icons'; +import Popover from 'antd/lib/popover'; +import React, { useEffect, useRef, useState } from 'react'; +import ReactDOM from 'react-dom'; + +const extraControlsContentClassName = 'cvat-extra-controls-control-content'; + +let onUpdateChildren: Function | null = null; +export function ExtraControlsControl(): JSX.Element { + const [hasChildren, setHasChildren] = useState(false); + const [initialized, setInitialized] = useState(false); + + useEffect(() => { + if (!initialized) { + setInitialized(true); + } + + window.document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); + }, []); + + onUpdateChildren = () => { + const contentElement = window.document.getElementsByClassName(extraControlsContentClassName)[0]; + if (contentElement) { + setHasChildren(contentElement.children.length > 0); + } + }; + + return ( + } + > + + + ); +} + +export default function ControlVisibilityObserver

( + ControlComponent: React.FunctionComponent

, +): React.FunctionComponent

{ + let visibilityHeightThreshold = 0; // minimum value of height when element can be pushed to main panel + + return (props: P): JSX.Element | null => { + const ref = useRef(null); + const [visible, setVisible] = useState(true); + + useEffect(() => { + if (ref && ref.current) { + const wrapper = ref.current; + const parentElement = ref.current.parentElement as HTMLElement; + + const reservedHeight = 45; // for itself + const observer = new ResizeObserver(() => { + // when parent size was changed, check again can we put the control + // into the side panel or not + const availableHeight = parentElement.offsetHeight; + setVisible(availableHeight - reservedHeight >= visibilityHeightThreshold); + }); + + if (ref && ref.current) { + const availableHeight = parentElement.offsetHeight; + // when first mount, remember bottom coordinate which equal to minimum parent width + // to put the control into side panel + visibilityHeightThreshold = wrapper.offsetTop + wrapper.offsetHeight; + // start observing parent size + observer.observe(ref.current.parentElement as HTMLElement); + // then put it to extra controls if parent height is not enought + setVisible(availableHeight - reservedHeight >= visibilityHeightThreshold); + } + + return () => { + observer.disconnect(); + }; + } + + return () => {}; + }, []); + + useEffect(() => { + // when visibility changed, we notify extra content element because now its children changed + if (onUpdateChildren) { + onUpdateChildren(); + } + }, [visible]); + + if (!visible) { + const extraControlsContent = window.document.getElementsByClassName(extraControlsContentClassName)[0]; + if (extraControlsContent) { + return ReactDOM.createPortal(, extraControlsContent); + } + + return null; + } + + // first mount always to side panel + return ( +

+ +
+ ); + }; +} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx index 2c03ef043ea..2ac25ef204d 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx @@ -9,22 +9,23 @@ import Layout from 'antd/lib/layout'; import { ActiveControl, Rotation } from 'reducers/interfaces'; import { Canvas } from 'cvat-canvas-wrapper'; -import RotateControl from './rotate-control'; -import CursorControl from './cursor-control'; -import MoveControl from './move-control'; -import FitControl from './fit-control'; -import ResizeControl from './resize-control'; +import ControlVisibilityObserver, { ExtraControlsControl } from './control-visibility-observer'; +import RotateControl, { Props as RotateControlProps } from './rotate-control'; +import CursorControl, { Props as CursorControlProps } from './cursor-control'; +import MoveControl, { Props as MoveControlProps } from './move-control'; +import FitControl, { Props as FitControlProps } from './fit-control'; +import ResizeControl, { Props as ResizeControlProps } from './resize-control'; import ToolsControl from './tools-control'; import OpenCVControl from './opencv-control'; -import DrawRectangleControl from './draw-rectangle-control'; -import DrawPolygonControl from './draw-polygon-control'; -import DrawPolylineControl from './draw-polyline-control'; -import DrawPointsControl from './draw-points-control'; -import DrawCuboidControl from './draw-cuboid-control'; -import SetupTagControl from './setup-tag-control'; -import MergeControl from './merge-control'; -import GroupControl from './group-control'; -import SplitControl from './split-control'; +import DrawRectangleControl, { Props as DrawRectangleControlProps } from './draw-rectangle-control'; +import DrawPolygonControl, { Props as DrawPolygonControlProps } from './draw-polygon-control'; +import DrawPolylineControl, { Props as DrawPolylineControlProps } from './draw-polyline-control'; +import DrawPointsControl, { Props as DrawPointsControlProps } from './draw-points-control'; +import DrawCuboidControl, { Props as DrawCuboidControlProps } from './draw-cuboid-control'; +import SetupTagControl, { Props as SetupTagControlProps } from './setup-tag-control'; +import MergeControl, { Props as MergeControlProps } from './merge-control'; +import GroupControl, { Props as GroupControlProps } from './group-control'; +import SplitControl, { Props as SplitControlProps } from './split-control'; interface Props { canvasInstance: Canvas; @@ -42,6 +43,25 @@ interface Props { redrawShape(): void; } +// We use the observer to see if these controls are in the viewport +// They automatically put to extra if not +const ObservedCursorControl = ControlVisibilityObserver(CursorControl); +const ObservedMoveControl = ControlVisibilityObserver(MoveControl); +const ObservedRotateControl = ControlVisibilityObserver(RotateControl); +const ObservedFitControl = ControlVisibilityObserver(FitControl); +const ObservedResizeControl = ControlVisibilityObserver(ResizeControl); +const ObservedToolsControl = ControlVisibilityObserver(ToolsControl); +const ObservedOpenCVControl = ControlVisibilityObserver(OpenCVControl); +const ObservedDrawRectangleControl = ControlVisibilityObserver(DrawRectangleControl); +const ObservedDrawPolygonControl = ControlVisibilityObserver(DrawPolygonControl); +const ObservedDrawPolylineControl = ControlVisibilityObserver(DrawPolylineControl); +const ObservedDrawPointsControl = ControlVisibilityObserver(DrawPointsControl); +const ObservedDrawCuboidControl = ControlVisibilityObserver(DrawCuboidControl); +const ObservedSetupTagControl = ControlVisibilityObserver(SetupTagControl); +const ObservedMergeControl = ControlVisibilityObserver(MergeControl); +const ObservedGroupControl = ControlVisibilityObserver(GroupControl); +const ObservedSplitControl = ControlVisibilityObserver(SplitControl); + export default function ControlsSideBarComponent(props: Props): JSX.Element { const { canvasInstance, @@ -170,13 +190,13 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element { return ( - - - + - - + +
- - - + + - - - - - +
- - - + +
); } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/cursor-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/cursor-control.tsx index dd26d376f25..826e7990875 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/cursor-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/cursor-control.tsx @@ -10,7 +10,7 @@ import { ActiveControl } from 'reducers/interfaces'; import { Canvas } from 'cvat-canvas-wrapper'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; cursorShortkey: string; activeControl: ActiveControl; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-cuboid-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-cuboid-control.tsx index 0a86df2ebe9..50e28fecae5 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-cuboid-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-cuboid-control.tsx @@ -14,7 +14,7 @@ import { CubeIcon } from 'icons'; import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { canvasInstance: Canvas; isDrawing: boolean; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-points-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-points-control.tsx index 462e78c9d69..6a513be11d9 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-points-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-points-control.tsx @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces'; import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { canvasInstance: Canvas; isDrawing: boolean; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polygon-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polygon-control.tsx index f07a324d7cf..61da0d3d106 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polygon-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polygon-control.tsx @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces'; import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { canvasInstance: Canvas; isDrawing: boolean; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polyline-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polyline-control.tsx index a1d3c157b3d..766c15c425c 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polyline-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-polyline-control.tsx @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces'; import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { canvasInstance: Canvas; isDrawing: boolean; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-rectangle-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-rectangle-control.tsx index 0d0462818f5..a94dabea55c 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-rectangle-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-rectangle-control.tsx @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces'; import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { canvasInstance: Canvas; isDrawing: boolean; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/fit-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/fit-control.tsx index baa544ff412..9921d86d6b9 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/fit-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/fit-control.tsx @@ -9,7 +9,7 @@ import { FitIcon } from 'icons'; import { Canvas } from 'cvat-canvas-wrapper'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/group-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/group-control.tsx index 8b93c7577e5..e26158437bf 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/group-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/group-control.tsx @@ -10,7 +10,7 @@ import { Canvas } from 'cvat-canvas-wrapper'; import { ActiveControl } from 'reducers/interfaces'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; activeControl: ActiveControl; switchGroupShortcut: string; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/merge-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/merge-control.tsx index 2d29fb21196..e1c96c3baf8 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/merge-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/merge-control.tsx @@ -10,7 +10,7 @@ import { Canvas } from 'cvat-canvas-wrapper'; import { ActiveControl } from 'reducers/interfaces'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; activeControl: ActiveControl; switchMergeShortcut: string; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/move-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/move-control.tsx index 760a59ddb22..ffbaeb3585f 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/move-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/move-control.tsx @@ -10,7 +10,7 @@ import { ActiveControl } from 'reducers/interfaces'; import { Canvas } from 'cvat-canvas-wrapper'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; activeControl: ActiveControl; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/resize-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/resize-control.tsx index ac7806cb070..ec461b1d7e4 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/resize-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/resize-control.tsx @@ -10,7 +10,7 @@ import { ActiveControl } from 'reducers/interfaces'; import { Canvas } from 'cvat-canvas-wrapper'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; activeControl: ActiveControl; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/rotate-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/rotate-control.tsx index 9fa1e2f9796..5c5a33cb457 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/rotate-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/rotate-control.tsx @@ -11,7 +11,7 @@ import { Rotation } from 'reducers/interfaces'; import CVATTooltip from 'components/common/cvat-tooltip'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { clockwiseShortcut: string; anticlockwiseShortcut: string; rotateFrame(rotation: Rotation): void; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-control.tsx index 5b57b98c451..d8b4a2998f8 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-control.tsx @@ -12,7 +12,7 @@ import { TagIcon } from 'icons'; import SetupTagPopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover'; import withVisibilityHandling from './handle-popover-visibility'; -interface Props { +export interface Props { canvasInstance: Canvas; isDrawing: boolean; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/split-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/split-control.tsx index 61d6bfcee71..c6877459698 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/split-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/split-control.tsx @@ -10,7 +10,7 @@ import { Canvas } from 'cvat-canvas-wrapper'; import { ActiveControl } from 'reducers/interfaces'; import CVATTooltip from 'components/common/cvat-tooltip'; -interface Props { +export interface Props { canvasInstance: Canvas; activeControl: ActiveControl; switchSplitShortcut: string; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx index 0e7cda1ecd0..d33a578540c 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx @@ -98,7 +98,11 @@ function ItemTopComponent(props: Props): JSX.Element { {clientID}
- + {type} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss index 5c34151403f..80f275d7e85 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss @@ -36,8 +36,7 @@ .cvat-canvas-controls-sidebar { background-color: $background-color-2; border-right: 1px solid $border-color-1; - overflow-y: auto; - overflow-x: hidden; + overflow: hidden; } .cvat-cursor-control, @@ -56,6 +55,7 @@ .cvat-split-track-control, .cvat-issue-control, .cvat-tools-control, +.cvat-extra-controls-control, .cvat-opencv-control { border-radius: 3.3px; transform: scale(0.65); @@ -75,6 +75,13 @@ } } +.cvat-extra-controls-control { + > svg { + width: 40px; + height: 40px; + } +} + .cvat-active-canvas-control { background: $header-color; transform: scale(0.75); diff --git a/cvat-ui/src/components/create-task-page/project-search-field.tsx b/cvat-ui/src/components/create-task-page/project-search-field.tsx index 450cbf953d1..9bab28e2b4f 100644 --- a/cvat-ui/src/components/create-task-page/project-search-field.tsx +++ b/cvat-ui/src/components/create-task-page/project-search-field.tsx @@ -59,19 +59,23 @@ export default function ProjectSearchField(props: Props): JSX.Element { }; useEffect(() => { - if (value && !projects.filter((project) => project.id === value).length) { - core.projects.get({ id: value }).then((result: Project[]) => { - const [project] = result; - setProjects([ - ...projects, - { - id: project.id, - name: project.name, - }, - ]); - setSearchPhrase(project.name); - onSelect(project.id); - }); + if (value) { + if (!projects.filter((project) => project.id === value).length) { + core.projects.get({ id: value }).then((result: Project[]) => { + const [project] = result; + setProjects([ + ...projects, + { + id: project.id, + name: project.name, + }, + ]); + setSearchPhrase(project.name); + onSelect(project.id); + }); + } + } else { + setSearchPhrase(''); } }, [value]); diff --git a/cvat-ui/src/components/labels-editor/label-form.tsx b/cvat-ui/src/components/labels-editor/label-form.tsx index c0f78329a53..23f16c962c6 100644 --- a/cvat-ui/src/components/labels-editor/label-form.tsx +++ b/cvat-ui/src/components/labels-editor/label-form.tsx @@ -390,10 +390,11 @@ export default class LabelForm extends React.Component { message: patterns.validateAttributeName.message, }, { - validator: async (_rule: any, labelName: string, callback: Function) => { + validator: (_rule: any, labelName: string) => { if (labelNames && labelNames.includes(labelName)) { - callback('Label name must be unique for the task'); + return Promise.reject(new Error('Label name must be unique for the task')); } + return Promise.resolve(); }, }, ]} @@ -522,7 +523,7 @@ export default class LabelForm extends React.Component { public render(): JSX.Element { return (
- + {this.renderLabelNameInput()} {this.renderChangeColorButton()} @@ -531,7 +532,7 @@ export default class LabelForm extends React.Component { {this.renderNewAttributeButton()} - + {this.renderAttributes()} diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 3bac02373db..5451eba9002 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -345,6 +345,7 @@ export default function (state = defaultState, action: AnyAction): Notifications 'Could not upload annotation for the ' + `task ${taskID}`, reason: action.payload.error.toString(), + className: 'cvat-notification-notice-load-annotation-failed', }, }, }, diff --git a/cvat/apps/dataset_manager/annotation.py b/cvat/apps/dataset_manager/annotation.py index 892924c6339..391b26bf588 100644 --- a/cvat/apps/dataset_manager/annotation.py +++ b/cvat/apps/dataset_manager/annotation.py @@ -110,7 +110,7 @@ def filter_track_shapes(shapes): # Track and TrackedShape models don't expect these fields del track['interpolated_shapes'] for shape in segment_shapes: - del shape['keyframe'] + shape.pop('keyframe', None) track['shapes'] = segment_shapes track['frame'] = track['shapes'][0]['frame'] @@ -749,6 +749,10 @@ def interpolate(shape0, shape1): curr_frame = shape["frame"] prev_shape = shape + # keep at least 1 shape + if end_frame <= curr_frame: + break + if not prev_shape["outside"]: shape = copy(prev_shape) shape["frame"] = end_frame diff --git a/cvat/apps/dataset_manager/bindings.py b/cvat/apps/dataset_manager/bindings.py index 38444d4d18b..a336a00715d 100644 --- a/cvat/apps/dataset_manager/bindings.py +++ b/cvat/apps/dataset_manager/bindings.py @@ -267,6 +267,11 @@ def get_frame(idx): anno_manager = AnnotationManager(self._annotation_ir) for shape in sorted(anno_manager.to_shapes(self._db_task.data.size), key=lambda shape: shape.get("z_order", 0)): + if shape['frame'] not in self._frame_info: + # After interpolation there can be a finishing frame + # outside of the task boundaries. Filter it out to avoid errors. + # https://github.com/openvinotoolkit/cvat/issues/2827 + continue if 'track_id' in shape: if shape['outside']: continue diff --git a/cvat/apps/dataset_manager/formats/camvid.py b/cvat/apps/dataset_manager/formats/camvid.py index bcd00b7a7bf..a8fb50592e9 100644 --- a/cvat/apps/dataset_manager/formats/camvid.py +++ b/cvat/apps/dataset_manager/formats/camvid.py @@ -4,7 +4,7 @@ from tempfile import TemporaryDirectory -from datumaro.components.project import Dataset +from datumaro.components.dataset import Dataset from pyunpack import Archive from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, @@ -17,16 +17,15 @@ @exporter(name='CamVid', ext='ZIP', version='1.0') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - envt = dm_env.transforms - extractor = extractor.transform(envt.get('polygons_to_masks')) - extractor = extractor.transform(envt.get('boxes_to_masks')) - extractor = extractor.transform(envt.get('merge_instance_segments')) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) + dataset.transform('polygons_to_masks') + dataset.transform('boxes_to_masks') + dataset.transform('merge_instance_segments') label_map = make_colormap(task_data) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('camvid').convert(extractor, - save_dir=temp_dir, save_images=save_images, apply_colormap=True, + dataset.export(temp_dir, 'camvid', + save_images=save_images, apply_colormap=True, label_map={label: label_map[label][0] for label in label_map}) make_zip_archive(temp_dir, dst_file) @@ -36,7 +35,6 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: Archive(src_file.name).extractall(tmp_dir) - dataset = dm_env.make_importer('camvid')(tmp_dir).make_dataset() - masks_to_polygons = dm_env.transforms.get('masks_to_polygons') - dataset = dataset.transform(masks_to_polygons) + dataset = Dataset.import_from(tmp_dir, 'camvid', env=dm_env) + dataset.transform('masks_to_polygons') import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/coco.py b/cvat/apps/dataset_manager/formats/coco.py index 84472d3aab1..3ec3ab18e82 100644 --- a/cvat/apps/dataset_manager/formats/coco.py +++ b/cvat/apps/dataset_manager/formats/coco.py @@ -5,7 +5,8 @@ import zipfile from tempfile import TemporaryDirectory -from datumaro.components.project import Dataset +from datumaro.components.dataset import Dataset + from cvat.apps.dataset_manager.bindings import CvatTaskDataExtractor, \ import_dm_annotations from cvat.apps.dataset_manager.util import make_zip_archive @@ -15,11 +16,10 @@ @exporter(name='COCO', ext='ZIP', version='1.0') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('coco_instances').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'coco_instances', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -29,8 +29,9 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: zipfile.ZipFile(src_file).extractall(tmp_dir) - dataset = dm_env.make_importer('coco')(tmp_dir).make_dataset() + dataset = Dataset.import_from(tmp_dir, 'coco', env=dm_env) import_dm_annotations(dataset, task_data) else: - dataset = dm_env.make_extractor('coco_instances', src_file.name) + dataset = Dataset.import_from(src_file.name, + 'coco_instances', env=dm_env) import_dm_annotations(dataset, task_data) \ No newline at end of file diff --git a/cvat/apps/dataset_manager/formats/cvat.py b/cvat/apps/dataset_manager/formats/cvat.py index d825dae0c65..02025afc750 100644 --- a/cvat/apps/dataset_manager/formats/cvat.py +++ b/cvat/apps/dataset_manager/formats/cvat.py @@ -9,10 +9,11 @@ from glob import glob from tempfile import TemporaryDirectory +from datumaro.components.extractor import DatasetItem + from cvat.apps.dataset_manager.bindings import match_dm_item from cvat.apps.dataset_manager.util import make_zip_archive from cvat.apps.engine.frame_provider import FrameProvider -from datumaro.components.extractor import DatasetItem from .registry import exporter, importer diff --git a/cvat/apps/dataset_manager/formats/imagenet.py b/cvat/apps/dataset_manager/formats/imagenet.py index d9847549f9e..2ed0cb47484 100644 --- a/cvat/apps/dataset_manager/formats/imagenet.py +++ b/cvat/apps/dataset_manager/formats/imagenet.py @@ -3,12 +3,12 @@ # SPDX-License-Identifier: MIT import os.path as osp -from glob import glob - import zipfile +from glob import glob from tempfile import TemporaryDirectory -from datumaro.components.project import Dataset +from datumaro.components.dataset import Dataset + from cvat.apps.dataset_manager.bindings import CvatTaskDataExtractor, \ import_dm_annotations from cvat.apps.dataset_manager.util import make_zip_archive @@ -18,15 +18,13 @@ @exporter(name='ImageNet', ext='ZIP', version='1.0') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transform + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: if save_images: - dm_env.converters.get('imagenet').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'imagenet', save_images=save_images) else: - dm_env.converters.get('imagenet_txt').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'imagenet_txt', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -35,7 +33,7 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: zipfile.ZipFile(src_file).extractall(tmp_dir) if glob(osp.join(tmp_dir, '*.txt')): - dataset = dm_env.make_importer('imagenet_txt')(tmp_dir).make_dataset() + dataset = Dataset.import_from(tmp_dir, 'imagenet_txt', env=dm_env) else: - dataset = dm_env.make_importer('imagenet')(tmp_dir).make_dataset() + dataset = Dataset.import_from(tmp_dir, 'imagenet', env=dm_env) import_dm_annotations(dataset, task_data) \ No newline at end of file diff --git a/cvat/apps/dataset_manager/formats/labelme.py b/cvat/apps/dataset_manager/formats/labelme.py index d3bd074d4d3..744b11faab0 100644 --- a/cvat/apps/dataset_manager/formats/labelme.py +++ b/cvat/apps/dataset_manager/formats/labelme.py @@ -4,23 +4,22 @@ from tempfile import TemporaryDirectory +from datumaro.components.dataset import Dataset from pyunpack import Archive from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, import_dm_annotations) from cvat.apps.dataset_manager.util import make_zip_archive -from datumaro.components.project import Dataset from .registry import dm_env, exporter, importer @exporter(name='LabelMe', ext='ZIP', version='3.0') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('label_me').convert(extractor, save_dir=temp_dir, - save_images=save_images) + dataset.export(temp_dir, 'label_me', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -29,7 +28,6 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: Archive(src_file.name).extractall(tmp_dir) - dataset = dm_env.make_importer('label_me')(tmp_dir).make_dataset() - masks_to_polygons = dm_env.transforms.get('masks_to_polygons') - dataset = dataset.transform(masks_to_polygons) + dataset = Dataset.import_from(tmp_dir, 'label_me', env=dm_env) + dataset.transform('masks_to_polygons') import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/mask.py b/cvat/apps/dataset_manager/formats/mask.py index b1307a9cead..3e3780e8c6a 100644 --- a/cvat/apps/dataset_manager/formats/mask.py +++ b/cvat/apps/dataset_manager/formats/mask.py @@ -4,12 +4,12 @@ from tempfile import TemporaryDirectory +from datumaro.components.dataset import Dataset from pyunpack import Archive from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, import_dm_annotations) from cvat.apps.dataset_manager.util import make_zip_archive -from datumaro.components.project import Dataset from .registry import dm_env, exporter, importer from .utils import make_colormap @@ -17,15 +17,13 @@ @exporter(name='Segmentation mask', ext='ZIP', version='1.1') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - envt = dm_env.transforms - extractor = extractor.transform(envt.get('polygons_to_masks')) - extractor = extractor.transform(envt.get('boxes_to_masks')) - extractor = extractor.transform(envt.get('merge_instance_segments')) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) + dataset.transform('polygons_to_masks') + dataset.transform('boxes_to_masks') + dataset.transform('merge_instance_segments') with TemporaryDirectory() as temp_dir: - dm_env.converters.get('voc_segmentation').convert(extractor, - save_dir=temp_dir, save_images=save_images, + dataset.export(temp_dir, 'voc_segmentation', save_images=save_images, apply_colormap=True, label_map=make_colormap(task_data)) make_zip_archive(temp_dir, dst_file) @@ -35,7 +33,6 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: Archive(src_file.name).extractall(tmp_dir) - dataset = dm_env.make_importer('voc')(tmp_dir).make_dataset() - masks_to_polygons = dm_env.transforms.get('masks_to_polygons') - dataset = dataset.transform(masks_to_polygons) + dataset = Dataset.import_from(tmp_dir, 'voc', env=dm_env) + dataset.transform('masks_to_polygons') import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/mot.py b/cvat/apps/dataset_manager/formats/mot.py index 81131dc1504..29d5182a674 100644 --- a/cvat/apps/dataset_manager/formats/mot.py +++ b/cvat/apps/dataset_manager/formats/mot.py @@ -4,23 +4,22 @@ from tempfile import TemporaryDirectory +import datumaro.components.extractor as datumaro +from datumaro.components.dataset import Dataset from pyunpack import Archive -import datumaro.components.extractor as datumaro from cvat.apps.dataset_manager.bindings import CvatTaskDataExtractor from cvat.apps.dataset_manager.util import make_zip_archive -from datumaro.components.project import Dataset from .registry import dm_env, exporter, importer @exporter(name='MOT', ext='ZIP', version='1.1') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('mot_seq_gt').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'mot_seq_gt', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -29,7 +28,7 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: Archive(src_file.name).extractall(tmp_dir) - dataset = dm_env.make_importer('mot_seq')(tmp_dir).make_dataset() + dataset = Dataset.import_from(tmp_dir, 'mot_seq', env=dm_env) tracks = {} label_cat = dataset.categories()[datumaro.AnnotationType.label] diff --git a/cvat/apps/dataset_manager/formats/mots.py b/cvat/apps/dataset_manager/formats/mots.py index 52bf0fa623a..22b9dd08c7e 100644 --- a/cvat/apps/dataset_manager/formats/mots.py +++ b/cvat/apps/dataset_manager/formats/mots.py @@ -4,13 +4,13 @@ from tempfile import TemporaryDirectory +from datumaro.components.dataset import Dataset +from datumaro.components.extractor import AnnotationType, Transform from pyunpack import Archive from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, find_dataset_root, match_dm_item) from cvat.apps.dataset_manager.util import make_zip_archive -from datumaro.components.extractor import AnnotationType, Transform -from datumaro.components.project import Dataset from .registry import dm_env, exporter, importer @@ -22,16 +22,14 @@ def transform_item(self, item): @exporter(name='MOTS PNG', ext='ZIP', version='1.0') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - envt = dm_env.transforms - extractor = extractor.transform(KeepTracks) # can only export tracks - extractor = extractor.transform(envt.get('polygons_to_masks')) - extractor = extractor.transform(envt.get('boxes_to_masks')) - extractor = extractor.transform(envt.get('merge_instance_segments')) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) + dataset.transform(KeepTracks) # can only export tracks + dataset.transform('polygons_to_masks') + dataset.transform('boxes_to_masks') + dataset.transform('merge_instance_segments') with TemporaryDirectory() as temp_dir: - dm_env.converters.get('mots_png').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'mots_png', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -40,9 +38,8 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: Archive(src_file.name).extractall(tmp_dir) - dataset = dm_env.make_importer('mots')(tmp_dir).make_dataset() - masks_to_polygons = dm_env.transforms.get('masks_to_polygons') - dataset = dataset.transform(masks_to_polygons) + dataset = Dataset.import_from(tmp_dir, 'mots', env=dm_env) + dataset.transform('masks_to_polygons') tracks = {} label_cat = dataset.categories()[AnnotationType.label] diff --git a/cvat/apps/dataset_manager/formats/pascal_voc.py b/cvat/apps/dataset_manager/formats/pascal_voc.py index ee30564bc1e..3f10b93aa93 100644 --- a/cvat/apps/dataset_manager/formats/pascal_voc.py +++ b/cvat/apps/dataset_manager/formats/pascal_voc.py @@ -6,26 +6,25 @@ import os.path as osp import shutil from glob import glob - from tempfile import TemporaryDirectory +from datumaro.components.dataset import Dataset from pyunpack import Archive from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, import_dm_annotations) from cvat.apps.dataset_manager.util import make_zip_archive -from datumaro.components.project import Dataset from .registry import dm_env, exporter, importer @exporter(name='PASCAL VOC', ext='ZIP', version='1.1') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('voc').convert(extractor, - save_dir=temp_dir, save_images=save_images, label_map='source') + dataset.export(temp_dir, 'voc', save_images=save_images, + label_map='source') make_zip_archive(temp_dir, dst_file) @@ -56,7 +55,6 @@ def _import(src_file, task_data): for f in anno_files: shutil.move(f, anno_dir) - dataset = dm_env.make_importer('voc')(tmp_dir).make_dataset() - masks_to_polygons = dm_env.transforms.get('masks_to_polygons') - dataset = dataset.transform(masks_to_polygons) + dataset = Dataset.import_from(tmp_dir, 'voc', env=dm_env) + dataset.transform('masks_to_polygons') import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/tfrecord.py b/cvat/apps/dataset_manager/formats/tfrecord.py index 3b7e123eb63..9847bf61b66 100644 --- a/cvat/apps/dataset_manager/formats/tfrecord.py +++ b/cvat/apps/dataset_manager/formats/tfrecord.py @@ -24,11 +24,10 @@ @exporter(name='TFRecord', ext='ZIP', version='1.0', enabled=tf_available) def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('tf_detection_api').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'tf_detection_api', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -37,5 +36,5 @@ def _import(src_file, task_data): with TemporaryDirectory() as tmp_dir: Archive(src_file.name).extractall(tmp_dir) - dataset = dm_env.make_importer('tf_detection_api')(tmp_dir).make_dataset() + dataset = Dataset.import_from(tmp_dir, 'tf_detection_api', env=dm_env) import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/yolo.py b/cvat/apps/dataset_manager/formats/yolo.py index bea73b3c3bd..0df6f5fe27a 100644 --- a/cvat/apps/dataset_manager/formats/yolo.py +++ b/cvat/apps/dataset_manager/formats/yolo.py @@ -20,11 +20,10 @@ @exporter(name='YOLO', ext='ZIP', version='1.1') def _export(dst_file, task_data, save_images=False): - extractor = CvatTaskDataExtractor(task_data, include_images=save_images) - extractor = Dataset.from_extractors(extractor) # apply lazy transforms + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) with TemporaryDirectory() as temp_dir: - dm_env.converters.get('yolo').convert(extractor, - save_dir=temp_dir, save_images=save_images) + dataset.export(temp_dir, 'yolo', save_images=save_images) make_zip_archive(temp_dir, dst_file) @@ -44,11 +43,11 @@ def _import(src_file, task_data): frame_id = match_dm_item(DatasetItem(id=frame), task_data, root_hint=root_hint) frame_info = task_data.frame_info[frame_id] - except Exception: + except Exception: # nosec pass if frame_info is not None: image_info[frame] = (frame_info['height'], frame_info['width']) - dataset = dm_env.make_importer('yolo')(tmp_dir, image_info=image_info) \ - .make_dataset() + dataset = Dataset.import_from(tmp_dir, 'yolo', + env=dm_env, image_info=image_info) import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/tests/test_formats.py b/cvat/apps/dataset_manager/tests/test_formats.py index a01e60b78ae..5103da65ba3 100644 --- a/cvat/apps/dataset_manager/tests/test_formats.py +++ b/cvat/apps/dataset_manager/tests/test_formats.py @@ -71,6 +71,13 @@ def _put_api_v1_task_id_annotations(self, tid, data): return response + def _put_api_v1_job_id_annotations(self, jid, data): + with ForceLogin(self.user, self.client): + response = self.client.put("/api/v1/jobs/%s/annotations" % jid, + data=data, format="json") + + return response + def _create_task(self, data, image_data): with ForceLogin(self.user, self.client): response = self.client.post('/api/v1/tasks', data=data, format="json") @@ -87,6 +94,10 @@ def _create_task(self, data, image_data): return task class TaskExportTest(_DbTestBase): + def _generate_custom_annotations(self, annotations, task): + self._put_api_v1_task_id_annotations(task["id"], annotations) + return annotations + def _generate_annotations(self, task): annotations = { "version": 0, @@ -204,8 +215,7 @@ def _generate_annotations(self, task): }, ] } - self._put_api_v1_task_id_annotations(task["id"], annotations) - return annotations + return self._generate_custom_annotations(annotations, task) def _generate_task_images(self, count): # pylint: disable=no-self-use images = { @@ -215,7 +225,7 @@ def _generate_task_images(self, count): # pylint: disable=no-self-use images["image_quality"] = 75 return images - def _generate_task(self, images): + def _generate_task(self, images, **overrides): task = { "name": "my task #1", "overlap": 0, @@ -242,6 +252,7 @@ def _generate_task(self, images): {"name": "person"}, ] } + task.update(overrides) return self._create_task(task, images) @staticmethod @@ -346,17 +357,18 @@ def load_dataset(src): project.config.remove('sources') return project.make_dataset() - return dm_env.make_importer(importer_name)(src) \ - .make_dataset() + return datumaro.components.dataset. \ + Dataset.import_from(src, importer_name, env=dm_env) if zipfile.is_zipfile(file_path): with tempfile.TemporaryDirectory() as tmp_dir: zipfile.ZipFile(file_path).extractall(tmp_dir) dataset = load_dataset(tmp_dir) + self.assertEqual(len(dataset), task["size"]) else: dataset = load_dataset(file_path) + self.assertEqual(len(dataset), task["size"]) - self.assertEqual(len(dataset), task["size"]) self._test_export(check, task, format_name, save_images=False) def test_can_skip_outside(self): @@ -422,6 +434,47 @@ def test_can_make_abs_frame_id_from_known(self): self.assertEqual(5, task_data.abs_frame_id(2)) + def test_frames_outside_are_not_generated(self): + # https://github.com/openvinotoolkit/cvat/issues/2827 + images = self._generate_task_images(10) + images['start_frame'] = 0 + task = self._generate_task(images, overlap=3, segment_size=6) + annotations = { + "version": 0, + "tags": [], + "shapes": [], + "tracks": [ + { + "frame": 6, + "label_id": task["labels"][0]["id"], + "group": None, + "source": "manual", + "attributes": [], + "shapes": [ + { + "frame": 6, + "points": [1.0, 2.1, 100, 300.222], + "type": "rectangle", + "occluded": False, + "outside": False, + "attributes": [], + }, + ] + }, + ] + } + self._put_api_v1_job_id_annotations( + task["segments"][2]["jobs"][0]["id"], annotations) + + task_ann = TaskAnnotation(task["id"]) + task_ann.init_from_db() + task_data = TaskData(task_ann.ir_data, Task.objects.get(pk=task['id'])) + + i = -1 + for i, frame in enumerate(task_data.group_by_frame()): + self.assertTrue(frame.frame in range(6, 10)) + self.assertEqual(i + 1, 4) + class FrameMatchingTest(_DbTestBase): def _generate_task_images(self, paths): # pylint: disable=no-self-use f = BytesIO() diff --git a/cvat/requirements/base.txt b/cvat/requirements/base.txt index c8a6bda4791..45cb5f16630 100644 --- a/cvat/requirements/base.txt +++ b/cvat/requirements/base.txt @@ -1,12 +1,12 @@ click==7.1.2 -Django==3.1.1 +Django==3.1.7 django-appconf==1.0.4 django-auth-ldap==2.2.0 django-cacheops==5.0.1 django-compressor==2.4 django-rq==2.3.2 EasyProcess==0.3 -Pillow==7.2.0 +Pillow==8.1.1 numpy==1.19.5 python-ldap==3.3.1 pytz==2020.1 @@ -45,5 +45,9 @@ tensorflow==2.4.1 # Optional requirement of Datumaro patool==1.12 diskcache==5.0.2 open3d==0.11.2 -# workaround for binary incompatibility with numpy when pycocotools is installed by wheel -datumaro==0.1.5.1 --no-binary=datumaro --no-binary=pycocotools +# --no-binary=datumaro: workaround for pip to install +# opencv-headless instead of regular opencv, to actually run setup script +# --no-binary=pycocotools: workaround for binary incompatibility on numpy 1.20 +# of pycocotools and tensorflow 2.4.1 +# when pycocotools is installed by wheel in python 3.8+ +datumaro==0.1.6.1 --no-binary=datumaro --no-binary=pycocotools diff --git a/tests/cypress/integration/actions_tasks_objects/case_43_create_label_with_existing_label_name.js b/tests/cypress/integration/actions_tasks_objects/case_43_create_label_with_existing_label_name.js index 198f7788986..796e41d1a57 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_43_create_label_with_existing_label_name.js +++ b/tests/cypress/integration/actions_tasks_objects/case_43_create_label_with_existing_label_name.js @@ -24,11 +24,10 @@ context('Creating a label with existing label name.', () => { // Try to create a label with existing label name cy.get('.cvat-constructor-viewer-new-item').click(); cy.get('[placeholder="Label name"]').type(firstLabelName); - cy.contains('[type="submit"]', 'Done').click(); + cy.contains('[role="alert"]', 'Label name must be unique for the task') // Checking alert visibility + .should('exist') + .and('be.visible'); }); - cy.get('.cvat-notification-notice-update-task-failed') - .should('exist') - .and('contain.text', 'label names must be unique'); }); }); }); diff --git a/tests/cypress/integration/actions_tasks_objects/case_52_dump_upload_annotation.js b/tests/cypress/integration/actions_tasks_objects/case_52_dump_upload_annotation.js index e5d34b89f3b..ae1d9e01ba3 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_52_dump_upload_annotation.js +++ b/tests/cypress/integration/actions_tasks_objects/case_52_dump_upload_annotation.js @@ -18,9 +18,47 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => { secondY: 450, }; + const labelNameSecond = `Case ${caseId}`; + const taskNameSecond = `New annotation task for ${labelNameSecond}`; + const attrName = `Attr for ${labelNameSecond}`; + const textDefaultValue = 'Some default value for type Text'; + const imagesCount = 1; + const imageFileName = `image_${labelNameSecond.replace(' ', '_').toLowerCase()}`; + const width = 800; + const height = 800; + const posX = 10; + const posY = 10; + const color = 'gray'; + const archiveName = `${imageFileName}.zip`; + const archivePath = `cypress/fixtures/${archiveName}`; + const imagesFolder = `cypress/fixtures/${imageFileName}`; + const directoryToArchive = imagesFolder; + const dumpType = 'CVAT for images'; let annotationArchiveName = ''; + function uploadToTask(toTaskName) { + cy.contains('.cvat-item-task-name', toTaskName) + .parents('.cvat-tasks-list-item') + .find('.cvat-menu-icon') + .trigger('mouseover'); + cy.contains('Upload annotations').trigger('mouseover'); + cy.contains('.cvat-menu-load-submenu-item', dumpType.split(' ')[0]) + .should('be.visible') + .within(() => { + cy.get('.cvat-menu-load-submenu-item-button') + .click() + .get('input[type=file]') + .attachFile(annotationArchiveName); + }); + } + + function confirmUpdate(modalWindowClassName) { + cy.get(modalWindowClassName).within(() => { + cy.contains('button', 'Update').click(); + }); + } + before(() => { cy.openTaskJob(taskName); cy.createRectangle(createRectangleTrack2Points); @@ -49,7 +87,7 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => { }); }); - it('Upload annotation.', () => { + it('Upload annotation to job.', () => { cy.interactMenu('Upload annotations'); cy.contains('.cvat-menu-load-submenu-item', dumpType.split(' ')[0]) .should('be.visible') @@ -61,14 +99,48 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => { }); cy.intercept('PUT', '/api/v1/jobs/**/annotations**').as('uploadAnnotationsPut'); cy.intercept('GET', '/api/v1/jobs/**/annotations**').as('uploadAnnotationsGet'); - cy.get('.cvat-modal-content-load-job-annotation').within(() => { - cy.contains('button', 'Update').click(); - }); + confirmUpdate('.cvat-modal-content-load-job-annotation'); cy.wait('@uploadAnnotationsPut', { timeout: 5000 }).its('response.statusCode').should('equal', 202); cy.wait('@uploadAnnotationsPut').its('response.statusCode').should('equal', 201); cy.wait('@uploadAnnotationsGet').its('response.statusCode').should('equal', 200); cy.get('#cvat_canvas_shape_1').should('exist'); cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); + cy.removeAnnotations(); + }); + + it('Upload annotation to task.', () => { + cy.goToTaskList(); + uploadToTask(taskName); + confirmUpdate('.cvat-modal-content-load-task-annotation'); + cy.contains('Annotations have been loaded').should('be.visible'); + cy.get('[data-icon="close"]').click(); + cy.openTaskJob(taskName, 0, false); + cy.get('#cvat_canvas_shape_1').should('exist'); + cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); + }); + + it('Upload annotation to task which does not match the parameters. The error should be exist.', () => { + cy.goToTaskList(); + cy.imageGenerator( + imagesFolder, + imageFileName, + width, + height, + color, + posX, + posY, + labelNameSecond, + imagesCount, + ); + cy.createZipArchive(directoryToArchive, archivePath); + cy.createAnnotationTask(taskNameSecond, labelNameSecond, attrName, textDefaultValue, archiveName); + uploadToTask(taskNameSecond); + confirmUpdate('.cvat-modal-content-load-task-annotation'); + cy.get('.cvat-notification-notice-load-annotation-failed') + .should('exist') + .find('[aria-label="close"]') + .click(); + cy.deleteTask(taskNameSecond); }); }); }); diff --git a/tests/cypress/integration/actions_tasks_objects/case_54_redraw_feature.js b/tests/cypress/integration/actions_tasks_objects/case_54_redraw_feature.js new file mode 100644 index 00000000000..f64c3ca7e9c --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_54_redraw_feature.js @@ -0,0 +1,149 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName, labelName } from '../../support/const'; + +context('Redraw feature.', () => { + const caseId = '54'; + const createRectangleShape2Points = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 150, + firstY: 350, + secondX: 250, + secondY: 450, + }; + const createCuboidShape2Points = { + points: 'From rectangle', + type: 'Shape', + labelName: labelName, + firstX: 300, + firstY: 350, + secondX: 400, + secondY: 450, + }; + const createPolygonShape = { + reDraw: false, + type: 'Shape', + labelName: labelName, + pointsMap: [ + { x: 450, y: 350 }, + { x: 550, y: 350 }, + { x: 550, y: 450 }, + ], + complete: true, + numberOfPoints: null, + }; + const createPolylinesShape = { + type: 'Shape', + labelName: labelName, + pointsMap: [ + { x: 600, y: 350 }, + { x: 700, y: 350 }, + { x: 700, y: 450 }, + ], + complete: true, + numberOfPoints: null, + }; + const createPointsShape = { + type: 'Shape', + labelName: labelName, + pointsMap: [{ x: 750, y: 400 }], + complete: true, + numberOfPoints: null, + }; + const keyCodeN = 78; + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Draw and redraw a rectangle.', () => { + cy.createRectangle(createRectangleShape2Points); + cy.get('.cvat-canvas-container').trigger('mousemove', 200, 400); + cy.get('#cvat_canvas_shape_1').should('have.class', 'cvat_canvas_shape_activated'); + cy.get('body').trigger('keydown', { keyCode: keyCodeN, shiftKey: true }); // Start redraw the rectangle + cy.get('.cvat-canvas-container') + .click(createRectangleShape2Points.firstX, createRectangleShape2Points.firstY - 50) + .click(createRectangleShape2Points.secondX, createRectangleShape2Points.secondY - 50); + cy.get('.cvat_canvas_shape').then(($shape) => { + expect($shape.length).to.be.equal(1); + }); + cy.get('.cvat-objects-sidebar-state-item').then(($sidebarItem) => { + expect($sidebarItem.length).to.be.equal(1); + }); + }); + + it('Draw and redraw a polygon.', () => { + cy.createPolygon(createPolygonShape); + cy.get('.cvat-canvas-container').trigger('mousemove', 520, 400); + cy.get('#cvat_canvas_shape_2').should('have.class', 'cvat_canvas_shape_activated'); + cy.get('body').trigger('keydown', { keyCode: keyCodeN, shiftKey: true }); // Start redraw the polygon + createPolygonShape.pointsMap.forEach((element) => { + cy.get('.cvat-canvas-container').click(element.x, element.y - 50); + }); + cy.get('.cvat-canvas-container').trigger('keydown', { keyCode: keyCodeN }).trigger('keyup'); + cy.get('.cvat_canvas_shape').then(($shape) => { + expect($shape.length).to.be.equal(2); + }); + cy.get('.cvat-objects-sidebar-state-item').then(($sidebarItem) => { + expect($sidebarItem.length).to.be.equal(2); + }); + }); + + it('Draw and redraw a polyline.', () => { + cy.createPolyline(createPolylinesShape); + cy.get('.cvat-canvas-container').trigger('mousemove', 700, 400); + cy.get('#cvat_canvas_shape_3').should('have.class', 'cvat_canvas_shape_activated'); + cy.get('body').trigger('keydown', { keyCode: keyCodeN, shiftKey: true }); // Start redraw the polyline + createPolylinesShape.pointsMap.forEach((element) => { + cy.get('.cvat-canvas-container').click(element.x, element.y - 50); + }); + cy.get('.cvat-canvas-container').trigger('keydown', { keyCode: keyCodeN }).trigger('keyup'); + cy.get('.cvat_canvas_shape').then(($shape) => { + expect($shape.length).to.be.equal(3); + }); + cy.get('.cvat-objects-sidebar-state-item').then(($sidebarItem) => { + expect($sidebarItem.length).to.be.equal(3); + }); + }); + + it('Draw and redraw a point.', () => { + cy.createPoint(createPointsShape); + cy.get('.cvat-canvas-container').trigger('mousemove', 750, 400); + cy.get('body').trigger('keydown', { keyCode: keyCodeN, shiftKey: true }); // Start redraw the point + createPointsShape.pointsMap.forEach((element) => { + cy.get('.cvat-canvas-container').click(element.x, element.y - 50); + }); + cy.get('.cvat-canvas-container').trigger('keydown', { keyCode: keyCodeN }).trigger('keyup'); + cy.get('.cvat_canvas_shape').then(($shape) => { + expect($shape.length).to.be.equal(4); + }); + cy.get('.cvat-objects-sidebar-state-item').then(($sidebarItem) => { + expect($sidebarItem.length).to.be.equal(4); + }); + }); + + it.skip('Draw and redraw a cuboid.', () => { + // Need to fix issue https://github.com/openvinotoolkit/cvat/issues/2873 + cy.createCuboid(createCuboidShape2Points); + cy.get('.cvat-canvas-container').trigger('mousemove', 300, 400); + cy.get('#cvat_canvas_shape_5').should('have.class', 'cvat_canvas_shape_activated'); + cy.get('body').trigger('keydown', { keyCode: keyCodeN, shiftKey: true }); // Start redraw the cuboid + cy.get('.cvat-canvas-container') + .click(createCuboidShape2Points.firstX, createCuboidShape2Points.firstY - 50) + .click(createCuboidShape2Points.secondX, createCuboidShape2Points.secondY - 50); + cy.get('.cvat_canvas_shape').then(($shape) => { + expect($shape.length).to.be.equal(5); + }); + cy.get('.cvat-objects-sidebar-state-item').then(($sidebarItem) => { + expect($sidebarItem.length).to.be.equal(5); + }); + }); + }); +}); diff --git a/tests/cypress/integration/actions_tasks_objects/case_55_repeat_draw_feature.js b/tests/cypress/integration/actions_tasks_objects/case_55_repeat_draw_feature.js new file mode 100644 index 00000000000..fe1738ca1ed --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_55_repeat_draw_feature.js @@ -0,0 +1,149 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName, labelName } from '../../support/const'; + +context('Repeat draw feature.', () => { + const caseId = '55'; + const createRectangleShape2Points = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 150, + firstY: 350, + secondX: 250, + secondY: 450, + }; + const createCuboidShape2Points = { + points: 'From rectangle', + type: 'Shape', + labelName: labelName, + firstX: 300, + firstY: 350, + secondX: 400, + secondY: 450, + }; + const createPolygonShape = { + redraw: false, + type: 'Shape', + labelName: labelName, + pointsMap: [ + { x: 450, y: 350 }, + { x: 550, y: 350 }, + { x: 550, y: 450 }, + ], + complete: true, + numberOfPoints: null, + }; + const createPolylinesShape = { + type: 'Shape', + labelName: labelName, + pointsMap: [ + { x: 600, y: 350 }, + { x: 700, y: 350 }, + { x: 700, y: 450 }, + ], + complete: true, + numberOfPoints: null, + }; + const createPointsShape = { + type: 'Shape', + labelName: labelName, + pointsMap: [{ x: 750, y: 400 }], + complete: true, + numberOfPoints: null, + }; + const keyCodeN = 78; + + function checkCountShapes(expectedCount) { + cy.get('.cvat-objects-sidebar-state-item').then(($sidebarItem) => { + expect($sidebarItem.length).to.be.equal(expectedCount); + }); + } + + function checkShapeType(id, expectedType) { + cy.get(id).find('.cvat-objects-sidebar-state-item-object-type-text').should('have.text', expectedType); + } + + function repeatDrawningStart() { + cy.get('body').trigger('keydown', { keyCode: keyCodeN }); + } + + function repeatDrawningFinish() { + cy.get('.cvat-canvas-container').trigger('keydown', { keyCode: keyCodeN }).trigger('keyup'); + } + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Draw and repeat the drawing of the rectangle.', () => { + cy.createRectangle(createRectangleShape2Points); + repeatDrawningStart(); // Repeat the drawing the rectangle + cy.get('.cvat-canvas-container') + .click(createRectangleShape2Points.firstX, createRectangleShape2Points.firstY - 200) + .click(createRectangleShape2Points.secondX, createRectangleShape2Points.secondY - 200); + cy.get('#cvat_canvas_shape_2').should('exist'); + checkCountShapes(2); + checkShapeType('#cvat-objects-sidebar-state-item-2', 'RECTANGLE SHAPE'); + }); + + it('Draw and repeat the drawing of the polygon.', () => { + cy.createPolygon(createPolygonShape); + repeatDrawningStart(); // Repeat the drawing the polygon + createPolygonShape.pointsMap.forEach((element) => { + cy.get('.cvat-canvas-container').click(element.x, element.y - 200); + }); + repeatDrawningFinish(); + cy.get('#cvat_canvas_shape_4').should('exist'); + checkCountShapes(4); + checkShapeType('#cvat-objects-sidebar-state-item-4', 'POLYGON SHAPE'); + }); + + it('Draw and repeat the drawing of the polyline.', () => { + cy.createPolyline(createPolylinesShape); + repeatDrawningStart(); // Repeat the drawing the polyline + createPolylinesShape.pointsMap.forEach((element) => { + cy.get('.cvat-canvas-container').click(element.x, element.y - 200); + }); + repeatDrawningFinish(); + cy.get('#cvat_canvas_shape_6').should('exist'); + checkCountShapes(6); + checkShapeType('#cvat-objects-sidebar-state-item-6', 'POLYLINE SHAPE'); + }); + + it('Draw and repeat the drawing of the point.', () => { + cy.createPoint(createPointsShape); + repeatDrawningStart(); // Repeat the drawing the point + createPointsShape.pointsMap.forEach((element) => { + cy.get('.cvat-canvas-container').click(element.x, element.y - 200); + }); + repeatDrawningFinish(); + cy.get('#cvat_canvas_shape_8').should('exist'); + checkCountShapes(8); + checkShapeType('#cvat-objects-sidebar-state-item-8', 'POINTS SHAPE'); + }); + + it('Draw and repeat the drawing of the cuboid.', () => { + cy.createCuboid(createCuboidShape2Points); + repeatDrawningStart(); // Repeat the drawing the cuboid + cy.get('.cvat-canvas-container') + .click(createCuboidShape2Points.firstX, createCuboidShape2Points.firstY - 200) + .click(createCuboidShape2Points.secondX, createCuboidShape2Points.secondY - 200); + cy.get('#cvat_canvas_shape_10').should('exist'); + checkCountShapes(10); + checkShapeType('#cvat-objects-sidebar-state-item-10', 'CUBOID SHAPE'); + }); + + it('Cteate and repeat the creating of the tag.', () => { + cy.createTag(labelName); + repeatDrawningStart(); // Repeat the creating the tag + cy.get('#cvat-objects-sidebar-state-item-12').should('exist'); + checkShapeType('#cvat-objects-sidebar-state-item-12', 'TAG'); + }); + }); +}); diff --git a/tests/cypress/integration/actions_tasks_objects/issue_2230_maintenance_popover_visibility.js b/tests/cypress/integration/actions_tasks_objects/issue_2230_maintenance_popover_visibility.js new file mode 100644 index 00000000000..176b9087e1f --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/issue_2230_maintenance_popover_visibility.js @@ -0,0 +1,28 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName } from '../../support/const'; + +context('Check maintenance of popups visibility.', () => { + const issueId = '2230'; + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing issue "${issueId}"`, () => { + it('Open a popover for draw an object and apply the "mouseout" event to it. The popover be visible.', () => { + cy.interactControlButton('draw-rectangle'); + cy.get('.cvat-draw-rectangle-popover-visible').trigger('mouseout').wait(500); + cy.get('.cvat-draw-rectangle-popover-visible').should('exist'); + }); + + it('Click to another element. The popover hidden.', () => { + cy.get('.cvat-canvas-container').click(); + cy.get('.cvat-draw-rectangle-popover-visible').should('not.exist'); + }); + }); +});