Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scanner score #67

Merged
merged 5 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions backend/scanner-service/src/abstract/scanner.abstract.ts
Original file line number Diff line number Diff line change
@@ -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<string>;
}
Expand Down
7 changes: 4 additions & 3 deletions backend/scanner-service/src/controller/scanner.controller.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -14,8 +14,9 @@ class ScannerController {
): Promise<Response<any, Record<string, any>> | 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);
}
Expand Down
12 changes: 9 additions & 3 deletions backend/scanner-service/src/repository/scanner.repository.ts
Original file line number Diff line number Diff line change
@@ -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<string> => {
Expand All @@ -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();
Expand Down
123 changes: 117 additions & 6 deletions backend/scanner-service/src/service/scanner.service.ts
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
55 changes: 55 additions & 0 deletions backend/scanner-service/src/types.ts
Original file line number Diff line number Diff line change
@@ -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<typeof ScannerSchema>["body"];

export const sustainable: Array<string> = ["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<string> = ["sweden",
"norway", "denmark", "finland",
"germany", "france", "netherlands",
"canada", "australia", "new zealand",
"switzerland", "austria", "united kingdom",
"belgium", "luxembourg", "iceland",
"japan", "south korea", "costa rica",
"uruguay"];
30 changes: 18 additions & 12 deletions backend/scanner-service/test/scanner.service.test.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 () => {
Expand All @@ -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<string> {
const { imageUrl } = scannerRequest;
if (!imageUrl) {
Expand Down
Loading