|
|
|
@ -2,16 +2,17 @@
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
import React, { RefObject } from 'react';
|
|
|
|
import { Row, Col } from 'antd/lib/grid';
|
|
|
|
import { Row, Col } from 'antd/lib/grid';
|
|
|
|
import { PercentageOutlined } from '@ant-design/icons';
|
|
|
|
import { PercentageOutlined } from '@ant-design/icons';
|
|
|
|
import Input from 'antd/lib/input';
|
|
|
|
import Input from 'antd/lib/input';
|
|
|
|
import Checkbox from 'antd/lib/checkbox';
|
|
|
|
import Checkbox from 'antd/lib/checkbox';
|
|
|
|
import Tooltip from 'antd/lib/tooltip';
|
|
|
|
import Tooltip from 'antd/lib/tooltip';
|
|
|
|
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form';
|
|
|
|
import Form, { FormInstance, RuleObject, RuleRender } from 'antd/lib/form';
|
|
|
|
import Text from 'antd/lib/typography/Text';
|
|
|
|
import Text from 'antd/lib/typography/Text';
|
|
|
|
|
|
|
|
|
|
|
|
import patterns from 'utils/validation-patterns';
|
|
|
|
import patterns from 'utils/validation-patterns';
|
|
|
|
|
|
|
|
import { Store } from 'antd/lib/form/interface';
|
|
|
|
|
|
|
|
|
|
|
|
export interface AdvancedConfiguration {
|
|
|
|
export interface AdvancedConfiguration {
|
|
|
|
bugTracker?: string;
|
|
|
|
bugTracker?: string;
|
|
|
|
@ -29,273 +30,266 @@ export interface AdvancedConfiguration {
|
|
|
|
copyData?: boolean;
|
|
|
|
copyData?: boolean;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Props = FormComponentProps & {
|
|
|
|
const initialValues: AdvancedConfiguration = {
|
|
|
|
|
|
|
|
imageQuality: 70,
|
|
|
|
|
|
|
|
lfs: false,
|
|
|
|
|
|
|
|
useZipChunks: true,
|
|
|
|
|
|
|
|
useCache: true,
|
|
|
|
|
|
|
|
copyData: false,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
onSubmit(values: AdvancedConfiguration): void;
|
|
|
|
onSubmit(values: AdvancedConfiguration): void;
|
|
|
|
installedGit: boolean;
|
|
|
|
installedGit: boolean;
|
|
|
|
activeFileManagerTab: string;
|
|
|
|
activeFileManagerTab: string;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isPositiveInteger(_: any, value: any, callback: any): void {
|
|
|
|
function validateURL(_: RuleObject, value: string): Promise<void> {
|
|
|
|
if (!value) {
|
|
|
|
if (value && !patterns.validateURL.pattern.test(value)) {
|
|
|
|
callback();
|
|
|
|
return Promise.reject(new Error('URL is not a valid URL'));
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const intValue = +value;
|
|
|
|
return Promise.resolve();
|
|
|
|
if (Number.isNaN(intValue) || !Number.isInteger(intValue) || intValue < 1) {
|
|
|
|
|
|
|
|
callback('Value must be a positive integer');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
callback();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isNonNegativeInteger(_: any, value: any, callback: any): void {
|
|
|
|
function validateRepositoryPath(_: RuleObject, value: string): Promise<void> {
|
|
|
|
if (!value) {
|
|
|
|
if (value && !patterns.validatePath.pattern.test(value)) {
|
|
|
|
callback();
|
|
|
|
return Promise.reject(new Error('Repository path is not a valid path'));
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const intValue = +value;
|
|
|
|
return Promise.resolve();
|
|
|
|
if (Number.isNaN(intValue) || intValue < 0) {
|
|
|
|
}
|
|
|
|
callback('Value must be a non negative integer');
|
|
|
|
|
|
|
|
|
|
|
|
function validateRepository(_: RuleObject, value: string): Promise<[void, void]> | Promise<void> {
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
|
|
|
const [url, path] = value.split(/\s+/);
|
|
|
|
|
|
|
|
return Promise.all([validateURL(_, url), validateRepositoryPath(_, path)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
callback();
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isIntegerRange(min: number, max: number, _: any, value: any, callback: any): void {
|
|
|
|
const isInteger = ({ min, max }: { min?: number, max?: number }) => (
|
|
|
|
if (!value) {
|
|
|
|
_: RuleObject, value?: number | string,
|
|
|
|
callback();
|
|
|
|
): Promise<void> => {
|
|
|
|
return;
|
|
|
|
if (typeof value === 'undefined' || value === '') {
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const intValue = +value;
|
|
|
|
const intValue = +value;
|
|
|
|
if (Number.isNaN(intValue) || !Number.isInteger(intValue) || intValue < min || intValue > max) {
|
|
|
|
if (Number.isNaN(intValue) || !Number.isInteger(intValue)) {
|
|
|
|
callback(`Value must be an integer [${min}, ${max}]`);
|
|
|
|
return Promise.reject(new Error('Value must be a positive integer'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
callback();
|
|
|
|
if (typeof min !== 'undefined' && intValue < min) {
|
|
|
|
}
|
|
|
|
return Promise.reject(new Error(`Value must be more than ${min}`));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof max !== 'undefined' && intValue > max) {
|
|
|
|
|
|
|
|
return Promise.reject(new Error(`Value must be less than ${max}`));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const validateOverlapSize: RuleRender = ({ getFieldValue }): RuleObject => ({
|
|
|
|
|
|
|
|
validator(_: RuleObject, value?: string | number): Promise<void> {
|
|
|
|
|
|
|
|
if (typeof value !== 'undefined' && value !== '') {
|
|
|
|
|
|
|
|
const segmentSize = getFieldValue('segmentSize');
|
|
|
|
|
|
|
|
if (typeof segmentSize !== 'undefined' && segmentSize !== '') {
|
|
|
|
|
|
|
|
if (+segmentSize <= +value) {
|
|
|
|
|
|
|
|
return Promise.reject(new Error('Segment size must be more than overlap size'));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const validateStopFrame: RuleRender = ({ getFieldValue }): RuleObject => ({
|
|
|
|
|
|
|
|
validator(_: RuleObject, value?: string | number): Promise<void> {
|
|
|
|
|
|
|
|
if (typeof value !== 'undefined' && value !== '') {
|
|
|
|
|
|
|
|
const startFrame = getFieldValue('startFrame');
|
|
|
|
|
|
|
|
if (typeof startFrame !== 'undefined' && startFrame !== '') {
|
|
|
|
|
|
|
|
if (+startFrame > +value) {
|
|
|
|
|
|
|
|
return Promise.reject(new Error('Start frame must not be more than stop frame'));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
class AdvancedConfigurationForm extends React.PureComponent<Props> {
|
|
|
|
class AdvancedConfigurationForm extends React.PureComponent<Props> {
|
|
|
|
|
|
|
|
private formRef: RefObject<FormInstance>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public constructor(props: Props) {
|
|
|
|
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.formRef = React.createRef<FormInstance>();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public submit(): Promise<void> {
|
|
|
|
public submit(): Promise<void> {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const { onSubmit } = this.props;
|
|
|
|
const { form, onSubmit } = this.props;
|
|
|
|
if (this.formRef.current) {
|
|
|
|
|
|
|
|
this.formRef.current.resetFields();
|
|
|
|
form.validateFields((error, values): void => {
|
|
|
|
return this.formRef.current.validateFields().then((values: Store): Promise<void> => {
|
|
|
|
if (!error) {
|
|
|
|
const frameFilter = values.frameStep ? `step=${values.frameStep}` : undefined;
|
|
|
|
const filteredValues = { ...values };
|
|
|
|
const entries = Object.entries(values)
|
|
|
|
delete filteredValues.frameStep;
|
|
|
|
.filter((entry: [string, unknown]): boolean => entry[0] !== frameFilter);
|
|
|
|
|
|
|
|
|
|
|
|
if (values.overlapSize && +values.segmentSize <= +values.overlapSize) {
|
|
|
|
onSubmit({
|
|
|
|
reject(new Error('Segment size must be more than overlap size'));
|
|
|
|
...Object.fromEntries(entries) as any as AdvancedConfiguration,
|
|
|
|
}
|
|
|
|
frameFilter,
|
|
|
|
|
|
|
|
});
|
|
|
|
if (
|
|
|
|
return Promise.resolve();
|
|
|
|
typeof values.startFrame !== 'undefined' &&
|
|
|
|
|
|
|
|
typeof values.stopFrame !== 'undefined' &&
|
|
|
|
|
|
|
|
+values.stopFrame < +values.startFrame
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
reject(new Error('Stop frame must be more or equal start frame'));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onSubmit({
|
|
|
|
|
|
|
|
...values,
|
|
|
|
|
|
|
|
frameFilter: values.frameStep ? `step=${values.frameStep}` : undefined,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
reject();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.reject(new Error('Form ref is empty'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public resetFields(): void {
|
|
|
|
public resetFields(): void {
|
|
|
|
const { form } = this.props;
|
|
|
|
if (this.formRef.current) {
|
|
|
|
form.resetFields();
|
|
|
|
this.formRef.current.resetFields();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
renderCopyDataChechbox(): JSX.Element {
|
|
|
|
/* eslint-disable class-methods-use-this */
|
|
|
|
const { form } = this.props;
|
|
|
|
private renderCopyDataChechbox(): JSX.Element {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Row>
|
|
|
|
<Form.Item
|
|
|
|
<Col>
|
|
|
|
help='If you have a low data transfer rate over the network you can copy data into CVAT to speed up work'
|
|
|
|
<Form.Item help='If you have a low data transfer rate over the network you can copy data into CVAT to speed up work'>
|
|
|
|
name='copyData'
|
|
|
|
{form.getFieldDecorator('copyData', {
|
|
|
|
valuePropName='checked'
|
|
|
|
initialValue: false,
|
|
|
|
>
|
|
|
|
valuePropName: 'checked',
|
|
|
|
<Checkbox>
|
|
|
|
})(
|
|
|
|
<Text className='cvat-text-color'>Copy data into CVAT</Text>
|
|
|
|
<Checkbox>
|
|
|
|
</Checkbox>
|
|
|
|
<Text className='cvat-text-color'>Copy data into CVAT</Text>
|
|
|
|
</Form.Item>
|
|
|
|
</Checkbox>,
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderImageQuality(): JSX.Element {
|
|
|
|
private renderImageQuality(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Image quality</span>}>
|
|
|
|
<Tooltip title='Defines images compression level' mouseLeaveDelay={0}>
|
|
|
|
<Tooltip title='Defines image quality level' mouseLeaveDelay={0}>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('imageQuality', {
|
|
|
|
label='Image quality'
|
|
|
|
initialValue: 70,
|
|
|
|
name='imageQuality'
|
|
|
|
rules: [
|
|
|
|
rules={[
|
|
|
|
{
|
|
|
|
{
|
|
|
|
required: true,
|
|
|
|
required: true,
|
|
|
|
message: 'The field is required.',
|
|
|
|
message: 'The field is required.',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{ validator: isInteger({ min: 5, max: 100 }) },
|
|
|
|
validator: isIntegerRange.bind(null, 5, 100),
|
|
|
|
]}
|
|
|
|
},
|
|
|
|
>
|
|
|
|
],
|
|
|
|
<Input
|
|
|
|
})(<Input size='large' type='number' suffix={<PercentageOutlined />} />)}
|
|
|
|
size='large'
|
|
|
|
</Tooltip>
|
|
|
|
type='number'
|
|
|
|
</Form.Item>
|
|
|
|
min={5}
|
|
|
|
|
|
|
|
max={100}
|
|
|
|
|
|
|
|
suffix={<PercentageOutlined />}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
</Tooltip>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderOverlap(): JSX.Element {
|
|
|
|
private renderOverlap(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Overlap size</span>}>
|
|
|
|
<Tooltip title='Defines a number of intersected frames between different segments' mouseLeaveDelay={0}>
|
|
|
|
<Tooltip title='Defines a number of intersected frames between different segments' mouseLeaveDelay={0}>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('overlapSize', {
|
|
|
|
label='Overlap size'
|
|
|
|
rules: [
|
|
|
|
name='overlapSize'
|
|
|
|
{
|
|
|
|
dependencies={['segmentSize']}
|
|
|
|
validator: isNonNegativeInteger,
|
|
|
|
rules={[{ validator: isInteger({ min: 0 }) }, validateOverlapSize]}
|
|
|
|
},
|
|
|
|
>
|
|
|
|
],
|
|
|
|
<Input size='large' type='number' min={0} />
|
|
|
|
})(<Input size='large' type='number' />)}
|
|
|
|
</Form.Item>
|
|
|
|
</Tooltip>
|
|
|
|
</Tooltip>
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderSegmentSize(): JSX.Element {
|
|
|
|
private renderSegmentSize(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Segment size</span>}>
|
|
|
|
<Tooltip title='Defines a number of frames in a segment' mouseLeaveDelay={0}>
|
|
|
|
<Tooltip title='Defines a number of frames in a segment' mouseLeaveDelay={0}>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('segmentSize', {
|
|
|
|
label='Segment size'
|
|
|
|
rules: [
|
|
|
|
name='segmentSize'
|
|
|
|
{
|
|
|
|
rules={[{ validator: isInteger({ min: 1 }) }]}
|
|
|
|
validator: isPositiveInteger,
|
|
|
|
>
|
|
|
|
},
|
|
|
|
<Input size='large' type='number' min={1} />
|
|
|
|
],
|
|
|
|
</Form.Item>
|
|
|
|
})(<Input size='large' type='number' />)}
|
|
|
|
</Tooltip>
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderStartFrame(): JSX.Element {
|
|
|
|
private renderStartFrame(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Start frame</span>}>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('startFrame', {
|
|
|
|
label='Start frame'
|
|
|
|
rules: [
|
|
|
|
name='startFrame'
|
|
|
|
{
|
|
|
|
rules={[{ validator: isInteger({ min: 0 }) }]}
|
|
|
|
validator: isNonNegativeInteger,
|
|
|
|
>
|
|
|
|
},
|
|
|
|
<Input size='large' type='number' min={0} step={1} />
|
|
|
|
],
|
|
|
|
|
|
|
|
})(<Input size='large' type='number' min={0} step={1} />)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderStopFrame(): JSX.Element {
|
|
|
|
private renderStopFrame(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Stop frame</span>}>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('stopFrame', {
|
|
|
|
label='Stop frame'
|
|
|
|
rules: [
|
|
|
|
name='stopFrame'
|
|
|
|
{
|
|
|
|
dependencies={['startFrame']}
|
|
|
|
validator: isNonNegativeInteger,
|
|
|
|
rules={[{ validator: isInteger({ min: 0 }) }, validateStopFrame]}
|
|
|
|
},
|
|
|
|
>
|
|
|
|
],
|
|
|
|
<Input size='large' type='number' min={0} step={1} />
|
|
|
|
})(<Input size='large' type='number' min={0} step={1} />)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderFrameStep(): JSX.Element {
|
|
|
|
private renderFrameStep(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Frame step</span>}>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('frameStep', {
|
|
|
|
label='Frame step'
|
|
|
|
rules: [
|
|
|
|
name='frameStep'
|
|
|
|
{
|
|
|
|
rules={[{ validator: isInteger({ min: 1 }) }]}
|
|
|
|
validator: isPositiveInteger,
|
|
|
|
>
|
|
|
|
},
|
|
|
|
<Input size='large' type='number' min={1} step={1} />
|
|
|
|
],
|
|
|
|
|
|
|
|
})(<Input size='large' type='number' min={1} step={1} />)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderGitLFSBox(): JSX.Element {
|
|
|
|
private renderGitLFSBox(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item help='If annotation files are large, you can use git LFS feature'>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('lfs', {
|
|
|
|
help='If annotation files are large, you can use git LFS feature'
|
|
|
|
valuePropName: 'checked',
|
|
|
|
name='lfs'
|
|
|
|
initialValue: false,
|
|
|
|
valuePropName='checked'
|
|
|
|
})(
|
|
|
|
>
|
|
|
|
<Checkbox>
|
|
|
|
<Checkbox>
|
|
|
|
<Text className='cvat-text-color'>Use LFS (Large File Support):</Text>
|
|
|
|
<Text className='cvat-text-color'>Use LFS (Large File Support):</Text>
|
|
|
|
</Checkbox>,
|
|
|
|
</Checkbox>
|
|
|
|
)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderGitRepositoryURL(): JSX.Element {
|
|
|
|
private renderGitRepositoryURL(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item
|
|
|
|
<Form.Item
|
|
|
|
hasFeedback
|
|
|
|
hasFeedback
|
|
|
|
label={<span>Dataset repository URL</span>}
|
|
|
|
name='repository'
|
|
|
|
|
|
|
|
label='Dataset repository URL'
|
|
|
|
extra='Attach a repository to store annotations there'
|
|
|
|
extra='Attach a repository to store annotations there'
|
|
|
|
|
|
|
|
rules={[{ validator: validateRepository }]}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{form.getFieldDecorator('repository', {
|
|
|
|
<Input
|
|
|
|
rules: [
|
|
|
|
size='large'
|
|
|
|
{
|
|
|
|
placeholder='e.g. https//github.com/user/repos [annotation/<anno_file_name>.zip]'
|
|
|
|
validator: (_, value, callback): void => {
|
|
|
|
/>
|
|
|
|
if (!value) {
|
|
|
|
|
|
|
|
callback();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
const [url, path] = value.split(/\s+/);
|
|
|
|
|
|
|
|
if (!patterns.validateURL.pattern.test(url)) {
|
|
|
|
|
|
|
|
callback('Git URL is not a valid');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (path && !patterns.validatePath.pattern.test(path)) {
|
|
|
|
|
|
|
|
callback('Git path is not a valid');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
callback();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
})(
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
size='large'
|
|
|
|
|
|
|
|
placeholder='e.g. https//github.com/user/repos [annotation/<anno_file_name>.zip]'
|
|
|
|
|
|
|
|
/>,
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -304,124 +298,106 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<>
|
|
|
|
<Row>
|
|
|
|
<Row>
|
|
|
|
<Col>{this.renderGitRepositoryURL()}</Col>
|
|
|
|
<Col span={24}>{this.renderGitRepositoryURL()}</Col>
|
|
|
|
</Row>
|
|
|
|
</Row>
|
|
|
|
<Row>
|
|
|
|
<Row>
|
|
|
|
<Col>{this.renderGitLFSBox()}</Col>
|
|
|
|
<Col span={24}>{this.renderGitLFSBox()}</Col>
|
|
|
|
</Row>
|
|
|
|
</Row>
|
|
|
|
</>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderBugTracker(): JSX.Element {
|
|
|
|
private renderBugTracker(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item
|
|
|
|
<Form.Item
|
|
|
|
hasFeedback
|
|
|
|
hasFeedback
|
|
|
|
label={<span>Issue tracker</span>}
|
|
|
|
name='bugTracker'
|
|
|
|
|
|
|
|
label='Issue tracker'
|
|
|
|
extra='Attach issue tracker where the task is described'
|
|
|
|
extra='Attach issue tracker where the task is described'
|
|
|
|
|
|
|
|
rules={[{ validator: validateURL }]}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{form.getFieldDecorator('bugTracker', {
|
|
|
|
<Input size='large' />
|
|
|
|
rules: [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
validator: (_, value, callback): void => {
|
|
|
|
|
|
|
|
if (value && !patterns.validateURL.pattern.test(value)) {
|
|
|
|
|
|
|
|
callback('Issue tracker must be URL');
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
callback();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
})(<Input size='large' />)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderUzeZipChunks(): JSX.Element {
|
|
|
|
private renderUzeZipChunks(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item help='Force to use zip chunks as compressed data. Actual for videos only.'>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('useZipChunks', {
|
|
|
|
help='Force to use zip chunks as compressed data. Actual for videos only.'
|
|
|
|
initialValue: true,
|
|
|
|
name='useZipChunks'
|
|
|
|
valuePropName: 'checked',
|
|
|
|
valuePropName='checked'
|
|
|
|
})(
|
|
|
|
>
|
|
|
|
<Checkbox>
|
|
|
|
<Checkbox>
|
|
|
|
<Text className='cvat-text-color'>Use zip chunks</Text>
|
|
|
|
<Text className='cvat-text-color'>Use zip chunks</Text>
|
|
|
|
</Checkbox>,
|
|
|
|
</Checkbox>
|
|
|
|
)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderCreateTaskMethod(): JSX.Element {
|
|
|
|
private renderCreateTaskMethod(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item help='Using cache to store data.'>
|
|
|
|
<Form.Item
|
|
|
|
{form.getFieldDecorator('useCache', {
|
|
|
|
help='Using cache to store data.'
|
|
|
|
initialValue: true,
|
|
|
|
name='useCache'
|
|
|
|
valuePropName: 'checked',
|
|
|
|
valuePropName='checked'
|
|
|
|
})(
|
|
|
|
>
|
|
|
|
<Checkbox>
|
|
|
|
<Checkbox>
|
|
|
|
<Text className='cvat-text-color'>Use cache</Text>
|
|
|
|
<Text className='cvat-text-color'>Use cache</Text>
|
|
|
|
</Checkbox>,
|
|
|
|
</Checkbox>
|
|
|
|
)}
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
</Form.Item>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private renderChunkSize(): JSX.Element {
|
|
|
|
private renderChunkSize(): JSX.Element {
|
|
|
|
const { form } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form.Item label={<span>Chunk size</span>}>
|
|
|
|
<Tooltip
|
|
|
|
<Tooltip
|
|
|
|
title={(
|
|
|
|
title={(
|
|
|
|
<>
|
|
|
|
<>
|
|
|
|
Defines a number of frames to be packed in a chunk when send from client to server. Server
|
|
|
|
Defines a number of frames to be packed in a chunk when send from client to server. Server
|
|
|
|
defines automatically if empty.
|
|
|
|
defines automatically if empty.
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
Recommended values:
|
|
|
|
Recommended values:
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
1080p or less: 36
|
|
|
|
1080p or less: 36
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
2k or less: 8 - 16
|
|
|
|
2k or less: 8 - 16
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
4k or less: 4 - 8
|
|
|
|
4k or less: 4 - 8
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
More: 1 - 4
|
|
|
|
More: 1 - 4
|
|
|
|
</>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
mouseLeaveDelay={0}
|
|
|
|
mouseLeaveDelay={0}
|
|
|
|
>
|
|
|
|
|
|
|
|
<Form.Item
|
|
|
|
|
|
|
|
label='Chunk size'
|
|
|
|
|
|
|
|
name='dataChunkSize'
|
|
|
|
|
|
|
|
rules={[{ validator: isInteger({ min: 1 }) }]}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{form.getFieldDecorator('dataChunkSize', {
|
|
|
|
<Input size='large' type='number' />
|
|
|
|
rules: [
|
|
|
|
</Form.Item>
|
|
|
|
{
|
|
|
|
</Tooltip>
|
|
|
|
validator: isPositiveInteger,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
})(<Input size='large' type='number' />)}
|
|
|
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public render(): JSX.Element {
|
|
|
|
public render(): JSX.Element {
|
|
|
|
const { installedGit, activeFileManagerTab } = this.props;
|
|
|
|
const { installedGit, activeFileManagerTab } = this.props;
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Form>
|
|
|
|
<Form initialValues={initialValues} ref={this.formRef} layout='vertical'>
|
|
|
|
|
|
|
|
{activeFileManagerTab === 'share' ? (
|
|
|
|
{activeFileManagerTab === 'share' ? this.renderCopyDataChechbox() : null}
|
|
|
|
<Row>
|
|
|
|
|
|
|
|
<Col>
|
|
|
|
|
|
|
|
{ this.renderCopyDataChechbox() }
|
|
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
) : null}
|
|
|
|
<Row>
|
|
|
|
<Row>
|
|
|
|
<Col>{this.renderUzeZipChunks()}</Col>
|
|
|
|
<Col>{this.renderUzeZipChunks()}</Col>
|
|
|
|
</Row>
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
|
|
<Row>
|
|
|
|
<Row>
|
|
|
|
<Col>{this.renderCreateTaskMethod()}</Col>
|
|
|
|
<Col>{this.renderCreateTaskMethod()}</Col>
|
|
|
|
</Row>
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
|
|
<Row type='flex' justify='start'>
|
|
|
|
<Row type='flex' justify='start'>
|
|
|
|
<Col span={7}>{this.renderImageQuality()}</Col>
|
|
|
|
<Col span={7}>{this.renderImageQuality()}</Col>
|
|
|
|
<Col span={7} offset={1}>
|
|
|
|
<Col span={7} offset={1}>
|
|
|
|
@ -449,11 +425,11 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
|
|
|
|
{installedGit ? this.renderGit() : null}
|
|
|
|
{installedGit ? this.renderGit() : null}
|
|
|
|
|
|
|
|
|
|
|
|
<Row>
|
|
|
|
<Row>
|
|
|
|
<Col>{this.renderBugTracker()}</Col>
|
|
|
|
<Col span={24}>{this.renderBugTracker()}</Col>
|
|
|
|
</Row>
|
|
|
|
</Row>
|
|
|
|
</Form>
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default Form.create<Props>()(AdvancedConfigurationForm);
|
|
|
|
export default AdvancedConfigurationForm;
|
|
|
|
|