diff --git a/cvat-core/package.json b/cvat-core/package.json index dbff47de467..534eba0da08 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -1,6 +1,6 @@ { "name": "cvat-core", - "version": "3.1.0", + "version": "3.2.0", "description": "Part of Computer Vision Tool which presents an interface for client-side integration", "main": "babel.config.js", "scripts": { @@ -27,7 +27,7 @@ "eslint-plugin-security": "^1.4.0", "jest": "^24.8.0", "jest-junit": "^6.4.0", - "jsdoc": "^3.6.2", + "jsdoc": "^3.6.4", "webpack": "^4.31.0", "webpack-cli": "^3.3.2" }, diff --git a/cvat-core/src/api-implementation.js b/cvat-core/src/api-implementation.js index da83fcac47e..2beb9f123af 100644 --- a/cvat-core/src/api-implementation.js +++ b/cvat-core/src/api-implementation.js @@ -1,5 +1,5 @@ /* -* Copyright (C) 2019 Intel Corporation +* Copyright (C) 2019-2020 Intel Corporation * SPDX-License-Identifier: MIT */ @@ -12,6 +12,7 @@ (() => { const PluginRegistry = require('./plugins'); const serverProxy = require('./server-proxy'); + const lambdaManager = require('./lambda-manager'); const { isBoolean, isInteger, @@ -20,10 +21,7 @@ checkFilter, } = require('./common'); - const { - TaskStatus, - TaskMode, - } = require('./enums'); + const { TaskStatus, TaskMode } = require('./enums'); const User = require('./user'); const { AnnotationFormats } = require('./annotation-formats.js'); @@ -54,6 +52,12 @@ cvat.plugins.list.implementation = PluginRegistry.list; cvat.plugins.register.implementation = PluginRegistry.register.bind(cvat); + cvat.lambda.list.implementation = lambdaManager.list.bind(lambdaManager); + cvat.lambda.run.implementation = lambdaManager.run.bind(lambdaManager); + cvat.lambda.cancel.implementation = lambdaManager.cancel.bind(lambdaManager); + cvat.lambda.listen.implementation = lambdaManager.listen.bind(lambdaManager); + cvat.lambda.requests.implementation = lambdaManager.requests.bind(lambdaManager); + cvat.server.about.implementation = async () => { const result = await serverProxy.server.about(); return result; diff --git a/cvat-core/src/api.js b/cvat-core/src/api.js index 0e04c5c8fc6..5013e5d789a 100644 --- a/cvat-core/src/api.js +++ b/cvat-core/src/api.js @@ -20,6 +20,7 @@ function build() { const Statistics = require('./statistics'); const { Job, Task } = require('./session'); const { Attribute, Label } = require('./labels'); + const MLModel = require('./ml-model'); const { ShareFileType, @@ -30,6 +31,7 @@ function build() { ObjectShape, LogType, HistoryActions, + RQStatus, colors, } = require('./enums'); @@ -127,10 +129,10 @@ function build() { * @throws {module:API.cvat.exceptions.PluginError} * @throws {module:API.cvat.exceptions.ServerError} */ - async userAgreements() { - const result = await PluginRegistry - .apiWrapper(cvat.server.userAgreements); - return result; + async userAgreements() { + const result = await PluginRegistry + .apiWrapper(cvat.server.userAgreements); + return result; }, /** @@ -148,7 +150,15 @@ function build() { * @throws {module:API.cvat.exceptions.PluginError} * @throws {module:API.cvat.exceptions.ServerError} */ - async register(username, firstName, lastName, email, password1, password2, userConfirmations) { + async register( + username, + firstName, + lastName, + email, + password1, + password2, + userConfirmations, + ) { const result = await PluginRegistry .apiWrapper(cvat.server.register, username, firstName, lastName, email, password1, password2, userConfirmations); @@ -423,6 +433,108 @@ function build() { return result; }, }, + /** + * Namespace is used for serverless functions management (mainly related with DL models) + * @namespace lambda + * @memberof module:API.cvat + */ + /** + * @typedef {Object} RunLambdaArguments + * @property {boolean} [cleanup] flag that means shall + * we remove previous annotation or not + * @property {Object} [mapping] label mapping [model label -> task label] + * @property {number} [frame] annotate only a specific frame + * @global + */ + lambda: { + /** + * Method returns list of available serverless models + * @method list + * @async + * @memberof module:API.cvat.lambda + * @returns {module:API.cvat.classes.MLModel[]} + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async list() { + const result = await PluginRegistry + .apiWrapper(cvat.lambda.list); + return result; + }, + + /** + * Run serverless function on a specific task + * @method run + * @async + * @memberof module:API.cvat.lambda + * @param {module:API.cvat.classes.Task} task task to be annotated + * @param {module:API.cvat.classes.MLModel} model model used to get annotation + * @param {RunLambdaArguments} [arguments] extra arguments + * @returns {string} requestID + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + async run(task, model, args) { + const result = await PluginRegistry + .apiWrapper(cvat.lambda.run, task, model, args); + return result; + }, + + /** + * Cancel running of a serverless function for a specific task + * @method cancel + * @async + * @memberof module:API.cvat.lambda + * @param {string} requestID + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + async cancel(requestID) { + const result = await PluginRegistry + .apiWrapper(cvat.lambda.cancel, requestID); + return result; + }, + + /** + * @callback onRequestStatusChange + * @param {string} status + * @param {number} progress + * @param {string} [message] + * @global + */ + /** + * Listen for a specific request + * @method listen + * @async + * @memberof module:API.cvat.lambda + * @param {string} requestID + * @param {onRequestStatusChange} onChange + * @throws {module:API.cvat.exceptions.ArgumentError} + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async listen(requestID, onChange) { + const result = await PluginRegistry + .apiWrapper(cvat.lambda.listen, requestID, onChange); + return result; + }, + + /** + * Get active lambda requests + * @method requests + * @async + * @memberof module:API.cvat.lambda + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async requests() { + const result = await PluginRegistry + .apiWrapper(cvat.lambda.requests); + return result; + }, + }, /** * Namespace to working with logs * @namespace logger @@ -530,6 +642,7 @@ function build() { ObjectShape, LogType, HistoryActions, + RQStatus, colors, }, /** @@ -559,6 +672,7 @@ function build() { Label, Statistics, ObjectState, + MLModel, }, }; @@ -567,6 +681,7 @@ function build() { cvat.jobs = Object.freeze(cvat.jobs); cvat.users = Object.freeze(cvat.users); cvat.plugins = Object.freeze(cvat.plugins); + cvat.lambda = Object.freeze(cvat.lambda); cvat.client = Object.freeze(cvat.client); cvat.enums = Object.freeze(cvat.enums); diff --git a/cvat-core/src/enums.js b/cvat-core/src/enums.js index 1c91de235eb..84b52d88664 100644 --- a/cvat-core/src/enums.js +++ b/cvat-core/src/enums.js @@ -34,6 +34,26 @@ COMPLETED: 'completed', }); + /** + * List of RQ statuses + * @enum {string} + * @name RQStatus + * @memberof module:API.cvat.enums + * @property {string} QUEUED 'queued' + * @property {string} STARTED 'started' + * @property {string} FINISHED 'finished' + * @property {string} FAILED 'failed' + * @property {string} UNKNOWN 'unknown' + * @readonly + */ + const RQStatus = Object.freeze({ + QUEUED: 'queued', + STARTED: 'started', + FINISHED: 'finished', + FAILED: 'failed', + UNKNOWN: 'unknown', + }); + /** * Task modes * @enum {string} @@ -216,6 +236,18 @@ REMOVED_OBJECT: 'Removed object', }); + /** + * Enum string values. + * @name ModelType + * @memberof module:API.cvat.enums + * @enum {string} + */ + const ModelType = { + DETECTOR: 'detector', + INTERACTOR: 'interactor', + TRACKER: 'tracker', + }; + /** * Array of hex colors * @name colors @@ -239,7 +271,9 @@ ObjectType, ObjectShape, LogType, + ModelType, HistoryActions, + RQStatus, colors, }; })(); diff --git a/cvat-core/src/lambda-manager.js b/cvat-core/src/lambda-manager.js new file mode 100644 index 00000000000..800aedcad0c --- /dev/null +++ b/cvat-core/src/lambda-manager.js @@ -0,0 +1,119 @@ +/* +* Copyright (C) 2020 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +const serverProxy = require('./server-proxy'); +const { ArgumentError } = require('./exceptions'); +const { Task } = require('./session'); +const MLModel = require('./ml-model'); +const { RQStatus } = require('./enums'); + +class LambdaManager { + constructor() { + this.listening = {}; + } + + async list() { + const result = await serverProxy.lambda.list(); + const models = []; + + for (const model of result) { + models.push(new MLModel({ + id: model.id, + name: model.name, + description: model.description, + framework: model.framework, + labels: [...model.labels], + type: model.kind, + })); + } + + return models; + } + + async run(task, model, args) { + if (!(task instanceof Task)) { + throw new ArgumentError( + `Argument task is expected to be an instance of Task class, but got ${typeof (task)}`, + ); + } + + if (!(model instanceof MLModel)) { + throw new ArgumentError( + `Argument model is expected to be an instance of MLModel class, but got ${typeof (model)}`, + ); + } + + if (args && typeof (args) !== 'object') { + throw new ArgumentError( + `Argument args is expected to be an object, but got ${typeof (model)}`, + ); + } + + const body = {}; + body.cleanup = args ? (args.cleanup || false) : false; + body.mapping = args ? (args.mapping || {}) : {}; + body.task = task.id; + body.function = model.id; + + if (args && Number.isInteger(args.frame)) { + body.frame = args.frame; + } + + const result = await serverProxy.lambda.run(body); + return result.id; + } + + async requests() { + const result = await serverProxy.lambda.requests(); + return result.filter((request) => ['queued', 'started'].includes(request.status)); + } + + async cancel(requestID) { + if (typeof (requestID) !== 'string') { + throw new ArgumentError(`Request id argument is required to be a string. But got ${requestID}`); + } + + if (this.listening[requestID]) { + clearTimeout(this.listening[requestID].timeout); + delete this.listening[requestID]; + } + await serverProxy.lambda.cancel(requestID); + } + + async listen(requestID, onUpdate) { + const timeoutCallback = async () => { + try { + this.listening[requestID].timeout = null; + const response = await serverProxy.lambda.status(requestID); + + if (response.status === RQStatus.QUEUED || response.status === RQStatus.STARTED) { + onUpdate(response.status, response.progress || 0); + this.listening[requestID].timeout = setTimeout(timeoutCallback, 2000); + } else { + if (response.status === RQStatus.FINISHED) { + onUpdate(response.status, response.progress || 100); + } else { + onUpdate(response.status, response.progress || 0, response.exc_info || ''); + } + + delete this.listening[requestID]; + } + } catch (error) { + onUpdate(RQStatus.UNKNOWN, 0, `Could not get a status of the request ${requestID}. ${error.toString()}`); + } + }; + + this.listening[requestID] = { + onUpdate, + timeout: setTimeout(timeoutCallback, 2000), + }; + } +} + +module.exports = new LambdaManager(); diff --git a/cvat-core/src/ml-model.js b/cvat-core/src/ml-model.js new file mode 100644 index 00000000000..4169be8e206 --- /dev/null +++ b/cvat-core/src/ml-model.js @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2019-2020 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/** + * Class representing a machine learning model + * @memberof module:API.cvat.classes +*/ +class MLModel { + constructor(data) { + this._id = data.id; + this._name = data.name; + this._labels = data.labels; + this._framework = data.framework; + this._description = data.description; + this._type = data.type; + } + + /** + * @returns {string} + * @readonly + */ + get id() { + return this._id; + } + + /** + * @returns {string} + * @readonly + */ + get name() { + return this._name; + } + + /** + * @returns {string[]} + * @readonly + */ + get labels() { + if (Array.isArray(this._labels)) { + return [...this._labels]; + } + + return []; + } + + /** + * @returns {string} + * @readonly + */ + get framework() { + return this._framework; + } + + /** + * @returns {string} + * @readonly + */ + get description() { + return this._description; + } + + /** + * @returns {module:API.cvat.enums.ModelType} + * @readonly + */ + get type() { + return this._type; + } +} + +module.exports = MLModel; diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js index 5b685114e3a..1c1c637f374 100644 --- a/cvat-core/src/server-proxy.js +++ b/cvat-core/src/server-proxy.js @@ -162,7 +162,6 @@ response = await Axios.get(`${backendAPI}/restrictions/user-agreements`, { proxy: config.proxy, }); - } catch (errorData) { throw generateError(errorData); } @@ -170,7 +169,15 @@ return response.data; } - async function register(username, firstName, lastName, email, password1, password2, confirmations) { + async function register( + username, + firstName, + lastName, + email, + password1, + password2, + confirmations, + ) { let response = null; try { const data = JSON.stringify({ @@ -662,6 +669,78 @@ } } + async function getLambdaFunctions() { + const { backendAPI } = config; + + try { + const response = await Axios.get(`${backendAPI}/lambda/functions`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } + } + + async function runLambdaFunction(body) { + const { backendAPI } = config; + + try { + const response = await Axios.post(`${backendAPI}/lambda/requests`, + JSON.stringify(body), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + + return response.data; + } catch (errorData) { + throw generateError(errorData); + } + } + + async function getLambdaRequests() { + const { backendAPI } = config; + + try { + const response = await Axios.get(`${backendAPI}/lambda/requests`, { + proxy: config.proxy, + }); + + return response.data; + } catch (errorData) { + throw generateError(errorData); + } + } + + async function getRequestStatus(requestID) { + const { backendAPI } = config; + + try { + const response = await Axios.get(`${backendAPI}/lambda/requests/${requestID}`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } + } + + async function cancelLambdaRequest(requestId) { + const { backendAPI } = config; + + try { + await Axios.delete( + `${backendAPI}/lambda/requests/${requestId}`, { + method: 'DELETE', + }, + ); + } catch (errorData) { + throw generateError(errorData); + } + } + Object.defineProperties(this, Object.freeze({ server: { value: Object.freeze({ @@ -731,6 +810,17 @@ }), writable: false, }, + + lambda: { + value: Object.freeze({ + list: getLambdaFunctions, + status: getRequestStatus, + requests: getLambdaRequests, + run: runLambdaFunction, + cancel: cancelLambdaRequest, + }), + writable: false, + }, })); } } diff --git a/cvat-ui/src/actions/models-actions.ts b/cvat-ui/src/actions/models-actions.ts index 991920e2032..18d51822683 100644 --- a/cvat-ui/src/actions/models-actions.ts +++ b/cvat-ui/src/actions/models-actions.ts @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; -import { Model, ModelFiles, ActiveInference } from 'reducers/interfaces'; +import { Model, ActiveInference, RQStatus } from 'reducers/interfaces'; import getCore from 'cvat-core-wrapper'; export enum ModelsActionTypes { @@ -37,18 +37,6 @@ export const modelsActions = { error, }, ), - createModel: () => createAction(ModelsActionTypes.CREATE_MODEL), - createModelSuccess: () => createAction(ModelsActionTypes.CREATE_MODEL_SUCCESS), - createModelFailed: (error: any) => createAction( - ModelsActionTypes.CREATE_MODEL_FAILED, { - error, - }, - ), - createModelUpdateStatus: (status: string) => createAction( - ModelsActionTypes.CREATE_MODEL_STATUS_UPDATED, { - status, - }, - ), fetchMetaFailed: (error: any) => createAction(ModelsActionTypes.FETCH_META_FAILED, { error }), getInferenceStatusSuccess: (taskID: number, activeInference: ActiveInference) => createAction( ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS, { @@ -90,192 +78,59 @@ export const modelsActions = { export type ModelsActions = ActionUnion; const core = getCore(); -const baseURL = core.config.backendAPI.slice(0, -7); export function getModelsAsync(): ThunkAction { return async (dispatch): Promise => { dispatch(modelsActions.getModels()); - const models: Model[] = []; try { - const response = await core.server.request( - `${core.config.backendAPI}/lambda/functions`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }, - ); - - for (const model of response) { - if (model.kind === 'detector') { - models.push({ - id: model.id, - name: model.name, - description: model.description, - framework: model.framework, - labels: [...model.labels], - type: model.kind, - }); - } - } + const models = (await core.lambda.list()) + .filter((model: Model) => model.type === 'detector'); + dispatch(modelsActions.getModelsSuccess(models)); } catch (error) { dispatch(modelsActions.getModelsFailed(error)); return; } - - dispatch(modelsActions.getModelsSuccess(models)); }; } -export function createModelAsync(name: string, files: ModelFiles, global: boolean): ThunkAction { - return async (dispatch): Promise => { - async function checkCallback(id: string): Promise { - try { - const data = await core.server.request( - `${baseURL}/auto_annotation/check/${id}`, { - method: 'GET', - }, - ); - - switch (data.status) { - case 'failed': - dispatch(modelsActions.createModelFailed( - `Checking request has returned the "${data.status}" status. Message: ${data.error}`, - )); - break; - case 'unknown': - dispatch(modelsActions.createModelFailed( - `Checking request has returned the "${data.status}" status.`, - )); - break; - case 'finished': - dispatch(modelsActions.createModelSuccess()); - break; - default: - if ('progress' in data) { - modelsActions.createModelUpdateStatus(data.progress); - } - setTimeout(checkCallback.bind(null, id), 1000); - } - } catch (error) { - dispatch(modelsActions.createModelFailed(error)); - } - } - - dispatch(modelsActions.createModel()); - const data = new FormData(); - data.append('name', name); - data.append('storage', typeof files.bin === 'string' ? 'shared' : 'local'); - data.append('shared', global.toString()); - Object.keys(files).reduce((acc, key: string): FormData => { - acc.append(key, files[key]); - return acc; - }, data); - - try { - dispatch(modelsActions.createModelUpdateStatus('Request is beign sent..')); - const response = await core.server.request( - `${baseURL}/auto_annotation/create`, { - method: 'POST', - data, - contentType: false, - processData: false, - }, - ); - - dispatch(modelsActions.createModelUpdateStatus('Request is being processed..')); - setTimeout(checkCallback.bind(null, response.id), 1000); - } catch (error) { - dispatch(modelsActions.createModelFailed(error)); - } - }; -} interface InferenceMeta { - active: boolean; taskID: number; requestID: string; } -const timers: any = {}; - -async function timeoutCallback( - url: string, - taskID: number, +function listen( + inferenceMeta: InferenceMeta, dispatch: (action: ModelsActions) => void, -): Promise { - try { - delete timers[taskID]; - - const response = await core.server.request(url, { - method: 'GET', - }); - - const activeInference: ActiveInference = { - status: response.status, - progress: +response.progress || 0, - error: response.exc_info || '', - id: response.id, - }; - - - if (activeInference.status === 'unknown') { - dispatch(modelsActions.getInferenceStatusFailed( - taskID, - new Error( - `Inference status for the task ${taskID} is unknown.`, - ), - )); - - return; - } - - if (activeInference.status === 'failed') { +): void { + const { taskID, requestID } = inferenceMeta; + core.lambda.listen(requestID, (status: RQStatus, progress: number, message: string) => { + if (status === RQStatus.failed || status === RQStatus.unknown) { dispatch(modelsActions.getInferenceStatusFailed( taskID, new Error( - `Inference status for the task ${taskID} is failed. ${activeInference.error}`, + `Inference status for the task ${taskID} is ${status}. ${message}`, ), )); return; } - if (activeInference.status !== 'finished') { - timers[taskID] = setTimeout( - timeoutCallback.bind( - null, - url, - taskID, - dispatch, - ), 3000, - ); - } - - dispatch(modelsActions.getInferenceStatusSuccess(taskID, activeInference)); - } catch (error) { - dispatch(modelsActions.getInferenceStatusFailed(taskID, new Error( - `Server request for the task ${taskID} was failed`, - ))); - } -} - -function subscribe( - inferenceMeta: InferenceMeta, - dispatch: (action: ModelsActions) => void, -): void { - if (!(inferenceMeta.taskID in timers)) { - const requestURL = `${core.config.backendAPI}/lambda/requests/${inferenceMeta.requestID}`; - timers[inferenceMeta.taskID] = setTimeout( - timeoutCallback.bind( - null, - requestURL, - inferenceMeta.taskID, - dispatch, - ), - ); - } + dispatch(modelsActions.getInferenceStatusSuccess(taskID, { + status, + progress, + error: message, + id: requestID, + })); + }).catch((error: Error) => { + dispatch(modelsActions.getInferenceStatusFailed(taskID, { + status: 'unknown', + progress: 0, + error: error.toString(), + id: requestID, + })); + }); } export function getInferenceStatusAsync(): ThunkAction { @@ -285,21 +140,14 @@ export function getInferenceStatusAsync(): ThunkAction { }; try { - const response = await core.server.request( - `${core.config.backendAPI}/lambda/requests`, { - method: 'GET', - }, - ); - - response - .map((request: any): InferenceMeta => ({ + const requests = await core.lambda.requests(); + requests + .map((request: any): object => ({ taskID: +request.function.task, requestID: request.id, - active: request.progress < 100, })) - .filter((inferenceMeta: InferenceMeta): boolean => inferenceMeta.active) .forEach((inferenceMeta: InferenceMeta): void => { - subscribe(inferenceMeta, dispatchCallback); + listen(inferenceMeta, dispatchCallback); }); } catch (error) { dispatch(modelsActions.fetchMetaFailed(error)); @@ -313,26 +161,23 @@ export function startInferenceAsync( mapping: { [index: string]: string; }, - cleanOut: boolean, + cleanup: boolean, ): ThunkAction { return async (dispatch): Promise => { try { - await core.server.request( - `${baseURL}/api/v1/lambda/requests`, { - method: 'POST', - data: JSON.stringify({ - cleanup: cleanOut, - mapping, - task: taskInstance.id, - function: model.id, - }), - headers: { - 'Content-Type': 'application/json', - }, - }, - ); - - dispatch(getInferenceStatusAsync()); + const requestID: string = await core.lambda.run(taskInstance, model, { + mapping, + cleanup, + }); + + const dispatchCallback = (action: ModelsActions): void => { + dispatch(action); + }; + + listen({ + taskID: taskInstance.id, + requestID, + }, dispatchCallback); } catch (error) { dispatch(modelsActions.startInferenceFailed(taskInstance.id, error)); } @@ -343,19 +188,7 @@ export function cancelInferenceAsync(taskID: number): ThunkAction { return async (dispatch, getState): Promise => { try { const inference = getState().models.inferences[taskID]; - if (inference) { - await core.server.request( - `${baseURL}/api/v1/lambda/requests/${inference.id}`, { - method: 'DELETE', - }, - ); - - if (timers[taskID]) { - clearTimeout(timers[taskID]); - delete timers[taskID]; - } - } - + await core.lambda.cancel(inference.id); dispatch(modelsActions.cancelInferenceSuccess(taskID)); } catch (error) { dispatch(modelsActions.cancelInferenceFailed(taskID, error)); diff --git a/cvat-ui/src/containers/create-model-page/create-model-page.tsx b/cvat-ui/src/containers/create-model-page/create-model-page.tsx deleted file mode 100644 index 74cd406ea87..00000000000 --- a/cvat-ui/src/containers/create-model-page/create-model-page.tsx +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2020 Intel Corporation -// -// SPDX-License-Identifier: MIT - -import { connect } from 'react-redux'; - -import CreateModelPageComponent from 'components/create-model-page/create-model-page'; -import { createModelAsync } from 'actions/models-actions'; -import { - ModelFiles, - CombinedState, -} from 'reducers/interfaces'; - -interface StateToProps { - isAdmin: boolean; - modelCreatingStatus: string; -} - -interface DispatchToProps { - createModel(name: string, files: ModelFiles, global: boolean): void; -} - -function mapStateToProps(state: CombinedState): StateToProps { - const { models } = state; - - return { - isAdmin: state.auth.user.isAdmin, - modelCreatingStatus: models.creatingStatus, - }; -} - -function mapDispatchToProps(dispatch: any): DispatchToProps { - return { - createModel(name: string, files: ModelFiles, global: boolean): void { - dispatch(createModelAsync(name, files, global)); - }, - }; -} - -export default connect( - mapStateToProps, - mapDispatchToProps, -)(CreateModelPageComponent); diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index c406c2689de..d7d23d23b9c 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -165,14 +165,6 @@ export interface ModelsState { activeRunTask: any; } -export interface ModelFiles { - [key: string]: string | File; - xml: string | File; - bin: string | File; - py: string | File; - json: string | File; -} - export interface ErrorState { message: string; reason: string; diff --git a/cvat-ui/src/reducers/models-reducer.ts b/cvat-ui/src/reducers/models-reducer.ts index b6a39822b8c..3e369584b5f 100644 --- a/cvat-ui/src/reducers/models-reducer.ts +++ b/cvat-ui/src/reducers/models-reducer.ts @@ -44,31 +44,6 @@ export default function ( fetching: false, }; } - case ModelsActionTypes.CREATE_MODEL: { - return { - ...state, - creatingStatus: '', - }; - } - case ModelsActionTypes.CREATE_MODEL_STATUS_UPDATED: { - return { - ...state, - creatingStatus: action.payload.status, - }; - } - case ModelsActionTypes.CREATE_MODEL_FAILED: { - return { - ...state, - creatingStatus: '', - }; - } - case ModelsActionTypes.CREATE_MODEL_SUCCESS: { - return { - ...state, - initialized: false, - creatingStatus: 'CREATED', - }; - } case ModelsActionTypes.SHOW_RUN_MODEL_DIALOG: { return { ...state, diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 10a47bc62e3..68a65ca5479 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -405,7 +405,7 @@ export default function (state = defaultState, action: AnyAction): Notifications models: { ...state.errors.models, inferenceStatusFetching: { - message: 'Could not fetch inference status for the ' + message: 'Fetching inference status for the ' + `task ${taskID}`, reason: action.payload.error.toString(), },