Merge pull request #932 from opencv/bs/show_hidden_tasks

Ability to show hidden tasks by a user request
main
Nikita Manovich 6 years ago committed by GitHub
commit 719ad1a4e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -30,7 +30,7 @@ export enum TasksActionTypes {
UPDATE_TASK = 'UPDATE_TASK', UPDATE_TASK = 'UPDATE_TASK',
UPDATE_TASK_SUCCESS = 'UPDATE_TASK_SUCCESS', UPDATE_TASK_SUCCESS = 'UPDATE_TASK_SUCCESS',
UPDATE_TASK_FAILED = 'UPDATE_TASK_FAILED', UPDATE_TASK_FAILED = 'UPDATE_TASK_FAILED',
RESET_ERROR = 'RESET_ERROR', HIDE_EMPTY_TASKS = 'HIDE_EMPTY_TASKS',
} }
function getTasks(): AnyAction { function getTasks(): AnyAction {
@ -90,8 +90,7 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
return; return;
} }
const array = Array.from(result) const array = Array.from(result);
.filter((task: any) => task.jobs.length > 0);
const previews = []; const previews = [];
const promises = array const promises = array
.map((task): string => (task as any).frames.preview()); .map((task): string => (task as any).frames.preview());
@ -513,3 +512,14 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
} }
}; };
} }
export function hideEmptyTasks(hideEmpty: boolean): AnyAction {
const action = {
type: TasksActionTypes.HIDE_EMPTY_TASKS,
payload: {
hideEmpty,
},
};
return action;
}

@ -57,7 +57,7 @@ function HeaderContainer(props: Props): JSX.Element {
type='link' type='link'
value='tasks' value='tasks'
onClick={ onClick={
(): void => props.history.push('/tasks') (): void => props.history.push('/tasks?page=1')
} }
> >
Tasks Tasks

@ -21,6 +21,7 @@ export interface TaskItemProps {
taskInstance: any; taskInstance: any;
previewImage: string; previewImage: string;
deleted: boolean; deleted: boolean;
hidden: boolean;
activeInference: ActiveInference | null; activeInference: ActiveInference | null;
} }
@ -49,7 +50,8 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
return ( return (
<Col span={10}> <Col span={10}>
<Text strong>{`${id} ${name}`}</Text> <Text strong type='secondary'>{`#${id}: `}</Text>
<Text strong className='cvat-black-color'>{name}</Text>
<br /> <br />
{ owner { owner
&& ( && (
@ -180,13 +182,20 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
} }
public render(): JSX.Element { public render(): JSX.Element {
const { deleted } = this.props; const {
deleted,
hidden,
} = this.props;
const style = {}; const style = {};
if (deleted) { if (deleted) {
(style as any).pointerEvents = 'none'; (style as any).pointerEvents = 'none';
(style as any).opacity = 0.5; (style as any).opacity = 0.5;
} }
if (hidden) {
(style as any).display = 'none';
}
return ( return (
<Row className='cvat-tasks-list-item' type='flex' justify='center' align='top' style={{ ...style }}> <Row className='cvat-tasks-list-item' type='flex' justify='center' align='top' style={{ ...style }}>
{this.renderPreview()} {this.renderPreview()}

@ -4,8 +4,12 @@ import { withRouter } from 'react-router-dom';
import { import {
Spin, Spin,
Button,
message,
} from 'antd'; } from 'antd';
import Text from 'antd/lib/typography/Text';
import { import {
TasksQuery, TasksQuery,
} from '../../reducers/interfaces'; } from '../../reducers/interfaces';
@ -19,7 +23,9 @@ interface TasksPageProps {
gettingQuery: TasksQuery; gettingQuery: TasksQuery;
numberOfTasks: number; numberOfTasks: number;
numberOfVisibleTasks: number; numberOfVisibleTasks: number;
numberOfHiddenTasks: number;
onGetTasks: (gettingQuery: TasksQuery) => void; onGetTasks: (gettingQuery: TasksQuery) => void;
hideEmptyTasks: (hideEmpty: boolean) => void;
} }
function getSearchField(gettingQuery: TasksQuery): string { function getSearchField(gettingQuery: TasksQuery): string {
@ -43,6 +49,31 @@ function getSearchField(gettingQuery: TasksQuery): string {
return searchString.slice(0, -5); return searchString.slice(0, -5);
} }
function updateQuery(previousQuery: TasksQuery, searchString: string): TasksQuery {
const params = new URLSearchParams(searchString);
const query = { ...previousQuery };
for (const field of Object.keys(query)) {
if (params.has(field)) {
const value = params.get(field);
if (value) {
if (field === 'id' || field === 'page') {
if (Number.isInteger(+value)) {
query[field] = +value;
}
} else {
query[field] = value;
}
}
} else if (field === 'page') {
query[field] = 1;
} else {
query[field] = null;
}
}
return query;
}
class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteComponentProps> { class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteComponentProps> {
public componentDidMount(): void { public componentDidMount(): void {
const { const {
@ -50,36 +81,52 @@ class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteCompo
location, location,
onGetTasks, onGetTasks,
} = this.props; } = this.props;
const params = new URLSearchParams(location.search);
const query = { ...gettingQuery }; const query = updateQuery(gettingQuery, location.search);
for (const field of Object.keys(query)) { onGetTasks(query);
if (params.has(field)) { }
const value = params.get(field);
if (value) { public componentDidUpdate(prevProps: TasksPageProps & RouteComponentProps): void {
if (field === 'id' || field === 'page') { const {
if (Number.isInteger(+value)) { location,
query[field] = +value; gettingQuery,
} onGetTasks,
} else { numberOfHiddenTasks,
query[field] = value; hideEmptyTasks,
} } = this.props;
}
} else if (field === 'page') { if (prevProps.location.search !== location.search) {
query[field] = 1; // get new tasks if any query changes
} else { const query = updateQuery(gettingQuery, location.search);
query[field] = null; message.destroy();
} onGetTasks(query);
return;
} }
this.updateURL(query); if (numberOfHiddenTasks) {
onGetTasks(query); message.destroy();
message.info(
<>
<Text>
Some tasks have not been showed because they do not have any data.
</Text>
<Button
type='link'
onClick={(): void => {
hideEmptyTasks(false);
message.destroy();
}}
>
Show all
</Button>
</>, 7,
);
}
} }
private handleSearch = (value: string): void => { private handleSearch = (value: string): void => {
const { const {
gettingQuery, gettingQuery,
onGetTasks,
} = this.props; } = this.props;
const query = { ...gettingQuery }; const query = { ...gettingQuery };
@ -114,19 +161,19 @@ class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteCompo
} }
this.updateURL(query); this.updateURL(query);
onGetTasks(query);
}; };
private handlePagination = (page: number): void => { private handlePagination = (page: number): void => {
const { const {
gettingQuery, gettingQuery,
onGetTasks,
} = this.props; } = this.props;
const query = { ...gettingQuery };
// modify query object
const query = { ...gettingQuery };
query.page = page; query.page = page;
// update url according to new query object
this.updateURL(query); this.updateURL(query);
onGetTasks(query);
}; };
private updateURL(gettingQuery: TasksQuery): void { private updateURL(gettingQuery: TasksQuery): void {
@ -137,9 +184,16 @@ class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteCompo
queryString += `${field}=${gettingQuery[field]}&`; queryString += `${field}=${gettingQuery[field]}&`;
} }
} }
history.replace({
search: queryString.slice(0, -1), const oldQueryString = history.location.search;
}); if (oldQueryString !== queryString) {
history.push({
search: queryString.slice(0, -1),
});
// force update if any changes
this.forceUpdate();
}
} }
public render(): JSX.Element { public render(): JSX.Element {

@ -15,6 +15,7 @@ import {
interface StateToProps { interface StateToProps {
deleted: boolean; deleted: boolean;
hidden: boolean;
previewImage: string; previewImage: string;
taskInstance: any; taskInstance: any;
activeInference: ActiveInference | null; activeInference: ActiveInference | null;
@ -35,6 +36,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
const id = own.taskID; const id = own.taskID;
return { return {
hidden: state.tasks.hideEmpty && task.instance.jobs.length === 0,
deleted: deletes.byTask[id] ? deletes.byTask[id] === true : false, deleted: deletes.byTask[id] ? deletes.byTask[id] === true : false,
previewImage: task.preview, previewImage: task.preview,
taskInstance: task.instance, taskInstance: task.instance,

@ -2,23 +2,29 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
Task,
TasksQuery, TasksQuery,
CombinedState, CombinedState,
} from '../../reducers/interfaces'; } from '../../reducers/interfaces';
import TasksPageComponent from '../../components/tasks-page/tasks-page'; import TasksPageComponent from '../../components/tasks-page/tasks-page';
import { getTasksAsync } from '../../actions/tasks-actions'; import {
getTasksAsync,
hideEmptyTasks,
} from '../../actions/tasks-actions';
interface StateToProps { interface StateToProps {
tasksFetching: boolean; tasksFetching: boolean;
gettingQuery: TasksQuery; gettingQuery: TasksQuery;
numberOfTasks: number; numberOfTasks: number;
numberOfVisibleTasks: number; numberOfVisibleTasks: number;
numberOfHiddenTasks: number;
} }
interface DispatchToProps { interface DispatchToProps {
onGetTasks: (gettingQuery: TasksQuery) => void; onGetTasks: (gettingQuery: TasksQuery) => void;
hideEmptyTasks: (hideEmpty: boolean) => void;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -29,6 +35,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
gettingQuery: tasks.gettingQuery, gettingQuery: tasks.gettingQuery,
numberOfTasks: state.tasks.count, numberOfTasks: state.tasks.count,
numberOfVisibleTasks: state.tasks.current.length, numberOfVisibleTasks: state.tasks.current.length,
numberOfHiddenTasks: tasks.hideEmpty ? tasks.current
.filter((task: Task): boolean => !task.instance.jobs.length).length : 0,
}; };
} }
@ -37,6 +45,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
onGetTasks: (query: TasksQuery): void => { onGetTasks: (query: TasksQuery): void => {
dispatch(getTasksAsync(query)); dispatch(getTasksAsync(query));
}, },
hideEmptyTasks: (hideEmpty: boolean): void => {
dispatch(hideEmptyTasks(hideEmpty));
},
}; };
} }

@ -24,6 +24,7 @@ export interface Task {
export interface TasksState { export interface TasksState {
initialized: boolean; initialized: boolean;
fetching: boolean; fetching: boolean;
hideEmpty: boolean;
gettingQuery: TasksQuery; gettingQuery: TasksQuery;
count: number; count: number;
current: Task[]; current: Task[];

@ -7,6 +7,7 @@ import { TasksState, Task } from './interfaces';
const defaultState: TasksState = { const defaultState: TasksState = {
initialized: false, initialized: false,
fetching: false, fetching: false,
hideEmpty: false,
count: 0, count: 0,
current: [], current: [],
gettingQuery: { gettingQuery: {
@ -51,6 +52,10 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
}, },
initialized: false, initialized: false,
fetching: true, fetching: true,
hideEmpty: true,
count: 0,
current: [],
gettingQuery: { ...action.payload.query },
}; };
case TasksActionTypes.GET_TASKS_SUCCESS: { case TasksActionTypes.GET_TASKS_SUCCESS: {
const combinedWithPreviews = action.payload.array const combinedWithPreviews = action.payload.array
@ -73,9 +78,6 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
...state, ...state,
initialized: true, initialized: true,
fetching: false, fetching: false,
count: 0,
current: [],
gettingQuery: { ...action.payload.query },
}; };
case TasksActionTypes.DUMP_ANNOTATIONS: { case TasksActionTypes.DUMP_ANNOTATIONS: {
const { task } = action.payload; const { task } = action.payload;
@ -405,6 +407,12 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
}), }),
}; };
} }
case TasksActionTypes.HIDE_EMPTY_TASKS: {
return {
...state,
hideEmpty: action.payload.hideEmpty,
};
}
case AuthActionTypes.LOGOUT_SUCCESS: { case AuthActionTypes.LOGOUT_SUCCESS: {
return { return {
...defaultState, ...defaultState,

@ -48,7 +48,7 @@
} }
.cvat-black-color { .cvat-black-color {
color: black; color: #363435;
} }
.cvat-feedback-button { .cvat-feedback-button {

Loading…
Cancel
Save