You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
918 lines
30 KiB
TypeScript
918 lines
30 KiB
TypeScript
// Copyright (C) 2020-2022 Intel Corporation
|
|
// Copyright (C) 2022 CVAT.ai Corporation
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import React, { RefObject } from 'react';
|
|
import { RouteComponentProps } from 'react-router';
|
|
import { withRouter } from 'react-router-dom';
|
|
import { Row, Col } from 'antd/lib/grid';
|
|
import Button from 'antd/lib/button';
|
|
import Collapse from 'antd/lib/collapse';
|
|
import notification from 'antd/lib/notification';
|
|
import Text from 'antd/lib/typography/Text';
|
|
import Alert from 'antd/lib/alert';
|
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
import { ValidateErrorEntity } from 'rc-field-form/lib/interface';
|
|
import { StorageLocation } from 'reducers';
|
|
import { getCore, Storage } from 'cvat-core-wrapper';
|
|
import ConnectedFileManager from 'containers/file-manager/file-manager';
|
|
import LabelsEditor from 'components/labels-editor/labels-editor';
|
|
import { Files } from 'components/file-manager/file-manager';
|
|
|
|
import {
|
|
getFileContentType,
|
|
getContentTypeRemoteFile,
|
|
getFileNameFromPath,
|
|
} from 'utils/files';
|
|
|
|
import BasicConfigurationForm, { BaseConfiguration } from './basic-configuration-form';
|
|
import ProjectSearchField from './project-search-field';
|
|
import ProjectSubsetField from './project-subset-field';
|
|
import MultiTasksProgress from './multi-task-progress';
|
|
import AdvancedConfigurationForm, { AdvancedConfiguration, SortingMethod } from './advanced-configuration-form';
|
|
|
|
type TabName = 'local' | 'share' | 'remote' | 'cloudStorage';
|
|
const core = getCore();
|
|
|
|
export interface CreateTaskData {
|
|
projectId: number | null;
|
|
basic: BaseConfiguration;
|
|
subset: string;
|
|
advanced: AdvancedConfiguration;
|
|
labels: any[];
|
|
files: Files;
|
|
activeFileManagerTab: TabName;
|
|
cloudStorageId: number | null;
|
|
}
|
|
|
|
interface Props {
|
|
onCreate: (data: CreateTaskData, onProgress?: (status: string, progress?: number) => void) => Promise<any>;
|
|
projectId: number | null;
|
|
installedGit: boolean;
|
|
dumpers:[];
|
|
many: boolean;
|
|
}
|
|
|
|
type State = CreateTaskData & {
|
|
multiTasks: (CreateTaskData & {
|
|
status: 'pending' | 'progress' | 'failed' | 'completed' | 'cancelled';
|
|
})[];
|
|
uploadFileErrorMessage: string;
|
|
loading: boolean;
|
|
statusInProgressTask: string;
|
|
};
|
|
|
|
const defaultState: State = {
|
|
projectId: null,
|
|
basic: {
|
|
name: '',
|
|
},
|
|
subset: '',
|
|
advanced: {
|
|
lfs: false,
|
|
useZipChunks: true,
|
|
useCache: true,
|
|
sortingMethod: SortingMethod.LEXICOGRAPHICAL,
|
|
sourceStorage: {
|
|
location: StorageLocation.LOCAL,
|
|
cloudStorageId: undefined,
|
|
},
|
|
targetStorage: {
|
|
location: StorageLocation.LOCAL,
|
|
cloudStorageId: undefined,
|
|
},
|
|
useProjectSourceStorage: true,
|
|
useProjectTargetStorage: true,
|
|
},
|
|
labels: [],
|
|
files: {
|
|
local: [],
|
|
share: [],
|
|
remote: [],
|
|
cloudStorage: [],
|
|
},
|
|
activeFileManagerTab: 'local',
|
|
cloudStorageId: null,
|
|
multiTasks: [],
|
|
uploadFileErrorMessage: '',
|
|
loading: false,
|
|
statusInProgressTask: '',
|
|
};
|
|
|
|
const UploadFileErrorMessages = {
|
|
one: 'It can not be processed. You can upload an archive with images, a video or multiple images',
|
|
multi: 'It can not be processed. You can upload one or more videos',
|
|
};
|
|
|
|
class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps, State> {
|
|
private basicConfigurationComponent: RefObject<BasicConfigurationForm>;
|
|
private advancedConfigurationComponent: RefObject<AdvancedConfigurationForm>;
|
|
private fileManagerContainer: any;
|
|
|
|
public constructor(props: Props & RouteComponentProps) {
|
|
super(props);
|
|
this.state = { ...defaultState };
|
|
this.basicConfigurationComponent = React.createRef<BasicConfigurationForm>();
|
|
this.advancedConfigurationComponent = React.createRef<AdvancedConfigurationForm>();
|
|
}
|
|
|
|
public componentDidMount(): void {
|
|
const { projectId } = this.props;
|
|
|
|
if (projectId) {
|
|
this.handleProjectIdChange(projectId);
|
|
}
|
|
|
|
this.focusToForm();
|
|
}
|
|
|
|
private handleChangeStorageLocation(field: 'sourceStorage' | 'targetStorage', value: StorageLocation): void {
|
|
this.setState((state) => ({
|
|
advanced: {
|
|
...state.advanced,
|
|
[field]: {
|
|
location: value,
|
|
},
|
|
},
|
|
}));
|
|
}
|
|
|
|
private resetState = (): void => {
|
|
this.basicConfigurationComponent.current?.resetFields();
|
|
this.advancedConfigurationComponent.current?.resetFields();
|
|
|
|
this.fileManagerContainer.reset();
|
|
|
|
this.setState((state) => ({
|
|
...defaultState,
|
|
projectId: state.projectId,
|
|
}));
|
|
};
|
|
|
|
private validateLabelsOrProject = (): boolean => {
|
|
const { projectId, labels } = this.state;
|
|
return !!labels.length || !!projectId;
|
|
};
|
|
|
|
private validateFiles = (): boolean => {
|
|
const { activeFileManagerTab, files } = this.state;
|
|
|
|
if (activeFileManagerTab === 'cloudStorage') {
|
|
this.setState({
|
|
cloudStorageId: this.fileManagerContainer.getCloudStorageId(),
|
|
});
|
|
}
|
|
const totalLen = Object.keys(files).reduce((acc, key: string) => acc + files[(key as TabName)].length, 0);
|
|
|
|
return !!totalLen;
|
|
};
|
|
|
|
private startLoading = (): void => {
|
|
this.setState({
|
|
loading: true,
|
|
});
|
|
};
|
|
|
|
private stopLoading = (): void => {
|
|
this.setState({
|
|
loading: false,
|
|
});
|
|
};
|
|
|
|
private changeStatusInProgressTask = (status: string): void => {
|
|
this.setState({
|
|
statusInProgressTask: status,
|
|
});
|
|
};
|
|
|
|
private handleProjectIdChange = (value: null | number): void => {
|
|
const { projectId, subset } = this.state;
|
|
|
|
this.setState((state) => ({
|
|
projectId: value,
|
|
subset: value && value === projectId ? subset : '',
|
|
labels: value ? [] : state.labels,
|
|
}));
|
|
};
|
|
|
|
private handleChangeBasicConfiguration = (values: BaseConfiguration): void => {
|
|
this.setState({
|
|
basic: { ...values },
|
|
});
|
|
};
|
|
|
|
private handleSubmitAdvancedConfiguration = (values: AdvancedConfiguration): void => {
|
|
this.setState({
|
|
advanced: { ...values },
|
|
});
|
|
};
|
|
|
|
private handleTaskSubsetChange = (value: string): void => {
|
|
this.setState({
|
|
subset: value,
|
|
});
|
|
};
|
|
|
|
private changeFileManagerTab = (value: TabName): void => {
|
|
this.setState({
|
|
activeFileManagerTab: value,
|
|
});
|
|
};
|
|
|
|
private handleUseProjectSourceStorageChange = (value: boolean): void => {
|
|
this.setState((state) => ({
|
|
advanced: {
|
|
...state.advanced,
|
|
useProjectSourceStorage: value,
|
|
},
|
|
}));
|
|
};
|
|
|
|
private handleUseProjectTargetStorageChange = (value: boolean): void => {
|
|
this.setState((state) => ({
|
|
advanced: {
|
|
...state.advanced,
|
|
useProjectTargetStorage: value,
|
|
},
|
|
}));
|
|
};
|
|
|
|
private focusToForm = (): void => {
|
|
this.basicConfigurationComponent.current?.focus();
|
|
};
|
|
|
|
private handleUploadLocalFiles = (uploadedFiles: File[]): void => {
|
|
const { many } = this.props;
|
|
const { files } = this.state;
|
|
|
|
let uploadFileErrorMessage = '';
|
|
|
|
if (!many && uploadedFiles.length > 1) {
|
|
uploadFileErrorMessage = uploadedFiles.every((it) => (getFileContentType(it) === 'image' || it.name === 'manifest.jsonl')) ? '' : UploadFileErrorMessages.one;
|
|
} else if (many) {
|
|
uploadFileErrorMessage = uploadedFiles.every((it) => getFileContentType(it) === 'video') ? '' : UploadFileErrorMessages.multi;
|
|
}
|
|
|
|
this.setState({
|
|
uploadFileErrorMessage,
|
|
});
|
|
|
|
if (!uploadFileErrorMessage) {
|
|
this.setState({
|
|
files: {
|
|
...files,
|
|
local: uploadedFiles,
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
private handleUploadRemoteFiles = async (urls: string[]): Promise<void> => {
|
|
const { many } = this.props;
|
|
|
|
const { files } = this.state;
|
|
const { length } = urls;
|
|
|
|
let uploadFileErrorMessage = '';
|
|
|
|
try {
|
|
if (!many && length > 1) {
|
|
let index = 0;
|
|
while (index < length) {
|
|
const isImageFile = await getContentTypeRemoteFile(urls[index]) === 'image';
|
|
if (!isImageFile) {
|
|
uploadFileErrorMessage = UploadFileErrorMessages.one;
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
} else if (many) {
|
|
let index = 0;
|
|
while (index < length) {
|
|
const isVideoFile = await getContentTypeRemoteFile(urls[index]) === 'video';
|
|
if (!isVideoFile) {
|
|
uploadFileErrorMessage = UploadFileErrorMessages.multi;
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
uploadFileErrorMessage = `We can't process it. ${err}`;
|
|
}
|
|
|
|
this.setState({
|
|
uploadFileErrorMessage,
|
|
});
|
|
|
|
if (!uploadFileErrorMessage) {
|
|
this.setState({
|
|
files: {
|
|
...files,
|
|
remote: urls,
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
private handleUploadShareFiles = (shareFiles: {
|
|
key: string;
|
|
type: string;
|
|
mime_type: string;
|
|
}[]): void => {
|
|
const { many } = this.props;
|
|
const { files } = this.state;
|
|
|
|
let uploadFileErrorMessage = '';
|
|
|
|
if (!many && shareFiles.length > 1) {
|
|
uploadFileErrorMessage = shareFiles.every((it) => it.mime_type === 'image') ?
|
|
'' : UploadFileErrorMessages.one;
|
|
} else if (many) {
|
|
uploadFileErrorMessage = shareFiles.every((it) => it.mime_type === 'video') ?
|
|
'' : UploadFileErrorMessages.multi;
|
|
}
|
|
|
|
this.setState({
|
|
uploadFileErrorMessage,
|
|
});
|
|
|
|
if (!uploadFileErrorMessage) {
|
|
this.setState({
|
|
files: {
|
|
...files,
|
|
share: shareFiles.map((it) => it.key),
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
private handleUploadCloudStorageFiles = (cloudStorageFiles: string[]): void => {
|
|
const { files } = this.state;
|
|
|
|
this.setState({
|
|
files: {
|
|
...files,
|
|
cloudStorage: cloudStorageFiles,
|
|
},
|
|
});
|
|
};
|
|
|
|
private validateBlocks = (): Promise<any> => new Promise((resolve, reject) => {
|
|
const { projectId } = this.state;
|
|
if (!this.validateLabelsOrProject()) {
|
|
notification.error({
|
|
message: 'Could not create a task',
|
|
description: 'A task must contain at least one label or belong to some project',
|
|
className: 'cvat-notification-create-task-fail',
|
|
});
|
|
reject();
|
|
return;
|
|
}
|
|
|
|
if (!this.validateFiles()) {
|
|
notification.error({
|
|
message: 'Could not create a task',
|
|
description: 'A task must contain at least one file',
|
|
className: 'cvat-notification-create-task-fail',
|
|
});
|
|
reject();
|
|
return;
|
|
}
|
|
|
|
if (!this.basicConfigurationComponent.current) {
|
|
reject();
|
|
return;
|
|
}
|
|
|
|
this.basicConfigurationComponent.current
|
|
.submit()
|
|
.then(() => {
|
|
if (this.advancedConfigurationComponent.current) {
|
|
return this.advancedConfigurationComponent.current.submit();
|
|
}
|
|
if (projectId) {
|
|
return core.projects.get({ id: projectId })
|
|
.then((response: any) => {
|
|
const [project] = response;
|
|
const { advanced } = this.state;
|
|
this.handleSubmitAdvancedConfiguration({
|
|
...advanced,
|
|
sourceStorage: new Storage(
|
|
project.sourceStorage || { location: StorageLocation.LOCAL },
|
|
),
|
|
targetStorage: new Storage(
|
|
project.targetStorage || { location: StorageLocation.LOCAL },
|
|
),
|
|
});
|
|
return Promise.resolve();
|
|
})
|
|
.catch((error: Error): void => {
|
|
throw new Error(`Couldn't fetch the project ${projectId} ${error.toString()}`);
|
|
});
|
|
}
|
|
return Promise.resolve();
|
|
})
|
|
.then(resolve)
|
|
.catch((error: Error | ValidateErrorEntity): void => {
|
|
notification.error({
|
|
message: 'Could not create a task',
|
|
description: (error as ValidateErrorEntity).errorFields ?
|
|
(error as ValidateErrorEntity).errorFields
|
|
.map((field) => `${field.name} : ${field.errors.join(';')}`)
|
|
.map((text: string): JSX.Element => <div>{text}</div>) :
|
|
error.toString(),
|
|
className: 'cvat-notification-create-task-fail',
|
|
});
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
private handleSubmitAndOpen = (): void => {
|
|
const { history } = this.props;
|
|
|
|
this.validateBlocks()
|
|
.then(this.createOneTask)
|
|
.then((createdTask) => {
|
|
const { id } = createdTask;
|
|
history.push(`/tasks/${id}`);
|
|
})
|
|
.catch(() => {});
|
|
};
|
|
|
|
private handleSubmitAndContinue = (): void => {
|
|
this.validateBlocks()
|
|
.then(this.createOneTask)
|
|
.then(() => {
|
|
notification.info({
|
|
message: 'The task has been created',
|
|
className: 'cvat-notification-create-task-success',
|
|
});
|
|
})
|
|
.then(this.resetState)
|
|
.then(this.focusToForm)
|
|
.catch(() => {});
|
|
};
|
|
|
|
private createOneTask = (): Promise<any> => {
|
|
const { onCreate } = this.props;
|
|
this.startLoading();
|
|
return onCreate(this.state, this.changeStatusInProgressTask)
|
|
.finally(this.stopLoading);
|
|
};
|
|
|
|
private setStatusOneOfMultiTasks = async (index: number, status: string): Promise<void> => {
|
|
const { multiTasks } = this.state;
|
|
const resultTask = {
|
|
...multiTasks[index],
|
|
status,
|
|
};
|
|
|
|
return new Promise((resolve) => {
|
|
const newMultiTasks: any = [
|
|
...multiTasks.slice(0, index),
|
|
resultTask,
|
|
...multiTasks.slice(index + 1),
|
|
];
|
|
this.setState({
|
|
multiTasks: newMultiTasks,
|
|
}, resolve);
|
|
});
|
|
};
|
|
|
|
private createOneOfMultiTasks = async (index: any): Promise<void> => {
|
|
const { onCreate } = this.props;
|
|
const { multiTasks } = this.state;
|
|
const task = multiTasks[index];
|
|
|
|
if (task.status !== 'pending') return;
|
|
|
|
await this.setStatusOneOfMultiTasks(index, 'progress');
|
|
try {
|
|
await onCreate(task);
|
|
await this.setStatusOneOfMultiTasks(index, 'completed');
|
|
} catch (err) {
|
|
console.warn(err);
|
|
await this.setStatusOneOfMultiTasks(index, 'failed');
|
|
}
|
|
};
|
|
|
|
private createMultiTasks = async (): Promise<any> => {
|
|
const { multiTasks } = this.state;
|
|
this.startLoading();
|
|
const { length } = multiTasks;
|
|
let index = 0;
|
|
const queueSize = 1;
|
|
const promises = Array(queueSize)
|
|
.fill(undefined)
|
|
.map(async (): Promise<void> => {
|
|
// eslint-disable-next-line no-constant-condition
|
|
while (true) {
|
|
index++; // preliminary increase is needed to avoid using the same index when queueSize > 1
|
|
if (index > length) break;
|
|
await this.createOneOfMultiTasks(index - 1);
|
|
}
|
|
});
|
|
await Promise.allSettled(promises);
|
|
this.stopLoading();
|
|
};
|
|
|
|
private addMultiTasks = async (): Promise<void> => new Promise((resolve) => {
|
|
const {
|
|
projectId,
|
|
subset,
|
|
advanced,
|
|
labels,
|
|
files: allFiles,
|
|
activeFileManagerTab,
|
|
cloudStorageId,
|
|
} = this.state;
|
|
|
|
const files: (File | string)[] = allFiles[activeFileManagerTab];
|
|
|
|
this.setState({
|
|
multiTasks: files.map((file, index) => ({
|
|
projectId,
|
|
basic: {
|
|
name: this.getTaskName(index, activeFileManagerTab),
|
|
},
|
|
subset,
|
|
advanced,
|
|
labels,
|
|
files: {
|
|
...defaultState.files,
|
|
[activeFileManagerTab]: [file],
|
|
},
|
|
activeFileManagerTab,
|
|
cloudStorageId,
|
|
status: 'pending',
|
|
}
|
|
)),
|
|
}, resolve);
|
|
});
|
|
|
|
private handleSubmitMutliTasks = (): void => {
|
|
this.validateBlocks()
|
|
.then(() => {
|
|
this.addMultiTasks();
|
|
})
|
|
.then(this.createMultiTasks)
|
|
.then(() => {
|
|
const { multiTasks } = this.state;
|
|
const countCompleted = multiTasks.filter((item) => item.status === 'completed').length;
|
|
const countFailed = multiTasks.filter((item) => item.status === 'failed').length;
|
|
const countCancelled = multiTasks.filter((item) => item.status === 'cancelled').length;
|
|
const countAll = multiTasks.length;
|
|
|
|
notification.info({
|
|
message: 'The tasks have been created',
|
|
description:
|
|
`Completed: ${countCompleted}, failed: ${countFailed},${countCancelled ?
|
|
` cancelled: ${countCancelled},` :
|
|
''} total: ${countAll}, `,
|
|
className: 'cvat-notification-create-task-success',
|
|
});
|
|
});
|
|
};
|
|
|
|
private handleCancelMultiTasks = (): void => {
|
|
const { multiTasks } = this.state;
|
|
let count = 0;
|
|
const newMultiTasks: any = multiTasks.map((it) => {
|
|
if (it.status === 'pending') {
|
|
count++;
|
|
return {
|
|
...it,
|
|
status: 'cancelled',
|
|
};
|
|
}
|
|
return it;
|
|
});
|
|
this.setState({
|
|
multiTasks: newMultiTasks,
|
|
}, () => {
|
|
notification.info({
|
|
message: `Creation of ${count} tasks have been canceled`,
|
|
className: 'cvat-notification-create-task-success',
|
|
});
|
|
});
|
|
};
|
|
|
|
private handleOkMultiTasks = (): void => {
|
|
const { history } = this.props;
|
|
history.push('/tasks/');
|
|
};
|
|
|
|
private handleRetryCancelledMultiTasks = (): void => {
|
|
const { multiTasks } = this.state;
|
|
const newMultiTasks: any = multiTasks.map((it) => {
|
|
if (it.status === 'cancelled') {
|
|
return {
|
|
...it,
|
|
status: 'pending',
|
|
};
|
|
}
|
|
return it;
|
|
});
|
|
this.setState({
|
|
multiTasks: newMultiTasks,
|
|
}, () => {
|
|
this.createMultiTasks();
|
|
});
|
|
};
|
|
|
|
private handleRetryFailedMultiTasks = (): void => {
|
|
const { multiTasks } = this.state;
|
|
const newMultiTasks: any = multiTasks.map((it) => {
|
|
if (it.status === 'failed') {
|
|
return {
|
|
...it,
|
|
status: 'pending',
|
|
};
|
|
}
|
|
return it;
|
|
});
|
|
this.setState({
|
|
multiTasks: newMultiTasks,
|
|
}, () => {
|
|
this.createMultiTasks();
|
|
});
|
|
};
|
|
|
|
private getTaskName = (indexFile: number, fileManagerTabName: TabName, defaultFileName = ''): string => {
|
|
const { many } = this.props;
|
|
const { basic } = this.state;
|
|
const { files } = this.state;
|
|
const file = files[fileManagerTabName][indexFile];
|
|
let fileName = defaultFileName;
|
|
switch (fileManagerTabName) {
|
|
case 'remote':
|
|
fileName = getFileNameFromPath(file as string) || defaultFileName;
|
|
break;
|
|
case 'share':
|
|
fileName = getFileNameFromPath(file as string) || defaultFileName;
|
|
break;
|
|
default:
|
|
fileName = (file as File)?.name || (file as string) || defaultFileName;
|
|
break;
|
|
}
|
|
return many ?
|
|
basic.name
|
|
.replaceAll('{{file_name}}', fileName)
|
|
.replaceAll('{{index}}', indexFile.toString()) :
|
|
basic.name;
|
|
};
|
|
|
|
private renderBasicBlock(): JSX.Element {
|
|
const { many } = this.props;
|
|
const exampleMultiTaskName = many ? this.getTaskName(0, 'local', 'fileName.mp4') : '';
|
|
|
|
return (
|
|
<Col span={24}>
|
|
<BasicConfigurationForm
|
|
ref={this.basicConfigurationComponent}
|
|
many={many}
|
|
exampleMultiTaskName={exampleMultiTaskName}
|
|
onChange={this.handleChangeBasicConfiguration}
|
|
/>
|
|
</Col>
|
|
);
|
|
}
|
|
|
|
private renderProjectBlock(): JSX.Element {
|
|
const { projectId } = this.state;
|
|
|
|
return (
|
|
<>
|
|
<Col span={24}>
|
|
<Text className='cvat-text-color'>Project</Text>
|
|
</Col>
|
|
<Col span={24}>
|
|
<ProjectSearchField onSelect={this.handleProjectIdChange} value={projectId} />
|
|
</Col>
|
|
</>
|
|
);
|
|
}
|
|
|
|
private renderSubsetBlock(): JSX.Element | null {
|
|
const { projectId, subset } = this.state;
|
|
|
|
if (projectId !== null) {
|
|
return (
|
|
<>
|
|
<Col span={24}>
|
|
<Text className='cvat-text-color'>Subset</Text>
|
|
</Col>
|
|
<Col span={24}>
|
|
<ProjectSubsetField
|
|
value={subset}
|
|
onChange={this.handleTaskSubsetChange}
|
|
projectId={projectId}
|
|
/>
|
|
</Col>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private renderLabelsBlock(): JSX.Element {
|
|
const { projectId, labels } = this.state;
|
|
|
|
if (projectId) {
|
|
return (
|
|
<>
|
|
<Col span={24}>
|
|
<Text className='cvat-text-color'>Labels</Text>
|
|
</Col>
|
|
<Col span={24}>
|
|
<Text type='secondary'>Project labels will be used</Text>
|
|
</Col>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Col span={24}>
|
|
<Text type='danger'>* </Text>
|
|
<Text className='cvat-text-color'>Labels</Text>
|
|
<LabelsEditor
|
|
labels={labels}
|
|
onSubmit={(newLabels): void => {
|
|
this.setState({
|
|
labels: newLabels,
|
|
});
|
|
}}
|
|
/>
|
|
</Col>
|
|
);
|
|
}
|
|
|
|
private renderFilesBlock(): JSX.Element {
|
|
const { many } = this.props;
|
|
const { uploadFileErrorMessage } = this.state;
|
|
|
|
return (
|
|
<>
|
|
<Col span={24}>
|
|
<Text type='danger'>* </Text>
|
|
<Text className='cvat-text-color'>Select files</Text>
|
|
<ConnectedFileManager
|
|
many={many}
|
|
onChangeActiveKey={this.changeFileManagerTab}
|
|
onUploadLocalFiles={this.handleUploadLocalFiles}
|
|
onUploadRemoteFiles={this.handleUploadRemoteFiles}
|
|
onUploadShareFiles={this.handleUploadShareFiles}
|
|
onUploadCloudStorageFiles={this.handleUploadCloudStorageFiles}
|
|
ref={(container: any): void => {
|
|
this.fileManagerContainer = container;
|
|
}}
|
|
/>
|
|
</Col>
|
|
{ uploadFileErrorMessage ? (
|
|
<Col span={24}>
|
|
<Alert
|
|
className='cvat-create-task-content-alert'
|
|
type='error'
|
|
message={uploadFileErrorMessage}
|
|
showIcon
|
|
/>
|
|
</Col>
|
|
) : null }
|
|
</>
|
|
);
|
|
}
|
|
|
|
private renderAdvancedBlock(): JSX.Element {
|
|
const { installedGit, dumpers } = this.props;
|
|
const { activeFileManagerTab, projectId } = this.state;
|
|
|
|
const {
|
|
advanced: {
|
|
useProjectSourceStorage,
|
|
useProjectTargetStorage,
|
|
sourceStorage: {
|
|
location: sourceStorageLocation,
|
|
},
|
|
targetStorage: {
|
|
location: targetStorageLocation,
|
|
},
|
|
},
|
|
} = this.state;
|
|
return (
|
|
<Col span={24}>
|
|
<Collapse>
|
|
<Collapse.Panel key='1' header={<Text className='cvat-title'>Advanced configuration</Text>}>
|
|
<AdvancedConfigurationForm
|
|
dumpers={dumpers}
|
|
installedGit={installedGit}
|
|
activeFileManagerTab={activeFileManagerTab}
|
|
ref={this.advancedConfigurationComponent}
|
|
onSubmit={this.handleSubmitAdvancedConfiguration}
|
|
projectId={projectId}
|
|
useProjectSourceStorage={useProjectSourceStorage}
|
|
useProjectTargetStorage={useProjectTargetStorage}
|
|
sourceStorageLocation={sourceStorageLocation}
|
|
targetStorageLocation={targetStorageLocation}
|
|
onChangeUseProjectSourceStorage={this.handleUseProjectSourceStorageChange}
|
|
onChangeUseProjectTargetStorage={this.handleUseProjectTargetStorageChange}
|
|
onChangeSourceStorageLocation={(value: StorageLocation) => {
|
|
this.handleChangeStorageLocation('sourceStorage', value);
|
|
}}
|
|
onChangeTargetStorageLocation={(value: StorageLocation) => {
|
|
this.handleChangeStorageLocation('targetStorage', value);
|
|
}}
|
|
/>
|
|
</Collapse.Panel>
|
|
</Collapse>
|
|
</Col>
|
|
);
|
|
}
|
|
|
|
private renderFooterSingleTask(): JSX.Element {
|
|
const { uploadFileErrorMessage, loading, statusInProgressTask: status } = this.state;
|
|
|
|
if (status === 'FAILED' || loading) {
|
|
return (<Alert message={status} />);
|
|
}
|
|
return (
|
|
<Row justify='end' gutter={5}>
|
|
<Col>
|
|
<Button type='primary' onClick={this.handleSubmitAndOpen} disabled={!!uploadFileErrorMessage}>
|
|
Submit & Open
|
|
</Button>
|
|
</Col>
|
|
<Col>
|
|
<Button type='primary' onClick={this.handleSubmitAndContinue} disabled={!!uploadFileErrorMessage}>
|
|
Submit & Continue
|
|
</Button>
|
|
</Col>
|
|
</Row>
|
|
);
|
|
}
|
|
|
|
private renderFooterMutliTasks(): JSX.Element {
|
|
const {
|
|
multiTasks: items,
|
|
uploadFileErrorMessage,
|
|
files,
|
|
activeFileManagerTab,
|
|
loading,
|
|
} = this.state;
|
|
const currentFiles = files[activeFileManagerTab];
|
|
const countPending = items.filter((item) => item.status === 'pending').length;
|
|
const countAll = items.length;
|
|
|
|
if ((loading || countPending !== countAll) && currentFiles.length) {
|
|
return (
|
|
<MultiTasksProgress
|
|
tasks={items}
|
|
onOk={this.handleOkMultiTasks}
|
|
onCancel={this.handleCancelMultiTasks}
|
|
onRetryFailedTasks={this.handleRetryFailedMultiTasks}
|
|
onRetryCancelledTasks={this.handleRetryCancelledMultiTasks}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Row justify='end' gutter={5}>
|
|
<Col>
|
|
<Button type='primary' onClick={this.handleSubmitMutliTasks} disabled={!!uploadFileErrorMessage}>
|
|
Submit
|
|
{currentFiles.length}
|
|
tasks
|
|
</Button>
|
|
</Col>
|
|
</Row>
|
|
);
|
|
}
|
|
|
|
public render(): JSX.Element {
|
|
const { many } = this.props;
|
|
|
|
return (
|
|
<Row justify='start' align='middle' className='cvat-create-task-content'>
|
|
<Col span={24}>
|
|
<Text className='cvat-title'>Basic configuration</Text>
|
|
</Col>
|
|
|
|
{this.renderBasicBlock()}
|
|
{this.renderProjectBlock()}
|
|
{this.renderSubsetBlock()}
|
|
{this.renderLabelsBlock()}
|
|
{this.renderFilesBlock()}
|
|
{this.renderAdvancedBlock()}
|
|
|
|
<Col span={24} className='cvat-create-task-content-footer'>
|
|
{many ? this.renderFooterMutliTasks() : this.renderFooterSingleTask() }
|
|
</Col>
|
|
</Row>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default withRouter(CreateTaskContent);
|