From 3a1a591a3daa717e9416684f4247aae61d1cd7fa Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Tue, 26 Nov 2019 18:23:46 +0300
Subject: [PATCH 01/15] Automatic label matching (by the same name) in model
running window
---
.../model-runner-modal/model-runner-modal.tsx | 44 +++++++++++++------
1 file changed, 31 insertions(+), 13 deletions(-)
diff --git a/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx b/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
index aeb0439306b2..42ea7cf93b21 100644
--- a/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
+++ b/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
@@ -14,12 +14,14 @@ import {
import { Model } from '../../reducers/interfaces';
+interface StringObject {
+ [index: string]: string;
+}
+
interface Props {
modelsInitialized: boolean;
models: Model[];
- activeProcesses: {
- [index: string]: string;
- };
+ activeProcesses: StringObject;
visible: boolean;
taskInstance: any;
startingError: string;
@@ -28,9 +30,7 @@ interface Props {
runInference(
taskInstance: any,
model: Model,
- mapping: {
- [index: string]: string
- },
+ mapping: StringObject,
cleanOut: boolean,
): void;
}
@@ -38,12 +38,8 @@ interface Props {
interface State {
selectedModel: string | null;
cleanOut: boolean;
- mapping: {
- [index: string]: string;
- };
- colors: {
- [index: string]: string;
- };
+ mapping: StringObject;
+ colors: StringObject;
matching: {
model: string,
task: string,
@@ -277,7 +273,7 @@ export default class ModelRunnerModalComponent extends React.PureComponent model.name === this.state.selectedModel)[0];
+ if (!model.primary) {
+ let taskLabels: string[] = this.props.taskInstance.labels
+ .map((label: any) => label.name);
+ const defaultMapping: StringObject = model.labels
+ .reduce((acc: StringObject, label) => {
+ if (taskLabels.includes(label)) {
+ acc[label] = label;
+ taskLabels = taskLabels.filter((_label) => _label !== label)
+ }
+
+ return acc;
+ }, {});
+
+ this.setState({
+ mapping: defaultMapping,
+ });
+ }
+ }
}
public componentDidMount() {
From 1f5f8a9c5ce92419aa84e356910510f655559c51 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Tue, 26 Nov 2019 19:21:02 +0300
Subject: [PATCH 02/15] Improved create task window
---
.../create-model-page/create-model-form.tsx | 2 +-
.../advanced-configuration-form.tsx | 131 +++++++++---------
.../basic-configuration-form.tsx | 3 +-
.../create-task-page/create-task-content.tsx | 9 +-
.../create-task-page/create-task-page.tsx | 2 +-
.../components/file-manager/file-manager.tsx | 1 -
6 files changed, 73 insertions(+), 75 deletions(-)
diff --git a/cvat-ui/src/components/create-model-page/create-model-form.tsx b/cvat-ui/src/components/create-model-page/create-model-form.tsx
index 44eb7b23ceed..81966a9359e0 100644
--- a/cvat-ui/src/components/create-model-page/create-model-form.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-form.tsx
@@ -50,7 +50,7 @@ export class CreateModelForm extends React.PureComponent {
-
+
{ getFieldDecorator('name', {
rules: [{
required: true,
diff --git a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx
index 93c100ca4422..56cc88da846a 100644
--- a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx
+++ b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx
@@ -58,28 +58,25 @@ class AdvancedConfigurationForm extends React.PureComponent {
private renderZOrder() {
return (
-
-
- {this.props.form.getFieldDecorator('zOrder', {
- initialValue: false,
- valuePropName: 'checked',
- })(
-
-
- Z-order
-
-
- )}
-
+
+ {this.props.form.getFieldDecorator('zOrder', {
+ initialValue: false,
+ valuePropName: 'checked',
+ })(
+
+
+ Z-order
+
+
+ )}
);
}
private renderImageQuality() {
return (
-
+
- {'Image quality'}
{this.props.form.getFieldDecorator('imageQuality', {
initialValue: 70,
rules: [{
@@ -102,9 +99,8 @@ class AdvancedConfigurationForm extends React.PureComponent {
private renderOverlap() {
return (
-
+
- {'Overlap size'}
{this.props.form.getFieldDecorator('overlapSize')(
)}
@@ -115,9 +111,8 @@ class AdvancedConfigurationForm extends React.PureComponent {
private renderSegmentSize() {
return (
-
+
- {'Segment size'}
{this.props.form.getFieldDecorator('segmentSize')(
)}
@@ -128,8 +123,7 @@ class AdvancedConfigurationForm extends React.PureComponent {
private renderStartFrame() {
return (
-
- {'Start frame'}
+
{this.props.form.getFieldDecorator('startFrame')(
{
private renderStopFrame() {
return (
-
- {'Stop frame'}
+
{this.props.form.getFieldDecorator('stopFrame')(
{
private renderFrameStep() {
return (
-
- {'Frame step'}
+
{this.props.form.getFieldDecorator('frameStep')(
{
private renderGitLFSBox() {
return (
-
-
- {this.props.form.getFieldDecorator('lfs', {
- valuePropName: 'checked',
- initialValue: false,
- })(
-
-
- Use LFS (Large File Support)
-
-
- )}
-
+
+ {this.props.form.getFieldDecorator('lfs', {
+ valuePropName: 'checked',
+ initialValue: false,
+ })(
+
+
+ Use LFS (Large File Support):
+
+
+ )}
);
}
private renderGitRepositoryURL() {
return (
-
-
- {'Dataset repository URL'}
- {this.props.form.getFieldDecorator('repository', {
- rules: [{
- validator: (_, value, callback) => {
+
+ {this.props.form.getFieldDecorator('repository', {
+ rules: [{
+ validator: (_, value, callback) => {
+ if (!value) {
+ callback();
+ } else {
const [url, path] = value.split(/\s+/);
if (!patterns.validateURL.pattern.test(url)) {
callback('Git URL is not a valid');
@@ -213,14 +207,13 @@ class AdvancedConfigurationForm extends React.PureComponent {
callback();
}
- }]
- })(
-
- )}
-
+ }
+ }]
+ })(
+
+ )}
);
}
@@ -231,7 +224,6 @@ class AdvancedConfigurationForm extends React.PureComponent {
{this.renderGitRepositoryURL()}
-
@@ -245,19 +237,24 @@ class AdvancedConfigurationForm extends React.PureComponent {
private renderBugTracker() {
return (
-
-
- {'Issue tracker'}
- {this.props.form.getFieldDecorator('bugTracker', {
- rules: [{
- ...patterns.validateURL,
- }]
- })(
-
- )}
-
+
+ {this.props.form.getFieldDecorator('bugTracker', {
+ rules: [{
+ validator: (_, value, callback) => {
+ if (value && !patterns.validateURL.pattern.test(value)) {
+ callback('Issue tracker must be URL');
+ } else {
+ callback();
+ }
+ }
+ }]
+ })(
+
+ )}
)
}
diff --git a/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx b/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx
index 2d0d5d0a656a..e0b877a45072 100644
--- a/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx
+++ b/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx
@@ -39,8 +39,7 @@ class BasicConfigurationForm extends React.PureComponent {
const { getFieldDecorator } = this.props.form;
return (
+
{ getFieldDecorator('name', {
rules: [{
required: true,
diff --git a/cvat-ui/src/components/create-task-page/create-task-content.tsx b/cvat-ui/src/components/create-task-page/create-task-content.tsx
index 9b4ac355d873..c67452ac2c50 100644
--- a/cvat-ui/src/components/create-task-page/create-task-content.tsx
+++ b/cvat-ui/src/components/create-task-page/create-task-content.tsx
@@ -137,7 +137,8 @@ export default class CreateTaskContent extends React.PureComponent
private renderLabelsBlock() {
return (
- Labels
+ *
+ Labels:
private renderFilesBlock() {
return (
+ *
+ Select files:
this.fileManagerContainer = container
@@ -169,7 +172,7 @@ export default class CreateTaskContent extends React.PureComponent
{'Advanced configuration'}
+ Advanced configuration
} key='1'>
return (
- {'Basic configuration'}
+ Basic configuration
{ this.renderBasicBlock() }
diff --git a/cvat-ui/src/components/create-task-page/create-task-page.tsx b/cvat-ui/src/components/create-task-page/create-task-page.tsx
index d615584399a9..d59d5f3615ab 100644
--- a/cvat-ui/src/components/create-task-page/create-task-page.tsx
+++ b/cvat-ui/src/components/create-task-page/create-task-page.tsx
@@ -20,7 +20,7 @@ export default function CreateTaskPage(props: Props) {
return (
- {'Create a new task'}
+ Create a new task
{
public render() {
return (
<>
- {'Select files'}
this.setState({
active: activeKey as any,
})}>
From 27b0b5f127db923f78d9ed322d76057161180cf6 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Tue, 26 Nov 2019 19:35:13 +0300
Subject: [PATCH 03/15] Imroved upload model window
---
.../create-model-page/create-model-content.tsx | 6 ++++++
.../create-model-page/create-model-form.tsx | 12 ++++--------
.../create-model-page/create-model-page.tsx | 2 +-
3 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/cvat-ui/src/components/create-model-page/create-model-content.tsx b/cvat-ui/src/components/create-model-page/create-model-content.tsx
index 94c36e111cda..e2da3337130b 100644
--- a/cvat-ui/src/components/create-model-page/create-model-content.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-content.tsx
@@ -11,6 +11,8 @@ import {
message,
} from 'antd';
+import Text from 'antd/lib/typography/Text';
+
import CreateModelForm, {
CreateModelForm as WrappedCreateModelForm
} from './create-model-form';
@@ -116,6 +118,10 @@ export default class CreateModelContent extends React.PureComponent {
}
/>
+
+ *
+ Select files:
+
diff --git a/cvat-ui/src/components/create-model-page/create-model-form.tsx b/cvat-ui/src/components/create-model-page/create-model-form.tsx
index 81966a9359e0..1a1965933468 100644
--- a/cvat-ui/src/components/create-model-page/create-model-form.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-form.tsx
@@ -43,12 +43,11 @@ export class CreateModelForm extends React.PureComponent {
return (
);
}
diff --git a/cvat-ui/src/components/create-model-page/create-model-page.tsx b/cvat-ui/src/components/create-model-page/create-model-page.tsx
index 1f1e2e05a712..205e7fe91055 100644
--- a/cvat-ui/src/components/create-model-page/create-model-page.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-page.tsx
@@ -21,7 +21,7 @@ export default function CreateModelPageComponent(props: Props) {
return (
- {`Upload a new model`}
+ Upload a new model
Date: Tue, 26 Nov 2019 19:53:52 +0300
Subject: [PATCH 04/15] Minor fixes
---
.../components/create-model-page/create-model-content.tsx | 4 +++-
cvat-ui/src/components/file-manager/file-manager.tsx | 8 ++++----
.../src/components/models-page/uploaded-model-item.tsx | 3 +--
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/cvat-ui/src/components/create-model-page/create-model-content.tsx b/cvat-ui/src/components/create-model-page/create-model-content.tsx
index e2da3337130b..60ab1dd90ac1 100644
--- a/cvat-ui/src/components/create-model-page/create-model-content.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-content.tsx
@@ -108,7 +108,9 @@ export default class CreateModelContent extends React.PureComponent {
- {window.open(guideLink, '_blank')}} type='question-circle'/>
+ {
+ window.open(guideLink, '_blank')
+ }} type='question-circle'/>
diff --git a/cvat-ui/src/components/file-manager/file-manager.tsx b/cvat-ui/src/components/file-manager/file-manager.tsx
index ba1df9d9f007..e34aeaba6673 100644
--- a/cvat-ui/src/components/file-manager/file-manager.tsx
+++ b/cvat-ui/src/components/file-manager/file-manager.tsx
@@ -81,13 +81,13 @@ export default class FileManager extends React.PureComponent {
Support for a bulk images or a single video
- { this.state.files.local.length ?
+ { !!this.state.files.local.length &&
<>
- {this.state.files.local.length} file(s) selected
+ {`${this.state.files.local.length} file(s) selected`}
- > : null
+ >
}
);
@@ -189,7 +189,7 @@ export default class FileManager extends React.PureComponent {
})}>
{ this.renderLocalSelector() }
{ this.renderShareSelector() }
- { this.props.withRemote ? this.renderRemoteSelector() : null }
+ { this.props.withRemote && this.renderRemoteSelector() }
>
);
diff --git a/cvat-ui/src/components/models-page/uploaded-model-item.tsx b/cvat-ui/src/components/models-page/uploaded-model-item.tsx
index 01d488973150..bad3f1a63cae 100644
--- a/cvat-ui/src/components/models-page/uploaded-model-item.tsx
+++ b/cvat-ui/src/components/models-page/uploaded-model-item.tsx
@@ -7,7 +7,6 @@ import {
Select,
Menu,
Dropdown,
- Button,
Icon,
} from 'antd';
@@ -62,7 +61,7 @@ export default function UploadedModelItem(props: Props) {
Actions
+
{
props.onDelete();
}}key='delete'>Delete
From 37cc807753f5e975b18d1f0ef0a655ddb4874778 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Tue, 26 Nov 2019 20:16:55 +0300
Subject: [PATCH 05/15] Fixed: error window showed twice
---
cvat-ui/src/components/task-page/details.tsx | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx
index 11cccc2b7fb5..6d2b0b2e5de3 100644
--- a/cvat-ui/src/components/task-page/details.tsx
+++ b/cvat-ui/src/components/task-page/details.tsx
@@ -209,12 +209,19 @@ export default class DetailsComponent extends React.PureComponent
const { taskInstance } = this.props;
const { bugTracker } = this.state;
+ let shown = false;
const onChangeValue = (value: string) => {
if (value && !patterns.validateURL.pattern.test(value)) {
- Modal.error({
- title: `Could not update the task ${taskInstance.id}`,
- content: 'Issue tracker is expected to be URL',
- });
+ if (!shown) {
+ Modal.error({
+ title: `Could not update the task ${taskInstance.id}`,
+ content: 'Issue tracker is expected to be URL',
+ onOk: (() => {
+ shown = false;
+ }),
+ });
+ shown = true;
+ }
} else {
this.setState({
bugTracker: value,
From abf72519d163e55ecf37c196280fc0c325279f85 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Tue, 26 Nov 2019 20:20:03 +0300
Subject: [PATCH 06/15] Updated CONTRIBUTING.md
---
CONTRIBUTING.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e2c6e88d7d29..ea7c4a631d5e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -29,6 +29,7 @@ $ python3 -m venv .env
$ . .env/bin/activate
$ pip install -U pip wheel
$ pip install -r cvat/requirements/development.txt
+$ pip install -r datumaro/requirements.txt
$ python manage.py migrate
$ python manage.py collectstatic
```
From 0d92d33e505c6da20948de21fda77606f160667d Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Wed, 27 Nov 2019 14:02:01 +0300
Subject: [PATCH 07/15] Removed token before login, fixed dump submenu
(adjustment), fixed case when empty models list displayed
---
cvat-core/src/server-proxy.js | 1 +
cvat-ui/src/components/models-page/models-page.tsx | 11 +++++++----
cvat-ui/src/stylesheet.css | 4 ++++
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js
index 4faf9e21a43e..b151768473b3 100644
--- a/cvat-core/src/server-proxy.js
+++ b/cvat-core/src/server-proxy.js
@@ -147,6 +147,7 @@
`${encodeURIComponent('password')}=${encodeURIComponent(password)}`,
]).join('&').replace(/%20/g, '+');
+ Axios.defaults.headers.common.Authorization = '';
let authenticationResponse = null;
try {
authenticationResponse = await Axios.post(
diff --git a/cvat-ui/src/components/models-page/models-page.tsx b/cvat-ui/src/components/models-page/models-page.tsx
index f6d3df5099a4..fe6fb1c277d1 100644
--- a/cvat-ui/src/components/models-page/models-page.tsx
+++ b/cvat-ui/src/components/models-page/models-page.tsx
@@ -35,15 +35,18 @@ export default function ModelsPageComponent(props: Props) {
return (
- { integratedModels.length ?
- : null }
- { uploadedModels.length &&
+ { !!integratedModels.length &&
+
+ }
+ { !!uploadedModels.length &&
- } { props.installedAutoAnnotation &&
+ }
+ { props.installedAutoAnnotation &&
+ !uploadedModels.length &&
!props.installedTFAnnotation &&
!props.installedTFSegmentation &&
diff --git a/cvat-ui/src/stylesheet.css b/cvat-ui/src/stylesheet.css
index c170431dc9da..283aeae60b8d 100644
--- a/cvat-ui/src/stylesheet.css
+++ b/cvat-ui/src/stylesheet.css
@@ -316,6 +316,10 @@
background-color: rgba(24,144,255,0.05);
}
+.cvat-actions-menu-dump-submenu-item > button {
+ text-align: start;
+}
+
.cvat-actions-menu-dump-submenu-item:hover {
background-color: rgba(24,144,255,0.05);
}
From a29288f23a2b3e8f70e7504b559d472ae2a604b4 Mon Sep 17 00:00:00 2001
From: Nobody
Date: Wed, 27 Nov 2019 11:07:18 +0000
Subject: [PATCH 08/15] Initial commit
From c197c7baa124af106ec3f2dd6577d20cd4b2f2c0 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Wed, 27 Nov 2019 19:18:42 +0300
Subject: [PATCH 09/15] Export as dataset, better error showing system
---
cvat-core/src/server-proxy.js | 2 +-
cvat-ui/src/actions/formats-actions.ts | 16 +-
cvat-ui/src/actions/notification-actions.ts | 24 ++
cvat-ui/src/actions/tasks-actions.ts | 63 +++-
cvat-ui/src/actions/users-actions.ts | 11 -
.../components/actions-menu/actions-menu.tsx | 28 +-
.../components/actions-menu/dumper-item.tsx | 8 +-
.../components/actions-menu/export-item.tsx | 38 +++
.../create-task-page/create-task-content.tsx | 16 +-
.../create-task-page/create-task-page.tsx | 2 -
cvat-ui/src/components/cvat-app.tsx | 166 ++++++---
cvat-ui/src/components/header/header.tsx | 130 +++----
.../src/components/login-page/login-page.tsx | 9 -
.../model-runner-modal/model-runner-modal.tsx | 8 -
.../components/models-page/models-page.tsx | 1 -
.../register-page/register-page.tsx | 8 -
.../src/components/task-page/task-page.tsx | 40 +--
.../src/components/tasks-page/tasks-page.tsx | 42 ---
.../containers/actions-menu/actions-menu.tsx | 26 +-
.../create-task-page/create-task-page.tsx | 3 -
.../containers/file-manager/file-manager.tsx | 1 +
cvat-ui/src/containers/header/header.tsx | 3 -
.../src/containers/login-page/login-page.tsx | 14 +-
.../model-runner-dialog.tsx | 3 -
.../containers/models-page/models-page.tsx | 3 -
.../register-page/register-page.tsx | 15 +-
.../src/containers/task-page/task-page.tsx | 19 +-
.../src/containers/tasks-page/tasks-page.tsx | 19 --
cvat-ui/src/index.tsx | 27 +-
cvat-ui/src/reducers/auth-reducer.ts | 31 --
cvat-ui/src/reducers/formats-reducer.ts | 11 +-
cvat-ui/src/reducers/interfaces.ts | 72 ++--
cvat-ui/src/reducers/models-reducer.ts | 36 --
cvat-ui/src/reducers/notifications-reducer.ts | 319 ++++++++++++++++++
cvat-ui/src/reducers/root-reducer.ts | 2 +
cvat-ui/src/reducers/share-reducer.ts | 15 -
cvat-ui/src/reducers/tasks-reducer.ts | 120 ++++---
cvat-ui/src/reducers/users-reducer.ts | 8 -
cvat-ui/src/stylesheet.css | 8 +
39 files changed, 859 insertions(+), 508 deletions(-)
create mode 100644 cvat-ui/src/actions/notification-actions.ts
create mode 100644 cvat-ui/src/components/actions-menu/export-item.tsx
create mode 100644 cvat-ui/src/reducers/notifications-reducer.ts
diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js
index b151768473b3..fcd5a2a06ef7 100644
--- a/cvat-core/src/server-proxy.js
+++ b/cvat-core/src/server-proxy.js
@@ -247,7 +247,7 @@
try {
await Axios.delete(`${backendAPI}/tasks/${id}`);
} catch (errorData) {
- throw generateError(errorData, 'Could not delete the task from the server');
+ throw generateError(errorData, `Could not delete the task ${id} from the server`);
}
}
diff --git a/cvat-ui/src/actions/formats-actions.ts b/cvat-ui/src/actions/formats-actions.ts
index 806e73aa0f66..1168b7d9ae67 100644
--- a/cvat-ui/src/actions/formats-actions.ts
+++ b/cvat-ui/src/actions/formats-actions.ts
@@ -10,11 +10,15 @@ export enum FormatsActionTypes {
GETTING_FORMATS_FAILED = 'GETTING_FORMATS_FAILED',
}
-export function gettingFormatsSuccess(formats: any): AnyAction {
+export function gettingFormatsSuccess(
+ annotationFormats: any[],
+ datasetFormats: any[],
+): AnyAction {
return {
type: FormatsActionTypes.GETTING_FORMATS_SUCCESS,
payload: {
- formats,
+ annotationFormats,
+ datasetFormats,
},
};
}
@@ -30,14 +34,16 @@ export function gettingFormatsFailed(error: any): AnyAction {
export function gettingFormatsAsync(): ThunkAction, {}, {}, AnyAction> {
return async (dispatch: ActionCreator): Promise => {
- let formats = null;
+ let annotationFormats = null;
+ let datasetFormats = null;
try {
- formats = await cvat.server.formats();
+ annotationFormats = await cvat.server.formats();
+ datasetFormats = await cvat.server.datasetFormats();
} catch (error) {
dispatch(gettingFormatsFailed(error));
return;
}
- dispatch(gettingFormatsSuccess(formats));
+ dispatch(gettingFormatsSuccess(annotationFormats, datasetFormats));
};
}
diff --git a/cvat-ui/src/actions/notification-actions.ts b/cvat-ui/src/actions/notification-actions.ts
new file mode 100644
index 000000000000..b8431106c524
--- /dev/null
+++ b/cvat-ui/src/actions/notification-actions.ts
@@ -0,0 +1,24 @@
+import { AnyAction } from 'redux';
+
+export enum NotificationsActionType {
+ RESET_ERRORS = 'RESET_ERRORS',
+ RESET_MESSAGES = 'RESET_MESSAGES',
+}
+
+export function resetErrors(): AnyAction {
+ const action = {
+ type: NotificationsActionType.RESET_ERRORS,
+ payload: {},
+ };
+
+ return action;
+}
+
+export function resetMessages(): AnyAction {
+ const action = {
+ type: NotificationsActionType.RESET_MESSAGES,
+ payload: {},
+ };
+
+ return action;
+}
diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts
index b54378234ed2..911c8e1d0357 100644
--- a/cvat-ui/src/actions/tasks-actions.ts
+++ b/cvat-ui/src/actions/tasks-actions.ts
@@ -16,6 +16,9 @@ export enum TasksActionTypes {
DUMP_ANNOTATIONS = 'DUMP_ANNOTATIONS',
DUMP_ANNOTATIONS_SUCCESS = 'DUMP_ANNOTATIONS_SUCCESS',
DUMP_ANNOTATIONS_FAILED = 'DUMP_ANNOTATIONS_FAILED',
+ EXPORT_DATASET = 'EXPORT_DATASET',
+ EXPORT_DATASET_SUCCESS = 'EXPORT_DATASET_SUCCESS',
+ EXPORT_DATASET_FAILED = 'EXPORT_DATASET_FAILED',
DELETE_TASK = 'DELETE_TASK',
DELETE_TASK_SUCCESS = 'DELETE_TASK_SUCCESS',
DELETE_TASK_FAILED = 'DELETE_TASK_FAILED',
@@ -26,6 +29,7 @@ export enum TasksActionTypes {
UPDATE_TASK = 'UPDATE_TASK',
UPDATE_TASK_SUCCESS = 'UPDATE_TASK_SUCCESS',
UPDATE_TASK_FAILED = 'UPDATE_TASK_FAILED',
+ RESET_ERROR = 'RESET_ERROR',
}
function getTasks(): AnyAction {
@@ -150,7 +154,9 @@ ThunkAction, {}, {}, AnyAction> {
try {
dispatch(dumpAnnotation(task, dumper));
const url = await task.annotations.dump(task.name, dumper);
- window.location.assign(url);
+ // false positive
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
+ window.open(url);
} catch (error) {
dispatch(dumpAnnotationFailed(task, dumper, error));
return;
@@ -210,6 +216,61 @@ ThunkAction, {}, {}, AnyAction> {
};
}
+function exportDataset(task: any, exporter: any): AnyAction {
+ const action = {
+ type: TasksActionTypes.EXPORT_DATASET,
+ payload: {
+ task,
+ exporter,
+ },
+ };
+
+ return action;
+}
+
+function exportDatasetSuccess(task: any, exporter: any): AnyAction {
+ const action = {
+ type: TasksActionTypes.EXPORT_DATASET_SUCCESS,
+ payload: {
+ task,
+ exporter,
+ },
+ };
+
+ return action;
+}
+
+function exportDatasetFailed(task: any, exporter: any, error: any): AnyAction {
+ const action = {
+ type: TasksActionTypes.EXPORT_DATASET_FAILED,
+ payload: {
+ task,
+ exporter,
+ error,
+ },
+ };
+
+ return action;
+}
+
+export function exportDatasetAsync(task: any, exporter: any):
+ThunkAction, {}, {}, AnyAction> {
+ return async (dispatch: ActionCreator): Promise => {
+ dispatch(exportDataset(task, exporter));
+
+ try {
+ const url = await task.annotations.exportDataset(task.name, exporter);
+ // false positive
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
+ window.open(url, '_blank');
+ } catch (error) {
+ dispatch(exportDatasetFailed(task, exporter, error));
+ }
+
+ dispatch(exportDatasetSuccess(task, exporter));
+ };
+}
+
function deleteTask(taskID: number): AnyAction {
const action = {
type: TasksActionTypes.DELETE_TASK,
diff --git a/cvat-ui/src/actions/users-actions.ts b/cvat-ui/src/actions/users-actions.ts
index c64cf111ed81..1ea373052c83 100644
--- a/cvat-ui/src/actions/users-actions.ts
+++ b/cvat-ui/src/actions/users-actions.ts
@@ -6,20 +6,10 @@ import getCore from '../core';
const core = getCore();
export enum UsersActionTypes {
- GET_USERS = 'GET_USERS',
GET_USERS_SUCCESS = 'GET_USERS_SUCCESS',
GET_USERS_FAILED = 'GET_USERS_FAILED',
}
-function getUsers(): AnyAction {
- const action = {
- type: UsersActionTypes.GET_USERS,
- payload: { },
- };
-
- return action;
-}
-
function getUsersSuccess(users: any[]): AnyAction {
const action = {
type: UsersActionTypes.GET_USERS_SUCCESS,
@@ -42,7 +32,6 @@ export function getUsersAsync():
ThunkAction, {}, {}, AnyAction> {
return async (dispatch: ActionCreator): Promise => {
try {
- dispatch(getUsers());
const users = await core.users.get();
dispatch(
getUsersSuccess(
diff --git a/cvat-ui/src/components/actions-menu/actions-menu.tsx b/cvat-ui/src/components/actions-menu/actions-menu.tsx
index c782aaf22a65..9b0003569a6f 100644
--- a/cvat-ui/src/components/actions-menu/actions-menu.tsx
+++ b/cvat-ui/src/components/actions-menu/actions-menu.tsx
@@ -5,24 +5,27 @@ import {
Modal,
} from 'antd';
-import Text from 'antd/lib/typography/Text';
import { ClickParam } from 'antd/lib/menu/index';
import LoaderItemComponent from './loader-item';
import DumperItemComponent from './dumper-item';
+import ExportItemComponent from './export-item';
interface ActionsMenuComponentProps {
taskInstance: any;
loaders: any[];
dumpers: any[];
+ exporters: any[];
loadActivity: string | null;
dumpActivities: string[] | null;
+ exportActivities: string[] | null;
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
installedAutoAnnotation: boolean;
onLoadAnnotation: (taskInstance: any, loader: any, file: File) => void;
onDumpAnnotation: (taskInstance: any, dumper: any) => void;
+ onExportDataset: (taskInstance: any, exporter: any) => void;
onDeleteTask: (taskInstance: any) => void;
onOpenRunWindow: (taskInstance: any) => void;
}
@@ -66,24 +69,22 @@ export default function ActionsMenuComponent(props: ActionsMenuComponentProps) {
const tracker = props.taskInstance.bugTracker;
const renderModelRunner = props.installedAutoAnnotation ||
props.installedTFAnnotation || props.installedTFSegmentation;
+
return (
handleMenuClick(props, params)
}>
- {'Dump annotations'}
- }>
+
{
props.dumpers.map((dumper) => DumperItemComponent({
dumper,
taskInstance: props.taskInstance,
- dumpActivities: props.dumpActivities,
+ dumpActivity: (props.dumpActivities || [])
+ .filter((_dumper: string) => _dumper === dumper.name)[0] || null,
onDumpAnnotation: props.onDumpAnnotation,
} ))}
- {'Upload annotations'}
- }>
+
{
props.loaders.map((loader) => LoaderItemComponent({
loader,
@@ -93,6 +94,17 @@ export default function ActionsMenuComponent(props: ActionsMenuComponentProps) {
}))
}
+
+ {
+ props.exporters.map((exporter) => ExportItemComponent({
+ exporter,
+ taskInstance: props.taskInstance,
+ exportActivity: (props.exportActivities || [])
+ .filter((_exporter: string) => _exporter === exporter.name)[0] || null,
+ onExportDataset: props.onExportDataset,
+ }))
+ }
+
{tracker && Open bug tracker }
{renderModelRunner && Automatic annotation }
diff --git a/cvat-ui/src/components/actions-menu/dumper-item.tsx b/cvat-ui/src/components/actions-menu/dumper-item.tsx
index e9e9c36e4689..ca862a8ec327 100644
--- a/cvat-ui/src/components/actions-menu/dumper-item.tsx
+++ b/cvat-ui/src/components/actions-menu/dumper-item.tsx
@@ -11,7 +11,7 @@ import Text from 'antd/lib/typography/Text';
interface DumperItemComponentProps {
taskInstance: any;
dumper: any;
- dumpActivities: string[] | null;
+ dumpActivity: string | null;
onDumpAnnotation: (task: any, dumper: any) => void;
}
@@ -24,11 +24,7 @@ export default function DumperItemComponent(props: DumperItemComponentProps) {
const task = props.taskInstance;
const { mode } = task;
const { dumper } = props;
-
- const dumpingWithThisDumper = (props.dumpActivities || [])
- .filter((_dumper: string) => _dumper === dumper.name)[0];
-
- const pending = !!dumpingWithThisDumper;
+ const pending = !!props.dumpActivity;
return (
diff --git a/cvat-ui/src/components/actions-menu/export-item.tsx b/cvat-ui/src/components/actions-menu/export-item.tsx
new file mode 100644
index 000000000000..157fd495b227
--- /dev/null
+++ b/cvat-ui/src/components/actions-menu/export-item.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+
+import {
+ Menu,
+ Button,
+ Icon,
+} from 'antd';
+
+import Text from 'antd/lib/typography/Text';
+
+interface DumperItemComponentProps {
+ taskInstance: any;
+ exporter: any;
+ exportActivity: string | null;
+ onExportDataset: (task: any, exporter: any) => void;
+}
+
+export default function DumperItemComponent(props: DumperItemComponentProps) {
+ const task = props.taskInstance;
+ const { exporter } = props;
+ const pending = !!props.exportActivity;
+
+ return (
+
+ {
+ props.onExportDataset(task, exporter);
+ }}>
+
+
+ {exporter.name}
+
+ {pending && }
+
+
+ );
+}
+
diff --git a/cvat-ui/src/components/create-task-page/create-task-content.tsx b/cvat-ui/src/components/create-task-page/create-task-content.tsx
index c67452ac2c50..28d8f29c45c3 100644
--- a/cvat-ui/src/components/create-task-page/create-task-content.tsx
+++ b/cvat-ui/src/components/create-task-page/create-task-content.tsx
@@ -7,7 +7,7 @@ import {
Modal,
Button,
Collapse,
- message,
+ notification,
} from 'antd';
import Text from 'antd/lib/typography/Text';
@@ -28,7 +28,6 @@ export interface CreateTaskData {
interface Props {
onCreate: (data: CreateTaskData) => void;
status: string;
- error: string;
installedGit: boolean;
}
@@ -190,15 +189,10 @@ export default class CreateTaskContent extends React.PureComponent
}
public componentDidUpdate(prevProps: Props) {
- if (this.props.error && prevProps.error !== this.props.error) {
- Modal.error({
- title: 'Could not create task',
- content: this.props.error,
- });
- }
-
if (this.props.status === 'CREATED' && prevProps.status !== 'CREATED') {
- message.success('The task has been created');
+ notification.info({
+ message: 'The task has been created',
+ });
this.basicConfigurationComponent.resetFields();
if (this.advancedConfigurationComponent) {
@@ -216,7 +210,7 @@ export default class CreateTaskContent extends React.PureComponent
public render() {
const loading = !!this.props.status
&& this.props.status !== 'CREATED'
- && !this.props.error;
+ && this.props.status !== 'FAILED';
return (
diff --git a/cvat-ui/src/components/create-task-page/create-task-page.tsx b/cvat-ui/src/components/create-task-page/create-task-page.tsx
index d59d5f3615ab..2287ab5c51a2 100644
--- a/cvat-ui/src/components/create-task-page/create-task-page.tsx
+++ b/cvat-ui/src/components/create-task-page/create-task-page.tsx
@@ -11,7 +11,6 @@ import CreateTaskContent, { CreateTaskData } from './create-task-content';
interface Props {
onCreate: (data: CreateTaskData) => void;
- error: string;
status: string;
installedGit: boolean;
}
@@ -23,7 +22,6 @@ export default function CreateTaskPage(props: Props) {
Create a new task
diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx
index 6d7ad26f3f92..a1a8fcb9cf5e 100644
--- a/cvat-ui/src/components/cvat-app.tsx
+++ b/cvat-ui/src/components/cvat-app.tsx
@@ -1,12 +1,18 @@
import React from 'react';
-
-import { Switch, Route, Redirect } from 'react-router';
-import { BrowserRouter } from 'react-router-dom';
-
-import { Spin, Layout, Modal } from 'antd';
-
import 'antd/dist/antd.css';
import '../stylesheet.css';
+import { BrowserRouter } from 'react-router-dom';
+import {
+ Switch,
+ Route,
+ Redirect,
+} from 'react-router';
+import {
+ Spin,
+ Layout,
+ Icon,
+ notification,
+} from 'antd';
import TasksPageContainer from '../containers/tasks-page/tasks-page';
import CreateTaskPageContainer from '../containers/create-task-page/create-task-page';
@@ -20,22 +26,23 @@ import HeaderContainer from '../containers/header/header';
import ModelRunnerModalContainer from '../containers/model-runner-dialog/model-runner-dialog';
import FeedbackComponent from './feedback';
+import { NotificationsState } from '../reducers/interfaces';
type CVATAppProps = {
loadFormats: () => void;
loadUsers: () => void;
verifyAuthorized: () => void;
initPlugins: () => void;
+ resetErrors: () => void;
+ resetMessages: () => void;
pluginsInitialized: boolean;
userInitialized: boolean;
formatsInitialized: boolean;
usersInitialized: boolean;
- gettingAuthError: string;
- gettingFormatsError: string;
- gettingUsersError: string;
installedAutoAnnotation: boolean;
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
+ notifications: NotificationsState;
user: any;
}
@@ -44,54 +51,137 @@ export default class CVATApplication extends React.PureComponent {
super(props);
}
+ private showMessages() {
+ function showMessage(title: string) {
+ notification.info({
+ message: title,
+ duration: null,
+ });
+ }
+
+ const { tasks } = this.props.notifications.messages;
+ let shown = !!tasks.loading;
+
+ if (tasks.loading) {
+ showMessage(tasks.loading);
+ }
+
+ if (shown) {
+ this.props.resetMessages();
+ }
+ }
+
+ private showErrors() {
+ function showError(title: string, _error: any) {
+ const error = _error.toString();
+ notification.error({
+ message: title,
+ duration: null,
+ description: error.length > 200 ? '' : error,
+ });
+
+ console.error(error);
+ }
+
+ const { auth } = this.props.notifications.errors;
+ const { tasks } = this.props.notifications.errors;
+ const { formats } = this.props.notifications.errors;
+ const { users } = this.props.notifications.errors;
+ const { share } = this.props.notifications.errors;
+ const { models } = this.props.notifications.errors;
+
+ let shown = !!auth.authorized || !!auth.login || !!auth.logout || !!auth.register
+ || !!tasks.fetching || !!tasks.updating || !!tasks.dumping || !!tasks.loading
+ || !!tasks.exporting || !!tasks.deleting || !!tasks.creating || !!formats.fetching
+ || !!users.fetching || !!share.fetching || !!models.creating || !!models.starting
+ || !!models.fetching || !!models.deleting || !!models.inferenceStatusFetching;
+
+ if (auth.authorized) {
+ showError('Could not check authorization on the server', auth.authorized);
+ }
+ if (auth.login) {
+ showError('Could not login on the server', auth.login);
+ }
+ if (auth.register) {
+ showError('Could not register on the server', auth.register);
+ }
+ if (auth.logout) {
+ showError('Could not logout on the server', auth.logout);
+ }
+ if (tasks.fetching) {
+ showError('Could not fetch tasks from the server', tasks.fetching);
+ }
+ if (tasks.updating) {
+ showError('Could not update task on the server', tasks.updating);
+ }
+ if (tasks.dumping) {
+ showError('Could not dump annotations from the server', tasks.dumping);
+ }
+ if (tasks.loading) {
+ showError('Could not upload annotations to the server', tasks.loading);
+ }
+ if (tasks.exporting) {
+ showError('Could not export task from the server', tasks.exporting);
+ }
+ if (tasks.deleting) {
+ showError('Could not delete task on the server', tasks.deleting);
+ }
+ if (tasks.creating) {
+ showError('Could not create task on the server', tasks.creating);
+ }
+ if (formats.fetching) {
+ showError('Could not get annotations and dataset formats from the server', formats.fetching);
+ }
+ if (users.fetching) {
+ showError('Could not get users from the server', users.fetching);
+ }
+ if (share.fetching) {
+ showError('Could not get share info from the server', share.fetching);
+ }
+ if (models.creating) {
+ showError('Could not create model on the server', models.creating);
+ }
+ if (models.starting) {
+ showError('Could not run model on the server', models.starting);
+ }
+ if (models.fetching) {
+ showError('Could not get models from the server', models.fetching);
+ }
+ if (models.deleting) {
+ showError('Could not delete model from the server', models.deleting);
+ }
+ if (models.inferenceStatusFetching) {
+ showError('Could not fetch inference status from the server', models.inferenceStatusFetching);
+ }
+
+ if (shown) {
+ this.props.resetErrors();
+ }
+ }
+
public componentDidMount() {
this.props.verifyAuthorized();
}
public componentDidUpdate() {
- if (!this.props.userInitialized ||
- this.props.userInitialized && this.props.user == null) {
- return;
- }
+ this.showErrors();
+ this.showMessages();
- if (this.props.gettingAuthError) {
- Modal.error({
- title: 'Could not check authorization',
- content: `${this.props.gettingAuthError}`,
- });
+ if (!this.props.userInitialized || this.props.user == null) {
+ // not authorized user
return;
}
if (!this.props.formatsInitialized) {
this.props.loadFormats();
- return;
- }
-
- if (this.props.gettingFormatsError) {
- Modal.error({
- title: 'Could not receive annotations formats',
- content: `${this.props.gettingFormatsError}`,
- });
- return;
}
if (!this.props.usersInitialized) {
this.props.loadUsers();
- return;
- }
-
- if (this.props.gettingUsersError) {
- Modal.error({
- title: 'Could not receive users',
- content: `${this.props.gettingUsersError}`,
- });
-
- return;
}
if (!this.props.pluginsInitialized) {
this.props.initPlugins();
- return;
}
}
diff --git a/cvat-ui/src/components/header/header.tsx b/cvat-ui/src/components/header/header.tsx
index 5f04b5ab58e0..0efeb86b117f 100644
--- a/cvat-ui/src/components/header/header.tsx
+++ b/cvat-ui/src/components/header/header.tsx
@@ -8,7 +8,6 @@ import {
Icon,
Button,
Menu,
- Modal,
} from 'antd';
import Text from 'antd/lib/typography/Text';
@@ -24,89 +23,70 @@ interface HeaderContainerProps {
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
username: string;
- logoutError: string;
}
type Props = HeaderContainerProps & RouteComponentProps;
-class HeaderContainer extends React.PureComponent {
- private cvatLogo: React.FunctionComponent;
- private userLogo: React.FunctionComponent;
+const cvatLogo = () => ;
+const userLogo = () => ;
- public constructor(props: Props) {
- super(props);
- this.cvatLogo = () => ;
- this.userLogo = () => ;
- }
+function HeaderContainer(props: Props) {
+ const renderModels = props.installedAutoAnnotation
+ || props.installedTFAnnotation
+ || props.installedTFSegmentation;
+ return (
+
+
+
- public componentDidUpdate(prevProps: Props) {
- if (!prevProps.logoutError && this.props.logoutError) {
- Modal.error({
- title: 'Could not logout',
- content: `${this.props.logoutError}`,
- });
- }
- }
-
- public render() {
- const { props } = this;
- const renderModels = props.installedAutoAnnotation
- || props.installedTFAnnotation
- || props.installedTFSegmentation;
- return (
-
-
-
-
- props.history.push('/tasks')
- }> Tasks
- { renderModels ?
- props.history.push('/models')
- }> Models : null
- }
- { props.installedAnalytics ?
- {
- const serverHost = core.config.backendAPI.slice(0, -7);
- window.open(`${serverHost}/analytics/app/kibana`, '_blank');
- }
- }> Analytics : null
- }
-
-
+ props.history.push('/tasks')
+ }> Tasks
+ { renderModels ?
+ props.history.push('/models')
+ }> Models : null
+ }
+ { props.installedAnalytics ?
window.open('https://github.com/opencv/cvat', '_blank')
- }>
-
- GitHub
-
- {
- const serverHost = core.config.backendAPI.slice(0, -7);
- window.open(`${serverHost}/documentation/user_guide.html`, '_blank')
- }
- }> Help
-
- {
+ const serverHost = core.config.backendAPI.slice(0, -7);
+ window.open(`${serverHost}/analytics/app/kibana`, '_blank');
+ }
+ }> Analytics : null
+ }
+
+
+ window.open('https://github.com/opencv/cvat', '_blank')
+ }>
+
+ GitHub
+
+ {
+ const serverHost = core.config.backendAPI.slice(0, -7);
+ window.open(`${serverHost}/documentation/user_guide.html`, '_blank')
+ }
+ }> Help
+
+
+
-
-
-
- {props.username.length > 14 ? `${props.username.slice(0, 10)} ...` : props.username}
-
-
-
+
+ {props.username.length > 14 ? `${props.username.slice(0, 10)} ...` : props.username}
+
+
- }>
- Logout
-
-
-
-
- );
- }
+
+ }>
+ Logout
+
+
+
+
+ );
}
export default withRouter(HeaderContainer);
diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx
index 96d76da3fbf2..fb2befa0b2b2 100644
--- a/cvat-ui/src/components/login-page/login-page.tsx
+++ b/cvat-ui/src/components/login-page/login-page.tsx
@@ -8,13 +8,11 @@ import Text from 'antd/lib/typography/Text';
import {
Col,
Row,
- Modal,
} from 'antd';
import LoginForm, { LoginData } from './login-form';
interface LoginPageComponentProps {
- loginError: string;
onLogin: (username: string, password: string) => void;
}
@@ -27,13 +25,6 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps
xl: { span: 4 },
}
- if (props.loginError) {
- Modal.error({
- title: 'Could not login',
- content: props.loginError,
- });
- }
-
return (
diff --git a/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx b/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
index 42ea7cf93b21..7898b452d121 100644
--- a/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
+++ b/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
@@ -24,7 +24,6 @@ interface Props {
activeProcesses: StringObject;
visible: boolean;
taskInstance: any;
- startingError: string;
getModels(): void;
closeDialog(): void;
runInference(
@@ -286,13 +285,6 @@ export default class ModelRunnerModalComponent extends React.PureComponent model.name === this.state.selectedModel)[0];
diff --git a/cvat-ui/src/components/models-page/models-page.tsx b/cvat-ui/src/components/models-page/models-page.tsx
index fe6fb1c277d1..f6264176ac63 100644
--- a/cvat-ui/src/components/models-page/models-page.tsx
+++ b/cvat-ui/src/components/models-page/models-page.tsx
@@ -15,7 +15,6 @@ interface Props {
installedTFSegmentation: boolean;
installedTFAnnotation: boolean;
modelsAreBeingFetched: boolean;
- modelsFetchingError: any;
registeredUsers: any[];
models: Model[];
getModels(): void;
diff --git a/cvat-ui/src/components/register-page/register-page.tsx b/cvat-ui/src/components/register-page/register-page.tsx
index 5605789e4dd5..d38c023478c4 100644
--- a/cvat-ui/src/components/register-page/register-page.tsx
+++ b/cvat-ui/src/components/register-page/register-page.tsx
@@ -14,7 +14,6 @@ import {
import RegisterForm, { RegisterData } from '../../components/register-page/register-form';
interface RegisterPageComponentProps {
- registerError: string;
onRegister: (username: string, firstName: string,
lastName: string, email: string,
password1: string, password2: string) => void;
@@ -29,13 +28,6 @@ function RegisterPageComponent(props: RegisterPageComponentProps & RouteComponen
xl: { span: 4 },
}
- if (props.registerError) {
- Modal.error({
- title: 'Could not register',
- content: props.registerError,
- });
- }
-
return (
diff --git a/cvat-ui/src/components/task-page/task-page.tsx b/cvat-ui/src/components/task-page/task-page.tsx
index ada1fa5c0f67..aceb9512d00d 100644
--- a/cvat-ui/src/components/task-page/task-page.tsx
+++ b/cvat-ui/src/components/task-page/task-page.tsx
@@ -6,7 +6,6 @@ import {
Col,
Row,
Spin,
- Modal,
} from 'antd';
import TopBarComponent from './top-bar';
@@ -15,10 +14,7 @@ import JobListContainer from '../../containers/task-page/job-list';
import { Task } from '../../reducers/interfaces';
interface TaskPageComponentProps {
- task: Task;
- taskFetchingError: string;
- taskUpdatingError: string;
- taskDeletingError: string;
+ task: Task | undefined | null;
deleteActivity: boolean | null;
installedGit: boolean;
onFetchTask: (tid: number) => void;
@@ -31,51 +27,29 @@ class TaskPageComponent extends React.PureComponent {
if (this.props.deleteActivity) {
this.props.history.replace('/tasks');
}
-
- const { id } = this.props.match.params;
-
- if (this.props.taskFetchingError) {
- Modal.error({
- title: `Could not receive the task ${id}`,
- content: this.props.taskFetchingError,
- });
- }
-
- if (this.props.taskUpdatingError) {
- Modal.error({
- title: `Could not update the task ${id}`,
- content: this.props.taskUpdatingError,
- });
- }
-
- if (this.props.taskDeletingError) {
- Modal.error({
- title: `Could not delete the task ${id}`,
- content: this.props.taskDeletingError,
- });
- }
}
public render() {
const { id } = this.props.match.params;
- const fetchTask = !this.props.task && !this.props.taskFetchingError;
+ const fetchTask = !this.props.task && !(typeof(this.props.task) === 'undefined');
if (fetchTask) {
this.props.onFetchTask(+id);
return (
);
- } else if (this.props.taskFetchingError) {
+ } else if (typeof(this.props.task) === 'undefined') {
return (
)
} else {
+ const task = this.props.task as Task;
return (
-
-
-
+
+
+
);
diff --git a/cvat-ui/src/components/tasks-page/tasks-page.tsx b/cvat-ui/src/components/tasks-page/tasks-page.tsx
index 424620cab207..06f0f651ba9e 100644
--- a/cvat-ui/src/components/tasks-page/tasks-page.tsx
+++ b/cvat-ui/src/components/tasks-page/tasks-page.tsx
@@ -16,11 +16,6 @@ import EmptyListComponent from './empty-list';
import TaskListContainer from '../../containers/tasks-page/tasks-list';
interface TasksPageProps {
- deletingError: string;
- dumpingError: string;
- loadingError: string;
- tasksFetchingError: string;
- loadingDoneMessage: string;
tasksAreBeingFetched: boolean;
gettingQuery: TasksQuery;
numberOfTasks: number;
@@ -137,43 +132,6 @@ class TasksPageComponent extends React.PureComponent void;
onDumpAnnotation: (taskInstance: any, dumper: any) => void;
+ onExportDataset: (taskInstance: any, exporter: any) => void;
onDeleteTask: (taskInstance: any) => void;
onOpenRunWindow: (taskInstance: any) => void;
}
function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
const { formats } = state;
- const { dumps } = state.tasks.activities;
- const { loads } = state.tasks.activities;
+ const { activities } = state.tasks;
+ const { dumps } = activities;
+ const { loads } = activities;
+ const _exports = activities.exports;
const { plugins } = state.plugins;
const id = own.taskInstance.id;
@@ -43,10 +49,14 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
installedTFSegmentation: plugins.TF_SEGMENTATION,
installedAutoAnnotation: plugins.AUTO_ANNOTATION,
dumpActivities: dumps.byTask[id] ? dumps.byTask[id] : null,
+ exportActivities: _exports.byTask[id] ? _exports.byTask[id] : null,
loadActivity: loads.byTask[id] ? loads.byTask[id] : null,
- loaders: formats.loaders,
- dumpers: formats.dumpers,
- };
+ loaders: formats.annotationFormats
+ .map((format: any): any[] => format.loaders).flat(),
+ dumpers: formats.annotationFormats
+ .map((format: any): any[] => format.dumpers).flat(),
+ exporters: formats.datasetFormats,
+ };
}
function mapDispatchToProps(dispatch: any): DispatchToProps {
@@ -57,6 +67,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
onDumpAnnotation: (taskInstance: any, dumper: any) => {
dispatch(dumpAnnotationsAsync(taskInstance, dumper));
},
+ onExportDataset: (taskInstance: any, exporter: any) => {
+ dispatch(exportDatasetAsync(taskInstance, exporter));
+ },
onDeleteTask: (taskInstance: any) => {
dispatch(deleteTaskAsync(taskInstance));
},
@@ -72,13 +85,16 @@ function ActionsMenuContainer(props: OwnProps & StateToProps & DispatchToProps)
taskInstance={props.taskInstance}
loaders={props.loaders}
dumpers={props.dumpers}
+ exporters={props.exporters}
loadActivity={props.loadActivity}
dumpActivities={props.dumpActivities}
+ exportActivities={props.exportActivities}
installedTFAnnotation={props.installedTFAnnotation}
installedTFSegmentation={props.installedTFSegmentation}
installedAutoAnnotation={props.installedAutoAnnotation}
onLoadAnnotation={props.onLoadAnnotation}
onDumpAnnotation={props.onDumpAnnotation}
+ onExportDataset={props.onExportDataset}
onDeleteTask={props.onDeleteTask}
onOpenRunWindow={props.onOpenRunWindow}
/>
diff --git a/cvat-ui/src/containers/create-task-page/create-task-page.tsx b/cvat-ui/src/containers/create-task-page/create-task-page.tsx
index 2deb946116de..a7655dce34ea 100644
--- a/cvat-ui/src/containers/create-task-page/create-task-page.tsx
+++ b/cvat-ui/src/containers/create-task-page/create-task-page.tsx
@@ -7,7 +7,6 @@ import { CreateTaskData } from '../../components/create-task-page/create-task-co
import { createTaskAsync } from '../../actions/tasks-actions';
interface StateToProps {
- creatingError: string;
status: string;
installedGit: boolean;
}
@@ -27,14 +26,12 @@ function mapStateToProps(state: CombinedState): StateToProps {
return {
...creates,
installedGit: state.plugins.plugins.GIT_INTEGRATION,
- creatingError: creates.creatingError ? creates.creatingError.toString() : '',
};
}
function CreateTaskPageContainer(props: StateToProps & DispatchToProps) {
return (
);
}
diff --git a/cvat-ui/src/containers/login-page/login-page.tsx b/cvat-ui/src/containers/login-page/login-page.tsx
index c1c6e13ac0dc..76974d2afda7 100644
--- a/cvat-ui/src/containers/login-page/login-page.tsx
+++ b/cvat-ui/src/containers/login-page/login-page.tsx
@@ -1,21 +1,16 @@
import React from 'react';
import { connect } from 'react-redux';
import { loginAsync } from '../../actions/auth-actions';
-import { CombinedState } from '../../reducers/interfaces';
import LoginPageComponent from '../../components/login-page/login-page';
-interface StateToProps {
- loginError: any;
-}
+interface StateToProps {}
interface DispatchToProps {
login(username: string, password: string): void;
}
-function mapStateToProps(state: CombinedState): StateToProps {
- return {
- loginError: state.auth.loginError,
- };
+function mapStateToProps(): StateToProps {
+ return {};
}
function mapDispatchToProps(dispatch: any): DispatchToProps {
@@ -24,11 +19,10 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
};
}
-function LoginPageContainer(props: StateToProps & DispatchToProps) {
+function LoginPageContainer(props: DispatchToProps) {
return (
);
}
diff --git a/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx b/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx
index 8fa8ef0d4ee6..233f1b4ae72c 100644
--- a/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx
+++ b/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx
@@ -14,7 +14,6 @@ import {
interface StateToProps {
- startingError: any;
modelsInitialized: boolean;
models: Model[];
activeProcesses: {
@@ -46,7 +45,6 @@ function mapStateToProps(state: CombinedState): StateToProps {
activeProcesses: {},
taskInstance: models.activeRunTask,
visible: models.visibleRunWindows,
- startingError: models.startingError,
};
}
@@ -82,7 +80,6 @@ function ModelRunnerModalContainer(props: StateToProps & DispatchToProps) {
getModels={props.getModels}
closeDialog={props.closeDialog}
runInference={props.inferModelAsync}
- startingError={props.startingError ? props.startingError.toString() : ''}
/>
);
}
diff --git a/cvat-ui/src/containers/models-page/models-page.tsx b/cvat-ui/src/containers/models-page/models-page.tsx
index e10cff23ca27..65f54ab025ab 100644
--- a/cvat-ui/src/containers/models-page/models-page.tsx
+++ b/cvat-ui/src/containers/models-page/models-page.tsx
@@ -16,7 +16,6 @@ interface StateToProps {
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
modelsAreBeingFetched: boolean;
- modelsFetchingError: any;
models: Model[];
registeredUsers: any[];
}
@@ -35,7 +34,6 @@ function mapStateToProps(state: CombinedState): StateToProps {
installedTFAnnotation: plugins.TF_ANNOTATION,
installedTFSegmentation: plugins.TF_SEGMENTATION,
modelsAreBeingFetched: !models.initialized,
- modelsFetchingError: models.fetchingError,
models: models.models,
registeredUsers: state.users.users,
};
@@ -64,7 +62,6 @@ function ModelsPageContainer(props: DispatchToProps & StateToProps) {
installedTFSegmentation={props.installedTFSegmentation}
installedTFAnnotation={props.installedTFAnnotation}
modelsAreBeingFetched={props.modelsAreBeingFetched}
- modelsFetchingError={props.modelsFetchingError}
registeredUsers={props.registeredUsers}
models={props.models}
getModels={props.getModels}
diff --git a/cvat-ui/src/containers/register-page/register-page.tsx b/cvat-ui/src/containers/register-page/register-page.tsx
index bdd7f230ac3d..b4e8a3054e36 100644
--- a/cvat-ui/src/containers/register-page/register-page.tsx
+++ b/cvat-ui/src/containers/register-page/register-page.tsx
@@ -1,12 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { registerAsync } from '../../actions/auth-actions';
-import { CombinedState } from '../../reducers/interfaces';
import RegisterPageComponent from '../../components/register-page/register-page';
-interface StateToProps {
- registerError: any;
-}
+interface StateToProps {}
interface DispatchToProps {
register: (username: string, firstName: string,
@@ -14,10 +11,8 @@ interface DispatchToProps {
password1: string, password2: string) => void;
}
-function mapStateToProps(state: CombinedState): StateToProps {
- return {
- registerError: state.auth.registerError,
- };
+function mapStateToProps(): StateToProps {
+ return {};
}
function mapDispatchToProps(dispatch: any): DispatchToProps {
@@ -26,11 +21,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
}
}
-type RegisterPageContainerProps = StateToProps & DispatchToProps;
-function RegisterPageContainer(props: RegisterPageContainerProps) {
+function RegisterPageContainer(props: StateToProps & DispatchToProps) {
return (
);
diff --git a/cvat-ui/src/containers/task-page/task-page.tsx b/cvat-ui/src/containers/task-page/task-page.tsx
index bc29944ff4dc..5aafa643bdf3 100644
--- a/cvat-ui/src/containers/task-page/task-page.tsx
+++ b/cvat-ui/src/containers/task-page/task-page.tsx
@@ -14,10 +14,7 @@ import {
type Props = RouteComponentProps<{id: string}>;
interface StateToProps {
- task: Task;
- taskFetchingError: any;
- taskUpdatingError: any;
- taskDeletingError: any;
+ task: Task | undefined | null;
deleteActivity: boolean | null;
installedGit: boolean;
}
@@ -29,11 +26,15 @@ interface DispatchToProps {
function mapStateToProps(state: CombinedState, own: Props): StateToProps {
const { plugins } = state.plugins;
const { deletes } = state.tasks.activities;
- const taskDeletingError = deletes.deletingError;
const id = +own.match.params.id;
const filtered = state.tasks.current.filter((task) => task.instance.id === id);
- const task = filtered[0] || null;
+ let task = null;
+ if (filtered.length) {
+ task = filtered[0];
+ } else if (state.notifications.errors.tasks.fetching) {
+ task = undefined;
+ }
let deleteActivity = null;
if (task && id in deletes.byTask) {
@@ -42,9 +43,6 @@ function mapStateToProps(state: CombinedState, own: Props): StateToProps {
return {
task,
- taskFetchingError: state.tasks.tasksFetchingError,
- taskUpdatingError: state.tasks.taskUpdatingError,
- taskDeletingError,
deleteActivity,
installedGit: plugins.GIT_INTEGRATION,
};
@@ -71,9 +69,6 @@ function TaskPageContainer(props: StateToProps & DispatchToProps) {
return (
void;
loadUsers: () => void;
initPlugins: () => void;
+ resetErrors: () => void;
+ resetMessages: () => void;
}
function mapStateToProps(state: CombinedState): StateToProps {
@@ -49,12 +56,10 @@ function mapStateToProps(state: CombinedState): StateToProps {
userInitialized: auth.initialized,
usersInitialized: users.initialized,
formatsInitialized: formats.initialized,
- gettingAuthError: auth.authError,
- gettingUsersError: users.gettingUsersError,
- gettingFormatsError: formats.gettingFormatsError,
installedAutoAnnotation: plugins.plugins.AUTO_ANNOTATION,
installedTFSegmentation: plugins.plugins.TF_SEGMENTATION,
installedTFAnnotation: plugins.plugins.TF_ANNOTATION,
+ notifications: {...state.notifications},
user: auth.user,
};
}
@@ -65,6 +70,8 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
verifyAuthorized: (): void => dispatch(authorizedAsync()),
initPlugins: (): void => dispatch(checkPluginsAsync()),
loadUsers: (): void => dispatch(getUsersAsync()),
+ resetErrors: (): void => dispatch(resetErrors()),
+ resetMessages: (): void => dispatch(resetMessages()),
};
}
@@ -75,16 +82,16 @@ function reduxAppWrapper(props: StateToProps & DispatchToProps) {
loadFormats={props.loadFormats}
loadUsers={props.loadUsers}
verifyAuthorized={props.verifyAuthorized}
+ resetErrors={props.resetErrors}
+ resetMessages={props.resetMessages}
pluginsInitialized={props.pluginsInitialized}
userInitialized={props.userInitialized}
usersInitialized={props.usersInitialized}
formatsInitialized={props.formatsInitialized}
- gettingAuthError={props.gettingAuthError ? props.gettingAuthError.toString() : ''}
- gettingFormatsError={props.gettingFormatsError ? props.gettingFormatsError.toString() : ''}
- gettingUsersError={props.gettingUsersError ? props.gettingUsersError.toString() : ''}
installedAutoAnnotation={props.installedAutoAnnotation}
installedTFSegmentation={props.installedTFSegmentation}
installedTFAnnotation={props.installedTFAnnotation}
+ notifications={props.notifications}
user={props.user}
/>
)
diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts
index d4c9cc97d4de..b4cf915a1670 100644
--- a/cvat-ui/src/reducers/auth-reducer.ts
+++ b/cvat-ui/src/reducers/auth-reducer.ts
@@ -5,10 +5,6 @@ import { AuthState } from './interfaces';
const defaultState: AuthState = {
initialized: false,
- authError: null,
- loginError: null,
- logoutError: null,
- registerError: null,
user: null,
};
@@ -19,48 +15,21 @@ export default (state = defaultState, action: AnyAction): AuthState => {
...state,
initialized: true,
user: action.payload.user,
- authError: null,
- };
- case AuthActionTypes.AUTHORIZED_FAILED:
- return {
- ...state,
- initialized: true,
- authError: action.payload.error,
};
case AuthActionTypes.LOGIN_SUCCESS:
return {
...state,
user: action.payload.user,
- loginError: null,
- };
- case AuthActionTypes.LOGIN_FAILED:
- return {
- ...state,
- user: null,
- loginError: action.payload.error,
};
case AuthActionTypes.LOGOUT_SUCCESS:
return {
...state,
user: null,
- logoutError: null,
- };
- case AuthActionTypes.LOGOUT_FAILED:
- return {
- ...state,
- logoutError: action.payload.error,
};
case AuthActionTypes.REGISTER_SUCCESS:
return {
...state,
user: action.payload.user,
- registerError: null,
- };
- case AuthActionTypes.REGISTER_FAILED:
- return {
- ...state,
- user: null,
- registerError: action.payload.error,
};
default:
return state;
diff --git a/cvat-ui/src/reducers/formats-reducer.ts b/cvat-ui/src/reducers/formats-reducer.ts
index 2bdc2f9219e2..ac9d9b62c6f5 100644
--- a/cvat-ui/src/reducers/formats-reducer.ts
+++ b/cvat-ui/src/reducers/formats-reducer.ts
@@ -4,9 +4,8 @@ import { FormatsActionTypes } from '../actions/formats-actions';
import { FormatsState } from './interfaces';
const defaultState: FormatsState = {
- loaders: [],
- dumpers: [],
- gettingFormatsError: null,
+ annotationFormats: [],
+ datasetFormats: [],
initialized: false,
};
@@ -16,15 +15,13 @@ export default (state = defaultState, action: AnyAction): FormatsState => {
return {
...state,
initialized: true,
- gettingFormatsError: null,
- dumpers: action.payload.formats.map((format: any): any[] => format.dumpers).flat(),
- loaders: action.payload.formats.map((format: any): any[] => format.loaders).flat(),
+ annotationFormats: action.payload.annotationFormats,
+ datasetFormats: action.payload.datasetFormats,
};
case FormatsActionTypes.GETTING_FORMATS_FAILED:
return {
...state,
initialized: true,
- gettingFormatsError: action.payload.error,
};
default:
return state;
diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts
index bd51b400da67..52d5f469dcf1 100644
--- a/cvat-ui/src/reducers/interfaces.ts
+++ b/cvat-ui/src/reducers/interfaces.ts
@@ -1,9 +1,5 @@
export interface AuthState {
initialized: boolean;
- authError: any;
- loginError: any;
- logoutError: any;
- registerError: any;
user: any;
}
@@ -26,45 +22,43 @@ export interface Task {
export interface TasksState {
initialized: boolean;
- tasksFetchingError: any;
- taskUpdatingError: any;
gettingQuery: TasksQuery;
count: number;
current: Task[];
activities: {
dumps: {
- dumpingError: any;
byTask: {
// dumps in different formats at the same time
[tid: number]: string[]; // dumper names
};
};
+ exports: {
+ byTask: {
+ // exports in different formats at the same time
+ [tid: number]: string[]; // dumper names
+ };
+ };
loads: {
- loadingError: any;
- loadingDoneMessage: string;
byTask: {
// only one loading simultaneously
[tid: number]: string; // loader name
};
};
deletes: {
- deletingError: any;
byTask: {
[tid: number]: boolean; // deleted (deleting if in dictionary)
};
};
creates: {
- creatingError: any;
status: string;
};
};
}
export interface FormatsState {
- loaders: any[];
- dumpers: any[];
+ annotationFormats: any[];
+ datasetFormats: any[];
initialized: boolean;
- gettingFormatsError: any;
}
// eslint-disable-next-line import/prefer-default-export
@@ -86,7 +80,6 @@ export interface PluginsState {
export interface UsersState {
users: any[];
initialized: boolean;
- gettingUsersError: any;
}
export interface ShareFileInfo { // get this data from cvat-core
@@ -102,7 +95,6 @@ export interface ShareItem {
export interface ShareState {
root: ShareItem;
- error: any;
}
export interface Model {
@@ -126,12 +118,6 @@ export interface Running {
export interface ModelsState {
initialized: boolean;
creatingStatus: string;
- creatingError: any;
- startingError: any;
- fetchingError: any;
- deletingErrors: { // by id
- [index: number]: any;
- };
models: Model[];
runnings: Running[];
visibleRunWindows: boolean;
@@ -146,6 +132,47 @@ export interface ModelFiles {
json: string | File;
}
+export interface NotificationsState {
+ errors: {
+ auth: {
+ authorized: any;
+ login: any;
+ logout: any;
+ register: any;
+ };
+ tasks: {
+ fetching: any;
+ updating: any;
+ dumping: any;
+ loading: any;
+ exporting: any;
+ deleting: any;
+ creating: any;
+ };
+ formats: {
+ fetching: any;
+ };
+ users: {
+ fetching: any;
+ };
+ share: {
+ fetching: any;
+ };
+ models: {
+ creating: any;
+ starting: any;
+ fetching: any;
+ deleting: any;
+ inferenceStatusFetching: any;
+ };
+ };
+ messages: {
+ tasks: {
+ loading: string;
+ };
+ };
+}
+
export interface CombinedState {
auth: AuthState;
tasks: TasksState;
@@ -154,4 +181,5 @@ export interface CombinedState {
formats: FormatsState;
plugins: PluginsState;
models: ModelsState;
+ notifications: NotificationsState;
}
diff --git a/cvat-ui/src/reducers/models-reducer.ts b/cvat-ui/src/reducers/models-reducer.ts
index ec241ac530b8..199325cf5a4a 100644
--- a/cvat-ui/src/reducers/models-reducer.ts
+++ b/cvat-ui/src/reducers/models-reducer.ts
@@ -6,10 +6,6 @@ import { ModelsState } from './interfaces';
const defaultState: ModelsState = {
initialized: false,
creatingStatus: '',
- creatingError: null,
- startingError: null,
- fetchingError: null,
- deletingErrors: {},
models: [],
visibleRunWindows: false,
activeRunTask: null,
@@ -21,7 +17,6 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
case ModelsActionTypes.GET_MODELS: {
return {
...state,
- fetchingError: null,
initialized: false,
};
}
@@ -35,18 +30,9 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
case ModelsActionTypes.GET_MODELS_FAILED: {
return {
...state,
- fetchingError: action.payload.error,
initialized: true,
};
}
- case ModelsActionTypes.DELETE_MODEL: {
- const errors = { ...state.deletingErrors };
- delete errors[action.payload.id];
- return {
- ...state,
- deletingErrors: errors,
- };
- }
case ModelsActionTypes.DELETE_MODEL_SUCCESS: {
return {
...state,
@@ -55,18 +41,9 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
),
};
}
- case ModelsActionTypes.DELETE_MODEL_FAILED: {
- const errors = { ...state.deletingErrors };
- errors[action.payload.id] = action.payload.error;
- return {
- ...state,
- deletingErrors: errors,
- };
- }
case ModelsActionTypes.CREATE_MODEL: {
return {
...state,
- creatingError: null,
creatingStatus: '',
};
}
@@ -79,7 +56,6 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
case ModelsActionTypes.CREATE_MODEL_FAILED: {
return {
...state,
- creatingError: action.payload.error,
creatingStatus: '',
};
}
@@ -90,18 +66,6 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
creatingStatus: 'CREATED',
};
}
- case ModelsActionTypes.INFER_MODEL: {
- return {
- ...state,
- startingError: null,
- };
- }
- case ModelsActionTypes.INFER_MODEL_FAILED: {
- return {
- ...state,
- startingError: action.payload.error,
- };
- }
case ModelsActionTypes.SHOW_RUN_MODEL_DIALOG: {
return {
...state,
diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts
new file mode 100644
index 000000000000..cca896e8c890
--- /dev/null
+++ b/cvat-ui/src/reducers/notifications-reducer.ts
@@ -0,0 +1,319 @@
+import { AnyAction } from 'redux';
+
+import { AuthActionTypes } from '../actions/auth-actions';
+import { FormatsActionTypes } from '../actions/formats-actions';
+import { ModelsActionTypes } from '../actions/models-actions';
+import { ShareActionTypes } from '../actions/share-actions';
+import { TasksActionTypes } from '../actions/tasks-actions';
+import { UsersActionTypes } from '../actions/users-actions';
+import { NotificationsActionType } from '../actions/notification-actions';
+
+import { NotificationsState } from './interfaces';
+
+const defaultState: NotificationsState = {
+ errors: {
+ auth: {
+ authorized: null,
+ login: null,
+ logout: null,
+ register: null,
+ },
+ tasks: {
+ fetching: null,
+ updating: null,
+ dumping: null,
+ loading: null,
+ exporting: null,
+ deleting: null,
+ creating: null,
+ },
+ formats: {
+ fetching: null,
+ },
+ users: {
+ fetching: null,
+ },
+ share: {
+ fetching: null,
+ },
+ models: {
+ creating: null,
+ starting: null,
+ fetching: null,
+ deleting: null,
+ inferenceStatusFetching: null,
+ },
+ },
+ messages: {
+ tasks: {
+ loading: '',
+ },
+ },
+};
+
+export default function (state = defaultState, action: AnyAction): NotificationsState {
+ switch (action.type) {
+ case AuthActionTypes.AUTHORIZED_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ auth: {
+ ...state.errors.auth,
+ authorized: action.payload.error,
+ },
+ },
+ };
+ }
+ case AuthActionTypes.LOGIN_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ auth: {
+ ...state.errors.auth,
+ login: action.payload.error,
+ },
+ },
+ };
+ }
+ case AuthActionTypes.LOGOUT_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ auth: {
+ ...state.errors.auth,
+ logout: action.payload.error,
+ },
+ },
+ };
+ }
+ case AuthActionTypes.REGISTER_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ auth: {
+ ...state.errors.auth,
+ register: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.EXPORT_DATASET_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ exporting: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.GET_TASKS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ fetching: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.LOAD_ANNOTATIONS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ loading: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.LOAD_ANNOTATIONS_SUCCESS: {
+ const { task } = action.payload;
+ return {
+ ...state,
+ messages: {
+ ...state.messages,
+ tasks: {
+ ...state.messages.tasks,
+ loading: `Annotations have been loaded to the task ${task.id}`,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.UPDATE_TASK_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ updating: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.DUMP_ANNOTATIONS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ dumping: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.DELETE_TASK_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ deleting: action.payload.error,
+ },
+ },
+ };
+ }
+ case TasksActionTypes.CREATE_TASK_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ tasks: {
+ ...state.errors.tasks,
+ creating: action.payload.error,
+ },
+ },
+ };
+ }
+ case FormatsActionTypes.GETTING_FORMATS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ formats: {
+ ...state.errors.formats,
+ fetching: action.payload.error,
+ },
+ },
+ };
+ }
+ case UsersActionTypes.GET_USERS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ users: {
+ ...state.errors.users,
+ fetching: action.payload.error,
+ },
+ },
+ };
+ }
+ case ShareActionTypes.LOAD_SHARE_DATA_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ share: {
+ ...state.errors.share,
+ fetching: action.payload.error,
+ },
+ },
+ };
+ }
+ case ModelsActionTypes.CREATE_MODEL_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ models: {
+ ...state.errors.models,
+ creating: action.payload.error,
+ },
+ },
+ };
+ }
+ case ModelsActionTypes.DELETE_MODEL_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ models: {
+ ...state.errors.models,
+ deleting: action.payload.error,
+ },
+ },
+ };
+ }
+ case ModelsActionTypes.GET_INFERENCE_STATUS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ models: {
+ ...state.errors.models,
+ inferenceStatusFetching: action.payload.error,
+ },
+ },
+ };
+ }
+ case ModelsActionTypes.GET_MODELS_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ models: {
+ ...state.errors.models,
+ fetching: action.payload.error,
+ },
+ },
+ };
+ }
+ case ModelsActionTypes.INFER_MODEL_FAILED: {
+ return {
+ ...state,
+ errors: {
+ ...state.errors,
+ models: {
+ ...state.errors.models,
+ starting: action.payload.error,
+ },
+ },
+ };
+ }
+ case NotificationsActionType.RESET_ERRORS: {
+ return {
+ ...state,
+ errors: {
+ ...defaultState.errors,
+ },
+ };
+ }
+ case NotificationsActionType.RESET_MESSAGES: {
+ return {
+ ...state,
+ messages: {
+ ...defaultState.messages,
+ },
+ };
+ }
+ default: {
+ return {
+ ...state,
+ };
+ }
+ }
+}
diff --git a/cvat-ui/src/reducers/root-reducer.ts b/cvat-ui/src/reducers/root-reducer.ts
index d6073be0f752..52d0723b9212 100644
--- a/cvat-ui/src/reducers/root-reducer.ts
+++ b/cvat-ui/src/reducers/root-reducer.ts
@@ -6,6 +6,7 @@ import shareReducer from './share-reducer';
import formatsReducer from './formats-reducer';
import pluginsReducer from './plugins-reducer';
import modelsReducer from './models-reducer';
+import notificationsReducer from './notifications-reducer';
export default function createRootReducer(): Reducer {
return combineReducers({
@@ -16,5 +17,6 @@ export default function createRootReducer(): Reducer {
formats: formatsReducer,
plugins: pluginsReducer,
models: modelsReducer,
+ notifications: notificationsReducer,
});
}
diff --git a/cvat-ui/src/reducers/share-reducer.ts b/cvat-ui/src/reducers/share-reducer.ts
index 6ce035e18c55..b85ce5622d03 100644
--- a/cvat-ui/src/reducers/share-reducer.ts
+++ b/cvat-ui/src/reducers/share-reducer.ts
@@ -9,17 +9,10 @@ const defaultState: ShareState = {
type: 'DIR',
children: [],
},
- error: null,
};
export default function (state = defaultState, action: AnyAction): ShareState {
switch (action.type) {
- case ShareActionTypes.LOAD_SHARE_DATA: {
- return {
- ...state,
- error: null,
- };
- }
case ShareActionTypes.LOAD_SHARE_DATA_SUCCESS: {
const { values } = action.payload;
const { directory } = action.payload;
@@ -45,14 +38,6 @@ export default function (state = defaultState, action: AnyAction): ShareState {
...state,
};
}
- case ShareActionTypes.LOAD_SHARE_DATA_FAILED: {
- const { error } = action.payload;
-
- return {
- ...state,
- error,
- };
- }
default:
return {
...state,
diff --git a/cvat-ui/src/reducers/tasks-reducer.ts b/cvat-ui/src/reducers/tasks-reducer.ts
index ea27bfa32468..b7a6c22ed6f7 100644
--- a/cvat-ui/src/reducers/tasks-reducer.ts
+++ b/cvat-ui/src/reducers/tasks-reducer.ts
@@ -5,8 +5,6 @@ import { TasksState, Task } from './interfaces';
const defaultState: TasksState = {
initialized: false,
- tasksFetchingError: null,
- taskUpdatingError: null,
count: 0,
current: [],
gettingQuery: {
@@ -21,52 +19,24 @@ const defaultState: TasksState = {
},
activities: {
dumps: {
- dumpingError: null,
+ byTask: {},
+ },
+ exports: {
byTask: {},
},
loads: {
- loadingError: null,
- loadingDoneMessage: '',
byTask: {},
},
deletes: {
- deletingError: null,
byTask: {},
},
creates: {
- creatingError: null,
status: '',
},
},
};
-export default (inputState: TasksState = defaultState, action: AnyAction): TasksState => {
- function cleanupTemporaryInfo(stateToResetErrors: TasksState): TasksState {
- return {
- ...stateToResetErrors,
- tasksFetchingError: null,
- taskUpdatingError: null,
- activities: {
- ...stateToResetErrors.activities,
- dumps: {
- ...stateToResetErrors.activities.dumps,
- dumpingError: null,
- },
- loads: {
- ...stateToResetErrors.activities.loads,
- loadingError: null,
- loadingDoneMessage: '',
- },
- deletes: {
- ...stateToResetErrors.activities.deletes,
- deletingError: null,
- },
- },
- };
- }
-
- const state = cleanupTemporaryInfo(inputState);
-
+export default (state: TasksState = defaultState, action: AnyAction): TasksState => {
switch (action.type) {
case TasksActionTypes.GET_TASKS:
return {
@@ -74,7 +44,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
activities: {
...state.activities,
deletes: {
- deletingError: null,
byTask: {},
},
},
@@ -102,7 +71,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
count: 0,
current: [],
gettingQuery: { ...action.payload.query },
- tasksFetchingError: action.payload.error,
};
case TasksActionTypes.DUMP_ANNOTATIONS: {
const { task } = action.payload;
@@ -115,8 +83,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
const theTaskDumpingActivities = [...tasksDumpingActivities.byTask[task.id] || []];
if (!theTaskDumpingActivities.includes(dumper.name)) {
theTaskDumpingActivities.push(dumper.name);
- } else {
- throw Error('Dump with the same dumper for this same task has been already started');
}
tasksDumpingActivities.byTask[task.id] = theTaskDumpingActivities;
@@ -152,11 +118,9 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
case TasksActionTypes.DUMP_ANNOTATIONS_FAILED: {
const { task } = action.payload;
const { dumper } = action.payload;
- const dumpingError = action.payload.error;
const tasksDumpingActivities = {
...state.activities.dumps,
- dumpingError,
};
const theTaskDumpingActivities = tasksDumpingActivities.byTask[task.id]
@@ -172,6 +136,70 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
},
};
}
+ case TasksActionTypes.EXPORT_DATASET: {
+ const { task } = action.payload;
+ const { exporter } = action.payload;
+
+ const tasksExportingActivities = {
+ ...state.activities.exports,
+ };
+
+ const theTaskDumpingActivities = [...tasksExportingActivities.byTask[task.id] || []];
+ if (!theTaskDumpingActivities.includes(exporter.name)) {
+ theTaskDumpingActivities.push(exporter.name);
+ }
+ tasksExportingActivities.byTask[task.id] = theTaskDumpingActivities;
+
+ return {
+ ...state,
+ activities: {
+ ...state.activities,
+ exports: tasksExportingActivities,
+ },
+ };
+ }
+ case TasksActionTypes.EXPORT_DATASET_SUCCESS: {
+ const { task } = action.payload;
+ const { exporter } = action.payload;
+
+ const tasksExportingActivities = {
+ ...state.activities.exports,
+ };
+
+ const theTaskExportingActivities = tasksExportingActivities.byTask[task.id]
+ .filter((exporterName: string): boolean => exporterName !== exporter.name);
+
+ tasksExportingActivities.byTask[task.id] = theTaskExportingActivities;
+
+ return {
+ ...state,
+ activities: {
+ ...state.activities,
+ exports: tasksExportingActivities,
+ },
+ };
+ }
+ case TasksActionTypes.EXPORT_DATASET_FAILED: {
+ const { task } = action.payload;
+ const { exporter } = action.payload;
+
+ const tasksExportingActivities = {
+ ...state.activities.exports,
+ };
+
+ const theTaskExportingActivities = tasksExportingActivities.byTask[task.id]
+ .filter((exporterName: string): boolean => exporterName !== exporter.name);
+
+ tasksExportingActivities.byTask[task.id] = theTaskExportingActivities;
+
+ return {
+ ...state,
+ activities: {
+ ...state.activities,
+ exports: tasksExportingActivities,
+ },
+ };
+ }
case TasksActionTypes.LOAD_ANNOTATIONS: {
const { task } = action.payload;
const { loader } = action.payload;
@@ -209,14 +237,12 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
...state.activities,
loads: {
...tasksLoadingActivity,
- loadingDoneMessage: `Annotations have been loaded to the task ${task.id}`,
},
},
};
}
case TasksActionTypes.LOAD_ANNOTATIONS_FAILED: {
const { task } = action.payload;
- const loadingError = action.payload.error;
const tasksLoadingActivity = {
...state.activities.loads,
@@ -230,7 +256,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
...state.activities,
loads: {
...tasksLoadingActivity,
- loadingError,
},
},
};
@@ -273,7 +298,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
}
case TasksActionTypes.DELETE_TASK_FAILED: {
const { taskID } = action.payload;
- const { error } = action.payload;
const deletesActivities = state.activities.deletes;
@@ -288,7 +312,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
...state.activities,
deletes: {
...deletesActivities,
- deletingError: error,
},
},
};
@@ -299,7 +322,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
activities: {
...state.activities,
creates: {
- creatingError: null,
status: '',
},
},
@@ -332,15 +354,13 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
};
}
case TasksActionTypes.CREATE_TASK_FAILED: {
- const { error } = action.payload;
-
return {
...state,
activities: {
...state.activities,
creates: {
...state.activities.creates,
- creatingError: error,
+ status: 'FAILED',
},
},
};
@@ -348,7 +368,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
case TasksActionTypes.UPDATE_TASK: {
return {
...state,
- taskUpdatingError: null,
};
}
case TasksActionTypes.UPDATE_TASK_SUCCESS: {
@@ -369,7 +388,6 @@ export default (inputState: TasksState = defaultState, action: AnyAction): Tasks
case TasksActionTypes.UPDATE_TASK_FAILED: {
return {
...state,
- taskUpdatingError: action.payload.error,
current: state.current.map((task): Task => {
if (task.instance.id === action.payload.taskInstance.id) {
return {
diff --git a/cvat-ui/src/reducers/users-reducer.ts b/cvat-ui/src/reducers/users-reducer.ts
index cb821d1c8cac..f367b56ec65d 100644
--- a/cvat-ui/src/reducers/users-reducer.ts
+++ b/cvat-ui/src/reducers/users-reducer.ts
@@ -6,17 +6,10 @@ import { UsersActionTypes } from '../actions/users-actions';
const initialState: UsersState = {
users: [],
initialized: false,
- gettingUsersError: null,
};
export default function (state: UsersState = initialState, action: AnyAction): UsersState {
switch (action.type) {
- case UsersActionTypes.GET_USERS:
- return {
- ...state,
- initialized: false,
- gettingUsersError: null,
- };
case UsersActionTypes.GET_USERS_SUCCESS:
return {
...state,
@@ -28,7 +21,6 @@ export default function (state: UsersState = initialState, action: AnyAction): U
...state,
initialized: true,
users: [],
- gettingUsersError: action.payload.error,
};
default:
return {
diff --git a/cvat-ui/src/stylesheet.css b/cvat-ui/src/stylesheet.css
index 283aeae60b8d..1efae2742083 100644
--- a/cvat-ui/src/stylesheet.css
+++ b/cvat-ui/src/stylesheet.css
@@ -320,6 +320,14 @@
text-align: start;
}
+.cvat-actions-menu-export-submenu-item:hover {
+ background-color: rgba(24,144,255,0.05);
+}
+
+.cvat-actions-menu-export-submenu-item > button {
+ text-align: start;
+}
+
.cvat-actions-menu-dump-submenu-item:hover {
background-color: rgba(24,144,255,0.05);
}
From 2e5a41ebe9f161a4b700d3446de4633406fde31b Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Wed, 27 Nov 2019 20:14:47 +0300
Subject: [PATCH 10/15] Removed extra requests, improved UI
---
cvat-ui/src/actions/formats-actions.ts | 27 ++++++++++++-------
cvat-ui/src/actions/plugins-actions.ts | 11 ++++++++
cvat-ui/src/actions/users-actions.ts | 12 +++++++++
.../create-model-content.tsx | 23 ++++++----------
.../create-model-page/create-model-page.tsx | 2 --
.../create-task-page/create-task-content.tsx | 19 +++++++------
cvat-ui/src/components/cvat-app.tsx | 12 +++++----
.../labels-editor/labels-editor.tsx | 8 +++---
.../components/models-page/models-page.tsx | 5 ++--
cvat-ui/src/components/task-page/details.tsx | 13 ++++-----
.../src/components/task-page/task-page.tsx | 23 +++++++++++++---
.../create-model-page/create-model-page.tsx | 3 ---
.../containers/file-manager/file-manager.tsx | 2 +-
.../containers/models-page/models-page.tsx | 9 ++++---
.../src/containers/task-page/task-page.tsx | 12 ++++-----
cvat-ui/src/index.tsx | 17 +++++++++---
cvat-ui/src/reducers/formats-reducer.ts | 14 ++++++++--
cvat-ui/src/reducers/interfaces.ts | 5 ++++
cvat-ui/src/reducers/models-reducer.ts | 4 +++
cvat-ui/src/reducers/notifications-reducer.ts | 2 +-
cvat-ui/src/reducers/plugins-reducer.ts | 9 +++++++
cvat-ui/src/reducers/tasks-reducer.ts | 4 +++
cvat-ui/src/reducers/users-reducer.ts | 11 +++++++-
cvat-ui/src/stylesheet.css | 2 +-
24 files changed, 170 insertions(+), 79 deletions(-)
diff --git a/cvat-ui/src/actions/formats-actions.ts b/cvat-ui/src/actions/formats-actions.ts
index 1168b7d9ae67..a3e74685a9d8 100644
--- a/cvat-ui/src/actions/formats-actions.ts
+++ b/cvat-ui/src/actions/formats-actions.ts
@@ -6,16 +6,24 @@ import getCore from '../core';
const cvat = getCore();
export enum FormatsActionTypes {
- GETTING_FORMATS_SUCCESS = 'GETTING_FORMATS_SUCCESS',
- GETTING_FORMATS_FAILED = 'GETTING_FORMATS_FAILED',
+ GET_FORMATS = 'GET_FORMATS',
+ GET_FORMATS_SUCCESS = 'GET_FORMATS_SUCCESS',
+ GET_FORMATS_FAILED = 'GET_FORMATS_FAILED',
}
-export function gettingFormatsSuccess(
+function getFormats(): AnyAction {
+ return {
+ type: FormatsActionTypes.GET_FORMATS,
+ payload: {},
+ };
+}
+
+function getFormatsSuccess(
annotationFormats: any[],
datasetFormats: any[],
): AnyAction {
return {
- type: FormatsActionTypes.GETTING_FORMATS_SUCCESS,
+ type: FormatsActionTypes.GET_FORMATS_SUCCESS,
payload: {
annotationFormats,
datasetFormats,
@@ -23,27 +31,28 @@ export function gettingFormatsSuccess(
};
}
-export function gettingFormatsFailed(error: any): AnyAction {
+function getFormatsFailed(error: any): AnyAction {
return {
- type: FormatsActionTypes.GETTING_FORMATS_FAILED,
+ type: FormatsActionTypes.GET_FORMATS_FAILED,
payload: {
error,
},
};
}
-export function gettingFormatsAsync(): ThunkAction, {}, {}, AnyAction> {
+export function getFormatsAsync(): ThunkAction, {}, {}, AnyAction> {
return async (dispatch: ActionCreator): Promise => {
+ dispatch(getFormats());
let annotationFormats = null;
let datasetFormats = null;
try {
annotationFormats = await cvat.server.formats();
datasetFormats = await cvat.server.datasetFormats();
} catch (error) {
- dispatch(gettingFormatsFailed(error));
+ dispatch(getFormatsFailed(error));
return;
}
- dispatch(gettingFormatsSuccess(annotationFormats, datasetFormats));
+ dispatch(getFormatsSuccess(annotationFormats, datasetFormats));
};
}
diff --git a/cvat-ui/src/actions/plugins-actions.ts b/cvat-ui/src/actions/plugins-actions.ts
index 89c04d03afb7..0e27013fa4ff 100644
--- a/cvat-ui/src/actions/plugins-actions.ts
+++ b/cvat-ui/src/actions/plugins-actions.ts
@@ -4,6 +4,7 @@ import { SupportedPlugins } from '../reducers/interfaces';
import PluginChecker from '../utils/plugin-checker';
export enum PluginsActionTypes {
+ CHECK_PLUGINS = 'CHECK_PLUGINS',
CHECKED_ALL_PLUGINS = 'CHECKED_ALL_PLUGINS'
}
@@ -11,6 +12,15 @@ interface PluginObjects {
[plugin: string]: boolean;
}
+function checkPlugins(): AnyAction {
+ const action = {
+ type: PluginsActionTypes.CHECK_PLUGINS,
+ payload: {},
+ };
+
+ return action;
+}
+
function checkedAllPlugins(plugins: PluginObjects): AnyAction {
const action = {
type: PluginsActionTypes.CHECKED_ALL_PLUGINS,
@@ -25,6 +35,7 @@ function checkedAllPlugins(plugins: PluginObjects): AnyAction {
export function checkPluginsAsync():
ThunkAction, {}, {}, AnyAction> {
return async (dispatch: ActionCreator): Promise => {
+ dispatch(checkPlugins());
const plugins: PluginObjects = {};
const promises: Promise[] = [];
diff --git a/cvat-ui/src/actions/users-actions.ts b/cvat-ui/src/actions/users-actions.ts
index 1ea373052c83..3d75197a5c28 100644
--- a/cvat-ui/src/actions/users-actions.ts
+++ b/cvat-ui/src/actions/users-actions.ts
@@ -6,10 +6,20 @@ import getCore from '../core';
const core = getCore();
export enum UsersActionTypes {
+ GET_USERS = 'GET_USERS',
GET_USERS_SUCCESS = 'GET_USERS_SUCCESS',
GET_USERS_FAILED = 'GET_USERS_FAILED',
}
+function getUsers(): AnyAction {
+ const action = {
+ type: UsersActionTypes.GET_USERS,
+ payload: {},
+ };
+
+ return action;
+}
+
function getUsersSuccess(users: any[]): AnyAction {
const action = {
type: UsersActionTypes.GET_USERS_SUCCESS,
@@ -31,6 +41,8 @@ function getUsersFailed(error: any): AnyAction {
export function getUsersAsync():
ThunkAction, {}, {}, AnyAction> {
return async (dispatch: ActionCreator): Promise => {
+ dispatch(getUsers());
+
try {
const users = await core.users.get();
dispatch(
diff --git a/cvat-ui/src/components/create-model-page/create-model-content.tsx b/cvat-ui/src/components/create-model-page/create-model-content.tsx
index 60ab1dd90ac1..92719519c561 100644
--- a/cvat-ui/src/components/create-model-page/create-model-content.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-content.tsx
@@ -9,6 +9,7 @@ import {
Tooltip,
Modal,
message,
+ notification,
} from 'antd';
import Text from 'antd/lib/typography/Text';
@@ -24,7 +25,6 @@ import { ModelFiles } from '../../reducers/interfaces';
interface Props {
createModel(name: string, files: ModelFiles, global: boolean): void;
isAdmin: boolean;
- modelCreatingError: string;
modelCreatingStatus: string;
}
@@ -66,17 +66,17 @@ export default class CreateModelContent extends React.PureComponent {
if (Object.keys(grouppedFiles)
.map((key: string) => grouppedFiles[key])
.filter((val) => !!val).length !== 4) {
- Modal.error({
- title: 'Could not upload a model',
- content: 'Please, specify correct files',
+ notification.error({
+ message: 'Could not upload a model',
+ description: 'Please, specify correct files',
});
} else {
this.props.createModel(data.name, grouppedFiles, data.global);
}
}).catch(() => {
- Modal.error({
- title: 'Could not upload a model',
- content: 'Please, check input fields',
+ notification.error({
+ message: 'Could not upload a model',
+ description: 'Please, check input fields',
});
})
}
@@ -88,13 +88,6 @@ export default class CreateModelContent extends React.PureComponent {
this.modelForm.resetFields();
this.fileManagerContainer.reset();
}
-
- if (!prevProps.modelCreatingError && this.props.modelCreatingError) {
- Modal.error({
- title: 'Could not create task',
- content: this.props.modelCreatingError,
- });
- }
}
public render() {
@@ -128,7 +121,7 @@ export default class CreateModelContent extends React.PureComponent {
this.fileManagerContainer = container
- }/>
+ } withRemote={true}/>
{status && }
diff --git a/cvat-ui/src/components/create-model-page/create-model-page.tsx b/cvat-ui/src/components/create-model-page/create-model-page.tsx
index 205e7fe91055..e58a8ff1c928 100644
--- a/cvat-ui/src/components/create-model-page/create-model-page.tsx
+++ b/cvat-ui/src/components/create-model-page/create-model-page.tsx
@@ -13,7 +13,6 @@ import { ModelFiles } from '../../reducers/interfaces';
interface Props {
createModel(name: string, files: ModelFiles, global: boolean): void;
isAdmin: boolean;
- modelCreatingError: string;
modelCreatingStatus: string;
}
@@ -24,7 +23,6 @@ export default function CreateModelPageComponent(props: Props) {
Upload a new model
diff --git a/cvat-ui/src/components/create-task-page/create-task-content.tsx b/cvat-ui/src/components/create-task-page/create-task-content.tsx
index 28d8f29c45c3..57801a1b8ba8 100644
--- a/cvat-ui/src/components/create-task-page/create-task-content.tsx
+++ b/cvat-ui/src/components/create-task-page/create-task-content.tsx
@@ -4,7 +4,6 @@ import {
Row,
Col,
Alert,
- Modal,
Button,
Collapse,
notification,
@@ -89,17 +88,17 @@ export default class CreateTaskContent extends React.PureComponent
private handleSubmitClick = () => {
if (!this.validateLabels()) {
- Modal.error({
- title: 'Could not create a task',
- content: 'A task must contain at least one label',
+ notification.error({
+ message: 'Could not create a task',
+ description: 'A task must contain at least one label',
});
return;
}
if (!this.validateFiles()) {
- Modal.error({
- title: 'Could not create a task',
- content: 'A task must contain at least one file',
+ notification.error({
+ message: 'Could not create a task',
+ description: 'A task must contain at least one file',
});
return;
}
@@ -116,9 +115,9 @@ export default class CreateTaskContent extends React.PureComponent
this.props.onCreate(this.state);
})
.catch((_: any) => {
- Modal.error({
- title: 'Could not create a task',
- content: 'Please, check configuration you specified',
+ notification.error({
+ message: 'Could not create a task',
+ description: 'Please, check configuration you specified',
});
});
}
diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx
index a1a8fcb9cf5e..275eb69df515 100644
--- a/cvat-ui/src/components/cvat-app.tsx
+++ b/cvat-ui/src/components/cvat-app.tsx
@@ -10,7 +10,6 @@ import {
import {
Spin,
Layout,
- Icon,
notification,
} from 'antd';
@@ -35,10 +34,13 @@ type CVATAppProps = {
initPlugins: () => void;
resetErrors: () => void;
resetMessages: () => void;
- pluginsInitialized: boolean;
userInitialized: boolean;
+ pluginsInitialized: boolean;
+ pluginsFetching: boolean;
formatsInitialized: boolean;
+ formatsFetching: boolean;
usersInitialized: boolean;
+ usersFetching: boolean;
installedAutoAnnotation: boolean;
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
@@ -172,15 +174,15 @@ export default class CVATApplication extends React.PureComponent {
return;
}
- if (!this.props.formatsInitialized) {
+ if (!this.props.formatsInitialized && !this.props.formatsFetching) {
this.props.loadFormats();
}
- if (!this.props.usersInitialized) {
+ if (!this.props.usersInitialized && !this.props.usersFetching) {
this.props.loadUsers();
}
- if (!this.props.pluginsInitialized) {
+ if (!this.props.pluginsInitialized && !this.props.pluginsFetching) {
this.props.initPlugins();
}
}
diff --git a/cvat-ui/src/components/labels-editor/labels-editor.tsx b/cvat-ui/src/components/labels-editor/labels-editor.tsx
index 6c7f24a1878a..f49b2e2236d0 100644
--- a/cvat-ui/src/components/labels-editor/labels-editor.tsx
+++ b/cvat-ui/src/components/labels-editor/labels-editor.tsx
@@ -3,7 +3,7 @@ import React from 'react';
import {
Tabs,
Icon,
- Modal,
+ notification,
} from 'antd';
import Text from 'antd/lib/typography/Text';
@@ -128,9 +128,9 @@ export default class LabelsEditor
private handleDelete = (label: Label) => {
// the label is saved on the server, cannot delete it
if (typeof(label.id) !== 'undefined' && label.id >= 0) {
- Modal.error({
- title: 'Could not delete the label',
- content: 'It has been already saved on the server',
+ notification.error({
+ message: 'Could not delete the label',
+ description: 'It has been already saved on the server',
});
}
diff --git a/cvat-ui/src/components/models-page/models-page.tsx b/cvat-ui/src/components/models-page/models-page.tsx
index f6264176ac63..8db88abedf09 100644
--- a/cvat-ui/src/components/models-page/models-page.tsx
+++ b/cvat-ui/src/components/models-page/models-page.tsx
@@ -14,7 +14,8 @@ interface Props {
installedAutoAnnotation: boolean;
installedTFSegmentation: boolean;
installedTFAnnotation: boolean;
- modelsAreBeingFetched: boolean;
+ modelsInitialized: boolean;
+ modelsFetching: boolean;
registeredUsers: any[];
models: Model[];
getModels(): void;
@@ -22,7 +23,7 @@ interface Props {
}
export default function ModelsPageComponent(props: Props) {
- if (props.modelsAreBeingFetched) {
+ if (!props.modelsInitialized && !props.modelsFetching) {
props.getModels();
return (
diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx
index 6d2b0b2e5de3..355f2e279df9 100644
--- a/cvat-ui/src/components/task-page/details.tsx
+++ b/cvat-ui/src/components/task-page/details.tsx
@@ -7,6 +7,7 @@ import {
Icon,
Modal,
Button,
+ notification,
} from 'antd';
import Text from 'antd/lib/typography/Text';
@@ -287,9 +288,9 @@ export default class DetailsComponent extends React.PureComponent
.then((data) => {
if (data !== null && this.mounted) {
if (data.status.error) {
- Modal.error({
- title: 'Could not receive repository status',
- content: data.status.error
+ notification.error({
+ message: 'Could not receive repository status',
+ description: data.status.error
});
} else {
this.setState({
@@ -303,9 +304,9 @@ export default class DetailsComponent extends React.PureComponent
}
}).catch((error) => {
if (this.mounted) {
- Modal.error({
- title: 'Could not receive repository status',
- content: error.toString(),
+ notification.error({
+ message: 'Could not receive repository status',
+ description: error.toString(),
});
}
});
diff --git a/cvat-ui/src/components/task-page/task-page.tsx b/cvat-ui/src/components/task-page/task-page.tsx
index aceb9512d00d..6e29066e5eb4 100644
--- a/cvat-ui/src/components/task-page/task-page.tsx
+++ b/cvat-ui/src/components/task-page/task-page.tsx
@@ -6,6 +6,7 @@ import {
Col,
Row,
Spin,
+ notification,
} from 'antd';
import TopBarComponent from './top-bar';
@@ -14,7 +15,8 @@ import JobListContainer from '../../containers/task-page/job-list';
import { Task } from '../../reducers/interfaces';
interface TaskPageComponentProps {
- task: Task | undefined | null;
+ task: Task | null;
+ fetching: boolean;
deleteActivity: boolean | null;
installedGit: boolean;
onFetchTask: (tid: number) => void;
@@ -23,18 +25,33 @@ interface TaskPageComponentProps {
type Props = TaskPageComponentProps & RouteComponentProps<{id: string}>;
class TaskPageComponent extends React.PureComponent {
+ private attempts: number = 0;
+
public componentDidUpdate() {
if (this.props.deleteActivity) {
this.props.history.replace('/tasks');
}
+
+ if (this.attempts > 1) {
+ notification.warning({
+ message: 'Something wrong with the task. It cannot be fetched from the server',
+ });
+ }
}
public render() {
const { id } = this.props.match.params;
- const fetchTask = !this.props.task && !(typeof(this.props.task) === 'undefined');
+ const fetchTask = !this.props.task;
if (fetchTask) {
- this.props.onFetchTask(+id);
+ if (!this.props.fetching) {
+ if (!this.attempts) {
+ this.attempts ++;
+ this.props.onFetchTask(+id);
+ } else {
+ this.attempts ++;
+ }
+ }
return (
);
diff --git a/cvat-ui/src/containers/create-model-page/create-model-page.tsx b/cvat-ui/src/containers/create-model-page/create-model-page.tsx
index 1e47db0d60ba..188589f47ea6 100644
--- a/cvat-ui/src/containers/create-model-page/create-model-page.tsx
+++ b/cvat-ui/src/containers/create-model-page/create-model-page.tsx
@@ -10,7 +10,6 @@ import {
interface StateToProps {
isAdmin: boolean;
- modelCreatingError: any;
modelCreatingStatus: string;
}
@@ -23,7 +22,6 @@ function mapStateToProps(state: CombinedState): StateToProps {
return {
isAdmin: state.auth.user.isAdmin,
- modelCreatingError: models.creatingError,
modelCreatingStatus: models.creatingStatus,
};
}
@@ -40,7 +38,6 @@ function CreateModelPageContainer(props: StateToProps & DispatchToProps) {
return (
diff --git a/cvat-ui/src/containers/file-manager/file-manager.tsx b/cvat-ui/src/containers/file-manager/file-manager.tsx
index 4eb3ab018f41..9cdbd9ed9fd5 100644
--- a/cvat-ui/src/containers/file-manager/file-manager.tsx
+++ b/cvat-ui/src/containers/file-manager/file-manager.tsx
@@ -11,8 +11,8 @@ import {
} from '../../reducers/interfaces';
interface OwnProps {
- withRemote: boolean;
ref: any;
+ withRemote: boolean;
}
interface StateToProps {
diff --git a/cvat-ui/src/containers/models-page/models-page.tsx b/cvat-ui/src/containers/models-page/models-page.tsx
index 65f54ab025ab..76c2e7f81b8a 100644
--- a/cvat-ui/src/containers/models-page/models-page.tsx
+++ b/cvat-ui/src/containers/models-page/models-page.tsx
@@ -15,7 +15,8 @@ interface StateToProps {
installedAutoAnnotation: boolean;
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
- modelsAreBeingFetched: boolean;
+ modelsInitialized: boolean;
+ modelsFetching: boolean;
models: Model[];
registeredUsers: any[];
}
@@ -33,7 +34,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
installedAutoAnnotation: plugins.AUTO_ANNOTATION,
installedTFAnnotation: plugins.TF_ANNOTATION,
installedTFSegmentation: plugins.TF_SEGMENTATION,
- modelsAreBeingFetched: !models.initialized,
+ modelsInitialized: models.initialized,
+ modelsFetching: models.fetching,
models: models.models,
registeredUsers: state.users.users,
};
@@ -61,7 +63,8 @@ function ModelsPageContainer(props: DispatchToProps & StateToProps) {
installedAutoAnnotation={props.installedAutoAnnotation}
installedTFSegmentation={props.installedTFSegmentation}
installedTFAnnotation={props.installedTFAnnotation}
- modelsAreBeingFetched={props.modelsAreBeingFetched}
+ modelsInitialized={props.modelsInitialized}
+ modelsFetching={props.modelsFetching}
registeredUsers={props.registeredUsers}
models={props.models}
getModels={props.getModels}
diff --git a/cvat-ui/src/containers/task-page/task-page.tsx b/cvat-ui/src/containers/task-page/task-page.tsx
index 5aafa643bdf3..844df0844ff8 100644
--- a/cvat-ui/src/containers/task-page/task-page.tsx
+++ b/cvat-ui/src/containers/task-page/task-page.tsx
@@ -14,7 +14,8 @@ import {
type Props = RouteComponentProps<{id: string}>;
interface StateToProps {
- task: Task | undefined | null;
+ task: Task | null;
+ fetching: boolean;
deleteActivity: boolean | null;
installedGit: boolean;
}
@@ -29,12 +30,7 @@ function mapStateToProps(state: CombinedState, own: Props): StateToProps {
const id = +own.match.params.id;
const filtered = state.tasks.current.filter((task) => task.instance.id === id);
- let task = null;
- if (filtered.length) {
- task = filtered[0];
- } else if (state.notifications.errors.tasks.fetching) {
- task = undefined;
- }
+ const task = filtered[0] || null;
let deleteActivity = null;
if (task && id in deletes.byTask) {
@@ -44,6 +40,7 @@ function mapStateToProps(state: CombinedState, own: Props): StateToProps {
return {
task,
deleteActivity,
+ fetching: state.tasks.fetching,
installedGit: plugins.GIT_INTEGRATION,
};
}
@@ -69,6 +66,7 @@ function TaskPageContainer(props: StateToProps & DispatchToProps) {
return (
dispatch(gettingFormatsAsync()),
+ loadFormats: (): void => dispatch(getFormatsAsync()),
verifyAuthorized: (): void => dispatch(authorizedAsync()),
initPlugins: (): void => dispatch(checkPluginsAsync()),
loadUsers: (): void => dispatch(getUsersAsync()),
@@ -84,10 +90,13 @@ function reduxAppWrapper(props: StateToProps & DispatchToProps) {
verifyAuthorized={props.verifyAuthorized}
resetErrors={props.resetErrors}
resetMessages={props.resetMessages}
- pluginsInitialized={props.pluginsInitialized}
userInitialized={props.userInitialized}
+ pluginsInitialized={props.pluginsInitialized}
+ pluginsFetching={props.pluginsFetching}
usersInitialized={props.usersInitialized}
+ usersFetching={props.usersFetching}
formatsInitialized={props.formatsInitialized}
+ formatsFetching={props.formatsFetching}
installedAutoAnnotation={props.installedAutoAnnotation}
installedTFSegmentation={props.installedTFSegmentation}
installedTFAnnotation={props.installedTFAnnotation}
diff --git a/cvat-ui/src/reducers/formats-reducer.ts b/cvat-ui/src/reducers/formats-reducer.ts
index ac9d9b62c6f5..8eedb90acb35 100644
--- a/cvat-ui/src/reducers/formats-reducer.ts
+++ b/cvat-ui/src/reducers/formats-reducer.ts
@@ -7,21 +7,31 @@ const defaultState: FormatsState = {
annotationFormats: [],
datasetFormats: [],
initialized: false,
+ fetching: false,
};
export default (state = defaultState, action: AnyAction): FormatsState => {
switch (action.type) {
- case FormatsActionTypes.GETTING_FORMATS_SUCCESS:
+ case FormatsActionTypes.GET_FORMATS: {
+ return {
+ ...state,
+ fetching: true,
+ initialized: false,
+ };
+ }
+ case FormatsActionTypes.GET_FORMATS_SUCCESS:
return {
...state,
initialized: true,
+ fetching: false,
annotationFormats: action.payload.annotationFormats,
datasetFormats: action.payload.datasetFormats,
};
- case FormatsActionTypes.GETTING_FORMATS_FAILED:
+ case FormatsActionTypes.GET_FORMATS_FAILED:
return {
...state,
initialized: true,
+ fetching: false,
};
default:
return state;
diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts
index 52d5f469dcf1..0675c482f60a 100644
--- a/cvat-ui/src/reducers/interfaces.ts
+++ b/cvat-ui/src/reducers/interfaces.ts
@@ -22,6 +22,7 @@ export interface Task {
export interface TasksState {
initialized: boolean;
+ fetching: boolean;
gettingQuery: TasksQuery;
count: number;
current: Task[];
@@ -58,6 +59,7 @@ export interface TasksState {
export interface FormatsState {
annotationFormats: any[];
datasetFormats: any[];
+ fetching: boolean;
initialized: boolean;
}
@@ -71,6 +73,7 @@ export enum SupportedPlugins {
}
export interface PluginsState {
+ fetching: boolean;
initialized: boolean;
plugins: {
[name in SupportedPlugins]: boolean;
@@ -79,6 +82,7 @@ export interface PluginsState {
export interface UsersState {
users: any[];
+ fetching: boolean;
initialized: boolean;
}
@@ -117,6 +121,7 @@ export interface Running {
export interface ModelsState {
initialized: boolean;
+ fetching: boolean;
creatingStatus: string;
models: Model[];
runnings: Running[];
diff --git a/cvat-ui/src/reducers/models-reducer.ts b/cvat-ui/src/reducers/models-reducer.ts
index 199325cf5a4a..565dc26b8f39 100644
--- a/cvat-ui/src/reducers/models-reducer.ts
+++ b/cvat-ui/src/reducers/models-reducer.ts
@@ -5,6 +5,7 @@ import { ModelsState } from './interfaces';
const defaultState: ModelsState = {
initialized: false,
+ fetching: false,
creatingStatus: '',
models: [],
visibleRunWindows: false,
@@ -18,6 +19,7 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
return {
...state,
initialized: false,
+ fetching: true,
};
}
case ModelsActionTypes.GET_MODELS_SUCCESS: {
@@ -25,12 +27,14 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
...state,
models: action.payload.models,
initialized: true,
+ fetching: false,
};
}
case ModelsActionTypes.GET_MODELS_FAILED: {
return {
...state,
initialized: true,
+ fetching: false,
};
}
case ModelsActionTypes.DELETE_MODEL_SUCCESS: {
diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts
index cca896e8c890..d006d7c89e65 100644
--- a/cvat-ui/src/reducers/notifications-reducer.ts
+++ b/cvat-ui/src/reducers/notifications-reducer.ts
@@ -198,7 +198,7 @@ export default function (state = defaultState, action: AnyAction): Notifications
},
};
}
- case FormatsActionTypes.GETTING_FORMATS_FAILED: {
+ case FormatsActionTypes.GET_FORMATS_FAILED: {
return {
...state,
errors: {
diff --git a/cvat-ui/src/reducers/plugins-reducer.ts b/cvat-ui/src/reducers/plugins-reducer.ts
index f594023c8131..27913b9629ef 100644
--- a/cvat-ui/src/reducers/plugins-reducer.ts
+++ b/cvat-ui/src/reducers/plugins-reducer.ts
@@ -8,6 +8,7 @@ import {
const defaultState: PluginsState = {
+ fetching: false,
initialized: false,
plugins: {
GIT_INTEGRATION: false,
@@ -19,6 +20,13 @@ const defaultState: PluginsState = {
};
export default function (state = defaultState, action: AnyAction): PluginsState {
switch (action.type) {
+ case PluginsActionTypes.CHECK_PLUGINS: {
+ return {
+ ...state,
+ initialized: false,
+ fetching: true,
+ };
+ }
case PluginsActionTypes.CHECKED_ALL_PLUGINS: {
const { plugins } = action.payload;
@@ -29,6 +37,7 @@ export default function (state = defaultState, action: AnyAction): PluginsState
return {
...state,
initialized: true,
+ fetching: false,
plugins,
};
}
diff --git a/cvat-ui/src/reducers/tasks-reducer.ts b/cvat-ui/src/reducers/tasks-reducer.ts
index b7a6c22ed6f7..4b1be0648728 100644
--- a/cvat-ui/src/reducers/tasks-reducer.ts
+++ b/cvat-ui/src/reducers/tasks-reducer.ts
@@ -5,6 +5,7 @@ import { TasksState, Task } from './interfaces';
const defaultState: TasksState = {
initialized: false,
+ fetching: false,
count: 0,
current: [],
gettingQuery: {
@@ -48,6 +49,7 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
},
},
initialized: false,
+ fetching: true,
};
case TasksActionTypes.GET_TASKS_SUCCESS: {
const combinedWithPreviews = action.payload.array
@@ -59,6 +61,7 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
return {
...state,
initialized: true,
+ fetching: false,
count: action.payload.count,
current: combinedWithPreviews,
gettingQuery: { ...action.payload.query },
@@ -68,6 +71,7 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
return {
...state,
initialized: true,
+ fetching: false,
count: 0,
current: [],
gettingQuery: { ...action.payload.query },
diff --git a/cvat-ui/src/reducers/users-reducer.ts b/cvat-ui/src/reducers/users-reducer.ts
index f367b56ec65d..f86755a19ea6 100644
--- a/cvat-ui/src/reducers/users-reducer.ts
+++ b/cvat-ui/src/reducers/users-reducer.ts
@@ -5,22 +5,31 @@ import { UsersActionTypes } from '../actions/users-actions';
const initialState: UsersState = {
users: [],
+ fetching: false,
initialized: false,
};
export default function (state: UsersState = initialState, action: AnyAction): UsersState {
switch (action.type) {
+ case UsersActionTypes.GET_USERS: {
+ return {
+ ...state,
+ fetching: true,
+ initialized: false,
+ };
+ }
case UsersActionTypes.GET_USERS_SUCCESS:
return {
...state,
+ fetching: false,
initialized: true,
users: action.payload.users,
};
case UsersActionTypes.GET_USERS_FAILED:
return {
...state,
+ fetching: false,
initialized: true,
- users: [],
};
default:
return {
diff --git a/cvat-ui/src/stylesheet.css b/cvat-ui/src/stylesheet.css
index 1efae2742083..1a2aef1828a4 100644
--- a/cvat-ui/src/stylesheet.css
+++ b/cvat-ui/src/stylesheet.css
@@ -771,7 +771,7 @@ textarea.ant-input.cvat-raw-labels-viewer {
margin-top: 10px;
}
-.cvat-create-model-content > div:nth-child(5) > button {
+.cvat-create-model-content > div:nth-child(6) > button {
margin-top: 10px;
float: right;
width: 120px;
From 56948deddf6bc18af1cc250946656cee9797f7c3 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Thu, 28 Nov 2019 08:47:45 +0300
Subject: [PATCH 11/15] Fixed a name of a format
---
cvat-ui/src/actions/tasks-actions.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts
index 911c8e1d0357..e6cda76fd0d5 100644
--- a/cvat-ui/src/actions/tasks-actions.ts
+++ b/cvat-ui/src/actions/tasks-actions.ts
@@ -259,7 +259,7 @@ ThunkAction, {}, {}, AnyAction> {
dispatch(exportDataset(task, exporter));
try {
- const url = await task.annotations.exportDataset(task.name, exporter);
+ const url = await task.annotations.exportDataset(exporter.tag);
// false positive
// eslint-disable-next-line security/detect-non-literal-fs-filename
window.open(url, '_blank');
From 3c3f30af32de7ca265c3a74263781806846289e5 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Thu, 28 Nov 2019 10:48:33 +0300
Subject: [PATCH 12/15] Light refactoring
---
cvat-ui/src/components/actions-menu/actions-menu.tsx | 2 +-
cvat-ui/src/components/tasks-page/tasks-page.tsx | 5 ++---
cvat-ui/src/containers/tasks-page/tasks-page.tsx | 6 +++---
3 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/cvat-ui/src/components/actions-menu/actions-menu.tsx b/cvat-ui/src/components/actions-menu/actions-menu.tsx
index 9b0003569a6f..53ee12e79020 100644
--- a/cvat-ui/src/components/actions-menu/actions-menu.tsx
+++ b/cvat-ui/src/components/actions-menu/actions-menu.tsx
@@ -94,7 +94,7 @@ export default function ActionsMenuComponent(props: ActionsMenuComponentProps) {
}))
}
-
+
{
props.exporters.map((exporter) => ExportItemComponent({
exporter,
diff --git a/cvat-ui/src/components/tasks-page/tasks-page.tsx b/cvat-ui/src/components/tasks-page/tasks-page.tsx
index 06f0f651ba9e..2b8da555cc01 100644
--- a/cvat-ui/src/components/tasks-page/tasks-page.tsx
+++ b/cvat-ui/src/components/tasks-page/tasks-page.tsx
@@ -4,7 +4,6 @@ import { withRouter } from 'react-router-dom';
import {
Spin,
- Modal,
} from 'antd';
import {
@@ -16,7 +15,7 @@ import EmptyListComponent from './empty-list';
import TaskListContainer from '../../containers/tasks-page/tasks-list';
interface TasksPageProps {
- tasksAreBeingFetched: boolean;
+ tasksFetching: boolean;
gettingQuery: TasksQuery;
numberOfTasks: number;
numberOfVisibleTasks: number;
@@ -133,7 +132,7 @@ class TasksPageComponent extends React.PureComponent
);
diff --git a/cvat-ui/src/containers/tasks-page/tasks-page.tsx b/cvat-ui/src/containers/tasks-page/tasks-page.tsx
index 5b65812b4058..1420ca58927c 100644
--- a/cvat-ui/src/containers/tasks-page/tasks-page.tsx
+++ b/cvat-ui/src/containers/tasks-page/tasks-page.tsx
@@ -11,7 +11,7 @@ import TasksPageComponent from '../../components/tasks-page/tasks-page';
import { getTasksAsync } from '../../actions/tasks-actions';
interface StateToProps {
- tasksAreBeingFetched: boolean;
+ tasksFetching: boolean;
gettingQuery: TasksQuery;
numberOfTasks: number;
numberOfVisibleTasks: number;
@@ -25,7 +25,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
const { tasks } = state;
return {
- tasksAreBeingFetched: !state.tasks.initialized,
+ tasksFetching: state.tasks.fetching,
gettingQuery: tasks.gettingQuery,
numberOfTasks: state.tasks.count,
numberOfVisibleTasks: state.tasks.current.length,
@@ -43,7 +43,7 @@ type TasksPageContainerProps = StateToProps & DispatchToProps;
function TasksPageContainer(props: TasksPageContainerProps) {
return (
Date: Thu, 28 Nov 2019 14:09:15 +0300
Subject: [PATCH 13/15] Show inference progress
---
cvat-ui/src/actions/models-actions.ts | 202 +++++++++++++++++-
cvat-ui/src/actions/tasks-actions.ts | 8 +
.../components/actions-menu/actions-menu.tsx | 7 +-
cvat-ui/src/components/cvat-app.tsx | 10 +-
cvat-ui/src/components/feedback.tsx | 2 +-
.../src/components/tasks-page/task-item.tsx | 29 ++-
.../containers/actions-menu/actions-menu.tsx | 9 +-
.../src/containers/tasks-page/task-item.tsx | 4 +
cvat-ui/src/reducers/interfaces.ts | 27 ++-
cvat-ui/src/reducers/models-reducer.ts | 24 ++-
cvat-ui/src/reducers/notifications-reducer.ts | 25 ++-
11 files changed, 327 insertions(+), 20 deletions(-)
diff --git a/cvat-ui/src/actions/models-actions.ts b/cvat-ui/src/actions/models-actions.ts
index d11549d89e7a..f2a53e30589d 100644
--- a/cvat-ui/src/actions/models-actions.ts
+++ b/cvat-ui/src/actions/models-actions.ts
@@ -3,7 +3,12 @@ import { ThunkAction } from 'redux-thunk';
import getCore from '../core';
import { getCVATStore } from '../store';
-import { Model, ModelFiles, CombinedState } from '../reducers/interfaces';
+import {
+ Model,
+ ModelFiles,
+ ActiveInference,
+ CombinedState,
+} from '../reducers/interfaces';
export enum ModelsActionTypes {
GET_MODELS = 'GET_MODELS',
@@ -324,6 +329,199 @@ ThunkAction, {}, {}, AnyAction> {
};
}
+
+function getInferenceStatusSuccess(
+ taskID: number,
+ activeInference: ActiveInference,
+): AnyAction {
+ const action = {
+ type: ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS,
+ payload: {
+ taskID,
+ activeInference,
+ },
+ };
+
+ return action;
+}
+
+function getInferenceStatusFailed(taskID: number, error: any): AnyAction {
+ const action = {
+ type: ModelsActionTypes.GET_INFERENCE_STATUS_FAILED,
+ payload: {
+ taskID,
+ error,
+ },
+ };
+
+ return action;
+}
+
+interface InferenceMeta {
+ active: boolean;
+ taskID: number;
+ requestID: string;
+}
+
+const timers: any = {};
+
+async function timeoutCallback(
+ url: string,
+ taskID: number,
+ dispatch: ActionCreator,
+): Promise {
+ try {
+ delete timers[taskID];
+
+ const response = await core.server.request(url, {
+ method: 'GET',
+ });
+
+ const activeInference: ActiveInference = {
+ status: response.status,
+ progress: +response.progress || 0,
+ error: response.error || response.stderr || '',
+ };
+
+
+ if (activeInference.status === 'unknown') {
+ dispatch(getInferenceStatusFailed(
+ taskID,
+ new Error(
+ `Inference status for the task ${taskID} is unknown.`,
+ ),
+ ));
+
+ return;
+ }
+
+ if (activeInference.status === 'failed') {
+ dispatch(getInferenceStatusFailed(
+ taskID,
+ new Error(
+ `Inference status for the task ${taskID} is failed. ${activeInference.error}`,
+ ),
+ ));
+
+ return;
+ }
+
+ if (activeInference.status !== 'finished') {
+ timers[taskID] = setTimeout(
+ timeoutCallback.bind(
+ null,
+ url,
+ taskID,
+ dispatch,
+ ), 3000,
+ );
+ }
+
+ dispatch(getInferenceStatusSuccess(taskID, activeInference));
+ } catch (error) {
+ dispatch(getInferenceStatusFailed(taskID, error));
+ }
+}
+
+function subscribe(
+ urlPath: string,
+ inferenceMeta: InferenceMeta,
+ dispatch: ActionCreator,
+): void {
+ if (!(inferenceMeta.taskID in timers)) {
+ const requestURL = `${baseURL}/${urlPath}/${inferenceMeta.requestID}`;
+ timers[inferenceMeta.taskID] = setTimeout(
+ timeoutCallback.bind(
+ null,
+ requestURL,
+ inferenceMeta.taskID,
+ dispatch,
+ ),
+ );
+ }
+}
+
+export function getInferenceStatusAsync(tasks: number[]):
+ThunkAction, {}, {}, AnyAction> {
+ return async (dispatch: ActionCreator): Promise => {
+ function parse(response: any): InferenceMeta[] {
+ return Object.keys(response).map((key: string): InferenceMeta => ({
+ taskID: +key,
+ requestID: response[key].rq_id || key,
+ active: typeof (response[key].active) === 'undefined' ? ['queued', 'started']
+ .includes(response[key].status.toLowerCase()) : response[key].active,
+ }));
+ }
+
+ const store = getCVATStore();
+ const state: CombinedState = store.getState();
+ const OpenVINO = state.plugins.plugins.AUTO_ANNOTATION;
+ const RCNN = state.plugins.plugins.TF_ANNOTATION;
+ const MaskRCNN = state.plugins.plugins.TF_SEGMENTATION;
+
+ try {
+ if (OpenVINO) {
+ const response = await core.server.request(
+ `${baseURL}/auto_annotation/meta/get`, {
+ method: 'POST',
+ data: JSON.stringify(tasks),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ },
+ );
+
+ parse(response.run)
+ .filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active)
+ .forEach((inferenceMeta: InferenceMeta): void => {
+ subscribe('auto_annotation/check', inferenceMeta, dispatch);
+ });
+ }
+
+ if (RCNN) {
+ const response = await core.server.request(
+ `${baseURL}/tensorflow/annotation/meta/get`, {
+ method: 'POST',
+ data: JSON.stringify(tasks),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ },
+ );
+
+ parse(response)
+ .filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active)
+ .forEach((inferenceMeta: InferenceMeta): void => {
+ subscribe('tensorflow/annotation/check/task', inferenceMeta, dispatch);
+ });
+ }
+
+ if (MaskRCNN) {
+ const response = await core.server.request(
+ `${baseURL}/tensorflow/annotation/meta/get`, {
+ method: 'POST',
+ data: JSON.stringify(tasks),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ },
+ );
+
+ parse(response)
+ .filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active)
+ .forEach((inferenceMeta: InferenceMeta): void => {
+ subscribe('tensorflow/segmentation/check/task', inferenceMeta, dispatch);
+ });
+ }
+ } catch (error) {
+ tasks.forEach((task: number): void => {
+ dispatch(getInferenceStatusFailed(task, error));
+ });
+ }
+ };
+}
+
+
function inferModel(): AnyAction {
const action = {
type: ModelsActionTypes.INFER_MODEL,
@@ -387,6 +585,8 @@ export function inferModelAsync(
},
);
}
+
+ dispatch(getInferenceStatusAsync([taskInstance.id]));
} catch (error) {
dispatch(inferModelFailed(error));
return;
diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts
index e6cda76fd0d5..8bb5c381708f 100644
--- a/cvat-ui/src/actions/tasks-actions.ts
+++ b/cvat-ui/src/actions/tasks-actions.ts
@@ -1,6 +1,7 @@
import { AnyAction, Dispatch, ActionCreator } from 'redux';
import { ThunkAction } from 'redux-thunk';
import { TasksQuery } from '../reducers/interfaces';
+import { getInferenceStatusAsync } from './models-actions';
import getCore from '../core';
@@ -93,6 +94,13 @@ ThunkAction, {}, {}, AnyAction> {
const previews = [];
const promises = array
.map((task): string => (task as any).frames.preview());
+ dispatch(
+ getInferenceStatusAsync(
+ array.map(
+ (task: any): number => task.id,
+ ),
+ ),
+ );
for (const promise of promises) {
try {
diff --git a/cvat-ui/src/components/actions-menu/actions-menu.tsx b/cvat-ui/src/components/actions-menu/actions-menu.tsx
index 53ee12e79020..f6057cb8574b 100644
--- a/cvat-ui/src/components/actions-menu/actions-menu.tsx
+++ b/cvat-ui/src/components/actions-menu/actions-menu.tsx
@@ -11,7 +11,6 @@ import LoaderItemComponent from './loader-item';
import DumperItemComponent from './dumper-item';
import ExportItemComponent from './export-item';
-
interface ActionsMenuComponentProps {
taskInstance: any;
loaders: any[];
@@ -23,6 +22,7 @@ interface ActionsMenuComponentProps {
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
installedAutoAnnotation: boolean;
+ inferenceIsActive: boolean;
onLoadAnnotation: (taskInstance: any, loader: any, file: File) => void;
onDumpAnnotation: (taskInstance: any, dumper: any) => void;
onExportDataset: (taskInstance: any, exporter: any) => void;
@@ -106,7 +106,10 @@ export default function ActionsMenuComponent(props: ActionsMenuComponentProps) {
}
{tracker && Open bug tracker }
- {renderModelRunner && Automatic annotation }
+ {
+ renderModelRunner &&
+ Automatic annotation
+ }
Delete
diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx
index 275eb69df515..ba9c40d7c494 100644
--- a/cvat-ui/src/components/cvat-app.tsx
+++ b/cvat-ui/src/components/cvat-app.tsx
@@ -62,10 +62,14 @@ export default class CVATApplication extends React.PureComponent {
}
const { tasks } = this.props.notifications.messages;
- let shown = !!tasks.loading;
+ const { models } = this.props.notifications.messages;
+ let shown = !!tasks.loadingDone || !!models.inferenceDone;
- if (tasks.loading) {
- showMessage(tasks.loading);
+ if (tasks.loadingDone) {
+ showMessage(tasks.loadingDone);
+ }
+ if (models.inferenceDone) {
+ showMessage(models.inferenceDone);
}
if (shown) {
diff --git a/cvat-ui/src/components/feedback.tsx b/cvat-ui/src/components/feedback.tsx
index bd00ea8155cc..e4bbf7e84af3 100644
--- a/cvat-ui/src/components/feedback.tsx
+++ b/cvat-ui/src/components/feedback.tsx
@@ -98,7 +98,7 @@ export default class Feedback extends React.PureComponent<{}, State> {
Help to make CVAT better
+ Help to make CVAT better
}
content={this.renderContent()}
visible={this.state.active}
diff --git a/cvat-ui/src/components/tasks-page/task-item.tsx b/cvat-ui/src/components/tasks-page/task-item.tsx
index 658a2a700a86..02dd8ba5a219 100644
--- a/cvat-ui/src/components/tasks-page/task-item.tsx
+++ b/cvat-ui/src/components/tasks-page/task-item.tsx
@@ -15,11 +15,13 @@ import {
import moment from 'moment';
import ActionsMenuContainer from '../../containers/actions-menu/actions-menu';
+import { ActiveInference } from '../../reducers/interfaces';
export interface TaskItemProps {
taskInstance: any;
previewImage: string;
deleted: boolean;
+ activeInference: ActiveInference | null;
}
class TaskItemComponent extends React.PureComponent {
@@ -94,7 +96,8 @@ class TaskItemComponent extends React.PureComponent
-
+
+
+ { this.props.activeInference ?
+ <>
+
+
+ Automatic annotation
+
+
+
+
+
+
+
+ > : null
+ }
)
}
diff --git a/cvat-ui/src/containers/actions-menu/actions-menu.tsx b/cvat-ui/src/containers/actions-menu/actions-menu.tsx
index a4eebbe7998b..1fab00afa0f3 100644
--- a/cvat-ui/src/containers/actions-menu/actions-menu.tsx
+++ b/cvat-ui/src/containers/actions-menu/actions-menu.tsx
@@ -2,7 +2,11 @@ import React from 'react';
import { connect } from 'react-redux';
import ActionsMenuComponent from '../../components/actions-menu/actions-menu';
-import { CombinedState } from '../../reducers/interfaces';
+import {
+ CombinedState,
+ ActiveInference,
+} from '../../reducers/interfaces';
+
import { showRunModelDialog } from '../../actions/models-actions';
import {
dumpAnnotationsAsync,
@@ -25,6 +29,7 @@ interface StateToProps {
installedTFAnnotation: boolean;
installedTFSegmentation: boolean;
installedAutoAnnotation: boolean;
+ inferenceIsActive: boolean;
};
interface DispatchToProps {
@@ -56,6 +61,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
dumpers: formats.annotationFormats
.map((format: any): any[] => format.dumpers).flat(),
exporters: formats.datasetFormats,
+ inferenceIsActive: id in state.models.inferences,
};
}
@@ -97,6 +103,7 @@ function ActionsMenuContainer(props: OwnProps & StateToProps & DispatchToProps)
onExportDataset={props.onExportDataset}
onDeleteTask={props.onDeleteTask}
onOpenRunWindow={props.onOpenRunWindow}
+ inferenceIsActive={props.inferenceIsActive}
/>
);
}
diff --git a/cvat-ui/src/containers/tasks-page/task-item.tsx b/cvat-ui/src/containers/tasks-page/task-item.tsx
index b01159750dbf..e13390d7e3f5 100644
--- a/cvat-ui/src/containers/tasks-page/task-item.tsx
+++ b/cvat-ui/src/containers/tasks-page/task-item.tsx
@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import {
TasksQuery,
CombinedState,
+ ActiveInference,
} from '../../reducers/interfaces';
import TaskItemComponent from '../../components/tasks-page/task-item'
@@ -16,6 +17,7 @@ interface StateToProps {
deleteActivity: boolean | null;
previewImage: string;
taskInstance: any;
+ activeInference: ActiveInference | null;
}
interface DispatchToProps {
@@ -36,6 +38,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
deleteActivity: deletes.byTask[id] ? deletes.byTask[id] : null,
previewImage: task.preview,
taskInstance: task.instance,
+ activeInference: state.models.inferences[id] || null,
};
}
@@ -55,6 +58,7 @@ function TaskItemContainer(props: TasksItemContainerProps) {
deleted={props.deleteActivity === true}
taskInstance={props.taskInstance}
previewImage={props.previewImage}
+ activeInference={props.activeInference}
/>
);
}
diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts
index 0675c482f60a..4f107fee8f1f 100644
--- a/cvat-ui/src/reducers/interfaces.ts
+++ b/cvat-ui/src/reducers/interfaces.ts
@@ -111,12 +111,18 @@ export interface Model {
labels: string[];
}
-export interface Running {
- [tid: string]: {
- status: string;
- processId: string;
- error: any;
- };
+export enum RQStatus {
+ unknown = 'unknown',
+ queued = 'queued',
+ started = 'started',
+ finished = 'finished',
+ failed = 'failed',
+}
+
+export interface ActiveInference {
+ status: RQStatus;
+ progress: number;
+ error: string;
}
export interface ModelsState {
@@ -124,7 +130,9 @@ export interface ModelsState {
fetching: boolean;
creatingStatus: string;
models: Model[];
- runnings: Running[];
+ inferences: {
+ [index: number]: ActiveInference;
+ };
visibleRunWindows: boolean;
activeRunTask: any;
}
@@ -173,7 +181,10 @@ export interface NotificationsState {
};
messages: {
tasks: {
- loading: string;
+ loadingDone: string;
+ };
+ models: {
+ inferenceDone: string;
};
};
}
diff --git a/cvat-ui/src/reducers/models-reducer.ts b/cvat-ui/src/reducers/models-reducer.ts
index 565dc26b8f39..8372c01f9643 100644
--- a/cvat-ui/src/reducers/models-reducer.ts
+++ b/cvat-ui/src/reducers/models-reducer.ts
@@ -10,7 +10,7 @@ const defaultState: ModelsState = {
models: [],
visibleRunWindows: false,
activeRunTask: null,
- runnings: [],
+ inferences: {},
};
export default function (state = defaultState, action: AnyAction): ModelsState {
@@ -84,6 +84,28 @@ export default function (state = defaultState, action: AnyAction): ModelsState {
activeRunTask: null,
};
}
+ case ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS: {
+ const inferences = { ...state.inferences };
+ if (action.payload.activeInference.status === 'finished') {
+ delete inferences[action.payload.taskID];
+ } else {
+ inferences[action.payload.taskID] = action.payload.activeInference;
+ }
+
+ return {
+ ...state,
+ inferences,
+ };
+ }
+ case ModelsActionTypes.GET_INFERENCE_STATUS_FAILED: {
+ const inferences = { ...state.inferences };
+ delete inferences[action.payload.taskID];
+
+ return {
+ ...state,
+ inferences,
+ };
+ }
default: {
return {
...state,
diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts
index d006d7c89e65..c4291e71cee7 100644
--- a/cvat-ui/src/reducers/notifications-reducer.ts
+++ b/cvat-ui/src/reducers/notifications-reducer.ts
@@ -46,7 +46,10 @@ const defaultState: NotificationsState = {
},
messages: {
tasks: {
- loading: '',
+ loadingDone: '',
+ },
+ models: {
+ inferenceDone: '',
},
},
};
@@ -145,7 +148,7 @@ export default function (state = defaultState, action: AnyAction): Notifications
...state.messages,
tasks: {
...state.messages.tasks,
- loading: `Annotations have been loaded to the task ${task.id}`,
+ loadingDone: `Annotations have been loaded to the task ${task.id}`,
},
},
};
@@ -258,6 +261,24 @@ export default function (state = defaultState, action: AnyAction): Notifications
},
};
}
+ case ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS: {
+ if (action.payload.activeInference.status === 'finished') {
+ return {
+ ...state,
+ messages: {
+ ...state.messages,
+ models: {
+ ...state.messages.models,
+ inferenceDone: `Automatic annotation finished for the task ${action.payload.taskID}`,
+ },
+ },
+ };
+ }
+
+ return {
+ ...state,
+ };
+ }
case ModelsActionTypes.GET_INFERENCE_STATUS_FAILED: {
return {
...state,
From 0caf044aea176b132f69f4b0502a2a44e3af9c7d Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Thu, 28 Nov 2019 14:29:56 +0300
Subject: [PATCH 14/15] Fixed link
---
cvat-ui/src/actions/models-actions.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cvat-ui/src/actions/models-actions.ts b/cvat-ui/src/actions/models-actions.ts
index f2a53e30589d..c2745195f42d 100644
--- a/cvat-ui/src/actions/models-actions.ts
+++ b/cvat-ui/src/actions/models-actions.ts
@@ -498,7 +498,7 @@ ThunkAction, {}, {}, AnyAction> {
if (MaskRCNN) {
const response = await core.server.request(
- `${baseURL}/tensorflow/annotation/meta/get`, {
+ `${baseURL}/tensorflow/segmentation/meta/get`, {
method: 'POST',
data: JSON.stringify(tasks),
headers: {
From a0a7be12a284d2cd4b79728ce595c80212078517 Mon Sep 17 00:00:00 2001
From: Boris Sekachev
Date: Thu, 28 Nov 2019 14:44:39 +0300
Subject: [PATCH 15/15] Fixed model loading after a model was uploaded
---
.../model-runner-modal/model-runner-modal.tsx | 11 +++++------
.../model-runner-dialog/model-runner-dialog.tsx | 3 +++
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx b/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
index 7898b452d121..5b347713af42 100644
--- a/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
+++ b/cvat-ui/src/components/model-runner-modal/model-runner-modal.tsx
@@ -19,6 +19,7 @@ interface StringObject {
}
interface Props {
+ modelsFetching: boolean;
modelsInitialized: boolean;
models: Model[];
activeProcesses: StringObject;
@@ -273,6 +274,10 @@ export default class ModelRunnerModalComponent extends React.PureComponent model.name === this.state.selectedModel
diff --git a/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx b/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx
index 233f1b4ae72c..290c4df86e36 100644
--- a/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx
+++ b/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx
@@ -14,6 +14,7 @@ import {
interface StateToProps {
+ modelsFetching: boolean;
modelsInitialized: boolean;
models: Model[];
activeProcesses: {
@@ -40,6 +41,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
const { models } = state;
return {
+ modelsFetching: models.fetching,
modelsInitialized: models.initialized,
models: models.models,
activeProcesses: {},
@@ -72,6 +74,7 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
function ModelRunnerModalContainer(props: StateToProps & DispatchToProps) {
return (