diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts index f76e38f7b..429bee5ad 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts +++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePhotoCaptureSightState.ts @@ -7,9 +7,8 @@ import { } from '@monkvision/network'; import { useMonitoring } from '@monkvision/monitoring'; import { LoadingState, useAsyncEffect } from '@monkvision/common'; -import { ComplianceOptions, Image, Sight, TaskName } from '@monkvision/types'; +import { ComplianceOptions, Image, Sight, TaskName, MonkPicture } from '@monkvision/types'; import { sights } from '@monkvision/sights'; -import { MonkPicture } from '@monkvision/types'; import { PhotoCaptureErrorName } from '../errors'; /** diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts index efa132b01..fdafc1208 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts +++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/usePictureTaken.ts @@ -1,5 +1,4 @@ -import { MonkPicture } from '@monkvision/types'; -import { TaskName } from '@monkvision/types'; +import { MonkPicture, TaskName } from '@monkvision/types'; import { Queue } from '@monkvision/common'; import { useCallback } from 'react'; import { PictureUpload } from './useUploadQueue'; diff --git a/packages/inspection-capture-web/src/PhotoCapture/hooks/useStartTasksOnComplete.ts b/packages/inspection-capture-web/src/PhotoCapture/hooks/useStartTasksOnComplete.ts index 026f116c0..9dfd05185 100644 --- a/packages/inspection-capture-web/src/PhotoCapture/hooks/useStartTasksOnComplete.ts +++ b/packages/inspection-capture-web/src/PhotoCapture/hooks/useStartTasksOnComplete.ts @@ -44,6 +44,8 @@ export interface UseStartTasksOnCompleteParams { */ export type StartTasksFunction = () => Promise; +const TASKS_NOT_TO_START = [TaskName.HUMAN_IN_THE_LOOP]; + function getTasksToStart({ sights, tasksBySight, @@ -52,13 +54,15 @@ function getTasksToStart({ UseStartTasksOnCompleteParams, 'sights' | 'tasksBySight' | 'startTasksOnComplete' >): TaskName[] { + let tasks = []; if (Array.isArray(startTasksOnComplete)) { - return startTasksOnComplete; - } - if (tasksBySight) { - return uniq(flatMap(sights, (sight) => tasksBySight[sight.id])); + tasks = startTasksOnComplete; + } else if (tasksBySight) { + tasks = uniq(flatMap(sights, (sight) => tasksBySight[sight.id])); + } else { + tasks = uniq(flatMap(sights, (sight) => sight.tasks)); } - return uniq(flatMap(sights, (sight) => sight.tasks)); + return tasks.filter((task) => !TASKS_NOT_TO_START.includes(task)); } /** diff --git a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDButtons.test.tsx b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDButtons.test.tsx index 5668db02e..2e7fed1ec 100644 --- a/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDButtons.test.tsx +++ b/packages/inspection-capture-web/test/PhotoCapture/PhotoCaptureHUD/PhotoCaptureHUDButtons.test.tsx @@ -1,7 +1,6 @@ import '@testing-library/jest-dom'; import { expectPropsOnChildMock } from '@monkvision/test-utils'; -import { InteractiveStatus } from '@monkvision/types'; -import { MonkPicture } from '@monkvision/types'; +import { InteractiveStatus, MonkPicture } from '@monkvision/types'; import { TakePictureButton, Icon } from '@monkvision/common-ui-web'; import { fireEvent, render, screen } from '@testing-library/react'; import { PhotoCaptureHUDButtons } from '../../../src'; diff --git a/packages/inspection-capture-web/test/PhotoCapture/hooks/usePictureTaken.test.ts b/packages/inspection-capture-web/test/PhotoCapture/hooks/usePictureTaken.test.ts index f8d86ac53..28161d1b3 100644 --- a/packages/inspection-capture-web/test/PhotoCapture/hooks/usePictureTaken.test.ts +++ b/packages/inspection-capture-web/test/PhotoCapture/hooks/usePictureTaken.test.ts @@ -1,4 +1,4 @@ -import { TaskName } from '@monkvision/types'; +import { TaskName, MonkPicture } from '@monkvision/types'; import { AddDamageHandle, PhotoCaptureMode, @@ -6,7 +6,6 @@ import { UseTakePictureParams, } from '../../../src/PhotoCapture/hooks'; import { renderHook } from '@testing-library/react-hooks'; -import { MonkPicture } from '@monkvision/types'; function createParams(): UseTakePictureParams { return { diff --git a/packages/inspection-capture-web/test/PhotoCapture/hooks/useStartTasksOnComplete.test.ts b/packages/inspection-capture-web/test/PhotoCapture/hooks/useStartTasksOnComplete.test.ts index fd562d44b..4207c9d45 100644 --- a/packages/inspection-capture-web/test/PhotoCapture/hooks/useStartTasksOnComplete.test.ts +++ b/packages/inspection-capture-web/test/PhotoCapture/hooks/useStartTasksOnComplete.test.ts @@ -180,4 +180,23 @@ describe('useStartTasksOnComplete hook', () => { unmount(); }); + + it('should not start the Human in the Loop task', () => { + const initialProps = { + ...createParams(), + startTasksOnComplete: [TaskName.WHEEL_ANALYSIS, TaskName.HUMAN_IN_THE_LOOP], + }; + const { result, unmount } = renderHook(useStartTasksOnComplete, { initialProps }); + + const startInspectionTasksMock = (useMonkApi as jest.Mock).mock.results[0].value + .startInspectionTasks; + result.current(); + expect(startInspectionTasksMock).toHaveBeenCalledWith( + expect.objectContaining({ + names: [TaskName.WHEEL_ANALYSIS], + }), + ); + + unmount(); + }); }); diff --git a/packages/network/src/api/image/requests.ts b/packages/network/src/api/image/requests.ts index a25d94d1a..51e067224 100644 --- a/packages/network/src/api/image/requests.ts +++ b/packages/network/src/api/image/requests.ts @@ -16,12 +16,7 @@ import { import { v4 } from 'uuid'; import { labels, sights } from '@monkvision/sights'; import { getDefaultOptions, MonkApiConfig } from '../config'; -import { - ApiBusinessTaskName, - ApiImage, - ApiImageCompliancesTaskPost, - ApiImagePost, -} from '../models'; +import { ApiImage, ApiImagePost, ApiImagePostTask } from '../models'; import { MonkApiResponse } from '../types'; import { mapApiImage } from './mappers'; @@ -123,17 +118,22 @@ function createBeautyShotImageData( filetype: string, ): { filename: string; body: ApiImagePost } { const filename = `${options.sightId}-${options.inspectionId}-${Date.now()}.${filetype}`; - const tasks: (ApiBusinessTaskName | ApiImageCompliancesTaskPost)[] = options.tasks.filter( - (task) => task !== TaskName.COMPLIANCES, - ); + const tasks = options.tasks.filter( + (task) => ![TaskName.COMPLIANCES, TaskName.HUMAN_IN_THE_LOOP].includes(task), + ) as ApiImagePostTask[]; tasks.push({ name: TaskName.COMPLIANCES, image_details: { sight_id: options.sightId }, - wait_for_result: !!( - options.compliance?.enableCompliance && options.compliance?.useLiveCompliance - ), + wait_for_result: options.compliance?.enableCompliance && options.compliance?.useLiveCompliance, }); + if (options.tasks.includes(TaskName.HUMAN_IN_THE_LOOP)) { + tasks.push({ + name: TaskName.HUMAN_IN_THE_LOOP, + image_details: { sight_label: sights[options.sightId].label }, + }); + } + const body: ApiImagePost = { acquisition: { strategy: 'upload_multipart_form_keys', @@ -166,9 +166,8 @@ function createCloseUpImageData( TaskName.DAMAGE_DETECTION, { name: TaskName.COMPLIANCES, - wait_for_result: !!( - options.compliance?.enableCompliance && options.compliance?.useLiveCompliance - ), + wait_for_result: + options.compliance?.enableCompliance && options.compliance?.useLiveCompliance, }, ], additional_data: getAdditionalData(options), diff --git a/packages/network/src/api/models/image.ts b/packages/network/src/api/models/image.ts index b9114ad53..f8a421b92 100644 --- a/packages/network/src/api/models/image.ts +++ b/packages/network/src/api/models/image.ts @@ -3,7 +3,7 @@ import type { ApiAdditionalData, ApiCenterOnElement, ApiLabelPrediction } from ' import type { ApiRenderedOutputs } from './renderedOutput'; import type { ApiImageComplianceResults } from './compliance'; import type { ApiViews } from './view'; -import { ApiBusinessTaskName, ApiImageCompliancesTaskPost } from './task'; +import { ApiBusinessTaskName, ApiHinlTaskPost, ApiImageCompliancesTaskPost } from './task'; export type ApiImageType = 'unknown' | 'beauty_shot' | 'close_up'; @@ -78,9 +78,11 @@ export interface ApiCompliance { zoom_level?: ApiComplianceParameters; } +export type ApiImagePostTask = ApiBusinessTaskName | ApiImageCompliancesTaskPost | ApiHinlTaskPost; + export interface ApiImagePost { acquisition: ApiAcquisition; - tasks?: (ApiBusinessTaskName | ApiImageCompliancesTaskPost)[]; + tasks?: ApiImagePostTask[]; name?: string; image_type?: ApiImageType; image_subtype?: ApiImageSubType; diff --git a/packages/network/src/api/models/task.ts b/packages/network/src/api/models/task.ts index 5796351bc..908bb7b08 100644 --- a/packages/network/src/api/models/task.ts +++ b/packages/network/src/api/models/task.ts @@ -26,6 +26,15 @@ export interface ApiImageCompliancesTaskPost { wait_for_result?: boolean; } +export interface ApiHinlImageDetails { + sight_label: string; +} + +export interface ApiHinlTaskPost { + name: 'human_in_the_loop'; + image_details: ApiHinlImageDetails; +} + export type ApiTaskProgressStatus = | 'NOT_STARTED' | 'TODO' diff --git a/packages/network/test/api/image/requests.test.ts b/packages/network/test/api/image/requests.test.ts index d64c4da18..8084ba700 100644 --- a/packages/network/test/api/image/requests.test.ts +++ b/packages/network/test/api/image/requests.test.ts @@ -33,8 +33,8 @@ function createBeautyShotImageOptions(): AddBeautyShotImageOptions { mimetype: 'image/jpeg', }, inspectionId: 'test-inspection-id', - sightId: 'test-sight-id', - tasks: [TaskName.DAMAGE_DETECTION, TaskName.WHEEL_ANALYSIS], + sightId: 'test-sight-1', + tasks: [TaskName.DAMAGE_DETECTION, TaskName.WHEEL_ANALYSIS, TaskName.HUMAN_IN_THE_LOOP], compliance: { enableCompliance: true, complianceIssues: [ComplianceIssue.INTERIOR_NOT_SUPPORTED], @@ -168,11 +168,14 @@ describe('Image requests', () => { }, image_type: ImageType.BEAUTY_SHOT, tasks: [ - ...options.tasks, + ...options.tasks.filter((task) => task !== TaskName.HUMAN_IN_THE_LOOP), { name: TaskName.COMPLIANCES, image_details: { sight_id: options.sightId }, - wait_for_result: false, + }, + { + name: TaskName.HUMAN_IN_THE_LOOP, + image_details: { sight_label: sights[options.sightId].label }, }, ], additional_data: { @@ -211,7 +214,7 @@ describe('Image requests', () => { ? ImageSubtype.CLOSE_UP_PART : ImageSubtype.CLOSE_UP_DAMAGE, image_sibling_key: options.siblingKey, - tasks: [TaskName.DAMAGE_DETECTION, { name: TaskName.COMPLIANCES, wait_for_result: false }], + tasks: [TaskName.DAMAGE_DETECTION, { name: TaskName.COMPLIANCES }], additional_data: { label: { en: options.firstShot ? 'Close Up (part)' : 'Close Up (damage)', diff --git a/packages/types/src/state/task.ts b/packages/types/src/state/task.ts index e94365c44..7cae9085f 100644 --- a/packages/types/src/state/task.ts +++ b/packages/types/src/state/task.ts @@ -45,6 +45,12 @@ export enum TaskName { * sight_id of the image for instance). */ COMPLIANCES = 'compliances', + /** + * Human in the loop task. + * + * // TODO : Add better doc for this task. + */ + HUMAN_IN_THE_LOOP = 'human_in_the_loop', } /**