diff --git a/backend/scanner-service/src/abstract/scanner.abstract.ts b/backend/scanner-service/src/abstract/scanner.abstract.ts index 933bd6c..6282aba 100644 --- a/backend/scanner-service/src/abstract/scanner.abstract.ts +++ b/backend/scanner-service/src/abstract/scanner.abstract.ts @@ -1,7 +1,11 @@ -import { ScannerRequest, Tag } from "../types"; +import { ScannerRequest, Tag, Info, Flags } from "../types"; abstract class ScannerProvider { - abstract getMaterials(text: string): Tag[]; + abstract getMaterials(text: string): Flags; + + abstract getSustainability(arr: Flags): Info; + + abstract checkPercent(arr: Tag[]): Tag[]; abstract getTextFromImage(scannerRequest: ScannerRequest): Promise; } diff --git a/backend/scanner-service/src/controller/scanner.controller.ts b/backend/scanner-service/src/controller/scanner.controller.ts index 3a36e6c..5607cf8 100644 --- a/backend/scanner-service/src/controller/scanner.controller.ts +++ b/backend/scanner-service/src/controller/scanner.controller.ts @@ -1,4 +1,4 @@ -import { ScannerRequest, Tag } from "../types.js"; +import { ScannerRequest, Tag, Info, Flags } from "../types.js"; import ScannerProvider from "../abstract/scanner.abstract.js"; import { Request, Response, NextFunction } from "express"; @@ -14,8 +14,9 @@ class ScannerController { ): Promise> | void> => { try { const text: string = await this.service.getTextFromImage(req.body); - const materials: Tag[] = this.service.getMaterials(text); - return res.status(201).json(materials); + const materials: Flags = this.service.getMaterials(text); + const score: Info = this.service.getSustainability(materials); + return res.status(201).json(score); } catch (e) { next(e); } diff --git a/backend/scanner-service/src/repository/scanner.repository.ts b/backend/scanner-service/src/repository/scanner.repository.ts index 7bbc5d0..26e853a 100644 --- a/backend/scanner-service/src/repository/scanner.repository.ts +++ b/backend/scanner-service/src/repository/scanner.repository.ts @@ -1,13 +1,18 @@ import ScannerProvider from "../abstract/scanner.abstract.js"; import { createWorker } from "tesseract.js"; -import { ScannerRequest, Tag } from "../types.js"; +import { ScannerRequest, Tag, Info, Flags} from "../types.js"; import { TesseractServiceError } from "../error/tesseract.error.js"; class ScannerRepository implements ScannerProvider { - public getMaterials = (text: string): Tag[] => { + public getMaterials = (text: string): Flags => { + throw new Error("Method not implemented."); + }; + public checkPercent = (arr: Tag[]): Tag[] => { + throw new Error("Method not implemented."); + }; + public getSustainability = (arr: Flags): Info => { throw new Error("Method not implemented."); }; - public getTextFromImage = async ( scannerRequest: ScannerRequest, ): Promise => { @@ -17,6 +22,7 @@ class ScannerRepository implements ScannerProvider { scannerRequest.imageUrl, ); await worker.terminate(); + console.log(ret.data.text); return ret.data.text; } catch (e) { throw new TesseractServiceError(); diff --git a/backend/scanner-service/src/service/scanner.service.ts b/backend/scanner-service/src/service/scanner.service.ts index 4956baa..adf7eed 100644 --- a/backend/scanner-service/src/service/scanner.service.ts +++ b/backend/scanner-service/src/service/scanner.service.ts @@ -1,26 +1,137 @@ import ScannerProvider from "../abstract/scanner.abstract.js"; -import { ScannerRequest, Tag } from "../types.js"; +import { + ScannerRequest, + Tag, + sustainable, + Info, + Flags, + countries, +} from "../types.js"; class ScannerService implements ScannerProvider { constructor(private provider: ScannerProvider) { this.provider = provider; } - public getMaterials = (text: string): Tag[] => { - const regex = /(100|\d{1,2})% *(\b\w+\b)/g; - const matches = text.matchAll(regex); + public getMaterials = (text: string): Flags => { + const materialRegex = /(100|\d{1,2})% *(\b\w+\b)/g; + const countryRegex = /(Made\s+in|Product\s+of) *(\b\w+\b)/gi; + const materialMatches = text.matchAll(materialRegex); + const countryMatches = text.matchAll(countryRegex); + let dryClean = false; + let coldWater = false; + let lineDry = false; + const scannedTags: Tag[] = []; + let country = ""; - for (const match of matches) { + for (const match of materialMatches) { if (match) { scannedTags.push({ material: match[2], percentage: match[1], }); + console.log(match[2]); + } + } + + for (const match of countryMatches) { + if (match) { + country = match[2]; + console.log("Country: ", country); + } + } + + if (text.match(/dry\s+clean/gi)) { + console.log("dry cleaning"); + dryClean = true; + } + + if (text.match(/cold/gi)) { + console.log("cold water"); + coldWater = true; + } + if (text.match(/(line|hang)\s+dry/gi)) { + lineDry = true; + } + const infoFound: Flags = { + country: country, + dryClean: dryClean, + coldWater: coldWater, + lineDry: lineDry, + tags: this.checkPercent(scannedTags), + }; + return infoFound; + }; + + public checkPercent = (arr: Tag[]): Tag[] => { + let total = 0; + const res: Tag[] = []; + for (let i = 0; i < arr.length; i++) { + if (total < 100) { + res.push(arr[i]); + total += +arr[i].percentage; } } + if (total != 100) { + console.log("Composition did not add to 100%"); + return []; + } + return res; + }; - return scannedTags; + public getSustainability = (arr: Flags): Info => { + let score = 0; + let notes = ""; + if (arr.tags.length == 0) { + notes = "Composition did not add to 100%"; + } + for (let i = 0; i < arr.tags.length; i++) { + if (sustainable.includes(arr.tags[i].material.toLowerCase())) { + score += +arr.tags[i].percentage; + notes = notes.concat(" " + arr.tags[i].material + " is sustainable "); + } else { + notes = notes.concat( + " " + arr.tags[i].material + " is not sustainable", + ); + } + } + if (arr.country != "") { + if (countries.includes(arr.country.toLowerCase())) { + score += 50; + notes = notes.concat( + ". Produced in " + + arr.country + + " which is has strong environmental and labor regulations", + ); + } else { + notes = notes.concat( + ". Produced in " + + arr.country + + " which does not have strong environmental and labor regulations", + ); + } + } + if (arr.lineDry) { + score += 20; + notes = notes.concat( + ". Line drying is recommended, which consumes less energy", + ); + } + if (arr.coldWater) { + score += 20; + notes = notes.concat( + ". Cold water is recommended, which consumes less energy", + ); + } + if (arr.dryClean) { + score -= 20; + notes = notes.concat( + ". Dry cleaning is recommended, which consumes more energy", + ); + } + const info: Info = { score: score, notes: notes }; + return info; }; public getTextFromImage = async ( diff --git a/backend/scanner-service/src/types.ts b/backend/scanner-service/src/types.ts index d4c3c29..60b90cb 100644 --- a/backend/scanner-service/src/types.ts +++ b/backend/scanner-service/src/types.ts @@ -1,9 +1,64 @@ import { z } from "zod"; import { ScannerSchema } from "./schema/scanner.schema.js"; +export type Info = { + score: number; + notes: string; +} + export type Tag = { material: string; percentage: string; }; +export type Flags = { + country: string; + dryClean: boolean; + coldWater: boolean; + lineDry: boolean; + tags: Tag[]; +}; + export type ScannerRequest = z.infer["body"]; + +export const sustainable: Array = ["organic cotton", +"recycled cotton", +"hemp", +"linen", +"bamboo linen", +"cork", +"econyl", +"recycled polyester", +"deadstock", +"lyocell", +"modal", +"bamboo lyocell", +"ecovero", +"piñatex", +"bananatex", +"scoby leather", +"s.cafe", +"brewed protein", +"apple leather", +"woocoa", +"cupro", +"qmilk", +"sheep wool", +"merino wool", +"alpaca wool", +"cashmere", +"camel", +"yak wool", +"vegetable tanned leather", +"down", +"silk", +"recycled"]; //list of sustainable materials. must be lowercase + +export const countries: Array = ["sweden", +"norway", "denmark", "finland", +"germany", "france", "netherlands", +"canada", "australia", "new zealand", +"switzerland", "austria", "united kingdom", +"belgium", "luxembourg", "iceland", +"japan", "south korea", "costa rica", +"uruguay"]; \ No newline at end of file diff --git a/backend/scanner-service/test/scanner.service.test.ts b/backend/scanner-service/test/scanner.service.test.ts index dcb1338..dd9adc8 100644 --- a/backend/scanner-service/test/scanner.service.test.ts +++ b/backend/scanner-service/test/scanner.service.test.ts @@ -1,7 +1,7 @@ import ScannerProvider from "../src/abstract/scanner.abstract.js"; import { TesseractServiceError } from "../src/error/tesseract.error.js"; import ScannerService from "../src/service/scanner.service.js"; -import { ScannerRequest, Tag } from "../src/types.js"; +import { ScannerRequest, Tag, Info, Flags } from "../src/types.js"; describe("ScannerService Unit Tests", () => { let scannerService: ScannerService; @@ -12,16 +12,17 @@ describe("ScannerService Unit Tests", () => { scannerService = new ScannerService(mockScannerProvider); }); - test("Get materials from text", () => { - const materials: Tag[] = scannerService.getMaterials( - "90% Cotton 10% Nylon", + test("Get info from text", () => { + const info: Flags = scannerService.getMaterials( + "90% Cotton 10% Nylon Made in Canada Dry clean only", ); - expect(materials).toBeInstanceOf(Array); - expect(materials).toHaveLength(2); - expect(materials[0].material).toBe("Cotton"); - expect(materials[0].percentage).toBe("90"); - expect(materials[1].material).toBe("Nylon"); - expect(materials[1].percentage).toBe("10"); + expect(info).toBeInstanceOf(Object); + expect(info.country).toBe("Canada"); + expect(info.tags).toHaveLength(2); + expect(info.tags[0].material).toBe("Cotton"); + expect(info.tags[0].percentage).toBe("90"); + expect(info.tags[1].material).toBe("Nylon"); + expect(info.tags[1].percentage).toBe("10"); }); test("Get text from image", async () => { @@ -44,10 +45,15 @@ describe("ScannerService Unit Tests", () => { }); class MockScannerProvider implements ScannerProvider { - getMaterials(text: string): Tag[] { + getMaterials(text: string): Flags { throw new Error("Method not implemented."); } - + checkPercent = (arr: Tag[]): Tag[] => { + throw new Error("Method not implemented."); + }; + getSustainability = (arr: Flags): Info => { + throw new Error("Method not implemented."); + }; getTextFromImage(scannerRequest: ScannerRequest): Promise { const { imageUrl } = scannerRequest; if (!imageUrl) {