diff --git a/cvat-core/src/lambda-manager.ts b/cvat-core/src/lambda-manager.ts index 66733d7ed23..283a89d34ce 100644 --- a/cvat-core/src/lambda-manager.ts +++ b/cvat-core/src/lambda-manager.ts @@ -137,47 +137,58 @@ class LambdaManager { } if (requestID in this.listening) { - this.listening[requestID].onUpdate.push(callback); // already listening, avoid sending extra requests + this.listening[requestID].onUpdate.push(callback); return; } - const timeoutCallback = (): void => { - serverProxy.lambda.status(requestID).then((response) => { - const { status } = response; - if (requestID in this.listening) { - // check it was not cancelled - const { onUpdate } = this.listening[requestID]; - if ([RQStatus.QUEUED, RQStatus.STARTED].includes(status)) { - onUpdate.forEach((update) => update(status, response.progress || 0)); - this.listening[requestID].timeout = window - .setTimeout(timeoutCallback, status === RQStatus.QUEUED ? 30000 : 10000); - } else { - delete this.listening[requestID]; - if (status === RQStatus.FINISHED) { - onUpdate - .forEach((update) => update(status, response.progress || 100)); - } else { - onUpdate - .forEach((update) => update(status, response.progress || 0, response.exc_info || '')); - } - } - } - }).catch((error) => { + + const timeoutCallback = async (): Promise => { + let response = null; + try { + response = await serverProxy.lambda.status(requestID); + } catch (error: unknown) { if (requestID in this.listening) { - // check it was not cancelled + // check the request was not cancelled const { onUpdate } = this.listening[requestID]; - onUpdate - .forEach((update) => update( - RQStatus.UNKNOWN, - 0, - `Could not get a status of the request ${requestID}. ${error.toString()}`, - )); + onUpdate.forEach((update) => update( + RQStatus.UNKNOWN, + 0, + `Could not get a status of the request ${requestID}. ${error instanceof Error ? error.message : ''}`, + )); } - }).finally(() => { - if (requestID in this.listening) { - this.listening[requestID].timeout = null; + } + + if (!(requestID in this.listening)) { + // request was already canceled + return; + } + + const { status } = response; + const { onUpdate } = this.listening[requestID]; + if ([RQStatus.QUEUED, RQStatus.STARTED].includes(status)) { + onUpdate.forEach((update) => update(status, response.progress ?? 0)); + this.listening[requestID].timeout = window + .setTimeout(timeoutCallback, status === RQStatus.QUEUED ? 30000 : 10000); + } else { + delete this.listening[requestID]; + if (status === RQStatus.FINISHED) { + onUpdate.forEach((update) => update(status, response.progress ?? 100)); + } else if (status === RQStatus.FAILED) { + onUpdate.forEach((update) => update( + status, + response.progress ?? 0, + `The process has failed. ${response.exc_info}`, + )); + } else { + onUpdate.forEach((update) => update( + status, + response.progress ?? 0, + `Unexpected status received: ${status}.`, + )); } - }); + } + + this.listening[requestID].timeout = null; }; this.listening[requestID] = { diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index 37f2337c0e5..e3c1c0f01ae 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -171,26 +171,10 @@ async function chunkUpload(file: File, uploadConfig): Promise<{ uploadSentSize: }); } -function filterPythonTraceback(data: string): string { - if (typeof data === 'string' && data.trim().startsWith('Traceback')) { - const lastRow = data.split('\n').findLastIndex((str) => str.trim().length); - let errorText = `${data.split('\n').slice(lastRow, lastRow + 1)[0]}`; - if (errorText.includes('CvatDatasetNotFoundError')) { - errorText = errorText.replace(/.*CvatDatasetNotFoundError: /, ''); - } - return errorText; - } - - return data; -} - function generateError(errorData: AxiosError): ServerError { if (errorData.response) { if (errorData.response.status >= 500 && typeof errorData.response.data === 'string') { - return new ServerError( - filterPythonTraceback(errorData.response.data), - errorData.response.status, - ); + return new ServerError(errorData.response.data, errorData.response.status); } if (errorData.response.status >= 400 && errorData.response.data) { diff --git a/cvat-ui/src/actions/models-actions.ts b/cvat-ui/src/actions/models-actions.ts index 38a99181623..98cd80355cf 100644 --- a/cvat-ui/src/actions/models-actions.ts +++ b/cvat-ui/src/actions/models-actions.ts @@ -138,7 +138,7 @@ function listen(inferenceMeta: InferenceMeta, dispatch: (action: ModelsActions) error: message as string, id: requestID, }, - new Error(`Inference status for the task ${taskID} is ${status}. ${message}`), + new Error(message as string), ), ); diff --git a/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx b/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx index 8b2ecd50768..d0c6c248869 100644 --- a/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx +++ b/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { Row, Col } from 'antd/lib/grid'; -import { CloseOutlined, LoadingOutlined } from '@ant-design/icons'; +import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import Text from 'antd/lib/typography/Text'; import Progress from 'antd/lib/progress'; import Modal from 'antd/lib/modal'; @@ -56,7 +56,14 @@ export default function AutomaticAnnotationProgress(props: Props): JSX.Element | } if (activeInference.status === RQStatus.FAILED) { - return (<>Automatic annotation failed); + return ( + <> + Automatic annotation failed + + + + + ); } if (activeInference.status === RQStatus.UNKNOWN) { diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 5bc4becb223..13d167d72b2 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -885,7 +885,7 @@ export default function (state = defaultState, action: AnyAction): Notifications models: { ...state.errors.models, inferenceStatusFetching: { - message: `Fetching inference status for the [task ${taskID}](/tasks/${taskID})`, + message: `Automatic annotation for the [task ${taskID}](/tasks/${taskID})`, reason: action.payload.error, shouldLog: shouldLog(action.payload.error), }, diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 5b3845f8260..f1c0b426c35 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -29,7 +29,7 @@ from cvat.apps.dataset_manager.formats.utils import get_label_color from cvat.apps.engine.frame_provider import TaskFrameProvider -from cvat.apps.engine.utils import format_list, parse_exception_message +from cvat.apps.engine.utils import format_list from cvat.apps.engine import field_validation, models from cvat.apps.engine.cloud_provider import get_cloud_storage_instance, Credentials, Status from cvat.apps.engine.log import ServerLogManager @@ -3230,12 +3230,9 @@ def get_message(self, rq_job: RQJob) -> str: message = '' if RQJobStatus.STARTED == rq_job_status: - message = rq_job.meta.get(RQJobMetaField.STATUS, '') + message = rq_job.meta.get(RQJobMetaField.STATUS, "") elif RQJobStatus.FAILED == rq_job_status: - message = rq_job.meta.get( - RQJobMetaField.FORMATTED_EXCEPTION, - parse_exception_message(str(rq_job.exc_info or "Unknown error")), - ) + message = rq_job.meta.get(RQJobMetaField.FORMATTED_EXCEPTION, "Unknown error") return message diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index ac046b1d0b2..2493a3e4cde 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -3252,8 +3252,9 @@ def perform_destroy(self, instance): target.touch() def rq_exception_handler(rq_job, exc_type, exc_value, tb): - rq_job.meta[RQJobMetaField.FORMATTED_EXCEPTION] = "".join( + formatted_exception = "".join( traceback.format_exception_only(exc_type, exc_value)) + rq_job.meta[RQJobMetaField.FORMATTED_EXCEPTION] = parse_exception_message(formatted_exception) rq_job.save_meta() return True diff --git a/cvat/apps/lambda_manager/views.py b/cvat/apps/lambda_manager/views.py index 143537985fd..2082a787b4c 100644 --- a/cvat/apps/lambda_manager/views.py +++ b/cvat/apps/lambda_manager/views.py @@ -621,11 +621,15 @@ def to_dict(self): "enqueued": self.job.enqueued_at, "started": self.job.started_at, "ended": self.job.ended_at, - "exc_info": self.job.exc_info + "exc_info": None } + if dict_['status'] == rq.job.JobStatus.DEFERRED: dict_['status'] = rq.job.JobStatus.QUEUED.value + if dict_['status'] == rq.job.JobStatus.FAILED: + dict_['exc_info'] = self.job.meta.get(RQJobMetaField.FORMATTED_EXCEPTION, 'Unknown error') + return dict_ def get_task(self): @@ -1203,7 +1207,6 @@ def retrieve(self, request, pk): self.check_object_permissions(request, pk) queue = LambdaQueue() rq_job = queue.fetch_job(pk) - response_serializer = FunctionCallSerializer(rq_job.to_dict()) return response_serializer.data