diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml new file mode 100644 index 0000000..22fe54c --- /dev/null +++ b/backend/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3" + +services: + mysql: + image: mysql + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: rethread + ports: + - "3306:3306" + volumes: + - mysql:/var/lib/mysql + +volumes: + mysql: diff --git a/backend/eng.traineddata b/backend/eng.traineddata deleted file mode 100644 index 6d11002..0000000 Binary files a/backend/eng.traineddata and /dev/null differ diff --git a/backend/src/abstracts/product.abstract.ts b/backend/src/abstracts/product.abstract.ts index 5c09f95..7c807e9 100644 --- a/backend/src/abstracts/product.abstract.ts +++ b/backend/src/abstracts/product.abstract.ts @@ -1,23 +1,9 @@ -type PrismaProduct = { - id: string; - title: string; - size: string; - color: string; - description: string; - gender: string; - category: string; - price: number; - imageUrl: string; - createdAt: Date; - updatedAt: Date; -}; - -type PrismaProducts = PrismaProduct[]; +import { PrismaProduct, PrismaProducts } from "../../types"; abstract class ProductProvider { - abstract getProducts(): Promise; + abstract getProducts(): Promise; - abstract getProductById(id: string): Promise; + abstract getProductById(id: string): Promise; abstract createProduct( title: string, @@ -29,7 +15,9 @@ abstract class ProductProvider { price: number, imageUrl: string, url: string - ): Promise; + ): Promise; - abstract deleteProduct(id: string): Promise; + abstract deleteProduct(id: string): Promise; } + +export default ProductProvider; diff --git a/backend/src/abstracts/scanner.abstract.ts b/backend/src/abstracts/scanner.abstract.ts new file mode 100644 index 0000000..a172764 --- /dev/null +++ b/backend/src/abstracts/scanner.abstract.ts @@ -0,0 +1,9 @@ +import { Tag } from "../../types"; + +abstract class ScannerProvider { + abstract getMaterials(text: string): Tag[]; + + abstract getTextFromImage(imagePath: string): Promise; +} + +export default ScannerProvider; diff --git a/backend/src/controllers/algorithm.controller.ts b/backend/src/controllers/algorithm.controller.ts deleted file mode 100644 index a1d09ba..0000000 --- a/backend/src/controllers/algorithm.controller.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { HttpBadRequestError } from "../errors/http.error.js"; -import { AlgorithmServiceInterface } from "../../types.js"; - -class AlgorithmController { - constructor(private algorithmService: AlgorithmServiceInterface) {} - - postAlgorithm = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise>> => { - try { - const prompt: string = req.body.prompt; - if (!prompt) { - return next(new HttpBadRequestError()); - } - - const isEcoFriendly: boolean = - await this.algorithmService.isClothingEcoFriendly(prompt); - - if (!isEcoFriendly) { - return res.status(200).json("Clothing is not eco-friendly!"); - } - return res.status(200).json("Clothing is eco-friendly!"); - } catch (e) { - next(e); - } - }; -} - -export default AlgorithmController; diff --git a/backend/src/controllers/product.controller.ts b/backend/src/controllers/product.controller.ts index b86466c..0fd0d8d 100644 --- a/backend/src/controllers/product.controller.ts +++ b/backend/src/controllers/product.controller.ts @@ -1,3 +1,5 @@ +import { PrismaProduct } from "../../types"; +import ProductProvider from "../abstracts/product.abstract"; import { Request, Response, NextFunction } from "express"; class ProductController { @@ -5,7 +7,7 @@ class ProductController { this.service = service; } - getProducts = async ( + public getProducts = async ( req: Request, res: Response, next: NextFunction @@ -18,20 +20,22 @@ class ProductController { } }; - getProductById = async ( + public getProductById = async ( req: Request, res: Response, next: NextFunction ): Promise> | void> => { try { - const product = await this.service.getProductById(req.params.id); + const product: PrismaProduct = await this.service.getProductById( + req.params.id + ); return res.status(200).json(product); } catch (e) { next(e); } }; - postProduct = async ( + public postProduct = async ( req: Request, res: Response, next: NextFunction @@ -54,7 +58,7 @@ class ProductController { } }; - deleteProduct = async ( + public deleteProduct = async ( req: Request, res: Response, next: NextFunction diff --git a/backend/src/controllers/scanner.controller.ts b/backend/src/controllers/scanner.controller.ts index 9cf8f9e..b33884a 100644 --- a/backend/src/controllers/scanner.controller.ts +++ b/backend/src/controllers/scanner.controller.ts @@ -1,30 +1,25 @@ -import { Request, Response, NextFunction, response } from "express"; -import ScannerService from "../services/scanner.service.js"; +import { Tag } from "../../types.js"; +import ScannerProvider from "../abstracts/scanner.abstract.js"; +import { Request, Response, NextFunction } from "express"; class ScannerController { - constructor(private service: ScannerService) { - this.service = service; - } - postMaterials = async (//must send image as tag "image" and value base64 string. Returns JSON with the percentage and material of each material found on the tag - req: Request, - res: Response, - next: NextFunction - ): Promise> | void> => { + constructor(private service: ScannerProvider) { + this.service = service; + } + + public postMaterials = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise> | void> => { try { - console.log("postMaterials called"); - let materials = await (async () => { - var str; - str = await this.service.getMaterials(await this.service.getTextFromImage(req.body.image)); - // console.log("Fuck: ", await str); - return str; - })(); - console.log("materials: ",materials); - return res.status(201).json(JSON.parse(materials)); + const text: string = await this.service.getTextFromImage(req.body.image); + const materials: Tag[] = this.service.getMaterials(text); + return res.status(201).json(materials); } catch (e) { - console.log("Error: ", e); - next(e); + next(e); } - }; + }; } -export default ScannerController; \ No newline at end of file +export default ScannerController; diff --git a/backend/src/errors/tesseract.error.ts b/backend/src/errors/tesseract.error.ts new file mode 100644 index 0000000..74a7793 --- /dev/null +++ b/backend/src/errors/tesseract.error.ts @@ -0,0 +1 @@ +export class TesseractServiceError extends Error {} diff --git a/backend/src/index.ts b/backend/src/index.ts index facd85b..94ae1d9 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -3,21 +3,18 @@ import cors from "cors"; import { PORT } from "./config/config.js"; import errorHandler from "./middlewares/error.middleware.js"; import { PrismaClient } from "@prisma/client"; -import algorithmRouter from "./routes/algorithm.routes.js"; import productRouter from "./routes/product.routes.js"; import scannerRouter from "./routes/scanner.routes.js"; export const app: Express = express(); export const prisma = new PrismaClient(); -app.use(express.json({ limit: '50mb' })); +app.use(express.json({ limit: "50mb" })); app.use(cors()); app.use("/api/v1/scanner", scannerRouter); -app.use("/api/v1/algorithm", algorithmRouter); - app.use("/api/v1/products", productRouter); app.use(errorHandler); diff --git a/backend/src/middlewares/error.middleware.ts b/backend/src/middlewares/error.middleware.ts index becceca..23ace51 100644 --- a/backend/src/middlewares/error.middleware.ts +++ b/backend/src/middlewares/error.middleware.ts @@ -13,9 +13,11 @@ import { PrismaGenericError, } from "../errors/prisma.error.js"; import { ProductNotFoundError } from "../errors/product.error.js"; +import { TesseractServiceError } from "../errors/tesseract.error.js"; + export default function errorHandler( e: Error, - req: Request, + _: Request, res: Response, next: NextFunction ) { @@ -39,6 +41,8 @@ export default function errorHandler( res.status(500).json({ error: "Prisma Generic Error" }); } else if (e instanceof ProductNotFoundError) { res.status(500).json({ error: "Product Not Found Error" }); + } else if (e instanceof TesseractServiceError) { + res.status(500).json({ error: "Tesseract Service Error" }); } else { res.status(500).json({ error: "Unexpected error" }); } diff --git a/backend/src/repositories/product.repository.ts b/backend/src/repositories/product.repository.ts index d1e5e4c..e61d2a1 100644 --- a/backend/src/repositories/product.repository.ts +++ b/backend/src/repositories/product.repository.ts @@ -1,6 +1,8 @@ +import ProductProvider from "../abstracts/product.abstract.js"; import { prisma } from "../index.js"; import { Prisma } from "@prisma/client"; import { ProductNotFoundError } from "../errors/product.error.js"; +import { PrismaProduct, PrismaProducts } from "../../types.js"; import { PrismaClientInitializationError, PrismaClientRustPanicError, @@ -10,7 +12,7 @@ import { } from "../errors/prisma.error.js"; class ProductRepository implements ProductProvider { - getProducts = async (): Promise => { + public getProducts = async (): Promise => { try { const products: PrismaProducts = await prisma.product.findMany(); return products; @@ -31,7 +33,7 @@ class ProductRepository implements ProductProvider { } }; - getProductById = async (id: string): Promise => { + public getProductById = async (id: string): Promise => { try { const product: PrismaProduct | null = await prisma.product.findUnique({ where: { id: id }, @@ -59,7 +61,7 @@ class ProductRepository implements ProductProvider { } }; - createProduct = async ( + public createProduct = async ( title: string, size: string, color: string, @@ -68,7 +70,7 @@ class ProductRepository implements ProductProvider { category: string, price: number, imageUrl: string - ): Promise => { + ): Promise => { try { const newProduct: PrismaProduct = await prisma.product.create({ data: { @@ -100,7 +102,7 @@ class ProductRepository implements ProductProvider { } }; - deleteProduct = async (id: string): Promise => { + public deleteProduct = async (id: string): Promise => { try { const product: PrismaProduct | null = await prisma.product.delete({ where: { id: id }, diff --git a/backend/src/repositories/scanner.repository.ts b/backend/src/repositories/scanner.repository.ts new file mode 100644 index 0000000..a04aea5 --- /dev/null +++ b/backend/src/repositories/scanner.repository.ts @@ -0,0 +1,23 @@ +import { createWorker } from "tesseract.js"; +import { Tag } from "../../types.js"; +import ScannerProvider from "../abstracts/scanner.abstract.js"; +import { TesseractServiceError } from "../errors/tesseract.error.js"; + +class ScannerRepository implements ScannerProvider { + public getMaterials = (_: string): Tag[] => { + throw new Error("Method not implemented."); + }; + + public getTextFromImage = async (imagePath: string): Promise => { + try { + const worker: Tesseract.Worker = await createWorker("eng"); + const ret: Tesseract.RecognizeResult = await worker.recognize(imagePath); + await worker.terminate(); + return ret.data.text; + } catch (e) { + throw new TesseractServiceError(); + } + }; +} + +export default ScannerRepository; diff --git a/backend/src/routes/algorithm.routes.ts b/backend/src/routes/algorithm.routes.ts deleted file mode 100644 index 8a34326..0000000 --- a/backend/src/routes/algorithm.routes.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Router } from "express"; -import AlgorithmController from "../controllers/algorithm.controller.js"; -import AlgorithmService from "../services/algorithm.service.js"; -import { - AlgorithmControllerInterface, - AlgorithmServiceInterface, -} from "../../types.js"; - -const algorithmRouter: Router = Router(); -const algorithmService: AlgorithmServiceInterface = new AlgorithmService(); -const algorithmController: AlgorithmControllerInterface = - new AlgorithmController(algorithmService); - -algorithmRouter.post("/", algorithmController.postAlgorithm); - -export default algorithmRouter; diff --git a/backend/src/routes/product.routes.ts b/backend/src/routes/product.routes.ts index d6eba30..b25f6e6 100644 --- a/backend/src/routes/product.routes.ts +++ b/backend/src/routes/product.routes.ts @@ -1,7 +1,7 @@ -import { Router } from "express"; import ProductController from "../controllers/product.controller.js"; import ProductService from "../services/product.service.js"; import ProductRepository from "../repositories/product.repository.js"; +import { Router } from "express"; const productRouter = Router(); const productController = new ProductController( diff --git a/backend/src/routes/scanner.routes.ts b/backend/src/routes/scanner.routes.ts index 430159b..f79d438 100644 --- a/backend/src/routes/scanner.routes.ts +++ b/backend/src/routes/scanner.routes.ts @@ -1,11 +1,13 @@ -import { Router } from "express"; import ScannerController from "../controllers/scanner.controller.js"; import ScannerService from "../services/scanner.service.js"; +import ScannerRepository from "../repositories/scanner.repository.js"; +import { Router } from "express"; const scannerRouter = Router(); - -const scannerController = new ScannerController(new ScannerService()); +const scannerController = new ScannerController( + new ScannerService(new ScannerRepository()) +); scannerRouter.post("/", scannerController.postMaterials); -export default scannerRouter; \ No newline at end of file +export default scannerRouter; diff --git a/backend/src/services/algorithm.service.ts b/backend/src/services/algorithm.service.ts deleted file mode 100644 index f712d8c..0000000 --- a/backend/src/services/algorithm.service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { AlgorithmServiceInterface, Synthetic } from "../../types.js"; - -class AlgorithmService implements AlgorithmServiceInterface { - isClothingEcoFriendly = async (prompt: string): Promise => { - const testString: string = prompt; - - const testArray: Array = testString.split(" "); - let isEcoFriendly: boolean = true; - - for (const element of testArray) { - const upperCaseElement: String = element.toUpperCase(); - if (Synthetic[upperCaseElement as keyof typeof Synthetic]) { - isEcoFriendly = false; - break; - } - } - - return isEcoFriendly; - }; -} - -export default AlgorithmService; diff --git a/backend/src/services/product.service.ts b/backend/src/services/product.service.ts index bec1557..0a39cb4 100644 --- a/backend/src/services/product.service.ts +++ b/backend/src/services/product.service.ts @@ -1,3 +1,5 @@ +import ProductProvider from "../abstracts/product.abstract.js"; +import { PrismaProduct, PrismaProducts } from "../../types.js"; import { HttpBadRequestError } from "../errors/http.error.js"; class ProductService implements ProductProvider { @@ -5,15 +7,18 @@ class ProductService implements ProductProvider { this.provider = provider; } - getProducts = async (): Promise => { + public getProducts = async (): Promise => { return await this.provider.getProducts(); }; - getProductById = async (id: string): Promise => { + public getProductById = async (id: string): Promise => { + if (!id) { + throw new HttpBadRequestError(); + } return await this.provider.getProductById(id); }; - createProduct = async ( + public createProduct = async ( title: string, size: string, color: string, @@ -23,7 +28,7 @@ class ProductService implements ProductProvider { price: number, imageUrl: string, url: string - ): Promise => { + ): Promise => { if ( !title || !size || @@ -50,7 +55,7 @@ class ProductService implements ProductProvider { ); }; - deleteProduct = async (id: string): Promise => { + public deleteProduct = async (id: string): Promise => { return await this.provider.deleteProduct(id); }; } diff --git a/backend/src/services/scanner.service.ts b/backend/src/services/scanner.service.ts index a6c54ee..f8d0a20 100644 --- a/backend/src/services/scanner.service.ts +++ b/backend/src/services/scanner.service.ts @@ -1,51 +1,35 @@ -import Tesseract from 'tesseract.js'; //Javascript OCR +import ScannerProvider from "../abstracts/scanner.abstract.js"; +import { Tag } from "../../types.js"; +import { HttpBadRequestError } from "../errors/http.error.js"; -type Tag = { - material: string; - percentage: string; -} +class ScannerService implements ScannerProvider { + constructor(private provider: ScannerProvider) { + this.provider = provider; + } -class ScannerService{ + public getMaterials = (text: string): Tag[] => { + const regex = /(100|\d{1,2})% *(\b\w+\b)/g; + const matches = text.matchAll(regex); + const scannedTags: Tag[] = []; - getTextFromImage = async(imagePath: string): Promise => { - console.log("getTextFromImage called"); - // return "20% Cotton 40% Shit 40% Rizz"; - return new Promise((resolve, reject) => { - console.log("Test1"); - Tesseract.recognize(imagePath,'eng',{ - logger: info => console.log(info), // optional logger function - }) - .then(({ data: { text } }) => { - console.log("Test2"); - resolve(text); - console.log("text: ",text); - return text; - }) - .catch((error) => { - console.log("Test2.5"); - reject(error); - }); + for (const match of matches) { + if (match) { + scannedTags.push({ + material: match[2], + percentage: match[1], }); } + } - getMaterials = async(txt: string): Promise => { - console.log("getMaterials called"); - const regex = /(100|\d{1,2})% *(\b\w+\b)/g; - let m; - let scannedTags: Tag[] = []; - do { - m = regex.exec(txt); - if (m){ - scannedTags.push({ - material: m[2], - percentage: m[1] - }); - } - }while (m); - console.log(JSON.stringify(scannedTags)); - return JSON.stringify(scannedTags); - } -} + return scannedTags; + }; + public getTextFromImage = async (imagePath: string): Promise => { + if (!imagePath) { + throw new HttpBadRequestError(); + } + return await this.provider.getTextFromImage(imagePath); + }; +} -export default ScannerService; \ No newline at end of file +export default ScannerService; diff --git a/backend/types.ts b/backend/types.ts index 474bcc1..dc0005e 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -1,28 +1,20 @@ -import { Request, Response, NextFunction } from "express"; +export type PrismaProduct = { + id: string; + title: string; + size: string; + color: string; + description: string; + gender: string; + category: string; + price: number; + imageUrl: string; + createdAt: Date; + updatedAt: Date; +}; -export enum Synthetic { - NYLON = "NYLON", - POLYESTER = "POLYESTER", - SPANDEX = "SPANDEX", - ACETATE = "ACETATE", - CUPRO = "CUPRO", - FLANNEL = "FLANNEL", - LYOCELL = "LYOCELL", - PVC = "PVC", - RAYON = "RAYON", - PET = "PET", - TYVEK = "TYVEK", - VINYLON = "VINYLON", -} +export type PrismaProducts = PrismaProduct[]; -export interface AlgorithmControllerInterface { - postAlgorithm: ( - req: Request, - res: Response, - next: NextFunction - ) => Promise>>; -} - -export interface AlgorithmServiceInterface { - isClothingEcoFriendly(prompt: string): Promise; -} +export type Tag = { + material: string; + percentage: string; +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..459e9c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "fashion", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}