Skip to content

Commit

Permalink
Editable git
Browse files Browse the repository at this point in the history
  • Loading branch information
PMazarovich committed Jan 31, 2022
1 parent bec253f commit 49e1596
Showing 8 changed files with 224 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -30,6 +30,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>)
### Changed
- Users don't have access to a task object anymore if they are assigneed only on some jobs of the task (<https://github.com/openvinotoolkit/cvat/pull/3788>)
- Different resources (tasks, projects) are not visible anymore for all CVAT instance users by default (<https://github.com/openvinotoolkit/cvat/pull/3788>)
203 changes: 134 additions & 69 deletions cvat-ui/src/components/task-page/details.tsx
Original file line number Diff line number Diff line change
@@ -11,16 +11,21 @@ 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 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 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';
import { Space } from 'antd';

const { Option } = Select;

const core = getCore();

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

interface State {
@@ -40,6 +47,7 @@ interface State {
repository: string;
repositoryStatus: string;
format: string;
lfs: boolean;
}

export default class DetailsComponent extends React.PureComponent<Props, State> {
@@ -63,6 +71,8 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
repository: '',
format: '',
repositoryStatus: '',
lfs: '',
updatingRepository: false,
};
}

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

private onChangeRepoValue = (value: string): void => {
const { taskInstance, repository } = this.props;
let 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 = (value): void => {
const { taskInstance, lfs } = this.props;
let old = lfs
this.setState({ lfs: value.target.checked, updatingRepository: true });
changeRepo(taskInstance.id, 'lfs', value.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, format } = this.props;
let 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;
@@ -205,81 +262,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',
});
}}
>
<WarningOutlined />
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',
});
}
});
}}
>
<WarningOutlined />
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>
)
35 changes: 24 additions & 11 deletions cvat-ui/src/components/task-page/styles.scss
Original file line number Diff line number Diff line change
@@ -46,25 +46,38 @@

.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;
}
}
}
}
.cvat-repository-format-select {
width: 100%;
}

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

.cvat-task-job-list {
8 changes: 7 additions & 1 deletion cvat-ui/src/containers/task-page/details.tsx
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@ interface StateToProps {
activeInference: ActiveInference | null;
installedGit: boolean;
projectSubsets: string[];
dumpers: any[];
user: any;
}

interface DispatchToProps {
@@ -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 ?
@@ -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}
19 changes: 19 additions & 0 deletions cvat-ui/src/utils/git-utils.ts
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ interface ReposData {
error: string | null;
};
format: string
lfs: boolean
}

function waitForClone(cloneResponse: any): Promise<void> {
@@ -157,6 +158,7 @@ export async function getReposData(tid: number): Promise<ReposData | null> {
error: response.status.error,
},
format: response.format,
lfs: response.lfs,
};
}

@@ -193,3 +195,20 @@ export function syncRepos(tid: number): Promise<void> {
});
});
}

export async function changeRepo(taskId: number, type: string, value: any): Promise<void> {
return new Promise((resolve, reject): void => {
core.server
.request(`${baseURL}/git/repository/${taskId}`, {
method: 'PATCH',
data: JSON.stringify({
type,
value,
}),
})
.then(resolve)
.catch((error: any): void => {
reject(error);
});
});
}
Loading

0 comments on commit 49e1596

Please sign in to comment.