From e8afdcce27c88f434020519fa48276f14c606df8 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 6 Nov 2024 09:58:15 +0200 Subject: [PATCH 1/6] Fixed: Cannot read properties of undefined (reading 'push') --- .../20241106_095739_sekachev.bs_fixed_exception_1.md | 4 ++++ cvat-core/src/requests-manager.ts | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md diff --git a/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md b/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md new file mode 100644 index 000000000000..5d0a310423a0 --- /dev/null +++ b/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md @@ -0,0 +1,4 @@ +### Fixed + +- Fixed issue 'Cannot read properties of undefined (reading 'push')' + () diff --git a/cvat-core/src/requests-manager.ts b/cvat-core/src/requests-manager.ts index 711073988955..6ea070135096 100644 --- a/cvat-core/src/requests-manager.ts +++ b/cvat-core/src/requests-manager.ts @@ -34,7 +34,7 @@ class RequestsManager { requestDelayIdx: number | null, request: Request | null, timeout: number | null; - promise?: Promise; + promise: Promise; }>; private requestStack: number[]; @@ -71,6 +71,7 @@ class RequestsManager { } return this.listening[requestID].promise; } + const promise = new Promise((resolve, reject) => { const timeoutCallback = async (): Promise => { // We make sure that no more than REQUESTS_COUNT requests are sent simultaneously @@ -131,6 +132,8 @@ class RequestsManager { message: `Could not get a status of the request ${requestID}. ${error.toString()}`, }))); } + + delete this.listening[requestID]; reject(error); } } @@ -144,14 +147,11 @@ class RequestsManager { timeout: window.setTimeout(timeoutCallback), request: initialRequest, requestDelayIdx: 0, + promise, }; } }); - this.listening[requestID] = { - ...this.listening[requestID], - promise, - }; return promise; } From e74144dbd3fef4c4c55556892874dba399ed3eaa Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 6 Nov 2024 10:00:17 +0200 Subject: [PATCH 2/6] Update 20241106_095739_sekachev.bs_fixed_exception_1.md --- changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md b/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md index 5d0a310423a0..6f9e5b219a29 100644 --- a/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md +++ b/changelog.d/20241106_095739_sekachev.bs_fixed_exception_1.md @@ -1,4 +1,4 @@ ### Fixed - Fixed issue 'Cannot read properties of undefined (reading 'push')' - () + () From 7790d9a457767da9049cb938c9ed8aae835a3d0b Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 6 Nov 2024 11:28:29 +0200 Subject: [PATCH 3/6] Run in microtask --- cvat-core/src/requests-manager.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/cvat-core/src/requests-manager.ts b/cvat-core/src/requests-manager.ts index 6ea070135096..800b577242c8 100644 --- a/cvat-core/src/requests-manager.ts +++ b/cvat-core/src/requests-manager.ts @@ -139,17 +139,20 @@ class RequestsManager { } }; - if (initialRequest?.status === RQStatus.FAILED) { - reject(new RequestError(initialRequest?.message)); - } else { - this.listening[requestID] = { - onUpdate: callback ? [callback] : [], - timeout: window.setTimeout(timeoutCallback), - request: initialRequest, - requestDelayIdx: 0, - promise, - }; - } + Promise.resolve().then(() => { + // running as microtask to make sure "promise" was initialized + if (initialRequest?.status === RQStatus.FAILED) { + reject(new RequestError(initialRequest?.message)); + } else { + this.listening[requestID] = { + onUpdate: callback ? [callback] : [], + timeout: window.setTimeout(timeoutCallback), + request: initialRequest, + requestDelayIdx: 0, + promise, + }; + } + }); }); return promise; From 3efcb719f20bb7e9888268ab6d00810bc408e5f9 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 6 Nov 2024 11:30:00 +0200 Subject: [PATCH 4/6] Fixed: Re-newed import/export request failed immediately if the previous failed --- ...06_112907_sekachev.bs_fixed_exception_2.md | 4 + cvat-ui/src/actions/export-actions.ts | 134 +++++++++-------- cvat-ui/src/actions/import-actions.ts | 135 ++++++++++-------- cvat-ui/src/actions/requests-actions.ts | 13 +- cvat-ui/src/cvat-core-wrapper.ts | 3 +- 5 files changed, 165 insertions(+), 124 deletions(-) create mode 100644 changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md diff --git a/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md b/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md new file mode 100644 index 000000000000..a52e39b575d5 --- /dev/null +++ b/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md @@ -0,0 +1,4 @@ +### Fixed + +- Re-newed import/export request failed immediately if the previous failed + () diff --git a/cvat-ui/src/actions/export-actions.ts b/cvat-ui/src/actions/export-actions.ts index db59a6315c6a..170a50c6dbcb 100644 --- a/cvat-ui/src/actions/export-actions.ts +++ b/cvat-ui/src/actions/export-actions.ts @@ -4,11 +4,12 @@ // SPDX-License-Identifier: MIT import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; - -import { Storage, ProjectOrTaskOrJob, Job } from 'cvat-core-wrapper'; import { - getInstanceType, RequestInstanceType, listen, RequestsActions, - shouldListenForProgress, + Storage, ProjectOrTaskOrJob, Job, getCore, StorageLocation, +} from 'cvat-core-wrapper'; +import { + getInstanceType, RequestInstanceType, listen, + RequestsActions, updateRequestProgress, } from './requests-actions'; export enum ExportActionTypes { @@ -24,6 +25,8 @@ export enum ExportActionTypes { EXPORT_BACKUP_FAILED = 'EXPORT_BACKUP_FAILED', } +const core = getCore(); + export const exportActions = { openExportDatasetModal: (instance: ProjectOrTaskOrJob) => ( createAction(ExportActionTypes.OPEN_EXPORT_DATASET_MODAL, { instance }) @@ -75,30 +78,9 @@ export const exportActions = { ), }; -export async function listenExportDatasetAsync( - rqID: string, - dispatch: (action: ExportActions | RequestsActions) => void, - params: { - instance: ProjectOrTaskOrJob | RequestInstanceType, - format: string, - saveImages: boolean, - }, -): Promise { - const { instance, format, saveImages } = params; - const resource = saveImages ? 'dataset' : 'annotations'; - - const instanceType = getInstanceType(instance); - try { - const result = await listen(rqID, dispatch); - const target = !result?.url ? 'cloudstorage' : 'local'; - dispatch(exportActions.exportDatasetSuccess( - instance, instanceType, format, resource, target, - )); - } catch (error) { - dispatch(exportActions.exportDatasetFailed(instance, instanceType, format, resource, error)); - } -} - +/** * + * Function is supposed to be used when a new dataset export request initiated by a user +** */ export const exportDatasetAsync = ( instance: ProjectOrTaskOrJob, format: string, @@ -106,21 +88,23 @@ export const exportDatasetAsync = ( useDefaultSettings: boolean, targetStorage: Storage, name?: string, -): ThunkAction => async (dispatch, getState) => { - const state = getState(); - +): ThunkAction => async (dispatch) => { const resource = saveImages ? 'dataset' : 'annotations'; const instanceType = getInstanceType(instance); try { const rqID = await instance.annotations .exportDataset(format, saveImages, useDefaultSettings, targetStorage, name); - if (shouldListenForProgress(rqID, state.requests)) { - await listenExportDatasetAsync(rqID, dispatch, { - instance, format, saveImages, + + if (rqID) { + await core.requests.listen(rqID, { + callback: (updatedRequest) => updateRequestProgress(updatedRequest, dispatch), }); - } - if (!rqID) { + const target = targetStorage.location === StorageLocation.LOCAL ? 'local' : 'cloudstorage'; + dispatch(exportActions.exportDatasetSuccess( + instance, instanceType, format, resource, target, + )); + } else { dispatch(exportActions.exportDatasetSuccess( instance, instanceType, format, resource, )); @@ -130,47 +114,79 @@ export const exportDatasetAsync = ( } }; -export async function listenExportBackupAsync( +/** * + * Function is supposed to be used when a new backup export request initiated by a user +** */ +export const exportBackupAsync = ( + instance: Exclude, + targetStorage: Storage, + useDefaultSetting: boolean, + fileName: string, +): ThunkAction => async (dispatch) => { + const instanceType = getInstanceType(instance) as 'project' | 'task'; + try { + const rqID = await instance.backup(targetStorage, useDefaultSetting, fileName); + if (rqID) { + await core.requests.listen(rqID, { + callback: (updatedRequest) => updateRequestProgress(updatedRequest, dispatch), + }); + const target = targetStorage.location === StorageLocation.LOCAL ? 'local' : 'cloudstorage'; + dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); + } else { + dispatch(exportActions.exportBackupSuccess(instance, instanceType)); + } + } catch (error) { + dispatch(exportActions.exportBackupFailed(instance, instanceType, error as Error)); + } +}; + +/** * + * Function is supposed to be used when application starts listening to existing dataset export request +** */ +export async function listenExportDatasetAsync( rqID: string, dispatch: (action: ExportActions | RequestsActions) => void, params: { - instance: Exclude | RequestInstanceType, + instance: ProjectOrTaskOrJob | RequestInstanceType, + format: string, + saveImages: boolean, }, ): Promise { - const { instance } = params; - const instanceType = getInstanceType(instance) as 'project' | 'task'; + const { instance, format, saveImages } = params; + const resource = saveImages ? 'dataset' : 'annotations'; + const instanceType = getInstanceType(instance); try { const result = await listen(rqID, dispatch); const target = !result?.url ? 'cloudstorage' : 'local'; - dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); + dispatch(exportActions.exportDatasetSuccess( + instance, instanceType, format, resource, target, + )); } catch (error) { - dispatch(exportActions.exportBackupFailed(instance, instanceType, error as Error)); + dispatch(exportActions.exportDatasetFailed(instance, instanceType, format, resource, error)); } } -export const exportBackupAsync = ( - instance: Exclude, - targetStorage: Storage, - useDefaultSetting: boolean, - fileName: string, -): ThunkAction => async (dispatch, getState) => { - const state = getState(); - +/** * + * Function is supposed to be used when application starts listening to existing backup export request +** */ +export async function listenExportBackupAsync( + rqID: string, + dispatch: (action: ExportActions | RequestsActions) => void, + params: { + instance: Exclude | RequestInstanceType, + }, +): Promise { + const { instance } = params; const instanceType = getInstanceType(instance) as 'project' | 'task'; try { - const rqID = await instance - .backup(targetStorage, useDefaultSetting, fileName); - if (shouldListenForProgress(rqID, state.requests)) { - await listenExportBackupAsync(rqID, dispatch, { instance }); - } - if (!rqID) { - dispatch(exportActions.exportBackupSuccess(instance, instanceType)); - } + const result = await listen(rqID, dispatch); + const target = !result?.url ? 'cloudstorage' : 'local'; + dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); } catch (error) { dispatch(exportActions.exportBackupFailed(instance, instanceType, error as Error)); } -}; +} export type ExportActions = ActionUnion; diff --git a/cvat-ui/src/actions/import-actions.ts b/cvat-ui/src/actions/import-actions.ts index e47db0b47818..6132af3e3a79 100644 --- a/cvat-ui/src/actions/import-actions.ts +++ b/cvat-ui/src/actions/import-actions.ts @@ -11,8 +11,8 @@ import { import { getProjectsAsync } from './projects-actions'; import { AnnotationActionTypes, fetchAnnotationsAsync } from './annotation-actions'; import { - getInstanceType, listen, RequestInstanceType, RequestsActions, - shouldListenForProgress, + getInstanceType, listen, RequestInstanceType, + RequestsActions, shouldListenForProgress, updateRequestProgress, } from './requests-actions'; const core = getCore(); @@ -69,25 +69,9 @@ export const importActions = { ), }; -export async function listenImportDatasetAsync( - rqID: string, - dispatch: (action: ImportActions | RequestsActions) => void, - params: { - instance: ProjectOrTaskOrJob | RequestInstanceType, - }, -): Promise { - const { instance } = params; - - const instanceType = getInstanceType(instance); - const resource = instanceType === 'project' ? 'dataset' : 'annotation'; - try { - await listen(rqID, dispatch); - dispatch(importActions.importDatasetSuccess(instance, resource)); - } catch (error) { - dispatch(importActions.importDatasetFailed(instance, resource, error)); - } -} - +/** * + * Function is supposed to be used when a new dataset import request initiated by a user +** */ export const importDatasetAsync = ( instance: ProjectOrTaskOrJob, format: string, @@ -100,40 +84,53 @@ export const importDatasetAsync = ( const instanceType = getInstanceType(instance); const resource = instanceType === 'project' ? 'dataset' : 'annotation'; + const listenForImport = (rqID: string) => core.requests.listen(rqID, { + callback: (updatedRequest) => updateRequestProgress(updatedRequest, dispatch), + }); + try { const state: CombinedState = getState(); if (instanceType === 'project') { dispatch(importActions.importDataset(instance, format)); - const rqID = await (instance as Project).annotations - .importDataset(format, useDefaultSettings, sourceStorage, file, { + const rqID = await (instance as Project).annotations.importDataset( + format, + useDefaultSettings, + sourceStorage, + file, + { convMaskToPoly, updateStatusCallback: (message: string, progress: number) => ( dispatch(importActions.importDatasetUpdateStatus( instance, Math.floor(progress * 100), message, )) ), - }); - if (shouldListenForProgress(rqID, state.requests)) { - await listen(rqID, dispatch); - } + }, + ); + + await listenForImport(rqID); } else if (instanceType === 'task') { dispatch(importActions.importDataset(instance, format)); - const rqID = await (instance as Task).annotations - .upload(format, useDefaultSettings, sourceStorage, file, { - convMaskToPoly, - }); - if (shouldListenForProgress(rqID, state.requests)) { - await listen(rqID, dispatch); - } + const rqID = await (instance as Task).annotations.upload( + format, + useDefaultSettings, + sourceStorage, + file, + { convMaskToPoly }, + ); + await listenForImport(rqID); } else { // job dispatch(importActions.importDataset(instance, format)); - const rqID = await (instance as Job).annotations - .upload(format, useDefaultSettings, sourceStorage, file, { - convMaskToPoly, - }); + const rqID = await (instance as Job).annotations.upload( + format, + useDefaultSettings, + sourceStorage, + file, + { convMaskToPoly }, + ); + if (shouldListenForProgress(rqID, state.requests)) { - await listen(rqID, dispatch); + await listenForImport(rqID); await (instance as Job).annotations.clear({ reload: true }); await (instance as Job).actions.clear(); @@ -163,6 +160,28 @@ export const importDatasetAsync = ( } ); +/** * + * Function is supposed to be used when a new backup import request initiated by a user +** */ +export const importBackupAsync = (instanceType: 'project' | 'task', storage: Storage, file: File | string): ThunkAction => ( + async (dispatch) => { + dispatch(importActions.importBackup()); + try { + const instanceClass = (instanceType === 'task') ? core.classes.Task : core.classes.Project; + const rqID = await instanceClass.restore(storage, file); + const result = await core.requests.listen(rqID, { + callback: (updatedRequest) => updateRequestProgress(updatedRequest, dispatch), + }); + dispatch(importActions.importBackupSuccess(result?.resultID as number, instanceType)); + } catch (error) { + dispatch(importActions.importBackupFailed(instanceType, error)); + } + } +); + +/** * + * Function is supposed to be used when application starts listening to existing backup import request +** */ export async function listenImportBackupAsync( rqID: string, dispatch: (action: ImportActions | RequestsActions) => void, @@ -171,32 +190,34 @@ export async function listenImportBackupAsync( }, ): Promise { const { instanceType } = params; - try { const result = await listen(rqID, dispatch); - - dispatch(importActions.importBackupSuccess(result?.resultID, instanceType)); + dispatch(importActions.importBackupSuccess(result?.resultID as number, instanceType)); } catch (error) { dispatch(importActions.importBackupFailed(instanceType, error)); } } -export const importBackupAsync = (instanceType: 'project' | 'task', storage: Storage, file: File | string): ThunkAction => ( - async (dispatch, getState) => { - const state: CombinedState = getState(); - - dispatch(importActions.importBackup()); +/** * + * Function is supposed to be used when application starts listening to existing dataset import request +** */ +export async function listenImportDatasetAsync( + rqID: string, + dispatch: (action: ImportActions | RequestsActions) => void, + params: { + instance: ProjectOrTaskOrJob | RequestInstanceType, + }, +): Promise { + const { instance } = params; - try { - const instanceClass = (instanceType === 'task') ? core.classes.Task : core.classes.Project; - const rqID = await instanceClass.restore(storage, file); - if (shouldListenForProgress(rqID, state.requests)) { - await listenImportBackupAsync(rqID, dispatch, { instanceType }); - } - } catch (error) { - dispatch(importActions.importBackupFailed(instanceType, error)); - } + const instanceType = getInstanceType(instance); + const resource = instanceType === 'project' ? 'dataset' : 'annotation'; + try { + await listen(rqID, dispatch); + dispatch(importActions.importDatasetSuccess(instance, resource)); + } catch (error) { + dispatch(importActions.importDatasetFailed(instance, resource, error)); } -); +} export type ImportActions = ActionUnion; diff --git a/cvat-ui/src/actions/requests-actions.ts b/cvat-ui/src/actions/requests-actions.ts index 1f3972746e7e..af611863a7a7 100644 --- a/cvat-ui/src/actions/requests-actions.ts +++ b/cvat-ui/src/actions/requests-actions.ts @@ -100,11 +100,10 @@ export function listen( dispatch: (action: RequestsActions) => void, ) : Promise { const { requests } = getStore().getState().requests; - return core.requests - .listen(requestID, { - callback: (updatedRequest) => { - updateRequestProgress(updatedRequest, dispatch); - }, - initialRequest: requests[requestID], - }); + return core.requests.listen(requestID, { + callback: (updatedRequest) => { + updateRequestProgress(updatedRequest, dispatch); + }, + initialRequest: requests[requestID], + }); } diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index 94b70373a1c7..52f71d6044bc 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -28,7 +28,7 @@ import { ServerError, RequestError } from 'cvat-core/src/exceptions'; import { ShapeType, LabelType, ModelKind, ModelProviders, ModelReturnType, DimensionType, JobType, - JobStage, JobState, RQStatus, + JobStage, JobState, RQStatus, StorageLocation, } from 'cvat-core/src/enums'; import { Storage, StorageData } from 'cvat-core/src/storage'; import Issue from 'cvat-core/src/issue'; @@ -109,6 +109,7 @@ export { Request, JobValidationLayout, TaskValidationLayout, + StorageLocation, }; export type { From c46caeec368697346f8fe3ce63a50d18fdb2fb32 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 7 Nov 2024 10:59:17 +0200 Subject: [PATCH 5/6] Applied comments --- ...06_112907_sekachev.bs_fixed_exception_2.md | 2 +- cvat-ui/src/actions/export-actions.ts | 12 +++---- cvat-ui/src/actions/import-actions.ts | 36 ++++++++----------- cvat-ui/src/actions/requests-actions.ts | 13 ++----- cvat-ui/src/reducers/notifications-reducer.ts | 10 +++--- 5 files changed, 29 insertions(+), 44 deletions(-) diff --git a/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md b/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md index a52e39b575d5..52b5d05a54cb 100644 --- a/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md +++ b/changelog.d/20241106_112907_sekachev.bs_fixed_exception_2.md @@ -1,4 +1,4 @@ ### Fixed - Re-newed import/export request failed immediately if the previous failed - () + () diff --git a/cvat-ui/src/actions/export-actions.ts b/cvat-ui/src/actions/export-actions.ts index 170a50c6dbcb..6872e4f853c0 100644 --- a/cvat-ui/src/actions/export-actions.ts +++ b/cvat-ui/src/actions/export-actions.ts @@ -39,7 +39,7 @@ export const exportActions = { instanceType: 'project' | 'task' | 'job', format: string, resource: 'dataset' | 'annotations', - target?: 'local' | 'cloudstorage', + target?: StorageLocation, ) => ( createAction(ExportActionTypes.EXPORT_DATASET_SUCCESS, { instance, @@ -70,7 +70,7 @@ export const exportActions = { closeExportBackupModal: (instance: ProjectOrTaskOrJob) => ( createAction(ExportActionTypes.CLOSE_EXPORT_BACKUP_MODAL, { instance }) ), - exportBackupSuccess: (instance: Exclude | RequestInstanceType, instanceType: 'task' | 'project', target?: 'local' | 'cloudstorage') => ( + exportBackupSuccess: (instance: Exclude | RequestInstanceType, instanceType: 'task' | 'project', target?: StorageLocation) => ( createAction(ExportActionTypes.EXPORT_BACKUP_SUCCESS, { instance, instanceType, target }) ), exportBackupFailed: (instance: Exclude | RequestInstanceType, instanceType: 'task' | 'project', error: any) => ( @@ -100,7 +100,7 @@ export const exportDatasetAsync = ( await core.requests.listen(rqID, { callback: (updatedRequest) => updateRequestProgress(updatedRequest, dispatch), }); - const target = targetStorage.location === StorageLocation.LOCAL ? 'local' : 'cloudstorage'; + const target = targetStorage.location; dispatch(exportActions.exportDatasetSuccess( instance, instanceType, format, resource, target, )); @@ -130,7 +130,7 @@ export const exportBackupAsync = ( await core.requests.listen(rqID, { callback: (updatedRequest) => updateRequestProgress(updatedRequest, dispatch), }); - const target = targetStorage.location === StorageLocation.LOCAL ? 'local' : 'cloudstorage'; + const target = targetStorage.location; dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); } else { dispatch(exportActions.exportBackupSuccess(instance, instanceType)); @@ -158,7 +158,7 @@ export async function listenExportDatasetAsync( const instanceType = getInstanceType(instance); try { const result = await listen(rqID, dispatch); - const target = !result?.url ? 'cloudstorage' : 'local'; + const target = !result?.url ? StorageLocation.CLOUD_STORAGE : StorageLocation.LOCAL; dispatch(exportActions.exportDatasetSuccess( instance, instanceType, format, resource, target, )); @@ -182,7 +182,7 @@ export async function listenExportBackupAsync( try { const result = await listen(rqID, dispatch); - const target = !result?.url ? 'cloudstorage' : 'local'; + const target = !result?.url ? StorageLocation.CLOUD_STORAGE : StorageLocation.LOCAL; dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); } catch (error) { dispatch(exportActions.exportBackupFailed(instance, instanceType, error as Error)); diff --git a/cvat-ui/src/actions/import-actions.ts b/cvat-ui/src/actions/import-actions.ts index 6132af3e3a79..d7e3a548bb3b 100644 --- a/cvat-ui/src/actions/import-actions.ts +++ b/cvat-ui/src/actions/import-actions.ts @@ -4,7 +4,6 @@ // SPDX-License-Identifier: MIT import { createAction, ActionUnion, ThunkAction } from 'utils/redux'; -import { CombinedState } from 'reducers'; import { getCore, Storage, Job, Task, Project, ProjectOrTaskOrJob, } from 'cvat-core-wrapper'; @@ -12,7 +11,7 @@ import { getProjectsAsync } from './projects-actions'; import { AnnotationActionTypes, fetchAnnotationsAsync } from './annotation-actions'; import { getInstanceType, listen, RequestInstanceType, - RequestsActions, shouldListenForProgress, updateRequestProgress, + RequestsActions, updateRequestProgress, } from './requests-actions'; const core = getCore(); @@ -89,8 +88,6 @@ export const importDatasetAsync = ( }); try { - const state: CombinedState = getState(); - if (instanceType === 'project') { dispatch(importActions.importDataset(instance, format)); const rqID = await (instance as Project).annotations.importDataset( @@ -129,23 +126,20 @@ export const importDatasetAsync = ( { convMaskToPoly }, ); - if (shouldListenForProgress(rqID, state.requests)) { - await listenForImport(rqID); - - await (instance as Job).annotations.clear({ reload: true }); - await (instance as Job).actions.clear(); - - // first set empty objects list - // to escape some problems in canvas when shape with the same - // clientID has different type (polygon, rectangle) for example - dispatch({ type: AnnotationActionTypes.UPLOAD_JOB_ANNOTATIONS_SUCCESS }); - - const relevantInstance = getState().annotation.job.instance; - if (relevantInstance && relevantInstance.id === instance.id) { - setTimeout(() => { - dispatch(fetchAnnotationsAsync()); - }); - } + await listenForImport(rqID); + await (instance as Job).annotations.clear({ reload: true }); + await (instance as Job).actions.clear(); + + // first set empty objects list + // to escape some problems in canvas when shape with the same + // clientID has different type (polygon, rectangle) for example + dispatch({ type: AnnotationActionTypes.UPLOAD_JOB_ANNOTATIONS_SUCCESS }); + + const relevantInstance = getState().annotation.job.instance; + if (relevantInstance && relevantInstance.id === instance.id) { + setTimeout(() => { + dispatch(fetchAnnotationsAsync()); + }); } } } catch (error) { diff --git a/cvat-ui/src/actions/requests-actions.ts b/cvat-ui/src/actions/requests-actions.ts index af611863a7a7..f0e2d2f6adc1 100644 --- a/cvat-ui/src/actions/requests-actions.ts +++ b/cvat-ui/src/actions/requests-actions.ts @@ -3,10 +3,8 @@ // SPDX-License-Identifier: MIT import { ActionUnion, createAction } from 'utils/redux'; -import { CombinedState, RequestsQuery, RequestsState } from 'reducers'; -import { - Request, ProjectOrTaskOrJob, getCore, RQStatus, -} from 'cvat-core-wrapper'; +import { CombinedState, RequestsQuery } from 'reducers'; +import { Request, ProjectOrTaskOrJob, getCore } from 'cvat-core-wrapper'; import { Store } from 'redux'; import { getCVATStore } from 'cvat-store'; @@ -88,13 +86,6 @@ export function updateRequestProgress(request: Request, dispatch: (action: Reque ); } -export function shouldListenForProgress(rqID: string | void, state: RequestsState): boolean { - return ( - typeof rqID === 'string' && - (!state.requests[rqID] || [RQStatus.FINISHED, RQStatus.FAILED].includes(state.requests[rqID]?.status)) - ); -} - export function listen( requestID: string, dispatch: (action: RequestsActions) => void, diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 5bc4becb223e..6fc44fc5c8d9 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -5,7 +5,7 @@ import { AnyAction } from 'redux'; -import { ServerError, RequestError } from 'cvat-core-wrapper'; +import { ServerError, RequestError, StorageLocation } from 'cvat-core-wrapper'; import { AuthActionTypes } from 'actions/auth-actions'; import { FormatsActionTypes } from 'actions/formats-actions'; import { ModelsActionTypes } from 'actions/models-actions'; @@ -546,9 +546,9 @@ export default function (state = defaultState, action: AnyAction): Notifications instance, instanceType, resource, target, } = action.payload; let description = `Export ${resource} for ${instanceType} ${instance.id} is finished. `; - if (target === 'local') { + if (target === StorageLocation.LOCAL) { description += 'You can [download it here](/requests).'; - } else if (target === 'cloudstorage') { + } else if (target === StorageLocation.CLOUD_STORAGE) { description = `Export ${resource} for ${instanceType} ${instance.id} has been uploaded to cloud storage.`; } @@ -590,9 +590,9 @@ export default function (state = defaultState, action: AnyAction): Notifications instance, instanceType, target, } = action.payload; let description = `Backup for the ${instanceType} ${instance.id} is finished. `; - if (target === 'local') { + if (target === StorageLocation.LOCAL) { description += 'You can [download it here](/requests).'; - } else if (target === 'cloudstorage') { + } else if (target === StorageLocation.CLOUD_STORAGE) { description = `Backup for the ${instanceType} ${instance.id} has been uploaded to cloud storage.`; } From 08a85e6694e07d7d48c022ac1d7bbcb14942b867 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 7 Nov 2024 11:08:12 +0200 Subject: [PATCH 6/6] Do not listen to finished requests --- cvat-ui/src/actions/requests-async-actions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cvat-ui/src/actions/requests-async-actions.ts b/cvat-ui/src/actions/requests-async-actions.ts index 06a137eafd28..86151cbd076e 100644 --- a/cvat-ui/src/actions/requests-async-actions.ts +++ b/cvat-ui/src/actions/requests-async-actions.ts @@ -37,12 +37,14 @@ export function getRequestsAsync(query: RequestsQuery): ThunkAction { .forEach((request: Request): void => { const { id: rqID, + status, operation: { type, target, format, taskID, projectID, jobID, }, } = request; - if (state.requests.requests[rqID]) { + const isRequestFinished = [RQStatus.FINISHED, RQStatus.FAILED].includes(status); + if (state.requests.requests[rqID] || isRequestFinished) { return; }