Some client code refactoring (#3077)

* Some refactoring, fixed unnesasary rerenderss

* Refactored filter

* Refactored filter

* Removed extra call of queryString() method
main
Boris Sekachev 5 years ago committed by GitHub
parent fad8612405
commit b0b36312e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -65,8 +65,8 @@
} }
data.task_subsets = Array.from(subsetsSet); data.task_subsets = Array.from(subsetsSet);
} }
if (initialData.training_project) { if (typeof initialData.training_project === 'object') {
data.training_project = JSON.parse(JSON.stringify(initialData.training_project)); data.training_project = { ...initialData.training_project };
} }
Object.defineProperties( Object.defineProperties(
@ -222,21 +222,34 @@
subsets: { subsets: {
get: () => [...data.task_subsets], get: () => [...data.task_subsets],
}, },
/**
_internalData: { * Training project associated with this annotation project
get: () => data, * This is a simple object which contains
}, * keys like host, username, password, enabled, project_class
* @name trainingProject
training_project: { * @type {object}
get: () => data.training_project, * @memberof module:API.cvat.classes.Project
set: (training) => { * @readonly
if (training) { * @instance
data.training_project = JSON.parse(JSON.stringify(training)); */
trainingProject: {
get: () => {
if (typeof data.training_project === 'object') {
return { ...data.training_project };
}
return data.training_project;
},
set: (updatedProject) => {
if (typeof training === 'object') {
data.training_project = { ...updatedProject };
} else { } else {
data.training_project = training; data.training_project = updatedProject;
} }
}, },
}, },
_internalData: {
get: () => data,
},
}), }),
); );
} }
@ -278,33 +291,38 @@
}; };
Project.prototype.save.implementation = async function () { Project.prototype.save.implementation = async function () {
let trainingProject; const trainingProjectCopy = this.trainingProject;
if (this.training_project) {
trainingProject = JSON.parse(JSON.stringify(this.training_project));
}
if (typeof this.id !== 'undefined') { if (typeof this.id !== 'undefined') {
// project has been already created, need to update some data
const projectData = { const projectData = {
name: this.name, name: this.name,
assignee_id: this.assignee ? this.assignee.id : null, assignee_id: this.assignee ? this.assignee.id : null,
bug_tracker: this.bugTracker, bug_tracker: this.bugTracker,
labels: [...this._internalData.labels.map((el) => el.toJSON())], labels: [...this._internalData.labels.map((el) => el.toJSON())],
training_project: trainingProject,
}; };
if (trainingProjectCopy) {
projectData.training_project = trainingProjectCopy;
}
await serverProxy.projects.save(this.id, projectData); await serverProxy.projects.save(this.id, projectData);
return this; return this;
} }
// initial creating
const projectSpec = { const projectSpec = {
name: this.name, name: this.name,
labels: [...this.labels.map((el) => el.toJSON())], labels: [...this.labels.map((el) => el.toJSON())],
training_project: trainingProject,
}; };
if (this.bugTracker) { if (this.bugTracker) {
projectSpec.bug_tracker = this.bugTracker; projectSpec.bug_tracker = this.bugTracker;
} }
if (trainingProjectCopy) {
projectSpec.training_project = trainingProjectCopy;
}
const project = await serverProxy.projects.create(projectSpec); const project = await serverProxy.projects.create(projectSpec);
return new Project(project); return new Project(project);
}; };

@ -1068,7 +1068,10 @@ export function getJobAsync(tid: number, jid: number, initialFrame: number, init
setTimeout(updatePredictorStatus, 20 * 1000); setTimeout(updatePredictorStatus, 20 * 1000);
} }
}; };
updatePredictorStatus();
if (state.plugins.list.PREDICT && job.task.projectId !== null) {
updatePredictorStatus();
}
dispatch(changeFrameAsync(frameNumber, false)); dispatch(changeFrameAsync(frameNumber, false));
} catch (error) { } catch (error) {

@ -16,7 +16,7 @@ import SubmitReviewModal from 'components/annotation-page/review/submit-review-m
import StandardWorkspaceComponent from 'components/annotation-page/standard-workspace/standard-workspace'; import StandardWorkspaceComponent from 'components/annotation-page/standard-workspace/standard-workspace';
import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace'; import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace';
import TagAnnotationWorkspace from 'components/annotation-page/tag-annotation-workspace/tag-annotation-workspace'; import TagAnnotationWorkspace from 'components/annotation-page/tag-annotation-workspace/tag-annotation-workspace';
import FiltersModalContainer from 'containers/annotation-page/top-bar/filters-modal'; import FiltersModalComponent from 'components/annotation-page/top-bar/filters-modal';
import StatisticsModalContainer from 'containers/annotation-page/top-bar/statistics-modal'; import StatisticsModalContainer from 'containers/annotation-page/top-bar/statistics-modal';
import AnnotationTopBarContainer from 'containers/annotation-page/top-bar/top-bar'; import AnnotationTopBarContainer from 'containers/annotation-page/top-bar/top-bar';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
@ -131,7 +131,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element {
<ReviewAnnotationsWorkspace /> <ReviewAnnotationsWorkspace />
</Layout.Content> </Layout.Content>
)} )}
<FiltersModalContainer visible={false} /> <FiltersModalComponent />
<StatisticsModalContainer /> <StatisticsModalContainer />
<SubmitAnnotationsModal /> <SubmitAnnotationsModal />
<SubmitReviewModal /> <SubmitReviewModal />

@ -13,14 +13,12 @@ import GlobalHotKeys from 'utils/mousetrap-react';
function LabelsListComponent(): JSX.Element { function LabelsListComponent(): JSX.Element {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
annotation: { const listHeight = useSelector((state: CombinedState) => state.annotation.tabContentHeight);
job: { labels }, const activatedStateID = useSelector((state: CombinedState) => state.annotation.annotations.activatedStateID);
tabContentHeight: listHeight, const states = useSelector((state: CombinedState) => state.annotation.annotations.states);
annotations: { activatedStateID, states }, const keyMap = useSelector((state: CombinedState) => state.shortcuts.keyMap);
},
shortcuts: { keyMap },
} = useSelector((state: CombinedState) => state);
const labelIDs = labels.map((label: any): number => label.id); const labelIDs = labels.map((label: any): number => label.id);
const [keyToLabelMapping, setKeyToLabelMapping] = useState<Record<string, number>>( const [keyToLabelMapping, setKeyToLabelMapping] = useState<Record<string, number>>(

@ -23,19 +23,15 @@ const { FieldDropdown } = AntdWidgets;
const FILTERS_HISTORY = 'annotationFiltersHistory'; const FILTERS_HISTORY = 'annotationFiltersHistory';
interface Props {
visible: boolean;
}
interface StoredFilter { interface StoredFilter {
id: string; id: string;
logic: JsonLogicTree; logic: JsonLogicTree;
} }
export default function FiltersModalComponent(props: Props): JSX.Element { function FiltersModalComponent(): JSX.Element {
const { visible } = props; const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
const { labels } = useSelector((state: CombinedState) => state.annotation.job); const activeFilters = useSelector((state: CombinedState) => state.annotation.annotations.filters);
const { filters: activeFilters } = useSelector((state: CombinedState) => state.annotation.annotations); const visible = useSelector((state: CombinedState) => state.annotation.filtersPanelVisible);
const getConvertedInputType = (inputType: string): string => { const getConvertedInputType = (inputType: string): string => {
switch (inputType) { switch (inputType) {
@ -234,18 +230,23 @@ export default function FiltersModalComponent(props: Props): JSX.Element {
const menu = ( const menu = (
<Menu> <Menu>
{filters {filters
.filter((filter: StoredFilter) => {
const tree = QbUtils.loadFromJsonLogic(filter.logic, config);
return !!QbUtils.queryString(tree, config);
})
.map((filter: StoredFilter) => { .map((filter: StoredFilter) => {
// if a logic received from local storage does not correspond to current config
// which depends on label specification
// (it can be when history from another task with another specification or when label was removed)
// loadFromJsonLogic() prints a warning to console
// the are not ways to configure this behaviour
const tree = QbUtils.loadFromJsonLogic(filter.logic, config); const tree = QbUtils.loadFromJsonLogic(filter.logic, config);
return ( const queryString = QbUtils.queryString(tree, config);
<Menu.Item key={filter.id} onClick={() => setState({ tree, config })}> return { tree, queryString, filter };
{QbUtils.queryString(tree, config)} })
</Menu.Item> .filter(({ queryString }) => !!queryString)
); .map(({ filter, tree, queryString }) => (
})} <Menu.Item key={filter.id} onClick={() => setState({ tree, config })}>
{queryString}
</Menu.Item>
))}
</Menu> </Menu>
); );
@ -286,3 +287,5 @@ export default function FiltersModalComponent(props: Props): JSX.Element {
</Modal> </Modal>
); );
} }
export default React.memo(FiltersModalComponent);

@ -47,8 +47,8 @@ function RightGroup(props: Props): JSX.Element {
isTrainingActive, isTrainingActive,
showFilters, showFilters,
} = props; } = props;
predictor.annotationAmount = predictor.annotationAmount ? predictor.annotationAmount : 0; const annotationAmount = predictor.annotationAmount || 0;
predictor.mediaAmount = predictor.mediaAmount ? predictor.mediaAmount : 0; const mediaAmount = predictor.mediaAmount || 0;
const formattedScore = `${(predictor.projectScore * 100).toFixed(0)}%`; const formattedScore = `${(predictor.projectScore * 100).toFixed(0)}%`;
const predictorTooltip = ( const predictorTooltip = (
<div className='cvat-predictor-tooltip'> <div className='cvat-predictor-tooltip'>
@ -65,15 +65,15 @@ function RightGroup(props: Props): JSX.Element {
<br /> <br />
<span> <span>
Annotations amount: Annotations amount:
{predictor.annotationAmount} {annotationAmount}
</span> </span>
<br /> <br />
<span> <span>
Media amount: Media amount:
{predictor.mediaAmount} {mediaAmount}
</span> </span>
<br /> <br />
{predictor.annotationAmount > 0 ? ( {annotationAmount > 0 ? (
<span> <span>
Model mAP is Model mAP is
{' '} {' '}
@ -139,7 +139,7 @@ function RightGroup(props: Props): JSX.Element {
<Tooltip title={predictorTooltip}> <Tooltip title={predictorTooltip}>
<Icon component={BrainIcon} /> <Icon component={BrainIcon} />
</Tooltip> </Tooltip>
{predictor.annotationAmount ? `mAP ${formattedScore}` : 'not trained'} {annotationAmount ? `mAP ${formattedScore}` : 'not trained'}
</Button> </Button>
)} )}
<Button <Button

@ -154,26 +154,19 @@ export default function CreateProjectContent(): JSX.Element {
}, [newProjectId]); }, [newProjectId]);
const onSumbit = async (): Promise<void> => { const onSumbit = async (): Promise<void> => {
interface Project { let projectData: Record<string, any> = {};
[key: string]: any;
}
const projectData: Project = {};
if (nameFormRef.current && advancedFormRef.current) { if (nameFormRef.current && advancedFormRef.current) {
const basicValues = await nameFormRef.current.validateFields(); const basicValues = await nameFormRef.current.validateFields();
const advancedValues = await advancedFormRef.current.validateFields(); const advancedValues = await advancedFormRef.current.validateFields();
const adaptiveAutoAnnotationValues = await adaptiveAutoAnnotationFormRef.current?.validateFields(); const adaptiveAutoAnnotationValues = await adaptiveAutoAnnotationFormRef.current?.validateFields();
projectData.name = basicValues.name; projectData = {
projectData.training_project = null; ...projectData,
if (adaptiveAutoAnnotationValues) { ...advancedValues,
projectData.training_project = {}; name: basicValues.name,
for (const [field, value] of Object.entries(adaptiveAutoAnnotationValues)) { };
projectData.training_project[field] = value;
}
}
for (const [field, value] of Object.entries(advancedValues)) { if (adaptiveAutoAnnotationValues) {
projectData[field] = value; projectData.training_project = { ...adaptiveAutoAnnotationValues };
} }
} }

@ -2,13 +2,17 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import 'antd/dist/antd.css'; import React from 'react';
import { Redirect, Route, Switch } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { Col, Row } from 'antd/lib/grid'; import { Col, Row } from 'antd/lib/grid';
import Layout from 'antd/lib/layout'; import Layout from 'antd/lib/layout';
import Modal from 'antd/lib/modal'; import Modal from 'antd/lib/modal';
import notification from 'antd/lib/notification'; import notification from 'antd/lib/notification';
import Spin from 'antd/lib/spin'; import Spin from 'antd/lib/spin';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import 'antd/dist/antd.css';
import GlobalErrorBoundary from 'components/global-error-boundary/global-error-boundary'; import GlobalErrorBoundary from 'components/global-error-boundary/global-error-boundary';
import Header from 'components/header/header'; import Header from 'components/header/header';
import ResetPasswordPageConfirmComponent from 'components/reset-password-confirm-page/reset-password-confirm-page'; import ResetPasswordPageConfirmComponent from 'components/reset-password-confirm-page/reset-password-confirm-page';
@ -26,10 +30,7 @@ import AnnotationPageContainer from 'containers/annotation-page/annotation-page'
import LoginPageContainer from 'containers/login-page/login-page'; import LoginPageContainer from 'containers/login-page/login-page';
import RegisterPageContainer from 'containers/register-page/register-page'; import RegisterPageContainer from 'containers/register-page/register-page';
import getCore from 'cvat-core-wrapper'; import getCore from 'cvat-core-wrapper';
import React from 'react';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react'; import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Redirect, Route, Switch } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { NotificationsState } from 'reducers/interfaces'; import { NotificationsState } from 'reducers/interfaces';
import { customWaViewHit } from 'utils/enviroment'; import { customWaViewHit } from 'utils/enviroment';
import showPlatformNotification, { platformInfo, stopNotifications } from 'utils/platform-checker'; import showPlatformNotification, { platformInfo, stopNotifications } from 'utils/platform-checker';

@ -4,8 +4,9 @@
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import GlobalHotKeys from 'utils/mousetrap-react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import GlobalHotKeys from 'utils/mousetrap-react';
import { CombinedState } from 'reducers/interfaces'; import { CombinedState } from 'reducers/interfaces';
import './styles.scss'; import './styles.scss';

@ -1,26 +0,0 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import React from 'react';
import { connect } from 'react-redux';
import { CombinedState } from 'reducers/interfaces';
import FiltersModalComponent from 'components/annotation-page/top-bar/filters-modal';
interface StateToProps {
visible: boolean;
}
const mapStateToProps = (state: CombinedState): StateToProps => {
const {
annotation: { filtersPanelVisible: visible },
} = state;
return { visible };
};
function FiltersModalContainer(props: StateToProps): JSX.Element {
const { visible } = props;
return <FiltersModalComponent visible={visible} />;
}
export default connect(mapStateToProps, null)(FiltersModalContainer);

@ -2,6 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react';
import ReactDOM from 'react-dom';
import { connect, Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { getAboutAsync } from 'actions/about-actions'; import { getAboutAsync } from 'actions/about-actions';
import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions'; import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions';
import { getFormatsAsync } from 'actions/formats-actions'; import { getFormatsAsync } from 'actions/formats-actions';
@ -14,11 +19,7 @@ import CVATApplication from 'components/cvat-app';
import LayoutGrid from 'components/layout-grid/layout-grid'; import LayoutGrid from 'components/layout-grid/layout-grid';
import logger, { LogType } from 'cvat-logger'; import logger, { LogType } from 'cvat-logger';
import createCVATStore, { getCVATStore } from 'cvat-store'; import createCVATStore, { getCVATStore } from 'cvat-store';
import React from 'react';
import ReactDOM from 'react-dom';
import { KeyMap } from 'utils/mousetrap-react'; import { KeyMap } from 'utils/mousetrap-react';
import { connect, Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import createRootReducer from 'reducers/root-reducer'; import createRootReducer from 'reducers/root-reducer';
import { resetErrors, resetMessages } from './actions/notification-actions'; import { resetErrors, resetMessages } from './actions/notification-actions';
import { CombinedState, NotificationsState } from './reducers/interfaces'; import { CombinedState, NotificationsState } from './reducers/interfaces';

@ -116,6 +116,10 @@ const defaultState: AnnotationState = {
projectScore: 0, projectScore: 0,
fetching: false, fetching: false,
annotatedFrames: [], annotatedFrames: [],
timeRemaining: 0,
progress: 0,
annotationAmount: 0,
mediaAmount: 0,
}, },
workspace: Workspace.STANDARD, workspace: Workspace.STANDARD,
}; };

Loading…
Cancel
Save