Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue deleting #3952

Merged
merged 8 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Player option: Smooth image when zoom-in, enabled by default (<https://github.com/openvinotoolkit/cvat/pull/3933>)
- Google Cloud Storage support in UI (<https://github.com/openvinotoolkit/cvat/pull/3919>)
- Add project tasks paginations (<https://github.com/openvinotoolkit/cvat/pull/3910>)
- Add remove issue button (<https://github.com/openvinotoolkit/cvat/pull/3952>)

### Changed
- TDB
Expand Down
24 changes: 23 additions & 1 deletion cvat-core/src/issue.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -241,6 +241,21 @@ class Issue {
return result;
}

/**
* The method deletes the issue
* Deletes local or server-saved issues
* @method delete
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async delete() {
await PluginRegistry.apiWrapper.call(this, Issue.prototype.delete);
}

serialize() {
const { comments } = this;
const data = {
Expand Down Expand Up @@ -332,4 +347,11 @@ Issue.prototype.reopen.implementation = async function () {
}
};

Issue.prototype.delete.implementation = async function () {
const { id } = this;
if (id >= 0) {
await serverProxy.issues.delete(id);
}
};

module.exports = Issue;
10 changes: 9 additions & 1 deletion cvat-core/src/review.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -250,6 +250,10 @@ class Review {
return result;
}

async deleteIssue(issueId) {
await PluginRegistry.apiWrapper.call(this, Review.prototype.deleteIssue, issueId);
}

/**
* Method submits local review to the server
* @method submit
Expand Down Expand Up @@ -394,4 +398,8 @@ Review.prototype.submit.implementation = async function () {
}
};

Review.prototype.deleteIssue.implementation = function (issueId) {
this.__internal.issue_set = this.__internal.issue_set.filter((issue) => issue.id !== issueId);
};

module.exports = Review;
11 changes: 11 additions & 0 deletions cvat-core/src/server-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,16 @@
return response.data;
}

async function deleteIssue(issueID) {
const { backendAPI } = config;

try {
await Axios.delete(`${backendAPI}/issues/${issueID}`);
} catch (errorData) {
throw generateError(errorData);
}
}

async function saveJob(id, jobData) {
const { backendAPI } = config;

Expand Down Expand Up @@ -1413,6 +1423,7 @@
issues: {
value: Object.freeze({
update: updateIssue,
delete: deleteIssue,
}),
writable: false,
},
Expand Down
4 changes: 2 additions & 2 deletions cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.28.0",
"version": "1.28.1",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
34 changes: 34 additions & 0 deletions cvat-ui/src/actions/review-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ export enum ReviewActionTypes {
COMMENT_ISSUE = 'COMMENT_ISSUE',
COMMENT_ISSUE_SUCCESS = 'COMMENT_ISSUE_SUCCESS',
COMMENT_ISSUE_FAILED = 'COMMENT_ISSUE_FAILED',
REMOVE_ISSUE_SUCCESS = 'REMOVE_ISSUE_SUCCESS',
REMOVE_ISSUE_FAILED = 'REMOVE_ISSUE_FAILED',
SUBMIT_REVIEW = 'SUBMIT_REVIEW',
SUBMIT_REVIEW_SUCCESS = 'SUBMIT_REVIEW_SUCCESS',
SUBMIT_REVIEW_FAILED = 'SUBMIT_REVIEW_FAILED',
SWITCH_ISSUES_HIDDEN_FLAG = 'SWITCH_ISSUES_HIDDEN_FLAG',
SWITCH_RESOLVED_ISSUES_HIDDEN_FLAG = 'SWITCH_RESOLVED_ISSUES_HIDDEN_FLAG',
}

export const reviewActions = {
Expand Down Expand Up @@ -57,7 +60,14 @@ export const reviewActions = {
submitReview: (reviewId: number) => createAction(ReviewActionTypes.SUBMIT_REVIEW, { reviewId }),
submitReviewSuccess: () => createAction(ReviewActionTypes.SUBMIT_REVIEW_SUCCESS),
submitReviewFailed: (error: any) => createAction(ReviewActionTypes.SUBMIT_REVIEW_FAILED, { error }),
removeIssueSuccess: (issueId: number, frame: number) => (
createAction(ReviewActionTypes.REMOVE_ISSUE_SUCCESS, { issueId, frame })
),
removeIssueFailed: (error: any) => createAction(ReviewActionTypes.REMOVE_ISSUE_FAILED, { error }),
switchIssuesHiddenFlag: (hidden: boolean) => createAction(ReviewActionTypes.SWITCH_ISSUES_HIDDEN_FLAG, { hidden }),
switchIssuesHiddenResolvedFlag: (hidden: boolean) => (
createAction(ReviewActionTypes.SWITCH_RESOLVED_ISSUES_HIDDEN_FLAG, { hidden })
),
};

export type ReviewActions = ActionUnion<typeof reviewActions>;
Expand Down Expand Up @@ -204,3 +214,27 @@ export const submitReviewAsync = (review: any): ThunkAction => async (dispatch,
dispatch(reviewActions.submitReviewFailed(error));
}
};

export const deleteIssueAsync = (id: number): ThunkAction => async (dispatch, getState) => {
const state = getState();
const {
review: { frameIssues, activeReview },
annotation: {
player: {
frame: { number: frameNumber },
},
},
} = state;

try {
const [issue] = frameIssues.filter((_issue: any): boolean => _issue.id === id);
await issue.delete();
if (activeReview !== null) {
await activeReview.deleteIssue(id);
await activeReview.toLocalStorage();
}
dispatch(reviewActions.removeIssueSuccess(id, frameNumber));
} catch (error) {
dispatch(reviewActions.removeIssueFailed(error));
}
};
34 changes: 32 additions & 2 deletions cvat-ui/src/components/annotation-page/review/issue-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
//
// SPDX-License-Identifier: MIT

import React, { useState, useEffect, useRef } from 'react';
import React, {
useState,
useEffect,
useRef,
useCallback,
} from 'react';
import ReactDOM from 'react-dom';
import { useDispatch } from 'react-redux';
import Modal from 'antd/lib/modal';
import { Row, Col } from 'antd/lib/grid';
import { CloseOutlined } from '@ant-design/icons';
import Comment from 'antd/lib/comment';
Expand All @@ -13,6 +20,7 @@ import Button from 'antd/lib/button';
import Input from 'antd/lib/input';
import moment from 'moment';
import CVATTooltip from 'components/common/cvat-tooltip';
import { deleteIssueAsync } from 'actions/review-actions';

interface Props {
id: number;
Expand All @@ -32,6 +40,7 @@ interface Props {
export default function IssueDialog(props: Props): JSX.Element {
const ref = useRef<HTMLDivElement>(null);
const [currentText, setCurrentText] = useState<string>('');
const dispatch = useDispatch();
const {
comments,
id,
Expand All @@ -55,6 +64,22 @@ export default function IssueDialog(props: Props): JSX.Element {
}
}, [resolved]);

const onDeleteIssue = useCallback((): void => {
Modal.confirm({
title: `The issue${id >= 0 ? ` #${id}` : ''} will be deleted.`,
className: 'cvat-modal-confirm-remove-issue',
onOk: () => {
collapse();
dispatch(deleteIssueAsync(id));
},
okButtonProps: {
type: 'primary',
danger: true,
},
okText: 'Delete',
});
}, []);

const lines = comments.map(
(_comment: any): JSX.Element => {
const created = _comment.createdDate ? moment(_comment.createdDate) : moment(moment.now());
Expand Down Expand Up @@ -118,7 +143,12 @@ export default function IssueDialog(props: Props): JSX.Element {
/>
</Col>
</Row>
<Row className='cvat-issue-dialog-footer' justify='end'>
<Row className='cvat-issue-dialog-footer' justify='space-between'>
<Col>
<Button type='link' danger onClick={onDeleteIssue}>
Remove
</Button>
</Col>
<Col>
{currentText.length ? (
<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -29,14 +29,17 @@ export default function IssueAggregatorComponent(): JSX.Element | null {
const dispatch = useDispatch();
const [expandedIssue, setExpandedIssue] = useState<number | null>(null);
const frameIssues = useSelector((state: CombinedState): any[] => state.review.frameIssues);
const canvasInstance = useSelector((state: CombinedState): Canvas => state.annotation.canvas.instance);
const canvasInstance = useSelector((state: CombinedState) => state.annotation.canvas.instance);
const canvasIsReady = useSelector((state: CombinedState): boolean => state.annotation.canvas.ready);
const newIssuePosition = useSelector((state: CombinedState): number[] | null => state.review.newIssuePosition);
const issuesHidden = useSelector((state: CombinedState): any => state.review.issuesHidden);
const issuesResolvedHidden = useSelector((state: CombinedState): any => state.review.issuesResolvedHidden);
const issueFetching = useSelector((state: CombinedState): number | null => state.review.fetching.issueId);
const issueLabels: JSX.Element[] = [];
const issueDialogs: JSX.Element[] = [];

if (!(canvasInstance instanceof Canvas)) return null;

useEffect(() => {
scaleHandler(canvasInstance);
});
Expand Down Expand Up @@ -81,6 +84,7 @@ export default function IssueAggregatorComponent(): JSX.Element | null {
const { geometry } = canvasInstance;
for (const issue of frameIssues) {
if (issuesHidden) break;
if (issuesResolvedHidden && !!issue.resolvedDate) continue;
const issueResolved = !!issue.resolver;
const offset = 15;
const translated = issue.position.map((coord: number): number => coord + geometry.offset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
LeftOutlined, RightOutlined, EyeInvisibleFilled, EyeOutlined,
CheckCircleFilled, CheckCircleOutlined,
} from '@ant-design/icons';
import Alert from 'antd/lib/alert';
import { Row, Col } from 'antd/lib/grid';
Expand All @@ -22,6 +23,7 @@ export default function LabelsListComponent(): JSX.Element {
const issues = useSelector((state: CombinedState): any[] => state.review.issues);
const activeReview = useSelector((state: CombinedState): any => state.review.activeReview);
const issuesHidden = useSelector((state: CombinedState): any => state.review.issuesHidden);
const issuesResolvedHidden = useSelector((state: CombinedState): any => state.review.issuesResolvedHidden);
const combinedIssues = activeReview ? issues.concat(activeReview.issues) : issues;
const frames = combinedIssues.map((issue: any): number => issue.frame).sort((a: number, b: number) => +a - +b);
const nearestLeft = frames.filter((_frame: number): boolean => _frame < frame).reverse()[0];
Expand Down Expand Up @@ -62,8 +64,8 @@ export default function LabelsListComponent(): JSX.Element {
<RightOutlined className='cvat-issues-sidebar-next-frame' {...dinamicRightProps} />
</CVATTooltip>
</Col>
<Col offset={3}>
<CVATTooltip title='Show/hide all the issues'>
<Col offset={2}>
<CVATTooltip title='Show/hide all issues'>
{issuesHidden ? (
<EyeInvisibleFilled
className='cvat-issues-sidebar-hidden-issues'
Expand All @@ -77,6 +79,22 @@ export default function LabelsListComponent(): JSX.Element {
)}
</CVATTooltip>
</Col>
<Col offset={2}>
<CVATTooltip title='Show/hide resolved issues'>
{ issuesResolvedHidden ? (
<CheckCircleFilled
className='cvat-issues-sidebar-hidden-resolved-status'
onClick={() => dispatch(reviewActions.switchIssuesHiddenResolvedFlag(false))}
/>
) : (
<CheckCircleOutlined
className='cvat-issues-sidebar-hidden-resolved-status'
onClick={() => dispatch(reviewActions.switchIssuesHiddenResolvedFlag(true))}
/>

)}
</CVATTooltip>
</Col>
</Row>
</div>
<div className='cvat-objects-sidebar-issues-list'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,20 @@ function mapStateToProps(state: CombinedState): StateToProps {
opacity, colorBy, selectedOpacity, outlined, outlineColor, showBitmap, showProjections,
},
},
review: { frameIssues, issuesHidden },
review: { frameIssues, issuesHidden, issuesResolvedHidden },
shortcuts: { keyMap },
} = state;

const issues = frameIssues.filter((issue) => (
!issuesHidden && [Workspace.REVIEW_WORKSPACE, Workspace.STANDARD].includes(workspace) &&
!(!!issue.resolvedDate && issuesResolvedHidden)
));

return {
sidebarCollapsed,
canvasInstance,
jobInstance,
frameIssues:
issuesHidden || ![Workspace.REVIEW_WORKSPACE, Workspace.STANDARD].includes(workspace) ? null : frameIssues,
frameIssues: issues,
frameData,
frameAngle: frameAngles[frame - jobInstance.startFrame],
frameFetching,
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/reducers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ export interface NotificationsState {
reopeningIssue: null | ErrorState;
commentingIssue: null | ErrorState;
submittingReview: null | ErrorState;
deletingIssue: null | ErrorState;
};
predictor: {
prediction: null | ErrorState;
Expand Down Expand Up @@ -678,6 +679,7 @@ export interface ReviewState {
activeReview: any | null;
newIssuePosition: number[] | null;
issuesHidden: boolean;
issuesResolvedHidden: boolean;
fetching: {
reviewId: number | null;
issueId: number | null;
Expand Down
Loading