Skip to content

Commit

Permalink
Merge pull request #67 from techstartucalgary/scanner-score
Browse files Browse the repository at this point in the history
Scanner score
  • Loading branch information
Axeloooo authored Mar 31, 2024
2 parents 8e10747 + 6a90d0d commit fa4cee9
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 26 deletions.
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

0 comments on commit fa4cee9

Please sign in to comment.