Skip to content

Commit

Permalink
Git flexibility after task creation (#3886)
Browse files Browse the repository at this point in the history
  • Loading branch information
PMazarovich committed Feb 25, 2022
1 parent eac5b60 commit cca2586
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 88 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for working with ellipses (<https://github.com/openvinotoolkit/cvat/pull/4062>)
- Add several flags to task creation CLI (<https://github.com/openvinotoolkit/cvat/pull/4119>)
- Add YOLOv5 serverless function for automatic annotation (<https://github.com/openvinotoolkit/cvat/pull/4178>)
- Add possibility to change git repository and git export format from already created task (<https://github.com/openvinotoolkit/cvat/pull/3886>)
- Basic page with jobs list, basic filtration to this list (<https://github.com/openvinotoolkit/cvat/pull/4258>)
- Added OpenCV.js TrackerMIL as tracking tool (<https://github.com/openvinotoolkit/cvat/pull/4200>)
- Ability to continue working from the latest frame where an annotator was before (<https://github.com/openvinotoolkit/cvat/pull/4297>)
Expand Down
208 changes: 137 additions & 71 deletions cvat-ui/src/components/task-page/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,22 @@ import notification from 'antd/lib/notification';
import Text from 'antd/lib/typography/Text';
import Title from 'antd/lib/typography/Title';
import moment from 'moment';

import Descriptions from 'antd/lib/descriptions';
import Paragraph from 'antd/lib/typography/Paragraph';
import Select from 'antd/lib/select';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import getCore from 'cvat-core-wrapper';
import { getReposData, syncRepos } from 'utils/git-utils';
import { getReposData, syncRepos, changeRepo } from 'utils/git-utils';
import { ActiveInference } from 'reducers/interfaces';
import AutomaticAnnotationProgress from 'components/tasks-page/automatic-annotation-progress';
import Descriptions from 'antd/lib/descriptions';
import Space from 'antd/lib/space';
import UserSelector, { User } from './user-selector';
import BugTrackerEditor from './bug-tracker-editor';
import LabelsEditorComponent from '../labels-editor/labels-editor';
import ProjectSubsetField from '../create-task-page/project-subset-field';

const { Option } = Select;

const core = getCore();

interface Props {
Expand All @@ -32,6 +37,8 @@ interface Props {
projectSubsets: string[];
cancelAutoAnnotation(): void;
onTaskUpdate: (taskInstance: any) => void;
dumpers: any[];
user: any;
}

interface State {
Expand All @@ -40,13 +47,13 @@ interface State {
repository: string;
repositoryStatus: string;
format: string;
lfs: boolean;
updatingRepository: boolean;
}

export default class DetailsComponent extends React.PureComponent<Props, State> {
private mounted: boolean;

private previewImageElement: HTMLImageElement;

private previewWrapperRef: React.RefObject<HTMLDivElement>;

constructor(props: Props) {
Expand All @@ -63,6 +70,8 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
repository: '',
format: '',
repositoryStatus: '',
lfs: false,
updatingRepository: false,
};
}

Expand Down Expand Up @@ -103,6 +112,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
this.setState({
repository: data.url,
format: data.format,
lfs: !!data.lfs,
});
}
})
Expand Down Expand Up @@ -130,6 +140,54 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
this.mounted = false;
}

private onChangeRepoValue = (value: string): void => {
const { taskInstance } = this.props;
const { repository } = this.state;
const old = repository;
this.setState({ repository: value, updatingRepository: true });
changeRepo(taskInstance.id, 'url', value)
.catch((error) => {
this.setState({ repository: old });
notification.error({
message: 'Could not update repository',
description: error,
});
})
.finally(() => this.setState({ updatingRepository: false }));
};

private onChangeLFSValue = (event: CheckboxChangeEvent): void => {
const { taskInstance } = this.props;
const { lfs } = this.state;
const old = lfs;
this.setState({ lfs: event.target.checked, updatingRepository: true });
changeRepo(taskInstance.id, 'lfs', event.target.checked)
.catch((error) => {
this.setState({ lfs: old });
notification.error({
message: 'Could not update LFS',
description: error,
});
})
.finally(() => this.setState({ updatingRepository: false }));
};

private onChangeFormatValue = (value: string): void => {
const { taskInstance } = this.props;
const { format } = this.state;
const old = format;
this.setState({ format: value, updatingRepository: true });
changeRepo(taskInstance.id, 'format', value)
.catch((error) => {
this.setState({ format: old });
notification.error({
message: 'Could not update format',
description: error,
});
})
.finally(() => this.setState({ updatingRepository: false }));
};

private renderTaskName(): JSX.Element {
const { name } = this.state;
const { taskInstance, onTaskUpdate } = this.props;
Expand Down Expand Up @@ -205,81 +263,89 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
}

private renderDatasetRepository(): JSX.Element | boolean {
const { taskInstance } = this.props;
const { repository, repositoryStatus, format } = this.state;

const { taskInstance, dumpers } = this.props;
const {
repository, repositoryStatus, format, lfs, updatingRepository,
} = this.state;
return (
!!repository && (
<Row>
<Col className='cvat-dataset-repository-url'>
<Text strong className='cvat-text-color'>
Dataset Repository
</Text>
<br />
<a href={repository} rel='noopener noreferrer' target='_blank'>
{repository}
</a>
<br />
<p>
Using format
{' '}
<Text strong>
{format}
<Paragraph>
<Text editable={{ onChange: this.onChangeRepoValue }} disabled={updatingRepository}>
{repository}
</Text>
</p>
{repositoryStatus === 'sync' && (
<Tag color='blue'>
<CheckCircleOutlined />
Synchronized
</Tag>
)}
{repositoryStatus === 'merged' && (
<Tag color='green'>
<CheckCircleOutlined />
Merged
</Tag>
)}
{repositoryStatus === 'syncing' && (
<Tag color='purple'>
<LoadingOutlined />
Syncing
</Tag>
)}
{repositoryStatus === '!sync' && (
<Tag
color='red'
onClick={(): void => {
this.setState({
repositoryStatus: 'syncing',
});

syncRepos(taskInstance.id)
.then((): void => {
if (this.mounted) {
this.setState({
repositoryStatus: 'sync',
});
}
})
.catch((error): void => {
if (this.mounted) {
Modal.error({
width: 800,
title: 'Could not synchronize the repository',
content: error.toString(),
});

this.setState({
repositoryStatus: '!sync',
});
}
{repositoryStatus === 'sync' && (
<Tag color='blue'>
<CheckCircleOutlined />
Synchronized
</Tag>
)}
{repositoryStatus === 'merged' && (
<Tag color='green'>
<CheckCircleOutlined />
Merged
</Tag>
)}
{repositoryStatus === 'syncing' && (
<Tag color='purple'>
<LoadingOutlined />
Syncing
</Tag>
)}
{repositoryStatus === '!sync' && (
<Tag
color='red'
onClick={(): void => {
this.setState({
repositoryStatus: 'syncing',
});
}}
>
<ExclamationCircleOutlined />
Synchronize
</Tag>
)}

syncRepos(taskInstance.id)
.then((): void => {
if (this.mounted) {
this.setState({
repositoryStatus: 'sync',
});
}
})
.catch((error): void => {
if (this.mounted) {
Modal.error({
width: 800,
title: 'Could not synchronize the repository',
content: error.toString(),
});

this.setState({
repositoryStatus: '!sync',
});
}
});
}}
>
<ExclamationCircleOutlined />
Synchronize
</Tag>
)}
</Paragraph>
<Text strong className='cvat-text-color'>Using format: </Text>
<Space>
<Select disabled={updatingRepository} onChange={this.onChangeFormatValue} className='cvat-repository-format-select' value={format}>
{
dumpers.map((dumper: any) => (
<Option key={dumper.name} value={dumper.name}>{dumper.name}</Option>
))
}
</Select>
<Checkbox disabled={updatingRepository} onChange={this.onChangeLFSValue} checked={lfs}>
Large file support
</Checkbox>
{updatingRepository && <LoadingOutlined style={{ fontSize: 14 }} spin />}
</Space>
</Col>
</Row>
)
Expand Down
32 changes: 21 additions & 11 deletions cvat-ui/src/components/task-page/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,35 @@

.cvat-dataset-repository-url {
> a {
margin-right: 10px;
margin-right: $grid-unit-size;
}

> .ant-tag-red {
user-select: none;
opacity: 0.4;
> .ant-typography {
> .ant-tag {
margin-left: $grid-unit-size;

&:hover {
opacity: 0.8;
> span[role='img'] {
margin-right: $grid-unit-size;
}
}

&:active {
opacity: 1;
> .ant-tag-red {
user-select: none;
opacity: 0.4;

&:hover {
opacity: 0.8;
}

&:active {
opacity: 1;
}
}
}
}

> .ant-tag > span[role='img'] {
margin-right: 5px;
}
.cvat-repository-format-select {
width: 100%;
}

.cvat-task-job-list {
Expand Down
10 changes: 8 additions & 2 deletions cvat-ui/src/containers/task-page/details.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2021 Intel Corporation
// Copyright (C) 2019-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -18,6 +18,8 @@ interface StateToProps {
activeInference: ActiveInference | null;
installedGit: boolean;
projectSubsets: string[];
dumpers: any[];
user: any;
}

interface DispatchToProps {
Expand All @@ -30,6 +32,8 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
const [taskProject] = state.projects.current.filter((project) => project.id === own.task.instance.projectId);

return {
dumpers: state.formats.annotationFormats.dumpers,
user: state.auth.user,
installedGit: list.GIT_INTEGRATION,
activeInference: state.models.inferences[own.task.instance.id] || null,
projectSubsets: taskProject ?
Expand All @@ -53,11 +57,13 @@ function mapDispatchToProps(dispatch: any, own: OwnProps): DispatchToProps {

function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps): JSX.Element {
const {
task, installedGit, activeInference, projectSubsets, cancelAutoAnnotation, onTaskUpdate,
task, installedGit, activeInference, projectSubsets, cancelAutoAnnotation, onTaskUpdate, dumpers, user,
} = props;

return (
<DetailsComponent
dumpers={dumpers}
user={user}
previewImage={task.preview}
taskInstance={task.instance}
installedGit={installedGit}
Expand Down
Loading

0 comments on commit cca2586

Please sign in to comment.