Skip to content

Commit

Permalink
Feat/mn 562/network video requests (#815)
Browse files Browse the repository at this point in the history
* Added isVideoCapture param to the CreateInspection request

* Added AddVideoFrameOptions to the add image request

* Fixed Pr comments
  • Loading branch information
souyahia-monk authored Jul 18, 2024
1 parent c7562a4 commit 1c6ede4
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 40 deletions.
4 changes: 3 additions & 1 deletion configs/test-utils/src/__mocks__/@monkvision/network.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { MonkApiPermission, MonkNetworkError } = jest.requireActual('@monkvision/network');
const { MonkApiPermission, MonkNetworkError, ImageUploadType } =
jest.requireActual('@monkvision/network');

const MonkApi = {
getInspection: jest.fn(() => Promise.resolve()),
Expand All @@ -13,6 +14,7 @@ export = {
/* Actual exports */
MonkApiPermission,
MonkNetworkError,
ImageUploadType,

/* Mocks */
decodeMonkJwt: jest.fn((str) => str),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { Queue, uniq, useQueue } from '@monkvision/common';
import { AddImageOptions, MonkApiConfig, useMonkApi } from '@monkvision/network';
import {
CaptureAppConfig,
ComplianceOptions,
ImageType,
MonkPicture,
TaskName,
} from '@monkvision/types';
import { AddImageOptions, ImageUploadType, MonkApiConfig, useMonkApi } from '@monkvision/network';
import { CaptureAppConfig, ComplianceOptions, MonkPicture, TaskName } from '@monkvision/types';
import { useRef } from 'react';
import { useMonitoring } from '@monkvision/monitoring';
import { PhotoCaptureMode } from './useAddDamageMode';
Expand Down Expand Up @@ -116,7 +110,7 @@ function createAddImageOptions(
): AddImageOptions {
if (upload.mode === PhotoCaptureMode.SIGHT) {
return {
type: ImageType.BEAUTY_SHOT,
uploadType: ImageUploadType.BEAUTY_SHOT,
picture: upload.picture,
sightId: upload.sightId,
tasks: additionalTasks ? uniq([...upload.tasks, ...additionalTasks]) : upload.tasks,
Expand All @@ -125,7 +119,7 @@ function createAddImageOptions(
};
}
return {
type: ImageType.CLOSE_UP,
uploadType: ImageUploadType.CLOSE_UP_2_SHOT,
picture: upload.picture,
siblingKey: `closeup-sibling-key-${siblingId}`,
firstShot: upload.mode === PhotoCaptureMode.ADD_DAMAGE_1ST_SHOT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
UploadQueueParams,
useUploadQueue,
} from '../../../src/PhotoCapture/hooks';
import { ComplianceIssue, ImageType, TaskName } from '@monkvision/types';
import { useMonkApi } from '@monkvision/network';
import { ComplianceIssue, TaskName } from '@monkvision/types';
import { ImageUploadType, useMonkApi } from '@monkvision/network';
import { useMonitoring } from '@monkvision/monitoring';
import { act } from '@testing-library/react';

Expand Down Expand Up @@ -78,7 +78,7 @@ describe('useUploadQueue hook', () => {
await process(defaultUploadOptions);

expect(addImageMock).toHaveBeenCalledWith({
type: ImageType.BEAUTY_SHOT,
uploadType: ImageUploadType.BEAUTY_SHOT,
picture: defaultUploadOptions.picture,
sightId: defaultUploadOptions.sightId,
tasks: defaultUploadOptions.tasks,
Expand All @@ -102,7 +102,7 @@ describe('useUploadQueue hook', () => {
await process({ ...defaultUploadOptions, tasks });

expect(addImageMock).toHaveBeenCalledWith({
type: ImageType.BEAUTY_SHOT,
uploadType: ImageUploadType.BEAUTY_SHOT,
picture: defaultUploadOptions.picture,
sightId: defaultUploadOptions.sightId,
tasks: expect.arrayContaining([
Expand Down Expand Up @@ -139,7 +139,7 @@ describe('useUploadQueue hook', () => {
await process(upload1);
});
expect(addImageMock).toHaveBeenCalledWith({
type: ImageType.CLOSE_UP,
uploadType: ImageUploadType.CLOSE_UP_2_SHOT,
picture: upload1.picture,
siblingKey: expect.any(String),
firstShot: true,
Expand All @@ -162,7 +162,7 @@ describe('useUploadQueue hook', () => {
await process(upload2);

expect(addImageMock).toHaveBeenCalledWith({
type: ImageType.CLOSE_UP,
uploadType: ImageUploadType.CLOSE_UP_2_SHOT,
picture: upload2.picture,
siblingKey,
firstShot: false,
Expand Down
139 changes: 120 additions & 19 deletions packages/network/src/api/image/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,33 @@ import { ApiImage, ApiImagePost, ApiImagePostTask } from '../models';
import { MonkApiResponse } from '../types';
import { mapApiImage } from './mappers';

/**
* The different upload types for inspection images.
*/
export enum ImageUploadType {
/**
* Upload type corresponding to a sight image in the PhotoCapture process (beauty shot image).
*/
BEAUTY_SHOT = 'beauty_shot',
/**
* Upload type corresponding to a close-up picture (add-damage) in the PhotoCapture process, when using the 2-shot
* add damage workflow.
*/
CLOSE_UP_2_SHOT = 'close_up_2_shot',
/**
* Upload type corresponding to a video frame in the VideoCapture process.
*/
VIDEO_FRAME = 'video_frame',
}

/**
* Options specififed when adding a beauty shot (normal "sight" image) to an inspection.
*/
export interface AddBeautyShotImageOptions {
/**
* The type of the image : `ImageType.BEAUTY_SHOT`;
* The type of the image upload : `ImageUploadType.BEAUTY_SHOT`;
*/
type: ImageType.BEAUTY_SHOT;
uploadType: ImageUploadType.BEAUTY_SHOT;
/**
* The picture to add to the inspection.
*/
Expand Down Expand Up @@ -55,9 +74,9 @@ export interface AddBeautyShotImageOptions {
*/
export interface Add2ShotCloseUpImageOptions {
/**
* The type of the image : `ImageType.CLOSE_UP`;
* The type of the image upload : `ImageUploadType.CLOSE_UP_2_SHOT`;
*/
type: ImageType.CLOSE_UP;
uploadType: ImageUploadType.CLOSE_UP_2_SHOT;
/**
* The picture to add to the inspection.
*/
Expand All @@ -83,15 +102,65 @@ export interface Add2ShotCloseUpImageOptions {
compliance?: ComplianceOptions;
}

/**
* Options specififed when adding a video frame to a VideoCapture inspection.
*/
export interface AddVideoFrameOptions {
/**
* The type of the image upload : `ImageUploadType.VIDEO_FRAME`;
*/
uploadType: ImageUploadType.VIDEO_FRAME;
/**
* The picture to add to the inspection.
*/
picture: MonkPicture;
/**
* The ID of the inspection to add the video frame to.
*/
inspectionId: string;
/**
* The index of the frame in the video. This index starts at 0 and increases by 1 for each video frame uploaded.
*/
frameIndex: number;
/**
* The duration (in milliseconds) between this video frame capture time and the previous one. For the first frame of
* the video, this value is equal to 0.
*/
timestamp: number;
}

/**
* Union type describing the different options that can be specified when adding a picture to an inspection.
*/
export type AddImageOptions = AddBeautyShotImageOptions | Add2ShotCloseUpImageOptions;
export type AddImageOptions =
| AddBeautyShotImageOptions
| Add2ShotCloseUpImageOptions
| AddVideoFrameOptions;

interface AddImageData {
filename: string;
body: ApiImagePost;
}

function getImageType(options: AddImageOptions): ImageType {
if (options.uploadType === ImageUploadType.CLOSE_UP_2_SHOT) {
return ImageType.CLOSE_UP;
}
return ImageType.BEAUTY_SHOT;
}

function getImageLabel(options: AddImageOptions): TranslationObject | undefined {
if (options.type === ImageType.BEAUTY_SHOT) {
if (options.uploadType === ImageUploadType.BEAUTY_SHOT) {
return sights[options.sightId] ? labels[sights[options.sightId].label] : undefined;
}
if (options.uploadType === ImageUploadType.VIDEO_FRAME) {
return {
en: `Video Frame ${options.frameIndex}`,
fr: `Trame Vidéo ${options.frameIndex}`,
de: `Videobild ${options.frameIndex}`,
nl: `Videoframe ${options.frameIndex}`,
};
}
return {
en: options.firstShot ? 'Close Up (part)' : 'Close Up (damage)',
fr: options.firstShot ? 'Photo Zoomée (partie)' : 'Photo Zoomée (dégât)',
Expand All @@ -105,9 +174,13 @@ function getAdditionalData(options: AddImageOptions): ImageAdditionalData {
label: getImageLabel(options),
created_at: new Date().toISOString(),
};
if (options.type === ImageType.BEAUTY_SHOT) {
if (options.uploadType === ImageUploadType.BEAUTY_SHOT) {
additionalData.sight_id = options.sightId;
}
if (options.uploadType === ImageUploadType.VIDEO_FRAME) {
additionalData.frame_index = options.frameIndex;
additionalData.timestamp = options.timestamp;
}
return additionalData;
}

Expand All @@ -117,7 +190,7 @@ const MULTIPART_KEY_JSON = 'json';
function createBeautyShotImageData(
options: AddBeautyShotImageOptions,
filetype: string,
): { filename: string; body: ApiImagePost } {
): AddImageData {
const filename = `${options.sightId}-${options.inspectionId}-${Date.now()}.${filetype}`;
const tasks = options.tasks.filter(
(task) => ![TaskName.COMPLIANCES, TaskName.HUMAN_IN_THE_LOOP].includes(task),
Expand Down Expand Up @@ -151,7 +224,7 @@ function createBeautyShotImageData(
function createCloseUpImageData(
options: Add2ShotCloseUpImageOptions,
filetype: string,
): { filename: string; body: ApiImagePost } {
): AddImageData {
const prefix = options.firstShot ? 'closeup-part' : 'closeup-damage';
const filename = `${prefix}-${options.inspectionId}-${Date.now()}.${filetype}`;

Expand All @@ -177,18 +250,42 @@ function createCloseUpImageData(
return { filename, body };
}

async function createImageFormData(
options: AddImageOptions | Add2ShotCloseUpImageOptions,
): Promise<FormData> {
function createVideoFrameData(options: AddVideoFrameOptions, filetype: string): AddImageData {
const filename = `video-frame-${options.frameIndex}.${filetype}`;

const body: ApiImagePost = {
acquisition: {
strategy: 'upload_multipart_form_keys',
file_key: MULTIPART_KEY_IMAGE,
},
tasks: [TaskName.DAMAGE_DETECTION],
additional_data: getAdditionalData(options),
};

return { filename, body };
}

function getAddImageData(options: AddImageOptions, filetype: string): AddImageData {
switch (options.uploadType) {
case ImageUploadType.BEAUTY_SHOT:
return createBeautyShotImageData(options, filetype);
case ImageUploadType.CLOSE_UP_2_SHOT:
return createCloseUpImageData(options, filetype);
case ImageUploadType.VIDEO_FRAME:
return createVideoFrameData(options, filetype);
default:
throw new Error('Unknown image upload type.');
}
}

async function createImageFormData(options: AddImageOptions): Promise<FormData> {
const extensions = getFileExtensions(options.picture.mimetype);
if (!extensions) {
throw new Error(`Unknown picture mimetype : ${options.picture.mimetype}`);
}
const filetype = extensions[0];
const { filename, body } =
options.type === ImageType.BEAUTY_SHOT
? createBeautyShotImageData(options, filetype)
: createCloseUpImageData(options, filetype);

const { filename, body } = getAddImageData(options, filetype);

const file = new File([options.picture.blob], filename, { type: filetype });

Expand All @@ -210,7 +307,7 @@ function createLocalImage(options: AddImageOptions): Image {
height: options.picture.height,
size: -1,
mimetype: options.picture.mimetype,
type: options.type,
type: getImageType(options),
status: ImageStatus.UPLOADING,
label: getImageLabel(options),
additionalData,
Expand All @@ -219,7 +316,7 @@ function createLocalImage(options: AddImageOptions): Image {
views: [],
renderedOutputs: [],
};
if (options.type === ImageType.CLOSE_UP) {
if (options.uploadType === ImageUploadType.CLOSE_UP_2_SHOT) {
image.siblingKey = options.siblingKey;
image.subtype = options.firstShot ? ImageSubtype.CLOSE_UP_PART : ImageSubtype.CLOSE_UP_DAMAGE;
}
Expand Down Expand Up @@ -265,7 +362,11 @@ export async function addImage(
body: formData,
});
const body = await response.json<ApiImage>();
const image = mapApiImage(body, options.inspectionId, options.compliance);
const image = mapApiImage(
body,
options.inspectionId,
(options as AddBeautyShotImageOptions).compliance,
);
dispatch?.({
type: MonkActionType.CREATED_ONE_IMAGE,
payload: {
Expand Down
2 changes: 2 additions & 0 deletions packages/network/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export {
type AddBeautyShotImageOptions,
type Add2ShotCloseUpImageOptions,
type AddImageOptions,
type AddVideoFrameOptions,
ImageUploadType,
} from './image';
export {
type UpdateProgressStatus,
Expand Down
1 change: 1 addition & 0 deletions packages/network/src/api/inspection/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ export function mapApiInspectionPost(options: CreateInspectionOptions): ApiInspe
monk_sdk_version: sdkVersion,
damage_detection_version: 'v2',
use_dynamic_crops: options.useDynamicCrops ?? true,
is_video_capture: options.isVideoCapture ?? false,
},
};
}
Loading

0 comments on commit 1c6ede4

Please sign in to comment.