Skip to content

Commit

Permalink
Merge pull request #20 from amplience/feature/TAR-1150-merge-ai
Browse files Browse the repository at this point in the history
feat: TAR-1150 merge changes from shoppable-image-ai repo
  • Loading branch information
jonnyAmplience authored Feb 13, 2024
2 parents c70a89d + 02f1c59 commit 6efa1ff
Show file tree
Hide file tree
Showing 33 changed files with 5,454 additions and 3,536 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ If you want to test on DC, you'll need to link an extension to that same localho

## Build

Run `yarn build`. The built extension will then be present in the `build/` directory, and you can upload it to any webserver.
Run `yarn build`. The built extension will then be present in the `build/` directory, and you can upload it to any webserver.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@types/react-dom": "^17.0.11",
"@types/uuid": "^8.3.4",
"clsx": "^1.1.1",
"dc-extensions-sdk": "^2.0.0",
"dc-extensions-sdk": "^2.3.0",
"dc-visualization-sdk": "^1.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down Expand Up @@ -50,5 +50,10 @@
},
"devDependencies": {
"@types/react": "^17.0.34"
},
"engines": {
"node": "~16.20.1",
"npm": "~8.19.4"
}

}
33 changes: 33 additions & 0 deletions public/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"objects": [
{
"id": "1d5c7d20-b226-46ac-9e24-8ac237ae405a",
"target": "chair-1",
"selector": ".chair-1",
"points": [
{
"x": 0.36824877250409166,
"y": 0.22707423580786026
},
{
"x": 0.613747954173486,
"y": 0.22707423580786026
},
{
"x": 0.613747954173486,
"y": 0.7248908296943232
},
{
"x": 0.36824877250409166,
"y": 0.7248908296943232
}
],
"center": {
"x": 0.36824877250409166,
"y": 0.7248908296943232
},
"width": "0.25",
"height": "0.5"
}
]
}
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./App.css";
import { WithEditorContext } from "./core/EditorContext";
import { PreviewCanvas } from "./preview/preview-canvas/preview-canvas";
import { AIDrawer } from "./preview/ai-drawer/ai-drawer";
import { EditToolbar } from "./preview/edit-toolbar/edit-toolbar";
import { WithExtensionContext } from "./core/ExtensionContext";
import { ThemeProvider } from "@emotion/react";
Expand Down Expand Up @@ -32,6 +33,7 @@ function App() {
<EditToolbar />
<PreviewCanvas />
<MetadataList />
<AIDrawer/>
</div>
</WithEditorContext>
</WithExtensionContext>
Expand Down
28 changes: 28 additions & 0 deletions src/core/AIImageData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Bounds, Coordinate } from "./dal/shoppable-image/types/GqlOutput";

export interface ContentItemPointOfInterest {
id: string;
target: string;
center: Coordinate;
bounds: Bounds | undefined;
outline: Coordinate[] | undefined;
}

export type ObjectData = ContentItemPointOfInterest & {
selector: string;
};

export enum AIState {
Stale,
Loading,
Loaded,
Error,
InsufficientCredits,
}

export interface AIImageData {
image?: string;
objects: ObjectData[];
state: AIState;
drawerOpen: boolean;
}
86 changes: 86 additions & 0 deletions src/core/AIRequestService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ContentFieldExtension } from "dc-extensions-sdk";
import { ObjectData } from "./AIImageData";
import { TempFileServiceDal } from "./dal/temp-file-service/TempFileServiceDal";
import { AiShoppableImageDal } from "./dal/shoppable-image/AiShoppableImageDal";
import { PointOfInterest } from "./dal/shoppable-image/types/GqlOutput";

interface cache {
[url: string]: ObjectData[];
}

class AIRequestService {
private basePath: string;
private cache: cache;
private currentPromise: null | ((e: Error) => void);
private readonly tempFileServiceDal: TempFileServiceDal;
private readonly aiPoiServiceDal: AiShoppableImageDal;

constructor(private readonly sdk: ContentFieldExtension, base: string) {
this.basePath = base;
this.cache = {} as cache;
this.currentPromise = null;
this.tempFileServiceDal = new TempFileServiceDal(sdk);
this.aiPoiServiceDal = new AiShoppableImageDal(sdk);
}

private cancel() {
if (this.currentPromise) {
this.currentPromise(new Error("Request cancelled"));
}
}

async get(imageUrl: string, useCache: boolean = true) {
if (useCache && this.cache[imageUrl]) {
return this.cache[imageUrl];
}
this.cancel();
return await this.retrieve(imageUrl);
}

private retrieve(imageUrl: string): Promise<ObjectData[]> {
return new Promise(async (resolve, reject) => {
this.currentPromise = reject;

try {
const { downloadUrl } = await this.tempFileServiceDal.uploadImage(imageUrl);
const organizationId = Buffer.from(`Organization:${this.sdk!.hub.organizationId}`, "utf-8").toString("base64");
const detectResponse = await this.aiPoiServiceDal.detectPointsOfInterestInImage({
organizationId,
imageUrl: downloadUrl,
});

const pointsOfInterest = detectResponse.output?.objects ?? [];
const objects = this.generateSelectors(pointsOfInterest);

this.cache[imageUrl] = objects;

resolve(objects);
} catch (err) {
reject(err);
}
});
}

private generateSelectors(objects: PointOfInterest[]): ObjectData[] {
const stringCount: { [key: string]: number } = {};

return objects.map(obj => {
const { label } = obj;
const count = (stringCount[label] || 0);
stringCount[label] = count + 1;

const selector = count > 0 ? `.${label}-${count}` : `.${label}`;
return {
...obj,
id: obj.id,
target: obj.label,
center: obj.center,
bounds: obj.bounds,
outline: obj.outline,
selector,
};
});
}
}

export default AIRequestService;
Loading

0 comments on commit 6efa1ff

Please sign in to comment.