Reformatted (#2349)

main
Vitaliy Nishukov 5 years ago committed by GitHub
parent 534ad3940c
commit 7512fd6883
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,5 @@
exclude_paths:
- '**/3rdparty/**'
- '**/engine/js/cvat-core.min.js'
- '**/engine/js/unzip_imgs.js'
- CHANGELOG.md
- '**/3rdparty/**'
- '**/engine/js/cvat-core.min.js'
- '**/engine/js/unzip_imgs.js'
- CHANGELOG.md

@ -0,0 +1,9 @@
.*/
3rdparty/
node_modules/
dist/
data/
datumaro/
keys/
logs/
static/

@ -1,53 +1,23 @@
/*
* Copyright (C) 2018 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
"env": {
"node": false,
"browser": true,
"es6": true,
"jquery": true,
"qunit": true,
module.exports = {
env: {
node: false,
browser: true,
es6: true,
jquery: true,
qunit: true,
'jest/globals': true,
'cypress/globals': true,
},
"parserOptions": {
"sourceType": "script",
parserOptions: {
sourceType: 'script',
},
"plugins": [
"security",
"no-unsanitized",
"no-unsafe-innerhtml",
],
"extends": [
"eslint:recommended",
"plugin:security/recommended",
"plugin:no-unsanitized/DOM",
"airbnb-base",
],
"rules": {
"no-await-in-loop": [0],
"global-require": [0],
"no-new": [0],
"class-methods-use-this": [0],
"no-restricted-properties": [0, {
"object": "Math",
"property": "pow",
}],
"no-param-reassign": [0],
"no-underscore-dangle": ["error", { "allowAfterThis": true }],
"no-restricted-syntax": [0, {"selector": "ForOfStatement"}],
"no-continue": [0],
"no-unsafe-innerhtml/no-unsafe-innerhtml": 1,
// This rule actual for user input data on the node.js environment mainly.
"security/detect-object-injection": 0,
"indent": ["warn", 4],
// recently added to airbnb
"max-classes-per-file": [0],
// it was opposite before and our code has been written according to previous rule
"arrow-parens": [0],
// object spread is a modern ECMA standard. Let's do not use it without babel
"prefer-object-spread": [0],
plugins: ['requirejs', 'jest', 'cypress', 'eslint-plugin-header'],
extends: ['eslint:recommended', 'plugin:requirejs/recommended', 'prettier'],
rules: {
'header/header': [2, '.header.js'],
},
};

@ -0,0 +1,9 @@
.*/
3rdparty/
node_modules/
dist/
data/
datumaro/
keys/
logs/
static/

@ -15,5 +15,13 @@
"tabWidth": 4,
"trailingComma": "all",
"useTabs": false,
"vueIndentScriptAndStyle": false
"vueIndentScriptAndStyle": false,
"overrides": [
{
"files": ["*.json", "*.yml", "*.yaml", "*.md"],
"options": {
"tabWidth": 2
}
}
]
}

@ -1,16 +1,16 @@
exports.settings = {bullet: '*', paddedTable: false}
exports.settings = { bullet: '*', paddedTable: false };
exports.plugins = [
'remark-preset-lint-recommended',
'remark-preset-lint-consistent',
['remark-preset-lint-markdown-style-guide', 'mixed'],
['remark-lint-no-dead-urls', { skipOffline: true }],
['remark-lint-maximum-line-length', 120],
['remark-lint-maximum-heading-length', 120],
['remark-lint-strong-marker', "*"],
['remark-lint-emphasis-marker', "_"],
['remark-lint-unordered-list-marker-style', "-"],
['remark-lint-ordered-list-marker-style', "."],
['remark-lint-no-file-name-irregular-characters', false],
['remark-lint-list-item-spacing', false],
]
'remark-preset-lint-recommended',
'remark-preset-lint-consistent',
['remark-preset-lint-markdown-style-guide', 'mixed'],
['remark-lint-no-dead-urls', { skipOffline: true }],
['remark-lint-maximum-line-length', 120],
['remark-lint-maximum-heading-length', 120],
['remark-lint-strong-marker', '*'],
['remark-lint-emphasis-marker', '_'],
['remark-lint-unordered-list-marker-style', '-'],
['remark-lint-ordered-list-marker-style', '.'],
['remark-lint-no-file-name-irregular-characters', false],
['remark-lint-list-item-spacing', false],
];

@ -1,20 +1,22 @@
{
"extends": "stylelint-config-standard",
"rules": {
"indentation": 4,
"value-keyword-case": null,
"selector-combinator-space-after": null,
"no-descending-specificity": null,
"at-rule-no-unknown": [true, {
"ignoreAtRules": ["extend"]
}],
"selector-type-no-unknown": [true, {
"ignoreTypes": ["first-child"]
}]
},
"ignoreFiles": [
"**/*.js",
"**/*.ts",
"**/*.py"
"extends": "stylelint-config-standard",
"rules": {
"indentation": 4,
"value-keyword-case": null,
"selector-combinator-space-after": null,
"no-descending-specificity": null,
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": ["extend"]
}
],
"selector-type-no-unknown": [
true,
{
"ignoreTypes": ["first-child"]
}
]
},
"ignoreFiles": ["**/*.js", "**/*.ts", "**/*.py"]
}

@ -1,7 +1,7 @@
language: python
python:
- "3.5"
- '3.5'
cache:
npm: true
@ -9,11 +9,11 @@ cache:
- ~/.cache
addons:
firefox: "latest"
firefox: 'latest'
chrome: stable
apt:
packages:
- libgconf-2-4
- libgconf-2-4
services:
- docker
@ -42,7 +42,7 @@ script:
- cd ./tests && npm install && cd ..
- if [[ $TRAVIS_EVENT_TYPE == "cron" && $TRAVIS_BRANCH == "develop" ]];
then
cd ./tests && npm run cypress:run:firefox && exit $?;
cd ./tests && npm run cypress:run:firefox && exit $?;
fi;
- npm install && npm run coverage
- docker-compose up -d --build

File diff suppressed because it is too large Load Diff

@ -9,85 +9,96 @@ they should reciprocate that respect in addressing your issue or assessing
patches and features.
## Development environment
- Install necessary dependencies:
Ubuntu 18.04
```sh
sudo apt-get update && sudo apt-get --no-install-recommends install -y build-essential curl redis-server python3-dev python3-pip python3-venv python3-tk libldap2-dev libsasl2-dev pkg-config libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev
```
```sh
# Node and npm (you can use default versions of these packages from apt (8.*, 3.*), but we would recommend to use newer versions)
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
```
- Install necessary dependencies:
MacOS 10.15
```sh
brew install git python pyenv redis curl openssl node
```
Ubuntu 18.04
- Install FFmpeg libraries (libav*) version 4.0 or higher.
- Install [Visual Studio Code](https://code.visualstudio.com/docs/setup/linux#_debian-and-ubuntu-based-distributions)
for development
- Install CVAT on your local host:
```sh
git clone https://github.com/opencv/cvat
cd cvat && mkdir logs keys
python3 -m venv .env
. .env/bin/activate
pip install -U pip wheel setuptools
pip install -r cvat/requirements/development.txt
python manage.py migrate
python manage.py collectstatic
```
> Note for Mac users
>
> If you have any problems with installing dependencies from
> ```cvat/requirements/*.txt```, you may need to reinstall your system python
> In some cases after system update it can be configured incorrectly and cannot compile some native modules
- Create a super user for CVAT:
```sh
$ python manage.py createsuperuser
Username (leave blank to use 'django'): ***
Email address: ***
Password: ***
Password (again): ***
```
```sh
sudo apt-get update && sudo apt-get --no-install-recommends install -y build-essential curl redis-server python3-dev python3-pip python3-venv python3-tk libldap2-dev libsasl2-dev pkg-config libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev
```
- Install npm packages for UI and start UI debug server (run the following command from CVAT root directory):
```sh
npm install && \
cd cvat-core && npm install && \
cd ../cvat-ui && npm install && npm start
```
> Note for Mac users
>
> If you faced with error
>
> ```Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (57)```
>
> Read this article [Node Sass does not yet support your current environment](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
- Open new terminal (Ctrl + Shift + T), run Visual Studio Code from the virtual environment
```sh
cd .. && source .env/bin/activate && code
```
```sh
# Node and npm (you can use default versions of these packages from apt (8.*, 3.*), but we would recommend to use newer versions)
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
```
MacOS 10.15
```sh
brew install git python pyenv redis curl openssl node
```
- Install FFmpeg libraries (libav\*) version 4.0 or higher.
- Install [Visual Studio Code](https://code.visualstudio.com/docs/setup/linux#_debian-and-ubuntu-based-distributions)
for development
- Install CVAT on your local host:
```sh
git clone https://github.com/opencv/cvat
cd cvat && mkdir logs keys
python3 -m venv .env
. .env/bin/activate
pip install -U pip wheel setuptools
pip install -r cvat/requirements/development.txt
python manage.py migrate
python manage.py collectstatic
```
> Note for Mac users
>
> If you have any problems with installing dependencies from
> `cvat/requirements/*.txt`, you may need to reinstall your system python
> In some cases after system update it can be configured incorrectly and cannot compile some native modules
- Create a super user for CVAT:
- Install following VS Code extensions:
- [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint)
- [vscode-remark-lint](https://marketplace.visualstudio.com/items?itemName=drewbourne.vscode-remark-lint)
- [licenser](https://marketplace.visualstudio.com/items?itemName=ymotongpoo.licenser)
- [Trailing Spaces](https://marketplace.visualstudio.com/items?itemName=shardulm94.trailing-spaces)
```sh
$ python manage.py createsuperuser
Username (leave blank to use 'django'): ***
Email address: ***
Password: ***
Password (again): ***
```
- Reload Visual Studio Code from virtual environment
- Install npm packages for UI and start UI debug server (run the following command from CVAT root directory):
- Select `server: debug` configuration and start it (F5) to run REST server and its workers
```sh
npm install && \
cd cvat-core && npm install && \
cd ../cvat-ui && npm install && npm start
```
> Note for Mac users
>
> If you faced with error
>
> `Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (57)`
>
> Read this article [Node Sass does not yet support your current environment](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
- Open new terminal (Ctrl + Shift + T), run Visual Studio Code from the virtual environment
```sh
cd .. && source .env/bin/activate && code
```
- Install following VS Code extensions:
- [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint)
- [vscode-remark-lint](https://marketplace.visualstudio.com/items?itemName=drewbourne.vscode-remark-lint)
- [licenser](https://marketplace.visualstudio.com/items?itemName=ymotongpoo.licenser)
- [Trailing Spaces](https://marketplace.visualstudio.com/items?itemName=shardulm94.trailing-spaces)
- Reload Visual Studio Code from virtual environment
- Select `server: debug` configuration and start it (F5) to run REST server and its workers
You have done! Now it is possible to insert breakpoints and debug server and client of the tool.
@ -95,30 +106,32 @@ You have done! Now it is possible to insert breakpoints and debug server and cli
You develop CVAT under WSL (Windows subsystem for Linux) following next steps.
- Install WSL using [this guide](https://docs.microsoft.com/ru-ru/windows/wsl/install-win10).
- Install WSL using [this guide](https://docs.microsoft.com/ru-ru/windows/wsl/install-win10).
- Following this guide install Ubuntu 18.04 Linux distribution for WSL.
- Following this guide install Ubuntu 18.04 Linux distribution for WSL.
- Run Ubuntu using start menu link or execute next command
```powershell
wsl -d Ubuntu-18.04
```
- Run Ubuntu using start menu link or execute next command
```powershell
wsl -d Ubuntu-18.04
```
- Run all commands from this isntallation guide in WSL Ubuntu shell.
- Run all commands from this isntallation guide in WSL Ubuntu shell.
## Setup additional components in development environment
### DL models as serverless functions
Install [nuclio platform](https://github.com/nuclio/nuclio):
- You have to install `nuctl` command line tool to build and deploy serverless
functions. Download [the latest release](
https://github.com/nuclio/nuclio/blob/development/docs/reference/nuctl/nuctl.md#download).
functions. Download [the latest release](https://github.com/nuclio/nuclio/blob/development/docs/reference/nuctl/nuctl.md#download).
- The simplest way to explore Nuclio is to run its graphical user interface (GUI)
of the Nuclio dashboard. All you need in order to run the dashboard is Docker. See
[nuclio documentation](https://github.com/nuclio/nuclio#quick-start-steps)
for more details.
of the Nuclio dashboard. All you need in order to run the dashboard is Docker. See
[nuclio documentation](https://github.com/nuclio/nuclio#quick-start-steps)
for more details.
- Create `cvat` project inside nuclio dashboard where you will deploy new
serverless functions and deploy a couple of DL models.
serverless functions and deploy a couple of DL models.
```bash
nuctl create project cvat
@ -181,7 +194,7 @@ nuctl deploy --project-name cvat \
</details>
- Display a list of running serverless functions using `nuctl` command or see them
in nuclio dashboard:
in nuclio dashboard:
```bash
nuctl get function
@ -198,7 +211,7 @@ nuctl get function
</details>
- Test your deployed DL model as a serverless function. The command below
should work on Linux and Mac OS.
should work on Linux and Mac OS.
```bash
image=$(curl https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png --output - | base64 | tr -d '\n')
@ -238,6 +251,7 @@ Server = nuclio
}
]
```
</details>
### Run Cypress tests
- Install Сypress as described in the [documentation](https://docs.cypress.io/guides/getting-started/installing-cypress.html).
@ -258,12 +272,12 @@ little exception - we prefer 4 spaces for indentation of nested blocks and state
The project uses [a successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model).
Thus it has a couple of branches. Some of them are described below:
- `origin/master` to be the main branch where the source code of
HEAD always reflects a production-ready state
- `origin/master` to be the main branch where the source code of
HEAD always reflects a production-ready state
- `origin/develop` to be the main branch where the source code of
HEAD always reflects a state with the latest delivered development
changes for the next release. Some would call this the “integration branch”.
- `origin/develop` to be the main branch where the source code of
HEAD always reflects a state with the latest delivered development
changes for the next release. Some would call this the “integration branch”.
## Using the issue tracker
@ -271,13 +285,14 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
[features requests](#features) and [submitting pull
requests](#pull-requests), but please respect the following restrictions:
- Please **do not** use the issue tracker for personal support requests (use
- Please **do not** use the issue tracker for personal support requests (use
[Stack Overflow](http://stackoverflow.com)).
- Please **do not** derail or troll issues. Keep the discussion on topic and
- Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
@ -286,10 +301,10 @@ Good bug reports are extremely helpful - thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported.
reported.
1. **Check if the issue has been fixed** &mdash; try to reproduce it using the
latest `develop` branch in the repository.
latest `develop` branch in the repository.
1. **Isolate the problem** &mdash; ideally create a reduced test case.
@ -316,6 +331,7 @@ Example:
> merits).
<a name="features"></a>
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
@ -324,6 +340,7 @@ case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
<a name="pull-requests"></a>
## Pull requests
Good pull requests - patches, improvements, new features - are a fantastic
@ -368,10 +385,10 @@ project:
```
1. Commit your changes in logical chunks. Please adhere to these [git commit
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Git's
[interactive rebase](https://docs.github.com/en/github/using-git/about-git-rebase)
feature to tidy up your commits before making them public.
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Git's
[interactive rebase](https://docs.github.com/en/github/using-git/about-git-rebase)
feature to tidy up your commits before making them public.
1. Locally merge (or rebase) the upstream development branch into your topic branch:

@ -46,18 +46,18 @@ and Dump annotation buttons.
framework allows additional dataset transformations
via its command line tool and Python library.
| Annotation format | Import | Export |
| ------------------------------------------------------------------------------------------ | ------ | ------ |
| [CVAT for images](cvat/apps/documentation/xml_format.md#annotation) | X | X |
| [CVAT for a video](cvat/apps/documentation/xml_format.md#interpolation) | X | X |
| [Datumaro](https://github.com/openvinotoolkit/datumaro) | | X |
| [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X |
| Segmentation masks from [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X |
| [YOLO](https://pjreddie.com/darknet/yolo/) | X | X |
| [MS COCO Object Detection](http://cocodataset.org/#format-data) | X | X |
| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X |
| [MOT](https://motchallenge.net/) | X | X |
| [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X |
| Annotation format | Import | Export |
| ----------------------------------------------------------------------------- | ------ | ------ |
| [CVAT for images](cvat/apps/documentation/xml_format.md#annotation) | X | X |
| [CVAT for a video](cvat/apps/documentation/xml_format.md#interpolation) | X | X |
| [Datumaro](https://github.com/openvinotoolkit/datumaro) | | X |
| [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X |
| Segmentation masks from [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X |
| [YOLO](https://pjreddie.com/darknet/yolo/) | X | X |
| [MS COCO Object Detection](http://cocodataset.org/#format-data) | X | X |
| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X |
| [MOT](https://motchallenge.net/) | X | X |
| [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X |
## Deep learning models for automatic labeling
@ -79,19 +79,21 @@ Try it online without local installation. Only own or assigned tasks
are visible to users.
Disabled features:
- [Analytics: management and monitoring of data annotation team](/components/analytics/README.md)
Limitations:
- No more than 10 tasks per user
- Uploaded data is limited to 500Mb
## REST API
Automatically generated Swagger documentation for Django REST API is
available on ``<cvat_origin>/api/swagger``
(default: ``localhost:8080/api/swagger``).
available on `<cvat_origin>/api/swagger`
(default: `localhost:8080/api/swagger`).
Swagger documentation is visiable on allowed hostes, Update environement variable in docker-compose.yml file with cvat hosted machine IP or domain name. Example - ``ALLOWED_HOSTS: 'localhost, 127.0.0.1'``)
Swagger documentation is visiable on allowed hostes, Update environement variable in docker-compose.yml file with cvat hosted machine IP or domain name. Example - `ALLOWED_HOSTS: 'localhost, 127.0.0.1'`)
## LICENSE
@ -105,16 +107,18 @@ contributors and other users.
However, if you have a feature request or a bug report that can reproduced,
feel free to open an issue (with steps to reproduce the bug if it's a bug
report) on [GitHub* issues](https://github.com/opencv/cvat/issues).
report) on [GitHub\* issues](https://github.com/opencv/cvat/issues).
If you are not sure or just want to browse other users common questions,
[Gitter chat](https://gitter.im/opencv-cvat) is the way to go.
Other ways to ask questions and get our support:
* [\#cvat](https://stackoverflow.com/search?q=%23cvat) tag on StackOverflow*
* [Forum on Intel Developer Zone](https://software.intel.com/en-us/forums/computer-vision)
- [\#cvat](https://stackoverflow.com/search?q=%23cvat) tag on StackOverflow\*
- [Forum on Intel Developer Zone](https://software.intel.com/en-us/forums/computer-vision)
## Links
- [Intel AI blog: New Computer Vision Tool Accelerates Annotation of Digital Images and Video](https://www.intel.ai/introducing-cvat)
- [Intel Software: Computer Vision Annotation Tool: A Universal Approach to Data Annotation](https://software.intel.com/en-us/articles/computer-vision-annotation-tool-a-universal-approach-to-data-annotation)
- [VentureBeat: Intel open-sources CVAT, a toolkit for data labeling](https://venturebeat.com/2019/03/05/intel-open-sources-cvat-a-toolkit-for-data-labeling/)

@ -5,12 +5,14 @@
It is possible to proxy annotation logs from client to ELK. To do that run the following command below:
### Build docker image
```bash
# From project root directory
docker-compose -f docker-compose.yml -f components/analytics/docker-compose.analytics.yml build
```
### Run docker container
```bash
# From project root directory
docker-compose -f docker-compose.yml -f components/analytics/docker-compose.analytics.yml up -d
@ -19,11 +21,12 @@ docker-compose -f docker-compose.yml -f components/analytics/docker-compose.anal
At the moment it is not possible to save advanced settings. Below values should be specified manually.
## Time picker default
{
"from": "now/d",
"to": "now/d",
"display": "Today",
"section": 0
"from": "now/d",
"to": "now/d",
"display": "Today",
"section": 0
}
## Time picker quick ranges

@ -35,9 +35,24 @@ services:
volumes: ['./components/analytics/kibana:/home/django/kibana:ro']
depends_on: ['cvat']
working_dir: '/home/django'
entrypoint: ['bash', 'wait-for-it.sh', 'elasticsearch:9200', '-t', '0', '--',
'/bin/bash', 'wait-for-it.sh', 'kibana:5601', '-t', '0', '--',
'/usr/bin/python3', 'kibana/setup.py', 'kibana/export.json']
entrypoint:
[
'bash',
'wait-for-it.sh',
'elasticsearch:9200',
'-t',
'0',
'--',
'/bin/bash',
'wait-for-it.sh',
'kibana:5601',
'-t',
'0',
'--',
'/usr/bin/python3',
'kibana/setup.py',
'kibana/export.json',
]
environment:
no_proxy: elasticsearch,kibana,${no_proxy}
@ -52,7 +67,7 @@ services:
context: ./components/analytics/logstash
args:
ELK_VERSION: 6.4.0
http_proxy: ${http_proxy}
http_proxy: ${http_proxy}
https_proxy: ${https_proxy}
depends_on: ['cvat_elasticsearch']
restart: always

@ -1,3 +1,3 @@
http.host: 0.0.0.0
script.painless.regex.enabled: true
path.repo: ["/usr/share/elasticsearch/data/backup"]
path.repo: ['/usr/share/elasticsearch/data/backup']

@ -40,19 +40,11 @@
"_type": "search",
"_source": {
"hits": 0,
"sort": [
"@timestamp",
"desc"
],
"sort": ["@timestamp", "desc"],
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"ec510550-c238-11e8-8e1b-758ef07f6de8\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"event:\\\"Send exception\\\"\"},\"filter\":[]}"
},
"columns": [
"task",
"type",
"userid",
"stack"
],
"columns": ["task", "type", "userid", "stack"],
"description": "",
"title": "Table with exceptions",
"version": 1
@ -195,4 +187,4 @@
"savedObjectVersion": 2
}
}
]
]

@ -1,5 +1,5 @@
server.host: 0.0.0.0
elasticsearch.url: http://elasticsearch:9200
elasticsearch.requestHeadersWhitelist: [ "cookie", "authorization", "x-forwarded-user" ]
kibana.defaultAppId: "discover"
elasticsearch.requestHeadersWhitelist: ['cookie', 'authorization', 'x-forwarded-user']
kibana.defaultAppId: 'discover'
server.basePath: /analytics

@ -1,6 +1,7 @@
## Serverless for Computer Vision Annotation Tool (CVAT)
### Run docker container
```bash
# From project root directory
docker-compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml up -d

@ -15,9 +15,9 @@ services:
http_proxy:
https_proxy:
no_proxy: 172.28.0.1,${no_proxy}
NUCLIO_CHECK_FUNCTION_CONTAINERS_HEALTHINESS: "true"
NUCLIO_CHECK_FUNCTION_CONTAINERS_HEALTHINESS: 'true'
ports:
- "8070:8070"
- '8070:8070'
cvat:
environment:

@ -1,39 +1,36 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
'env': {
'node': true,
'browser': true,
'es6': true,
env: {
node: true,
browser: true,
es6: true,
},
'parserOptions': {
'parser': '@typescript-eslint/parser',
'ecmaVersion': 6,
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 6,
},
'plugins': [
'@typescript-eslint',
'import',
],
'extends': [
plugins: ['@typescript-eslint', 'import'],
extends: [
'plugin:@typescript-eslint/recommended',
'airbnb-typescript/base',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
],
'rules': {
rules: {
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/indent': ['warn', 4],
'no-plusplus': 0,
'no-restricted-syntax': [
0,
{
'selector': 'ForOfStatement'
}
selector: 'ForOfStatement',
},
],
'max-len': ['error', { code: 120 }],
'no-continue': 0,
'func-names': 0,
'no-console': 0, // this rule deprecates console.log, console.warn etc. because 'it is not good in production code'
@ -41,10 +38,10 @@ module.exports = {
'import/prefer-default-export': 0, // works incorrect with interfaces
'newline-per-chained-call': 0, // makes code uglier
},
'settings': {
settings: {
'import/resolver': {
'node': {
'extensions': ['.ts', '.js', '.json'],
node: {
extensions: ['.ts', '.js', '.json'],
},
},
},

@ -1,18 +1,21 @@
# Module CVAT-CANVAS
## Description
The CVAT module written in TypeScript language.
It presents a canvas to viewing, drawing and editing of annotations.
## Versioning
If you make changes in this package, please do following:
- After not important changes (typos, backward compatible bug fixes, refactoring) do: ``npm version patch``
- After changing API (backward compatible new features) do: ``npm version minor``
- After changing API (changes that break backward compatibility) do: ``npm version major``
- After not important changes (typos, backward compatible bug fixes, refactoring) do: `npm version patch`
- After changing API (backward compatible new features) do: `npm version minor`
- After changing API (changes that break backward compatibility) do: `npm version major`
## Commands
- Building of the module from sources in the ```dist``` directory:
- Building of the module from sources in the `dist` directory:
```bash
npm run build
@ -22,6 +25,7 @@ npm run build -- --mode=development # without a minification
## Using
Canvas itself handles:
- Shape context menu (PKM)
- Image moving (mousedrag)
- Image resizing (mousewheel)
@ -136,22 +140,23 @@ Canvas itself handles:
### API CSS
- All drawn objects (shapes, tracks) have an id ```cvat_canvas_shape_{objectState.clientID}```
- Drawn shapes and tracks have classes ```cvat_canvas_shape```,
```cvat_canvas_shape_activated```,
```cvat_canvas_shape_grouping```,
```cvat_canvas_shape_merging```,
```cvat_canvas_shape_drawing```,
```cvat_canvas_shape_occluded```
- Drawn texts have the class ```cvat_canvas_text```
- Tags have the class ```cvat_canvas_tag```
- Canvas image has ID ```cvat_canvas_image```
- Grid on the canvas has ID ```cvat_canvas_grid``` and ```cvat_canvas_grid_pattern```
- Crosshair during a draw has class ```cvat_canvas_crosshair```
- All drawn objects (shapes, tracks) have an id `cvat_canvas_shape_{objectState.clientID}`
- Drawn shapes and tracks have classes `cvat_canvas_shape`,
`cvat_canvas_shape_activated`,
`cvat_canvas_shape_grouping`,
`cvat_canvas_shape_merging`,
`cvat_canvas_shape_drawing`,
`cvat_canvas_shape_occluded`
- Drawn texts have the class `cvat_canvas_text`
- Tags have the class `cvat_canvas_tag`
- Canvas image has ID `cvat_canvas_image`
- Grid on the canvas has ID `cvat_canvas_grid` and `cvat_canvas_grid_pattern`
- Crosshair during a draw has class `cvat_canvas_crosshair`
### Events
Standard JS events are used.
```js
- canvas.setup
- canvas.activated => {state: ObjectState}
@ -178,31 +183,32 @@ Standard JS events are used.
```
### WEB
```js
// Create an instance of a canvas
const canvas = new window.canvas.Canvas();
console.log('Version ', window.canvas.CanvasVersion);
console.log('Current mode is ', window.canvas.mode());
// Put canvas to a html container
htmlContainer.appendChild(canvas.html());
canvas.fitCanvas();
// Next you can use its API methods. For example:
canvas.rotate(270);
canvas.draw({
enabled: true,
shapeType: 'rectangle',
crosshair: true,
rectDrawingMethod: window.Canvas.RectDrawingMethod.CLASSIC,
});
// Create an instance of a canvas
const canvas = new window.canvas.Canvas();
console.log('Version ', window.canvas.CanvasVersion);
console.log('Current mode is ', window.canvas.mode());
// Put canvas to a html container
htmlContainer.appendChild(canvas.html());
canvas.fitCanvas();
// Next you can use its API methods. For example:
canvas.rotate(270);
canvas.draw({
enabled: true,
shapeType: 'rectangle',
crosshair: true,
rectDrawingMethod: window.Canvas.RectDrawingMethod.CLASSIC,
});
```
## API Reaction
| | IDLE | GROUP | SPLIT | DRAW | MERGE | EDIT | DRAG | RESIZE | ZOOM_CANVAS | DRAG_CANVAS | INTERACT |
|--------------|------|-------|-------|------|-------|------|------|--------|-------------|-------------|----------|
| ------------ | ---- | ----- | ----- | ---- | ----- | ---- | ---- | ------ | ----------- | ----------- | -------- |
| setup() | + | + | + | +/- | + | +/- | +/- | +/- | + | + | + |
| activate() | + | - | - | - | - | - | - | - | - | - | - |
| rotate() | + | + | + | + | + | + | + | + | + | + | + |

@ -1,8 +1,7 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/* eslint-disable */
module.exports = {
parser: false,
plugins: {

@ -41,7 +41,7 @@ polyline.cvat_shape_drawing_opacity {
font-size: 1.2em;
fill: white;
cursor: default;
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif;
font-family: Calibri, Candara, Segoe, 'Segoe UI', Optima, Arial, sans-serif;
text-shadow: 0 0 4px black;
user-select: none;
pointer-events: none;

@ -27,10 +27,16 @@ export class AutoborderHandlerImpl implements AutoborderHandler {
private groups: SVGGElement[];
private auxiliaryGroupID: number | null;
private auxiliaryClicks: number[];
private listeners: Record<number, Record<number, {
click: (event: MouseEvent) => void;
dblclick: (event: MouseEvent) => void;
}>>;
private listeners: Record<
number,
Record<
number,
{
click: (event: MouseEvent) => void;
dblclick: (event: MouseEvent) => void;
}
>
>;
public constructor(frameContent: SVGSVGElement) {
this.frameContent = frameContent;
@ -47,12 +53,11 @@ export class AutoborderHandlerImpl implements AutoborderHandler {
private removeMarkers(): void {
this.groups.forEach((group: SVGGElement): void => {
const groupID = group.dataset.groupId;
Array.from(group.children)
.forEach((circle: SVGCircleElement, pointID: number): void => {
circle.removeEventListener('click', this.listeners[+groupID][pointID].click);
circle.removeEventListener('dblclick', this.listeners[+groupID][pointID].click);
circle.remove();
});
Array.from(group.children).forEach((circle: SVGCircleElement, pointID: number): void => {
circle.removeEventListener('click', this.listeners[+groupID][pointID].click);
circle.removeEventListener('dblclick', this.listeners[+groupID][pointID].click);
circle.remove();
});
group.remove();
});
@ -89,8 +94,9 @@ export class AutoborderHandlerImpl implements AutoborderHandler {
if (this.auxiliaryGroupID !== null) {
while (this.auxiliaryClicks.length > 0) {
const resetID = this.auxiliaryClicks.pop();
this.groups[this.auxiliaryGroupID]
.children[resetID].classList.remove('cvat_canvas_autoborder_point_direction');
this.groups[this.auxiliaryGroupID].children[resetID].classList.remove(
'cvat_canvas_autoborder_point_direction',
);
}
}
@ -103,125 +109,125 @@ export class AutoborderHandlerImpl implements AutoborderHandler {
private drawMarkers(transformedShapes: TransformedShape[]): void {
const svgNamespace = 'http://www.w3.org/2000/svg';
this.groups = transformedShapes
.map((shape: TransformedShape, groupID: number): SVGGElement => {
this.groups = transformedShapes.map(
(shape: TransformedShape, groupID: number): SVGGElement => {
const group = document.createElementNS(svgNamespace, 'g');
group.setAttribute('data-group-id', `${groupID}`);
this.listeners[groupID] = this.listeners[groupID] || {};
const circles = shape.points.split(/\s/).map((
point: string, pointID: number, points: string[],
): SVGCircleElement => {
const [x, y] = point.split(',');
const circle = document.createElementNS(svgNamespace, 'circle');
circle.classList.add('cvat_canvas_autoborder_point');
circle.setAttribute('fill', shape.color);
circle.setAttribute('stroke', 'black');
circle.setAttribute('stroke-width', `${consts.POINTS_STROKE_WIDTH / this.scale}`);
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', `${consts.BASE_POINT_SIZE / this.scale}`);
const click = (event: MouseEvent): void => {
event.stopPropagation();
// another shape was clicked
if (this.auxiliaryGroupID !== null
&& this.auxiliaryGroupID !== groupID
) {
this.resetAuxiliaryShape();
}
this.auxiliaryGroupID = groupID;
// up clicked group for convenience
this.frameContent.appendChild(group);
if (this.auxiliaryClicks[1] === pointID) {
// the second point was clicked twice
this.addPointToCurrentShape(+x, +y);
this.resetAuxiliaryShape();
return;
}
// the first point can not be clicked twice
// just ignore such a click if it is
if (this.auxiliaryClicks[0] !== pointID) {
this.auxiliaryClicks.push(pointID);
} else {
return;
}
// it is the first click
if (this.auxiliaryClicks.length === 1) {
const handler = this.currentShape.remember('_paintHandler');
// draw and remove initial point just to initialize data structures
if (!handler || !handler.startPoint) {
(this.currentShape as any).draw('point', event);
(this.currentShape as any).draw('undo');
const circles = shape.points.split(/\s/).map(
(point: string, pointID: number, points: string[]): SVGCircleElement => {
const [x, y] = point.split(',');
const circle = document.createElementNS(svgNamespace, 'circle');
circle.classList.add('cvat_canvas_autoborder_point');
circle.setAttribute('fill', shape.color);
circle.setAttribute('stroke', 'black');
circle.setAttribute('stroke-width', `${consts.POINTS_STROKE_WIDTH / this.scale}`);
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', `${consts.BASE_POINT_SIZE / this.scale}`);
const click = (event: MouseEvent): void => {
event.stopPropagation();
// another shape was clicked
if (this.auxiliaryGroupID !== null && this.auxiliaryGroupID !== groupID) {
this.resetAuxiliaryShape();
}
this.addPointToCurrentShape(+x, +y);
// is is the second click
} else if (this.auxiliaryClicks.length === 2) {
circle.classList.add('cvat_canvas_autoborder_point_direction');
// it is the third click
} else {
// sign defines bypass direction
const landmarks = this.auxiliaryClicks;
const sign = Math.sign(landmarks[2] - landmarks[0])
* Math.sign(landmarks[1] - landmarks[0])
* Math.sign(landmarks[2] - landmarks[1]);
// go via a polygon and get vertexes
// the first vertex has been already drawn
const way = [];
for (let i = landmarks[0] + sign; ; i += sign) {
if (i < 0) {
i = points.length - 1;
} else if (i === points.length) {
i = 0;
}
this.auxiliaryGroupID = groupID;
// up clicked group for convenience
this.frameContent.appendChild(group);
way.push(points[i]);
if (i === this.auxiliaryClicks[this.auxiliaryClicks.length - 1]) {
// put the last element twice
// specific of svg.draw.js
// way.push(points[i]);
break;
}
if (this.auxiliaryClicks[1] === pointID) {
// the second point was clicked twice
this.addPointToCurrentShape(+x, +y);
this.resetAuxiliaryShape();
return;
}
// remove the latest cursor position from drawing array
for (const wayPoint of way) {
const [_x, _y] = wayPoint.split(',')
.map((coordinate: string): number => +coordinate);
this.addPointToCurrentShape(_x, _y);
// the first point can not be clicked twice
// just ignore such a click if it is
if (this.auxiliaryClicks[0] !== pointID) {
this.auxiliaryClicks.push(pointID);
} else {
return;
}
this.resetAuxiliaryShape();
}
};
// it is the first click
if (this.auxiliaryClicks.length === 1) {
const handler = this.currentShape.remember('_paintHandler');
// draw and remove initial point just to initialize data structures
if (!handler || !handler.startPoint) {
(this.currentShape as any).draw('point', event);
(this.currentShape as any).draw('undo');
}
this.addPointToCurrentShape(+x, +y);
// is is the second click
} else if (this.auxiliaryClicks.length === 2) {
circle.classList.add('cvat_canvas_autoborder_point_direction');
// it is the third click
} else {
// sign defines bypass direction
const landmarks = this.auxiliaryClicks;
const sign =
Math.sign(landmarks[2] - landmarks[0]) *
Math.sign(landmarks[1] - landmarks[0]) *
Math.sign(landmarks[2] - landmarks[1]);
// go via a polygon and get vertexes
// the first vertex has been already drawn
const way = [];
for (let i = landmarks[0] + sign; ; i += sign) {
if (i < 0) {
i = points.length - 1;
} else if (i === points.length) {
i = 0;
}
way.push(points[i]);
if (i === this.auxiliaryClicks[this.auxiliaryClicks.length - 1]) {
// put the last element twice
// specific of svg.draw.js
// way.push(points[i]);
break;
}
}
const dblclick = (event: MouseEvent): void => {
event.stopPropagation();
};
// remove the latest cursor position from drawing array
for (const wayPoint of way) {
const [_x, _y] = wayPoint
.split(',')
.map((coordinate: string): number => +coordinate);
this.addPointToCurrentShape(_x, _y);
}
this.listeners[groupID][pointID] = {
click,
dblclick,
};
this.resetAuxiliaryShape();
}
};
circle.addEventListener('mousedown', this.listeners[groupID][pointID].click);
circle.addEventListener('dblclick', this.listeners[groupID][pointID].click);
return circle;
});
const dblclick = (event: MouseEvent): void => {
event.stopPropagation();
};
this.listeners[groupID][pointID] = {
click,
dblclick,
};
circle.addEventListener('mousedown', this.listeners[groupID][pointID].click);
circle.addEventListener('dblclick', this.listeners[groupID][pointID].click);
return circle;
},
);
group.append(...circles);
return group;
});
},
);
this.frameContent.append(...this.groups);
}
@ -231,55 +237,54 @@ export class AutoborderHandlerImpl implements AutoborderHandler {
this.removeMarkers();
const currentClientID = this.currentShape.node.dataset.originClientId;
const shapes = Array.from(this.frameContent.getElementsByClassName('cvat_canvas_shape'))
.filter((shape: HTMLElement): boolean => +shape.getAttribute('clientID') !== this.currentID);
const transformedShapes = shapes.map((shape: HTMLElement): TransformedShape | null => {
const color = shape.getAttribute('fill');
const clientID = shape.getAttribute('clientID');
if (color === null || clientID === null) return null;
if (+clientID === +currentClientID) {
return null;
}
let points = '';
if (shape.tagName === 'polyline' || shape.tagName === 'polygon') {
points = shape.getAttribute('points');
} else if (shape.tagName === 'rect') {
const x = +shape.getAttribute('x');
const y = +shape.getAttribute('y');
const width = +shape.getAttribute('width');
const height = +shape.getAttribute('height');
if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(x) || Number.isNaN(x)) {
const shapes = Array.from(this.frameContent.getElementsByClassName('cvat_canvas_shape')).filter(
(shape: HTMLElement): boolean => +shape.getAttribute('clientID') !== this.currentID,
);
const transformedShapes = shapes
.map((shape: HTMLElement): TransformedShape | null => {
const color = shape.getAttribute('fill');
const clientID = shape.getAttribute('clientID');
if (color === null || clientID === null) return null;
if (+clientID === +currentClientID) {
return null;
}
points = `${x},${y} ${x + width},${y} ${x + width},${y + height} ${x},${y + height}`;
} else if (shape.tagName === 'g') {
const polylineID = shape.dataset.polylineId;
const polyline = this.frameContent.getElementById(polylineID);
if (polyline && polyline.getAttribute('points')) {
points = polyline.getAttribute('points');
} else {
return null;
let points = '';
if (shape.tagName === 'polyline' || shape.tagName === 'polygon') {
points = shape.getAttribute('points');
} else if (shape.tagName === 'rect') {
const x = +shape.getAttribute('x');
const y = +shape.getAttribute('y');
const width = +shape.getAttribute('width');
const height = +shape.getAttribute('height');
if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(x) || Number.isNaN(x)) {
return null;
}
points = `${x},${y} ${x + width},${y} ${x + width},${y + height} ${x},${y + height}`;
} else if (shape.tagName === 'g') {
const polylineID = shape.dataset.polylineId;
const polyline = this.frameContent.getElementById(polylineID);
if (polyline && polyline.getAttribute('points')) {
points = polyline.getAttribute('points');
} else {
return null;
}
}
}
return {
color,
points: points.trim(),
};
}).filter((state: TransformedShape | null): boolean => state !== null);
return {
color,
points: points.trim(),
};
})
.filter((state: TransformedShape | null): boolean => state !== null);
this.drawMarkers(transformedShapes);
}
public autoborder(
enabled: boolean,
currentShape?: SVG.Shape,
currentID?: number,
): void {
public autoborder(enabled: boolean, currentShape?: SVG.Shape, currentID?: number): void {
if (enabled && !this.enabled && currentShape) {
this.enabled = true;
this.currentShape = currentShape;

@ -72,10 +72,7 @@ class CanvasImpl implements Canvas {
}
public fitCanvas(): void {
this.model.fitCanvas(
this.view.html().clientWidth,
this.view.html().clientHeight,
);
this.model.fitCanvas(this.view.html().clientWidth, this.view.html().clientHeight);
}
public bitmap(enable: boolean): void {

@ -43,7 +43,7 @@ export interface ActiveElement {
export enum RectDrawingMethod {
CLASSIC = 'By 2 points',
EXTREME_POINTS = 'By 4 points'
EXTREME_POINTS = 'By 4 points',
}
export enum CuboidDrawingMethod {
@ -280,23 +280,23 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
public zoom(x: number, y: number, direction: number): void {
const oldScale: number = this.data.scale;
const newScale: number = direction > 0 ? oldScale * 6 / 5 : oldScale * 5 / 6;
const newScale: number = direction > 0 ? (oldScale * 6) / 5 : (oldScale * 5) / 6;
this.data.scale = Math.min(Math.max(newScale, FrameZoom.MIN), FrameZoom.MAX);
const { angle } = this.data;
const mutiplier = Math.sin(angle * Math.PI / 180) + Math.cos(angle * Math.PI / 180);
const mutiplier = Math.sin((angle * Math.PI) / 180) + Math.cos((angle * Math.PI) / 180);
if ((angle / 90) % 2) {
// 90, 270, ..
this.data.top += mutiplier * ((x - this.data.imageSize.width / 2)
* (oldScale / this.data.scale - 1)) * this.data.scale;
this.data.left -= mutiplier * ((y - this.data.imageSize.height / 2)
* (oldScale / this.data.scale - 1)) * this.data.scale;
this.data.top +=
mutiplier * ((x - this.data.imageSize.width / 2) * (oldScale / this.data.scale - 1)) * this.data.scale;
this.data.left -=
mutiplier * ((y - this.data.imageSize.height / 2) * (oldScale / this.data.scale - 1)) * this.data.scale;
} else {
this.data.left += mutiplier * ((x - this.data.imageSize.width / 2)
* (oldScale / this.data.scale - 1)) * this.data.scale;
this.data.top += mutiplier * ((y - this.data.imageSize.height / 2)
* (oldScale / this.data.scale - 1)) * this.data.scale;
this.data.left +=
mutiplier * ((x - this.data.imageSize.width / 2) * (oldScale / this.data.scale - 1)) * this.data.scale;
this.data.top +=
mutiplier * ((y - this.data.imageSize.height / 2) * (oldScale / this.data.scale - 1)) * this.data.scale;
}
this.notify(UpdateReasons.IMAGE_ZOOMED);
@ -312,10 +312,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.data.canvasSize.height = height;
this.data.canvasSize.width = width;
this.data.imageOffset = Math.floor(Math.max(
this.data.canvasSize.height / FrameZoom.MIN,
this.data.canvasSize.width / FrameZoom.MIN,
));
this.data.imageOffset = Math.floor(
Math.max(this.data.canvasSize.height / FrameZoom.MIN, this.data.canvasSize.width / FrameZoom.MIN),
);
this.notify(UpdateReasons.FITTED_CANVAS);
this.notify(UpdateReasons.OBJECTS_UPDATED);
@ -367,36 +366,35 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}
this.data.imageID = frameData.number;
frameData.data(
(): void => {
frameData
.data((): void => {
this.data.image = null;
this.notify(UpdateReasons.IMAGE_CHANGED);
},
).then((data: Image): void => {
if (frameData.number !== this.data.imageID) {
// already another image
return;
}
})
.then((data: Image): void => {
if (frameData.number !== this.data.imageID) {
// already another image
return;
}
this.data.imageSize = {
height: (frameData.height as number),
width: (frameData.width as number),
};
this.data.imageSize = {
height: frameData.height as number,
width: frameData.width as number,
};
this.data.image = data;
this.notify(UpdateReasons.IMAGE_CHANGED);
this.data.zLayer = zLayer;
this.data.objects = objectStates;
this.notify(UpdateReasons.OBJECTS_UPDATED);
}).catch((exception: any): void => {
throw exception;
});
this.data.image = data;
this.notify(UpdateReasons.IMAGE_CHANGED);
this.data.zLayer = zLayer;
this.data.objects = objectStates;
this.notify(UpdateReasons.OBJECTS_UPDATED);
})
.catch((exception: any): void => {
throw exception;
});
}
public activate(clientID: number | null, attributeID: number | null): void {
if (this.data.activeElement.clientID === clientID
&& this.data.activeElement.attributeID === attributeID
) {
if (this.data.activeElement.clientID === clientID && this.data.activeElement.attributeID === attributeID) {
return;
}
@ -404,9 +402,8 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
throw Error(`Canvas is busy. Action: ${this.data.mode}`);
}
if (typeof (clientID) === 'number') {
const [state] = this.objects
.filter((_state: any): boolean => _state.clientID === clientID);
if (typeof clientID === 'number') {
const [state] = this.objects.filter((_state: any): boolean => _state.clientID === clientID);
if (!state || state.objectType === 'tag') {
return;
}
@ -422,7 +419,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
public rotate(rotationAngle: number): void {
if (this.data.angle !== rotationAngle) {
this.data.angle = (360 + Math.floor((rotationAngle) / 90) * 90) % 360;
this.data.angle = (360 + Math.floor(rotationAngle / 90) * 90) % 360;
this.fit();
}
}
@ -452,13 +449,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
);
}
this.data.scale = Math.min(
Math.max(this.data.scale, FrameZoom.MIN),
FrameZoom.MAX,
);
this.data.scale = Math.min(Math.max(this.data.scale, FrameZoom.MIN), FrameZoom.MAX);
this.data.top = (this.data.canvasSize.height / 2 - this.data.imageSize.height / 2);
this.data.left = (this.data.canvasSize.width / 2 - this.data.imageSize.width / 2);
this.data.top = this.data.canvasSize.height / 2 - this.data.imageSize.height / 2;
this.data.left = this.data.canvasSize.width / 2 - this.data.imageSize.width / 2;
this.notify(UpdateReasons.IMAGE_FITTED);
}
@ -482,7 +476,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
throw new Error('Drawing has been already started');
} else if (!drawData.shapeType && !drawData.initialState) {
throw new Error('A shape type is not specified');
} else if (typeof (drawData.numberOfPoints) !== 'undefined') {
} else if (typeof drawData.numberOfPoints !== 'undefined') {
if (drawData.shapeType === 'polygon' && drawData.numberOfPoints < 3) {
throw new Error('A polygon consists of at least 3 points');
} else if (drawData.shapeType === 'polyline' && drawData.numberOfPoints < 2) {
@ -491,10 +485,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}
}
if (typeof (drawData.redraw) === 'number') {
if (typeof drawData.redraw === 'number') {
const clientID = drawData.redraw;
const [state] = this.data.objects
.filter((_state: any): boolean => _state.clientID === clientID);
const [state] = this.data.objects.filter((_state: any): boolean => _state.clientID === clientID);
if (state) {
this.data.drawData = { ...drawData };
@ -526,7 +519,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}
this.data.interactionData = interactionData;
if (typeof (this.data.interactionData.crosshair) !== 'boolean') {
if (typeof this.data.interactionData.crosshair !== 'boolean') {
this.data.interactionData.crosshair = true;
}
@ -591,18 +584,18 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}
public configure(configuration: Configuration): void {
if (typeof (configuration.displayAllText) !== 'undefined') {
if (typeof configuration.displayAllText !== 'undefined') {
this.data.configuration.displayAllText = configuration.displayAllText;
}
if (typeof (configuration.showProjections) !== 'undefined') {
if (typeof configuration.showProjections !== 'undefined') {
this.data.configuration.showProjections = configuration.showProjections;
}
if (typeof (configuration.autoborders) !== 'undefined') {
if (typeof configuration.autoborders !== 'undefined') {
this.data.configuration.autoborders = configuration.autoborders;
}
if (typeof (configuration.undefinedAttrValue) !== 'undefined') {
if (typeof configuration.undefinedAttrValue !== 'undefined') {
this.data.configuration.undefinedAttrValue = configuration.undefinedAttrValue;
}
@ -610,8 +603,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}
public isAbleToChangeFrame(): boolean {
const isUnable = [Mode.DRAG, Mode.EDIT, Mode.RESIZE, Mode.INTERACT].includes(this.data.mode)
|| (this.data.mode === Mode.DRAW && typeof (this.data.drawData.redraw) === 'number');
const isUnable =
[Mode.DRAG, Mode.EDIT, Mode.RESIZE, Mode.INTERACT].includes(this.data.mode) ||
(this.data.mode === Mode.DRAW && typeof this.data.drawData.redraw === 'number');
return !isUnable;
}
@ -647,10 +641,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.data.imageOffset = geometry.offset;
this.data.scale = geometry.scale;
this.data.imageOffset = Math.floor(Math.max(
this.data.canvasSize.height / FrameZoom.MIN,
this.data.canvasSize.width / FrameZoom.MIN,
));
this.data.imageOffset = Math.floor(
Math.max(this.data.canvasSize.height / FrameZoom.MIN, this.data.canvasSize.width / FrameZoom.MIN),
);
}
public get zLayer(): number | null {
@ -667,8 +660,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
public get objects(): any[] {
if (this.data.zLayer !== null) {
return this.data.objects
.filter((object: any): boolean => object.zOrder <= this.data.zLayer);
return this.data.objects.filter((object: any): boolean => object.zOrder <= this.data.zLayer);
}
return this.data.objects;

File diff suppressed because it is too large Load Diff

@ -14,8 +14,9 @@ const MIN_EDGE_LENGTH = 3;
const CUBOID_ACTIVE_EDGE_STROKE_WIDTH = 2.5;
const CUBOID_UNACTIVE_EDGE_STROKE_WIDTH = 1.75;
const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__';
const ARROW_PATH = 'M13.162 6.284L.682.524a.483.483 0 0 0-.574.134.477.477 0 '
+ '0 0-.012.59L4.2 6.72.096 12.192a.479.479 0 0 0 .585.724l12.48-5.76a.48.48 0 0 0 0-.872z';
const ARROW_PATH =
'M13.162 6.284L.682.524a.483.483 0 0 0-.574.134.477.477 0 ' +
'0 0-.012.59L4.2 6.72.096 12.192a.479.479 0 0 0 .585.724l12.48-5.76a.48.48 0 0 0 0-.872z';
export default {
BASE_STROKE_WIDTH,

@ -25,13 +25,19 @@ export default class Crosshair {
}
this.canvas = canvas;
this.x = this.canvas.line(0, y, this.canvas.node.clientWidth, y).attr({
'stroke-width': consts.BASE_STROKE_WIDTH / (2 * scale),
}).addClass('cvat_canvas_crosshair');
this.x = this.canvas
.line(0, y, this.canvas.node.clientWidth, y)
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / (2 * scale),
})
.addClass('cvat_canvas_crosshair');
this.y = this.canvas.line(x, 0, x, this.canvas.node.clientHeight).attr({
'stroke-width': consts.BASE_STROKE_WIDTH / (2 * scale),
}).addClass('cvat_canvas_crosshair');
this.y = this.canvas
.line(x, 0, x, this.canvas.node.clientHeight)
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / (2 * scale),
})
.addClass('cvat_canvas_crosshair');
}
public hide(): void {

@ -1,12 +1,3 @@
/* eslint-disable func-names */
/* eslint-disable no-underscore-dangle */
/* eslint-disable curly */
/*
* Copyright (C) 2020 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
import consts from './consts';
export interface Point {
@ -26,9 +17,7 @@ function line(p1: Point, p2: Point): number[] {
return [a, b, c];
}
function intersection(
p1: Point, p2: Point, p3: Point, p4: Point,
): Point | null {
function intersection(p1: Point, p2: Point, p3: Point, p4: Point): Point | null {
const L1 = line(p1, p2);
const L2 = line(p3, p4);
@ -246,8 +235,20 @@ export class CuboidModel {
this.rb = new Edge([3, 5], this.points);
this.db = new Edge([7, 5], this.points);
this.edgeList = [this.fl, this.fr, this.dl, this.dr, this.ft, this.lt,
this.rt, this.dt, this.fb, this.lb, this.rb, this.db];
this.edgeList = [
this.fl,
this.fr,
this.dl,
this.dr,
this.ft,
this.lt,
this.rt,
this.dt,
this.fb,
this.lb,
this.rb,
this.db,
];
}
private initFaces(): void {
@ -324,7 +325,7 @@ function sortPointsClockwise(points: any[]): any[] {
let ang = Math.atan2(point.y - center.y, point.x - center.x);
if (!startAng) {
startAng = ang;
// ensure that all points are clockwise of the start point
// ensure that all points are clockwise of the start point
} else if (ang < startAng) {
ang += Math.PI * 2;
}
@ -347,18 +348,18 @@ function setupCuboidPoints(points: Point[]): any[] {
let p3;
let p4;
const height = Math.abs(points[0].x - points[1].x)
< Math.abs(points[1].x - points[2].x)
? Math.abs(points[1].y - points[0].y)
: Math.abs(points[1].y - points[2].y);
const height =
Math.abs(points[0].x - points[1].x) < Math.abs(points[1].x - points[2].x)
? Math.abs(points[1].y - points[0].y)
: Math.abs(points[1].y - points[2].y);
// seperate into left and right point
// we pick the first and third point because we know assume they will be on
// opposite corners
if (points[0].x < points[2].x) {
[left,, right] = points;
[left, , right] = points;
} else {
[right,, left] = points;
[right, , left] = points;
}
// get other 2 points using the given height
@ -408,7 +409,7 @@ export function cuboidFrom4Points(flattenedPoints: any[]): any[] {
points.push({ x, y });
}
const unsortedPlanePoints = points.slice(0, 3);
function rotate(array: any[], times: number): void{
function rotate(array: any[], times: number): void {
let t = times;
while (t--) {
const temp = array.shift();
@ -460,28 +461,21 @@ export function cuboidFrom4Points(flattenedPoints: any[]): any[] {
plane2.p3 = { x: plane1.p3.x + vec.x, y: plane1.p3.y + vec.y };
plane2.p4 = { x: plane1.p4.x + vec.x, y: plane1.p4.y + vec.y };
let cuboidPoints;
// right
if (Math.abs(angle) < Math.PI / 2 - 0.1) {
cuboidPoints = setupCuboidPoints(points);
// left
// left
} else if (Math.abs(angle) > Math.PI / 2 + 0.1) {
cuboidPoints = setupCuboidPoints(points);
// down
// down
} else if (angle > 0) {
cuboidPoints = [
plane1.p1, plane2.p1, plane1.p2, plane2.p2,
plane1.p3, plane2.p3, plane1.p4, plane2.p4,
];
cuboidPoints = [plane1.p1, plane2.p1, plane1.p2, plane2.p2, plane1.p3, plane2.p3, plane1.p4, plane2.p4];
cuboidPoints[0].y += 0.1;
cuboidPoints[4].y += 0.1;
// up
// up
} else {
cuboidPoints = [
plane2.p1, plane1.p1, plane2.p2, plane1.p2,
plane2.p3, plane1.p3, plane2.p4, plane1.p4,
];
cuboidPoints = [plane2.p1, plane1.p1, plane2.p2, plane1.p2, plane2.p3, plane1.p3, plane2.p4, plane1.p4];
cuboidPoints[0].y += 0.1;
cuboidPoints[4].y += 0.1;
}

@ -18,13 +18,7 @@ import {
} from './shared';
import Crosshair from './crosshair';
import consts from './consts';
import {
DrawData,
Geometry,
RectDrawingMethod,
Configuration,
CuboidDrawingMethod,
} from './canvasModel';
import { DrawData, Geometry, RectDrawingMethod, Configuration, CuboidDrawingMethod } from './canvasModel';
import { cuboidFrom4Points } from './cuboid';
@ -64,8 +58,9 @@ export class DrawHandlerImpl implements DrawHandler {
const frameHeight = this.geometry.image.height;
const { offset } = this.geometry;
let [xtl, ytl, xbr, ybr] = [bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height]
.map((coord: number): number => coord - offset);
let [xtl, ytl, xbr, ybr] = [bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height].map(
(coord: number): number => coord - offset,
);
xtl = Math.min(Math.max(xtl, 0), frameWidth);
xbr = Math.min(Math.max(xbr, 0), frameWidth);
@ -75,7 +70,9 @@ export class DrawHandlerImpl implements DrawHandler {
return [xtl, ytl, xbr, ybr];
}
private getFinalPolyshapeCoordinates(targetPoints: number[]): {
private getFinalPolyshapeCoordinates(
targetPoints: number[],
): {
points: number[];
box: Box;
} {
@ -106,7 +103,9 @@ export class DrawHandlerImpl implements DrawHandler {
};
}
private getFinalCuboidCoordinates(targetPoints: number[]): {
private getFinalCuboidCoordinates(
targetPoints: number[],
): {
points: number[];
box: Box;
} {
@ -133,8 +132,7 @@ export class DrawHandlerImpl implements DrawHandler {
for (let i = 0; i < points.length - 1; i += 2) {
const [x, y] = points.slice(i);
if (x >= offset && x <= offset + frameWidth
&& y >= offset && y <= offset + frameHeight) continue;
if (x >= offset && x <= offset + frameWidth && y >= offset && y <= offset + frameHeight) continue;
let xOffset = 0;
let yOffset = 0;
@ -156,9 +154,8 @@ export class DrawHandlerImpl implements DrawHandler {
if (cuboidOffsets.length === points.length / 2) {
cuboidOffsets.forEach((offsetCoords: number[]): void => {
if (Math.sqrt((offsetCoords[0] ** 2) + (offsetCoords[1] ** 2))
< minCuboidOffset.d) {
minCuboidOffset.d = Math.sqrt((offsetCoords[0] ** 2) + (offsetCoords[1] ** 2));
if (Math.sqrt(offsetCoords[0] ** 2 + offsetCoords[1] ** 2) < minCuboidOffset.d) {
minCuboidOffset.d = Math.sqrt(offsetCoords[0] ** 2 + offsetCoords[1] ** 2);
[minCuboidOffset.dx, minCuboidOffset.dy] = offsetCoords;
}
});
@ -215,8 +212,10 @@ export class DrawHandlerImpl implements DrawHandler {
// Or when no drawn points, but we call cancel() drawing
// We check if it is activated with remember function
if (this.drawInstance.remember('_paintHandler')) {
if (this.drawData.shapeType !== 'rectangle'
&& this.drawData.cuboidDrawingMethod !== CuboidDrawingMethod.CLASSIC) {
if (
this.drawData.shapeType !== 'rectangle' &&
this.drawData.cuboidDrawingMethod !== CuboidDrawingMethod.CLASSIC
) {
// Check for unsaved drawn shapes
this.drawInstance.draw('done');
}
@ -248,37 +247,48 @@ export class DrawHandlerImpl implements DrawHandler {
private drawBox(): void {
this.drawInstance = this.canvas.rect();
this.drawInstance.on('drawstop', (e: Event): void => {
const bbox = (e.target as SVGRectElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType, redraw: clientID } = this.drawData;
this.release();
this.drawInstance
.on('drawstop', (e: Event): void => {
const bbox = (e.target as SVGRectElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType, redraw: clientID } = this.drawData;
this.release();
if (this.canceled) return;
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone({
clientID,
shapeType,
points: [xtl, ytl, xbr, ybr],
}, Date.now() - this.startTimestamp);
}
}).on('drawupdate', (): void => {
this.shapeSizeElement.update(this.drawInstance);
}).addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
if (this.canceled) return;
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone(
{
clientID,
shapeType,
points: [xtl, ytl, xbr, ybr],
},
Date.now() - this.startTimestamp,
);
}
})
.on('drawupdate', (): void => {
this.shapeSizeElement.update(this.drawInstance);
})
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
}
private drawBoxBy4Points(): void {
let numberOfPoints = 0;
this.drawInstance = (this.canvas as any).polygon()
.addClass('cvat_canvas_shape_drawing').attr({
this.drawInstance = (this.canvas as any)
.polygon()
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': 0,
opacity: 0,
}).on('drawstart', (): void => {
})
.on('drawstart', (): void => {
// init numberOfPoints as one on drawstart
numberOfPoints = 1;
}).on('drawpoint', (e: CustomEvent): void => {
})
.on('drawpoint', (e: CustomEvent): void => {
// increase numberOfPoints by one on drawpoint
numberOfPoints += 1;
@ -290,14 +300,18 @@ export class DrawHandlerImpl implements DrawHandler {
this.cancel();
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone({
shapeType,
clientID,
points: [xtl, ytl, xbr, ybr],
}, Date.now() - this.startTimestamp);
this.onDrawDone(
{
shapeType,
clientID,
points: [xtl, ytl, xbr, ybr],
},
Date.now() - this.startTimestamp,
);
}
}
}).on('undopoint', (): void => {
})
.on('undopoint', (): void => {
if (numberOfPoints > 0) {
numberOfPoints -= 1;
}
@ -351,10 +365,7 @@ export class DrawHandlerImpl implements DrawHandler {
} else {
this.drawInstance.draw('update', e);
const deltaTreshold = 15;
const delta = Math.sqrt(
((e.clientX - lastDrawnPoint.x) ** 2)
+ ((e.clientY - lastDrawnPoint.y) ** 2),
);
const delta = Math.sqrt((e.clientX - lastDrawnPoint.x) ** 2 + (e.clientY - lastDrawnPoint.y) ** 2);
if (delta > deltaTreshold) {
this.drawInstance.draw('point', e);
}
@ -375,50 +386,67 @@ export class DrawHandlerImpl implements DrawHandler {
this.drawInstance.on('drawdone', (e: CustomEvent): void => {
const targetPoints = pointsToNumberArray((e.target as SVGElement).getAttribute('points'));
const { shapeType, redraw: clientID } = this.drawData;
const { points, box } = shapeType === 'cuboid' ? this.getFinalCuboidCoordinates(targetPoints)
: this.getFinalPolyshapeCoordinates(targetPoints);
const { points, box } =
shapeType === 'cuboid'
? this.getFinalCuboidCoordinates(targetPoints)
: this.getFinalPolyshapeCoordinates(targetPoints);
this.release();
if (this.canceled) return;
if (shapeType === 'polygon'
&& ((box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD)
&& points.length >= 3 * 2) {
this.onDrawDone({
clientID,
shapeType,
points,
}, Date.now() - this.startTimestamp);
} else if (shapeType === 'polyline'
&& ((box.xbr - box.xtl) >= consts.SIZE_THRESHOLD
|| (box.ybr - box.ytl) >= consts.SIZE_THRESHOLD)
&& points.length >= 2 * 2) {
this.onDrawDone({
clientID,
shapeType,
points,
}, Date.now() - this.startTimestamp);
} else if (shapeType === 'points'
&& (e.target as any).getAttribute('points') !== '0,0') {
this.onDrawDone({
clientID,
shapeType,
points,
}, Date.now() - this.startTimestamp);
if (
shapeType === 'polygon' &&
(box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD &&
points.length >= 3 * 2
) {
this.onDrawDone(
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
} else if (
shapeType === 'polyline' &&
(box.xbr - box.xtl >= consts.SIZE_THRESHOLD || box.ybr - box.ytl >= consts.SIZE_THRESHOLD) &&
points.length >= 2 * 2
) {
this.onDrawDone(
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
} else if (shapeType === 'points' && (e.target as any).getAttribute('points') !== '0,0') {
this.onDrawDone(
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
// TODO: think about correct constraign for cuboids
} else if (shapeType === 'cuboid'
&& points.length === 4 * 2) {
this.onDrawDone({
clientID,
shapeType,
points: cuboidFrom4Points(points),
}, Date.now() - this.startTimestamp);
} else if (shapeType === 'cuboid' && points.length === 4 * 2) {
this.onDrawDone(
{
clientID,
shapeType,
points: cuboidFrom4Points(points),
},
Date.now() - this.startTimestamp,
);
}
});
}
private drawPolygon(): void {
this.drawInstance = (this.canvas as any).polygon()
.addClass('cvat_canvas_shape_drawing').attr({
this.drawInstance = (this.canvas as any)
.polygon()
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
@ -429,8 +457,10 @@ export class DrawHandlerImpl implements DrawHandler {
}
private drawPolyline(): void {
this.drawInstance = (this.canvas as any).polyline()
.addClass('cvat_canvas_shape_drawing').attr({
this.drawInstance = (this.canvas as any)
.polyline()
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
'fill-opacity': 0,
});
@ -442,18 +472,19 @@ export class DrawHandlerImpl implements DrawHandler {
}
private drawPoints(): void {
this.drawInstance = (this.canvas as any).polygon()
.addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': 0,
opacity: 0,
});
this.drawInstance = (this.canvas as any).polygon().addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': 0,
opacity: 0,
});
this.drawPolyshape();
}
private drawCuboidBy4Points(): void {
this.drawInstance = (this.canvas as any).polyline()
.addClass('cvat_canvas_shape_drawing').attr({
this.drawInstance = (this.canvas as any)
.polyline()
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
this.drawPolyshape();
@ -461,25 +492,32 @@ export class DrawHandlerImpl implements DrawHandler {
private drawCuboid(): void {
this.drawInstance = this.canvas.rect();
this.drawInstance.on('drawstop', (e: Event): void => {
const bbox = (e.target as SVGRectElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType } = this.drawData;
this.release();
this.drawInstance
.on('drawstop', (e: Event): void => {
const bbox = (e.target as SVGRectElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType } = this.drawData;
this.release();
if (this.canceled) return;
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
const d = { x: (xbr - xtl) * 0.1, y: (ybr - ytl) * 0.1 };
this.onDrawDone({
shapeType,
points: cuboidFrom4Points([xtl, ybr, xbr, ybr, xbr, ytl, xbr + d.x, ytl - d.y]),
}, Date.now() - this.startTimestamp);
}
}).on('drawupdate', (): void => {
this.shapeSizeElement.update(this.drawInstance);
}).addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
if (this.canceled) return;
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
const d = { x: (xbr - xtl) * 0.1, y: (ybr - ytl) * 0.1 };
this.onDrawDone(
{
shapeType,
points: cuboidFrom4Points([xtl, ybr, xbr, ybr, xbr, ytl, xbr + d.x, ytl - d.y]),
},
Date.now() - this.startTimestamp,
);
}
})
.on('drawupdate', (): void => {
this.shapeSizeElement.update(this.drawInstance);
})
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
}
private pastePolyshape(): void {
@ -489,22 +527,28 @@ export class DrawHandlerImpl implements DrawHandler {
.split(/[,\s]/g)
.map((coord: string): number => +coord);
const { points } = this.drawData.initialState.shapeType === 'cuboid' ? this.getFinalCuboidCoordinates(targetPoints)
: this.getFinalPolyshapeCoordinates(targetPoints);
const { points } =
this.drawData.initialState.shapeType === 'cuboid'
? this.getFinalCuboidCoordinates(targetPoints)
: this.getFinalPolyshapeCoordinates(targetPoints);
if (!e.detail.originalEvent.ctrlKey) {
this.release();
}
this.onDrawDone({
shapeType: this.drawData.initialState.shapeType,
objectType: this.drawData.initialState.objectType,
points,
occluded: this.drawData.initialState.occluded,
attributes: { ...this.drawData.initialState.attributes },
label: this.drawData.initialState.label,
color: this.drawData.initialState.color,
}, Date.now() - this.startTimestamp, e.detail.originalEvent.ctrlKey);
this.onDrawDone(
{
shapeType: this.drawData.initialState.shapeType,
objectType: this.drawData.initialState.objectType,
points,
occluded: this.drawData.initialState.occluded,
attributes: { ...this.drawData.initialState.attributes },
label: this.drawData.initialState.label,
color: this.drawData.initialState.color,
},
Date.now() - this.startTimestamp,
e.detail.originalEvent.ctrlKey,
);
});
}
@ -525,9 +569,11 @@ export class DrawHandlerImpl implements DrawHandler {
}
private pasteBox(box: BBox): void {
this.drawInstance = (this.canvas as any).rect(box.width, box.height)
this.drawInstance = (this.canvas as any)
.rect(box.width, box.height)
.move(box.x, box.y)
.addClass('cvat_canvas_shape_drawing').attr({
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
this.pasteShape();
@ -539,22 +585,27 @@ export class DrawHandlerImpl implements DrawHandler {
this.release();
}
this.onDrawDone({
shapeType: this.drawData.initialState.shapeType,
objectType: this.drawData.initialState.objectType,
points: [xtl, ytl, xbr, ybr],
occluded: this.drawData.initialState.occluded,
attributes: { ...this.drawData.initialState.attributes },
label: this.drawData.initialState.label,
color: this.drawData.initialState.color,
}, Date.now() - this.startTimestamp, e.detail.originalEvent.ctrlKey);
this.onDrawDone(
{
shapeType: this.drawData.initialState.shapeType,
objectType: this.drawData.initialState.objectType,
points: [xtl, ytl, xbr, ybr],
occluded: this.drawData.initialState.occluded,
attributes: { ...this.drawData.initialState.attributes },
label: this.drawData.initialState.label,
color: this.drawData.initialState.color,
},
Date.now() - this.startTimestamp,
e.detail.originalEvent.ctrlKey,
);
});
}
private pastePolygon(points: string): void {
this.drawInstance = (this.canvas as any).polygon(points)
.addClass('cvat_canvas_shape_drawing').attr({
this.drawInstance = (this.canvas as any)
.polygon(points)
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
this.pasteShape();
@ -562,8 +613,10 @@ export class DrawHandlerImpl implements DrawHandler {
}
private pastePolyline(points: string): void {
this.drawInstance = (this.canvas as any).polyline(points)
.addClass('cvat_canvas_shape_drawing').attr({
this.drawInstance = (this.canvas as any)
.polyline(points)
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
this.pasteShape();
@ -571,22 +624,19 @@ export class DrawHandlerImpl implements DrawHandler {
}
private pasteCuboid(points: string): void {
this.drawInstance = (this.canvas as any).cube(points).addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
'face-stroke': 'black',
});
this.drawInstance = (this.canvas as any)
.cube(points)
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
'face-stroke': 'black',
});
this.pasteShape();
this.pastePolyshape();
}
private pastePoints(initialPoints: string): void {
function moveShape(
shape: SVG.PolyLine,
group: SVG.G,
x: number,
y: number,
scale: number,
): void {
function moveShape(shape: SVG.PolyLine, group: SVG.G, x: number, y: number, scale: number): void {
const bbox = shape.bbox();
shape.move(x - bbox.width / 2, y - bbox.height / 2);
@ -601,10 +651,9 @@ export class DrawHandlerImpl implements DrawHandler {
const { x: initialX, y: initialY } = this.cursorPosition;
this.pointsGroup = this.canvas.group();
this.drawInstance = (this.canvas as any).polyline(initialPoints)
.addClass('cvat_canvas_shape_drawing').style({
'stroke-width': 0,
});
this.drawInstance = (this.canvas as any).polyline(initialPoints).addClass('cvat_canvas_shape_drawing').style({
'stroke-width': 0,
});
let numOfPoints = initialPoints.split(' ').length;
while (numOfPoints) {
@ -617,15 +666,11 @@ export class DrawHandlerImpl implements DrawHandler {
});
}
moveShape(
this.drawInstance, this.pointsGroup, initialX, initialY, this.geometry.scale,
);
moveShape(this.drawInstance, this.pointsGroup, initialX, initialY, this.geometry.scale);
this.canvas.on('mousemove.draw', (): void => {
const { x, y } = this.cursorPosition; // was computer in another callback
moveShape(
this.drawInstance, this.pointsGroup, x, y, this.geometry.scale,
);
moveShape(this.drawInstance, this.pointsGroup, x, y, this.geometry.scale);
});
this.pastePolyshape();
@ -659,8 +704,9 @@ export class DrawHandlerImpl implements DrawHandler {
if (this.drawData.initialState) {
const { offset } = this.geometry;
if (this.drawData.shapeType === 'rectangle') {
const [xtl, ytl, xbr, ybr] = this.drawData.initialState.points
.map((coord: number): number => coord + offset);
const [xtl, ytl, xbr, ybr] = this.drawData.initialState.points.map(
(coord: number): number => coord + offset,
);
this.pasteBox({
x: xtl,
@ -669,8 +715,7 @@ export class DrawHandlerImpl implements DrawHandler {
height: ybr - ytl,
});
} else {
const points = this.drawData.initialState.points
.map((coord: number): number => coord + offset);
const points = this.drawData.initialState.points.map((coord: number): number => coord + offset);
const stringifiedPoints = stringifyPoints(points);
if (this.drawData.shapeType === 'polygon') {
@ -741,10 +786,7 @@ export class DrawHandlerImpl implements DrawHandler {
};
this.canvas.on('mousemove.crosshair', (e: MouseEvent): void => {
const [x, y] = translateToSVG(
this.canvas.node as any as SVGSVGElement,
[e.clientX, e.clientY],
);
const [x, y] = translateToSVG((this.canvas.node as any) as SVGSVGElement, [e.clientX, e.clientY]);
this.cursorPosition = { x, y };
if (this.crosshair) {
this.crosshair.move(x, y);
@ -753,15 +795,11 @@ export class DrawHandlerImpl implements DrawHandler {
}
public configurate(configuration: Configuration): void {
if (typeof (configuration.autoborders) === 'boolean') {
if (typeof configuration.autoborders === 'boolean') {
this.autobordersEnabled = configuration.autoborders;
if (this.drawInstance) {
if (this.autobordersEnabled) {
this.autoborderHandler.autoborder(
true,
this.drawInstance,
this.drawData.redraw,
);
this.autoborderHandler.autoborder(true, this.drawInstance, this.drawData.redraw);
} else {
this.autoborderHandler.autoborder(false);
}
@ -798,14 +836,8 @@ export class DrawHandlerImpl implements DrawHandler {
const paintHandler = this.drawInstance.remember('_paintHandler');
for (const point of (paintHandler as any).set.members) {
point.attr(
'stroke-width',
`${consts.POINTS_STROKE_WIDTH / geometry.scale}`,
);
point.attr(
'r',
`${consts.BASE_POINT_SIZE / geometry.scale}`,
);
point.attr('stroke-width', `${consts.POINTS_STROKE_WIDTH / geometry.scale}`);
point.attr('r', `${consts.BASE_POINT_SIZE / geometry.scale}`);
}
}
}

@ -47,7 +47,8 @@ export class EditHandlerImpl implements EditHandler {
if (e.button !== 0) return;
const { offset } = this.geometry;
const stringifiedPoints = `${head} ${this.editLine.node.getAttribute('points').slice(0, -2)}`;
const points = pointsToNumberArray(stringifiedPoints).slice(0, -2)
const points = pointsToNumberArray(stringifiedPoints)
.slice(0, -2)
.map((coord: number): number => coord - offset);
if (points.length >= minimumPoints * 2) {
@ -63,7 +64,7 @@ export class EditHandlerImpl implements EditHandler {
private startEdit(): void {
// get started coordinates
const [clientX, clientY] = translateFromSVG(
this.canvas.node as any as SVGSVGElement,
(this.canvas.node as any) as SVGSVGElement,
this.editedShape.attr('points').split(' ')[this.editData.pointID].split(','),
);
@ -92,10 +93,7 @@ export class EditHandlerImpl implements EditHandler {
(this.editLine as any).draw('point', e);
} else {
const deltaTreshold = 15;
const delta = Math.sqrt(
((e.clientX - lastDrawnPoint.x) ** 2)
+ ((e.clientY - lastDrawnPoint.y) ** 2),
);
const delta = Math.sqrt((e.clientX - lastDrawnPoint.x) ** 2 + (e.clientY - lastDrawnPoint.y) ** 2);
if (delta > deltaTreshold) {
(this.editLine as any).draw('point', e);
}
@ -112,17 +110,22 @@ export class EditHandlerImpl implements EditHandler {
}
const strokeColor = this.editedShape.attr('stroke');
(this.editLine as any).addClass('cvat_canvas_shape_drawing').style({
'pointer-events': 'none',
'fill-opacity': 0,
stroke: strokeColor,
}).attr({
'data-origin-client-id': this.editData.state.clientID,
}).on('drawstart drawpoint', (e: CustomEvent): void => {
this.transform(this.geometry);
lastDrawnPoint.x = e.detail.event.clientX;
lastDrawnPoint.y = e.detail.event.clientY;
}).on('drawupdate', (): void => this.transform(this.geometry))
(this.editLine as any)
.addClass('cvat_canvas_shape_drawing')
.style({
'pointer-events': 'none',
'fill-opacity': 0,
stroke: strokeColor,
})
.attr({
'data-origin-client-id': this.editData.state.clientID,
})
.on('drawstart drawpoint', (e: CustomEvent): void => {
this.transform(this.geometry);
lastDrawnPoint.x = e.detail.event.clientX;
lastDrawnPoint.y = e.detail.event.clientY;
})
.on('drawupdate', (): void => this.transform(this.geometry))
.draw(dummyEvent, { snapToGrid: 0.1 });
if (this.editData.state.shapeType === 'points') {
@ -141,9 +144,7 @@ export class EditHandlerImpl implements EditHandler {
if (e.button === 0 && !e.altKey) {
(this.editLine as any).draw('point', e);
} else if (e.button === 2 && this.editLine) {
if (this.editData.state.shapeType === 'points'
|| this.editLine.attr('points').split(' ').length > 2
) {
if (this.editData.state.shapeType === 'points' || this.editLine.attr('points').split(' ').length > 2) {
(this.editLine as any).draw('undo');
}
}
@ -152,8 +153,7 @@ export class EditHandlerImpl implements EditHandler {
private selectPolygon(shape: SVG.Polygon): void {
const { offset } = this.geometry;
const points = pointsToNumberArray(shape.attr('points'))
.map((coord: number): number => coord - offset);
const points = pointsToNumberArray(shape.attr('points')).map((coord: number): number => coord - offset);
const { state } = this.editData;
this.edit({
@ -168,8 +168,7 @@ export class EditHandlerImpl implements EditHandler {
}
// Get stop point and all points
const stopPointID = Array.prototype.indexOf
.call((e.target as HTMLElement).parentElement.children, e.target);
const stopPointID = Array.prototype.indexOf.call((e.target as HTMLElement).parentElement.children, e.target);
const oldPoints = this.editedShape.attr('points').trim().split(' ');
const linePoints = this.editLine.attr('points').trim().split(' ');
@ -179,8 +178,7 @@ export class EditHandlerImpl implements EditHandler {
}
// Compute new point array
const [start, stop] = [this.editData.pointID, stopPointID]
.sort((a, b): number => +a - +b);
const [start, stop] = [this.editData.pointID, stopPointID].sort((a, b): number => +a - +b);
if (this.editData.state.shapeType !== 'polygon') {
let points = null;
@ -190,15 +188,15 @@ export class EditHandlerImpl implements EditHandler {
if (start !== this.editData.pointID) {
linePoints.reverse();
}
points = oldPoints.slice(0, start)
points = oldPoints
.slice(0, start)
.concat(linePoints)
.concat(oldPoints.slice(stop + 1));
} else {
points = oldPoints.concat(linePoints.slice(0, -1));
}
points = pointsToNumberArray(points.join(' '))
.map((coord: number): number => coord - offset);
points = pointsToNumberArray(points.join(' ')).map((coord: number): number => coord - offset);
const { state } = this.editData;
this.edit({
@ -209,21 +207,23 @@ export class EditHandlerImpl implements EditHandler {
return;
}
const cutIndexes1 = oldPoints.reduce((acc: string[], _: string, i: number) =>
i >= stop || i <= start ? [...acc, i] : acc, []);
const cutIndexes2 = oldPoints.reduce((acc: string[], _: string, i: number) =>
i <= stop && i >= start ? [...acc, i] : acc, []);
const cutIndexes1 = oldPoints.reduce(
(acc: string[], _: string, i: number) => (i >= stop || i <= start ? [...acc, i] : acc),
[],
);
const cutIndexes2 = oldPoints.reduce(
(acc: string[], _: string, i: number) => (i <= stop && i >= start ? [...acc, i] : acc),
[],
);
const curveLength = (indexes: number[]): number => {
const points = indexes.map((index: number): string => oldPoints[index])
const points = indexes
.map((index: number): string => oldPoints[index])
.map((point: string): string[] => point.split(','))
.map((point: string[]): number[] => [+point[0], +point[1]]);
let length = 0;
for (let i = 1; i < points.length; i++) {
length += Math.sqrt(
((points[i][0] - points[i - 1][0]) ** 2)
+ ((points[i][1] - points[i - 1][1]) ** 2),
);
length += Math.sqrt((points[i][0] - points[i - 1][0]) ** 2 + (points[i][1] - points[i - 1][1]) ** 2);
}
return length;
@ -236,11 +236,11 @@ export class EditHandlerImpl implements EditHandler {
linePoints.reverse();
}
const firstPart = oldPoints.slice(0, start)
const firstPart = oldPoints
.slice(0, start)
.concat(linePoints)
.concat(oldPoints.slice(stop + 1));
const secondPart = oldPoints.slice(start, stop)
.concat(linePoints.slice(1).reverse());
const secondPart = oldPoints.slice(start, stop).concat(linePoints.slice(1).reverse());
if (firstPart.length < 3 || secondPart.length < 3) {
this.cancel();
@ -264,19 +264,24 @@ export class EditHandlerImpl implements EditHandler {
this.selectPolygon(this.clones[0]);
} else {
for (const points of [firstPart, secondPart]) {
this.clones.push(this.canvas.polygon(points.join(' '))
.attr('fill', this.editedShape.attr('fill'))
.attr('fill-opacity', '0.5')
.addClass('cvat_canvas_shape'));
this.clones.push(
this.canvas
.polygon(points.join(' '))
.attr('fill', this.editedShape.attr('fill'))
.attr('fill-opacity', '0.5')
.addClass('cvat_canvas_shape'),
);
}
for (const clone of this.clones) {
clone.on('click', (): void => this.selectPolygon(clone));
clone.on('mouseenter', (): void => {
clone.addClass('cvat_canvas_shape_splitting');
}).on('mouseleave', (): void => {
clone.removeClass('cvat_canvas_shape_splitting');
});
clone
.on('mouseenter', (): void => {
clone.addClass('cvat_canvas_shape_splitting');
})
.on('mouseleave', (): void => {
clone.removeClass('cvat_canvas_shape_splitting');
});
}
}
}
@ -288,7 +293,7 @@ export class EditHandlerImpl implements EditHandler {
if (enabled) {
(this.editedShape as any).selectize(true, {
deepSelect: true,
pointSize: 2 * consts.BASE_POINT_SIZE / self.geometry.scale,
pointSize: (2 * consts.BASE_POINT_SIZE) / self.geometry.scale,
rotationPoint: false,
pointType(cx: number, cy: number): SVG.Circle {
const circle: SVG.Circle = this.nested
@ -354,9 +359,7 @@ export class EditHandlerImpl implements EditHandler {
}
private initEditing(): void {
this.editedShape = this.canvas
.select(`#cvat_canvas_shape_${this.editData.state.clientID}`)
.first().clone();
this.editedShape = this.canvas.select(`#cvat_canvas_shape_${this.editData.state.clientID}`).first().clone();
this.setupPoints(true);
this.startEdit();
// draw points for this with selected and start editing till another point is clicked
@ -406,15 +409,11 @@ export class EditHandlerImpl implements EditHandler {
}
public configurate(configuration: Configuration): void {
if (typeof (configuration.autoborders) === 'boolean') {
if (typeof configuration.autoborders === 'boolean') {
this.autobordersEnabled = configuration.autoborders;
if (this.editLine) {
if (this.autobordersEnabled) {
this.autoborderHandler.autoborder(
true,
this.editLine,
this.editData.state.clientID,
);
this.autoborderHandler.autoborder(true, this.editLine, this.editData.state.clientID);
} else {
this.autoborderHandler.autoborder(false);
}
@ -442,14 +441,8 @@ export class EditHandlerImpl implements EditHandler {
const paintHandler = this.editLine.remember('_paintHandler');
for (const point of (paintHandler as any).set.members) {
point.attr(
'stroke-width',
`${consts.POINTS_STROKE_WIDTH / geometry.scale}`,
);
point.attr(
'r',
`${consts.BASE_POINT_SIZE / geometry.scale}`,
);
point.attr('stroke-width', `${consts.POINTS_STROKE_WIDTH / geometry.scale}`);
point.attr('r', `${consts.BASE_POINT_SIZE / geometry.scale}`);
}
}
}

@ -5,10 +5,7 @@
import * as SVG from 'svg.js';
import { GroupData } from './canvasModel';
import {
translateToSVG,
} from './shared';
import { translateToSVG } from './shared';
export interface GroupHandler {
group(groupData: GroupData): void;
@ -35,16 +32,15 @@ export class GroupHandlerImpl implements GroupHandler {
private statesToBeGroupped: any[];
private highlightedShapes: Record<number, SVG.Shape>;
private getSelectionBox(event: MouseEvent): {
private getSelectionBox(
event: MouseEvent,
): {
xtl: number;
ytl: number;
xbr: number;
ybr: number;
} {
const point = translateToSVG(
(this.canvas.node as any as SVGSVGElement),
[event.clientX, event.clientY],
);
const point = translateToSVG((this.canvas.node as any) as SVGSVGElement, [event.clientX, event.clientY]);
const stopSelectionPoint = {
x: point[0],
y: point[1],
@ -60,10 +56,7 @@ export class GroupHandlerImpl implements GroupHandler {
private onSelectStart(event: MouseEvent): void {
if (!this.selectionRect) {
const point = translateToSVG(
this.canvas.node as any as SVGSVGElement,
[event.clientX, event.clientY],
);
const point = translateToSVG((this.canvas.node as any) as SVGSVGElement, [event.clientX, event.clientY]);
this.startSelectionPoint = {
x: point[0],
y: point[1],
@ -102,12 +95,16 @@ export class GroupHandlerImpl implements GroupHandler {
// TODO: Doesn't work properly for groups
const bbox = shape.bbox();
const clientID = shape.attr('clientID');
if (bbox.x > box.xtl && bbox.y > box.ytl
&& bbox.x + bbox.width < box.xbr
&& bbox.y + bbox.height < box.ybr
&& !(clientID in this.highlightedShapes)) {
const objectState = this.getStates()
.filter((state: any): boolean => state.clientID === clientID)[0];
if (
bbox.x > box.xtl &&
bbox.y > box.ytl &&
bbox.x + bbox.width < box.xbr &&
bbox.y + bbox.height < box.ybr &&
!(clientID in this.highlightedShapes)
) {
const objectState = this.getStates().filter(
(state: any): boolean => state.clientID === clientID,
)[0];
if (objectState) {
this.statesToBeGroupped.push(objectState);

@ -15,11 +15,7 @@ export interface InteractionHandler {
}
export class InteractionHandlerImpl implements InteractionHandler {
private onInteraction: (
shapes: InteractionResult[] | null,
shapesUpdated?: boolean,
isDone?: boolean,
) => void;
private onInteraction: (shapes: InteractionResult[] | null, shapesUpdated?: boolean, isDone?: boolean) => void;
private geometry: Geometry;
private canvas: SVG.Container;
private interactionData: InteractionData;
@ -30,42 +26,46 @@ export class InteractionHandlerImpl implements InteractionHandler {
private crosshair: Crosshair;
private prepareResult(): InteractionResult[] {
return this.interactionShapes.map((shape: SVG.Shape): InteractionResult => {
if (shape.type === 'circle') {
const points = [(shape as SVG.Circle).cx(), (shape as SVG.Circle).cy()];
return this.interactionShapes.map(
(shape: SVG.Shape): InteractionResult => {
if (shape.type === 'circle') {
const points = [(shape as SVG.Circle).cx(), (shape as SVG.Circle).cy()];
return {
points: points.map((coord: number): number => coord - this.geometry.offset),
shapeType: 'points',
button: shape.attr('stroke') === 'green' ? 0 : 2,
};
}
const bbox = ((shape.node as any) as SVGRectElement).getBBox();
const points = [bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height];
return {
points: points.map((coord: number): number => coord - this.geometry.offset),
shapeType: 'points',
button: shape.attr('stroke') === 'green' ? 0 : 2,
shapeType: 'rectangle',
button: 0,
};
}
const bbox = (shape.node as any as SVGRectElement).getBBox();
const points = [bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height];
return {
points: points.map((coord: number): number => coord - this.geometry.offset),
shapeType: 'rectangle',
button: 0,
};
});
},
);
}
private shouldRaiseEvent(ctrlKey: boolean): boolean {
const { interactionData, interactionShapes, shapesWereUpdated } = this;
const { minPosVertices, minNegVertices, enabled } = interactionData;
const positiveShapes = interactionShapes
.filter((shape: SVG.Shape): boolean => (shape as any).attr('stroke') === 'green');
const negativeShapes = interactionShapes
.filter((shape: SVG.Shape): boolean => (shape as any).attr('stroke') !== 'green');
const positiveShapes = interactionShapes.filter(
(shape: SVG.Shape): boolean => (shape as any).attr('stroke') === 'green',
);
const negativeShapes = interactionShapes.filter(
(shape: SVG.Shape): boolean => (shape as any).attr('stroke') !== 'green',
);
if (interactionData.shapeType === 'rectangle') {
return enabled && !ctrlKey && !!interactionShapes.length;
}
const minimumVerticesAchieved = (typeof (minPosVertices) === 'undefined'
|| minPosVertices <= positiveShapes.length) && (typeof (minNegVertices) === 'undefined'
|| minPosVertices <= negativeShapes.length);
const minimumVerticesAchieved =
(typeof minPosVertices === 'undefined' || minPosVertices <= positiveShapes.length) &&
(typeof minNegVertices === 'undefined' || minPosVertices <= negativeShapes.length);
return enabled && !ctrlKey && minimumVerticesAchieved && shapesWereUpdated;
}
@ -82,12 +82,10 @@ export class InteractionHandlerImpl implements InteractionHandler {
const eventListener = (e: MouseEvent): void => {
if ((e.button === 0 || e.button === 2) && !e.altKey) {
e.preventDefault();
const [cx, cy] = translateToSVG(
this.canvas.node as any as SVGSVGElement,
[e.clientX, e.clientY],
);
const [cx, cy] = translateToSVG((this.canvas.node as any) as SVGSVGElement, [e.clientX, e.clientY]);
this.currentInteractionShape = this.canvas
.circle(consts.BASE_POINT_SIZE * 2 / this.geometry.scale).center(cx, cy)
.circle((consts.BASE_POINT_SIZE * 2) / this.geometry.scale)
.center(cx, cy)
.fill('white')
.stroke(e.button === 0 ? 'green' : 'red')
.addClass('cvat_interaction_point')
@ -150,15 +148,18 @@ export class InteractionHandlerImpl implements InteractionHandler {
this.currentInteractionShape = this.canvas.rect();
this.canvas.on('mousedown.interaction', eventListener);
this.currentInteractionShape.on('drawstop', (): void => {
this.interactionShapes.push(this.currentInteractionShape);
this.shapesWereUpdated = true;
this.canvas.off('mousedown.interaction', eventListener);
this.interact({ enabled: false });
}).addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
this.currentInteractionShape
.on('drawstop', (): void => {
this.interactionShapes.push(this.currentInteractionShape);
this.shapesWereUpdated = true;
this.canvas.off('mousedown.interaction', eventListener);
this.interact({ enabled: false });
})
.addClass('cvat_canvas_shape_drawing')
.attr({
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
});
}
private initInteraction(): void {
@ -192,19 +193,11 @@ export class InteractionHandlerImpl implements InteractionHandler {
}
public constructor(
onInteraction: (
shapes: InteractionResult[] | null,
shapesUpdated?: boolean,
isDone?: boolean,
) => void,
onInteraction: (shapes: InteractionResult[] | null, shapesUpdated?: boolean, isDone?: boolean) => void,
canvas: SVG.Container,
geometry: Geometry,
) {
this.onInteraction = (
shapes: InteractionResult[] | null,
shapesUpdated?: boolean,
isDone?: boolean,
): void => {
this.onInteraction = (shapes: InteractionResult[] | null, shapesUpdated?: boolean, isDone?: boolean): void => {
this.shapesWereUpdated = false;
onInteraction(shapes, shapesUpdated, isDone);
};
@ -221,10 +214,7 @@ export class InteractionHandlerImpl implements InteractionHandler {
};
this.canvas.on('mousemove.interaction', (e: MouseEvent): void => {
const [x, y] = translateToSVG(
this.canvas.node as any as SVGSVGElement,
[e.clientX, e.clientY],
);
const [x, y] = translateToSVG((this.canvas.node as any) as SVGSVGElement, [e.clientX, e.clientY]);
this.cursorPosition = { x, y };
if (this.crosshair) {
this.crosshair.move(x, y);
@ -232,7 +222,8 @@ export class InteractionHandlerImpl implements InteractionHandler {
});
document.body.addEventListener('keyup', (e: KeyboardEvent): void => {
if (e.keyCode === 17 && this.shouldRaiseEvent(false)) { // 17 is ctrl
if (e.keyCode === 17 && this.shouldRaiseEvent(false)) {
// 17 is ctrl
this.onInteraction(this.prepareResult(), true, false);
}
});

@ -12,7 +12,6 @@ export interface MergeHandler {
repeatSelection(): void;
}
export class MergeHandlerImpl implements MergeHandler {
// callback is used to notify about merging end
private onMergeDone: (objects: any[] | null, duration?: number) => void;
@ -40,8 +39,10 @@ export class MergeHandlerImpl implements MergeHandler {
}
private checkConstraints(state: any): boolean {
return !this.constraints || (state.label.id === this.constraints.labelID
&& state.shapeType === this.constraints.shapeType);
return (
!this.constraints ||
(state.label.id === this.constraints.labelID && state.shapeType === this.constraints.shapeType)
);
}
private release(): void {
@ -118,8 +119,7 @@ export class MergeHandlerImpl implements MergeHandler {
}
} else {
const shape = this.canvas.select(`#cvat_canvas_shape_${objectState.clientID}`).first();
if (shape && this.checkConstraints(objectState)
&& !stateFrames.includes(objectState.frame)) {
if (shape && this.checkConstraints(objectState) && !stateFrames.includes(objectState.frame)) {
this.statesToBeMerged.push(objectState);
this.highlightedShapes[objectState.clientID] = shape;
shape.addClass('cvat_canvas_shape_merging');

@ -83,22 +83,25 @@ export function translateToSVG(svg: SVGSVGElement, points: number[]): number[] {
return output;
}
export function displayShapeSize(
shapesContainer: SVG.Container,
textContainer: SVG.Container,
): ShapeSizeElement {
export function displayShapeSize(shapesContainer: SVG.Container, textContainer: SVG.Container): ShapeSizeElement {
const shapeSize: ShapeSizeElement = {
sizeElement: textContainer.text('').font({
weight: 'bolder',
}).fill('white').addClass('cvat_canvas_text'),
update(shape: SVG.Shape): void{
sizeElement: textContainer
.text('')
.font({
weight: 'bolder',
})
.fill('white')
.addClass('cvat_canvas_text'),
update(shape: SVG.Shape): void {
const bbox = shape.bbox();
const text = `${bbox.width.toFixed(1)}x${bbox.height.toFixed(1)}`;
const [x, y]: number[] = translateToSVG(
textContainer.node as any as SVGSVGElement,
translateFromSVG((shapesContainer.node as any as SVGSVGElement), [bbox.x, bbox.y]),
(textContainer.node as any) as SVGSVGElement,
translateFromSVG((shapesContainer.node as any) as SVGSVGElement, [bbox.x, bbox.y]),
);
this.sizeElement.clear().plain(text)
this.sizeElement
.clear()
.plain(text)
.move(x + consts.TEXT_MARGIN, y + consts.TEXT_MARGIN);
},
rm(): void {
@ -120,7 +123,9 @@ export function pointsToNumberArray(points: string | Point[]): number[] {
}, []);
}
return points.trim().split(/[,\s]+/g)
return points
.trim()
.split(/[,\s]+/g)
.map((coord: string): number => +coord);
}
@ -138,14 +143,19 @@ export function parsePoints(source: string | number[]): Point[] {
}, []);
}
return source.trim().split(/\s/).map((point: string): Point => {
const [x, y] = point.split(',').map((coord: string): number => +coord);
return { x, y };
});
return source
.trim()
.split(/\s/)
.map(
(point: string): Point => {
const [x, y] = point.split(',').map((coord: string): number => +coord);
return { x, y };
},
);
}
export function stringifyPoints(points: (Point | number)[]): string {
if (typeof (points[0]) === 'number') {
if (typeof points[0] === 'number') {
return points.reduce((acc: string, val: number, idx: number): string => {
if (idx % 2) {
return `${acc},${val}`;
@ -166,5 +176,5 @@ export function scalarProduct(a: Vector2D, b: Vector2D): number {
}
export function vectorLength(vector: Vector2D): number {
return Math.sqrt((vector.i ** 2) + (vector.j ** 2));
return Math.sqrt(vector.i ** 2 + vector.j ** 2);
}

@ -85,12 +85,16 @@ export class SplitHandlerImpl implements SplitHandler {
this.highlightedShape = shape;
this.highlightedShape.addClass('cvat_canvas_shape_splitting');
this.canvas.node.append(this.highlightedShape.node);
this.highlightedShape.on('click.split', (): void => {
this.splitDone = true;
this.onSplitDone(state);
}, {
once: true,
});
this.highlightedShape.on(
'click.split',
(): void => {
this.splitDone = true;
this.onSplitDone(state);
},
{
once: true,
},
);
}
}
}

File diff suppressed because it is too large Load Diff

@ -5,14 +5,9 @@
import * as SVG from 'svg.js';
import consts from './consts';
import {
translateToSVG,
} from './shared';
import {
Geometry,
} from './canvasModel';
import { translateToSVG } from './shared';
import { Geometry } from './canvasModel';
export interface ZoomHandler {
zoom(): void;
@ -35,10 +30,7 @@ export class ZoomHandlerImpl implements ZoomHandler {
private onSelectStart(event: MouseEvent): void {
if (!this.selectionRect && event.which === 1) {
const point = translateToSVG(
(this.canvas.node as any as SVGSVGElement),
[event.clientX, event.clientY],
);
const point = translateToSVG((this.canvas.node as any) as SVGSVGElement, [event.clientX, event.clientY]);
this.startSelectionPoint = {
x: point[0],
y: point[1],
@ -52,16 +44,15 @@ export class ZoomHandlerImpl implements ZoomHandler {
}
}
private getSelectionBox(event: MouseEvent): {
private getSelectionBox(
event: MouseEvent,
): {
x: number;
y: number;
width: number;
height: number;
} {
const point = translateToSVG(
(this.canvas.node as any as SVGSVGElement),
[event.clientX, event.clientY],
);
const point = translateToSVG((this.canvas.node as any) as SVGSVGElement, [event.clientX, event.clientY]);
const stopSelectionPoint = {
x: point[0],
y: point[1],

@ -1,21 +1,19 @@
{
"compilerOptions": {
"baseUrl": ".",
"emitDeclarationOnly": true,
"module": "es6",
"target": "es6",
"noImplicitAny": true,
"preserveConstEnums": true,
"declaration": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"moduleResolution": "node",
"declarationDir": "dist/declaration",
"paths": {
"cvat-canvas.node": ["dist/cvat-canvas.node"]
}
},
"include": [
"src/typescript/*.ts"
]
"compilerOptions": {
"baseUrl": ".",
"emitDeclarationOnly": true,
"module": "es6",
"target": "es6",
"noImplicitAny": true,
"preserveConstEnums": true,
"declaration": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"moduleResolution": "node",
"declarationDir": "dist/declaration",
"paths": {
"cvat-canvas.node": ["dist/cvat-canvas.node"]
}
},
"include": ["src/typescript/*.ts"]
}

@ -1,11 +1,12 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/* eslint-disable */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
const DtsBundleWebpack = require('dts-bundle-webpack')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const DtsBundleWebpack = require('dts-bundle-webpack');
const nodeConfig = {
target: 'node',
@ -22,33 +23,38 @@ const nodeConfig = {
extensions: ['.ts', '.js', '.json'],
},
module: {
rules: [{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-optional-chaining'
],
presets: [
['@babel/preset-env'],
['@babel/typescript'],
],
sourceType: 'unambiguous',
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-optional-chaining',
],
presets: [['@babel/preset-env'], ['@babel/typescript']],
sourceType: 'unambiguous',
},
},
},
}, {
test: /\.(css|scss)$/,
exclude: /node_modules/,
use: ['style-loader', {
loader: 'css-loader',
options: {
importLoaders: 2,
},
}, 'postcss-loader', 'sass-loader']
}],
{
test: /\.(css|scss)$/,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
],
},
plugins: [
new DtsBundleWebpack({
@ -56,7 +62,7 @@ const nodeConfig = {
main: 'dist/declaration/src/typescript/canvas.d.ts',
out: '../cvat-canvas.node.d.ts',
}),
]
],
};
const webConfig = {
@ -82,32 +88,43 @@ const webConfig = {
extensions: ['.ts', '.js', '.json'],
},
module: {
rules: [{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-proposal-class-properties'],
presets: [
['@babel/preset-env', {
targets: '> 2.5%', // https://github.com/browserslist/browserslist
}],
['@babel/typescript'],
],
sourceType: 'unambiguous',
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-proposal-class-properties'],
presets: [
[
'@babel/preset-env',
{
targets: '> 2.5%', // https://github.com/browserslist/browserslist
},
],
['@babel/typescript'],
],
sourceType: 'unambiguous',
},
},
},
}, {
test: /\.scss$/,
exclude: /node_modules/,
use: ['style-loader', {
loader: 'css-loader',
options: {
importLoaders: 2,
},
}, 'postcss-loader', 'sass-loader']
}],
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
],
},
plugins: [
new DtsBundleWebpack({
@ -115,7 +132,7 @@ const webConfig = {
main: 'dist/declaration/src/typescript/canvas.d.ts',
out: '../cvat-canvas.d.ts',
}),
]
],
};
module.exports = [webConfig, nodeConfig]
module.exports = [webConfig, nodeConfig];

@ -1,55 +1,48 @@
/*
* Copyright (C) 2018 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
"env": {
"node": false,
"browser": true,
"es6": true,
"jquery": true,
"qunit": true,
module.exports = {
env: {
node: false,
browser: true,
es6: true,
jquery: true,
qunit: true,
},
"parserOptions": {
"parser": "babel-eslint",
"sourceType": "module",
"ecmaVersion": 2018,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module',
ecmaVersion: 2018,
},
"plugins": [
"security",
"no-unsanitized",
"no-unsafe-innerhtml",
],
"extends": [
"eslint:recommended",
"plugin:security/recommended",
"plugin:no-unsanitized/DOM",
"airbnb-base",
],
"rules": {
"no-await-in-loop": [0],
"global-require": [0],
"no-new": [0],
"class-methods-use-this": [0],
"no-restricted-properties": [0, {
"object": "Math",
"property": "pow",
}],
"no-plusplus": [0],
"no-param-reassign": [0],
"no-underscore-dangle": ["error", { "allowAfterThis": true }],
"no-restricted-syntax": [0, {"selector": "ForOfStatement"}],
"no-continue": [0],
"no-unsafe-innerhtml/no-unsafe-innerhtml": 1,
plugins: ['security', 'no-unsanitized', 'no-unsafe-innerhtml'],
extends: ['eslint:recommended', 'plugin:security/recommended', 'plugin:no-unsanitized/DOM', 'airbnb-base'],
rules: {
'no-await-in-loop': [0],
'global-require': [0],
'no-new': [0],
'class-methods-use-this': [0],
'no-restricted-properties': [
0,
{
object: 'Math',
property: 'pow',
},
],
'no-plusplus': [0],
'no-param-reassign': [0],
'no-underscore-dangle': ['error', { allowAfterThis: true }],
'no-restricted-syntax': [0, { selector: 'ForOfStatement' }],
'no-continue': [0],
'no-unsafe-innerhtml/no-unsafe-innerhtml': 1,
// This rule actual for user input data on the node.js environment mainly.
"security/detect-object-injection": 0,
"indent": ["warn", 4],
"no-useless-constructor": 0,
"func-names": [0],
"valid-typeof": [0],
"no-console": [0], // this rule deprecates console.log, console.warn etc. because "it is not good in production code"
"max-classes-per-file": [0],
'security/detect-object-injection': 0,
indent: ['warn', 4],
'no-useless-constructor': 0,
'func-names': [0],
'valid-typeof': [0],
'no-console': [0],
'max-classes-per-file': [0],
'max-len': ['warn', { code: 120 }],
},
};

@ -1,40 +1,47 @@
# Module CVAT-CORE
## Description
This CVAT module is a client-side JavaScipt library to management of objects, frames, logs, etc.
It contains the core logic of the Computer Vision Annotation Tool.
## Versioning
If you make changes in this package, please do following:
- After not important changes (typos, backward compatible bug fixes, refactoring) do: ``npm version patch``
- After changing API (backward compatible new features) do: ``npm version minor``
- After changing API (changes that break backward compatibility) do: ``npm version major``
- After not important changes (typos, backward compatible bug fixes, refactoring) do: `npm version patch`
- After changing API (backward compatible new features) do: `npm version minor`
- After changing API (changes that break backward compatibility) do: `npm version major`
### Commands
- Dependencies installation
```bash
npm install
```
- Building the module from sources in the ```dist``` directory:
- Building the module from sources in the `dist` directory:
```bash
npm run build
npm run build -- --mode=development # without a minification
```
- Building the documentation in the ```docs``` directory:
- Building the documentation in the `docs` directory:
```bash
npm run-script docs
```
- Running of tests:
```bash
npm run-script test
```
- Updating of a module version:
```bash
npm version patch # updated after minor fixes
npm version minor # updated after major changes which don't affect API compatibility with previous versions
@ -42,5 +49,6 @@ npm version major # updated after major changes which affect API compatibility
```
Visual studio code configurations:
- cvat.js debug starts debugging with entrypoint api.js
- cvat.js test builds library and runs entrypoint tests.js

@ -1,32 +1,15 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const { defaults } = require('jest-config');
module.exports = {
coverageDirectory: 'reports/coverage',
coverageReporters: ['lcov'],
moduleFileExtensions: [
...defaults.moduleFileExtensions,
'ts',
'tsx',
],
reporters: [
'default',
['jest-junit', { outputDirectory: 'reports/junit' }],
],
testMatch: [
'**/tests/**/*.js',
],
testPathIgnorePatterns: [
'/node_modules/',
'/tests/mocks/*',
],
moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'],
reporters: ['default', ['jest-junit', { outputDirectory: 'reports/junit' }]],
testMatch: ['**/tests/**/*.js'],
testPathIgnorePatterns: ['/node_modules/', '/tests/mocks/*'],
automock: false,
};

@ -1,8 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
plugins: [],

@ -1,14 +1,13 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
/**
* Class representing an annotation loader
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing an annotation loader
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class Loader {
constructor(initialData) {
const data = {
@ -21,42 +20,42 @@
Object.defineProperties(this, {
name: {
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
get: () => data.name,
},
format: {
/**
* @name format
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
* @name format
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
get: () => data.format,
},
version: {
/**
* @name version
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
* @name version
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
get: () => data.version,
},
enabled: {
/**
* @name enabled
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
* @name enabled
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
get: () => data.enabled,
},
});
@ -64,10 +63,10 @@
}
/**
* Class representing an annotation dumper
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing an annotation dumper
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class Dumper {
constructor(initialData) {
const data = {
@ -80,42 +79,42 @@
Object.defineProperties(this, {
name: {
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Dumper
* @readonly
* @instance
*/
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Dumper
* @readonly
* @instance
*/
get: () => data.name,
},
format: {
/**
* @name format
* @type {string}
* @memberof module:API.cvat.classes.Dumper
* @readonly
* @instance
*/
* @name format
* @type {string}
* @memberof module:API.cvat.classes.Dumper
* @readonly
* @instance
*/
get: () => data.format,
},
version: {
/**
* @name version
* @type {string}
* @memberof module:API.cvat.classes.Dumper
* @readonly
* @instance
*/
* @name version
* @type {string}
* @memberof module:API.cvat.classes.Dumper
* @readonly
* @instance
*/
get: () => data.version,
},
enabled: {
/**
* @name enabled
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
* @name enabled
* @type {string}
* @memberof module:API.cvat.classes.Loader
* @readonly
* @instance
*/
get: () => data.enabled,
},
});
@ -123,10 +122,10 @@
}
/**
* Class representing an annotation format
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing an annotation format
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class AnnotationFormats {
constructor(initialData) {
const data = {
@ -138,22 +137,22 @@
Object.defineProperties(this, {
loaders: {
/**
* @name loaders
* @type {module:API.cvat.classes.Loader[]}
* @memberof module:API.cvat.classes.AnnotationFormats
* @readonly
* @instance
*/
* @name loaders
* @type {module:API.cvat.classes.Loader[]}
* @memberof module:API.cvat.classes.AnnotationFormats
* @readonly
* @instance
*/
get: () => [...data.importers],
},
dumpers: {
/**
* @name dumpers
* @type {module:API.cvat.classes.Dumper[]}
* @memberof module:API.cvat.classes.AnnotationFormats
* @readonly
* @instance
*/
* @name dumpers
* @type {module:API.cvat.classes.Dumper[]}
* @memberof module:API.cvat.classes.AnnotationFormats
* @readonly
* @instance
*/
get: () => [...data.exporters],
},
});

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const {
@ -28,18 +23,9 @@
const { checkObjectType } = require('./common');
const Statistics = require('./statistics');
const { Label } = require('./labels');
const {
DataError,
ArgumentError,
ScriptingError,
} = require('./exceptions');
const { DataError, ArgumentError, ScriptingError } = require('./exceptions');
const {
HistoryActions,
ObjectShape,
ObjectType,
colors,
} = require('./enums');
const { HistoryActions, ObjectShape, ObjectType, colors } = require('./enums');
const ObjectState = require('./object-state');
function shapeFactory(shapeData, clientID, injection) {
@ -48,31 +34,28 @@
let shapeModel = null;
switch (type) {
case 'rectangle':
shapeModel = new RectangleShape(shapeData, clientID, color, injection);
break;
case 'polygon':
shapeModel = new PolygonShape(shapeData, clientID, color, injection);
break;
case 'polyline':
shapeModel = new PolylineShape(shapeData, clientID, color, injection);
break;
case 'points':
shapeModel = new PointsShape(shapeData, clientID, color, injection);
break;
case 'cuboid':
shapeModel = new CuboidShape(shapeData, clientID, color, injection);
break;
default:
throw new DataError(
`An unexpected type of shape "${type}"`,
);
case 'rectangle':
shapeModel = new RectangleShape(shapeData, clientID, color, injection);
break;
case 'polygon':
shapeModel = new PolygonShape(shapeData, clientID, color, injection);
break;
case 'polyline':
shapeModel = new PolylineShape(shapeData, clientID, color, injection);
break;
case 'points':
shapeModel = new PointsShape(shapeData, clientID, color, injection);
break;
case 'cuboid':
shapeModel = new CuboidShape(shapeData, clientID, color, injection);
break;
default:
throw new DataError(`An unexpected type of shape "${type}"`);
}
return shapeModel;
}
function trackFactory(trackData, clientID, injection) {
if (trackData.shapes.length) {
const { type } = trackData.shapes[0];
@ -80,25 +63,23 @@
let trackModel = null;
switch (type) {
case 'rectangle':
trackModel = new RectangleTrack(trackData, clientID, color, injection);
break;
case 'polygon':
trackModel = new PolygonTrack(trackData, clientID, color, injection);
break;
case 'polyline':
trackModel = new PolylineTrack(trackData, clientID, color, injection);
break;
case 'points':
trackModel = new PointsTrack(trackData, clientID, color, injection);
break;
case 'cuboid':
trackModel = new CuboidTrack(trackData, clientID, color, injection);
break;
default:
throw new DataError(
`An unexpected type of track "${type}"`,
);
case 'rectangle':
trackModel = new RectangleTrack(trackData, clientID, color, injection);
break;
case 'polygon':
trackModel = new PolygonTrack(trackData, clientID, color, injection);
break;
case 'polyline':
trackModel = new PolylineTrack(trackData, clientID, color, injection);
break;
case 'points':
trackModel = new PointsTrack(trackData, clientID, color, injection);
break;
case 'cuboid':
trackModel = new CuboidTrack(trackData, clientID, color, injection);
break;
default:
throw new DataError(`An unexpected type of track "${type}"`);
}
return trackModel;
@ -185,18 +166,20 @@
export() {
const data = {
tracks: this.tracks.filter((track) => !track.removed)
.map((track) => track.toJSON()),
tracks: this.tracks.filter((track) => !track.removed).map((track) => track.toJSON()),
shapes: Object.values(this.shapes)
.reduce((accumulator, value) => {
accumulator.push(...value);
return accumulator;
}, []).filter((shape) => !shape.removed)
}, [])
.filter((shape) => !shape.removed)
.map((shape) => shape.toJSON()),
tags: Object.values(this.tags).reduce((accumulator, value) => {
accumulator.push(...value);
return accumulator;
}, []).filter((tag) => !tag.removed)
tags: Object.values(this.tags)
.reduce((accumulator, value) => {
accumulator.push(...value);
return accumulator;
}, [])
.filter((tag) => !tag.removed)
.map((tag) => tag.toJSON()),
};
@ -252,7 +235,7 @@
const objectsForMerge = objectStates.map((state) => {
checkObjectType('object state', state, null, ObjectState);
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
if (typeof object === 'undefined') {
throw new ArgumentError(
'The object has not been saved yet. Call ObjectState.put([state]) before you can merge it',
);
@ -263,15 +246,11 @@
const keyframes = {}; // frame: position
const { label, shapeType } = objectStates[0];
if (!(label.id in this.labels)) {
throw new ArgumentError(
`Unknown label for the task: ${label.id}`,
);
throw new ArgumentError(`Unknown label for the task: ${label.id}`);
}
if (!Object.values(ObjectShape).includes(shapeType)) {
throw new ArgumentError(
`Got unknown shapeType "${shapeType}"`,
);
throw new ArgumentError(`Got unknown shapeType "${shapeType}"`);
}
const labelAttributes = label.attributes.reduce((accumulator, attribute) => {
@ -290,18 +269,14 @@
}
if (state.shapeType !== shapeType) {
throw new ArgumentError(
`All shapes are expected to be ${shapeType}, but got ${state.shapeType}`,
);
throw new ArgumentError(`All shapes are expected to be ${shapeType}, but got ${state.shapeType}`);
}
// If this object is shape, get it position and save as a keyframe
if (object instanceof Shape) {
// Frame already saved and it is not outside
if (object.frame in keyframes && !keyframes[object.frame].outside) {
throw new ArgumentError(
'Expected only one visible shape per frame',
);
throw new ArgumentError('Expected only one visible shape per frame');
}
keyframes[object.frame] = {
@ -325,9 +300,8 @@
// Push outside shape after each annotation shape
// Any not outside shape rewrites it
if (!((object.frame + 1) in keyframes) && object.frame + 1 <= this.stopFrame) {
keyframes[object.frame + 1] = JSON
.parse(JSON.stringify(keyframes[object.frame]));
if (!(object.frame + 1 in keyframes) && object.frame + 1 <= this.stopFrame) {
keyframes[object.frame + 1] = JSON.parse(JSON.stringify(keyframes[object.frame]));
keyframes[object.frame + 1].outside = true;
keyframes[object.frame + 1].frame++;
}
@ -344,17 +318,14 @@
continue;
}
throw new ArgumentError(
'Expected only one visible shape per frame',
);
throw new ArgumentError('Expected only one visible shape per frame');
}
// We do not save an attribute if it has the same value
// We save only updates
let updatedAttributes = false;
for (const attrID in shape.attributes) {
if (!(attrID in attributes)
|| attributes[attrID] !== shape.attributes[attrID]) {
if (!(attrID in attributes) || attributes[attrID] !== shape.attributes[attrID]) {
updatedAttributes = true;
attributes[attrID] = shape.attributes[attrID];
}
@ -367,21 +338,22 @@
occluded: shape.occluded,
outside: shape.outside,
zOrder: shape.zOrder,
attributes: updatedAttributes ? Object.keys(attributes)
.reduce((accumulator, attrID) => {
accumulator.push({
spec_id: +attrID,
value: attributes[attrID],
});
return accumulator;
}, []) : [],
attributes: updatedAttributes
? Object.keys(attributes).reduce((accumulator, attrID) => {
accumulator.push({
spec_id: +attrID,
value: attributes[attrID],
});
return accumulator;
}, [])
: [],
};
}
} else {
throw new ArgumentError(
`Trying to merge unknown object type: ${object.constructor.name}. `
+ 'Only shapes and tracks are expected.',
`Trying to merge unknown object type: ${object.constructor.name}. ` +
'Only shapes and tracks are expected.',
);
}
}
@ -399,22 +371,24 @@
const clientID = ++this.count;
const track = {
frame: Math.min.apply(null, Object.keys(keyframes).map((frame) => +frame)),
frame: Math.min.apply(
null,
Object.keys(keyframes).map((frame) => +frame),
),
shapes: Object.values(keyframes),
group: 0,
source: objectStates[0].source,
label_id: label.id,
attributes: Object.keys(objectStates[0].attributes)
.reduce((accumulator, attrID) => {
if (!labelAttributes[attrID].mutable) {
accumulator.push({
spec_id: +attrID,
value: objectStates[0].attributes[attrID],
});
}
attributes: Object.keys(objectStates[0].attributes).reduce((accumulator, attrID) => {
if (!labelAttributes[attrID].mutable) {
accumulator.push({
spec_id: +attrID,
value: objectStates[0].attributes[attrID],
});
}
return accumulator;
}, []),
return accumulator;
}, []),
};
const trackModel = trackFactory(track, clientID, this.injection);
@ -426,20 +400,23 @@
object.removed = true;
}
this.history.do(HistoryActions.MERGED_OBJECTS, () => {
trackModel.removed = true;
for (const object of objectsForMerge) {
object.removed = false;
}
}, () => {
trackModel.removed = false;
for (const object of objectsForMerge) {
object.removed = true;
}
}, [
...objectsForMerge
.map((object) => object.clientID), trackModel.clientID,
], objectStates[0].frame);
this.history.do(
HistoryActions.MERGED_OBJECTS,
() => {
trackModel.removed = true;
for (const object of objectsForMerge) {
object.removed = false;
}
},
() => {
trackModel.removed = false;
for (const object of objectsForMerge) {
object.removed = true;
}
},
[...objectsForMerge.map((object) => object.clientID), trackModel.clientID],
objectStates[0].frame,
);
}
split(objectState, frame) {
@ -447,10 +424,8 @@
checkObjectType('frame', frame, 'integer', null);
const object = this.objects[objectState.clientID];
if (typeof (object) === 'undefined') {
throw new ArgumentError(
'The object has not been saved yet. Call annotations.put([state]) before',
);
if (typeof object === 'undefined') {
throw new ArgumentError('The object has not been saved yet. Call annotations.put([state]) before');
}
if (objectState.objectType !== ObjectType.TRACK) {
@ -474,17 +449,16 @@
occluded: objectState.occluded,
outside: objectState.outside,
zOrder: objectState.zOrder,
attributes: Object.keys(objectState.attributes)
.reduce((accumulator, attrID) => {
if (!labelAttributes[attrID].mutable) {
accumulator.push({
spec_id: +attrID,
value: objectState.attributes[attrID],
});
}
attributes: Object.keys(objectState.attributes).reduce((accumulator, attrID) => {
if (!labelAttributes[attrID].mutable) {
accumulator.push({
spec_id: +attrID,
value: objectState.attributes[attrID],
});
}
return accumulator;
}, []),
return accumulator;
}, []),
frame,
};
@ -526,15 +500,21 @@
// Remove source object
object.removed = true;
this.history.do(HistoryActions.SPLITTED_TRACK, () => {
object.removed = false;
prevTrack.removed = true;
nextTrack.removed = true;
}, () => {
object.removed = true;
prevTrack.removed = false;
nextTrack.removed = false;
}, [object.clientID, prevTrack.clientID, nextTrack.clientID], frame);
this.history.do(
HistoryActions.SPLITTED_TRACK,
() => {
object.removed = false;
prevTrack.removed = true;
nextTrack.removed = true;
},
() => {
object.removed = true;
prevTrack.removed = false;
nextTrack.removed = false;
},
[object.clientID, prevTrack.clientID, nextTrack.clientID],
frame,
);
}
group(objectStates, reset) {
@ -543,10 +523,8 @@
const objectsForGroup = objectStates.map((state) => {
checkObjectType('object state', state, null, ObjectState);
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new ArgumentError(
'The object has not been saved yet. Call annotations.put([state]) before',
);
if (typeof object === 'undefined') {
throw new ArgumentError('The object has not been saved yet. Call annotations.put([state]) before');
}
return object;
});
@ -558,15 +536,21 @@
}
const redoGroups = objectsForGroup.map((object) => object.group);
this.history.do(HistoryActions.GROUPED_OBJECTS, () => {
objectsForGroup.forEach((object, idx) => {
object.group = undoGroups[idx];
});
}, () => {
objectsForGroup.forEach((object, idx) => {
object.group = redoGroups[idx];
});
}, objectsForGroup.map((object) => object.clientID), objectStates[0].frame);
this.history.do(
HistoryActions.GROUPED_OBJECTS,
() => {
objectsForGroup.forEach((object, idx) => {
object.group = undoGroups[idx];
});
},
() => {
objectsForGroup.forEach((object, idx) => {
object.group = redoGroups[idx];
});
},
objectsForGroup.map((object) => object.clientID),
objectStates[0].frame,
);
return groupIdx;
}
@ -629,9 +613,7 @@
} else if (object instanceof Tag) {
objectType = 'tag';
} else {
throw new ScriptingError(
`Unexpected object type: "${objectType}"`,
);
throw new ScriptingError(`Unexpected object type: "${objectType}"`);
}
const label = object.label.name;
@ -645,7 +627,8 @@
if (objectType === 'track') {
const keyframes = Object.keys(object.shapes)
.sort((a, b) => +a - +b).map((el) => +el);
.sort((a, b) => +a - +b)
.map((el) => +el);
let prevKeyframe = keyframes[0];
let visible = false;
@ -680,7 +663,7 @@
for (const label of Object.keys(labels)) {
for (const key of Object.keys(labels[label])) {
if (typeof (labels[label][key]) === 'object') {
if (typeof labels[label][key] === 'object') {
for (const objectType of Object.keys(labels[label][key])) {
total[key][objectType] += labels[label][key][objectType];
}
@ -723,8 +706,7 @@
checkObjectType('state attributes', state.attributes, null, Object);
checkObjectType('state label', state.label, null, Label);
const attributes = Object.keys(state.attributes)
.reduce(convertAttributes.bind(state), []);
const attributes = Object.keys(state.attributes).reduce(convertAttributes.bind(state), []);
const labelAttributes = state.label.attributes.reduce((accumulator, attribute) => {
accumulator[attribute.id] = attribute;
return accumulator;
@ -749,8 +731,7 @@
if (!Object.values(ObjectShape).includes(state.shapeType)) {
throw new ArgumentError(
'Object shape must be one of: '
+ `${JSON.stringify(Object.values(ObjectShape))}`,
`Object shape must be one of: ${JSON.stringify(Object.values(ObjectShape))}`,
);
}
@ -768,27 +749,26 @@
});
} else if (state.objectType === 'track') {
constructed.tracks.push({
attributes: attributes
.filter((attr) => !labelAttributes[attr.spec_id].mutable),
attributes: attributes.filter((attr) => !labelAttributes[attr.spec_id].mutable),
frame: state.frame,
group: 0,
source: state.source,
label_id: state.label.id,
shapes: [{
attributes: attributes
.filter((attr) => labelAttributes[attr.spec_id].mutable),
frame: state.frame,
occluded: state.occluded || false,
outside: false,
points: [...state.points],
type: state.shapeType,
z_order: state.zOrder,
}],
shapes: [
{
attributes: attributes.filter((attr) => labelAttributes[attr.spec_id].mutable),
frame: state.frame,
occluded: state.occluded || false,
outside: false,
points: [...state.points],
type: state.shapeType,
z_order: state.zOrder,
},
],
});
} else {
throw new ArgumentError(
'Object type must be one of: '
+ `${JSON.stringify(Object.values(ObjectType))}`,
`Object type must be one of: ${JSON.stringify(Object.values(ObjectType))}`,
);
}
}
@ -796,20 +776,24 @@
// Add constructed objects to a collection
const imported = this.import(constructed);
const importedArray = imported.tags
.concat(imported.tracks)
.concat(imported.shapes);
const importedArray = imported.tags.concat(imported.tracks).concat(imported.shapes);
if (objectStates.length) {
this.history.do(HistoryActions.CREATED_OBJECTS, () => {
importedArray.forEach((object) => {
object.removed = true;
});
}, () => {
importedArray.forEach((object) => {
object.removed = false;
});
}, importedArray.map((object) => object.clientID), objectStates[0].frame);
this.history.do(
HistoryActions.CREATED_OBJECTS,
() => {
importedArray.forEach((object) => {
object.removed = true;
});
},
() => {
importedArray.forEach((object) => {
object.removed = false;
});
},
importedArray.map((object) => object.clientID),
objectStates[0].frame,
);
}
return importedArray.map((value) => value.clientID);
@ -829,14 +813,11 @@
}
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new ArgumentError(
'The object has not been saved yet. Call annotations.put([state]) before',
);
if (typeof object === 'undefined') {
throw new ArgumentError('The object has not been saved yet. Call annotations.put([state]) before');
}
const distance = object.constructor.distance(state.points, x, y);
if (distance !== null && (minimumDistance === null
|| distance < minimumDistance)) {
if (distance !== null && (minimumDistance === null || distance < minimumDistance)) {
minimumDistance = distance;
minimumState = state;
}
@ -850,12 +831,8 @@
searchEmpty(frameFrom, frameTo) {
const sign = Math.sign(frameTo - frameFrom);
const predicate = sign > 0
? (frame) => frame <= frameTo
: (frame) => frame >= frameTo;
const update = sign > 0
? (frame) => frame + 1
: (frame) => frame - 1;
const predicate = sign > 0 ? (frame) => frame <= frameTo : (frame) => frame >= frameTo;
const update = sign > 0 ? (frame) => frame + 1 : (frame) => frame - 1;
for (let frame = frameFrom; predicate(frame); frame = update(frame)) {
if (frame in this.shapes && this.shapes[frame].some((shape) => !shape.removed)) {
continue;
@ -890,9 +867,9 @@
const sign = Math.sign(frameTo - frameFrom);
const flattenedQuery = groups.flat(Number.MAX_SAFE_INTEGER);
const containsDifficultProperties = flattenedQuery
.some((fragment) => fragment
.match(/^width/) || fragment.match(/^height/));
const containsDifficultProperties = flattenedQuery.some(
(fragment) => fragment.match(/^width/) || fragment.match(/^height/),
);
const deepSearch = (deepSearchFrom, deepSearchTo) => {
// deepSearchFrom is expected to be a frame that doesn't satisfy a filter
@ -915,12 +892,8 @@
};
const keyframesMemory = {};
const predicate = sign > 0
? (frame) => frame <= frameTo
: (frame) => frame >= frameTo;
const update = sign > 0
? (frame) => frame + 1
: (frame) => frame - 1;
const predicate = sign > 0 ? (frame) => frame <= frameTo : (frame) => frame >= frameTo;
const update = sign > 0 ? (frame) => frame + 1 : (frame) => frame - 1;
for (let frame = frameFrom; predicate(frame); frame = update(frame)) {
// First prepare all data for the frame
// Consider all shapes, tags, and not outside tracks that have keyframe here
@ -934,15 +907,9 @@
.map((tag) => tag.get(frame)),
);
const tracks = Object.values(this.tracks)
.filter((track) => (
frame in track.shapes
|| frame === frameFrom
|| frame === frameTo
)).filter((track) => !track.removed);
statesData.push(
...tracks.map((track) => track.get(frame))
.filter((state) => !state.outside),
);
.filter((track) => frame in track.shapes || frame === frameFrom || frame === frameTo)
.filter((track) => !track.removed);
statesData.push(...tracks.map((track) => track.get(frame)).filter((state) => !state.outside));
// Nothing to filtering, go to the next iteration
if (!statesData.length) {
@ -963,12 +930,8 @@
for (const track of tracks) {
const trackIsSatisfy = filtered.includes(track.clientID);
if (!trackIsSatisfy) {
keyframesMemory[track.clientID] = [
filtered.includes(track.clientID),
frame,
];
} else if (keyframesMemory[track.clientID]
&& keyframesMemory[track.clientID][0] === false) {
keyframesMemory[track.clientID] = [filtered.includes(track.clientID), frame];
} else if (keyframesMemory[track.clientID] && keyframesMemory[track.clientID][0] === false) {
withDeepSearch = true;
}
}
@ -976,9 +939,7 @@
if (withDeepSearch) {
const reducer = sign > 0 ? Math.min : Math.max;
const deepSearchFrom = reducer(
...Object.values(keyframesMemory).map((value) => value[1]),
);
const deepSearchFrom = reducer(...Object.values(keyframesMemory).map((value) => value[1]));
return deepSearch(deepSearchFrom, frame);
}

@ -1,20 +1,11 @@
/*
* Copyright (C) 2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const jsonpath = require('jsonpath');
const {
AttributeType,
ObjectType,
} = require('./enums');
const { AttributeType, ObjectType } = require('./enums');
const { ArgumentError } = require('./exceptions');
class AnnotationsFilter {
constructor() {
// eslint-disable-next-line security/detect-unsafe-regex
@ -47,8 +38,7 @@ class AnnotationsFilter {
if (operators.includes(expression[i])) {
if (!nestedCounter) {
const subexpression = expression
.substr(start + 1, i - start - 1).trim();
const subexpression = expression.substr(start + 1, i - start - 1).trim();
splitted.push(subexpression);
splitted.push(expression[i]);
start = i;
@ -56,18 +46,14 @@ class AnnotationsFilter {
}
}
const subexpression = expression
.substr(start + 1).trim();
const subexpression = expression.substr(start + 1).trim();
splitted.push(subexpression);
splitted.forEach((internalExpression) => {
if (internalExpression === '|' || internalExpression === '&') {
container.push(internalExpression);
} else {
this._groupByBrackets(
container,
internalExpression,
);
this._groupByBrackets(container, internalExpression);
}
});
}
@ -103,12 +89,8 @@ class AnnotationsFilter {
endBracket = i;
const subcontainer = [];
const subexpression = expression
.substr(startBracket + 1, endBracket - 1 - startBracket);
this._splitWithOperator(
subcontainer,
subexpression,
);
const subexpression = expression.substr(startBracket + 1, endBracket - 1 - startBracket);
this._splitWithOperator(subcontainer, subexpression);
container.push(subcontainer);
@ -136,7 +118,7 @@ class AnnotationsFilter {
for (const group of groups) {
if (Array.isArray(group)) {
expression += `(${this._join(group)})`;
} else if (typeof (group) === 'string') {
} else if (typeof group === 'string') {
// it can be operator or expression
if (group === '|' || group === '&') {
expression += group;
@ -158,11 +140,10 @@ class AnnotationsFilter {
_convertObjects(statesData) {
const objects = statesData.map((state) => {
const labelAttributes = state.label.attributes
.reduce((acc, attr) => {
acc[attr.id] = attr;
return acc;
}, {});
const labelAttributes = state.label.attributes.reduce((acc, attr) => {
acc[attr.id] = attr;
return acc;
}, {});
let xtl = Number.MAX_SAFE_INTEGER;
let xbr = Number.MIN_SAFE_INTEGER;
@ -172,10 +153,12 @@ class AnnotationsFilter {
if (state.objectType !== ObjectType.TAG) {
state.points.forEach((coord, idx) => {
if (idx % 2) { // y
if (idx % 2) {
// y
ytl = Math.min(ytl, coord);
ybr = Math.max(ybr, coord);
} else { // x
} else {
// x
xtl = Math.min(xtl, coord);
xbr = Math.max(xbr, coord);
}
@ -216,7 +199,7 @@ class AnnotationsFilter {
toJSONQuery(filters) {
try {
if (!Array.isArray(filters) || filters.some((value) => typeof (value) !== 'string')) {
if (!Array.isArray(filters) || filters.some((value) => typeof value !== 'string')) {
throw Error('Argument must be an array of strings');
}
@ -225,7 +208,10 @@ class AnnotationsFilter {
}
const groups = [];
const expression = filters.map((filter) => `(${filter})`).join('|').replace(/\\"/g, '`');
const expression = filters
.map((filter) => `(${filter})`)
.join('|')
.replace(/\\"/g, '`');
this._splitWithOperator(groups, expression);
return [groups, `$.objects[?(${this._join(groups)})].clientID`];
} catch (error) {

@ -1,7 +1,6 @@
/*
* Copyright (C) 2019-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const MAX_HISTORY_LENGTH = 128;

File diff suppressed because it is too large Load Diff

@ -1,16 +1,11 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const serverProxy = require('./server-proxy');
const { Task } = require('./session');
const { ScriptingError } = ('./exceptions');
const { ScriptingError } = './exceptions';
class AnnotationsSaver {
constructor(version, collection, session) {
@ -53,12 +48,7 @@
}
async _request(data, action) {
const result = await serverProxy.annotations.updateAnnotations(
this.sessionType,
this.id,
data,
action,
);
const result = await serverProxy.annotations.updateAnnotations(this.sessionType, this.id, data, action);
return result;
}
@ -102,27 +92,37 @@
},
};
const keys = ['id', 'label_id', 'group', 'frame',
'occluded', 'z_order', 'points', 'type', 'shapes',
'attributes', 'value', 'spec_id', 'source', 'outside'];
const keys = [
'id',
'label_id',
'group',
'frame',
'occluded',
'z_order',
'points',
'type',
'shapes',
'attributes',
'value',
'spec_id',
'source',
'outside',
];
// Find created and updated objects
for (const type of Object.keys(exported)) {
for (const object of exported[type]) {
if (object.id in this.initialObjects[type]) {
const exportedHash = JSON.stringify(object, keys);
const initialHash = JSON.stringify(
this.initialObjects[type][object.id], keys,
);
const initialHash = JSON.stringify(this.initialObjects[type][object.id], keys);
if (exportedHash !== initialHash) {
splitted.updated[type].push(object);
}
} else if (typeof (object.id) === 'undefined') {
} else if (typeof object.id === 'undefined') {
splitted.created[type].push(object);
} else {
throw new ScriptingError(
`Id of object is defined "${object.id}"`
+ 'but it absents in initial state',
`Id of object is defined "${object.id}" but it absents in initial state`,
);
}
}
@ -144,21 +144,17 @@
}
}
return splitted;
}
_updateCreatedObjects(saved, indexes) {
const savedLength = saved.tracks.length
+ saved.shapes.length + saved.tags.length;
const savedLength = saved.tracks.length + saved.shapes.length + saved.tags.length;
const indexesLength = indexes.tracks.length
+ indexes.shapes.length + indexes.tags.length;
const indexesLength = indexes.tracks.length + indexes.shapes.length + indexes.tags.length;
if (indexesLength !== savedLength) {
throw new ScriptingError(
'Number of indexes is differed by number of saved objects'
+ `${indexesLength} vs ${savedLength}`,
`Number of indexes is differed by number of saved objects ${indexesLength} vs ${savedLength}`,
);
}
@ -180,7 +176,9 @@
};
// Remove them from the request body
exported.tracks.concat(exported.shapes).concat(exported.tags)
exported.tracks
.concat(exported.shapes)
.concat(exported.tags)
.map((value) => {
delete value.clientID;
return value;
@ -214,11 +212,7 @@
}
}
} else {
const {
created,
updated,
deleted,
} = this._split(exported);
const { created, updated, deleted } = this._split(exported);
onUpdate('Created objects are being saved on the server');
const indexes = this._receiveIndexes(created);

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const serverProxy = require('./server-proxy');
@ -14,15 +9,8 @@
const AnnotationsHistory = require('./annotations-history');
const { checkObjectType } = require('./common');
const { Task } = require('./session');
const {
Loader,
Dumper,
} = require('./annotation-formats.js');
const {
ScriptingError,
DataError,
ArgumentError,
} = require('./exceptions');
const { Loader, Dumper } = require('./annotation-formats');
const { ScriptingError, DataError, ArgumentError } = require('./exceptions');
const jobCache = new WeakMap();
const taskCache = new WeakMap();
@ -36,9 +24,7 @@
return jobCache;
}
throw new ScriptingError(
`Unknown session type was received ${sessionType}`,
);
throw new ScriptingError(`Unknown session type was received ${sessionType}`);
}
async function getAnnotationsFromServer(session) {
@ -46,8 +32,7 @@
const cache = getCache(sessionType);
if (!cache.has(session)) {
const rawAnnotations = await serverProxy.annotations
.getAnnotations(sessionType, session.id);
const rawAnnotations = await serverProxy.annotations.getAnnotations(sessionType, session.id);
// Get meta information about frames
const startFrame = sessionType === 'job' ? session.startFrame : 0;
@ -242,28 +227,22 @@
async function uploadAnnotations(session, file, loader) {
const sessionType = session instanceof Task ? 'task' : 'job';
if (!(loader instanceof Loader)) {
throw new ArgumentError(
'A loader must be instance of Loader class',
);
throw new ArgumentError('A loader must be instance of Loader class');
}
await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, loader.name);
}
async function dumpAnnotations(session, name, dumper) {
if (!(dumper instanceof Dumper)) {
throw new ArgumentError(
'A dumper must be instance of Dumper class',
);
throw new ArgumentError('A dumper must be instance of Dumper class');
}
let result = null;
const sessionType = session instanceof Task ? 'task' : 'job';
if (sessionType === 'job') {
result = await serverProxy.annotations
.dumpAnnotations(session.task.id, name, dumper.name);
result = await serverProxy.annotations.dumpAnnotations(session.task.id, name, dumper.name);
} else {
result = await serverProxy.annotations
.dumpAnnotations(session.id, name, dumper.name);
result = await serverProxy.annotations.dumpAnnotations(session.id, name, dumper.name);
}
return result;
@ -297,19 +276,14 @@
async function exportDataset(session, format) {
if (!(format instanceof String || typeof format === 'string')) {
throw new ArgumentError(
'Format must be a string',
);
throw new ArgumentError('Format must be a string');
}
if (!(session instanceof Task)) {
throw new ArgumentError(
'A dataset can only be created from a task',
);
throw new ArgumentError('A dataset can only be created from a task');
}
let result = null;
result = await serverProxy.tasks
.exportDataset(session.id, format);
result = await serverProxy.tasks.exportDataset(session.id, format);
return result;
}

@ -1,30 +1,17 @@
/*
* Copyright (C) 2019-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const PluginRegistry = require('./plugins');
const serverProxy = require('./server-proxy');
const lambdaManager = require('./lambda-manager');
const {
isBoolean,
isInteger,
isEnum,
isString,
checkFilter,
} = require('./common');
const { isBoolean, isInteger, isEnum, isString, checkFilter } = require('./common');
const { TaskStatus, TaskMode } = require('./enums');
const User = require('./user');
const { AnnotationFormats } = require('./annotation-formats.js');
const { AnnotationFormats } = require('./annotation-formats');
const { ArgumentError } = require('./exceptions');
const { Task } = require('./session');
@ -79,10 +66,24 @@
return result;
};
cvat.server.register.implementation = async (username, firstName, lastName,
email, password1, password2, userConfirmations) => {
const user = await serverProxy.server.register(username, firstName,
lastName, email, password1, password2, userConfirmations);
cvat.server.register.implementation = async (
username,
firstName,
lastName,
email,
password1,
password2,
userConfirmations,
) => {
const user = await serverProxy.server.register(
username,
firstName,
lastName,
email,
password1,
password2,
userConfirmations,
);
return new User(user);
};
@ -95,9 +96,7 @@
await serverProxy.server.logout();
};
cvat.server.changePassword.implementation = async (
oldPassword, newPassword1, newPassword2,
) => {
cvat.server.changePassword.implementation = async (oldPassword, newPassword1, newPassword2) => {
await serverProxy.server.changePassword(oldPassword, newPassword1, newPassword2);
};
@ -105,9 +104,7 @@
await serverProxy.server.requestPasswordReset(email);
};
cvat.server.resetPassword.implementation = async (
newPassword1, newPassword2, uid, token,
) => {
cvat.server.resetPassword.implementation = async (newPassword1, newPassword2, uid, token) => {
await serverProxy.server.resetPassword(newPassword1, newPassword2, uid, token);
};
@ -144,16 +141,12 @@
jobID: isInteger,
});
if (('taskID' in filter) && ('jobID' in filter)) {
throw new ArgumentError(
'Only one of fields "taskID" and "jobID" allowed simultaneously',
);
if ('taskID' in filter && 'jobID' in filter) {
throw new ArgumentError('Only one of fields "taskID" and "jobID" allowed simultaneously');
}
if (!Object.keys(filter).length) {
throw new ArgumentError(
'Job filter must not be empty',
);
throw new ArgumentError('Job filter must not be empty');
}
let tasks = null;
@ -161,19 +154,17 @@
tasks = await serverProxy.tasks.getTasks(`id=${filter.taskID}`);
} else {
const job = await serverProxy.jobs.getJob(filter.jobID);
if (typeof (job.task_id) !== 'undefined') {
if (typeof job.task_id !== 'undefined') {
tasks = await serverProxy.tasks.getTasks(`id=${job.task_id}`);
}
}
// If task was found by its id, then create task instance and get Job instance from it
if (tasks !== null && tasks.length) {
const users = (await serverProxy.users.getUsers())
.map((userData) => new User(userData));
const users = (await serverProxy.users.getUsers()).map((userData) => new User(userData));
const task = new Task(attachUsers(tasks[0], users));
return filter.jobID ? task.jobs
.filter((job) => job.id === filter.jobID) : task.jobs;
return filter.jobID ? task.jobs.filter((job) => job.id === filter.jobID) : task.jobs;
}
return [];
@ -193,17 +184,13 @@
if ('search' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) {
throw new ArgumentError(
'Do not use the filter field "search" with others',
);
throw new ArgumentError('Do not use the filter field "search" with others');
}
}
if ('id' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) {
throw new ArgumentError(
'Do not use the filter field "id" with others',
);
throw new ArgumentError('Do not use the filter field "id" with others');
}
}
@ -214,13 +201,9 @@
}
}
const users = (await serverProxy.users.getUsers())
.map((userData) => new User(userData));
const users = (await serverProxy.users.getUsers()).map((userData) => new User(userData));
const tasksData = await serverProxy.tasks.getTasks(searchParams.toString());
const tasks = tasksData
.map((task) => attachUsers(task, users))
.map((task) => new Task(task));
const tasks = tasksData.map((task) => attachUsers(task, users)).map((task) => new Task(task));
tasks.count = tasksData.count;

File diff suppressed because it is too large Load Diff

@ -1,21 +1,16 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const { ArgumentError } = require('./exceptions');
function isBoolean(value) {
return typeof (value) === 'boolean';
return typeof value === 'boolean';
}
function isInteger(value) {
return typeof (value) === 'number' && Number.isInteger(value);
return typeof value === 'number' && Number.isInteger(value);
}
// Called with specific Enum context
@ -32,20 +27,16 @@
}
function isString(value) {
return typeof (value) === 'string';
return typeof value === 'string';
}
function checkFilter(filter, fields) {
for (const prop in filter) {
if (Object.prototype.hasOwnProperty.call(filter, prop)) {
if (!(prop in fields)) {
throw new ArgumentError(
`Unsupported filter property has been recieved: "${prop}"`,
);
throw new ArgumentError(`Unsupported filter property has been recieved: "${prop}"`);
} else if (!fields[prop](filter[prop])) {
throw new ArgumentError(
`Received filter property "${prop}" is not satisfied for checker`,
);
throw new ArgumentError(`Received filter property "${prop}" is not satisfied for checker`);
}
}
}
@ -53,28 +44,24 @@
function checkObjectType(name, value, type, instance) {
if (type) {
if (typeof (value) !== type) {
if (typeof value !== type) {
// specific case for integers which aren't native type in JS
if (type === 'integer' && Number.isInteger(value)) {
return true;
}
throw new ArgumentError(
`"${name}" is expected to be "${type}", but "${typeof (value)}" has been got.`,
);
throw new ArgumentError(`"${name}" is expected to be "${type}", but "${typeof value}" has been got.`);
}
} else if (instance) {
if (!(value instanceof instance)) {
if (value !== undefined) {
throw new ArgumentError(
`"${name}" is expected to be ${instance.name}, but `
+ `"${value.constructor.name}" has been got`,
`"${name}" is expected to be ${instance.name}, but ` +
`"${value.constructor.name}" has been got`,
);
}
throw new ArgumentError(
`"${name}" is expected to be ${instance.name}, but "undefined" has been got.`,
);
throw new ArgumentError(`"${name}" is expected to be ${instance.name}, but "undefined" has been got.`);
}
}

@ -1,7 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
backendAPI: 'http://localhost:7000/api/v1',

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const Axios = require('axios');
@ -13,7 +8,6 @@ Axios.defaults.withCredentials = true;
Axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN';
Axios.defaults.xsrfCookieName = 'csrftoken';
onmessage = (e) => {
Axios.get(e.data.url, e.data.config)
.then((response) => {

@ -1,33 +1,32 @@
/*
* Copyright (C) 2019-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
/**
* Share files types
* @enum {string}
* @name ShareFileType
* @memberof module:API.cvat.enums
* @property {string} DIR 'DIR'
* @property {string} REG 'REG'
* @readonly
*/
* Share files types
* @enum {string}
* @name ShareFileType
* @memberof module:API.cvat.enums
* @property {string} DIR 'DIR'
* @property {string} REG 'REG'
* @readonly
*/
const ShareFileType = Object.freeze({
DIR: 'DIR',
REG: 'REG',
});
/**
* Task statuses
* @enum {string}
* @name TaskStatus
* @memberof module:API.cvat.enums
* @property {string} ANNOTATION 'annotation'
* @property {string} VALIDATION 'validation'
* @property {string} COMPLETED 'completed'
* @readonly
*/
* Task statuses
* @enum {string}
* @name TaskStatus
* @memberof module:API.cvat.enums
* @property {string} ANNOTATION 'annotation'
* @property {string} VALIDATION 'validation'
* @property {string} COMPLETED 'completed'
* @readonly
*/
const TaskStatus = Object.freeze({
ANNOTATION: 'annotation',
VALIDATION: 'validation',
@ -35,17 +34,17 @@
});
/**
* List of RQ statuses
* @enum {string}
* @name RQStatus
* @memberof module:API.cvat.enums
* @property {string} QUEUED 'queued'
* @property {string} STARTED 'started'
* @property {string} FINISHED 'finished'
* @property {string} FAILED 'failed'
* @property {string} UNKNOWN 'unknown'
* @readonly
*/
* List of RQ statuses
* @enum {string}
* @name RQStatus
* @memberof module:API.cvat.enums
* @property {string} QUEUED 'queued'
* @property {string} STARTED 'started'
* @property {string} FINISHED 'finished'
* @property {string} FAILED 'failed'
* @property {string} UNKNOWN 'unknown'
* @readonly
*/
const RQStatus = Object.freeze({
QUEUED: 'queued',
STARTED: 'started',
@ -55,31 +54,31 @@
});
/**
* Task modes
* @enum {string}
* @name TaskMode
* @memberof module:API.cvat.enums
* @property {string} ANNOTATION 'annotation'
* @property {string} INTERPOLATION 'interpolation'
* @readonly
*/
* Task modes
* @enum {string}
* @name TaskMode
* @memberof module:API.cvat.enums
* @property {string} ANNOTATION 'annotation'
* @property {string} INTERPOLATION 'interpolation'
* @readonly
*/
const TaskMode = Object.freeze({
ANNOTATION: 'annotation',
INTERPOLATION: 'interpolation',
});
/**
* Attribute types
* @enum {string}
* @name AttributeType
* @memberof module:API.cvat.enums
* @property {string} CHECKBOX 'checkbox'
* @property {string} SELECT 'select'
* @property {string} RADIO 'radio'
* @property {string} NUMBER 'number'
* @property {string} TEXT 'text'
* @readonly
*/
* Attribute types
* @enum {string}
* @name AttributeType
* @memberof module:API.cvat.enums
* @property {string} CHECKBOX 'checkbox'
* @property {string} SELECT 'select'
* @property {string} RADIO 'radio'
* @property {string} NUMBER 'number'
* @property {string} TEXT 'text'
* @readonly
*/
const AttributeType = Object.freeze({
CHECKBOX: 'checkbox',
RADIO: 'radio',
@ -89,15 +88,15 @@
});
/**
* Object types
* @enum {string}
* @name ObjectType
* @memberof module:API.cvat.enums
* @property {string} TAG 'tag'
* @property {string} SHAPE 'shape'
* @property {string} TRACK 'track'
* @readonly
*/
* Object types
* @enum {string}
* @name ObjectType
* @memberof module:API.cvat.enums
* @property {string} TAG 'tag'
* @property {string} SHAPE 'shape'
* @property {string} TRACK 'track'
* @readonly
*/
const ObjectType = Object.freeze({
TAG: 'tag',
SHAPE: 'shape',
@ -105,17 +104,17 @@
});
/**
* Object shapes
* @enum {string}
* @name ObjectShape
* @memberof module:API.cvat.enums
* @property {string} RECTANGLE 'rectangle'
* @property {string} POLYGON 'polygon'
* @property {string} POLYLINE 'polyline'
* @property {string} POINTS 'points'
* @property {string} CUBOID 'cuboid'
* @readonly
*/
* Object shapes
* @enum {string}
* @name ObjectShape
* @memberof module:API.cvat.enums
* @property {string} RECTANGLE 'rectangle'
* @property {string} POLYGON 'polygon'
* @property {string} POLYLINE 'polyline'
* @property {string} POINTS 'points'
* @property {string} CUBOID 'cuboid'
* @readonly
*/
const ObjectShape = Object.freeze({
RECTANGLE: 'rectangle',
POLYGON: 'polygon',
@ -125,14 +124,14 @@
});
/**
* Annotation type
* @enum {string}
* @name Source
* @memberof module:API.cvat.enums
* @property {string} MANUAL 'manual'
* @property {string} AUTO 'auto'
* @readonly
*/
* Annotation type
* @enum {string}
* @name Source
* @memberof module:API.cvat.enums
* @property {string} MANUAL 'manual'
* @property {string} AUTO 'auto'
* @readonly
*/
const Source = Object.freeze({
MANUAL: 'manual',
AUTO: 'auto',
@ -211,27 +210,27 @@
});
/**
* Types of actions with annotations
* @enum {string}
* @name HistoryActions
* @memberof module:API.cvat.enums
* @property {string} CHANGED_LABEL Changed label
* @property {string} CHANGED_ATTRIBUTES Changed attributes
* @property {string} CHANGED_POINTS Changed points
* @property {string} CHANGED_OUTSIDE Changed outside
* @property {string} CHANGED_OCCLUDED Changed occluded
* @property {string} CHANGED_ZORDER Changed z-order
* @property {string} CHANGED_LOCK Changed lock
* @property {string} CHANGED_COLOR Changed color
* @property {string} CHANGED_HIDDEN Changed hidden
* @property {string} CHANGED_SOURCE Changed source
* @property {string} MERGED_OBJECTS Merged objects
* @property {string} SPLITTED_TRACK Splitted track
* @property {string} GROUPED_OBJECTS Grouped objects
* @property {string} CREATED_OBJECTS Created objects
* @property {string} REMOVED_OBJECT Removed object
* @readonly
*/
* Types of actions with annotations
* @enum {string}
* @name HistoryActions
* @memberof module:API.cvat.enums
* @property {string} CHANGED_LABEL Changed label
* @property {string} CHANGED_ATTRIBUTES Changed attributes
* @property {string} CHANGED_POINTS Changed points
* @property {string} CHANGED_OUTSIDE Changed outside
* @property {string} CHANGED_OCCLUDED Changed occluded
* @property {string} CHANGED_ZORDER Changed z-order
* @property {string} CHANGED_LOCK Changed lock
* @property {string} CHANGED_COLOR Changed color
* @property {string} CHANGED_HIDDEN Changed hidden
* @property {string} CHANGED_SOURCE Changed source
* @property {string} MERGED_OBJECTS Merged objects
* @property {string} SPLITTED_TRACK Splitted track
* @property {string} GROUPED_OBJECTS Grouped objects
* @property {string} CREATED_OBJECTS Created objects
* @property {string} REMOVED_OBJECT Removed object
* @readonly
*/
const HistoryActions = Object.freeze({
CHANGED_LABEL: 'Changed label',
CHANGED_ATTRIBUTES: 'Changed attributes',
@ -265,18 +264,43 @@
};
/**
* Array of hex colors
* @name colors
* @memberof module:API.cvat.enums
* @type {string[]}
* @readonly
*/
* Array of hex colors
* @name colors
* @memberof module:API.cvat.enums
* @type {string[]}
* @readonly
*/
const colors = [
'#33ddff', '#fa3253', '#34d1b7', '#ff007c', '#ff6037', '#ddff33',
'#24b353', '#b83df5', '#66ff66', '#32b7fa', '#ffcc33', '#83e070',
'#fafa37', '#5986b3', '#8c78f0', '#ff6a4d', '#f078f0', '#2a7dd1',
'#b25050', '#cc3366', '#cc9933', '#aaf0d1', '#ff00cc', '#3df53d',
'#fa32b7', '#fa7dbb', '#ff355e', '#f59331', '#3d3df5', '#733380',
'#33ddff',
'#fa3253',
'#34d1b7',
'#ff007c',
'#ff6037',
'#ddff33',
'#24b353',
'#b83df5',
'#66ff66',
'#32b7fa',
'#ffcc33',
'#83e070',
'#fafa37',
'#5986b3',
'#8c78f0',
'#ff6a4d',
'#f078f0',
'#2a7dd1',
'#b25050',
'#cc3366',
'#cc9933',
'#aaf0d1',
'#ff00cc',
'#3df53d',
'#fa32b7',
'#fa7dbb',
'#ff355e',
'#f59331',
'#3d3df5',
'#733380',
];
module.exports = {

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const Platform = require('platform');
@ -13,15 +8,15 @@
const config = require('./config');
/**
* Base exception class
* @memberof module:API.cvat.exceptions
* @extends Error
* @ignore
*/
* Base exception class
* @memberof module:API.cvat.exceptions
* @extends Error
* @ignore
*/
class Exception extends Error {
/**
* @param {string} message - Exception message
*/
* @param {string} message - Exception message
*/
constructor(message) {
super(message);
@ -32,126 +27,125 @@
const filename = `${info.fileName}`;
const line = info.lineNumber;
const column = info.columnNumber;
const {
jobID,
taskID,
clientID,
} = config;
const { jobID, taskID, clientID } = config;
const projID = undefined; // wasn't implemented
Object.defineProperties(this, Object.freeze({
system: {
/**
* @name system
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => system,
},
client: {
/**
* @name client
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => client,
},
time: {
/**
* @name time
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => time,
},
jobID: {
/**
* @name jobID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => jobID,
},
taskID: {
/**
* @name taskID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => taskID,
},
projID: {
/**
* @name projID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => projID,
},
clientID: {
/**
* @name clientID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => clientID,
},
filename: {
/**
* @name filename
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => filename,
},
line: {
/**
* @name line
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => line,
},
column: {
/**
* @name column
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => column,
},
}));
Object.defineProperties(
this,
Object.freeze({
system: {
/**
* @name system
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => system,
},
client: {
/**
* @name client
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => client,
},
time: {
/**
* @name time
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => time,
},
jobID: {
/**
* @name jobID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => jobID,
},
taskID: {
/**
* @name taskID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => taskID,
},
projID: {
/**
* @name projID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => projID,
},
clientID: {
/**
* @name clientID
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => clientID,
},
filename: {
/**
* @name filename
* @type {string}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => filename,
},
line: {
/**
* @name line
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => line,
},
column: {
/**
* @name column
* @type {integer}
* @memberof module:API.cvat.exceptions.Exception
* @readonly
* @instance
*/
get: () => column,
},
}),
);
}
/**
* Save an exception on a server
* @name save
* @method
* @memberof Exception
* @instance
* @async
*/
* Save an exception on a server
* @name save
* @method
* @memberof Exception
* @instance
* @async
*/
async save() {
const exceptionObject = {
system: this.system,
@ -178,86 +172,89 @@
}
/**
* Exceptions are referred with arguments data
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
* Exceptions are referred with arguments data
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
class ArgumentError extends Exception {
/**
* @param {string} message - Exception message
*/
* @param {string} message - Exception message
*/
constructor(message) {
super(message);
}
}
/**
* Unexpected problems with data which are not connected with a user input
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
* Unexpected problems with data which are not connected with a user input
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
class DataError extends Exception {
/**
* @param {string} message - Exception message
*/
/**
* @param {string} message - Exception message
*/
constructor(message) {
super(message);
}
}
/**
* Unexpected situations in code
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
* Unexpected situations in code
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
class ScriptingError extends Exception {
/**
* @param {string} message - Exception message
*/
* @param {string} message - Exception message
*/
constructor(message) {
super(message);
}
}
/**
* Plugin-referred exceptions
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
* Plugin-referred exceptions
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
class PluginError extends Exception {
/**
* @param {string} message - Exception message
*/
* @param {string} message - Exception message
*/
constructor(message) {
super(message);
}
}
/**
* Exceptions in interaction with a server
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
* Exceptions in interaction with a server
* @memberof module:API.cvat.exceptions
* @extends module:API.cvat.exceptions.Exception
*/
class ServerError extends Exception {
/**
* @param {string} message - Exception message
* @param {(string|integer)} code - Response code
*/
* @param {string} message - Exception message
* @param {(string|integer)} code - Response code
*/
constructor(message, code) {
super(message);
Object.defineProperties(this, Object.freeze({
/**
* @name code
* @type {(string|integer)}
* @memberof module:API.cvat.exceptions.ServerError
* @readonly
* @instance
*/
code: {
get: () => code,
},
}));
Object.defineProperties(
this,
Object.freeze({
/**
* @name code
* @type {(string|integer)}
* @memberof module:API.cvat.exceptions.ServerError
* @readonly
* @instance
*/
code: {
get: () => code,
},
}),
);
}
}

@ -1,12 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
global:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const cvatData = require('cvat-data');
@ -19,100 +13,93 @@
const frameDataCache = {};
/**
* Class provides meta information about specific frame and frame itself
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class provides meta information about specific frame and frame itself
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class FrameData {
constructor({
width,
height,
name,
taskID,
frameNumber,
startFrame,
stopFrame,
decodeForward,
}) {
Object.defineProperties(this, Object.freeze({
/**
* @name filename
* @type {string}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
filename: {
value: name,
writable: false,
},
/**
* @name width
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
width: {
value: width,
writable: false,
},
/**
* @name height
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
height: {
value: height,
writable: false,
},
tid: {
value: taskID,
writable: false,
},
/**
* @name number
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
number: {
value: frameNumber,
writable: false,
},
startFrame: {
value: startFrame,
writable: false,
},
stopFrame: {
value: stopFrame,
writable: false,
},
decodeForward: {
value: decodeForward,
writable: false,
},
}));
constructor({ width, height, name, taskID, frameNumber, startFrame, stopFrame, decodeForward }) {
Object.defineProperties(
this,
Object.freeze({
/**
* @name filename
* @type {string}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
filename: {
value: name,
writable: false,
},
/**
* @name width
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
width: {
value: width,
writable: false,
},
/**
* @name height
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
height: {
value: height,
writable: false,
},
tid: {
value: taskID,
writable: false,
},
/**
* @name number
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
number: {
value: frameNumber,
writable: false,
},
startFrame: {
value: startFrame,
writable: false,
},
stopFrame: {
value: stopFrame,
writable: false,
},
decodeForward: {
value: decodeForward,
writable: false,
},
}),
);
}
/**
* Method returns URL encoded image which can be placed in the img tag
* @method data
* @returns {string}
* @memberof module:API.cvat.classes.FrameData
* @instance
* @async
* @param {function} [onServerRequest = () => {}]
* callback which will be called if data absences local
* @throws {module:API.cvat.exception.ServerError}
* @throws {module:API.cvat.exception.PluginError}
*/
* Method returns URL encoded image which can be placed in the img tag
* @method data
* @returns {string}
* @memberof module:API.cvat.classes.FrameData
* @instance
* @async
* @param {function} [onServerRequest = () => {}]
* callback which will be called if data absences local
* @throws {module:API.cvat.exception.ServerError}
* @throws {module:API.cvat.exception.PluginError}
*/
async data(onServerRequest = () => {}) {
const result = await PluginRegistry
.apiWrapper.call(this, FrameData.prototype.data, onServerRequest);
const result = await PluginRegistry.apiWrapper.call(this, FrameData.prototype.data, onServerRequest);
return result;
}
}
@ -136,15 +123,14 @@
const { provider } = frameDataCache[this.tid];
const { chunkSize } = frameDataCache[this.tid];
const start = parseInt(this.number / chunkSize, 10) * chunkSize;
const stop = Math.min(
this.stopFrame,
(parseInt(this.number / chunkSize, 10) + 1) * chunkSize - 1,
);
const stop = Math.min(this.stopFrame, (parseInt(this.number / chunkSize, 10) + 1) * chunkSize - 1);
const chunkNumber = Math.floor(this.number / chunkSize);
const onDecodeAll = async (frameNumber) => {
if (frameDataCache[this.tid].activeChunkRequest
&& chunkNumber === frameDataCache[this.tid].activeChunkRequest.chunkNumber) {
if (
frameDataCache[this.tid].activeChunkRequest &&
chunkNumber === frameDataCache[this.tid].activeChunkRequest.chunkNumber
) {
const callbackArray = frameDataCache[this.tid].activeChunkRequest.callbacks;
for (let i = callbackArray.length - 1; i >= 0; --i) {
if (callbackArray[i].frameNumber === frameNumber) {
@ -160,8 +146,10 @@
};
const rejectRequestAll = () => {
if (frameDataCache[this.tid].activeChunkRequest
&& chunkNumber === frameDataCache[this.tid].activeChunkRequest.chunkNumber) {
if (
frameDataCache[this.tid].activeChunkRequest &&
chunkNumber === frameDataCache[this.tid].activeChunkRequest.chunkNumber
) {
for (const r of frameDataCache[this.tid].activeChunkRequest.callbacks) {
r.reject(r.frameNumber);
}
@ -172,147 +160,160 @@
const makeActiveRequest = () => {
const taskDataCache = frameDataCache[this.tid];
const activeChunk = taskDataCache.activeChunkRequest;
activeChunk.request = serverProxy.frames.getData(this.tid,
activeChunk.chunkNumber).then((chunk) => {
frameDataCache[this.tid].activeChunkRequest.completed = true;
if (!taskDataCache.nextChunkRequest) {
provider.requestDecodeBlock(chunk,
taskDataCache.activeChunkRequest.start,
taskDataCache.activeChunkRequest.stop,
taskDataCache.activeChunkRequest.onDecodeAll,
taskDataCache.activeChunkRequest.rejectRequestAll);
}
}).catch((exception) => {
if (exception instanceof Exception) {
reject(exception);
} else {
reject(new Exception(exception.message));
}
}).finally(() => {
if (taskDataCache.nextChunkRequest) {
if (taskDataCache.activeChunkRequest) {
for (const r of taskDataCache.activeChunkRequest.callbacks) {
r.reject(r.frameNumber);
activeChunk.request = serverProxy.frames
.getData(this.tid, activeChunk.chunkNumber)
.then((chunk) => {
frameDataCache[this.tid].activeChunkRequest.completed = true;
if (!taskDataCache.nextChunkRequest) {
provider.requestDecodeBlock(
chunk,
taskDataCache.activeChunkRequest.start,
taskDataCache.activeChunkRequest.stop,
taskDataCache.activeChunkRequest.onDecodeAll,
taskDataCache.activeChunkRequest.rejectRequestAll,
);
}
})
.catch((exception) => {
if (exception instanceof Exception) {
reject(exception);
} else {
reject(new Exception(exception.message));
}
})
.finally(() => {
if (taskDataCache.nextChunkRequest) {
if (taskDataCache.activeChunkRequest) {
for (const r of taskDataCache.activeChunkRequest.callbacks) {
r.reject(r.frameNumber);
}
}
taskDataCache.activeChunkRequest = taskDataCache.nextChunkRequest;
taskDataCache.nextChunkRequest = null;
makeActiveRequest();
}
taskDataCache.activeChunkRequest = taskDataCache.nextChunkRequest;
taskDataCache.nextChunkRequest = null;
makeActiveRequest();
}
});
});
};
if (isNode) {
resolve('Dummy data');
} else if (isBrowser) {
provider.frame(this.number).then((frame) => {
if (frame === null) {
onServerRequest();
const activeRequest = frameDataCache[this.tid].activeChunkRequest;
if (!provider.isChunkCached(start, stop)) {
if (!activeRequest
|| (activeRequest
&& activeRequest.completed
&& activeRequest.chunkNumber !== chunkNumber)) {
if (activeRequest && activeRequest.rejectRequestAll) {
activeRequest.rejectRequestAll();
}
frameDataCache[this.tid].activeChunkRequest = {
request: null,
chunkNumber,
start,
stop,
onDecodeAll,
rejectRequestAll,
completed: false,
callbacks: [{
provider
.frame(this.number)
.then((frame) => {
if (frame === null) {
onServerRequest();
const activeRequest = frameDataCache[this.tid].activeChunkRequest;
if (!provider.isChunkCached(start, stop)) {
if (
!activeRequest ||
(activeRequest &&
activeRequest.completed &&
activeRequest.chunkNumber !== chunkNumber)
) {
if (activeRequest && activeRequest.rejectRequestAll) {
activeRequest.rejectRequestAll();
}
frameDataCache[this.tid].activeChunkRequest = {
request: null,
chunkNumber,
start,
stop,
onDecodeAll,
rejectRequestAll,
completed: false,
callbacks: [
{
resolve: resolveWrapper,
reject,
frameNumber: this.number,
},
],
};
makeActiveRequest();
} else if (activeRequest.chunkNumber === chunkNumber) {
if (!activeRequest.onDecodeAll && !activeRequest.rejectRequestAll) {
activeRequest.onDecodeAll = onDecodeAll;
activeRequest.rejectRequestAll = rejectRequestAll;
}
activeRequest.callbacks.push({
resolve: resolveWrapper,
reject,
frameNumber: this.number,
}],
};
makeActiveRequest();
} else if (activeRequest.chunkNumber === chunkNumber) {
if (!activeRequest.onDecodeAll
&& !activeRequest.rejectRequestAll) {
activeRequest.onDecodeAll = onDecodeAll;
activeRequest.rejectRequestAll = rejectRequestAll;
});
} else {
if (frameDataCache[this.tid].nextChunkRequest) {
const { callbacks } = frameDataCache[this.tid].nextChunkRequest;
for (const r of callbacks) {
r.reject(r.frameNumber);
}
}
frameDataCache[this.tid].nextChunkRequest = {
request: null,
chunkNumber,
start,
stop,
onDecodeAll,
rejectRequestAll,
completed: false,
callbacks: [
{
resolve: resolveWrapper,
reject,
frameNumber: this.number,
},
],
};
}
} else {
activeRequest.callbacks.push({
resolve: resolveWrapper,
reject,
frameNumber: this.number,
});
} else {
if (frameDataCache[this.tid].nextChunkRequest) {
const { callbacks } = frameDataCache[this.tid].nextChunkRequest;
for (const r of callbacks) {
r.reject(r.frameNumber);
}
}
frameDataCache[this.tid].nextChunkRequest = {
request: null,
chunkNumber,
start,
stop,
onDecodeAll,
rejectRequestAll,
completed: false,
callbacks: [{
resolve: resolveWrapper,
reject,
frameNumber: this.number,
}],
};
provider.requestDecodeBlock(null, start, stop, onDecodeAll, rejectRequestAll);
}
} else {
activeRequest.callbacks.push({
resolve: resolveWrapper,
reject,
frameNumber: this.number,
});
provider.requestDecodeBlock(null, start, stop,
onDecodeAll, rejectRequestAll);
}
} else {
if (this.number % chunkSize > chunkSize / 4
&& provider.decodedBlocksCacheSize > 1
&& this.decodeForward
&& !provider.isNextChunkExists(this.number)) {
const nextChunkNumber = Math.floor(this.number / chunkSize) + 1;
if (nextChunkNumber * chunkSize < this.stopFrame) {
provider.setReadyToLoading(nextChunkNumber);
const nextStart = nextChunkNumber * chunkSize;
const nextStop = (nextChunkNumber + 1) * chunkSize - 1;
if (!provider.isChunkCached(nextStart, nextStop)) {
if (!frameDataCache[this.tid].activeChunkRequest) {
frameDataCache[this.tid].activeChunkRequest = {
request: null,
chunkNumber: nextChunkNumber,
start: nextStart,
stop: nextStop,
onDecodeAll: null,
rejectRequestAll: null,
completed: false,
callbacks: [],
};
makeActiveRequest();
if (
this.number % chunkSize > chunkSize / 4 &&
provider.decodedBlocksCacheSize > 1 &&
this.decodeForward &&
!provider.isNextChunkExists(this.number)
) {
const nextChunkNumber = Math.floor(this.number / chunkSize) + 1;
if (nextChunkNumber * chunkSize < this.stopFrame) {
provider.setReadyToLoading(nextChunkNumber);
const nextStart = nextChunkNumber * chunkSize;
const nextStop = (nextChunkNumber + 1) * chunkSize - 1;
if (!provider.isChunkCached(nextStart, nextStop)) {
if (!frameDataCache[this.tid].activeChunkRequest) {
frameDataCache[this.tid].activeChunkRequest = {
request: null,
chunkNumber: nextChunkNumber,
start: nextStart,
stop: nextStop,
onDecodeAll: null,
rejectRequestAll: null,
completed: false,
callbacks: [],
};
makeActiveRequest();
}
} else {
provider.requestDecodeBlock(null, nextStart, nextStop, null, null);
}
} else {
provider.requestDecodeBlock(null, nextStart, nextStop,
null, null);
}
}
resolveWrapper(frame);
}
resolveWrapper(frame);
}
}).catch((exception) => {
if (exception instanceof Exception) {
reject(exception);
} else {
reject(new Exception(exception.message));
}
});
})
.catch((exception) => {
if (exception instanceof Exception) {
reject(exception);
} else {
reject(new Exception(exception.message));
}
});
}
});
};
@ -324,16 +325,12 @@
[size] = meta.frames;
} else if (mode === 'annotation') {
if (frame >= meta.size) {
throw new ArgumentError(
`Meta information about frame ${frame} can't be received from the server`,
);
throw new ArgumentError(`Meta information about frame ${frame} can't be received from the server`);
} else {
size = meta.frames[frame];
}
} else {
throw new DataError(
`Invalid mode is specified ${mode}`,
);
throw new DataError(`Invalid mode is specified ${mode}`);
}
return size;
}
@ -377,23 +374,28 @@
decodeForward: false,
});
frameData.data().then(() => {
if (!(chunkIdx in this._requestedChunks)
|| !this._requestedChunks[chunkIdx].requestedFrames.has(requestedFrame)) {
reject(chunkIdx);
} else {
this._requestedChunks[chunkIdx].requestedFrames.delete(requestedFrame);
this._requestedChunks[chunkIdx].buffer[requestedFrame] = frameData;
if (this._requestedChunks[chunkIdx].requestedFrames.size === 0) {
const bufferedframes = Object.keys(
this._requestedChunks[chunkIdx].buffer,
).map((f) => +f);
this._requestedChunks[chunkIdx].resolve(new Set(bufferedframes));
frameData
.data()
.then(() => {
if (
!(chunkIdx in this._requestedChunks) ||
!this._requestedChunks[chunkIdx].requestedFrames.has(requestedFrame)
) {
reject(chunkIdx);
} else {
this._requestedChunks[chunkIdx].requestedFrames.delete(requestedFrame);
this._requestedChunks[chunkIdx].buffer[requestedFrame] = frameData;
if (this._requestedChunks[chunkIdx].requestedFrames.size === 0) {
const bufferedframes = Object.keys(this._requestedChunks[chunkIdx].buffer).map(
(f) => +f,
);
this._requestedChunks[chunkIdx].resolve(new Set(bufferedframes));
}
}
}
}).catch(() => {
reject(chunkIdx);
});
})
.catch(() => {
reject(chunkIdx);
});
}
});
}
@ -455,7 +457,7 @@
await this.fillBuffer(start, step, count);
this._activeFillBufferRequest = false;
} catch (error) {
if (typeof (error) === 'number' && error in this._requestedChunks) {
if (typeof error === 'number' && error in this._requestedChunks) {
this._activeFillBufferRequest = false;
}
throw error;
@ -465,8 +467,7 @@
async require(frameNumber, taskID, fillBuffer, frameStep) {
for (const frame in this._buffer) {
if (frame < frameNumber
|| frame >= frameNumber + this._size * frameStep) {
if (frame < frameNumber || frame >= frameNumber + this._size * frameStep) {
delete this._buffer[frame];
}
}
@ -486,9 +487,12 @@
frame = this._buffer[frameNumber];
delete this._buffer[frameNumber];
const cachedFrames = this.cachedFrames();
if (fillBuffer && !this._activeFillBufferRequest
&& this._size > this._chunkSize
&& cachedFrames.length < (this._size * 3) / 4) {
if (
fillBuffer &&
!this._activeFillBufferRequest &&
this._size > this._chunkSize &&
cachedFrames.length < (this._size * 3) / 4
) {
const maxFrame = cachedFrames ? Math.max(...cachedFrames) : frameNumber;
if (maxFrame < this._stopFrame) {
this.makeFillRequest(maxFrame + 1, frameStep).catch((e) => {
@ -512,8 +516,10 @@
clear() {
for (const chunkIdx in this._requestedChunks) {
if (Object.prototype.hasOwnProperty.call(this._requestedChunks, chunkIdx)
&& this._requestedChunks[chunkIdx].reject) {
if (
Object.prototype.hasOwnProperty.call(this._requestedChunks, chunkIdx) &&
this._requestedChunks[chunkIdx].reject
) {
this._requestedChunks[chunkIdx].reject('not needed');
}
}
@ -530,38 +536,39 @@
async function getPreview(taskID) {
return new Promise((resolve, reject) => {
// Just go to server and get preview (no any cache)
serverProxy.frames.getPreview(taskID).then((result) => {
if (isNode) {
resolve(global.Buffer.from(result, 'binary').toString('base64'));
} else if (isBrowser) {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(result);
}
}).catch((error) => {
reject(error);
});
serverProxy.frames
.getPreview(taskID)
.then((result) => {
if (isNode) {
// eslint-disable-next-line no-undef
resolve(global.Buffer.from(result, 'binary').toString('base64'));
} else if (isBrowser) {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(result);
}
})
.catch((error) => {
reject(error);
});
});
}
async function getFrame(taskID, chunkSize, chunkType, mode, frame,
startFrame, stopFrame, isPlaying, step) {
async function getFrame(taskID, chunkSize, chunkType, mode, frame, startFrame, stopFrame, isPlaying, step) {
if (!(taskID in frameDataCache)) {
const blockType = chunkType === 'video' ? cvatData.BlockType.MP4VIDEO
: cvatData.BlockType.ARCHIVE;
const blockType = chunkType === 'video' ? cvatData.BlockType.MP4VIDEO : cvatData.BlockType.ARCHIVE;
const meta = await serverProxy.frames.getMeta(taskID);
const mean = meta.frames.reduce((a, b) => a + b.width * b.height, 0)
/ meta.frames.length;
const stdDev = Math.sqrt(meta.frames.map(
(x) => Math.pow(x.width * x.height - mean, 2),
).reduce((a, b) => a + b) / meta.frames.length);
const mean = meta.frames.reduce((a, b) => a + b.width * b.height, 0) / meta.frames.length;
const stdDev = Math.sqrt(
meta.frames.map((x) => Math.pow(x.width * x.height - mean, 2)).reduce((a, b) => a + b) /
meta.frames.length,
);
// limit of decoded frames cache by 2GB
const decodedBlocksCacheSize = Math.floor(2147483648 / (mean + stdDev) / 4 / chunkSize)
|| 1;
const decodedBlocksCacheSize = Math.floor(2147483648 / (mean + stdDev) / 4 / chunkSize) || 1;
frameDataCache[taskID] = {
meta,
@ -570,8 +577,11 @@
startFrame,
stopFrame,
provider: new cvatData.FrameProvider(
blockType, chunkSize, Math.max(decodedBlocksCacheSize, 9),
decodedBlocksCacheSize, 1,
blockType,
chunkSize,
Math.max(decodedBlocksCacheSize, 9),
decodedBlocksCacheSize,
1,
),
frameBuffer: new FrameBuffer(
Math.min(180, decodedBlocksCacheSize * chunkSize),

@ -1,23 +1,16 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const {
AttributeType,
} = require('./enums');
const { AttributeType } = require('./enums');
const { ArgumentError } = require('./exceptions');
/**
* Class representing an attribute
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing an attribute
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class Attribute {
constructor(initialData) {
const data = {
@ -42,73 +35,74 @@
}
if (!Object.values(AttributeType).includes(data.input_type)) {
throw new ArgumentError(
`Got invalid attribute type ${data.input_type}`,
);
throw new ArgumentError(`Got invalid attribute type ${data.input_type}`);
}
Object.defineProperties(this, Object.freeze({
/**
* @name id
* @type {integer}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
id: {
get: () => data.id,
},
/**
* @name defaultValue
* @type {(string|integer|boolean)}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
defaultValue: {
get: () => data.default_value,
},
/**
* @name inputType
* @type {module:API.cvat.enums.AttributeType}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
inputType: {
get: () => data.input_type,
},
/**
* @name mutable
* @type {boolean}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
mutable: {
get: () => data.mutable,
},
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
name: {
get: () => data.name,
},
/**
* @name values
* @type {(string[]|integer[]|boolean[])}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
values: {
get: () => [...data.values],
},
}));
Object.defineProperties(
this,
Object.freeze({
/**
* @name id
* @type {integer}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
id: {
get: () => data.id,
},
/**
* @name defaultValue
* @type {(string|integer|boolean)}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
defaultValue: {
get: () => data.default_value,
},
/**
* @name inputType
* @type {module:API.cvat.enums.AttributeType}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
inputType: {
get: () => data.input_type,
},
/**
* @name mutable
* @type {boolean}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
mutable: {
get: () => data.mutable,
},
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
name: {
get: () => data.name,
},
/**
* @name values
* @type {(string[]|integer[]|boolean[])}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
values: {
get: () => [...data.values],
},
}),
);
}
toJSON() {
@ -120,7 +114,7 @@
values: this.values,
};
if (typeof (this.id) !== 'undefined') {
if (typeof this.id !== 'undefined') {
object.id = this.id;
}
@ -129,10 +123,10 @@
}
/**
* Class representing a label
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing a label
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class Label {
constructor(initialData) {
const data = {
@ -151,62 +145,67 @@
data.attributes = [];
if (Object.prototype.hasOwnProperty.call(initialData, 'attributes')
&& Array.isArray(initialData.attributes)) {
if (
Object.prototype.hasOwnProperty.call(initialData, 'attributes') &&
Array.isArray(initialData.attributes)
) {
for (const attrData of initialData.attributes) {
data.attributes.push(new Attribute(attrData));
}
}
Object.defineProperties(this, Object.freeze({
/**
* @name id
* @type {integer}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
id: {
get: () => data.id,
},
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
name: {
get: () => data.name,
},
/**
* @name color
* @type {string}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
color: {
get: () => data.color,
set: (color) => {
if (typeof color === 'string' && color.match(/^#[0-9a-f]{6}$|^$/)) {
data.color = color;
} else {
throw new ArgumentError('Trying to set wrong color format');
}
Object.defineProperties(
this,
Object.freeze({
/**
* @name id
* @type {integer}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
id: {
get: () => data.id,
},
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
name: {
get: () => data.name,
},
/**
* @name color
* @type {string}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
color: {
get: () => data.color,
set: (color) => {
if (typeof color === 'string' && color.match(/^#[0-9a-f]{6}$|^$/)) {
data.color = color;
} else {
throw new ArgumentError('Trying to set wrong color format');
}
},
},
/**
* @name attributes
* @type {module:API.cvat.classes.Attribute[]}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
attributes: {
get: () => [...data.attributes],
},
},
/**
* @name attributes
* @type {module:API.cvat.classes.Attribute[]}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
attributes: {
get: () => [...data.attributes],
},
}));
}),
);
}
toJSON() {
@ -216,7 +215,7 @@
color: this.color,
};
if (typeof (this.id) !== 'undefined') {
if (typeof this.id !== 'undefined') {
object.id = this.id;
}

@ -1,11 +1,6 @@
/*
* Copyright (C) 2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const serverProxy = require('./server-proxy');
const { ArgumentError } = require('./exceptions');
@ -28,10 +23,12 @@ class LambdaManager {
const models = [];
for (const model of result) {
models.push(new MLModel({
...model,
type: model.kind,
}));
models.push(
new MLModel({
...model,
type: model.kind,
}),
);
}
this.cachedList = models;
@ -41,20 +38,18 @@ class LambdaManager {
async run(task, model, args) {
if (!(task instanceof Task)) {
throw new ArgumentError(
`Argument task is expected to be an instance of Task class, but got ${typeof (task)}`,
`Argument task is expected to be an instance of Task class, but got ${typeof task}`,
);
}
if (!(model instanceof MLModel)) {
throw new ArgumentError(
`Argument model is expected to be an instance of MLModel class, but got ${typeof (model)}`,
`Argument model is expected to be an instance of MLModel class, but got ${typeof model}`,
);
}
if (args && typeof (args) !== 'object') {
throw new ArgumentError(
`Argument args is expected to be an object, but got ${typeof (model)}`,
);
if (args && typeof args !== 'object') {
throw new ArgumentError(`Argument args is expected to be an object, but got ${typeof model}`);
}
const body = args;
@ -78,7 +73,7 @@ class LambdaManager {
}
async cancel(requestID) {
if (typeof (requestID) !== 'string') {
if (typeof requestID !== 'string') {
throw new ArgumentError(`Request id argument is required to be a string. But got ${requestID}`);
}
@ -108,7 +103,11 @@ class LambdaManager {
delete this.listening[requestID];
}
} catch (error) {
onUpdate(RQStatus.UNKNOWN, 0, `Could not get a status of the request ${requestID}. ${error.toString()}`);
onUpdate(
RQStatus.UNKNOWN,
0,
`Could not get a status of the request ${requestID}. ${error.toString()}`,
);
}
};

@ -2,20 +2,16 @@
//
// SPDX-License-Identifier: MIT
/* global
require:false
*/
const { detect } = require('detect-browser');
const PluginRegistry = require('./plugins');
const { ArgumentError } = require('./exceptions');
const { LogType } = require('./enums');
/**
* Class representing a single log
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing a single log
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class Log {
constructor(logType, payload) {
this.onCloseCallback = null;
@ -30,7 +26,7 @@ class Log {
}
validatePayload() {
if (typeof (this.payload) !== 'object') {
if (typeof this.payload !== 'object') {
throw new ArgumentError('Payload must be an object');
}
@ -63,22 +59,21 @@ class Log {
}
/**
* Method saves a durable log in a storage <br>
* Note then you can call close() multiple times <br>
* Log duration will be computed based on the latest call <br>
* All payloads will be shallowly combined (all top level properties will exist)
* @method close
* @memberof module:API.cvat.classes.Log
* @param {object} [payload] part of payload can be added when close a log
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
* Method saves a durable log in a storage <br>
* Note then you can call close() multiple times <br>
* Log duration will be computed based on the latest call <br>
* All payloads will be shallowly combined (all top level properties will exist)
* @method close
* @memberof module:API.cvat.classes.Log
* @param {object} [payload] part of payload can be added when close a log
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async close(payload = {}) {
const result = await PluginRegistry
.apiWrapper.call(this, Log.prototype.close, payload);
const result = await PluginRegistry.apiWrapper.call(this, Log.prototype.close, payload);
return result;
}
}
@ -96,8 +91,7 @@ class LogWithCount extends Log {
validatePayload() {
Log.prototype.validatePayload.call(this);
if (!Number.isInteger(this.payload.count) || this.payload.count < 1) {
const message = `The field "count" is required for "${this.type}" log`
+ 'It must be a positive integer';
const message = `The field "count" is required for "${this.type}" log. It must be a positive integer`;
throw new ArgumentError(message);
}
}
@ -148,12 +142,14 @@ class LogWithWorkingTime extends Log {
validatePayload() {
Log.prototype.validatePayload.call(this);
if (!('working_time' in this.payload)
|| !typeof (this.payload.working_time) === 'number'
|| this.payload.working_time < 0
if (
!('working_time' in this.payload) ||
!typeof this.payload.working_time === 'number' ||
this.payload.working_time < 0
) {
const message = `The field "working_time" is required for ${this.type} log. `
+ 'It must be a number not less than 0';
const message = `
The field "working_time" is required for ${this.type} log. It must be a number not less than 0
`;
throw new ArgumentError(message);
}
}
@ -163,33 +159,28 @@ class LogWithExceptionInfo extends Log {
validatePayload() {
Log.prototype.validatePayload.call(this);
if (typeof (this.payload.message) !== 'string') {
const message = `The field "message" is required for ${this.type} log. `
+ 'It must be a string';
if (typeof this.payload.message !== 'string') {
const message = `The field "message" is required for ${this.type} log. It must be a string`;
throw new ArgumentError(message);
}
if (typeof (this.payload.filename) !== 'string') {
const message = `The field "filename" is required for ${this.type} log. `
+ 'It must be a string';
if (typeof this.payload.filename !== 'string') {
const message = `The field "filename" is required for ${this.type} log. It must be a string`;
throw new ArgumentError(message);
}
if (typeof (this.payload.line) !== 'number') {
const message = `The field "line" is required for ${this.type} log. `
+ 'It must be a number';
if (typeof this.payload.line !== 'number') {
const message = `The field "line" is required for ${this.type} log. It must be a number`;
throw new ArgumentError(message);
}
if (typeof (this.payload.column) !== 'number') {
const message = `The field "column" is required for ${this.type} log. `
+ 'It must be a number';
if (typeof this.payload.column !== 'number') {
const message = `The field "column" is required for ${this.type} log. It must be a number`;
throw new ArgumentError(message);
}
if (typeof (this.payload.stack) !== 'string') {
const message = `The field "stack" is required for ${this.type} log. `
+ 'It must be a string';
if (typeof this.payload.stack !== 'string') {
const message = `The field "stack" is required for ${this.type} log. It must be a string`;
throw new ArgumentError(message);
}
}
@ -222,8 +213,11 @@ class LogWithExceptionInfo extends Log {
function logFactory(logType, payload) {
const logsWithCount = [
LogType.deleteObject, LogType.mergeObjects, LogType.copyObject,
LogType.undoAction, LogType.redoAction,
LogType.deleteObject,
LogType.mergeObjects,
LogType.copyObject,
LogType.undoAction,
LogType.redoAction,
];
if (logsWithCount.includes(logType)) {

@ -2,10 +2,6 @@
//
// SPDX-License-Identifier: MIT
/* global
require:false
*/
const PluginRegistry = require('./plugins');
const serverProxy = require('./server-proxy');
const logFactory = require('./log');
@ -41,8 +37,10 @@ class LoggerStorage {
this.ignoreRules[LogType.changeAttribute] = {
lastLog: null,
ignore(previousLog, currentPayload) {
return currentPayload.object_id === previousLog.payload.object_id
&& currentPayload.id === previousLog.payload.id;
return (
currentPayload.object_id === previousLog.payload.object_id &&
currentPayload.id === previousLog.payload.id
);
},
};
}
@ -57,32 +55,28 @@ class LoggerStorage {
}
async configure(isActiveChecker, activityHelper) {
const result = await PluginRegistry
.apiWrapper.call(
this, LoggerStorage.prototype.configure,
isActiveChecker, activityHelper,
);
const result = await PluginRegistry.apiWrapper.call(
this,
LoggerStorage.prototype.configure,
isActiveChecker,
activityHelper,
);
return result;
}
async log(logType, payload = {}, wait = false) {
const result = await PluginRegistry
.apiWrapper.call(this, LoggerStorage.prototype.log, logType, payload, wait);
const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.log, logType, payload, wait);
return result;
}
async save() {
const result = await PluginRegistry
.apiWrapper.call(this, LoggerStorage.prototype.save);
const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.save);
return result;
}
}
LoggerStorage.prototype.configure.implementation = function (
isActiveChecker,
userActivityCallback,
) {
if (typeof (isActiveChecker) !== 'function') {
LoggerStorage.prototype.configure.implementation = function (isActiveChecker, userActivityCallback) {
if (typeof isActiveChecker !== 'function') {
throw new ArgumentError('isActiveChecker argument must be callable');
}
@ -95,11 +89,11 @@ LoggerStorage.prototype.configure.implementation = function (
};
LoggerStorage.prototype.log.implementation = function (logType, payload, wait) {
if (typeof (payload) !== 'object') {
if (typeof payload !== 'object') {
throw new ArgumentError('Payload must be an object');
}
if (typeof (wait) !== 'boolean') {
if (typeof wait !== 'boolean') {
throw new ArgumentError('Payload must be an object');
}

@ -1,12 +1,11 @@
/*
* Copyright (C) 2019-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/**
* Class representing a machine learning model
* @memberof module:API.cvat.classes
*/
* Class representing a machine learning model
* @memberof module:API.cvat.classes
*/
class MLModel {
constructor(data) {
this._id = data.id;
@ -25,7 +24,7 @@ class MLModel {
/**
* @returns {string}
* @readonly
*/
*/
get id() {
return this._id;
}
@ -33,7 +32,7 @@ class MLModel {
/**
* @returns {string}
* @readonly
*/
*/
get name() {
return this._name;
}
@ -41,7 +40,7 @@ class MLModel {
/**
* @returns {string[]}
* @readonly
*/
*/
get labels() {
if (Array.isArray(this._labels)) {
return [...this._labels];
@ -53,7 +52,7 @@ class MLModel {
/**
* @returns {string}
* @readonly
*/
*/
get framework() {
return this._framework;
}
@ -61,7 +60,7 @@ class MLModel {
/**
* @returns {string}
* @readonly
*/
*/
get description() {
return this._description;
}
@ -69,7 +68,7 @@ class MLModel {
/**
* @returns {module:API.cvat.enums.ModelType}
* @readonly
*/
*/
get type() {
return this._type;
}
@ -77,7 +76,7 @@ class MLModel {
/**
* @returns {object}
* @readonly
*/
*/
get params() {
return {
canvas: { ...this._params.canvas },

@ -1,31 +1,26 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const { Source } = require('./enums');
/* global
require:false
*/
(() => {
const PluginRegistry = require('./plugins');
const { ArgumentError } = require('./exceptions');
/**
* Class representing a state of an object on a specific frame
* @memberof module:API.cvat.classes
*/
* Class representing a state of an object on a specific frame
* @memberof module:API.cvat.classes
*/
class ObjectState {
/**
* @param {Object} serialized - is an dictionary which contains
* initial information about an ObjectState;
* </br> Necessary fields: objectType, shapeType, frame, updated, group
* </br> Optional fields: keyframes, clientID, serverID
* </br> Optional fields which can be set later: points, zOrder, outside,
* occluded, hidden, attributes, lock, label, color, keyframe, source
*/
* @param {Object} serialized - is an dictionary which contains
* initial information about an ObjectState;
* </br> Necessary fields: objectType, shapeType, frame, updated, group
* </br> Optional fields: keyframes, clientID, serverID
* </br> Optional fields which can be set later: points, zOrder, outside,
* occluded, hidden, attributes, lock, label, color, keyframe, source
*/
constructor(serialized) {
const data = {
label: null,
@ -77,282 +72,289 @@ const { Source } = require('./enums');
writable: false,
});
Object.defineProperties(this, Object.freeze({
// Internal property. We don't need document it.
updateFlags: {
get: () => data.updateFlags,
},
frame: {
/**
* @name frame
* @type {integer}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.frame,
},
objectType: {
/**
* @name objectType
* @type {module:API.cvat.enums.ObjectType}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.objectType,
},
shapeType: {
/**
* @name shapeType
* @type {module:API.cvat.enums.ObjectShape}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.shapeType,
},
source: {
/**
* @name source
* @type {module:API.cvat.enums.Source}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.source,
},
clientID: {
/**
* @name clientID
* @type {integer}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.clientID,
},
serverID: {
/**
* @name serverID
* @type {integer}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.serverID,
},
label: {
/**
* @name shape
* @type {module:API.cvat.classes.Label}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.label,
set: (labelInstance) => {
data.updateFlags.label = true;
data.label = labelInstance;
Object.defineProperties(
this,
Object.freeze({
// Internal property. We don't need document it.
updateFlags: {
get: () => data.updateFlags,
},
},
color: {
/**
* @name color
* @type {string}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.color,
set: (color) => {
data.updateFlags.color = true;
data.color = color;
frame: {
/**
* @name frame
* @type {integer}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.frame,
},
},
hidden: {
/**
* @name hidden
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.hidden,
set: (hidden) => {
data.updateFlags.hidden = true;
data.hidden = hidden;
objectType: {
/**
* @name objectType
* @type {module:API.cvat.enums.ObjectType}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.objectType,
},
},
points: {
/**
* @name points
* @type {number[]}
* @memberof module:API.cvat.classes.ObjectState
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
*/
get: () => data.points,
set: (points) => {
if (Array.isArray(points)) {
data.updateFlags.points = true;
data.points = [...points];
} else {
throw new ArgumentError(
'Points are expected to be an array '
+ `but got ${typeof (points) === 'object'
? points.constructor.name : typeof (points)}`,
);
}
shapeType: {
/**
* @name shapeType
* @type {module:API.cvat.enums.ObjectShape}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.shapeType,
},
},
group: {
/**
* Object with short group info { color, id }
* @name group
* @type {object}
* @memberof module:API.cvat.classes.ObjectState
* @instance
* @readonly
*/
get: () => data.group,
},
zOrder: {
/**
* @name zOrder
* @type {integer | null}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.zOrder,
set: (zOrder) => {
data.updateFlags.zOrder = true;
data.zOrder = zOrder;
source: {
/**
* @name source
* @type {module:API.cvat.enums.Source}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.source,
},
},
outside: {
/**
* @name outside
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.outside,
set: (outside) => {
data.updateFlags.outside = true;
data.outside = outside;
clientID: {
/**
* @name clientID
* @type {integer}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.clientID,
},
},
keyframe: {
/**
* @name keyframe
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.keyframe,
set: (keyframe) => {
data.updateFlags.keyframe = true;
data.keyframe = keyframe;
serverID: {
/**
* @name serverID
* @type {integer}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => data.serverID,
},
},
keyframes: {
/**
* Object of keyframes { first, prev, next, last }
* @name keyframes
* @type {object | null}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => {
if (typeof (data.keyframes) === 'object') {
return { ...data.keyframes };
}
label: {
/**
* @name shape
* @type {module:API.cvat.classes.Label}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.label,
set: (labelInstance) => {
data.updateFlags.label = true;
data.label = labelInstance;
},
},
color: {
/**
* @name color
* @type {string}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.color,
set: (color) => {
data.updateFlags.color = true;
data.color = color;
},
},
hidden: {
/**
* @name hidden
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.hidden,
set: (hidden) => {
data.updateFlags.hidden = true;
data.hidden = hidden;
},
},
points: {
/**
* @name points
* @type {number[]}
* @memberof module:API.cvat.classes.ObjectState
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
*/
get: () => data.points,
set: (points) => {
if (Array.isArray(points)) {
data.updateFlags.points = true;
data.points = [...points];
} else {
throw new ArgumentError(
'Points are expected to be an array ' +
`but got ${
typeof points === 'object' ? points.constructor.name : typeof points
}`,
);
}
},
},
group: {
/**
* Object with short group info { color, id }
* @name group
* @type {object}
* @memberof module:API.cvat.classes.ObjectState
* @instance
* @readonly
*/
get: () => data.group,
},
zOrder: {
/**
* @name zOrder
* @type {integer | null}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.zOrder,
set: (zOrder) => {
data.updateFlags.zOrder = true;
data.zOrder = zOrder;
},
},
outside: {
/**
* @name outside
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.outside,
set: (outside) => {
data.updateFlags.outside = true;
data.outside = outside;
},
},
keyframe: {
/**
* @name keyframe
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.keyframe,
set: (keyframe) => {
data.updateFlags.keyframe = true;
data.keyframe = keyframe;
},
},
keyframes: {
/**
* Object of keyframes { first, prev, next, last }
* @name keyframes
* @type {object | null}
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
*/
get: () => {
if (typeof data.keyframes === 'object') {
return { ...data.keyframes };
}
return null;
return null;
},
},
},
occluded: {
/**
* @name occluded
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.occluded,
set: (occluded) => {
data.updateFlags.occluded = true;
data.occluded = occluded;
occluded: {
/**
* @name occluded
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.occluded,
set: (occluded) => {
data.updateFlags.occluded = true;
data.occluded = occluded;
},
},
},
lock: {
/**
* @name lock
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.lock,
set: (lock) => {
data.updateFlags.lock = true;
data.lock = lock;
lock: {
/**
* @name lock
* @type {boolean}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => data.lock,
set: (lock) => {
data.updateFlags.lock = true;
data.lock = lock;
},
},
},
pinned: {
/**
* @name pinned
* @type {boolean | null}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => {
if (typeof (data.pinned) === 'boolean') {
return data.pinned;
}
pinned: {
/**
* @name pinned
* @type {boolean | null}
* @memberof module:API.cvat.classes.ObjectState
* @instance
*/
get: () => {
if (typeof data.pinned === 'boolean') {
return data.pinned;
}
return null;
return null;
},
set: (pinned) => {
data.updateFlags.pinned = true;
data.pinned = pinned;
},
},
set: (pinned) => {
data.updateFlags.pinned = true;
data.pinned = pinned;
updated: {
/**
* Timestamp of the latest updated of the object
* @name updated
* @type {number}
* @memberof module:API.cvat.classes.ObjectState
* @instance
* @readonly
*/
get: () => data.updated,
},
},
updated: {
/**
* Timestamp of the latest updated of the object
* @name updated
* @type {number}
* @memberof module:API.cvat.classes.ObjectState
* @instance
* @readonly
*/
get: () => data.updated,
},
attributes: {
/**
* Object is id:value pairs where "id" is an integer
* attribute identifier and "value" is an attribute value
* @name attributes
* @type {Object}
* @memberof module:API.cvat.classes.ObjectState
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
*/
get: () => data.attributes,
set: (attributes) => {
if (typeof (attributes) !== 'object') {
throw new ArgumentError(
'Attributes are expected to be an object '
+ `but got ${typeof (attributes) === 'object'
? attributes.constructor.name : typeof (attributes)}`,
);
}
attributes: {
/**
* Object is id:value pairs where "id" is an integer
* attribute identifier and "value" is an attribute value
* @name attributes
* @type {Object}
* @memberof module:API.cvat.classes.ObjectState
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
*/
get: () => data.attributes,
set: (attributes) => {
if (typeof attributes !== 'object') {
throw new ArgumentError(
'Attributes are expected to be an object ' +
`but got ${
typeof attributes === 'object'
? attributes.constructor.name
: typeof attributes
}`,
);
}
for (const attrID of Object.keys(attributes)) {
data.updateFlags.attributes = true;
data.attributes[attrID] = attributes[attrID];
}
for (const attrID of Object.keys(attributes)) {
data.updateFlags.attributes = true;
data.attributes[attrID] = attributes[attrID];
}
},
},
},
}));
}),
);
this.label = serialized.label;
this.lock = serialized.lock;
@ -360,31 +362,31 @@ const { Source } = require('./enums');
if ([Source.MANUAL, Source.AUTO].includes(serialized.source)) {
data.source = serialized.source;
}
if (typeof (serialized.zOrder) === 'number') {
if (typeof serialized.zOrder === 'number') {
this.zOrder = serialized.zOrder;
}
if (typeof (serialized.occluded) === 'boolean') {
if (typeof serialized.occluded === 'boolean') {
this.occluded = serialized.occluded;
}
if (typeof (serialized.outside) === 'boolean') {
if (typeof serialized.outside === 'boolean') {
this.outside = serialized.outside;
}
if (typeof (serialized.keyframe) === 'boolean') {
if (typeof serialized.keyframe === 'boolean') {
this.keyframe = serialized.keyframe;
}
if (typeof (serialized.pinned) === 'boolean') {
if (typeof serialized.pinned === 'boolean') {
this.pinned = serialized.pinned;
}
if (typeof (serialized.hidden) === 'boolean') {
if (typeof serialized.hidden === 'boolean') {
this.hidden = serialized.hidden;
}
if (typeof (serialized.color) === 'string') {
if (typeof serialized.color === 'string') {
this.color = serialized.color;
}
if (Array.isArray(serialized.points)) {
this.points = serialized.points;
}
if (typeof (serialized.attributes) === 'object') {
if (typeof serialized.attributes === 'object') {
this.attributes = serialized.attributes;
}
@ -392,38 +394,36 @@ const { Source } = require('./enums');
}
/**
* Method saves/updates an object state in a collection
* @method save
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @returns {module:API.cvat.classes.ObjectState} updated state of an object
*/
* Method saves/updates an object state in a collection
* @method save
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @returns {module:API.cvat.classes.ObjectState} updated state of an object
*/
async save() {
const result = await PluginRegistry
.apiWrapper.call(this, ObjectState.prototype.save);
const result = await PluginRegistry.apiWrapper.call(this, ObjectState.prototype.save);
return result;
}
/**
* Method deletes an object from a collection
* @method delete
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
* @param {integer} frame current frame number
* @param {boolean} [force=false] delete object even if it is locked
* @async
* @returns {boolean} true if object has been deleted
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
* Method deletes an object from a collection
* @method delete
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
* @param {integer} frame current frame number
* @param {boolean} [force=false] delete object even if it is locked
* @async
* @returns {boolean} true if object has been deleted
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async delete(frame, force = false) {
const result = await PluginRegistry
.apiWrapper.call(this, ObjectState.prototype.delete, frame, force);
const result = await PluginRegistry.apiWrapper.call(this, ObjectState.prototype.delete, frame, force);
return result;
}
}

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const { PluginError } = require('./exceptions');
@ -16,8 +11,7 @@
// I have to optimize the wrapper
const pluginList = await PluginRegistry.list();
for (const plugin of pluginList) {
const pluginDecorators = plugin.functions
.filter((obj) => obj.callback === wrappedFunc)[0];
const pluginDecorators = plugin.functions.filter((obj) => obj.callback === wrappedFunc)[0];
if (pluginDecorators && pluginDecorators.enter) {
try {
await pluginDecorators.enter.call(this, plugin, ...args);
@ -34,8 +28,7 @@
let result = await wrappedFunc.implementation.call(this, ...args);
for (const plugin of pluginList) {
const pluginDecorators = plugin.functions
.filter((obj) => obj.callback === wrappedFunc)[0];
const pluginDecorators = plugin.functions.filter((obj) => obj.callback === wrappedFunc)[0];
if (pluginDecorators && pluginDecorators.leave) {
try {
result = await pluginDecorators.leave.call(this, plugin, result, ...args);
@ -56,15 +49,15 @@
static async register(plug) {
const functions = [];
if (typeof (plug) !== 'object') {
throw new PluginError(`Plugin should be an object, but got "${typeof (plug)}"`);
if (typeof plug !== 'object') {
throw new PluginError(`Plugin should be an object, but got "${typeof plug}"`);
}
if (!('name' in plug) || typeof (plug.name) !== 'string') {
if (!('name' in plug) || typeof plug.name !== 'string') {
throw new PluginError('Plugin must contain a "name" field and it must be a string');
}
if (!('description' in plug) || typeof (plug.description) !== 'string') {
if (!('description' in plug) || typeof plug.description !== 'string') {
throw new PluginError('Plugin must contain a "description" field and it must be a string');
}
@ -76,13 +69,15 @@
const decorator = {};
for (const key in plugin) {
if (Object.prototype.hasOwnProperty.call(plugin, key)) {
if (typeof (plugin[key]) === 'object') {
if (typeof plugin[key] === 'object') {
if (Object.prototype.hasOwnProperty.call(api, key)) {
traverse(plugin[key], api[key]);
}
} else if (['enter', 'leave'].includes(key)
&& typeof (api) === 'function'
&& typeof (plugin[key] === 'function')) {
} else if (
['enter', 'leave'].includes(key) &&
typeof api === 'function' &&
typeof (plugin[key] === 'function')
) {
decorator.callback = api;
decorator[key] = plugin[key];
}
@ -92,9 +87,9 @@
if (Object.keys(decorator).length) {
functions.push(decorator);
}
}(plug, {
})(plug, {
cvat: this,
}));
});
Object.defineProperty(plug, 'functions', {
value: functions,

@ -1,17 +1,10 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
const FormData = require('form-data');
const {
ServerError,
} = require('./exceptions');
const { ServerError } = require('./exceptions');
const store = require('store');
const config = require('./config');
const DownloadWorker = require('./download.worker');
@ -71,12 +64,15 @@
});
}
Object.defineProperties(this, Object.freeze({
get: {
value: get,
writable: false,
},
}));
Object.defineProperties(
this,
Object.freeze({
get: {
value: get,
writable: false,
},
}),
);
}
}
@ -154,7 +150,6 @@
return response.data;
}
async function userAgreements() {
const { backendAPI } = config;
let response = null;
@ -169,15 +164,7 @@
return response.data;
}
async function register(
username,
firstName,
lastName,
email,
password1,
password2,
confirmations,
) {
async function register(username, firstName, lastName, email, password1, password2, confirmations) {
let response = null;
try {
const data = JSON.stringify({
@ -203,20 +190,19 @@
}
async function login(username, password) {
const authenticationData = ([
const authenticationData = [
`${encodeURIComponent('username')}=${encodeURIComponent(username)}`,
`${encodeURIComponent('password')}=${encodeURIComponent(password)}`,
]).join('&').replace(/%20/g, '+');
]
.join('&')
.replace(/%20/g, '+');
Axios.defaults.headers.common.Authorization = '';
let authenticationResponse = null;
try {
authenticationResponse = await Axios.post(
`${config.backendAPI}/auth/login`,
authenticationData, {
proxy: config.proxy,
},
);
authenticationResponse = await Axios.post(`${config.backendAPI}/auth/login`, authenticationData, {
proxy: config.proxy,
});
} catch (errorData) {
throw generateError(errorData);
}
@ -315,10 +301,12 @@
async function serverRequest(url, data) {
try {
return (await Axios({
url,
...data,
})).data;
return (
await Axios({
url,
...data,
})
).data;
} catch (errorData) {
throw generateError(errorData);
}
@ -372,10 +360,9 @@
return new Promise((resolve, reject) => {
async function request() {
try {
const response = await Axios
.get(`${url}`, {
proxy: config.proxy,
});
const response = await Axios.get(`${url}`, {
proxy: config.proxy,
});
if (response.status === 202) {
setTimeout(request, 3000);
} else {
@ -409,21 +396,22 @@
} else if (response.data.state === 'Failed') {
// If request has been successful, but task hasn't been created
// Then passed data is wrong and we can pass code 400
const message = 'Could not create the task on the server. '
+ `${response.data.message}.`;
const message = `
Could not create the task on the server. ${response.data.message}.
`;
reject(new ServerError(message, 400));
} else {
// If server has another status, it is unexpected
// Therefore it is server error and we can pass code 500
reject(new ServerError(
`Unknown task state has been received: ${response.data.state}`,
500,
));
reject(
new ServerError(
`Unknown task state has been received: ${response.data.state}`,
500,
),
);
}
} catch (errorData) {
reject(
generateError(errorData),
);
reject(generateError(errorData));
}
}
@ -559,10 +547,7 @@
});
} catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code;
throw new ServerError(
`Could not get preview frame for the task ${tid} from the server`,
code,
);
throw new ServerError(`Could not get preview frame for the task ${tid} from the server`, code);
}
return response.data;
@ -656,10 +641,13 @@
return new Promise((resolve, reject) => {
async function request() {
try {
const response = await Axios
.put(`${backendAPI}/${session}s/${id}/annotations?format=${format}`, annotationData, {
const response = await Axios.put(
`${backendAPI}/${session}s/${id}/annotations?format=${format}`,
annotationData,
{
proxy: config.proxy,
});
},
);
if (response.status === 202) {
annotationData = new FormData();
setTimeout(request, 3000);
@ -690,17 +678,19 @@
async function request() {
Axios.get(`${url}`, {
proxy: config.proxy,
}).then((response) => {
if (response.status === 202) {
setTimeout(request, 3000);
} else {
query = `${query}&action=download`;
url = `${baseURL}?${query}`;
resolve(url);
}
}).catch((errorData) => {
reject(generateError(errorData));
});
})
.then((response) => {
if (response.status === 202) {
setTimeout(request, 3000);
} else {
query = `${query}&action=download`;
url = `${baseURL}?${query}`;
resolve(url);
}
})
.catch((errorData) => {
reject(generateError(errorData));
});
}
setTimeout(request);
@ -739,13 +729,12 @@
const { backendAPI } = config;
try {
const response = await Axios.post(`${backendAPI}/lambda/requests`,
JSON.stringify(body), {
proxy: config.proxy,
headers: {
'Content-Type': 'application/json',
},
});
const response = await Axios.post(`${backendAPI}/lambda/requests`, JSON.stringify(body), {
proxy: config.proxy,
headers: {
'Content-Type': 'application/json',
},
});
return response.data;
} catch (errorData) {
@ -757,13 +746,12 @@
const { backendAPI } = config;
try {
const response = await Axios.post(`${backendAPI}/lambda/functions/${funId}`,
JSON.stringify(body), {
proxy: config.proxy,
headers: {
'Content-Type': 'application/json',
},
});
const response = await Axios.post(`${backendAPI}/lambda/functions/${funId}`, JSON.stringify(body), {
proxy: config.proxy,
headers: {
'Content-Type': 'application/json',
},
});
return response.data;
} catch (errorData) {
@ -802,11 +790,9 @@
const { backendAPI } = config;
try {
await Axios.delete(
`${backendAPI}/lambda/requests/${requestId}`, {
method: 'DELETE',
},
);
await Axios.delete(`${backendAPI}/lambda/requests/${requestId}`, {
method: 'DELETE',
});
} catch (errorData) {
throw generateError(errorData);
}
@ -824,92 +810,95 @@
}
}
Object.defineProperties(this, Object.freeze({
server: {
value: Object.freeze({
about,
share,
formats,
exception,
login,
logout,
changePassword,
requestPasswordReset,
resetPassword,
authorized,
register,
request: serverRequest,
userAgreements,
installedApps,
}),
writable: false,
},
tasks: {
value: Object.freeze({
getTasks,
saveTask,
createTask,
deleteTask,
exportDataset,
}),
writable: false,
},
jobs: {
value: Object.freeze({
getJob,
saveJob,
}),
writable: false,
},
users: {
value: Object.freeze({
getUsers,
getSelf,
}),
writable: false,
},
frames: {
value: Object.freeze({
getData,
getMeta,
getPreview,
}),
writable: false,
},
annotations: {
value: Object.freeze({
updateAnnotations,
getAnnotations,
dumpAnnotations,
uploadAnnotations,
}),
writable: false,
},
logs: {
value: Object.freeze({
save: saveLogs,
}),
writable: false,
},
lambda: {
value: Object.freeze({
list: getLambdaFunctions,
status: getRequestStatus,
requests: getLambdaRequests,
run: runLambdaRequest,
call: callLambdaFunction,
cancel: cancelLambdaRequest,
}),
writable: false,
},
}));
Object.defineProperties(
this,
Object.freeze({
server: {
value: Object.freeze({
about,
share,
formats,
exception,
login,
logout,
changePassword,
requestPasswordReset,
resetPassword,
authorized,
register,
request: serverRequest,
userAgreements,
installedApps,
}),
writable: false,
},
tasks: {
value: Object.freeze({
getTasks,
saveTask,
createTask,
deleteTask,
exportDataset,
}),
writable: false,
},
jobs: {
value: Object.freeze({
getJob,
saveJob,
}),
writable: false,
},
users: {
value: Object.freeze({
getUsers,
getSelf,
}),
writable: false,
},
frames: {
value: Object.freeze({
getData,
getMeta,
getPreview,
}),
writable: false,
},
annotations: {
value: Object.freeze({
updateAnnotations,
getAnnotations,
dumpAnnotations,
uploadAnnotations,
}),
writable: false,
},
logs: {
value: Object.freeze({
save: saveLogs,
}),
writable: false,
},
lambda: {
value: Object.freeze({
list: getLambdaFunctions,
status: getRequestStatus,
requests: getLambdaRequests,
run: runLambdaRequest,
call: callLambdaFunction,
cancel: cancelLambdaRequest,
}),
writable: false,
},
}),
);
}
}

File diff suppressed because it is too large Load Diff

@ -1,97 +1,98 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
/**
* Class representing collection statistics
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing collection statistics
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class Statistics {
constructor(label, total) {
Object.defineProperties(this, Object.freeze({
/**
* Statistics by labels with a structure:
* @example
* {
* label: {
* boxes: {
* tracks: 10,
* shapes: 11,
* },
* polygons: {
* tracks: 13,
* shapes: 14,
* },
* polylines: {
* tracks: 16,
* shapes: 17,
* },
* points: {
* tracks: 19,
* shapes: 20,
* },
* cuboids: {
* tracks: 21,
* shapes: 22,
* },
* tags: 66,
* manually: 186,
* interpolated: 500,
* total: 608,
* }
* }
* @name label
* @type {Object}
* @memberof module:API.cvat.classes.Statistics
* @readonly
* @instance
*/
label: {
get: () => JSON.parse(JSON.stringify(label)),
},
/**
* Total statistics (covers all labels) with a structure:
* @example
* {
* boxes: {
* tracks: 10,
* shapes: 11,
* },
* polygons: {
* tracks: 13,
* shapes: 14,
* },
* polylines: {
* tracks: 16,
* shapes: 17,
* },
* points: {
* tracks: 19,
* shapes: 20,
* },
* cuboids: {
* tracks: 21,
* shapes: 22,
* },
* tags: 66,
* manually: 186,
* interpolated: 500,
* total: 608,
* }
* @name total
* @type {Object}
* @memberof module:API.cvat.classes.Statistics
* @readonly
* @instance
*/
total: {
get: () => JSON.parse(JSON.stringify(total)),
},
}));
Object.defineProperties(
this,
Object.freeze({
/**
* Statistics by labels with a structure:
* @example
* {
* label: {
* boxes: {
* tracks: 10,
* shapes: 11,
* },
* polygons: {
* tracks: 13,
* shapes: 14,
* },
* polylines: {
* tracks: 16,
* shapes: 17,
* },
* points: {
* tracks: 19,
* shapes: 20,
* },
* cuboids: {
* tracks: 21,
* shapes: 22,
* },
* tags: 66,
* manually: 186,
* interpolated: 500,
* total: 608,
* }
* }
* @name label
* @type {Object}
* @memberof module:API.cvat.classes.Statistics
* @readonly
* @instance
*/
label: {
get: () => JSON.parse(JSON.stringify(label)),
},
/**
* Total statistics (covers all labels) with a structure:
* @example
* {
* boxes: {
* tracks: 10,
* shapes: 11,
* },
* polygons: {
* tracks: 13,
* shapes: 14,
* },
* polylines: {
* tracks: 16,
* shapes: 17,
* },
* points: {
* tracks: 19,
* shapes: 20,
* },
* cuboids: {
* tracks: 21,
* shapes: 22,
* },
* tags: 66,
* manually: 186,
* interpolated: 500,
* total: 608,
* }
* @name total
* @type {Object}
* @memberof module:API.cvat.classes.Statistics
* @readonly
* @instance
*/
total: {
get: () => JSON.parse(JSON.stringify(total)),
},
}),
);
}
}

@ -1,14 +1,13 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
(() => {
/**
* Class representing a user
* @memberof module:API.cvat.classes
* @hideconstructor
*/
* Class representing a user
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class User {
constructor(initialData) {
const data = {
@ -27,134 +26,136 @@
};
for (const property in data) {
if (Object.prototype.hasOwnProperty.call(data, property)
&& property in initialData) {
if (Object.prototype.hasOwnProperty.call(data, property) && property in initialData) {
data[property] = initialData[property];
}
}
Object.defineProperties(this, Object.freeze({
id: {
/**
* @name id
* @type {integer}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.id,
},
username: {
/**
* @name username
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.username,
},
email: {
/**
* @name email
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.email,
},
firstName: {
/**
* @name firstName
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.first_name,
},
lastName: {
/**
* @name lastName
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.last_name,
},
groups: {
/**
* @name groups
* @type {string[]}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => JSON.parse(JSON.stringify(data.groups)),
},
lastLogin: {
/**
* @name lastLogin
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.last_login,
},
dateJoined: {
/**
* @name dateJoined
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.date_joined,
},
isStaff: {
/**
* @name isStaff
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.is_staff,
},
isSuperuser: {
/**
* @name isSuperuser
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.is_superuser,
},
isActive: {
/**
* @name isActive
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.is_active,
},
isVerified: {
/**
* @name isVerified
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => !data.email_verification_required,
},
}));
Object.defineProperties(
this,
Object.freeze({
id: {
/**
* @name id
* @type {integer}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.id,
},
username: {
/**
* @name username
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.username,
},
email: {
/**
* @name email
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.email,
},
firstName: {
/**
* @name firstName
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.first_name,
},
lastName: {
/**
* @name lastName
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.last_name,
},
groups: {
/**
* @name groups
* @type {string[]}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => JSON.parse(JSON.stringify(data.groups)),
},
lastLogin: {
/**
* @name lastLogin
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.last_login,
},
dateJoined: {
/**
* @name dateJoined
* @type {string}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.date_joined,
},
isStaff: {
/**
* @name isStaff
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.is_staff,
},
isSuperuser: {
/**
* @name isSuperuser
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.is_superuser,
},
isActive: {
/**
* @name isActive
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => data.is_active,
},
isVerified: {
/**
* @name isVerified
* @type {boolean}
* @memberof module:API.cvat.classes.User
* @readonly
* @instance
*/
get: () => !data.email_verification_required,
},
}),
);
}
}

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -48,30 +41,25 @@ describe('Feature: get annotations', () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
// Out of task
expect(task.annotations.get(500))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.get(500)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
// Out of task
expect(task.annotations.get(-1))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.get(-1)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get annotations for frame out of job', async () => {
const job = (await window.cvat.jobs.get({ jobID: 101 }))[0];
// Out of segment
expect(job.annotations.get(500))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(job.annotations.get(500)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
// Out of segment
expect(job.annotations.get(-1))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(job.annotations.get(-1)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
// TODO: Test filter (hasn't been implemented yet)
});
describe('Feature: put annotations', () => {
test('put a shape to a task', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
@ -173,8 +161,7 @@ describe('Feature: put annotations', () => {
zOrder: 0,
});
expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('put shape with bad attributes to a task', async () => {
@ -191,8 +178,7 @@ describe('Feature: put annotations', () => {
zOrder: 0,
});
expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('put shape with bad zOrder to a task', async () => {
@ -209,8 +195,7 @@ describe('Feature: put annotations', () => {
zOrder: 'bad value',
});
expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
const state1 = new window.cvat.classes.ObjectState({
frame: 1,
@ -223,8 +208,7 @@ describe('Feature: put annotations', () => {
zOrder: NaN,
});
expect(task.annotations.put([state1]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state1])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('put shape without points and with invalud points to a task', async () => {
@ -240,16 +224,13 @@ describe('Feature: put annotations', () => {
zOrder: 0,
});
await expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.DataError);
await expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.DataError);
delete state.points;
await expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.DataError);
await expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.DataError);
state.points = ['150,50 250,30'];
expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('put shape without type to a task', async () => {
@ -264,8 +245,7 @@ describe('Feature: put annotations', () => {
zOrder: 0,
});
expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('put shape without label and with bad label to a task', async () => {
@ -280,16 +260,13 @@ describe('Feature: put annotations', () => {
zOrder: 0,
});
await expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.label = 'bad label';
await expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.label = {};
await expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('put shape with bad frame to a task', async () => {
@ -305,8 +282,7 @@ describe('Feature: put annotations', () => {
zOrder: 0,
});
expect(task.annotations.put([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.put([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
@ -436,8 +412,7 @@ describe('Feature: save annotations', () => {
// have been sent to a server
const oldImplementation = serverProxy.annotations.updateAnnotations;
serverProxy.annotations.updateAnnotations = async (session, id, data, action) => {
const result = await oldImplementation
.call(serverProxy.annotations, session, id, data, action);
const result = await oldImplementation.call(serverProxy.annotations, session, id, data, action);
if (action === 'delete') {
okay = okay || (action === 'delete' && !!(data.shapes.length || data.tracks.length));
}
@ -459,10 +434,12 @@ describe('Feature: merge annotations', () => {
const annotations1 = await task.annotations.get(1);
const states = [annotations0[0], annotations1[0]];
await task.annotations.merge(states);
const merged0 = (await task.annotations.get(0))
.filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK);
const merged1 = (await task.annotations.get(1))
.filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK);
const merged0 = (await task.annotations.get(0)).filter(
(state) => state.objectType === window.cvat.enums.ObjectType.TRACK,
);
const merged1 = (await task.annotations.get(1)).filter(
(state) => state.objectType === window.cvat.enums.ObjectType.TRACK,
);
expect(merged0).toHaveLength(1);
expect(merged1).toHaveLength(1);
@ -476,10 +453,12 @@ describe('Feature: merge annotations', () => {
const annotations1 = await job.annotations.get(1);
const states = [annotations0[0], annotations1[0]];
await job.annotations.merge(states);
const merged0 = (await job.annotations.get(0))
.filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK);
const merged1 = (await job.annotations.get(1))
.filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK);
const merged0 = (await job.annotations.get(0)).filter(
(state) => state.objectType === window.cvat.enums.ObjectType.TRACK,
);
const merged1 = (await job.annotations.get(1)).filter(
(state) => state.objectType === window.cvat.enums.ObjectType.TRACK,
);
expect(merged0).toHaveLength(1);
expect(merged1).toHaveLength(1);
@ -492,8 +471,7 @@ describe('Feature: merge annotations', () => {
const annotations0 = await task.annotations.get(0);
const states = [annotations0[0], {}];
expect(task.annotations.merge(states))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.merge(states)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to merge object state which is not saved in a collection', async () => {
@ -510,8 +488,7 @@ describe('Feature: merge annotations', () => {
});
const states = [annotations0[0], state];
expect(task.annotations.merge(states))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.merge(states)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to merge with bad label', async () => {
@ -525,19 +502,18 @@ describe('Feature: merge annotations', () => {
attributes: [],
});
expect(task.annotations.merge(states))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.merge(states)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to merge with different shape types', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations0 = await task.annotations.get(0);
const annotations1 = (await task.annotations.get(1))
.filter((state) => state.shapeType === window.cvat.enums.ObjectShape.POLYGON);
const annotations1 = (await task.annotations.get(1)).filter(
(state) => state.shapeType === window.cvat.enums.ObjectShape.POLYGON,
);
const states = [annotations0[0], annotations1[0]];
expect(task.annotations.merge(states))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.merge(states)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to merge with different labels', async () => {
@ -551,8 +527,7 @@ describe('Feature: merge annotations', () => {
attributes: [],
});
expect(task.annotations.merge(states))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.merge(states)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
@ -587,8 +562,9 @@ describe('Feature: split annotations', () => {
const annotations5 = await task.annotations.get(5);
expect(annotations4[0].clientID).toBe(annotations5[0].clientID);
expect(task.annotations.split(annotations5[0], 'bad frame'))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.split(annotations5[0], 'bad frame')).rejects.toThrow(
window.cvat.exceptions.ArgumentError,
);
});
});
@ -597,7 +573,7 @@ describe('Feature: group annotations', () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
let annotations = await task.annotations.get(0);
const groupID = await task.annotations.group(annotations);
expect(typeof (groupID)).toBe('number');
expect(typeof groupID).toBe('number');
annotations = await task.annotations.get(0);
for (const state of annotations) {
expect(state.group.id).toBe(groupID);
@ -608,7 +584,7 @@ describe('Feature: group annotations', () => {
const job = (await window.cvat.jobs.get({ jobID: 100 }))[0];
let annotations = await job.annotations.get(0);
const groupID = await job.annotations.group(annotations);
expect(typeof (groupID)).toBe('number');
expect(typeof groupID).toBe('number');
annotations = await job.annotations.get(0);
for (const state of annotations) {
expect(state.group.id).toBe(groupID);
@ -629,15 +605,13 @@ describe('Feature: group annotations', () => {
zOrder: 0,
});
expect(task.annotations.group([state]))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.group([state])).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to group not object state', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
expect(task.annotations.group(annotations.concat({})))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.group(annotations.concat({}))).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
@ -689,8 +663,7 @@ describe('Feature: clear annotations', () => {
test('clear annotations with bad reload parameter', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
await task.annotations.clear(true);
expect(task.annotations.clear('reload'))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.clear('reload')).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
@ -752,18 +725,16 @@ describe('Feature: select object', () => {
test('trying to select from not object states', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
expect(task.annotations.select(annotations.concat({}), 500, 500))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.select(annotations.concat({}), 500, 500)).rejects.toThrow(
window.cvat.exceptions.ArgumentError,
);
});
test('trying to select with invalid coordinates', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
expect(task.annotations.select(annotations, null, null))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.select(annotations, null, null))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.select(annotations, '5', '10'))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.select(annotations, null, null)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.select(annotations, null, null)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.annotations.select(annotations, '5', '10')).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -35,22 +28,18 @@ describe('Feature: get frame meta', () => {
test('pass frame number out of a task', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
expect(task.frames.get(100))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.frames.get(-1))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.frames.get(100)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.frames.get(-1)).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('pass bad frame number', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
expect(task.frames.get('5'))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.frames.get('5')).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('do not pass any frame number', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
expect(task.frames.get())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.frames.get()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
@ -59,14 +48,14 @@ describe('Feature: get frame data', () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const frame = await task.frames.get(0);
const frameData = await frame.data();
expect(typeof (frameData)).toBe('string');
expect(typeof frameData).toBe('string');
});
test('get frame data for a job', async () => {
const job = (await window.cvat.jobs.get({ jobID: 100 }))[0];
const frame = await job.frames.get(0);
const frameData = await frame.data();
expect(typeof (frameData)).toBe('string');
expect(typeof frameData).toBe('string');
});
});
@ -74,12 +63,12 @@ describe('Feature: get frame preview', () => {
test('get frame preview for a task', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const frame = await task.frames.preview();
expect(typeof (frame)).toBe('string');
expect(typeof frame).toBe('string');
});
test('get frame preview for a job', async () => {
const job = (await window.cvat.jobs.get({ jobID: 100 }))[0];
const frame = await job.frames.preview();
expect(typeof (frame)).toBe('string');
expect(typeof frame).toBe('string');
});
});

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -20,7 +13,6 @@ window.cvat = require('../../src/api');
const { Job } = require('../../src/session');
// Test cases
describe('Feature: get a list of jobs', () => {
test('get jobs by a task id', async () => {
@ -45,7 +37,6 @@ describe('Feature: get a list of jobs', () => {
expect(result).toHaveLength(0);
});
test('get jobs by a job id', async () => {
const result = await window.cvat.jobs.get({
jobID: 1,
@ -64,32 +55,39 @@ describe('Feature: get a list of jobs', () => {
});
test('get jobs by invalid filter with both taskID and jobID', async () => {
expect(window.cvat.jobs.get({
taskID: 1,
jobID: 1,
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.jobs.get({
taskID: 1,
jobID: 1,
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get jobs by invalid job id', async () => {
expect(window.cvat.jobs.get({
jobID: '1',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.jobs.get({
jobID: '1',
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get jobs by invalid task id', async () => {
expect(window.cvat.jobs.get({
taskID: '1',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.jobs.get({
taskID: '1',
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get jobs by unknown filter', async () => {
expect(window.cvat.jobs.get({
unknown: 50,
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.jobs.get({
unknown: 50,
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
describe('Feature: save job', () => {
test('save status of a job', async () => {
let result = await window.cvat.jobs.get({

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -168,30 +161,25 @@ describe('Feature: save object from its state', () => {
const state = annotations[0];
state.occluded = 'false';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldPoints = state.points;
state.occluded = false;
state.points = ['100', '50', '100', {}];
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.points = oldPoints;
state.lock = 'true';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldLabel = state.label;
state.lock = false;
state.label = 1;
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.label = oldLabel;
state.attributes = { 1: {}, 2: false, 3: () => {} };
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('save bad values for a track', async () => {
@ -200,40 +188,33 @@ describe('Feature: save object from its state', () => {
const state = annotations[0];
state.occluded = 'false';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldPoints = state.points;
state.occluded = false;
state.points = ['100', '50', '100', {}];
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.points = oldPoints;
state.lock = 'true';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldLabel = state.label;
state.lock = false;
state.label = 1;
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.label = oldLabel;
state.outside = 5;
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.outside = false;
state.keyframe = '10';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.keyframe = true;
state.attributes = { 1: {}, 2: false, 3: () => {} };
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
await expect(state.save()).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to change locked shape', async () => {

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -17,11 +10,7 @@ jest.mock('../../src/server-proxy', () => {
// Initialize api
window.cvat = require('../../src/api');
const {
AnnotationFormats,
Loader,
Dumper,
} = require('../../src/annotation-formats');
const { AnnotationFormats, Loader, Dumper } = require('../../src/annotation-formats');
// Test cases
describe('Feature: get info about cvat', () => {
@ -34,7 +23,6 @@ describe('Feature: get info about cvat', () => {
});
});
describe('Feature: get share storage info', () => {
test('get files in a root of a share storage', async () => {
const result = await window.cvat.server.share();
@ -49,9 +37,7 @@ describe('Feature: get share storage info', () => {
});
test('get files in a some unknown dir of a share storage', async () => {
expect(window.cvat.server.share(
'Unknown Directory',
)).rejects.toThrow(window.cvat.exceptions.ServerError);
expect(window.cvat.server.share('Unknown Directory')).rejects.toThrow(window.cvat.exceptions.ServerError);
});
});
@ -84,4 +70,4 @@ describe('Feature: get annotation dumpers', () => {
expect(dumper).toBeInstanceOf(Dumper);
}
});
});
});

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -20,7 +13,6 @@ window.cvat = require('../../src/api');
const { Task } = require('../../src/session');
// Test cases
describe('Feature: get a list of tasks', () => {
test('get all tasks', async () => {
@ -51,9 +43,11 @@ describe('Feature: get a list of tasks', () => {
});
test('get a task by an invalid id', async () => {
expect(window.cvat.tasks.get({
id: '50',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.tasks.get({
id: '50',
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get tasks by filters', async () => {
@ -69,9 +63,11 @@ describe('Feature: get a list of tasks', () => {
});
test('get tasks by invalid filters', async () => {
expect(window.cvat.tasks.get({
unknown: '5',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.tasks.get({
unknown: '5',
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get task by name, status and mode', async () => {
@ -117,14 +113,16 @@ describe('Feature: save a task', () => {
const labelsLength = result[0].labels.length;
const newLabel = new window.cvat.classes.Label({
name: 'My boss\'s car',
attributes: [{
default_value: 'false',
input_type: 'checkbox',
mutable: true,
name: 'parked',
values: ['false'],
}],
name: "My boss's car",
attributes: [
{
default_value: 'false',
input_type: 'checkbox',
mutable: true,
name: 'parked',
values: ['false'],
},
],
});
result[0].labels = [...result[0].labels, newLabel];
@ -135,7 +133,7 @@ describe('Feature: save a task', () => {
});
expect(result[0].labels).toHaveLength(labelsLength + 1);
const appendedLabel = result[0].labels.filter((el) => el.name === 'My boss\'s car');
const appendedLabel = result[0].labels.filter((el) => el.name === "My boss's car");
expect(appendedLabel).toHaveLength(1);
expect(appendedLabel[0].attributes).toHaveLength(1);
expect(appendedLabel[0].attributes[0].name).toBe('parked');
@ -147,22 +145,26 @@ describe('Feature: save a task', () => {
test('save new task without an id', async () => {
const task = new window.cvat.classes.Task({
name: 'New Task',
labels: [{
name: 'My boss\'s car',
attributes: [{
default_value: 'false',
input_type: 'checkbox',
mutable: true,
name: 'parked',
values: ['false'],
}],
}],
labels: [
{
name: "My boss's car",
attributes: [
{
default_value: 'false',
input_type: 'checkbox',
mutable: true,
name: 'parked',
values: ['false'],
},
],
},
],
bug_tracker: 'bug tracker value',
image_quality: 50,
});
const result = await task.save();
expect(typeof (result.id)).toBe('number');
expect(typeof result.id).toBe('number');
});
});

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -41,14 +34,18 @@ describe('Feature: get a list of users', () => {
});
test('get users with unknown filter key', async () => {
expect(window.cvat.users.get({
unknown: '50',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.users.get({
unknown: '50',
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get users with invalid filter key', async () => {
expect(window.cvat.users.get({
self: 1,
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(
window.cvat.users.get({
self: 1,
}),
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018-2020 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
@ -25,7 +18,7 @@ describe('Feature: toJSONQuery', () => {
const annotationsFilter = new AnnotationsFilter();
const [groups, query] = annotationsFilter.toJSONQuery([]);
expect(Array.isArray(groups)).toBeTruthy();
expect(typeof (query)).toBe('string');
expect(typeof query).toBe('string');
});
test('convert empty fitlers to a json query', () => {
@ -64,61 +57,65 @@ describe('Feature: toJSONQuery', () => {
test('convert filters to a json query', () => {
const annotationsFilter = new AnnotationsFilter();
const [groups, query] = annotationsFilter
.toJSONQuery(['clientID==5 & shape=="rectangle" & label==["car"]']);
expect(groups).toEqual([
['clientID==5', '&', 'shape=="rectangle"', '&', 'label==["car"]'],
]);
const [groups, query] = annotationsFilter.toJSONQuery(['clientID==5 & shape=="rectangle" & label==["car"]']);
expect(groups).toEqual([['clientID==5', '&', 'shape=="rectangle"', '&', 'label==["car"]']]);
expect(query).toBe('$.objects[?((@.clientID==5&@.shape=="rectangle"&@.label==["car"]))].clientID');
});
test('convert filters to a json query', () => {
const annotationsFilter = new AnnotationsFilter();
const [groups, query] = annotationsFilter
.toJSONQuery(['label=="car" | width >= height & type=="track"']);
expect(groups).toEqual([
['label=="car"', '|', 'width >= height', '&', 'type=="track"'],
]);
const [groups, query] = annotationsFilter.toJSONQuery(['label=="car" | width >= height & type=="track"']);
expect(groups).toEqual([['label=="car"', '|', 'width >= height', '&', 'type=="track"']]);
expect(query).toBe('$.objects[?((@.label=="car"|@.width>=@.height&@.type=="track"))].clientID');
});
test('convert filters to a json query', () => {
const annotationsFilter = new AnnotationsFilter();
const [groups, query] = annotationsFilter
.toJSONQuery(['label=="person" & attr["Attribute 1"] ==attr["Attribute 2"]']);
expect(groups).toEqual([
['label=="person"', '&', 'attr["Attribute 1"] ==attr["Attribute 2"]'],
const [groups, query] = annotationsFilter.toJSONQuery([
'label=="person" & attr["Attribute 1"] ==attr["Attribute 2"]',
]);
expect(groups).toEqual([['label=="person"', '&', 'attr["Attribute 1"] ==attr["Attribute 2"]']]);
expect(query).toBe('$.objects[?((@.label=="person"&@.attr["Attribute 1"]==@.attr["Attribute 2"]))].clientID');
});
test('convert filters to a json query', () => {
const annotationsFilter = new AnnotationsFilter();
const [groups, query] = annotationsFilter
.toJSONQuery(['label=="car" & attr["parked"]==true', 'label=="pedestrian" & width > 150']);
const [groups, query] = annotationsFilter.toJSONQuery([
'label=="car" & attr["parked"]==true',
'label=="pedestrian" & width > 150',
]);
expect(groups).toEqual([
['label=="car"', '&', 'attr["parked"]==true'],
'|',
['label=="pedestrian"', '&', 'width > 150'],
]);
expect(query).toBe('$.objects[?((@.label=="car"&@.attr["parked"]==true)|(@.label=="pedestrian"&@.width>150))].clientID');
expect(query).toBe(
'$.objects[?((@.label=="car"&@.attr["parked"]==true)|(@.label=="pedestrian"&@.width>150))].clientID',
);
});
test('convert filters to a json query', () => {
const annotationsFilter = new AnnotationsFilter();
const [groups, query] = annotationsFilter
.toJSONQuery(['(( label==["car \\"mazda\\""]) & (attr["sunglass ( help ) es"]==true | (width > 150 | height > 150 & (clientID == serverID))))) ']);
expect(groups).toEqual([[[
['label==["car `mazda`"]'],
'&',
['attr["sunglass ( help ) es"]==true', '|',
['width > 150', '|', 'height > 150', '&',
const [groups, query] = annotationsFilter.toJSONQuery([
// eslint-disable-next-line
'(( label==["car \\"mazda\\""]) & (attr["sunglass ( help ) es"]==true | (width > 150 | height > 150 & (clientID == serverID))))) ',
]);
expect(groups).toEqual([
[
[
['label==["car `mazda`"]'],
'&',
[
'clientID == serverID',
'attr["sunglass ( help ) es"]==true',
'|',
['width > 150', '|', 'height > 150', '&', ['clientID == serverID']],
],
],
],
]]]);
expect(query).toBe('$.objects[?((((@.label==["car `mazda`"])&(@.attr["sunglass ( help ) es"]==true|(@.width>150|@.height>150&(@.clientID==serverID))))))].clientID');
]);
expect(query).toBe(
// eslint-disable-next-line
'$.objects[?((((@.label==["car `mazda`"])&(@.attr["sunglass ( help ) es"]==true|(@.width>150|@.height>150&(@.clientID==serverID))))))].clientID',
);
});
});

File diff suppressed because it is too large Load Diff

@ -1,13 +1,6 @@
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* eslint import/no-extraneous-dependencies: 0 */
/* global
require:false
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const {
tasksDummyData,
@ -34,14 +27,11 @@ class ServerProxy {
const components = directory.split('/');
for (const component of components) {
const idx = position.map(x => x.name).indexOf(component);
const idx = position.map((x) => x.name).indexOf(component);
if (idx !== -1 && 'children' in position[idx]) {
position = position[idx].children;
} else {
throw new window.cvat.exceptions.ServerError(
`${component} is not a valid directory`,
400,
);
throw new window.cvat.exceptions.ServerError(`${component} is not a valid directory`, 400);
}
}
}
@ -101,17 +91,19 @@ class ServerProxy {
}
async function saveTask(id, taskData) {
const object = tasksDummyData.results.filter(task => task.id === id)[0];
const object = tasksDummyData.results.filter((task) => task.id === id)[0];
for (const prop in taskData) {
if (Object.prototype.hasOwnProperty.call(taskData, prop)
&& Object.prototype.hasOwnProperty.call(object, prop)) {
if (
Object.prototype.hasOwnProperty.call(taskData, prop) &&
Object.prototype.hasOwnProperty.call(object, prop)
) {
object[prop] = taskData[prop];
}
}
}
async function createTask(taskData) {
const id = Math.max(...tasksDummyData.results.map(el => el.id)) + 1;
const id = Math.max(...tasksDummyData.results.map((el) => el.id)) + 1;
tasksDummyData.results.push({
id,
url: `http://localhost:7000/api/v1/tasks/${id}`,
@ -137,47 +129,55 @@ class ServerProxy {
async function deleteTask(id) {
const tasks = tasksDummyData.results;
const task = tasks.filter(el => el.id === id)[0];
const task = tasks.filter((el) => el.id === id)[0];
if (task) {
tasks.splice(tasks.indexOf(task), 1);
}
}
async function getJob(jobID) {
const jobs = tasksDummyData.results.reduce((acc, task) => {
for (const segment of task.segments) {
for (const job of segment.jobs) {
const copy = JSON.parse(JSON.stringify(job));
copy.start_frame = segment.start_frame;
copy.stop_frame = segment.stop_frame;
copy.task_id = task.id;
acc.push(copy);
const jobs = tasksDummyData.results
.reduce((acc, task) => {
for (const segment of task.segments) {
for (const job of segment.jobs) {
const copy = JSON.parse(JSON.stringify(job));
copy.start_frame = segment.start_frame;
copy.stop_frame = segment.stop_frame;
copy.task_id = task.id;
acc.push(copy);
}
}
}
return acc;
}, []).filter(job => job.id === jobID);
return acc;
}, [])
.filter((job) => job.id === jobID);
return jobs[0] || {
detail: 'Not found.',
};
return (
jobs[0] || {
detail: 'Not found.',
}
);
}
async function saveJob(id, jobData) {
const object = tasksDummyData.results.reduce((acc, task) => {
for (const segment of task.segments) {
for (const job of segment.jobs) {
acc.push(job);
const object = tasksDummyData.results
.reduce((acc, task) => {
for (const segment of task.segments) {
for (const job of segment.jobs) {
acc.push(job);
}
}
}
return acc;
}, []).filter(job => job.id === id)[0];
return acc;
}, [])
.filter((job) => job.id === id)[0];
for (const prop in jobData) {
if (Object.prototype.hasOwnProperty.call(jobData, prop)
&& Object.prototype.hasOwnProperty.call(object, prop)) {
if (
Object.prototype.hasOwnProperty.call(jobData, prop) &&
Object.prototype.hasOwnProperty.call(object, prop)
) {
object[prop] = jobData[prop];
}
}
@ -223,10 +223,13 @@ class ServerProxy {
if (action === 'create') {
let idGenerator = 1000;
data.tracks.concat(data.tags).concat(data.shapes).map((el) => {
el.id = ++idGenerator;
return el;
});
data.tracks
.concat(data.tags)
.concat(data.shapes)
.map((el) => {
el.id = ++idGenerator;
return el;
});
return data;
}
@ -242,63 +245,66 @@ class ServerProxy {
return null;
}
Object.defineProperties(this, Object.freeze({
server: {
value: Object.freeze({
about,
share,
formats,
exception,
login,
logout,
}),
writable: false,
},
tasks: {
value: Object.freeze({
getTasks,
saveTask,
createTask,
deleteTask,
}),
writable: false,
},
jobs: {
value: Object.freeze({
getJob,
saveJob,
}),
writable: false,
},
users: {
value: Object.freeze({
getUsers,
getSelf,
}),
writable: false,
},
frames: {
value: Object.freeze({
getData,
getMeta,
getPreview,
}),
writable: false,
},
annotations: {
value: {
updateAnnotations,
getAnnotations,
Object.defineProperties(
this,
Object.freeze({
server: {
value: Object.freeze({
about,
share,
formats,
exception,
login,
logout,
}),
writable: false,
},
tasks: {
value: Object.freeze({
getTasks,
saveTask,
createTask,
deleteTask,
}),
writable: false,
},
jobs: {
value: Object.freeze({
getJob,
saveJob,
}),
writable: false,
},
users: {
value: Object.freeze({
getUsers,
getSelf,
}),
writable: false,
},
frames: {
value: Object.freeze({
getData,
getMeta,
getPreview,
}),
writable: false,
},
annotations: {
value: {
updateAnnotations,
getAnnotations,
},
// To implement on of important tests
writable: true,
},
// To implement on of important tests
writable: true,
},
}));
}),
);
}
}

@ -1,6 +1,9 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/* global
require:true,
__dirname:true,
__dirname:true
*/
const path = require('path');
@ -16,10 +19,12 @@ const nodeConfig = {
libraryTarget: 'commonjs',
},
module: {
rules: [{
test: /.js?$/,
exclude: /node_modules/,
}],
rules: [
{
test: /.js?$/,
exclude: /node_modules/,
},
],
},
stats: {
warnings: false,
@ -40,40 +45,46 @@ const webConfig = {
libraryTarget: 'window',
},
module: {
rules: [{
test: /.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
targets: '> 2.5%',
}],
],
sourceType: 'unambiguous',
rules: [
{
test: /.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: '> 2.5%',
},
],
],
sourceType: 'unambiguous',
},
},
},
}, {
test: /3rdparty\/.*\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
publicPath: '/static/engine/js/3rdparty/',
name: '[name].[contenthash].js',
{
test: /3rdparty\/.*\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
publicPath: '/static/engine/js/3rdparty/',
name: '[name].[contenthash].js',
},
},
},
}, {
test: /\.worker\.js$/,
exclude: /3rdparty/,
use: {
loader: 'worker-loader',
options: {
publicPath: '/static/engine/js/',
name: '[name].[contenthash].js',
{
test: /\.worker\.js$/,
exclude: /3rdparty/,
use: {
loader: 'worker-loader',
options: {
publicPath: '/static/engine/js/',
name: '[name].[contenthash].js',
},
},
},
},
],
},
};

@ -1,56 +1,50 @@
/*
* Copyright (C) 2018-2020 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
"env": {
"node": false,
"browser": true,
"es6": true,
"jquery": true,
"qunit": true,
env: {
node: false,
browser: true,
es6: true,
jquery: true,
qunit: true,
},
"parserOptions": {
"parser": "babel-eslint",
"sourceType": "module",
"ecmaVersion": 2018,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module',
ecmaVersion: 2018,
},
"plugins": [
"security",
"no-unsanitized",
"no-unsafe-innerhtml",
],
"extends": [
"eslint:recommended",
"plugin:security/recommended",
"plugin:no-unsanitized/DOM",
"airbnb-base",
],
"rules": {
"no-await-in-loop": [0],
"global-require": [0],
"no-new": [0],
"class-methods-use-this": [0],
"no-restricted-properties": [0, {
"object": "Math",
"property": "pow",
}],
"no-plusplus": [0],
"no-param-reassign": [0],
"no-underscore-dangle": ["error", { "allowAfterThis": true }],
"no-restricted-syntax": [0, {"selector": "ForOfStatement"}],
"no-continue": [0],
"no-unsafe-innerhtml/no-unsafe-innerhtml": 1,
plugins: ['security', 'no-unsanitized', 'no-unsafe-innerhtml'],
extends: ['eslint:recommended', 'plugin:security/recommended', 'plugin:no-unsanitized/DOM', 'airbnb-base'],
rules: {
'no-await-in-loop': [0],
'global-require': [0],
'no-new': [0],
'class-methods-use-this': [0],
'no-restricted-properties': [
0,
{
object: 'Math',
property: 'pow',
},
],
'no-plusplus': [0],
'no-param-reassign': [0],
'no-underscore-dangle': ['error', { allowAfterThis: true }],
'no-restricted-syntax': [0, { selector: 'ForOfStatement' }],
'no-continue': [0],
'no-unsafe-innerhtml/no-unsafe-innerhtml': 1,
// This rule actual for user input data on the node.js environment mainly.
"security/detect-object-injection": 0,
"indent": ["warn", 4],
"no-useless-constructor": 0,
"func-names": [0],
"valid-typeof": [0],
"no-console": [0], // this rule deprecates console.log, console.warn etc. because "it is not good in production code"
"max-classes-per-file": [0],
"quotes": ["warn", "single"],
'security/detect-object-injection': 0,
indent: ['warn', 4],
'no-useless-constructor': 0,
'func-names': [0],
'valid-typeof': [0],
'no-console': [0],
'max-classes-per-file': [0],
'max-len': ['error', { code: 120 }],
quotes: ['error', 'single'],
'operator-linebreak': ['error', 'after'],
},
};

@ -7,8 +7,9 @@ npm run server # run debug server
```
## Versioning
If you make changes in this package, please do following:
- After not important changes (typos, backward compatible bug fixes, refactoring) do: ``npm version patch``
- After changing API (backward compatible new features) do: ``npm version minor``
- After changing API (changes that break backward compatibility) do: ``npm version major``
- After not important changes (typos, backward compatible bug fixes, refactoring) do: `npm version patch`
- After changing API (backward compatible new features) do: `npm version minor`
- After changing API (changes that break backward compatibility) do: `npm version major`

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:true
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const { Mutex } = require('async-mutex');
// eslint-disable-next-line max-classes-per-file
@ -19,14 +14,12 @@ const BlockType = Object.freeze({
});
class FrameProvider {
constructor(blockType, blockSize, cachedBlockCount,
decodedBlocksCacheSize = 5, maxWorkerThreadCount = 2) {
constructor(blockType, blockSize, cachedBlockCount, decodedBlocksCacheSize = 5, maxWorkerThreadCount = 2) {
this._frames = {};
this._cachedBlockCount = Math.max(1, cachedBlockCount); // number of stored blocks
this._decodedBlocksCacheSize = decodedBlocksCacheSize;
this._blocksRanges = [];
this._blocks = {};
this._blockSize = blockSize;
this._running = false;
this._blockType = blockType;
this._currFrame = -1;
@ -42,15 +35,14 @@ class FrameProvider {
}
async _worker() {
if (this._requestedBlockDecode !== null
&& this._decodeThreadCount < this._maxWorkerThreadCount) {
if (this._requestedBlockDecode !== null && this._decodeThreadCount < this._maxWorkerThreadCount) {
await this.startDecode();
}
this._timerId = setTimeout(this._worker.bind(this), 100);
}
isChunkCached(start, end) {
return (`${start}:${end}` in this._blocksRanges);
return `${start}:${end}` in this._blocksRanges;
}
/* This method removes extra data from a cache when memory overflow */
@ -68,8 +60,10 @@ class FrameProvider {
const distance = Math.floor(this._decodedBlocksCacheSize / 2);
for (let i = 0; i < this._blocksRanges.length; i++) {
const [start, end] = this._blocksRanges[i].split(':').map((el) => +el);
if (end < this._currFrame - distance * this._blockSize
|| start > this._currFrame + distance * this._blockSize) {
if (
end < this._currFrame - distance * this._blockSize ||
start > this._currFrame + distance * this._blockSize
) {
for (let j = start; j <= end; j++) {
delete this._frames[j];
}
@ -81,8 +75,7 @@ class FrameProvider {
const release = await this._mutex.acquire();
try {
if (this._requestedBlockDecode !== null) {
if (start === this._requestedBlockDecode.start
&& end === this._requestedBlockDecode.end) {
if (start === this._requestedBlockDecode.start && end === this._requestedBlockDecode.end) {
this._requestedBlockDecode.resolveCallback = resolveCallback;
this._requestedBlockDecode.rejectCallback = rejectCallback;
} else if (this._requestedBlockDecode.rejectCallback) {
@ -158,8 +151,7 @@ class FrameProvider {
}
static cropImage(imageBuffer, imageWidth, imageHeight, xOffset, yOffset, width, height) {
if (xOffset === 0 && width === imageWidth
&& yOffset === 0 && height === imageHeight) {
if (xOffset === 0 && width === imageWidth && yOffset === 0 && height === imageHeight) {
return new ImageData(new Uint8ClampedArray(imageBuffer), width, height);
}
const source = new Uint32Array(imageBuffer);
@ -170,11 +162,7 @@ class FrameProvider {
const rgbaInt8Clamped = new Uint8ClampedArray(buffer);
if (imageWidth === width) {
return new ImageData(
new Uint8ClampedArray(imageBuffer, yOffset * 4, bufferSize),
width,
height,
);
return new ImageData(new Uint8ClampedArray(imageBuffer, yOffset * 4, bufferSize), width, height);
}
let writeIdx = 0;
@ -207,14 +195,20 @@ class FrameProvider {
let index = start;
worker.onmessage = (e) => {
if (e.data.consoleLog) { // ignore initialization message
if (e.data.consoleLog) {
// ignore initialization message
return;
}
const scaleFactor = Math.ceil(this._height / e.data.height);
this._frames[index] = FrameProvider.cropImage(
e.data.buf, e.data.width, e.data.height, 0, 0,
Math.floor(width / scaleFactor), Math.floor(height / scaleFactor),
e.data.buf,
e.data.width,
e.data.height,
0,
0,
Math.floor(width / scaleFactor),
Math.floor(height / scaleFactor),
);
if (this._decodingBlocks[`${start}:${end}`].resolveCallback) {
@ -302,10 +296,10 @@ class FrameProvider {
// but document.createElement doesn't work in worker
// so, we get raw data and decode it here, no other way
const createImageBitmap = async function(blob) {
return new Promise((resolve,reject) => {
let img = document.createElement('img');
img.addEventListener('load', function() {
const createImageBitmap = async function (blob) {
return new Promise((resolve) => {
const img = document.createElement('img');
img.addEventListener('load', function () {
resolve(this);
});
img.src = URL.createObjectURL(blob);
@ -322,9 +316,7 @@ class FrameProvider {
}
if (event.data.index in this._promisedFrames) {
this._promisedFrames[event.data.index].resolve(
this._frames[event.data.index],
);
this._promisedFrames[event.data.index].resolve(this._frames[event.data.index]);
delete this._promisedFrames[event.data.index];
}
@ -357,9 +349,7 @@ class FrameProvider {
Is an array of strings like "start:end"
*/
get cachedFrames() {
return [...this._blocksRanges].sort(
(a, b) => a.split(':')[0] - b.split(':')[0],
);
return [...this._blocksRanges].sort((a, b) => a.split(':')[0] - b.split(':')[0]);
}
}

@ -1,11 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:true
*/
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const JSZip = require('jszip');
@ -19,24 +14,27 @@ onmessage = (e) => {
_zip.forEach((relativePath) => {
const fileIndex = index++;
if (fileIndex <= end) {
_zip.file(relativePath).async('blob').then((fileData) => {
if (self.createImageBitmap) {
createImageBitmap(fileData).then((img) => {
_zip.file(relativePath)
.async('blob')
.then((fileData) => {
// eslint-disable-next-line no-restricted-globals
if (self.createImageBitmap) {
createImageBitmap(fileData).then((img) => {
postMessage({
fileName: relativePath,
index: fileIndex,
data: img,
});
});
} else {
postMessage({
fileName: relativePath,
index: fileIndex,
data: img,
data: fileData,
isRaw: true,
});
});
} else {
postMessage({
fileName: relativePath,
index: fileIndex,
data: fileData,
isRaw: true,
});
}
});
}
});
}
});
});

@ -1,6 +1,9 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/* global
require:true,
__dirname:true,
__dirname:true
*/
const path = require('path');
@ -27,14 +30,18 @@ const cvatData = {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
targets: '> 2.5%', // https://github.com/browserslist/browserslist
}],
[
'@babel/preset-env',
{
targets: '> 2.5%', // https://github.com/browserslist/browserslist
},
],
],
sourceType: 'unambiguous',
},
},
}, {
},
{
test: /\.worker\.js$/,
exclude: /3rdparty/,
use: {
@ -44,7 +51,8 @@ const cvatData = {
name: '[name].[contenthash].js',
},
},
}, {
},
{
test: /3rdparty\/.*\.worker\.js$/,
use: {
loader: 'worker-loader',
@ -56,11 +64,7 @@ const cvatData = {
},
],
},
plugins: [
new CopyPlugin([
'./src/js/3rdparty/avc.wasm',
]),
],
plugins: [new CopyPlugin(['./src/js/3rdparty/avc.wasm'])],
};
module.exports = cvatData;

@ -13,17 +13,13 @@ module.exports = {
ecmaVersion: 6,
project: './tsconfig.json',
},
plugins: ['@typescript-eslint', 'import', 'eslint-plugin-header'],
ignorePatterns: ['*.svg', '*.scss'],
plugins: ['@typescript-eslint', 'import'],
extends: [
'plugin:@typescript-eslint/recommended',
'airbnb-typescript',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'prettier',
'prettier/@typescript-eslint',
'prettier/react',
],
rules: {
'@typescript-eslint/indent': ['warn', 4],
@ -38,12 +34,29 @@ module.exports = {
'no-plusplus': [0],
'lines-between-class-members': 0,
'react/no-did-update-set-state': 0, // https://github.com/airbnb/javascript/issues/1875
'header/header': [2, '.header-tpl.ts'],
quotes: ['error', 'single'],
'max-len': ['error', { code: 120 }],
'func-names': ['warn', 'never'],
'operator-linebreak': ['error', 'after'],
'react/require-default-props': 'off',
'react/no-unused-prop-types': 'off',
'react/no-array-index-key': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/ban-types': [
'error',
{
types: {
'{}': false, // TODO: try to fix with Record<string, unknown>
object: false, // TODO: try to fix with Record<string, unknown>
Function: false, // TODO: try to fix somehow
},
},
],
},
settings: {
'import/resolver': {
typescript: {
directory: './tsconfig.json',
node: {
paths: ['src'],
},
},
},

@ -1,19 +1,22 @@
# cvat-ui module
## Description
This is a client UI for Computer Vision Annotation Tool based on React, Redux and Antd
## Versioning
If you make changes in this package, please do following:
- After not important changes (typos, bug fixes, refactoring) do: ``npm version patch``
- After adding new features do: ``npm version minor``
- After significant UI redesign do: ``npm version major``
- After not important changes (typos, bug fixes, refactoring) do: `npm version patch`
- After adding new features do: `npm version minor`
- After significant UI redesign do: `npm version major`
Important: If you have changed versions for ``cvat-core``, ``cvat-canvas``, ``cvat-data``,
you also need to do ``npm install`` to update ``package-lock.json``
Important: If you have changed versions for `cvat-core`, `cvat-canvas`, `cvat-data`,
you also need to do `npm install` to update `package-lock.json`
## Commands
- Installing dependencies:
```bash
@ -26,12 +29,12 @@ cd ../cvat-core && npm install && cd - && npm install
npm start
```
- Building the module from sources in the ```dist``` directory:
- Building the module from sources in the `dist` directory:
```bash
npm run build
npm run build -- --mode=development # without a minification
```
Important: You also have to run CVAT REST API server (please read ``CONTRIBUTING.md``)
Important: You also have to run CVAT REST API server (please read `CONTRIBUTING.md`)
to correct working since UI gets all necessary data (tasks, users, annotations) from there

File diff suppressed because it is too large Load Diff

@ -1,86 +1,82 @@
{
"name": "cvat-ui",
"version": "1.9.14",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
"build": "webpack --config ./webpack.config.js",
"start": "REACT_APP_API_URL=http://localhost:7000 webpack-dev-server --config ./webpack.config.js --mode=development",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"lint": "eslint './src/**/*.{ts,tsx}'",
"lint:fix": "eslint './src/**/*.{ts,tsx}' --fix"
},
"author": "Intel",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.6.0",
"@typescript-eslint/eslint-plugin": "^2.19.2",
"@typescript-eslint/parser": "^2.19.2",
"babel-loader": "^8.0.6",
"babel-plugin-import": "^1.12.2",
"copy-webpack-plugin": "^5.1.2",
"css-loader": "^3.2.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-typescript": "^7.0.0",
"eslint-config-prettier": "^6.12.0",
"eslint-import-resolver-typescript": "^2.0.0",
"eslint-plugin-header": "^3.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-react-hooks": "^1.7.0",
"html-webpack-plugin": "^3.2.0",
"lint-staged": "^10.4.0",
"node-sass": "^4.13.0",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"react-svg-loader": "^3.0.3",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^3.7.3",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.8",
"webpack-dev-server": "^3.8.0",
"worker-loader": "^2.0.0"
},
"dependencies": {
"@types/platform": "^1.3.3",
"@types/react": "^16.9.50",
"@types/react-color": "^3.0.4",
"@types/react-dom": "^16.9.0",
"@types/react-redux": "^7.1.2",
"@types/react-router": "^5.0.5",
"@types/react-router-dom": "^5.1.0",
"@types/react-share": "^3.0.3",
"@types/redux-logger": "^3.0.8",
"antd": "^3.26.18",
"copy-to-clipboard": "^3.3.1",
"cvat-canvas": "file:../cvat-canvas",
"cvat-core": "file:../cvat-core",
"dotenv-webpack": "^1.8.0",
"error-stack-parser": "^2.0.6",
"moment": "^2.29.0",
"platform": "^1.3.6",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-color": "^2.18.1",
"react-cookie": "^4.0.3",
"react-dom": "^16.13.1",
"react-hotkeys": "^2.0.0",
"react-redux": "^7.1.1",
"react-router": "^5.1.0",
"react-router-dom": "^5.1.0",
"react-share": "^3.0.1",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
}
"name": "cvat-ui",
"version": "1.9.14",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
"build": "webpack --config ./webpack.config.js",
"start": "REACT_APP_API_URL=http://localhost:7000 webpack-dev-server --config ./webpack.config.js --mode=development",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"lint": "eslint './src/**/*.{ts,tsx}'",
"lint:fix": "eslint './src/**/*.{ts,tsx}' --fix"
},
"author": "Intel",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.6.0",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"babel-loader": "^8.0.6",
"babel-plugin-import": "^1.12.2",
"copy-webpack-plugin": "^5.1.2",
"css-loader": "^3.2.0",
"eslint": "^7.11.0",
"eslint-config-airbnb-typescript": "^12.0.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"html-webpack-plugin": "^3.2.0",
"node-sass": "^4.13.0",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"react-svg-loader": "^3.0.3",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^3.7.3",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.8",
"webpack-dev-server": "^3.8.0",
"worker-loader": "^2.0.0"
},
"dependencies": {
"@types/platform": "^1.3.3",
"@types/react": "^16.9.50",
"@types/react-color": "^3.0.4",
"@types/react-dom": "^16.9.0",
"@types/react-redux": "^7.1.2",
"@types/react-router": "^5.0.5",
"@types/react-router-dom": "^5.1.0",
"@types/react-share": "^3.0.3",
"@types/redux-logger": "^3.0.8",
"antd": "^3.26.18",
"copy-to-clipboard": "^3.3.1",
"cvat-canvas": "file:../cvat-canvas",
"cvat-core": "file:../cvat-core",
"dotenv-webpack": "^1.8.0",
"error-stack-parser": "^2.0.6",
"moment": "^2.29.0",
"platform": "^1.3.6",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-color": "^2.18.1",
"react-cookie": "^4.0.3",
"react-dom": "^16.13.1",
"react-hotkeys": "^2.0.0",
"react-redux": "^7.1.1",
"react-router": "^5.1.0",
"react-router-dom": "^5.1.0",
"react-share": "^3.0.1",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
}
}

@ -26,9 +26,7 @@ export const getAboutAsync = (): ThunkAction => async (dispatch): Promise<void>
try {
const about = await core.server.about();
dispatch(
aboutActions.getAboutSuccess(about),
);
dispatch(aboutActions.getAboutSuccess(about));
} catch (error) {
dispatch(aboutActions.getAboutFailed(error));
}

@ -2,12 +2,7 @@
//
// SPDX-License-Identifier: MIT
import {
AnyAction,
Dispatch,
ActionCreator,
Store,
} from 'redux';
import { AnyAction, Dispatch, ActionCreator, Store } from 'redux';
import { ThunkAction } from 'utils/redux';
import {
@ -54,22 +49,14 @@ function receiveAnnotationsParameters(): AnnotationsParameters {
const state: CombinedState = getStore().getState();
const {
annotation: {
annotations: {
filters,
},
annotations: { filters },
player: {
frame: {
number: frame,
},
},
job: {
instance: jobInstance,
frame: { number: frame },
},
job: { instance: jobInstance },
},
settings: {
workspace: {
showAllInterpolationTracks,
},
workspace: { showAllInterpolationTracks },
},
} = state;
@ -97,11 +84,17 @@ async function jobInfoGenerator(job: any): Promise<Record<string, number>> {
const { total } = await job.annotations.statistics();
return {
'frame count': job.stopFrame - job.startFrame + 1,
'track count': total.rectangle.shape + total.rectangle.track
+ total.polygon.shape + total.polygon.track
+ total.polyline.shape + total.polyline.track
+ total.points.shape + total.points.track
+ total.cuboid.shape + total.cuboid.track,
'track count':
total.rectangle.shape +
total.rectangle.track +
total.polygon.shape +
total.polygon.track +
total.polyline.shape +
total.polyline.track +
total.points.shape +
total.points.track +
total.cuboid.shape +
total.cuboid.track,
'object count': total.total,
'box count': total.rectangle.shape + total.rectangle.track,
'polygon count': total.polygon.shape + total.polygon.track,
@ -241,14 +234,8 @@ export function switchZLayer(cur: number): AnyAction {
export function fetchAnnotationsAsync(): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const {
filters,
frame,
showAllInterpolationTracks,
jobInstance,
} = receiveAnnotationsParameters();
const states = await jobInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const { filters, frame, showAllInterpolationTracks, jobInstance } = receiveAnnotationsParameters();
const states = await jobInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
dispatch({
@ -358,11 +345,9 @@ export function uploadJobAnnotationsAsync(job: any, loader: any, file: File): Th
const frame = state.annotation.player.frame.number;
await job.annotations.upload(file, loader);
await job.logger.log(
LogType.uploadAnnotations, {
...(await jobInfoGenerator(job)),
},
);
await job.logger.log(LogType.uploadAnnotations, {
...(await jobInfoGenerator(job)),
});
await job.annotations.clear(true);
await job.actions.clear();
@ -470,20 +455,14 @@ export function showStatistics(visible: boolean): AnyAction {
};
}
export function propagateObjectAsync(
sessionInstance: any,
objectState: any,
from: number,
to: number,
): ThunkAction {
export function propagateObjectAsync(sessionInstance: any, objectState: any, from: number, to: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const copy = {
attributes: objectState.attributes,
points: objectState.points,
occluded: objectState.occluded,
objectType: objectState.objectType !== ObjectType.TRACK
? objectState.objectType : ObjectType.SHAPE,
objectType: objectState.objectType !== ObjectType.TRACK ? objectState.objectType : ObjectType.SHAPE,
shapeType: objectState.shapeType,
label: objectState.label,
zOrder: objectState.zOrder,
@ -491,9 +470,7 @@ export function propagateObjectAsync(
source: objectState.source,
};
await sessionInstance.logger.log(
LogType.propagateObject, { count: to - from + 1 },
);
await sessionInstance.logger.log(LogType.propagateObject, { count: to - from + 1 });
const states = [];
for (let frame = from; frame <= to; frame++) {
copy.frame = frame;
@ -540,8 +517,7 @@ export function changePropagateFrames(frames: number): AnyAction {
};
}
export function removeObjectAsync(sessionInstance: any, objectState: any, force: boolean):
ThunkAction {
export function removeObjectAsync(sessionInstance: any, objectState: any, force: boolean): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
await sessionInstance.logger.log(LogType.deleteObject, { count: 1 });
@ -602,10 +578,7 @@ export function selectObjects(selectedStatesID: number[]): AnyAction {
};
}
export function activateObject(
activatedStateID: number | null,
activatedAttributeID: number | null,
): AnyAction {
export function activateObject(activatedStateID: number | null, activatedAttributeID: number | null): AnyAction {
return {
type: AnnotationActionTypes.ACTIVATE_OBJECT,
payload: {
@ -657,8 +630,7 @@ export function switchPlay(playing: boolean): AnyAction {
};
}
export function changeFrameAsync(toFrame: number, fillBuffer?: boolean, frameStep?: number):
ThunkAction {
export function changeFrameAsync(toFrame: number, fillBuffer?: boolean, frameStep?: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const state: CombinedState = getStore().getState();
const { instance: job } = state.annotation.job;
@ -694,23 +666,21 @@ ThunkAction {
payload: {},
});
await job.logger.log(
LogType.changeFrame, {
from: frame,
to: toFrame,
},
);
await job.logger.log(LogType.changeFrame, {
from: frame,
to: toFrame,
});
const data = await job.frames.get(toFrame, fillBuffer, frameStep);
const states = await job.annotations.get(toFrame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
const currentTime = new Date().getTime();
let frameSpeed;
switch (state.settings.player.frameSpeed) {
case (FrameSpeed.Fast): {
case FrameSpeed.Fast: {
frameSpeed = (FrameSpeed.Fast as number) / 2;
break;
}
case (FrameSpeed.Fastest): {
case FrameSpeed.Fastest: {
frameSpeed = (FrameSpeed.Fastest as number) / 3;
break;
}
@ -718,8 +688,10 @@ ThunkAction {
frameSpeed = state.settings.player.frameSpeed as number;
}
}
const delay = Math.max(0, Math.round(1000 / frameSpeed)
- currentTime + (state.annotation.player.frame.changeTime as number));
const delay = Math.max(
0,
Math.round(1000 / frameSpeed) - currentTime + (state.annotation.player.frame.changeTime as number),
);
dispatch({
type: AnnotationActionTypes.CHANGE_FRAME_SUCCESS,
@ -749,8 +721,7 @@ ThunkAction {
};
}
export function undoActionAsync(sessionInstance: any, frame: number):
ThunkAction {
export function undoActionAsync(sessionInstance: any, frame: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state = getStore().getState();
@ -758,17 +729,20 @@ ThunkAction {
// TODO: use affected IDs as an optimization
const [undo] = state.annotation.annotations.history.undo.slice(-1);
const undoLog = await sessionInstance.logger.log(LogType.undoAction, {
name: undo[0],
frame: undo[1],
count: 1,
}, true);
const undoLog = await sessionInstance.logger.log(
LogType.undoAction,
{
name: undo[0],
frame: undo[1],
count: 1,
},
true,
);
dispatch(changeFrameAsync(undo[1]));
await sessionInstance.actions.undo();
const history = await sessionInstance.actions.get();
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
await undoLog.close();
@ -792,8 +766,7 @@ ThunkAction {
};
}
export function redoActionAsync(sessionInstance: any, frame: number):
ThunkAction {
export function redoActionAsync(sessionInstance: any, frame: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state = getStore().getState();
@ -801,16 +774,19 @@ ThunkAction {
// TODO: use affected IDs as an optimization
const [redo] = state.annotation.annotations.history.redo.slice(-1);
const redoLog = await sessionInstance.logger.log(LogType.redoAction, {
name: redo[0],
frame: redo[1],
count: 1,
}, true);
const redoLog = await sessionInstance.logger.log(
LogType.redoAction,
{
name: redo[0],
frame: redo[1],
count: 1,
},
true,
);
dispatch(changeFrameAsync(redo[1]));
await sessionInstance.actions.redo();
const history = await sessionInstance.actions.get();
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
await redoLog.close();
@ -839,27 +815,20 @@ export function rotateCurrentFrame(rotation: Rotation): AnyAction {
const {
annotation: {
player: {
frame: {
number: frameNumber,
},
frame: { number: frameNumber },
frameAngles,
},
job: {
instance: job,
instance: {
startFrame,
},
instance: { startFrame },
},
},
settings: {
player: {
rotateAll,
},
player: { rotateAll },
},
} = state;
const frameAngle = (frameAngles[frameNumber - startFrame]
+ (rotation === Rotation.CLOCKWISE90 ? 90 : 270)) % 360;
const frameAngle = (frameAngles[frameNumber - startFrame] + (rotation === Rotation.CLOCKWISE90 ? 90 : 270)) % 360;
job.logger.log(LogType.rotateImage, { angle: frameAngle });
@ -918,12 +887,7 @@ export function closeJob(): ThunkAction {
};
}
export function getJobAsync(
tid: number,
jid: number,
initialFrame: number,
initialFilters: string[],
): ThunkAction {
export function getJobAsync(tid: number, jid: number, initialFrame: number, initialFilters: string[]): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state: CombinedState = getStore().getState();
@ -938,10 +902,12 @@ export function getJobAsync(
});
const loadJobEvent = await logger.log(
LogType.loadJob, {
LogType.loadJob,
{
task_id: tid,
job_id: jid,
}, true,
},
true,
);
// Check state if the task is already there
@ -955,8 +921,7 @@ export function getJobAsync(
}
// Finally get the job from the task
const job = task.jobs
.filter((_job: any) => _job.id === jid)[0];
const job = task.jobs.filter((_job: any) => _job.id === jid)[0];
if (!job) {
throw new Error(`Task ${tid} doesn't contain the job ${jid}`);
}
@ -966,8 +931,7 @@ export function getJobAsync(
// call first getting of frame data before rendering interface
// to load and decode first chunk
await frameData.data();
const states = await job.annotations
.get(frameNumber, showAllInterpolationTracks, filters);
const states = await job.annotations.get(frameNumber, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
const colors = [...cvat.enums.colors];
@ -999,8 +963,7 @@ export function getJobAsync(
};
}
export function saveAnnotationsAsync(sessionInstance: any):
ThunkAction {
export function saveAnnotationsAsync(sessionInstance: any): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
@ -1010,9 +973,7 @@ ThunkAction {
});
try {
const saveJobEvent = await sessionInstance.logger.log(
LogType.saveJob, {}, true,
);
const saveJobEvent = await sessionInstance.logger.log(LogType.saveJob, {}, true);
await sessionInstance.annotations.save((status: string) => {
dispatch({
@ -1023,15 +984,11 @@ ThunkAction {
});
});
await saveJobEvent.close();
await sessionInstance.logger.log(
LogType.sendTaskInfo,
await jobInfoGenerator(sessionInstance),
);
await sessionInstance.logger.log(LogType.sendTaskInfo, await jobInfoGenerator(sessionInstance));
dispatch(saveLogsAsync());
const { frame } = receiveAnnotationsParameters();
const states = await sessionInstance
.annotations.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
dispatch({
type: AnnotationActionTypes.SAVE_ANNOTATIONS_SUCCESS,
@ -1120,12 +1077,7 @@ export function splitTrack(enabled: boolean): AnyAction {
export function updateAnnotationsAsync(statesToUpdate: any[]): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const {
jobInstance,
filters,
frame,
showAllInterpolationTracks,
} = receiveAnnotationsParameters();
const { jobInstance, filters, frame, showAllInterpolationTracks } = receiveAnnotationsParameters();
try {
if (statesToUpdate.some((state: any): boolean => state.updateFlags.zOrder)) {
@ -1133,8 +1085,7 @@ export function updateAnnotationsAsync(statesToUpdate: any[]): ThunkAction {
dispatch(activateObject(null, null));
}
const promises = statesToUpdate
.map((objectState: any): Promise<any> => objectState.save());
const promises = statesToUpdate.map((objectState: any): Promise<any> => objectState.save());
const states = await Promise.all(promises);
const history = await jobInstance.actions.get();
const [minZ, maxZ] = computeZRange(states);
@ -1149,8 +1100,7 @@ export function updateAnnotationsAsync(statesToUpdate: any[]): ThunkAction {
},
});
} catch (error) {
const states = await jobInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await jobInstance.annotations.get(frame, showAllInterpolationTracks, filters);
dispatch({
type: AnnotationActionTypes.UPDATE_ANNOTATIONS_FAILED,
payload: {
@ -1162,14 +1112,12 @@ export function updateAnnotationsAsync(statesToUpdate: any[]): ThunkAction {
};
}
export function createAnnotationsAsync(sessionInstance: any, frame: number, statesToCreate: any[]):
ThunkAction {
export function createAnnotationsAsync(sessionInstance: any, frame: number, statesToCreate: any[]): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
await sessionInstance.annotations.put(statesToCreate);
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const history = await sessionInstance.actions.get();
dispatch({
@ -1190,14 +1138,12 @@ ThunkAction {
};
}
export function mergeAnnotationsAsync(sessionInstance: any, frame: number, statesToMerge: any[]):
ThunkAction {
export function mergeAnnotationsAsync(sessionInstance: any, frame: number, statesToMerge: any[]): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
await sessionInstance.annotations.merge(statesToMerge);
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const history = await sessionInstance.actions.get();
dispatch({
@ -1225,11 +1171,7 @@ export function resetAnnotationsGroup(): AnyAction {
};
}
export function groupAnnotationsAsync(
sessionInstance: any,
frame: number,
statesToGroup: any[],
): ThunkAction {
export function groupAnnotationsAsync(sessionInstance: any, frame: number, statesToGroup: any[]): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
@ -1242,8 +1184,7 @@ export function groupAnnotationsAsync(
});
await sessionInstance.annotations.group(statesToGroup, reset);
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const history = await sessionInstance.actions.get();
dispatch({
@ -1264,14 +1205,12 @@ export function groupAnnotationsAsync(
};
}
export function splitAnnotationsAsync(sessionInstance: any, frame: number, stateToSplit: any):
ThunkAction {
export function splitAnnotationsAsync(sessionInstance: any, frame: number, stateToSplit: any): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
try {
await sessionInstance.annotations.split(stateToSplit, frame);
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters);
const history = await sessionInstance.actions.get();
dispatch({
@ -1292,14 +1231,12 @@ ThunkAction {
};
}
export function changeGroupColorAsync(
group: number,
color: string,
): ThunkAction {
export function changeGroupColorAsync(group: number, color: string): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const state: CombinedState = getStore().getState();
const groupStates = state.annotation.annotations.states
.filter((_state: any): boolean => _state.group.id === group);
const groupStates = state.annotation.annotations.states.filter(
(_state: any): boolean => _state.group.id === group,
);
if (groupStates.length) {
groupStates[0].group.color = color;
dispatch(updateAnnotationsAsync(groupStates));
@ -1309,11 +1246,7 @@ export function changeGroupColorAsync(
};
}
export function searchAnnotationsAsync(
sessionInstance: any,
frameFrom: number,
frameTo: number,
): ThunkAction {
export function searchAnnotationsAsync(sessionInstance: any, frameFrom: number, frameTo: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const { filters } = receiveAnnotationsParameters();
@ -1332,11 +1265,7 @@ export function searchAnnotationsAsync(
};
}
export function searchEmptyFrameAsync(
sessionInstance: any,
frameFrom: number,
frameTo: number,
): ThunkAction {
export function searchEmptyFrameAsync(sessionInstance: any, frameFrom: number, frameTo: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const frame = await sessionInstance.annotations.searchEmpty(frameFrom, frameTo);
@ -1357,20 +1286,12 @@ export function searchEmptyFrameAsync(
export function pasteShapeAsync(): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const {
canvas: {
instance: canvasInstance,
},
job: {
instance: jobInstance,
},
canvas: { instance: canvasInstance },
job: { instance: jobInstance },
player: {
frame: {
number: frameNumber,
},
},
drawing: {
activeInitialState: initialState,
frame: { number: frameNumber },
},
drawing: { activeInitialState: initialState },
} = getStore().getState().annotation;
if (initialState) {
@ -1432,21 +1353,13 @@ export function setAIToolsRef(ref: MutableRefObject<any>): AnyAction {
};
}
export function repeatDrawShapeAsync(): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const {
canvas: {
instance: canvasInstance,
},
job: {
labels,
instance: jobInstance,
},
canvas: { instance: canvasInstance },
job: { labels, instance: jobInstance },
player: {
frame: {
number: frameNumber,
},
frame: { number: frameNumber },
},
drawing: {
activeInteractor,
@ -1520,18 +1433,12 @@ export function repeatDrawShapeAsync(): ThunkAction {
export function redrawShapeAsync(): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const {
annotations: {
activatedStateID,
states,
},
canvas: {
instance: canvasInstance,
},
annotations: { activatedStateID, states },
canvas: { instance: canvasInstance },
} = getStore().getState().annotation;
if (activatedStateID !== null) {
const [state] = states
.filter((_state: any): boolean => _state.clientID === activatedStateID);
const [state] = states.filter((_state: any): boolean => _state.clientID === activatedStateID);
if (state && state.objectType !== ObjectType.TAG) {
let activeControl = ActiveControl.CURSOR;
if (state.shapeType === ShapeType.RECTANGLE) {

@ -50,32 +50,22 @@ export const authActions = {
logoutFailed: (error: any) => createAction(AuthActionTypes.LOGOUT_FAILED, { error }),
changePassword: () => createAction(AuthActionTypes.CHANGE_PASSWORD),
changePasswordSuccess: () => createAction(AuthActionTypes.CHANGE_PASSWORD_SUCCESS),
changePasswordFailed: (error: any) => (
createAction(AuthActionTypes.CHANGE_PASSWORD_FAILED, { error })
),
switchChangePasswordDialog: (showChangePasswordDialog: boolean) => (
createAction(AuthActionTypes.SWITCH_CHANGE_PASSWORD_DIALOG, { showChangePasswordDialog })
),
changePasswordFailed: (error: any) => createAction(AuthActionTypes.CHANGE_PASSWORD_FAILED, { error }),
switchChangePasswordDialog: (showChangePasswordDialog: boolean) =>
createAction(AuthActionTypes.SWITCH_CHANGE_PASSWORD_DIALOG, { showChangePasswordDialog }),
requestPasswordReset: () => createAction(AuthActionTypes.REQUEST_PASSWORD_RESET),
requestPasswordResetSuccess: () => createAction(AuthActionTypes.REQUEST_PASSWORD_RESET_SUCCESS),
requestPasswordResetFailed: (error: any) => (
createAction(AuthActionTypes.REQUEST_PASSWORD_RESET_FAILED, { error })
),
requestPasswordResetFailed: (error: any) => createAction(AuthActionTypes.REQUEST_PASSWORD_RESET_FAILED, { error }),
resetPassword: () => createAction(AuthActionTypes.RESET_PASSWORD),
resetPasswordSuccess: () => createAction(AuthActionTypes.RESET_PASSWORD_SUCCESS),
resetPasswordFailed: (error: any) => (
createAction(AuthActionTypes.RESET_PASSWORD_FAILED, { error })
),
resetPasswordFailed: (error: any) => createAction(AuthActionTypes.RESET_PASSWORD_FAILED, { error }),
loadServerAuthActions: () => createAction(AuthActionTypes.LOAD_AUTH_ACTIONS),
loadServerAuthActionsSuccess: (allowChangePassword: boolean, allowResetPassword: boolean) => (
loadServerAuthActionsSuccess: (allowChangePassword: boolean, allowResetPassword: boolean) =>
createAction(AuthActionTypes.LOAD_AUTH_ACTIONS_SUCCESS, {
allowChangePassword,
allowResetPassword,
})
),
loadServerAuthActionsFailed: (error: any) => (
createAction(AuthActionTypes.LOAD_AUTH_ACTIONS_FAILED, { error })
),
}),
loadServerAuthActionsFailed: (error: any) => createAction(AuthActionTypes.LOAD_AUTH_ACTIONS_FAILED, { error }),
};
export type AuthActions = ActionUnion<typeof authActions>;
@ -88,14 +78,19 @@ export const registerAsync = (
password1: string,
password2: string,
confirmations: UserConfirmation[],
): ThunkAction => async (
dispatch,
) => {
): ThunkAction => async (dispatch) => {
dispatch(authActions.register());
try {
const user = await cvat.server.register(username, firstName, lastName, email, password1,
password2, confirmations);
const user = await cvat.server.register(
username,
firstName,
lastName,
email,
password1,
password2,
confirmations,
);
dispatch(authActions.registerSuccess(user));
} catch (error) {
@ -142,8 +137,11 @@ export const authorizedAsync = (): ThunkAction => async (dispatch) => {
}
};
export const changePasswordAsync = (oldPassword: string,
newPassword1: string, newPassword2: string): ThunkAction => async (dispatch) => {
export const changePasswordAsync = (
oldPassword: string,
newPassword1: string,
newPassword2: string,
): ThunkAction => async (dispatch) => {
dispatch(authActions.changePassword());
try {
@ -189,14 +187,9 @@ export const loadAuthActionsAsync = (): ThunkAction => async (dispatch) => {
isReachable(`${cvat.config.backendAPI}/auth/password/change`, 'OPTIONS'),
isReachable(`${cvat.config.backendAPI}/auth/password/reset`, 'OPTIONS'),
];
const [
allowChangePassword,
allowResetPassword] = await Promise.all(promises);
const [allowChangePassword, allowResetPassword] = await Promise.all(promises);
dispatch(authActions.loadServerAuthActionsSuccess(
allowChangePassword,
allowResetPassword,
));
dispatch(authActions.loadServerAuthActionsSuccess(allowChangePassword, allowResetPassword));
} catch (error) {
dispatch(authActions.loadServerAuthActionsFailed(error));
}

@ -2,12 +2,7 @@
//
// SPDX-License-Identifier: MIT
import {
ActionUnion,
createAction,
ThunkAction,
ThunkDispatch,
} from 'utils/redux';
import { ActionUnion, createAction, ThunkAction, ThunkDispatch } from 'utils/redux';
import getCore from 'cvat-core-wrapper';
import { LogType } from 'cvat-logger';
import { computeZRange } from './annotation-actions';
@ -28,15 +23,16 @@ export const boundariesActions = {
minZ: number,
maxZ: number,
colors: string[],
) => createAction(BoundariesActionTypes.RESET_AFTER_ERROR, {
job,
states,
frameNumber,
frameData,
minZ,
maxZ,
colors,
}),
) =>
createAction(BoundariesActionTypes.RESET_AFTER_ERROR, {
job,
states,
frameNumber,
frameData,
minZ,
maxZ,
colors,
}),
throwResetError: () => createAction(BoundariesActionTypes.THROW_RESET_ERROR),
};
@ -51,33 +47,16 @@ export function resetAfterErrorAsync(): ThunkAction {
const { showAllInterpolationTracks } = state.settings.workspace;
const frameNumber = Math.max(Math.min(job.stopFrame, currentFrame), job.startFrame);
const states = await job.annotations
.get(frameNumber, showAllInterpolationTracks, []);
const states = await job.annotations.get(frameNumber, showAllInterpolationTracks, []);
const frameData = await job.frames.get(frameNumber);
const [minZ, maxZ] = computeZRange(states);
const colors = [...cvat.enums.colors];
await job.logger.log(LogType.restoreJob);
dispatch(boundariesActions.resetAfterError(
job,
states,
frameNumber,
frameData,
minZ,
maxZ,
colors,
));
dispatch(boundariesActions.resetAfterError(job, states, frameNumber, frameData, minZ, maxZ, colors));
} else {
dispatch(boundariesActions.resetAfterError(
null,
[],
0,
null,
0,
0,
[],
));
dispatch(boundariesActions.resetAfterError(null, [], 0, null, 0, 0, []));
}
} catch (error) {
dispatch(boundariesActions.throwResetError());
@ -85,4 +64,4 @@ export function resetAfterErrorAsync(): ThunkAction {
};
}
export type boundariesActions = ActionUnion<typeof boundariesActions>;
export type BoundariesActions = ActionUnion<typeof boundariesActions>;

@ -15,14 +15,11 @@ export enum FormatsActionTypes {
const formatsActions = {
getFormats: () => createAction(FormatsActionTypes.GET_FORMATS),
getFormatsSuccess: (annotationFormats: any) => (
getFormatsSuccess: (annotationFormats: any) =>
createAction(FormatsActionTypes.GET_FORMATS_SUCCESS, {
annotationFormats,
})
),
getFormatsFailed: (error: any) => (
createAction(FormatsActionTypes.GET_FORMATS_FAILED, { error })
),
}),
getFormatsFailed: (error: any) => createAction(FormatsActionTypes.GET_FORMATS_FAILED, { error }),
};
export type FormatsActions = ActionUnion<typeof formatsActions>;
@ -35,9 +32,7 @@ export function getFormatsAsync(): ThunkAction {
try {
annotationFormats = await cvat.server.formats();
dispatch(
formatsActions.getFormatsSuccess(annotationFormats),
);
dispatch(formatsActions.getFormatsSuccess(annotationFormats));
} catch (error) {
dispatch(formatsActions.getFormatsFailed(error));
}

@ -22,52 +22,44 @@ export enum ModelsActionTypes {
export const modelsActions = {
getModels: () => createAction(ModelsActionTypes.GET_MODELS),
getModelsSuccess: (models: Model[]) => createAction(
ModelsActionTypes.GET_MODELS_SUCCESS, {
getModelsSuccess: (models: Model[]) =>
createAction(ModelsActionTypes.GET_MODELS_SUCCESS, {
models,
},
),
getModelsFailed: (error: any) => createAction(
ModelsActionTypes.GET_MODELS_FAILED, {
}),
getModelsFailed: (error: any) =>
createAction(ModelsActionTypes.GET_MODELS_FAILED, {
error,
},
),
}),
fetchMetaFailed: (error: any) => createAction(ModelsActionTypes.FETCH_META_FAILED, { error }),
getInferenceStatusSuccess: (taskID: number, activeInference: ActiveInference) => createAction(
ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS, {
getInferenceStatusSuccess: (taskID: number, activeInference: ActiveInference) =>
createAction(ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS, {
taskID,
activeInference,
},
),
getInferenceStatusFailed: (taskID: number, error: any) => createAction(
ModelsActionTypes.GET_INFERENCE_STATUS_FAILED, {
}),
getInferenceStatusFailed: (taskID: number, error: any) =>
createAction(ModelsActionTypes.GET_INFERENCE_STATUS_FAILED, {
taskID,
error,
},
),
startInferenceFailed: (taskID: number, error: any) => createAction(
ModelsActionTypes.START_INFERENCE_FAILED, {
}),
startInferenceFailed: (taskID: number, error: any) =>
createAction(ModelsActionTypes.START_INFERENCE_FAILED, {
taskID,
error,
},
),
cancelInferenceSuccess: (taskID: number) => createAction(
ModelsActionTypes.CANCEL_INFERENCE_SUCCESS, {
}),
cancelInferenceSuccess: (taskID: number) =>
createAction(ModelsActionTypes.CANCEL_INFERENCE_SUCCESS, {
taskID,
},
),
cancelInferenceFailed: (taskID: number, error: any) => createAction(
ModelsActionTypes.CANCEL_INFERENCE_FAILED, {
}),
cancelInferenceFailed: (taskID: number, error: any) =>
createAction(ModelsActionTypes.CANCEL_INFERENCE_FAILED, {
taskID,
error,
},
),
}),
closeRunModelDialog: () => createAction(ModelsActionTypes.CLOSE_RUN_MODEL_DIALOG),
showRunModelDialog: (taskInstance: any) => createAction(
ModelsActionTypes.SHOW_RUN_MODEL_DIALOG, {
showRunModelDialog: (taskInstance: any) =>
createAction(ModelsActionTypes.SHOW_RUN_MODEL_DIALOG, {
taskInstance,
},
),
}),
};
export type ModelsActions = ActionUnion<typeof modelsActions>;
@ -87,43 +79,45 @@ export function getModelsAsync(): ThunkAction {
};
}
interface InferenceMeta {
taskID: number;
requestID: string;
}
function listen(
inferenceMeta: InferenceMeta,
dispatch: (action: ModelsActions) => void,
): void {
function listen(inferenceMeta: InferenceMeta, dispatch: (action: ModelsActions) => void): void {
const { taskID, requestID } = inferenceMeta;
core.lambda.listen(requestID, (status: RQStatus, progress: number, message: string) => {
if (status === RQStatus.failed || status === RQStatus.unknown) {
dispatch(modelsActions.getInferenceStatusFailed(
taskID,
new Error(
`Inference status for the task ${taskID} is ${status}. ${message}`,
),
));
return;
}
dispatch(modelsActions.getInferenceStatusSuccess(taskID, {
status,
progress,
error: message,
id: requestID,
}));
}).catch((error: Error) => {
dispatch(modelsActions.getInferenceStatusFailed(taskID, {
status: 'unknown',
progress: 0,
error: error.toString(),
id: requestID,
}));
});
core.lambda
.listen(requestID, (status: RQStatus, progress: number, message: string) => {
if (status === RQStatus.failed || status === RQStatus.unknown) {
dispatch(
modelsActions.getInferenceStatusFailed(
taskID,
new Error(`Inference status for the task ${taskID} is ${status}. ${message}`),
),
);
return;
}
dispatch(
modelsActions.getInferenceStatusSuccess(taskID, {
status,
progress,
error: message,
id: requestID,
}),
);
})
.catch((error: Error) => {
dispatch(
modelsActions.getInferenceStatusFailed(taskID, {
status: 'unknown',
progress: 0,
error: error.toString(),
id: requestID,
}),
);
});
}
export function getInferenceStatusAsync(): ThunkAction {
@ -148,11 +142,7 @@ export function getInferenceStatusAsync(): ThunkAction {
};
}
export function startInferenceAsync(
taskInstance: any,
model: Model,
body: object,
): ThunkAction {
export function startInferenceAsync(taskInstance: any, model: Model, body: object): ThunkAction {
return async (dispatch): Promise<void> => {
try {
const requestID: string = await core.lambda.run(taskInstance, model, body);
@ -160,10 +150,13 @@ export function startInferenceAsync(
dispatch(action);
};
listen({
taskID: taskInstance.id,
requestID,
}, dispatchCallback);
listen(
{
taskID: taskInstance.id,
requestID,
},
dispatchCallback,
);
} catch (error) {
dispatch(modelsActions.startInferenceFailed(taskInstance.id, error));
}

@ -16,12 +16,8 @@ export enum PluginsActionTypes {
const pluginActions = {
checkPlugins: () => createAction(PluginsActionTypes.GET_PLUGINS),
checkPluginsSuccess: (list: PluginsList) => createAction(
PluginsActionTypes.GET_PLUGINS_SUCCESS, { list },
),
checkPluginsFailed: (error: any) => createAction(
PluginsActionTypes.GET_PLUGINS_FAILED, { error },
),
checkPluginsSuccess: (list: PluginsList) => createAction(PluginsActionTypes.GET_PLUGINS_SUCCESS, { list }),
checkPluginsFailed: (error: any) => createAction(PluginsActionTypes.GET_PLUGINS_FAILED, { error }),
};
export type PluginActions = ActionUnion<typeof pluginActions>;

@ -3,10 +3,7 @@
// SPDX-License-Identifier: MIT
import { AnyAction } from 'redux';
import {
GridColor,
ColorBy,
} from 'reducers/interfaces';
import { GridColor, ColorBy } from 'reducers/interfaces';
export enum SettingsActionTypes {
SWITCH_ROTATE_ALL = 'SWITCH_ROTATE_ALL',

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save