Skip to content

Commit

Permalink
Batch of fixes (#2031)
Browse files Browse the repository at this point in the history
  • Loading branch information
ActiveChooN authored Aug 19, 2020
1 parent 9c4e717 commit 582e23b
Show file tree
Hide file tree
Showing 16 changed files with 100 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Issue loading openvino models for semi-automatic and automatic annotation (<https://github.com/opencv/cvat/pull/1996>)
- Basic functions of CVAT works without activated nuclio dashboard
- Fixed error with creating task with labels with the same name (<https://github.com/opencv/cvat/pull/2031>)

### Security
-
Expand Down
2 changes: 1 addition & 1 deletion 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.7.1",
"version": "1.7.2",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
10 changes: 6 additions & 4 deletions cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,12 @@ function createTask(): AnyAction {
return action;
}

function createTaskSuccess(): AnyAction {
function createTaskSuccess(taskId: number): AnyAction {
const action = {
type: TasksActionTypes.CREATE_TASK_SUCCESS,
payload: {},
payload: {
taskId,
},
};

return action;
Expand Down Expand Up @@ -433,10 +435,10 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {

dispatch(createTask());
try {
await taskInstance.save((status: string): void => {
const savedTask = await taskInstance.save((status: string): void => {
dispatch(createTaskUpdateStatus(status));
});
dispatch(createTaskSuccess());
dispatch(createTaskSuccess(savedTask.id));
} catch (error) {
dispatch(createTaskFailed(error));
}
Expand Down
20 changes: 17 additions & 3 deletions cvat-ui/src/components/create-task-page/create-task-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// SPDX-License-Identifier: MIT

import React from 'react';
import { RouteComponentProps } from 'react-router';
import { withRouter } from 'react-router-dom';
import { Row, Col } from 'antd/lib/grid';
import Alert from 'antd/lib/alert';
import Button from 'antd/lib/button';
Expand All @@ -26,6 +28,7 @@ export interface CreateTaskData {
interface Props {
onCreate: (data: CreateTaskData) => void;
status: string;
taskId: number | null;
installedGit: boolean;
}

Expand All @@ -48,22 +51,31 @@ const defaultState = {
},
};

export default class CreateTaskContent extends React.PureComponent<Props, State> {
class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps, State> {
private basicConfigurationComponent: any;
private advancedConfigurationComponent: any;
private fileManagerContainer: any;

public constructor(props: Props) {
public constructor(props: Props & RouteComponentProps) {
super(props);
this.state = { ...defaultState };
}

public componentDidUpdate(prevProps: Props): void {
const { status } = this.props;
const { status, history, taskId } = this.props;

if (status === 'CREATED' && prevProps.status !== 'CREATED') {
const btn = (
<Button
onClick={() => history.push(`/tasks/${taskId}`)}
>
Open task
</Button>
);

notification.info({
message: 'The task has been created',
btn,
});

this.basicConfigurationComponent.resetFields();
Expand Down Expand Up @@ -252,3 +264,5 @@ export default class CreateTaskContent extends React.PureComponent<Props, State>
);
}
}

export default withRouter(CreateTaskContent);
3 changes: 3 additions & 0 deletions cvat-ui/src/components/create-task-page/create-task-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ interface Props {
onCreate: (data: CreateTaskData) => void;
status: string;
error: string;
taskId: number | null;
installedGit: boolean;
}

export default function CreateTaskPage(props: Props): JSX.Element {
const {
error,
status,
taskId,
onCreate,
installedGit,
} = props;
Expand Down Expand Up @@ -66,6 +68,7 @@ export default function CreateTaskPage(props: Props): JSX.Element {
<Col md={20} lg={16} xl={14} xxl={9}>
<Text className='cvat-title'>Create a new task</Text>
<CreateTaskContent
taskId={taskId}
status={status}
onCreate={onCreate}
installedGit={installedGit}
Expand Down
26 changes: 20 additions & 6 deletions cvat-ui/src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT

import './styles.scss';
import React from 'react';
import React, { MouseEvent } from 'react';
import { RouteComponentProps } from 'react-router';
import { withRouter } from 'react-router-dom';
import { Row, Col } from 'antd/lib/grid';
Expand Down Expand Up @@ -174,8 +174,12 @@ function HeaderContainer(props: Props): JSX.Element {
className='cvat-header-button'
type='link'
value='tasks'
href='/tasks?page=1'
onClick={
(): void => props.history.push('/tasks?page=1')
(event: React.MouseEvent): void => {
event.preventDefault();
props.history.push('/tasks?page=1');
}
}
>
Tasks
Expand All @@ -184,8 +188,12 @@ function HeaderContainer(props: Props): JSX.Element {
className='cvat-header-button'
type='link'
value='models'
href='/models'
onClick={
(): void => props.history.push('/models')
(event: React.MouseEvent): void => {
event.preventDefault();
props.history.push('/models');
}
}
>
Models
Expand All @@ -195,8 +203,10 @@ function HeaderContainer(props: Props): JSX.Element {
<Button
className='cvat-header-button'
type='link'
href={`${serverHost}/analytics/app/kibana`}
onClick={
(): void => {
(event: React.MouseEvent): void => {
event.preventDefault();
// false positive
// eslint-disable-next-line
window.open(`${serverHost}/analytics/app/kibana`, '_blank');
Expand All @@ -211,8 +221,10 @@ function HeaderContainer(props: Props): JSX.Element {
<Button
className='cvat-header-button'
type='link'
href={GITHUB_URL}
onClick={
(): void => {
(event: React.MouseEvent): void => {
event.preventDefault();
// false positive
// eslint-disable-next-line security/detect-non-literal-fs-filename
window.open(GITHUB_URL, '_blank');
Expand All @@ -225,8 +237,10 @@ function HeaderContainer(props: Props): JSX.Element {
<Button
className='cvat-header-button'
type='link'
href={`${serverHost}/documentation/user_guide.html`}
onClick={
(): void => {
(event: React.MouseEvent): void => {
event.preventDefault();
// false positive
// eslint-disable-next-line
window.open(`${serverHost}/documentation/user_guide.html`, '_blank')
Expand Down
22 changes: 19 additions & 3 deletions cvat-ui/src/components/labels-editor/constructor-creator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,30 @@ import LabelForm from './label-form';
import { Label } from './common';

interface Props {
labelNames: string[];
onCreate: (label: Label | null) => void;
}

export default function ConstructorCreator(props: Props): JSX.Element {
const { onCreate } = props;
function compareProps(prevProps: Props, nextProps: Props): boolean {
if (prevProps.onCreate !== nextProps.onCreate) {
return false;
}
if (!(prevProps.labelNames.length === nextProps.labelNames.length
&& prevProps.labelNames.map((value, index) => value === nextProps.labelNames[index])
.reduce((prevValue, curValue) => prevValue && curValue, true)
)) {
return false;
}
return true;
}

function ConstructorCreator(props: Props): JSX.Element {
const { onCreate, labelNames } = props;
return (
<div className='cvat-label-constructor-creator'>
<LabelForm label={null} onSubmit={onCreate} />
<LabelForm label={null} onSubmit={onCreate} labelNames={labelNames} />
</div>
);
}

export default React.memo(ConstructorCreator, compareProps);
9 changes: 9 additions & 0 deletions cvat-ui/src/components/labels-editor/label-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export enum AttributeType {

type Props = FormComponentProps & {
label: Label | null;
labelNames?: string[];
onSubmit: (label: Label | null) => void;
};

Expand Down Expand Up @@ -384,6 +385,7 @@ class LabelForm extends React.PureComponent<Props, {}> {
const {
label,
form,
labelNames,
} = this.props;
const value = label ? label.name : '';
const locked = label ? label.id >= 0 : false;
Expand All @@ -399,6 +401,13 @@ class LabelForm extends React.PureComponent<Props, {}> {
}, {
pattern: patterns.validateAttributeName.pattern,
message: patterns.validateAttributeName.message,
}, {
validator:
async (_rule: any, labelName: string, callback: Function) => {
if (labelNames && labelNames.includes(labelName)) {
callback('Label name must be unique for the task');
}
},
}],
})(<Input disabled={locked} placeholder='Label name' />)}
</Form.Item>
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/components/labels-editor/labels-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export default class LabelsEditor
}

public render(): JSX.Element {
const { labels } = this.props;
const {
savedLabels,
unsavedLabels,
Expand Down Expand Up @@ -319,6 +320,7 @@ export default class LabelsEditor
constructorMode === ConstructorMode.CREATE
&& (
<ConstructorCreator
labelNames={labels.map((l) => l.name)}
onCreate={this.handleCreate}
/>
)
Expand Down
4 changes: 4 additions & 0 deletions cvat-ui/src/components/labels-editor/raw-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class RawViewer extends React.PureComponent<Props> {
if (!Array.isArray(parsed)) {
callback('Field is expected to be a JSON array');
}
const labelNames = parsed.map((label: Label) => label.name);
if (new Set(labelNames).size !== labelNames.length) {
callback('Label names must be unique for the task');
}

for (const label of parsed) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CreateTaskData } from 'components/create-task-page/create-task-content'
import { createTaskAsync } from 'actions/tasks-actions';

interface StateToProps {
taskId: number | null;
status: string;
error: string;
installedGit: boolean;
Expand Down
1 change: 1 addition & 0 deletions cvat-ui/src/reducers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface TasksState {
[tid: number]: boolean; // deleted (deleting if in dictionary)
};
creates: {
taskId: number | null;
status: string;
error: string;
};
Expand Down
4 changes: 4 additions & 0 deletions cvat-ui/src/reducers/tasks-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const defaultState: TasksState = {
loads: {},
deletes: {},
creates: {
taskId: null,
status: '',
error: '',
},
Expand Down Expand Up @@ -238,6 +239,7 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
activities: {
...state.activities,
creates: {
taskId: null,
status: '',
error: '',
},
Expand All @@ -259,12 +261,14 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
};
}
case TasksActionTypes.CREATE_TASK_SUCCESS: {
const { taskId } = action.payload;
return {
...state,
activities: {
...state.activities,
creates: {
...state.activities.creates,
taskId,
status: 'CREATED',
},
},
Expand Down
10 changes: 10 additions & 0 deletions cvat/apps/engine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def to_representation(self, instance):
class LabelSerializer(serializers.ModelSerializer):
attributes = AttributeSerializer(many=True, source='attributespec_set',
default=[])

class Meta:
model = models.Label
fields = ('id', 'name', 'attributes')
Expand Down Expand Up @@ -305,6 +306,15 @@ def update(self, instance, validated_data):
instance.save()
return instance

def validate_labels(self, value):
if not value:
raise serializers.ValidationError('Label set must not be empty')
label_names = [label['name'] for label in value]
if len(label_names) != len(set(label_names)):
raise serializers.ValidationError('All label names must be unique for the task')
return value


class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = models.Project
Expand Down
2 changes: 1 addition & 1 deletion tests/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Cypress.Commands.add('createAnnotationTask', (taksName='New annotation task',
cy.get('input[type="file"]').attachFile(image, { subjectType: 'drag-n-drop' });
cy.contains('button', 'Submit').click()
cy.contains('The task has been created', {timeout: '8000'})
cy.get('button[value="tasks"]').click()
cy.get('[value="tasks"]').click()
cy.url().should('include', '/tasks?page=')
})

Expand Down

0 comments on commit 582e23b

Please sign in to comment.