CVAT.js (part 1) (#463)
parent
40fe468196
commit
923bcacd77
@ -1,2 +1,3 @@
|
||||
exclude_paths:
|
||||
- '**/3rdparty/**'
|
||||
- '**/engine/js/cvat.js'
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@ -0,0 +1,14 @@
|
||||
const { override, fixBabelImports, addLessLoader } = require('customize-cra');
|
||||
|
||||
module.exports = override(
|
||||
fixBabelImports('import', {
|
||||
libraryName: 'antd',
|
||||
libraryDirectory: 'es',
|
||||
style: true,
|
||||
}),
|
||||
|
||||
addLessLoader({
|
||||
javascriptEnabled: true,
|
||||
// modifyVars: { '@primary-color': '#1DA57A' },
|
||||
}),
|
||||
);
|
||||
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "cvat-ui",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@types/jest": "24.0.13",
|
||||
"@types/node": "12.0.3",
|
||||
"@types/react": "16.8.19",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"antd": "^3.19.1",
|
||||
"babel-plugin-import": "^1.11.2",
|
||||
"customize-cra": "^0.2.12",
|
||||
"less": "^3.9.0",
|
||||
"less-loader": "^5.0.0",
|
||||
"react": "^16.8.6",
|
||||
"react-app-rewired": "^2.1.3",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"source-map-explorer": "^1.8.0",
|
||||
"typescript": "3.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "react-app-rewired start",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
.App {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: stretch;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import './App.css';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
<Button type="primary">Button</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@ -0,0 +1,143 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(
|
||||
(process as { env: { [key: string]: string } }).env.PUBLIC_URL,
|
||||
window.location.href
|
||||
);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,107 @@
|
||||
/*!
|
||||
* Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/)
|
||||
*/
|
||||
/*!
|
||||
* Bootstrap v3.4.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
.pagination {
|
||||
display: inline-block;
|
||||
padding-left: 0;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.pagination > li {
|
||||
display: inline;
|
||||
}
|
||||
.pagination > li > a,
|
||||
.pagination > li > span {
|
||||
position: relative;
|
||||
float: left;
|
||||
padding: 6px 12px;
|
||||
margin-left: -1px;
|
||||
line-height: 1.42857143;
|
||||
color: #337ab7;
|
||||
text-decoration: none;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
.pagination > li > a:hover,
|
||||
.pagination > li > span:hover,
|
||||
.pagination > li > a:focus,
|
||||
.pagination > li > span:focus {
|
||||
z-index: 2;
|
||||
color: #23527c;
|
||||
background-color: #eeeeee;
|
||||
border-color: #dddddd;
|
||||
}
|
||||
.pagination > li:first-child > a,
|
||||
.pagination > li:first-child > span {
|
||||
margin-left: 0;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.pagination > li:last-child > a,
|
||||
.pagination > li:last-child > span {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.pagination > .active > a,
|
||||
.pagination > .active > span,
|
||||
.pagination > .active > a:hover,
|
||||
.pagination > .active > span:hover,
|
||||
.pagination > .active > a:focus,
|
||||
.pagination > .active > span:focus {
|
||||
z-index: 3;
|
||||
color: #ffffff;
|
||||
cursor: default;
|
||||
background-color: #337ab7;
|
||||
border-color: #337ab7;
|
||||
}
|
||||
.pagination > .disabled > span,
|
||||
.pagination > .disabled > span:hover,
|
||||
.pagination > .disabled > span:focus,
|
||||
.pagination > .disabled > a,
|
||||
.pagination > .disabled > a:hover,
|
||||
.pagination > .disabled > a:focus {
|
||||
color: #777777;
|
||||
cursor: not-allowed;
|
||||
background-color: #ffffff;
|
||||
border-color: #dddddd;
|
||||
}
|
||||
.pagination-lg > li > a,
|
||||
.pagination-lg > li > span {
|
||||
padding: 10px 16px;
|
||||
font-size: 18px;
|
||||
line-height: 1.3333333;
|
||||
}
|
||||
.pagination-lg > li:first-child > a,
|
||||
.pagination-lg > li:first-child > span {
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
.pagination-lg > li:last-child > a,
|
||||
.pagination-lg > li:last-child > span {
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
.pagination-sm > li > a,
|
||||
.pagination-sm > li > span {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.pagination-sm > li:first-child > a,
|
||||
.pagination-sm > li:first-child > span {
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
.pagination-sm > li:last-child > a,
|
||||
.pagination-sm > li:last-child > span {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
@ -0,0 +1,364 @@
|
||||
/*!
|
||||
* jQuery pagination plugin v1.4.2
|
||||
* http://josecebe.github.io/twbs-pagination/
|
||||
*
|
||||
* Copyright 2014-2018, Eugene Simakin
|
||||
* Released under Apache 2.0 license
|
||||
* http://apache.org/licenses/LICENSE-2.0.html
|
||||
*/
|
||||
(function ($, window, document, undefined) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var old = $.fn.twbsPagination;
|
||||
|
||||
// PROTOTYPE AND CONSTRUCTOR
|
||||
|
||||
var TwbsPagination = function (element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, $.fn.twbsPagination.defaults, options);
|
||||
|
||||
if (this.options.startPage < 1 || this.options.startPage > this.options.totalPages) {
|
||||
throw new Error('Start page option is incorrect');
|
||||
}
|
||||
|
||||
this.options.totalPages = parseInt(this.options.totalPages);
|
||||
if (isNaN(this.options.totalPages)) {
|
||||
throw new Error('Total pages option is not correct!');
|
||||
}
|
||||
|
||||
this.options.visiblePages = parseInt(this.options.visiblePages);
|
||||
if (isNaN(this.options.visiblePages)) {
|
||||
throw new Error('Visible pages option is not correct!');
|
||||
}
|
||||
|
||||
if (this.options.beforePageClick instanceof Function) {
|
||||
this.$element.first().on('beforePage', this.options.beforePageClick);
|
||||
}
|
||||
|
||||
if (this.options.onPageClick instanceof Function) {
|
||||
this.$element.first().on('page', this.options.onPageClick);
|
||||
}
|
||||
|
||||
// hide if only one page exists
|
||||
if (this.options.hideOnlyOnePage && this.options.totalPages == 1) {
|
||||
if (this.options.initiateStartPageClick) {
|
||||
this.$element.trigger('page', 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.options.href) {
|
||||
this.options.startPage = this.getPageFromQueryString();
|
||||
if (!this.options.startPage) {
|
||||
this.options.startPage = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var tagName = (typeof this.$element.prop === 'function') ?
|
||||
this.$element.prop('tagName') : this.$element.attr('tagName');
|
||||
|
||||
if (tagName === 'UL') {
|
||||
this.$listContainer = this.$element;
|
||||
} else {
|
||||
var elements = this.$element;
|
||||
var $newListContainer = $([]);
|
||||
elements.each(function(index) {
|
||||
var $newElem = $("<ul></ul>");
|
||||
$(this).append($newElem);
|
||||
$newListContainer.push($newElem[0]);
|
||||
});
|
||||
this.$listContainer = $newListContainer;
|
||||
this.$element = $newListContainer;
|
||||
}
|
||||
|
||||
this.$listContainer.addClass(this.options.paginationClass);
|
||||
|
||||
if (this.options.initiateStartPageClick) {
|
||||
this.show(this.options.startPage);
|
||||
} else {
|
||||
this.currentPage = this.options.startPage;
|
||||
this.render(this.getPages(this.options.startPage));
|
||||
this.setupEvents();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
TwbsPagination.prototype = {
|
||||
|
||||
constructor: TwbsPagination,
|
||||
|
||||
destroy: function () {
|
||||
this.$element.empty();
|
||||
this.$element.removeData('twbs-pagination');
|
||||
this.$element.off('page');
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
show: function (page) {
|
||||
if (page < 1 || page > this.options.totalPages) {
|
||||
throw new Error('Page is incorrect.');
|
||||
}
|
||||
this.currentPage = page;
|
||||
|
||||
this.$element.trigger('beforePage', page);
|
||||
|
||||
var pages = this.getPages(page);
|
||||
this.render(pages);
|
||||
this.setupEvents();
|
||||
|
||||
this.$element.trigger('page', page);
|
||||
|
||||
return pages;
|
||||
},
|
||||
|
||||
enable: function () {
|
||||
this.show(this.currentPage);
|
||||
},
|
||||
|
||||
disable: function () {
|
||||
var _this = this;
|
||||
this.$listContainer.off('click').on('click', 'li', function (evt) {
|
||||
evt.preventDefault();
|
||||
});
|
||||
this.$listContainer.children().each(function () {
|
||||
var $this = $(this);
|
||||
if (!$this.hasClass(_this.options.activeClass)) {
|
||||
$(this).addClass(_this.options.disabledClass);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
buildListItems: function (pages) {
|
||||
var listItems = [];
|
||||
|
||||
if (this.options.first) {
|
||||
listItems.push(this.buildItem('first', 1));
|
||||
}
|
||||
|
||||
if (this.options.prev) {
|
||||
var prev = pages.currentPage > 1 ? pages.currentPage - 1 : this.options.loop ? this.options.totalPages : 1;
|
||||
listItems.push(this.buildItem('prev', prev));
|
||||
}
|
||||
|
||||
for (var i = 0; i < pages.numeric.length; i++) {
|
||||
listItems.push(this.buildItem('page', pages.numeric[i]));
|
||||
}
|
||||
|
||||
if (this.options.next) {
|
||||
var next = pages.currentPage < this.options.totalPages ? pages.currentPage + 1 : this.options.loop ? 1 : this.options.totalPages;
|
||||
listItems.push(this.buildItem('next', next));
|
||||
}
|
||||
|
||||
if (this.options.last) {
|
||||
listItems.push(this.buildItem('last', this.options.totalPages));
|
||||
}
|
||||
|
||||
return listItems;
|
||||
},
|
||||
|
||||
buildItem: function (type, page) {
|
||||
var $itemContainer = $('<li></li>'),
|
||||
$itemContent = $('<a></a>'),
|
||||
itemText = this.options[type] ? this.makeText(this.options[type], page) : page;
|
||||
|
||||
$itemContainer.addClass(this.options[type + 'Class']);
|
||||
$itemContainer.data('page', page);
|
||||
$itemContainer.data('page-type', type);
|
||||
$itemContainer.append($itemContent.attr('href', this.makeHref(page)).addClass(this.options.anchorClass).html(itemText));
|
||||
|
||||
return $itemContainer;
|
||||
},
|
||||
|
||||
getPages: function (currentPage) {
|
||||
var pages = [];
|
||||
|
||||
var half = Math.floor(this.options.visiblePages / 2);
|
||||
var start = currentPage - half + 1 - this.options.visiblePages % 2;
|
||||
var end = currentPage + half;
|
||||
|
||||
var visiblePages = this.options.visiblePages;
|
||||
if (visiblePages > this.options.totalPages) {
|
||||
visiblePages = this.options.totalPages;
|
||||
}
|
||||
|
||||
// handle boundary case
|
||||
if (start <= 0) {
|
||||
start = 1;
|
||||
end = visiblePages;
|
||||
}
|
||||
if (end > this.options.totalPages) {
|
||||
start = this.options.totalPages - visiblePages + 1;
|
||||
end = this.options.totalPages;
|
||||
}
|
||||
|
||||
var itPage = start;
|
||||
while (itPage <= end) {
|
||||
pages.push(itPage);
|
||||
itPage++;
|
||||
}
|
||||
|
||||
return {"currentPage": currentPage, "numeric": pages};
|
||||
},
|
||||
|
||||
render: function (pages) {
|
||||
var _this = this;
|
||||
this.$listContainer.children().remove();
|
||||
var items = this.buildListItems(pages);
|
||||
$.each(items, function(key, item){
|
||||
_this.$listContainer.append(item);
|
||||
});
|
||||
|
||||
this.$listContainer.children().each(function () {
|
||||
var $this = $(this),
|
||||
pageType = $this.data('page-type');
|
||||
|
||||
switch (pageType) {
|
||||
case 'page':
|
||||
if ($this.data('page') === pages.currentPage) {
|
||||
$this.addClass(_this.options.activeClass);
|
||||
}
|
||||
break;
|
||||
case 'first':
|
||||
$this.toggleClass(_this.options.disabledClass, pages.currentPage === 1);
|
||||
break;
|
||||
case 'last':
|
||||
$this.toggleClass(_this.options.disabledClass, pages.currentPage === _this.options.totalPages);
|
||||
break;
|
||||
case 'prev':
|
||||
$this.toggleClass(_this.options.disabledClass, !_this.options.loop && pages.currentPage === 1);
|
||||
break;
|
||||
case 'next':
|
||||
$this.toggleClass(_this.options.disabledClass,
|
||||
!_this.options.loop && pages.currentPage === _this.options.totalPages);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
setupEvents: function () {
|
||||
var _this = this;
|
||||
this.$listContainer.off('click').on('click', 'li', function (evt) {
|
||||
var $this = $(this);
|
||||
if ($this.hasClass(_this.options.disabledClass) || $this.hasClass(_this.options.activeClass)) {
|
||||
return false;
|
||||
}
|
||||
// Prevent click event if href is not set.
|
||||
!_this.options.href && evt.preventDefault();
|
||||
_this.show(parseInt($this.data('page')));
|
||||
});
|
||||
},
|
||||
|
||||
changeTotalPages: function(totalPages, currentPage) {
|
||||
this.options.totalPages = totalPages;
|
||||
return this.show(currentPage);
|
||||
},
|
||||
|
||||
makeHref: function (page) {
|
||||
return this.options.href ? this.generateQueryString(page) : "#";
|
||||
},
|
||||
|
||||
makeText: function (text, page) {
|
||||
return text.replace(this.options.pageVariable, page)
|
||||
.replace(this.options.totalPagesVariable, this.options.totalPages)
|
||||
},
|
||||
|
||||
getPageFromQueryString: function (searchStr) {
|
||||
var search = this.getSearchString(searchStr),
|
||||
regex = new RegExp(this.options.pageVariable + '(=([^&#]*)|&|#|$)'),
|
||||
page = regex.exec(search);
|
||||
if (!page || !page[2]) {
|
||||
return null;
|
||||
}
|
||||
page = decodeURIComponent(page[2]);
|
||||
page = parseInt(page);
|
||||
if (isNaN(page)) {
|
||||
return null;
|
||||
}
|
||||
return page;
|
||||
},
|
||||
|
||||
generateQueryString: function (pageNumber, searchStr) {
|
||||
var search = this.getSearchString(searchStr),
|
||||
regex = new RegExp(this.options.pageVariable + '=*[^&#]*');
|
||||
if (!search) return '';
|
||||
return '?' + search.replace(regex, this.options.pageVariable + '=' + pageNumber);
|
||||
},
|
||||
|
||||
getSearchString: function (searchStr) {
|
||||
var search = searchStr || window.location.search;
|
||||
if (search === '') {
|
||||
return null;
|
||||
}
|
||||
if (search.indexOf('?') === 0) search = search.substr(1);
|
||||
return search;
|
||||
},
|
||||
|
||||
getCurrentPage: function () {
|
||||
return this.currentPage;
|
||||
},
|
||||
|
||||
getTotalPages: function () {
|
||||
return this.options.totalPages;
|
||||
}
|
||||
};
|
||||
|
||||
// PLUGIN DEFINITION
|
||||
|
||||
$.fn.twbsPagination = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var methodReturn;
|
||||
|
||||
var $this = $(this);
|
||||
var data = $this.data('twbs-pagination');
|
||||
var options = typeof option === 'object' ? option : {};
|
||||
|
||||
if (!data) $this.data('twbs-pagination', (data = new TwbsPagination(this, options) ));
|
||||
if (typeof option === 'string') methodReturn = data[ option ].apply(data, args);
|
||||
|
||||
return ( methodReturn === undefined ) ? $this : methodReturn;
|
||||
};
|
||||
|
||||
$.fn.twbsPagination.defaults = {
|
||||
totalPages: 1,
|
||||
startPage: 1,
|
||||
visiblePages: 5,
|
||||
initiateStartPageClick: true,
|
||||
hideOnlyOnePage: false,
|
||||
href: false,
|
||||
pageVariable: '{{page}}',
|
||||
totalPagesVariable: '{{total_pages}}',
|
||||
page: null,
|
||||
first: 'First',
|
||||
prev: 'Previous',
|
||||
next: 'Next',
|
||||
last: 'Last',
|
||||
loop: false,
|
||||
beforePageClick: null,
|
||||
onPageClick: null,
|
||||
paginationClass: 'pagination',
|
||||
nextClass: 'page-item next',
|
||||
prevClass: 'page-item prev',
|
||||
lastClass: 'page-item last',
|
||||
firstClass: 'page-item first',
|
||||
pageClass: 'page-item',
|
||||
activeClass: 'active',
|
||||
disabledClass: 'disabled',
|
||||
anchorClass: 'page-link'
|
||||
};
|
||||
|
||||
$.fn.twbsPagination.Constructor = TwbsPagination;
|
||||
|
||||
$.fn.twbsPagination.noConflict = function () {
|
||||
$.fn.twbsPagination = old;
|
||||
return this;
|
||||
};
|
||||
|
||||
$.fn.twbsPagination.version = "1.4.2";
|
||||
|
||||
})(window.jQuery, window, document);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,125 @@
|
||||
# Generated by Django 2.1.7 on 2019-06-18 11:08
|
||||
|
||||
from django.db import migrations
|
||||
from django.conf import settings
|
||||
|
||||
from cvat.apps.engine.task import get_image_meta_cache
|
||||
from cvat.apps.engine.models import Job, ShapeType
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def _flip_shape(shape, size):
|
||||
if shape.type == ShapeType.RECTANGLE:
|
||||
shape.points = [
|
||||
shape.points[2], # xbr -> xtl
|
||||
shape.points[3], # ybr -> ytl
|
||||
shape.points[0], # xtl -> xbr
|
||||
shape.points[1] # ytl -> ybr
|
||||
]
|
||||
|
||||
for x in range(0, len(shape.points), 2):
|
||||
y = x + 1
|
||||
shape.points[x] = size['width'] - shape.points[x]
|
||||
shape.points[y] = size['height'] - shape.points[y]
|
||||
|
||||
|
||||
def frame_path(db_task, frame):
|
||||
task_dirname = os.path.join(settings.DATA_ROOT, str(db_task.id))
|
||||
d1 = str(int(frame) // 10000)
|
||||
d2 = str(int(frame) // 100)
|
||||
path = os.path.join(task_dirname, 'data', d1, d2, str(frame) + '.jpg')
|
||||
return path
|
||||
|
||||
|
||||
def _get_image_meta_cache_path(self):
|
||||
task_dirname = os.path.join(settings.DATA_ROOT, str(self.id))
|
||||
return os.path.join(task_dirname, "image_meta.cache")
|
||||
|
||||
|
||||
def forwards_func(apps, schema_editor):
|
||||
Task = apps.get_model('engine', 'Task')
|
||||
|
||||
# class methods unavailable in the class which got via get_model()
|
||||
# nevertheless it is needed for us to use the function get_image_meta_cache()
|
||||
setattr(Task, 'get_image_meta_cache_path', _get_image_meta_cache_path)
|
||||
|
||||
print('Getting flipped tasks...')
|
||||
db_flipped_tasks = Task.objects.prefetch_related(
|
||||
'image_set',
|
||||
).filter(flipped=True).all()
|
||||
|
||||
print('Conversion started...')
|
||||
for db_task in db_flipped_tasks:
|
||||
print('Processing task {}...'.format(db_task.id))
|
||||
db_image_by_frame = {}
|
||||
if db_task.mode == 'annotation':
|
||||
db_image_by_frame = {db_image.frame: {'width': db_image.width, 'height': db_image.height}
|
||||
for db_image in db_task.image_set.all()}
|
||||
else:
|
||||
im_meta_data = get_image_meta_cache(db_task)['original_size']
|
||||
db_image_by_frame = {
|
||||
0: {
|
||||
'width': im_meta_data[0]['width'],
|
||||
'height': im_meta_data[0]['height']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_size(frame):
|
||||
if frame in db_image_by_frame:
|
||||
return db_image_by_frame[frame]
|
||||
else:
|
||||
return db_image_by_frame[0]
|
||||
|
||||
db_jobs = Job.objects.select_related('segment').prefetch_related(
|
||||
'labeledshape_set',
|
||||
'labeledtrack_set',
|
||||
'labeledtrack_set__trackedshape_set').filter(segment__task_id=db_task.id).all()
|
||||
|
||||
for db_job in db_jobs:
|
||||
db_shapes = db_job.labeledshape_set.all()
|
||||
db_tracks = db_job.labeledtrack_set.all()
|
||||
for db_shape in db_shapes:
|
||||
_flip_shape(db_shape, get_size(db_shape.frame))
|
||||
db_shape.save()
|
||||
|
||||
for db_track in db_tracks:
|
||||
db_shapes = db_track.trackedshape_set.all()
|
||||
for db_shape in db_shapes:
|
||||
_flip_shape(db_shape, get_size(db_shape.frame))
|
||||
db_shape.save()
|
||||
|
||||
for db_task in db_flipped_tasks:
|
||||
for frame in range(db_task.size):
|
||||
path = frame_path(db_task, frame)
|
||||
if os.path.islink(path):
|
||||
path = os.path.realpath(path)
|
||||
|
||||
try:
|
||||
image = Image.open(path)
|
||||
image = image.transpose(Image.ROTATE_180)
|
||||
image.save(path)
|
||||
except IOError as ex:
|
||||
print('Error of handling the frame {}'.format(frame))
|
||||
print(ex)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('engine', '0019_frame_selection'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
forwards_func
|
||||
),
|
||||
|
||||
migrations.RemoveField(
|
||||
model_name='task',
|
||||
name='flipped',
|
||||
),
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
"env": {
|
||||
"node": false,
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"jquery": true,
|
||||
"qunit": true,
|
||||
},
|
||||
"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",
|
||||
],
|
||||
"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,
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,38 @@
|
||||
# Computer Vision Annotation Tool (JS)
|
||||
|
||||
## Description
|
||||
This CVAT module has been created in order to easy integration process with CVAT.
|
||||
|
||||
### Short development manual
|
||||
|
||||
- Install dependencies
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
- Build library from sources in ```dist``` directory:
|
||||
```bash
|
||||
npm run-script build
|
||||
npm run build -- --mode=development # without a minification
|
||||
```
|
||||
|
||||
- Build documentation in ```docs``` directory:
|
||||
```bash
|
||||
npm run-script docs
|
||||
```
|
||||
|
||||
- Run tests:
|
||||
```bash
|
||||
npm run-script test
|
||||
```
|
||||
|
||||
- Update version of library:
|
||||
```bash
|
||||
npm version patch # updated after minor fixes
|
||||
npm version minor # updated after major changes which don't affect API compatibility with previous versions
|
||||
npm version major # updated after major changes which affect API compatibility with previous versions
|
||||
```
|
||||
|
||||
Visual studio code configurations:
|
||||
- cvat.js debug starts debugging with entrypoint api.js
|
||||
- cvat.js test builds library and runs entrypoint tests.js
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
const { defaults } = require('jest-config');
|
||||
|
||||
module.exports = {
|
||||
moduleFileExtensions: [
|
||||
...defaults.moduleFileExtensions,
|
||||
'ts',
|
||||
'tsx',
|
||||
],
|
||||
reporters: [
|
||||
'default',
|
||||
'jest-junit',
|
||||
],
|
||||
testMatch: [
|
||||
'**/tests/**/*.js',
|
||||
],
|
||||
testPathIgnorePatterns: [
|
||||
'/node_modules/',
|
||||
'/tests/mocks/*',
|
||||
],
|
||||
automock: false,
|
||||
};
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
module.exports = {
|
||||
plugins: [],
|
||||
recurseDepth: 10,
|
||||
source: {
|
||||
includePattern: '.+\\.js(doc|x)?$',
|
||||
excludePattern: '(^|\\/|\\\\)_',
|
||||
},
|
||||
sourceType: 'module',
|
||||
tags: {
|
||||
allowUnknownTags: false,
|
||||
dictionaries: ['jsdoc', 'closure'],
|
||||
},
|
||||
templates: {
|
||||
cleverLinks: false,
|
||||
monospaceLinks: false,
|
||||
default: {
|
||||
outputSourceFiles: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "cvat.js",
|
||||
"version": "1.0.0",
|
||||
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
|
||||
"main": "babel.config.js",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"test": "jest --config=jest.config.js",
|
||||
"docs": "jsdoc --readme README.md src/*.js -p -c jsdoc.config.js -d docs"
|
||||
},
|
||||
"author": "Intel",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.4.4",
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/preset-env": "^7.4.4",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^8.0.6",
|
||||
"core-js": "^3.0.1",
|
||||
"jest": "^24.8.0",
|
||||
"jest-junit": "^6.4.0",
|
||||
"jsdoc": "^3.6.2",
|
||||
"webpack": "^4.31.0",
|
||||
"webpack-cli": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"browser-env": "^3.2.6",
|
||||
"error-stack-parser": "^2.0.2",
|
||||
"jest-config": "^24.8.0",
|
||||
"js-cookie": "^2.2.0",
|
||||
"platform": "^1.3.5",
|
||||
"stacktrace-gps": "^3.0.2"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
|
||||
(() => {
|
||||
const PluginRegistry = require('./plugins');
|
||||
const serverProxy = require('./server-proxy');
|
||||
|
||||
const {
|
||||
Task,
|
||||
Job,
|
||||
} = require('./session');
|
||||
|
||||
function isBoolean(value) {
|
||||
return typeof (value) === 'boolean';
|
||||
}
|
||||
|
||||
function isInteger(value) {
|
||||
return typeof (value) === 'number' && Number.isInteger(value);
|
||||
}
|
||||
|
||||
function isEnum(value) {
|
||||
// Called with specific Enum context
|
||||
for (const key in this) {
|
||||
if (Object.prototype.hasOwnProperty.call(this, key)) {
|
||||
if (this[key] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isString(value) {
|
||||
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 window.cvat.exceptions.ArgumentError(
|
||||
`Unsupported filter property has been recieved: "${prop}"`,
|
||||
);
|
||||
} else if (!fields[prop](filter[prop])) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
`Received filter property ${prop} is not satisfied for checker`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hidden = require('./hidden');
|
||||
function setupEnv(wrappedFunction) {
|
||||
return async function wrapper(...args) {
|
||||
try {
|
||||
if (this instanceof window.cvat.classes.Task) {
|
||||
hidden.taskID = this.id;
|
||||
} else if (this instanceof window.cvat.classes.Job) {
|
||||
hidden.jobID = this.id;
|
||||
hidden.taskID = this.task.id;
|
||||
} else {
|
||||
throw new window.cvat.exceptions.ScriptingError('Bad context for the function');
|
||||
}
|
||||
|
||||
const result = await wrappedFunction.call(this, ...args);
|
||||
return result;
|
||||
} finally {
|
||||
delete hidden.taskID;
|
||||
delete hidden.jobID;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function implementAPI(cvat) {
|
||||
cvat.plugins.list.implementation = PluginRegistry.list;
|
||||
cvat.plugins.register.implementation = PluginRegistry.register;
|
||||
|
||||
cvat.server.about.implementation = async () => {
|
||||
const result = await serverProxy.server.about();
|
||||
return result;
|
||||
};
|
||||
|
||||
cvat.server.share.implementation = async (directory) => {
|
||||
const result = await serverProxy.server.share(directory);
|
||||
return result;
|
||||
};
|
||||
|
||||
cvat.server.login.implementation = async (username, password) => {
|
||||
await serverProxy.server.login(username, password);
|
||||
};
|
||||
|
||||
cvat.users.get.implementation = async (filter) => {
|
||||
checkFilter(filter, {
|
||||
self: isBoolean,
|
||||
});
|
||||
|
||||
let users = null;
|
||||
if ('self' in filter && filter.self) {
|
||||
users = await serverProxy.users.getSelf();
|
||||
users = [users];
|
||||
} else {
|
||||
users = await serverProxy.users.getUsers();
|
||||
}
|
||||
|
||||
users = users.map(user => new window.cvat.classes.User(user));
|
||||
return users;
|
||||
};
|
||||
|
||||
cvat.jobs.get.implementation = async (filter) => {
|
||||
checkFilter(filter, {
|
||||
taskID: isInteger,
|
||||
jobID: isInteger,
|
||||
});
|
||||
|
||||
if (('taskID' in filter) && ('jobID' in filter)) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Only one of fields "taskID" and "jobID" allowed simultaneously',
|
||||
);
|
||||
}
|
||||
|
||||
if (!Object.keys(filter).length) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Job filter must not be empty',
|
||||
);
|
||||
}
|
||||
|
||||
let task = null;
|
||||
if ('taskID' in filter) {
|
||||
task = await serverProxy.tasks.getTasks(`id=${filter.taskID}`);
|
||||
} else {
|
||||
const [job] = await serverProxy.jobs.getJob(filter.jobID);
|
||||
task = 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 (task.length) {
|
||||
task = new window.cvat.classes.Task(task[0]);
|
||||
return filter.jobID ? task.jobs.filter(job => job.id === filter.jobID) : task.jobs;
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
cvat.tasks.get.implementation = async (filter) => {
|
||||
checkFilter(filter, {
|
||||
page: isInteger,
|
||||
name: isString,
|
||||
id: isInteger,
|
||||
owner: isString,
|
||||
assignee: isString,
|
||||
search: isString,
|
||||
status: isEnum.bind(window.cvat.enums.TaskStatus),
|
||||
mode: isEnum.bind(window.cvat.enums.TaskMode),
|
||||
});
|
||||
|
||||
if ('search' in filter && Object.keys(filter).length > 1) {
|
||||
if (!('page' in filter && Object.keys(filter).length === 2)) {
|
||||
throw new window.cvat.exceptions.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 window.cvat.exceptions.ArgumentError(
|
||||
'Do not use the filter field "id" with others',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams();
|
||||
for (const field of ['name', 'owner', 'assignee', 'search', 'status', 'mode', 'id', 'page']) {
|
||||
if (Object.prototype.hasOwnProperty.call(filter, field)) {
|
||||
searchParams.set(field, filter[field]);
|
||||
}
|
||||
}
|
||||
|
||||
const tasksData = await serverProxy.tasks.getTasks(searchParams.toString());
|
||||
const tasks = tasksData.map(task => new window.cvat.classes.Task(task));
|
||||
tasks.count = tasksData.count;
|
||||
|
||||
return tasks;
|
||||
};
|
||||
|
||||
Task.prototype.save.implementation = setupEnv(
|
||||
async function saveTaskImplementation(onUpdate) {
|
||||
// TODO: Add ability to change an owner and an assignee
|
||||
if (typeof (this.id) !== 'undefined') {
|
||||
// If the task has been already created, we update it
|
||||
const taskData = {
|
||||
name: this.name,
|
||||
bug_tracker: this.bugTracker,
|
||||
z_order: this.zOrder,
|
||||
labels: [...this.labels.map(el => el.toJSON())],
|
||||
};
|
||||
|
||||
await serverProxy.tasks.saveTask(this.id, taskData);
|
||||
return this;
|
||||
}
|
||||
|
||||
const taskData = {
|
||||
name: this.name,
|
||||
labels: this.labels.map(el => el.toJSON()),
|
||||
image_quality: this.imageQuality,
|
||||
z_order: Boolean(this.zOrder),
|
||||
};
|
||||
|
||||
if (this.bugTracker) {
|
||||
taskData.bug_tracker = this.bugTracker;
|
||||
}
|
||||
if (this.segmentSize) {
|
||||
taskData.segment_size = this.segmentSize;
|
||||
}
|
||||
if (this.overlap) {
|
||||
taskData.overlap = this.overlap;
|
||||
}
|
||||
|
||||
const taskFiles = {
|
||||
client_files: this.clientFiles,
|
||||
server_files: this.serverFiles,
|
||||
remote_files: [], // hasn't been supported yet
|
||||
};
|
||||
|
||||
const task = await serverProxy.tasks.createTask(taskData, taskFiles, onUpdate);
|
||||
return new Task(task);
|
||||
},
|
||||
);
|
||||
|
||||
Task.prototype.delete.implementation = setupEnv(
|
||||
async function deleteTaskImplementation() {
|
||||
await serverProxy.tasks.deleteTask(this.id);
|
||||
},
|
||||
);
|
||||
|
||||
Job.prototype.save.implementation = setupEnv(
|
||||
async function saveJobImplementation() {
|
||||
// TODO: Add ability to change an assignee
|
||||
if (this.id) {
|
||||
const jobData = {
|
||||
status: this.status,
|
||||
};
|
||||
|
||||
await serverProxy.jobs.saveJob(this.id, jobData);
|
||||
return this;
|
||||
}
|
||||
|
||||
throw window.cvat.exceptions.ArgumentError(
|
||||
'Can not save job without and id',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
return cvat;
|
||||
}
|
||||
|
||||
module.exports = implementAPI;
|
||||
})();
|
||||
@ -0,0 +1,650 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
/**
|
||||
* External API which should be used by for development
|
||||
* @module API
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const PluginRegistry = require('./plugins');
|
||||
const User = require('./user');
|
||||
const ObjectState = require('./object-state');
|
||||
const Statistics = require('./statistics');
|
||||
const { Job, Task } = require('./session');
|
||||
const { Attribute, Label } = require('./labels');
|
||||
|
||||
const {
|
||||
ShareFileType,
|
||||
TaskStatus,
|
||||
TaskMode,
|
||||
AttributeType,
|
||||
ObjectType,
|
||||
ObjectShape,
|
||||
LogType,
|
||||
EventType,
|
||||
} = require('./enums');
|
||||
|
||||
const {
|
||||
Exception,
|
||||
ArgumentError,
|
||||
ScriptingError,
|
||||
PluginError,
|
||||
ServerError,
|
||||
} = require('./exceptions');
|
||||
|
||||
const pjson = require('../package.json');
|
||||
|
||||
function buildDublicatedAPI() {
|
||||
const annotations = {
|
||||
async upload(file) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.upload, file);
|
||||
return result;
|
||||
},
|
||||
|
||||
async save() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.save);
|
||||
return result;
|
||||
},
|
||||
|
||||
async clear() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.clear);
|
||||
return result;
|
||||
},
|
||||
|
||||
async dump() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.dump);
|
||||
return result;
|
||||
},
|
||||
|
||||
async statistics() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.statistics);
|
||||
return result;
|
||||
},
|
||||
|
||||
async put(arrayOfObjects = []) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.put, arrayOfObjects);
|
||||
return result;
|
||||
},
|
||||
|
||||
async get(frame, filter = {}) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.get, frame, filter);
|
||||
return result;
|
||||
},
|
||||
|
||||
async search(filter, frameFrom, frameTo) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.search, filter, frameFrom, frameTo);
|
||||
return result;
|
||||
},
|
||||
|
||||
async select(frame, x, y) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, annotations.select, frame, x, y);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
const frames = {
|
||||
async get(frame) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, frames.get, frame);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
const logs = {
|
||||
async put(logType, details) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, logs.put, logType, details);
|
||||
return result;
|
||||
},
|
||||
async save() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, logs.save);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
const actions = {
|
||||
async undo(count) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, actions.undo, count);
|
||||
return result;
|
||||
},
|
||||
async redo(count) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, actions.redo, count);
|
||||
return result;
|
||||
},
|
||||
async clear() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, actions.clear);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
const events = {
|
||||
async subscribe(eventType, callback) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, events.subscribe, eventType, callback);
|
||||
return result;
|
||||
},
|
||||
async unsubscribe(eventType, callback = null) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, events.unsubscribe, eventType, callback);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
annotations,
|
||||
frames,
|
||||
logs,
|
||||
actions,
|
||||
events,
|
||||
};
|
||||
}
|
||||
|
||||
// Two copies of API for Task and for Job
|
||||
const jobAPI = buildDublicatedAPI();
|
||||
const taskAPI = buildDublicatedAPI();
|
||||
|
||||
/**
|
||||
* API entrypoint
|
||||
* @namespace cvat
|
||||
* @memberof module:API
|
||||
*/
|
||||
const cvat = {
|
||||
/**
|
||||
* Namespace is used for an interaction with a server
|
||||
* @namespace server
|
||||
* @package
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
server: {
|
||||
/**
|
||||
* @typedef {Object} ServerInfo
|
||||
* @property {string} name A name of the tool
|
||||
* @property {string} description A description of the tool
|
||||
* @property {string} version A version of the tool
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method returns some information about the annotation tool
|
||||
* @method about
|
||||
* @async
|
||||
* @memberof module:API.cvat.server
|
||||
* @return {ServerInfo}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async about() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.server.about);
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* @typedef {Object} FileInfo
|
||||
* @property {string} name A name of a file
|
||||
* @property {module:API.cvat.enums.ShareFileType} type
|
||||
* A type of a file
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method returns a list of files in a specified directory on a share
|
||||
* @method share
|
||||
* @async
|
||||
* @memberof module:API.cvat.server
|
||||
* @param {string} [directory=/] - Share directory path
|
||||
* @returns {FileInfo[]}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
*/
|
||||
async share(directory = '/') {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.server.share, directory);
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Method allows to login on a server
|
||||
* @method login
|
||||
* @async
|
||||
* @memberof module:API.cvat.server
|
||||
* @param {string} username An username of an account
|
||||
* @param {string} password A password of an account
|
||||
* @throws {module:API.cvat.exceptions.ScriptingError}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
*/
|
||||
async login(username, password) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.server.login, username, password);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Namespace is used for getting tasks
|
||||
* @namespace tasks
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
tasks: {
|
||||
/**
|
||||
* @typedef {Object} TaskFilter
|
||||
* @property {string} name Check if name contains this value
|
||||
* @property {module:API.cvat.enums.TaskStatus} status
|
||||
* Check if status contains this value
|
||||
* @property {module:API.cvat.enums.TaskMode} mode
|
||||
* Check if mode contains this value
|
||||
* @property {integer} id Check if id equals this value
|
||||
* @property {integer} page Get specific page
|
||||
* (default REST API returns 20 tasks per request.
|
||||
* In order to get more, it is need to specify next page)
|
||||
* @property {string} owner Check if owner user contains this value
|
||||
* @property {string} assignee Check if assigneed contains this value
|
||||
* @property {string} search Combined search of contains among all fields
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method returns list of tasks corresponding to a filter
|
||||
* @method get
|
||||
* @async
|
||||
* @memberof module:API.cvat.tasks
|
||||
* @param {TaskFilter} [filter={}] task filter
|
||||
* @returns {module:API.cvat.classes.Task[]}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
*/
|
||||
async get(filter = {}) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.tasks.get, filter);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Namespace is used for getting jobs
|
||||
* @namespace jobs
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
jobs: {
|
||||
/**
|
||||
* @typedef {Object} JobFilter
|
||||
* Only one of fields is allowed simultaneously
|
||||
* @property {integer} taskID filter all jobs of specific task
|
||||
* @property {integer} jobID filter job with a specific id
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method returns list of jobs corresponding to a filter
|
||||
* @method get
|
||||
* @async
|
||||
* @memberof module:API.cvat.jobs
|
||||
* @param {JobFilter} filter job filter
|
||||
* @returns {module:API.cvat.classes.Job[]}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
*/
|
||||
async get(filter = {}) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.jobs.get, filter);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Namespace is used for getting users
|
||||
* @namespace users
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
users: {
|
||||
/**
|
||||
* @typedef {Object} UserFilter
|
||||
* @property {boolean} self get only self
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method returns list of users corresponding to a filter
|
||||
* @method get
|
||||
* @async
|
||||
* @memberof module:API.cvat.users
|
||||
* @param {UserFilter} [filter={}] user filter
|
||||
* @returns {User[]}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
*/
|
||||
async get(filter = {}) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.users.get, filter);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Namespace is used for plugin management
|
||||
* @namespace plugins
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
plugins: {
|
||||
/**
|
||||
* @typedef {Object} Plugin
|
||||
* A plugin is a Javascript object. It must have properties are listed below. <br>
|
||||
* It also mustn't have property 'functions' which is used internally. <br>
|
||||
* You can expand any API method including class methods. <br>
|
||||
* In order to expand class method just use a class name
|
||||
* in a cvat space (example is listed below).
|
||||
*
|
||||
* @property {string} name A name of a plugin
|
||||
* @property {string} description A description of a plugin
|
||||
* Example plugin implementation listed below:
|
||||
* @example
|
||||
* plugin = {
|
||||
* name: 'Example Plugin',
|
||||
* description: 'This example plugin demonstrates how plugin system in CVAT works',
|
||||
* cvat: {
|
||||
* server: {
|
||||
* about: {
|
||||
* // Plugin adds some actions after executing the cvat.server.about()
|
||||
* // For example it adds a field with installed plugins to a result
|
||||
* // An argument "self" is a plugin itself
|
||||
* // An argument "result" is a return value of cvat.server.about()
|
||||
* // All next arguments are arguments of a wrapped function
|
||||
* // (in this case the wrapped function doesn't have any arguments)
|
||||
* async leave(self, result) {
|
||||
* result.plugins = await self.internal.getPlugins();
|
||||
* // Note that a method leave must return "result" (changed or not)
|
||||
* // Otherwise API won't work as expected
|
||||
* return result;
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* // In this example plugin also wraps a class method
|
||||
* classes: {
|
||||
* Job: {
|
||||
* prototype: {
|
||||
* annotations: {
|
||||
* put: {
|
||||
* // The first argument "self" is a plugin, like in a case above
|
||||
* // The second argument is an argument of the
|
||||
* // cvat.Job.annotations.put()
|
||||
* // It contains an array of objects to put
|
||||
* // In this sample we round objects coordinates and save them
|
||||
* enter(self, objects) {
|
||||
* for (const obj of objects) {
|
||||
* if (obj.type != 'tag') {
|
||||
* const points = obj.position.map((point) => {
|
||||
* const roundPoint = {
|
||||
* x: Math.round(point.x),
|
||||
* y: Math.round(point.y),
|
||||
* };
|
||||
* return roundPoint;
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* // In general you can add any others members to your plugin
|
||||
* // Members below are only examples
|
||||
* internal: {
|
||||
* async getPlugins() {
|
||||
* // Collect information about installed plugins
|
||||
* const plugins = await window.cvat.plugins.list();
|
||||
* return plugins.map((el) => {
|
||||
* return {
|
||||
* name: el.name,
|
||||
* description: el.description,
|
||||
* };
|
||||
* });
|
||||
* },
|
||||
* },
|
||||
* };
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method returns list of installed plugins
|
||||
* @method list
|
||||
* @async
|
||||
* @memberof module:API.cvat.plugins
|
||||
* @returns {Plugin[]}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async list() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.plugins.list);
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Install plugin to CVAT
|
||||
* @method register
|
||||
* @async
|
||||
* @memberof module:API.cvat.plugins
|
||||
* @param {Plugin} [plugin] plugin for registration
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async register(plugin) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper(cvat.plugins.register, plugin);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Namespace contains some changeable configurations
|
||||
* @namespace config
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
config: {
|
||||
/**
|
||||
* @property {string} backendAPI host with a backend api
|
||||
* @memberof module:API.cvat.config
|
||||
* @property {string} proxy Axios proxy settings.
|
||||
* For more details please read <a href="https://github.com/axios/axios"> here </a>
|
||||
* @memberof module:API.cvat.config
|
||||
* @property {integer} preloadFrames the number of subsequent frames which are
|
||||
* loaded in background
|
||||
* @memberof module:API.cvat.config
|
||||
*/
|
||||
preloadFrames: 300,
|
||||
backendAPI: 'http://localhost:7000/api/v1',
|
||||
proxy: false,
|
||||
},
|
||||
/**
|
||||
* Namespace contains some library information e.g. api version
|
||||
* @namespace client
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
client: {
|
||||
/**
|
||||
* @property {string} version Client version.
|
||||
* Format: <b>{major}.{minor}.{patch}</b>
|
||||
* <li style="margin-left: 10px;"> A major number is changed after an API becomes
|
||||
* incompatible with a previous version
|
||||
* <li style="margin-left: 10px;"> A minor number is changed after an API expands
|
||||
* <li style="margin-left: 10px;"> A patch number is changed after an each build
|
||||
* @memberof module:API.cvat.client
|
||||
* @readonly
|
||||
*/
|
||||
version: `${pjson.version}`,
|
||||
},
|
||||
/**
|
||||
* Namespace is used for access to enums
|
||||
* @namespace enums
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
enums: {
|
||||
ShareFileType,
|
||||
TaskStatus,
|
||||
TaskMode,
|
||||
AttributeType,
|
||||
ObjectType,
|
||||
ObjectShape,
|
||||
LogType,
|
||||
EventType,
|
||||
},
|
||||
/**
|
||||
* Namespace is used for access to exceptions
|
||||
* @namespace exceptions
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
exceptions: {
|
||||
Exception,
|
||||
ArgumentError,
|
||||
ScriptingError,
|
||||
PluginError,
|
||||
ServerError,
|
||||
},
|
||||
/**
|
||||
* Namespace is used for access to classes
|
||||
* @namespace classes
|
||||
* @memberof module:API.cvat
|
||||
*/
|
||||
classes: {
|
||||
Task,
|
||||
User,
|
||||
Job,
|
||||
Attribute,
|
||||
Label,
|
||||
Statistics,
|
||||
ObjectState,
|
||||
},
|
||||
};
|
||||
|
||||
cvat.server = Object.freeze(cvat.server);
|
||||
cvat.tasks = Object.freeze(cvat.tasks);
|
||||
cvat.jobs = Object.freeze(cvat.jobs);
|
||||
cvat.users = Object.freeze(cvat.users);
|
||||
cvat.plugins = Object.freeze(cvat.plugins);
|
||||
cvat.client = Object.freeze(cvat.client);
|
||||
cvat.enums = Object.freeze(cvat.enums);
|
||||
cvat.Job = Object.freeze(cvat.Job);
|
||||
cvat.Task = Object.freeze(cvat.Task);
|
||||
|
||||
Object.defineProperties(Job.prototype, Object.freeze({
|
||||
annotations: {
|
||||
value: Object.freeze({
|
||||
upload: jobAPI.annotations.upload,
|
||||
save: jobAPI.annotations.save,
|
||||
clear: jobAPI.annotations.clear,
|
||||
dump: jobAPI.annotations.dump,
|
||||
statistics: jobAPI.annotations,
|
||||
put: jobAPI.annotations.put,
|
||||
get: jobAPI.annotations.get,
|
||||
search: jobAPI.annotations.search,
|
||||
select: jobAPI.annotations.select,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
frames: {
|
||||
value: Object.freeze({
|
||||
get: jobAPI.frames.get,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
logs: {
|
||||
value: Object.freeze({
|
||||
put: jobAPI.logs.put,
|
||||
save: jobAPI.logs.save,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
actions: {
|
||||
value: Object.freeze({
|
||||
undo: jobAPI.actions.undo,
|
||||
redo: jobAPI.actions.redo,
|
||||
clear: jobAPI.actions.clear,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
events: {
|
||||
value: Object.freeze({
|
||||
subscribe: jobAPI.events.subscribe,
|
||||
unsubscribe: jobAPI.events.unsubscribe,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
}));
|
||||
|
||||
Object.defineProperties(Task.prototype, Object.freeze({
|
||||
annotations: {
|
||||
value: Object.freeze({
|
||||
upload: taskAPI.annotations.upload,
|
||||
save: taskAPI.annotations.save,
|
||||
clear: taskAPI.annotations.clear,
|
||||
dump: taskAPI.annotations.dump,
|
||||
statistics: taskAPI.annotations.statistics,
|
||||
put: taskAPI.annotations.put,
|
||||
get: taskAPI.annotations.get,
|
||||
search: taskAPI.annotations.search,
|
||||
select: taskAPI.annotations.select,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
frames: {
|
||||
value: Object.freeze({
|
||||
get: taskAPI.frames.get,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
logs: {
|
||||
value: Object.freeze({
|
||||
put: taskAPI.logs.put,
|
||||
save: taskAPI.logs.save,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
actions: {
|
||||
value: Object.freeze({
|
||||
undo: taskAPI.actions.undo,
|
||||
redo: taskAPI.actions.redo,
|
||||
clear: taskAPI.actions.clear,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
|
||||
events: {
|
||||
value: Object.freeze({
|
||||
subscribe: taskAPI.events.subscribe,
|
||||
unsubscribe: taskAPI.events.unsubscribe,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
}));
|
||||
|
||||
const implementAPI = require('./api-implementation');
|
||||
if (typeof (window) === 'undefined') {
|
||||
// Dummy browser environment
|
||||
require('browser-env')();
|
||||
}
|
||||
|
||||
window.cvat = Object.freeze(implementAPI(cvat));
|
||||
|
||||
const hidden = require('./hidden');
|
||||
hidden.location = cvat.config.backendAPI.slice(0, -7); // TODO: Use JS server instead
|
||||
})();
|
||||
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
(() => {
|
||||
/**
|
||||
* Enum for type of server files
|
||||
* @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',
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for a status of a task
|
||||
* @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',
|
||||
COMPLETED: 'completed',
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for a mode of a task
|
||||
* @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',
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for type of server files
|
||||
* @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',
|
||||
SELECT: 'select',
|
||||
NUMBER: 'number',
|
||||
TEXT: 'text',
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for type of an object
|
||||
* @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',
|
||||
TRACK: 'track',
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for type of server files
|
||||
* @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'
|
||||
* @readonly
|
||||
*/
|
||||
const ObjectShape = Object.freeze({
|
||||
RECTANGLE: 'rectangle',
|
||||
POLYGON: 'polygon',
|
||||
POLYLINE: 'polyline',
|
||||
POINTS: 'points',
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for type of server files
|
||||
* @enum {number}
|
||||
* @name LogType
|
||||
* @memberof module:API.cvat.enums
|
||||
* @property {number} pasteObject 0
|
||||
* @property {number} changeAttribute 1
|
||||
* @property {number} dragObject 2
|
||||
* @property {number} deleteObject 3
|
||||
* @property {number} pressShortcut 4
|
||||
* @property {number} resizeObject 5
|
||||
* @property {number} sendLogs 6
|
||||
* @property {number} saveJob 7
|
||||
* @property {number} jumpFrame 8
|
||||
* @property {number} drawObject 9
|
||||
* @property {number} changeLabel 10
|
||||
* @property {number} sendTaskInfo 11
|
||||
* @property {number} loadJob 12
|
||||
* @property {number} moveImage 13
|
||||
* @property {number} zoomImage 14
|
||||
* @property {number} lockObject 15
|
||||
* @property {number} mergeObjects 16
|
||||
* @property {number} copyObject 17
|
||||
* @property {number} propagateObject 18
|
||||
* @property {number} undoAction 19
|
||||
* @property {number} redoAction 20
|
||||
* @property {number} sendUserActivity 21
|
||||
* @property {number} sendException 22
|
||||
* @property {number} changeFrame 23
|
||||
* @property {number} debugInfo 24
|
||||
* @property {number} fitImage 25
|
||||
* @property {number} rotateImage 26
|
||||
* @readonly
|
||||
*/
|
||||
const LogType = {
|
||||
pasteObject: 0,
|
||||
changeAttribute: 1,
|
||||
dragObject: 2,
|
||||
deleteObject: 3,
|
||||
pressShortcut: 4,
|
||||
resizeObject: 5,
|
||||
sendLogs: 6,
|
||||
saveJob: 7,
|
||||
jumpFrame: 8,
|
||||
drawObject: 9,
|
||||
changeLabel: 10,
|
||||
sendTaskInfo: 11,
|
||||
loadJob: 12,
|
||||
moveImage: 13,
|
||||
zoomImage: 14,
|
||||
lockObject: 15,
|
||||
mergeObjects: 16,
|
||||
copyObject: 17,
|
||||
propagateObject: 18,
|
||||
undoAction: 19,
|
||||
redoAction: 20,
|
||||
sendUserActivity: 21,
|
||||
sendException: 22,
|
||||
changeFrame: 23,
|
||||
debugInfo: 24,
|
||||
fitImage: 25,
|
||||
rotateImage: 26,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum for type of server files
|
||||
* @enum {number}
|
||||
* @name EventType
|
||||
* @memberof module:API.cvat.enums
|
||||
* @property {number} frameDownloaded 0
|
||||
* @readonly
|
||||
*/
|
||||
const EventType = Object.freeze({
|
||||
frameDownloaded: 0,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
ShareFileType,
|
||||
TaskStatus,
|
||||
TaskMode,
|
||||
AttributeType,
|
||||
ObjectType,
|
||||
ObjectShape,
|
||||
LogType,
|
||||
EventType,
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const Platform = require('platform');
|
||||
const ErrorStackParser = require('error-stack-parser');
|
||||
|
||||
const hidden = require('./hidden');
|
||||
|
||||
/**
|
||||
* Base exception class
|
||||
* @memberof module:API.cvat.exceptions
|
||||
* @extends Error
|
||||
* @ignore
|
||||
*/
|
||||
class Exception extends Error {
|
||||
/**
|
||||
* @param {string} message - Exception message
|
||||
*/
|
||||
constructor(message) {
|
||||
super(message);
|
||||
|
||||
const time = new Date().toISOString();
|
||||
const system = Platform.os.toString();
|
||||
const client = `${Platform.name} ${Platform.version}`;
|
||||
const info = ErrorStackParser.parse(this)[0];
|
||||
const filename = `${hidden.location}${info.fileName}`;
|
||||
const line = info.lineNumber;
|
||||
const column = info.columnNumber;
|
||||
const {
|
||||
jobID,
|
||||
taskID,
|
||||
clientID,
|
||||
} = hidden;
|
||||
|
||||
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,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an exception on a server
|
||||
* @name save
|
||||
* @method
|
||||
* @memberof Exception
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
async save() {
|
||||
const exceptionObject = {
|
||||
system: this.system,
|
||||
client: this.client,
|
||||
time: this.time,
|
||||
job_id: this.jobID,
|
||||
task_id: this.taskID,
|
||||
proj_id: this.projID,
|
||||
client_id: this.clientID,
|
||||
message: this.message,
|
||||
filename: this.filename,
|
||||
line: this.line,
|
||||
column: this.column,
|
||||
stack: this.stack,
|
||||
};
|
||||
|
||||
try {
|
||||
const serverProxy = require('./server-proxy');
|
||||
await serverProxy.server.exception(exceptionObject);
|
||||
} catch (exception) {
|
||||
// add event
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unexpected situations in code
|
||||
* @memberof module:API.cvat.exceptions
|
||||
* @extends module:API.cvat.exceptions.Exception
|
||||
*/
|
||||
class ScriptingError extends Exception {
|
||||
/**
|
||||
* @param {string} message - Exception message
|
||||
*/
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin-referred exceptions
|
||||
* @memberof module:API.cvat.exceptions
|
||||
* @extends module:API.cvat.exceptions.Exception
|
||||
*/
|
||||
class PluginError extends Exception {
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
class ServerError extends Exception {
|
||||
/**
|
||||
* @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,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Exception,
|
||||
ArgumentError,
|
||||
ScriptingError,
|
||||
PluginError,
|
||||
ServerError,
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const PluginRegistry = require('./plugins');
|
||||
|
||||
/**
|
||||
* Class provides meta information about specific frame and frame itself
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class FrameData {
|
||||
constructor(width, height, tid, number) {
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
/**
|
||||
* @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: tid,
|
||||
writable: false,
|
||||
},
|
||||
number: {
|
||||
value: number,
|
||||
writable: false,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns URL encoded image which can be placed in the img tag
|
||||
* @method image
|
||||
* @returns {string}
|
||||
* @memberof module:API.cvat.classes.FrameData
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exception.ServerError}
|
||||
* @throws {module:API.cvat.exception.PluginError}
|
||||
*/
|
||||
async image() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, FrameData.prototype.image);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FrameData;
|
||||
})();
|
||||
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* Some shared cvat.js data which aren't intended for a user */
|
||||
(() => {
|
||||
const hidden = {
|
||||
clientID: +Date.now().toString().substr(-6),
|
||||
projID: undefined,
|
||||
taskID: undefined,
|
||||
jobID: undefined,
|
||||
location: undefined,
|
||||
};
|
||||
|
||||
module.exports = hidden;
|
||||
})();
|
||||
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
(() => {
|
||||
/**
|
||||
* Class representing an attribute
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class Attribute {
|
||||
constructor(initialData) {
|
||||
const data = {
|
||||
id: undefined,
|
||||
default_value: undefined,
|
||||
input_type: undefined,
|
||||
mutable: undefined,
|
||||
name: undefined,
|
||||
values: undefined,
|
||||
};
|
||||
|
||||
for (const key in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(initialData, key)) {
|
||||
if (Array.isArray(initialData[key])) {
|
||||
data[key] = [...initialData[key]];
|
||||
} else {
|
||||
data[key] = initialData[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.values(window.cvat.enums.AttributeType).includes(data.input_type)) {
|
||||
throw new window.cvat.exceptions.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],
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const object = {
|
||||
name: this.name,
|
||||
mutable: this.mutable,
|
||||
input_type: this.inputType,
|
||||
default_value: this.defaultValue,
|
||||
values: this.values,
|
||||
};
|
||||
|
||||
if (typeof (this.id) !== 'undefined') {
|
||||
object.id = this.id;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a label
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class Label {
|
||||
constructor(initialData) {
|
||||
const data = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
};
|
||||
|
||||
for (const key in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(initialData, key)) {
|
||||
data[key] = initialData[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.attributes = [];
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(initialData, 'attributes')
|
||||
&& Array.isArray(initialData.attributes)) {
|
||||
for (const attrData of initialData.attributes) {
|
||||
data.attributes.push(new window.cvat.classes.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 attributes
|
||||
* @type {module:API.cvat.classes.Attribute[]}
|
||||
* @memberof module:API.cvat.classes.Label
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
attributes: {
|
||||
get: () => [...data.attributes],
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const object = {
|
||||
name: this.name,
|
||||
attributes: [...this.attributes.map(el => el.toJSON())],
|
||||
};
|
||||
|
||||
if (typeof (this.id) !== 'undefined') {
|
||||
object.id = this.id;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Attribute,
|
||||
Label,
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const PluginRegistry = require('./plugins');
|
||||
|
||||
/**
|
||||
* Class describe scheme of a log object
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class Log {
|
||||
constructor(logType, continuous, details) {
|
||||
this.type = logType;
|
||||
this.continuous = continuous;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method closes a continue log
|
||||
* @method close
|
||||
* @memberof module:API.cvat.classes.Log
|
||||
* @readonly
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async close() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, Log.prototype.close);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Log;
|
||||
})();
|
||||
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const PluginRegistry = require('./plugins');
|
||||
|
||||
/**
|
||||
* Class representing a state of an object on a specific frame
|
||||
* @memberof module:API.cvat.classes
|
||||
*/
|
||||
class ObjectState {
|
||||
/**
|
||||
* @param {module:API.cvat.enums.ObjectType} type - a type of an object
|
||||
* @param {module:API.cvat.enums.ObjectShape} shape -
|
||||
* a type of a shape if an object is a shape of a track
|
||||
* @param {module:API.cvat.classes.Label} label - a label of an object
|
||||
*/
|
||||
constructor(type, shape, label) {
|
||||
const data = {
|
||||
position: null,
|
||||
group: null,
|
||||
zOrder: null,
|
||||
outside: false,
|
||||
occluded: false,
|
||||
attributes: null,
|
||||
lock: false,
|
||||
type,
|
||||
shape,
|
||||
label,
|
||||
};
|
||||
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
type: {
|
||||
/**
|
||||
* @name type
|
||||
* @type {module:API.cvat.enums.ShapeType}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.type,
|
||||
},
|
||||
shape: {
|
||||
/**
|
||||
* @name shape
|
||||
* @type {module:API.cvat.enums.ShapeType}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.shape,
|
||||
},
|
||||
label: {
|
||||
/**
|
||||
* @name shape
|
||||
* @type {module:API.cvat.classes.Label}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.label,
|
||||
set: (labelInstance) => {
|
||||
if (!(labelInstance instanceof window.cvat.classes.Label)) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected Label instance, but got "${typeof (labelInstance.constructor.name)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
data.label = labelInstance;
|
||||
},
|
||||
},
|
||||
position: {
|
||||
/**
|
||||
* @typedef {Object} Point
|
||||
* @property {number} x
|
||||
* @property {number} y
|
||||
* @global
|
||||
*/
|
||||
/**
|
||||
* @name position
|
||||
* @type {Point[]}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.position,
|
||||
set: (position) => {
|
||||
if (Array.isArray(position)) {
|
||||
for (const point of position) {
|
||||
if (typeof (point) !== 'object'
|
||||
|| !('x' in point) || !('y' in point)) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Got invalid point ${point}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Got invalid type "${typeof (position.constructor.name)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
data.position = position;
|
||||
},
|
||||
},
|
||||
group: {
|
||||
/**
|
||||
* @name group
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.group,
|
||||
set: (groupID) => {
|
||||
if (!Number.isInteger(groupID)) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected integer, but got ${groupID.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
data.group = groupID;
|
||||
},
|
||||
},
|
||||
zOrder: {
|
||||
/**
|
||||
* @name zOrder
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.zOrder,
|
||||
set: (zOrder) => {
|
||||
if (!Number.isInteger(zOrder)) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected integer, but got ${zOrder.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
data.zOrder = zOrder;
|
||||
},
|
||||
},
|
||||
outside: {
|
||||
/**
|
||||
* @name outside
|
||||
* @type {boolean}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.outside,
|
||||
set: (outside) => {
|
||||
if (!(typeof (outside) !== 'boolean')) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected integer, but got ${outside.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
data.outside = outside;
|
||||
},
|
||||
},
|
||||
occluded: {
|
||||
/**
|
||||
* @name occluded
|
||||
* @type {boolean}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.occluded,
|
||||
set: (occluded) => {
|
||||
if (!(typeof (occluded) !== 'boolean')) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected integer, but got ${occluded.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
data.occluded = occluded;
|
||||
},
|
||||
},
|
||||
lock: {
|
||||
/**
|
||||
* @name lock
|
||||
* @type {boolean}
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.lock,
|
||||
set: (lock) => {
|
||||
if (!(typeof (lock) !== 'boolean')) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected integer, but got ${lock.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
data.lock = lock;
|
||||
},
|
||||
},
|
||||
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
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
get: () => data.attributes,
|
||||
set: (attributes) => {
|
||||
if (typeof (attributes) !== 'object') {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected object, but got ${attributes.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (let attrId in attributes) {
|
||||
if (Object.prototype.hasOwnProperty.call(attributes, attrId)) {
|
||||
attrId = +attrId;
|
||||
if (!Number.isInteger(attrId)) {
|
||||
throw new window.cvat.exceptions.ArgumentException(
|
||||
`Expected integer attribute id, but got ${attrId.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
data.attributes[attrId] = attributes[attrId];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method saves object state in a collection
|
||||
* @method save
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @readonly
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async save() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, ObjectState.prototype.save);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method deletes object from a collection
|
||||
* @method delete
|
||||
* @memberof module:API.cvat.classes.ObjectState
|
||||
* @readonly
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async delete() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, ObjectState.prototype.delete);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = ObjectState;
|
||||
})();
|
||||
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const { PluginError } = require('./exceptions');
|
||||
|
||||
const plugins = [];
|
||||
class PluginRegistry {
|
||||
static async apiWrapper(wrappedFunc, ...args) {
|
||||
// I have to optimize the wrapper
|
||||
const pluginList = await window.cvat.plugins.list.implementation();
|
||||
for (const plugin of pluginList) {
|
||||
const pluginDecorators = plugin.functions
|
||||
.filter(obj => obj.callback === wrappedFunc)[0];
|
||||
if (pluginDecorators && pluginDecorators.enter) {
|
||||
try {
|
||||
await pluginDecorators.enter.call(this, plugin, ...args);
|
||||
} catch (exception) {
|
||||
if (exception instanceof PluginError) {
|
||||
throw exception;
|
||||
} else {
|
||||
throw new PluginError(`Exception in plugin ${plugin.name}: ${exception.toString()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = await wrappedFunc.implementation.call(this, ...args);
|
||||
|
||||
for (const plugin of pluginList) {
|
||||
const pluginDecorators = plugin.functions
|
||||
.filter(obj => obj.callback === wrappedFunc)[0];
|
||||
if (pluginDecorators && pluginDecorators.leave) {
|
||||
try {
|
||||
result = await pluginDecorators.leave.call(this, plugin, result, ...args);
|
||||
} catch (exception) {
|
||||
if (exception instanceof PluginError) {
|
||||
throw exception;
|
||||
} else {
|
||||
throw new PluginError(`Exception in plugin ${plugin.name}: ${exception.toString()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static async register(plug) {
|
||||
const functions = [];
|
||||
|
||||
if (typeof (plug) !== 'object') {
|
||||
throw new PluginError(`Plugin should be an object, but got "${typeof (plug)}"`);
|
||||
}
|
||||
|
||||
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') {
|
||||
throw new PluginError('Plugin must contain a "description" field and it must be a string');
|
||||
}
|
||||
|
||||
if ('functions' in plug) {
|
||||
throw new PluginError('Plugin must not contain a "functions" field');
|
||||
}
|
||||
|
||||
(function traverse(plugin, api) {
|
||||
const decorator = {};
|
||||
for (const key in plugin) {
|
||||
if (Object.prototype.hasOwnProperty.call(plugin, key)) {
|
||||
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')) {
|
||||
decorator.callback = api;
|
||||
decorator[key] = plugin[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(decorator).length) {
|
||||
functions.push(decorator);
|
||||
}
|
||||
}(plug, {
|
||||
cvat: window.cvat,
|
||||
}));
|
||||
|
||||
Object.defineProperty(plug, 'functions', {
|
||||
value: functions,
|
||||
writable: false,
|
||||
});
|
||||
|
||||
plugins.push(plug);
|
||||
}
|
||||
|
||||
static async list() {
|
||||
return plugins;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PluginRegistry;
|
||||
})();
|
||||
@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
encodeURIComponent:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
class ServerProxy {
|
||||
constructor() {
|
||||
const Cookie = require('js-cookie');
|
||||
const Axios = require('axios');
|
||||
|
||||
function setCSRFHeader(header) {
|
||||
Axios.defaults.headers.delete['X-CSRFToken'] = header;
|
||||
Axios.defaults.headers.patch['X-CSRFToken'] = header;
|
||||
Axios.defaults.headers.post['X-CSRFToken'] = header;
|
||||
Axios.defaults.headers.put['X-CSRFToken'] = header;
|
||||
}
|
||||
|
||||
async function about() {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/server/about`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get "about" information from the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function share(directory) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/server/share?directory=${directory}`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get "share" information from the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function exception(exceptionObject) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
try {
|
||||
await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), {
|
||||
proxy: window.cvat.config.proxy,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not send an exception to the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function login(username, password) {
|
||||
function setCookie(response) {
|
||||
if (response.headers['set-cookie']) {
|
||||
// Browser itself setup cookie and header is none
|
||||
// In NodeJS we need do it manually
|
||||
let cookies = '';
|
||||
for (let cookie of response.headers['set-cookie']) {
|
||||
[cookie] = cookie.split(';'); // truncate extra information
|
||||
const name = cookie.split('=')[0];
|
||||
const value = cookie.split('=')[1];
|
||||
if (name === 'csrftoken') {
|
||||
setCSRFHeader(value);
|
||||
}
|
||||
Cookie.set(name, value);
|
||||
cookies += `${cookie};`;
|
||||
}
|
||||
|
||||
Axios.defaults.headers.common.Cookie = cookies;
|
||||
} else {
|
||||
// Browser code. We need set additinal header for authentification
|
||||
const csrftoken = Cookie.get('csrftoken');
|
||||
if (csrftoken) {
|
||||
setCSRFHeader(csrftoken);
|
||||
} else {
|
||||
throw new window.cvat.exceptions.ScriptingError(
|
||||
'An environment has been detected as a browser'
|
||||
+ ', but CSRF token has not been found in cookies',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const host = window.cvat.config.backendAPI.slice(0, -7);
|
||||
let csrf = null;
|
||||
try {
|
||||
csrf = await Axios.get(`${host}/auth/login`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get CSRF token from a server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
setCookie(csrf);
|
||||
|
||||
const authentificationData = ([
|
||||
`${encodeURIComponent('username')}=${encodeURIComponent(username)}`,
|
||||
`${encodeURIComponent('password')}=${encodeURIComponent(password)}`,
|
||||
]).join('&').replace(/%20/g, '+');
|
||||
|
||||
let authentificationResponse = null;
|
||||
try {
|
||||
authentificationResponse = await Axios.post(
|
||||
`${host}/auth/login`,
|
||||
authentificationData,
|
||||
{
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
proxy: window.cvat.config.proxy,
|
||||
// do not redirect to a dashboard,
|
||||
// otherwise we don't get a session id in a response
|
||||
maxRedirects: 0,
|
||||
},
|
||||
);
|
||||
} catch (errorData) {
|
||||
if (errorData.response.status === 302) {
|
||||
// Redirection code expected
|
||||
authentificationResponse = errorData.response;
|
||||
} else {
|
||||
const code = errorData.response
|
||||
? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not login on a server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setCookie(authentificationResponse);
|
||||
}
|
||||
|
||||
async function getTasks(filter = '') {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/tasks?${filter}`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get tasks from a server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
response.data.results.count = response.data.count;
|
||||
return response.data.results;
|
||||
}
|
||||
|
||||
async function saveTask(id, taskData) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
try {
|
||||
await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), {
|
||||
proxy: window.cvat.config.proxy,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not save the task on the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteTask(id) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
try {
|
||||
await Axios.delete(`${backendAPI}/tasks/${id}`);
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not delete the task from the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function createTask(taskData, files, onUpdate) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
async function wait(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
async function checkStatus() {
|
||||
try {
|
||||
const response = await Axios.get(`${backendAPI}/tasks/${id}/status`);
|
||||
if (['Queued', 'Started'].includes(response.data.state)) {
|
||||
if (response.data.message !== '') {
|
||||
onUpdate(response.data.message);
|
||||
}
|
||||
setTimeout(checkStatus, 1000);
|
||||
} else if (response.data.state === 'Finished') {
|
||||
resolve();
|
||||
} 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
|
||||
reject(new window.cvat.exceptions.ServerError(
|
||||
'Could not create the task on the server',
|
||||
400,
|
||||
));
|
||||
} else {
|
||||
// If server has another status, it is unexpected
|
||||
// Therefore it is server error and we can pass code 500
|
||||
reject(new window.cvat.exceptions.ServerError(
|
||||
`Unknown task state has been recieved: ${response.data.state}`,
|
||||
500,
|
||||
));
|
||||
}
|
||||
} catch (errorData) {
|
||||
const code = errorData.response
|
||||
? errorData.response.status : errorData.code;
|
||||
|
||||
reject(new window.cvat.exceptions.ServerError(
|
||||
'Data uploading error occured',
|
||||
code,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(checkStatus, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
const batchOfFiles = new window.FormData();
|
||||
for (const key in files) {
|
||||
if (Object.prototype.hasOwnProperty.call(files, key)) {
|
||||
for (let i = 0; i < files[key].length; i++) {
|
||||
batchOfFiles.append(`${key}[${i}]`, files[key][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let response = null;
|
||||
|
||||
onUpdate('The task is being created on the server..');
|
||||
try {
|
||||
response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskData), {
|
||||
proxy: window.cvat.config.proxy,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not put task to the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
onUpdate('The data is being uploaded to the server..');
|
||||
try {
|
||||
await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, batchOfFiles, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
deleteTask(response.data.id);
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not put data to the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await wait(response.data.id);
|
||||
} catch (createException) {
|
||||
deleteTask(response.data.id);
|
||||
throw createException;
|
||||
}
|
||||
|
||||
|
||||
const createdTask = await getTasks(`?id=${response.id}`);
|
||||
return createdTask[0];
|
||||
}
|
||||
|
||||
async function getJob(jobID) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/jobs/${jobID}`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get jobs from a server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function saveJob(id, jobData) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
try {
|
||||
await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), {
|
||||
proxy: window.cvat.config.proxy,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not save the job on the server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function getUsers() {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/users`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get users from a server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data.results;
|
||||
}
|
||||
|
||||
async function getSelf() {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/users/self`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
'Could not get users from a server',
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getFrame(tid, frame) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/${frame}`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
`Could not get frame ${frame} for a task ${tid} from a server`,
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getMeta(tid) {
|
||||
const { backendAPI } = window.cvat.config;
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/meta`, {
|
||||
proxy: window.cvat.config.proxy,
|
||||
});
|
||||
} catch (errorData) {
|
||||
const code = errorData.response ? errorData.response.status : errorData.code;
|
||||
throw new window.cvat.exceptions.ServerError(
|
||||
`Could not get frame meta info for a task ${tid} from a server`,
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Set csrftoken header from browser cookies if it exists
|
||||
// NodeJS env returns 'undefined'
|
||||
// So in NodeJS we need login after each run
|
||||
const csrftoken = Cookie.get('csrftoken');
|
||||
if (csrftoken) {
|
||||
setCSRFHeader(csrftoken);
|
||||
}
|
||||
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
server: {
|
||||
value: Object.freeze({
|
||||
about,
|
||||
share,
|
||||
exception,
|
||||
login,
|
||||
}),
|
||||
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({
|
||||
getFrame,
|
||||
getMeta,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const serverProxy = new ServerProxy();
|
||||
module.exports = serverProxy;
|
||||
})();
|
||||
@ -0,0 +1,808 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const PluginRegistry = require('./plugins');
|
||||
|
||||
/**
|
||||
* Base abstract class for Task and Job. It contains common members.
|
||||
* @hideconstructor
|
||||
* @virtual
|
||||
*/
|
||||
class Session {
|
||||
constructor() {
|
||||
/**
|
||||
* An interaction with annotations
|
||||
* @namespace annotations
|
||||
* @memberof Session
|
||||
*/
|
||||
/**
|
||||
* Upload annotations from a dump file
|
||||
* @method upload
|
||||
* @memberof Session.annotations
|
||||
* @param {File} [annotations] - text file with annotations
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
/**
|
||||
* Save annotation changes on a server
|
||||
* @method save
|
||||
* @memberof Session.annotations
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Remove all annotations from a session
|
||||
* @method clear
|
||||
* @memberof Session.annotations
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Dump of annotations to a file.
|
||||
* Method always dumps annotations for a whole task.
|
||||
* @method dump
|
||||
* @memberof Session.annotations
|
||||
* @returns {string} URL which can be used in order to get a dump file
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Collect some statistics from a session.
|
||||
* For example number of shapes, tracks, polygons etc
|
||||
* @method statistics
|
||||
* @memberof Session.annotations
|
||||
* @returns {module:API.cvat.classes.Statistics} statistics object
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Add some annotations to a session
|
||||
* @method put
|
||||
* @memberof Session.annotations
|
||||
* @param {module:API.cvat.classes.ObjectState[]} data
|
||||
* array of objects on the specific frame
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* @typedef {Object} ObjectFilter
|
||||
* @property {string} [label] a name of a label
|
||||
* @property {module:API.cvat.enums.ObjectType} [type]
|
||||
* @property {module:API.cvat.enums.ObjectShape} [shape]
|
||||
* @property {boolean} [occluded] a value of occluded property
|
||||
* @property {boolean} [lock] a value of lock property
|
||||
* @property {number} [width] a width of a shape
|
||||
* @property {number} [height] a height of a shape
|
||||
* @property {Object[]} [attributes] dictionary with "name: value" pairs
|
||||
* @global
|
||||
*/
|
||||
/**
|
||||
* Get annotations for a specific frame
|
||||
* @method get
|
||||
* @param {integer} frame get objects from the frame
|
||||
* @param {ObjectFilter[]} [filter = []]
|
||||
* get only objects are satisfied to specific filter
|
||||
* @returns {module:API.cvat.classes.ObjectState[]}
|
||||
* @memberof Session.annotations
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Find frame which contains at least one object satisfied to a filter
|
||||
* @method search
|
||||
* @memberof Session.annotations
|
||||
* @param {ObjectFilter} [filter = []] filter
|
||||
* @param {integer} from lower bound of a search
|
||||
* @param {integer} to upper bound of a search
|
||||
* @returns {integer} the nearest frame which contains filtered objects
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Select shape under a cursor using smart alghorithms
|
||||
* @method select
|
||||
* @memberof Session.annotations
|
||||
* @param {integer} frame frame for selecting
|
||||
* @param {float} x horizontal coordinate
|
||||
* @param {float} y vertical coordinate
|
||||
* @returns {(integer|null)}
|
||||
* identifier of a selected object or null if no one of objects is on position
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Namespace is used for an interaction with frames
|
||||
* @namespace frames
|
||||
* @memberof Session
|
||||
*/
|
||||
/**
|
||||
* Get frame by its number
|
||||
* @method get
|
||||
* @memberof Session.frames
|
||||
* @param {integer} frame number of frame which you want to get
|
||||
* @returns {module:API.cvat.classes.FrameData}
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Namespace is used for an interaction with logs
|
||||
* @namespace logs
|
||||
* @memberof Session
|
||||
*/
|
||||
|
||||
/**
|
||||
* Append log to a log collection.
|
||||
* Continue logs will have been added after "close" method is called
|
||||
* @method put
|
||||
* @memberof Session.logs
|
||||
* @param {module:API.cvat.enums.LogType} type a type of a log
|
||||
* @param {boolean} continuous log is a continuous log
|
||||
* @param {Object} details any others data which will be append to log data
|
||||
* @returns {module:API.cvat.classes.Log}
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
/**
|
||||
* Save accumulated logs on a server
|
||||
* @method save
|
||||
* @memberof Session.logs
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
|
||||
/**
|
||||
* Namespace is used for an interaction with actions
|
||||
* @namespace actions
|
||||
* @memberof Session
|
||||
*/
|
||||
|
||||
/**
|
||||
* Is a dictionary of pairs "id:action" where "id" is an identifier of an object
|
||||
* which has been affected by undo/redo and "action" is what exactly has been
|
||||
* done with the object. Action can be: "created", "deleted", "updated".
|
||||
* Size of an output array equal the param "count".
|
||||
* @typedef {Object} HistoryAction
|
||||
* @global
|
||||
*/
|
||||
/**
|
||||
* Undo actions
|
||||
* @method undo
|
||||
* @memberof Session.actions
|
||||
* @returns {HistoryAction}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Redo actions
|
||||
* @method redo
|
||||
* @memberof Session.actions
|
||||
* @returns {HistoryAction}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Namespace is used for an interaction with events
|
||||
* @namespace events
|
||||
* @memberof Session
|
||||
*/
|
||||
/**
|
||||
* Subscribe on an event
|
||||
* @method subscribe
|
||||
* @memberof Session.events
|
||||
* @param {module:API.cvat.enums.EventType} type - event type
|
||||
* @param {functions} callback - function which will be called on event
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
/**
|
||||
* Unsubscribe from an event. If callback is not provided,
|
||||
* all callbacks will be removed from subscribers for the event
|
||||
* @method unsubscribe
|
||||
* @memberof Session.events
|
||||
* @param {module:API.cvat.enums.EventType} type - event type
|
||||
* @param {functions} [callback = null] - function which is called on event
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
* @instance
|
||||
* @async
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a job.
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
* @extends Session
|
||||
*/
|
||||
class Job extends Session {
|
||||
constructor(initialData) {
|
||||
super();
|
||||
const data = {
|
||||
id: undefined,
|
||||
assignee: undefined,
|
||||
status: undefined,
|
||||
start_frame: undefined,
|
||||
stop_frame: undefined,
|
||||
task: undefined,
|
||||
};
|
||||
|
||||
for (const property in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, property)) {
|
||||
if (property in initialData) {
|
||||
data[property] = initialData[property];
|
||||
}
|
||||
|
||||
if (data[property] === undefined) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
`Job field "${property}" was not initialized`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
/**
|
||||
* @name id
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
id: {
|
||||
get: () => data.id,
|
||||
},
|
||||
/**
|
||||
* Identifier of a user who is responsible for the job
|
||||
* @name assignee
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
assignee: {
|
||||
get: () => data.assignee,
|
||||
set: () => (assignee) => {
|
||||
if (!Number.isInteger(assignee) || assignee < 0) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a non negative integer',
|
||||
);
|
||||
}
|
||||
data.assignee = assignee;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name status
|
||||
* @type {module:API.cvat.enums.TaskStatus}
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
status: {
|
||||
get: () => data.status,
|
||||
set: (status) => {
|
||||
const type = window.cvat.enums.TaskStatus;
|
||||
let valueInEnum = false;
|
||||
for (const value in type) {
|
||||
if (type[value] === status) {
|
||||
valueInEnum = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valueInEnum) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a value from the enumeration cvat.enums.TaskStatus',
|
||||
);
|
||||
}
|
||||
|
||||
data.status = status;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name startFrame
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
startFrame: {
|
||||
get: () => data.start_frame,
|
||||
},
|
||||
/**
|
||||
* @name stopFrame
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
stopFrame: {
|
||||
get: () => data.stop_frame,
|
||||
},
|
||||
/**
|
||||
* @name task
|
||||
* @type {module:API.cvat.classes.Task}
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
task: {
|
||||
get: () => data.task,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method updates job data like status or assignee
|
||||
* @method save
|
||||
* @memberof module:API.cvat.classes.Job
|
||||
* @readonly
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async save() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, Job.prototype.save);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a task
|
||||
* @memberof module:API.cvat.classes
|
||||
* @extends Session
|
||||
*/
|
||||
class Task extends Session {
|
||||
/**
|
||||
* In a fact you need use the constructor only if you want to create a task
|
||||
* @param {object} initialData - Object which is used for initalization
|
||||
* <br> It can contain keys:
|
||||
* <br> <li style="margin-left: 10px;"> name
|
||||
* <br> <li style="margin-left: 10px;"> assignee
|
||||
* <br> <li style="margin-left: 10px;"> bug_tracker
|
||||
* <br> <li style="margin-left: 10px;"> z_order
|
||||
* <br> <li style="margin-left: 10px;"> labels
|
||||
* <br> <li style="margin-left: 10px;"> segment_size
|
||||
* <br> <li style="margin-left: 10px;"> overlap
|
||||
*/
|
||||
constructor(initialData) {
|
||||
super();
|
||||
const data = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
size: undefined,
|
||||
mode: undefined,
|
||||
owner: undefined,
|
||||
assignee: undefined,
|
||||
created_date: undefined,
|
||||
updated_date: undefined,
|
||||
bug_tracker: undefined,
|
||||
overlap: undefined,
|
||||
segment_size: undefined,
|
||||
z_order: undefined,
|
||||
image_quality: undefined,
|
||||
};
|
||||
|
||||
for (const property in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, property)
|
||||
&& property in initialData) {
|
||||
data[property] = initialData[property];
|
||||
}
|
||||
}
|
||||
|
||||
data.labels = [];
|
||||
data.jobs = [];
|
||||
data.files = Object.freeze({
|
||||
server_files: [],
|
||||
client_files: [],
|
||||
remote_files: [],
|
||||
});
|
||||
|
||||
if (Array.isArray(initialData.segments)) {
|
||||
for (const segment of initialData.segments) {
|
||||
if (Array.isArray(segment.jobs)) {
|
||||
for (const job of segment.jobs) {
|
||||
const jobInstance = new window.cvat.classes.Job({
|
||||
url: job.url,
|
||||
id: job.id,
|
||||
assignee: job.assignee,
|
||||
status: job.status,
|
||||
start_frame: segment.start_frame,
|
||||
stop_frame: segment.stop_frame,
|
||||
task: this,
|
||||
});
|
||||
data.jobs.push(jobInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(initialData.labels)) {
|
||||
for (const label of initialData.labels) {
|
||||
const classInstance = new window.cvat.classes.Label(label);
|
||||
data.labels.push(classInstance);
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
/**
|
||||
* @name id
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
id: {
|
||||
get: () => data.id,
|
||||
},
|
||||
/**
|
||||
* @name name
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
name: {
|
||||
get: () => data.name,
|
||||
set: (value) => {
|
||||
if (!value.trim().length) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must not be empty',
|
||||
);
|
||||
}
|
||||
data.name = value;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name status
|
||||
* @type {module:API.cvat.enums.TaskStatus}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
status: {
|
||||
get: () => data.status,
|
||||
},
|
||||
/**
|
||||
* @name size
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
size: {
|
||||
get: () => data.size,
|
||||
},
|
||||
/**
|
||||
* @name mode
|
||||
* @type {TaskMode}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
mode: {
|
||||
get: () => data.mode,
|
||||
},
|
||||
/**
|
||||
* Identificator of a user who has created the task
|
||||
* @name owner
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
owner: {
|
||||
get: () => data.owner,
|
||||
},
|
||||
/**
|
||||
* Identificator of a user who is responsible for the task
|
||||
* @name assignee
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
assignee: {
|
||||
get: () => data.assignee,
|
||||
set: () => (assignee) => {
|
||||
if (!Number.isInteger(assignee) || assignee < 0) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a non negative integer',
|
||||
);
|
||||
}
|
||||
data.assignee = assignee;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name createdDate
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
createdDate: {
|
||||
get: () => data.created_date,
|
||||
},
|
||||
/**
|
||||
* @name updatedDate
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
updatedDate: {
|
||||
get: () => data.updated_date,
|
||||
},
|
||||
/**
|
||||
* @name bugTracker
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
bugTracker: {
|
||||
get: () => data.bug_tracker,
|
||||
set: (tracker) => {
|
||||
data.bug_tracker = tracker;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name overlap
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
overlap: {
|
||||
get: () => data.overlap,
|
||||
set: (overlap) => {
|
||||
if (!Number.isInteger(overlap) || overlap < 0) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a non negative integer',
|
||||
);
|
||||
}
|
||||
data.overlap = overlap;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name segmentSize
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
segmentSize: {
|
||||
get: () => data.segment_size,
|
||||
set: (segment) => {
|
||||
if (!Number.isInteger(segment) || segment < 0) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a positive integer',
|
||||
);
|
||||
}
|
||||
data.segment_size = segment;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name zOrder
|
||||
* @type {boolean}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
zOrder: {
|
||||
get: () => data.z_order,
|
||||
set: (zOrder) => {
|
||||
if (typeof (zOrder) !== 'boolean') {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a boolean',
|
||||
);
|
||||
}
|
||||
data.z_order = zOrder;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name imageQuality
|
||||
* @type {integer}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
imageQuality: {
|
||||
get: () => data.image_quality,
|
||||
set: (quality) => {
|
||||
if (!Number.isInteger(quality) || quality < 0) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be a positive integer',
|
||||
);
|
||||
}
|
||||
data.image_quality = quality;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* After task has been created value can be appended only.
|
||||
* @name labels
|
||||
* @type {module:API.cvat.classes.Label[]}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
labels: {
|
||||
get: () => [...data.labels],
|
||||
set: (labels) => {
|
||||
if (!Array.isArray(labels)) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Value must be an array of Labels',
|
||||
);
|
||||
}
|
||||
|
||||
for (const label of labels) {
|
||||
if (!(label instanceof window.cvat.classes.Label)) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
'Each array value must be an instance of Label. '
|
||||
+ `${typeof (label)} was found`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof (data.id) === 'undefined') {
|
||||
data.labels = [...labels];
|
||||
} else {
|
||||
data.labels = data.labels.concat([...labels]);
|
||||
}
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name jobs
|
||||
* @type {module:API.cvat.classes.Job[]}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
jobs: {
|
||||
get: () => [...data.jobs],
|
||||
},
|
||||
/**
|
||||
* List of files from shared resource
|
||||
* @name serverFiles
|
||||
* @type {string[]}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
serverFiles: {
|
||||
get: () => [...data.files.server_files],
|
||||
set: (serverFiles) => {
|
||||
if (!Array.isArray(serverFiles)) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
`Value must be an array. But ${typeof (serverFiles)} has been got.`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const value of serverFiles) {
|
||||
if (typeof (value) !== 'string') {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
`Array values must be a string. But ${typeof (value)} has been got.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Array.prototype.push.apply(data.files.server_files, serverFiles);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* List of files from client host
|
||||
* @name clientFiles
|
||||
* @type {File[]}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @instance
|
||||
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||
*/
|
||||
clientFiles: {
|
||||
get: () => [...data.files.client_files],
|
||||
set: (clientFiles) => {
|
||||
if (!Array.isArray(clientFiles)) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
`Value must be an array. But ${typeof (clientFiles)} has been got.`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const value of clientFiles) {
|
||||
if (!(value instanceof window.File)) {
|
||||
throw new window.cvat.exceptions.ArgumentError(
|
||||
`Array values must be a File. But ${value.constructor.name} has been got.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Array.prototype.push.apply(data.files.client_files, clientFiles);
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method updates data of a created task or creates new task from scratch
|
||||
* @method save
|
||||
* @returns {module:API.cvat.classes.Task}
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @param {function} [onUpdate] - the function which is used only if task hasn't
|
||||
* been created yet. It called in order to notify about creation status.
|
||||
* It receives the string parameter which is a status message
|
||||
* @readonly
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async save(onUpdate = () => {}) {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, Task.prototype.save, onUpdate);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method deletes a task from a server
|
||||
* @method delete
|
||||
* @memberof module:API.cvat.classes.Task
|
||||
* @readonly
|
||||
* @instance
|
||||
* @async
|
||||
* @throws {module:API.cvat.exceptions.ServerError}
|
||||
* @throws {module:API.cvat.exceptions.PluginError}
|
||||
*/
|
||||
async delete() {
|
||||
const result = await PluginRegistry
|
||||
.apiWrapper.call(this, Task.prototype.delete);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Job,
|
||||
Task,
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
(() => {
|
||||
/**
|
||||
* Class representing statistics inside a session
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class Statistics {
|
||||
constructor(label, total) {
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
/**
|
||||
* Statistics by labels which have a structure:
|
||||
* @example
|
||||
* {
|
||||
* label1: {
|
||||
* boxes: {
|
||||
* tracks: 10,
|
||||
* shapes: 11,
|
||||
* },
|
||||
* polygons: {
|
||||
* tracks: 12,
|
||||
* shapes: 13,
|
||||
* },
|
||||
* polylines: {
|
||||
* tracks: 14,
|
||||
* shapes: 15,
|
||||
* },
|
||||
* points: {
|
||||
* tracks: 16,
|
||||
* shapes: 17,
|
||||
* },
|
||||
* manually: 108,
|
||||
* 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 (summed by all labels) which have a structure:
|
||||
* @example
|
||||
* {
|
||||
* total: {
|
||||
* boxes: {
|
||||
* tracks: 10,
|
||||
* shapes: 11,
|
||||
* },
|
||||
* polygons: {
|
||||
* tracks: 12,
|
||||
* shapes: 13,
|
||||
* },
|
||||
* polylines: {
|
||||
* tracks: 14,
|
||||
* shapes: 15,
|
||||
* },
|
||||
* points: {
|
||||
* tracks: 16,
|
||||
* shapes: 17,
|
||||
* },
|
||||
* manually: 108,
|
||||
* interpolated: 500,
|
||||
* total: 608,
|
||||
* }
|
||||
* }
|
||||
* @name total
|
||||
* @type {Object}
|
||||
* @memberof module:API.cvat.classes.Statistics
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
total: {
|
||||
get: () => JSON.parse(JSON.stringify(total)),
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Statistics;
|
||||
})();
|
||||
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
(() => {
|
||||
/**
|
||||
* Class representing a user
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class User {
|
||||
constructor(initialData) {
|
||||
const data = {
|
||||
id: null,
|
||||
username: null,
|
||||
email: null,
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
groups: null,
|
||||
last_login: null,
|
||||
date_joined: null,
|
||||
is_staff: null,
|
||||
is_superuser: null,
|
||||
is_active: null,
|
||||
};
|
||||
|
||||
for (const property in data) {
|
||||
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,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
})();
|
||||
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
jest:false
|
||||
describe:false
|
||||
*/
|
||||
|
||||
// Setup mock for a server
|
||||
jest.mock('../../src/server-proxy', () => {
|
||||
const mock = require('../mocks/server-proxy.mock');
|
||||
return mock;
|
||||
});
|
||||
|
||||
// Initialize api
|
||||
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 () => {
|
||||
const result = await window.cvat.jobs.get({
|
||||
taskID: 3,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(2);
|
||||
for (const el of result) {
|
||||
expect(el).toBeInstanceOf(Job);
|
||||
}
|
||||
|
||||
expect(result[0].task.id).toBe(3);
|
||||
expect(result[0].task).toBe(result[1].task);
|
||||
});
|
||||
|
||||
test('get jobs by an unknown task id', async () => {
|
||||
const result = await window.cvat.jobs.get({
|
||||
taskID: 50,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
||||
test('get jobs by a job id', async () => {
|
||||
const result = await window.cvat.jobs.get({
|
||||
jobID: 1,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe(1);
|
||||
});
|
||||
|
||||
test('get jobs by an unknown job id', async () => {
|
||||
const result = await window.cvat.jobs.get({
|
||||
taskID: 50,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('get jobs by invalid filter with both taskID and jobID', async () => {
|
||||
await expect(window.cvat.jobs.get({
|
||||
taskID: 1,
|
||||
jobID: 1,
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
|
||||
test('get jobs by invalid job id', async () => {
|
||||
await expect(window.cvat.jobs.get({
|
||||
jobID: '1',
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
|
||||
test('get jobs by invalid task id', async () => {
|
||||
await expect(window.cvat.jobs.get({
|
||||
taskID: '1',
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
|
||||
test('get jobs by unknown filter', async () => {
|
||||
await 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({
|
||||
jobID: 1,
|
||||
});
|
||||
|
||||
result[0].status = 'validation';
|
||||
await result[0].save();
|
||||
|
||||
result = await window.cvat.jobs.get({
|
||||
jobID: 1,
|
||||
});
|
||||
expect(result[0].status).toBe('validation');
|
||||
});
|
||||
|
||||
test('save invalid status of a job', async () => {
|
||||
const result = await window.cvat.jobs.get({
|
||||
jobID: 1,
|
||||
});
|
||||
|
||||
await result[0].save();
|
||||
expect(() => {
|
||||
result[0].status = 'invalid';
|
||||
}).toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
jest:false
|
||||
describe:false
|
||||
*/
|
||||
|
||||
// Setup mock for a server
|
||||
jest.mock('../../src/server-proxy', () => {
|
||||
const mock = require('../mocks/server-proxy.mock');
|
||||
return mock;
|
||||
});
|
||||
|
||||
// Initialize api
|
||||
require('../../src/api');
|
||||
|
||||
// Test cases
|
||||
describe('Feature: get info about cvat', () => {
|
||||
test('get info about server', async () => {
|
||||
const result = await window.cvat.server.about();
|
||||
expect(result).toBeInstanceOf(Object);
|
||||
expect('name' in result).toBeTruthy();
|
||||
expect('description' in result).toBeTruthy();
|
||||
expect('version' in result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Feature: get share storage info', () => {
|
||||
test('get files in a root of a share storage', async () => {
|
||||
const result = await window.cvat.server.share();
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(5);
|
||||
});
|
||||
|
||||
test('get files in a some dir of a share storage', async () => {
|
||||
const result = await window.cvat.server.share('images');
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(8);
|
||||
});
|
||||
|
||||
test('get files in a some unknown dir of a share storage', async () => {
|
||||
await expect(window.cvat.server.share(
|
||||
'Unknown Directory',
|
||||
)).rejects.toThrow(window.cvat.exceptions.ServerError);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
jest:false
|
||||
describe:false
|
||||
*/
|
||||
|
||||
// Setup mock for a server
|
||||
jest.mock('../../src/server-proxy', () => {
|
||||
const mock = require('../mocks/server-proxy.mock');
|
||||
return mock;
|
||||
});
|
||||
|
||||
// Initialize api
|
||||
require('../../src/api');
|
||||
|
||||
const { Task } = require('../../src/session');
|
||||
|
||||
|
||||
// Test cases
|
||||
describe('Feature: get a list of tasks', () => {
|
||||
test('get all tasks', async () => {
|
||||
const result = await window.cvat.tasks.get();
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(3);
|
||||
for (const el of result) {
|
||||
expect(el).toBeInstanceOf(Task);
|
||||
}
|
||||
});
|
||||
|
||||
test('get a task by an id', async () => {
|
||||
const result = await window.cvat.tasks.get({
|
||||
id: 3,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(Task);
|
||||
expect(result[0].id).toBe(3);
|
||||
});
|
||||
|
||||
test('get a task by an unknown id', async () => {
|
||||
const result = await window.cvat.tasks.get({
|
||||
id: 50,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('get a task by an invalid id', async () => {
|
||||
await expect(window.cvat.tasks.get({
|
||||
id: '50',
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
|
||||
test('get tasks by filters', async () => {
|
||||
const result = await window.cvat.tasks.get({
|
||||
mode: 'interpolation',
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(2);
|
||||
for (const el of result) {
|
||||
expect(el).toBeInstanceOf(Task);
|
||||
expect(el.mode).toBe('interpolation');
|
||||
}
|
||||
});
|
||||
|
||||
test('get tasks by invalid filters', async () => {
|
||||
await expect(window.cvat.tasks.get({
|
||||
unknown: '5',
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
|
||||
test('get task by name, status and mode', async () => {
|
||||
const result = await window.cvat.tasks.get({
|
||||
mode: 'interpolation',
|
||||
status: 'annotation',
|
||||
name: 'Test Task',
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(1);
|
||||
for (const el of result) {
|
||||
expect(el).toBeInstanceOf(Task);
|
||||
expect(el.mode).toBe('interpolation');
|
||||
expect(el.status).toBe('annotation');
|
||||
expect(el.name).toBe('Test Task');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Feature: save a task', () => {
|
||||
test('save some changed fields in a task', async () => {
|
||||
let result = await window.cvat.tasks.get({
|
||||
id: 2,
|
||||
});
|
||||
|
||||
result[0].bugTracker = 'newBugTracker';
|
||||
result[0].zOrder = true;
|
||||
result[0].name = 'New Task Name';
|
||||
|
||||
result[0].save();
|
||||
|
||||
result = await window.cvat.tasks.get({
|
||||
id: 2,
|
||||
});
|
||||
|
||||
expect(result[0].bugTracker).toBe('newBugTracker');
|
||||
expect(result[0].zOrder).toBe(true);
|
||||
expect(result[0].name).toBe('New Task Name');
|
||||
});
|
||||
|
||||
test('save some new labels in a task', async () => {
|
||||
let result = await window.cvat.tasks.get({
|
||||
id: 2,
|
||||
});
|
||||
|
||||
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'],
|
||||
}],
|
||||
});
|
||||
|
||||
result[0].labels = [newLabel];
|
||||
result[0].save();
|
||||
|
||||
result = await window.cvat.tasks.get({
|
||||
id: 2,
|
||||
});
|
||||
|
||||
expect(result[0].labels).toHaveLength(labelsLength + 1);
|
||||
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');
|
||||
expect(appendedLabel[0].attributes[0].defaultValue).toBe('false');
|
||||
expect(appendedLabel[0].attributes[0].mutable).toBe(true);
|
||||
expect(appendedLabel[0].attributes[0].inputType).toBe('checkbox');
|
||||
});
|
||||
|
||||
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'],
|
||||
}],
|
||||
}],
|
||||
bug_tracker: 'bug tracker value',
|
||||
image_quality: 50,
|
||||
z_order: true,
|
||||
});
|
||||
|
||||
const result = await task.save();
|
||||
expect(typeof (result.id)).toBe('number');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Feature: delete a task', () => {
|
||||
test('delete a task', async () => {
|
||||
let result = await window.cvat.tasks.get({
|
||||
id: 3,
|
||||
});
|
||||
|
||||
result[0].delete();
|
||||
result = await window.cvat.tasks.get({
|
||||
id: 3,
|
||||
});
|
||||
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
const plugin = {
|
||||
name: 'Example Plugin',
|
||||
description: 'This example plugin demonstrates how plugin system in CVAT works',
|
||||
cvat: {
|
||||
server: {
|
||||
about: {
|
||||
async leave(self, result) {
|
||||
result.plugins = await self.internal.getPlugins();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
classes: {
|
||||
Job: {
|
||||
prototype: {
|
||||
annotations: {
|
||||
put: {
|
||||
enter(self, objects) {
|
||||
for (const obj of objects) {
|
||||
if (obj.type !== 'tag') {
|
||||
const points = obj.position.map((point) => {
|
||||
const roundPoint = {
|
||||
x: Math.round(point.x),
|
||||
y: Math.round(point.y),
|
||||
};
|
||||
return roundPoint;
|
||||
});
|
||||
obj.points = points;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
internal: {
|
||||
async getPlugins() {
|
||||
const plugins = await window.cvat.plugins.list();
|
||||
return plugins.map((el) => {
|
||||
const obj = {
|
||||
name: el.name,
|
||||
description: el.description,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
async function test() {
|
||||
await window.cvat.plugins.register(plugin);
|
||||
await window.cvat.server.login('admin', 'nimda760');
|
||||
|
||||
try {
|
||||
console.log(JSON.stringify(await window.cvat.server.about()));
|
||||
console.log(await window.cvat.users.get({ self: false }));
|
||||
console.log(await window.cvat.users.get({ self: true }));
|
||||
console.log(JSON.stringify(await window.cvat.jobs.get({ taskID: 8 })));
|
||||
console.log(JSON.stringify(await window.cvat.jobs.get({ jobID: 10 })));
|
||||
console.log(await window.cvat.tasks.get());
|
||||
console.log(await window.cvat.tasks.get({ id: 8 }));
|
||||
console.log('Done.');
|
||||
} catch (exception) {
|
||||
console.log(exception.constructor.name);
|
||||
console.log(exception.message);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* global
|
||||
require:false
|
||||
jest:false
|
||||
describe:false
|
||||
*/
|
||||
|
||||
// Setup mock for a server
|
||||
jest.mock('../../src/server-proxy', () => {
|
||||
const mock = require('../mocks/server-proxy.mock');
|
||||
return mock;
|
||||
});
|
||||
|
||||
// Initialize api
|
||||
require('../../src/api');
|
||||
|
||||
const User = require('../../src/user');
|
||||
|
||||
// Test cases
|
||||
describe('Feature: get a list of users', () => {
|
||||
test('get all users', async () => {
|
||||
const result = await window.cvat.users.get();
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(2);
|
||||
for (const el of result) {
|
||||
expect(el).toBeInstanceOf(User);
|
||||
}
|
||||
});
|
||||
|
||||
test('get only self', async () => {
|
||||
const result = await window.cvat.users.get({
|
||||
self: true,
|
||||
});
|
||||
expect(Array.isArray(result)).toBeTruthy();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(User);
|
||||
});
|
||||
|
||||
test('get users with unknown filter key', async () => {
|
||||
await expect(window.cvat.users.get({
|
||||
unknown: '50',
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
|
||||
test('get users with invalid filter key', async () => {
|
||||
await expect(window.cvat.users.get({
|
||||
self: 1,
|
||||
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,889 @@
|
||||
/* eslint-disable */
|
||||
|
||||
const aboutDummyData = {
|
||||
"name": "Computer Vision Annotation Tool",
|
||||
"description": "CVAT is completely re-designed and re-implemented version of Video Annotation Tool from Irvine, California tool. It is free, online, interactive video and image annotation tool for computer vision. It is being used by our team to annotate million of objects with different properties. Many UI and UX decisions are based on feedbacks from professional data annotation team.",
|
||||
"version": "0.5.dev20190516142240"
|
||||
}
|
||||
|
||||
const usersDummyData = {
|
||||
"count": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/users/1",
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"email": "admin@dummy.com",
|
||||
"groups": [
|
||||
"admin"
|
||||
],
|
||||
"is_staff": true,
|
||||
"is_superuser": true,
|
||||
"is_active": true,
|
||||
"last_login": "2019-05-17T11:53:05.961434+03:00",
|
||||
"date_joined": "2019-05-13T15:33:17.833200+03:00"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/users/2",
|
||||
"id": 2,
|
||||
"username": "bsekache",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"email": "",
|
||||
"groups": [
|
||||
"user",
|
||||
"observer"
|
||||
],
|
||||
"is_staff": false,
|
||||
"is_superuser": false,
|
||||
"is_active": true,
|
||||
"last_login": "2019-05-16T13:07:19.564241+03:00",
|
||||
"date_joined": "2019-05-16T13:05:57+03:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const shareDummyData = [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "DIR",
|
||||
"children": [
|
||||
{
|
||||
"name": "image000001.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "nowy-jork-time-sqare.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "123123.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "ws_Oasis-night_1920x1200.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "image000002.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "fdgdfgfd.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "bbbbb.jpg",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "gdfgdfgdf.jpg",
|
||||
"type": "REG"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2.avi",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": "DIR",
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"name": "out.MOV",
|
||||
"type": "REG"
|
||||
},
|
||||
{
|
||||
"name": "bbbbb.jpg",
|
||||
"type": "REG"
|
||||
}
|
||||
]
|
||||
|
||||
const tasksDummyData = {
|
||||
"count": 3,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/tasks/3",
|
||||
"id": 3,
|
||||
"name": "Test Task",
|
||||
"size": 5002,
|
||||
"mode": "interpolation",
|
||||
"owner": 2,
|
||||
"assignee": null,
|
||||
"bug_tracker": "",
|
||||
"created_date": "2019-05-16T13:08:00.621747+03:00",
|
||||
"updated_date": "2019-05-16T13:08:00.621797+03:00",
|
||||
"overlap": 5,
|
||||
"segment_size": 5000,
|
||||
"z_order": true,
|
||||
"flipped": false,
|
||||
"status": "annotation",
|
||||
"labels": [
|
||||
{
|
||||
"id": 16,
|
||||
"name": "bicycle",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 43,
|
||||
"name": "driver",
|
||||
"mutable": false,
|
||||
"input_type": "radio",
|
||||
"default_value": "man",
|
||||
"values": [
|
||||
"man",
|
||||
"woman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"name": "sport",
|
||||
"mutable": true,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "false",
|
||||
"values": [
|
||||
"false"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "car",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 40,
|
||||
"name": "model",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"bmw",
|
||||
"mazda",
|
||||
"suzuki",
|
||||
"kia"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"name": "driver",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"man",
|
||||
"woman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"name": "parked",
|
||||
"mutable": true,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "true",
|
||||
"values": [
|
||||
"true"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "face",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 36,
|
||||
"name": "age",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"baby (0-5)",
|
||||
"child (6-12)",
|
||||
"adolescent (13-19)",
|
||||
"adult (20-45)",
|
||||
"middle-age (46-64)",
|
||||
"old (65-)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"name": "glass",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"no",
|
||||
"sunglass",
|
||||
"transparent",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"name": "beard",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"no",
|
||||
"yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"name": "race",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"asian",
|
||||
"black",
|
||||
"caucasian",
|
||||
"other"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "motorcycle",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 45,
|
||||
"name": "model",
|
||||
"mutable": false,
|
||||
"input_type": "text",
|
||||
"default_value": "unknown",
|
||||
"values": [
|
||||
"unknown"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "person, pedestrian",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 31,
|
||||
"name": "action",
|
||||
"mutable": true,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"sitting",
|
||||
"raising_hand",
|
||||
"standing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"name": "age",
|
||||
"mutable": false,
|
||||
"input_type": "number",
|
||||
"default_value": "1",
|
||||
"values": [
|
||||
"1",
|
||||
"100",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"name": "gender",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "male",
|
||||
"values": [
|
||||
"male",
|
||||
"female"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"name": "false positive",
|
||||
"mutable": false,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "false",
|
||||
"values": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"name": "clother",
|
||||
"mutable": true,
|
||||
"input_type": "text",
|
||||
"default_value": "non, initialized",
|
||||
"values": [
|
||||
"non, initialized"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "road",
|
||||
"attributes": []
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"start_frame": 0,
|
||||
"stop_frame": 4999,
|
||||
"jobs": [
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/jobs/3",
|
||||
"id": 3,
|
||||
"assignee": null,
|
||||
"status": "annotation"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"start_frame": 4995,
|
||||
"stop_frame": 5001,
|
||||
"jobs": [
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/jobs/4",
|
||||
"id": 4,
|
||||
"assignee": null,
|
||||
"status": "annotation"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"image_quality": 50
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/tasks/2",
|
||||
"id": 2,
|
||||
"name": "Video",
|
||||
"size": 75,
|
||||
"mode": "interpolation",
|
||||
"owner": 1,
|
||||
"assignee": null,
|
||||
"bug_tracker": "",
|
||||
"created_date": "2019-05-15T11:40:19.487999+03:00",
|
||||
"updated_date": "2019-05-15T16:58:27.992785+03:00",
|
||||
"overlap": 5,
|
||||
"segment_size": 0,
|
||||
"z_order": false,
|
||||
"flipped": false,
|
||||
"status": "annotation",
|
||||
"labels": [
|
||||
{
|
||||
"id": 10,
|
||||
"name": "bicycle",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 28,
|
||||
"name": "driver",
|
||||
"mutable": false,
|
||||
"input_type": "radio",
|
||||
"default_value": "man",
|
||||
"values": [
|
||||
"man",
|
||||
"woman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"name": "sport",
|
||||
"mutable": true,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "false",
|
||||
"values": [
|
||||
"false"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "car",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 25,
|
||||
"name": "model",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"bmw",
|
||||
"mazda",
|
||||
"suzuki",
|
||||
"kia"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"name": "driver",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"man",
|
||||
"woman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"name": "parked",
|
||||
"mutable": true,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "true",
|
||||
"values": [
|
||||
"true"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "face",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 21,
|
||||
"name": "age",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"baby (0-5)",
|
||||
"child (6-12)",
|
||||
"adolescent (13-19)",
|
||||
"adult (20-45)",
|
||||
"middle-age (46-64)",
|
||||
"old (65-)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"name": "glass",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"no",
|
||||
"sunglass",
|
||||
"transparent",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"name": "beard",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"no",
|
||||
"yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"name": "race",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"asian",
|
||||
"black",
|
||||
"caucasian",
|
||||
"other"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "motorcycle",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 30,
|
||||
"name": "model",
|
||||
"mutable": false,
|
||||
"input_type": "text",
|
||||
"default_value": "unknown",
|
||||
"values": [
|
||||
"unknown"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "person, pedestrian",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 16,
|
||||
"name": "action",
|
||||
"mutable": true,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"sitting",
|
||||
"raising_hand",
|
||||
"standing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "age",
|
||||
"mutable": false,
|
||||
"input_type": "number",
|
||||
"default_value": "1",
|
||||
"values": [
|
||||
"1",
|
||||
"100",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "gender",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "male",
|
||||
"values": [
|
||||
"male",
|
||||
"female"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "false positive",
|
||||
"mutable": false,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "false",
|
||||
"values": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"name": "clother",
|
||||
"mutable": true,
|
||||
"input_type": "text",
|
||||
"default_value": "non, initialized",
|
||||
"values": [
|
||||
"non, initialized"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "road",
|
||||
"attributes": []
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"start_frame": 0,
|
||||
"stop_frame": 74,
|
||||
"jobs": [
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/jobs/2",
|
||||
"id": 2,
|
||||
"assignee": null,
|
||||
"status": "annotation"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"image_quality": 50
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/tasks/1",
|
||||
"id": 1,
|
||||
"name": "Labels Set",
|
||||
"size": 9,
|
||||
"mode": "annotation",
|
||||
"owner": 1,
|
||||
"assignee": null,
|
||||
"bug_tracker": "http://bugtracker.com/issue12345",
|
||||
"created_date": "2019-05-13T15:35:29.871003+03:00",
|
||||
"updated_date": "2019-05-15T11:20:55.770587+03:00",
|
||||
"overlap": 0,
|
||||
"segment_size": 0,
|
||||
"z_order": true,
|
||||
"flipped": false,
|
||||
"status": "annotation",
|
||||
"labels": [
|
||||
{
|
||||
"id": 4,
|
||||
"name": "bicycle",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 13,
|
||||
"name": "driver",
|
||||
"mutable": false,
|
||||
"input_type": "radio",
|
||||
"default_value": "man",
|
||||
"values": [
|
||||
"man",
|
||||
"woman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "sport",
|
||||
"mutable": true,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "false",
|
||||
"values": [
|
||||
"false"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "car",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 10,
|
||||
"name": "model",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"bmw",
|
||||
"mazda",
|
||||
"suzuki",
|
||||
"kia"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "driver",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"man",
|
||||
"woman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "parked",
|
||||
"mutable": true,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "true",
|
||||
"values": [
|
||||
"true"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "face",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 6,
|
||||
"name": "age",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"baby (0-5)",
|
||||
"child (6-12)",
|
||||
"adolescent (13-19)",
|
||||
"adult (20-45)",
|
||||
"middle-age (46-64)",
|
||||
"old (65-)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "glass",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"no",
|
||||
"sunglass",
|
||||
"transparent",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "beard",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"no",
|
||||
"yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "race",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"skip",
|
||||
"asian",
|
||||
"black",
|
||||
"caucasian",
|
||||
"other"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "motorcycle",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 15,
|
||||
"name": "model",
|
||||
"mutable": false,
|
||||
"input_type": "text",
|
||||
"default_value": "unknown",
|
||||
"values": [
|
||||
"unknown"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "person, pedestrian",
|
||||
"attributes": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "action",
|
||||
"mutable": true,
|
||||
"input_type": "select",
|
||||
"default_value": "__undefined__",
|
||||
"values": [
|
||||
"__undefined__",
|
||||
"sitting",
|
||||
"raising_hand",
|
||||
"standing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "age",
|
||||
"mutable": false,
|
||||
"input_type": "number",
|
||||
"default_value": "1",
|
||||
"values": [
|
||||
"1",
|
||||
"100",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "gender",
|
||||
"mutable": false,
|
||||
"input_type": "select",
|
||||
"default_value": "male",
|
||||
"values": [
|
||||
"male",
|
||||
"female"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "false positive",
|
||||
"mutable": false,
|
||||
"input_type": "checkbox",
|
||||
"default_value": "false",
|
||||
"values": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "clother",
|
||||
"mutable": true,
|
||||
"input_type": "text",
|
||||
"default_value": "non, initialized",
|
||||
"values": [
|
||||
"non, initialized"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "road",
|
||||
"attributes": []
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"start_frame": 0,
|
||||
"stop_frame": 8,
|
||||
"jobs": [
|
||||
{
|
||||
"url": "http://localhost:7000/api/v1/jobs/1",
|
||||
"id": 1,
|
||||
"assignee": null,
|
||||
"status": "annotation"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"image_quality": 95
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tasksDummyData,
|
||||
aboutDummyData,
|
||||
shareDummyData,
|
||||
usersDummyData,
|
||||
}
|
||||
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* eslint import/no-extraneous-dependencies: 0 */
|
||||
|
||||
/* global
|
||||
require:false
|
||||
*/
|
||||
|
||||
const {
|
||||
tasksDummyData,
|
||||
aboutDummyData,
|
||||
shareDummyData,
|
||||
usersDummyData,
|
||||
} = require('./dummy-data.mock');
|
||||
|
||||
|
||||
class ServerProxy {
|
||||
constructor() {
|
||||
async function about() {
|
||||
return JSON.parse(JSON.stringify(aboutDummyData));
|
||||
}
|
||||
|
||||
async function share(directory) {
|
||||
let position = shareDummyData;
|
||||
|
||||
// Emulation of internal directories
|
||||
if (directory.length > 1) {
|
||||
const components = directory.split('/');
|
||||
|
||||
for (const component of components) {
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(position));
|
||||
}
|
||||
|
||||
async function exception() {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function login() {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getTasks(filter = '') {
|
||||
function QueryStringToJSON(query) {
|
||||
const pairs = [...new URLSearchParams(query).entries()];
|
||||
|
||||
const result = {};
|
||||
for (const pair of pairs) {
|
||||
const [key, value] = pair;
|
||||
if (['id'].includes(key)) {
|
||||
result[key] = +value;
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(result));
|
||||
}
|
||||
|
||||
// Emulation of a query filter
|
||||
const queries = QueryStringToJSON(filter);
|
||||
const result = tasksDummyData.results.filter((x) => {
|
||||
for (const key in queries) {
|
||||
if (Object.prototype.hasOwnProperty.call(queries, key)) {
|
||||
// TODO: Particular match for some fields is not checked
|
||||
if (queries[key] !== x[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function saveTask(id, taskData) {
|
||||
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)) {
|
||||
object[prop] = taskData[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createTask(taskData) {
|
||||
const id = Math.max(...tasksDummyData.results.map(el => el.id)) + 1;
|
||||
tasksDummyData.results.push({
|
||||
id,
|
||||
url: `http://localhost:7000/api/v1/tasks/${id}`,
|
||||
name: taskData.name,
|
||||
size: 5000,
|
||||
mode: 'interpolation',
|
||||
owner: 2,
|
||||
assignee: null,
|
||||
bug_tracker: taskData.bug_tracker,
|
||||
created_date: '2019-05-16T13:08:00.621747+03:00',
|
||||
updated_date: '2019-05-16T13:08:00.621797+03:00',
|
||||
overlap: taskData.overlap ? taskData.overlap : 5,
|
||||
segment_size: taskData.segment_size ? taskData.segment_size : 5000,
|
||||
z_order: taskData.z_order,
|
||||
flipped: false,
|
||||
status: 'annotation',
|
||||
image_quality: taskData.image_quality,
|
||||
labels: JSON.parse(JSON.stringify(taskData.labels)),
|
||||
});
|
||||
|
||||
const createdTask = await getTasks(`?id=${id}`);
|
||||
return createdTask[0];
|
||||
}
|
||||
|
||||
async function deleteTask(id) {
|
||||
const tasks = tasksDummyData.results;
|
||||
const task = tasks.filter(el => el.id === id)[0];
|
||||
if (task) {
|
||||
tasks.splice(tasks.indexOf(task), 1);
|
||||
}
|
||||
}
|
||||
|
||||
async function getJob(jobID) {
|
||||
return 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
object[prop] = jobData[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getUsers() {
|
||||
return JSON.parse(JSON.stringify(usersDummyData)).results;
|
||||
}
|
||||
|
||||
async function getSelf() {
|
||||
return JSON.parse(JSON.stringify(usersDummyData)).results[0];
|
||||
}
|
||||
|
||||
async function getFrame() {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getMeta() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object.defineProperties(this, Object.freeze({
|
||||
server: {
|
||||
value: Object.freeze({
|
||||
about,
|
||||
share,
|
||||
exception,
|
||||
login,
|
||||
}),
|
||||
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({
|
||||
getFrame,
|
||||
getMeta,
|
||||
}),
|
||||
writable: false,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const serverProxy = new ServerProxy();
|
||||
module.exports = serverProxy;
|
||||
@ -0,0 +1,59 @@
|
||||
/* global
|
||||
require:true,
|
||||
__dirname:true,
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
const configuration = 'production';
|
||||
const target = 'web';
|
||||
const plugins = [];
|
||||
|
||||
if (configuration === 'production' && target === 'web') {
|
||||
plugins.push(
|
||||
/**
|
||||
* IgnorePlugin will skip any require
|
||||
* that matches the following regex.
|
||||
*/
|
||||
new webpack.IgnorePlugin(/browser-env/),
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mode: configuration,
|
||||
devtool: 'source-map',
|
||||
entry: './src/api.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'cvat.js',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /.js?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
targets: {
|
||||
chrome: 58,
|
||||
},
|
||||
useBuiltIns: 'usage',
|
||||
corejs: 3,
|
||||
loose: false,
|
||||
spec: false,
|
||||
debug: false,
|
||||
include: [],
|
||||
exclude: [],
|
||||
}],
|
||||
],
|
||||
sourceType: 'unambiguous',
|
||||
},
|
||||
},
|
||||
}],
|
||||
},
|
||||
plugins,
|
||||
target,
|
||||
};
|
||||
Loading…
Reference in New Issue