Skip to content

Commit

Permalink
Update: client app
Browse files Browse the repository at this point in the history
  • Loading branch information
NguyenAnhTuan1912 committed Aug 8, 2024
1 parent 534cb5e commit d5b26f1
Show file tree
Hide file tree
Showing 26 changed files with 1,188 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Name of flow
name: Deploy to product environment

# The event of github
# When should it (workflows) run?
on:
# Push to master or main
# and has type is `closed`, but it isn't enough
push:
branches: [master, main]

concurrency:
group: "pages"
cancel-in-progress: false

defaults:
run:
shell: bash

jobs:
prepare:
runs-on: ubuntu-latest
steps:
# Check out to repository
- uses: actions/checkout@v4
with:
submodules: recursive

# Install NODEJS
- uses: actions/setup-node@v4
with:
node-version: 20

# Install dependencies
- run: npm install

build_website:
needs: prepare
runs-on: ubuntu-latest
steps:
# Build website with webpack
# The directory is build/gui
- run: npm run build

upload_to_s3:
needs: build_website
runs-on: ubuntu-latest
steps:
- uses: shallwefootball/s3-upload-action@master
with:
aws_key_id: ${{ secrets.AWS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: ${{ secrets.AWS_BUCKET }}
# Upload entire content of website to S3
source_dir: "build/gui"
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.git
node_modules
uploads
xml
bundle
build
dist

# Python
__pycache__
python/test/images/*
python/test/out/*
python/builds/*
!python/builds/*.md

package-lock.json
17 changes: 17 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"proseWrap": "preserve",
"embeddedLanguageFormatting": "auto",
"requirePragma": false,
"insertPragma": false
}
15 changes: 15 additions & 0 deletions gui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# DIITT GUI Application
Ứng dụng giao diện người dùng là ứng dụng web, giúp cho người dùng và quá trình demo có thể trực quan hóa hơn việc input và output.

## Tech stack
1. Javascript
2. Bootstrap

## About
Ứng dụng giao diện người dùng được xây dựng với HTML, CSS và Javascript, bên cạnh đó còn dùng Boostrap để hỗ trợ việc xây dựng giao diện.
![image](https://github.com/NguyenAnhTuan1912/datatable-image-to-text/assets/86825061/56c266a2-a194-43d2-abef-14b9863b4491)

Ứng dụng GUI sẽ là nơi mà người dùng nhập dữ liệu vào. Chịu trách nhiệm lấy thông tin từ người dùng và gửi về cho ứng dụng API. Khi có kết quả trả về thì sẽ thông báo trên giao diện cho người dùng và mở một nút download. Thì đó là nhiệm vụ chính của GUI.

## Images
Một số hình ảnh của ứng dụng GUI
16 changes: 16 additions & 0 deletions gui/css/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
width: 100%;
height: 100vh;
overflow: auto;
}

.app {
width: 100%;
height: 100vh;
}
28 changes: 28 additions & 0 deletions gui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Datatable in image to text</title>

<!-- Import styles -->
<link rel="stylesheet" href="./css/styles.css" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous"
/>
</head>
<body>
<div class="app" id="app"></div>

<!-- //Import js -->
<script type="module" defer src="./js/index/index.js"></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"
></script>
</body>
</html>
83 changes: 83 additions & 0 deletions gui/js/apis/image/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { API_ROOT } from '../index.js';

const path = API_ROOT + "/image";

/**
* Dùng api caller này để convert một tấm ảnh thành ảnh mờ.
* @param {FormData} data
* @returns
*/
async function convertBlurImageAsync(data) {
const response = await fetch(
path + "/blur_image",
{
method: "post",
body: data
}
);

// Vì trả về là ảnh, không phải là object message, nên không convert thành json.
return response.arrayBuffer();
}

/**
* Dùng api caller này để convert hệ màu của ảnh.
* @param {FormData} data
* @returns
*/
async function convertColorImageAsync(data) {
const response = await fetch(
path + "/change_color_image",
{
method: "post",
body: data
}
);

// Vì trả về là ảnh, không phải là object message, nên không convert thành json.
return response.arrayBuffer();
}

/**
* Dùng api caller này để nhận diện khuôn mặt trong ảnh.
* @param {FormData} data
* @returns
*/
async function recognizeFaceInImageAsync(data) {
const response = await fetch(
path + "/face_recognition_image",
{
method: "post",
body: data
}
);

// Vì trả về là ảnh, không phải là object message, nên không convert thành json.
return response.arrayBuffer();
}

/**
* Dùng api caller này để dùng tính năng chính trong app. Tính năng trích xuất văn bản trong table thành
* file excel.
* @param {FormData} data
* @returns
*/
async function extractDataFromDTInImageAsync(data) {
const response = await fetch(
path + "/datatable_image_to_excel",
{
method: "post",
body: data
}
);

// Vì trả về là ảnh, không phải là object message, nên không convert thành json.
return response.arrayBuffer();
}

export const ImageAPIs = {
convertBlurImageAsync,
convertColorImageAsync,
recognizeFaceInImageAsync,
extractDataFromDTInImageAsync
};
1 change: 1 addition & 0 deletions gui/js/apis/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const API_ROOT = "http://localhost:3000/api";
29 changes: 29 additions & 0 deletions gui/js/classes/Component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export class Component {
/**
* @param {HTMLDivElement} parent
* @param {UtilsType} utils
*/
constructor(parent, utils) {
/**
* @type {HTMLDivElement}
*/
this.parent = parent;
/**
* @type {UtilsType}
*/
this.utils = utils;
this.ref = null;
}

_createContainer() {}

getRef() {
if(!this.ref) this.ref = this._createContainer();
return this.ref;
}

render() {
if(!this.ref) this.ref = this._createContainer();
this.parent.append(this.ref);
}
}
99 changes: 99 additions & 0 deletions gui/js/components/blur_image/BlurImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Import from classes
import { Component } from "../../classes/Component.js";

// Import from apis
import { ImageAPIs } from "../../apis/image/index.js";

// Import from components
import { ImagePicker } from "../image_picker/ImagePicker.js";

// Import types
// import { UtilsType } from "../../utils/index.js";

export class BlurImage extends Component {
/**
* @param {HTMLDivElement} parent
* @param {UtilsType} utils
*/
constructor(parent, utils) {
super(parent, utils);
}

_createContainer() {
// Tạo input
let imgpker = new ImagePicker(this.parent, this.utils);
let strthRge = this.utils.Element.toElement(`
<div class="mb-3">
<label for="strength-range" class="form-label">Blur strength</label>
<input
type="range"
class="form-range"
min="1" max="20" step="1"
id="strength-range"
name="strength-range"
oninput="this.nextElementSibling.value = this.value"
>
<output>11</output>
</div>
`);
let submitBtn = `<button type="submit" class="btn btn-primary mt-3">Làm mờ</button>`;

let imgpkerRef = imgpker.getRef();

// Tạo output
let guide = this.utils.Element.createElement("div", {
className: "blur-image-guide",
children: `
<div>
<h3>Hướng dẫn</h3>
<p>Làm mờ ảnh có một thông số là Blur Strength, độ mạnh của mờ. Càng cao thì ảnh càng mờ, thông số này được giới hạn trong khoảng <strong>[1, 20]</strong>.</p>
<ol>
<li>Ấn vào <strong>Choose File</strong>.</li>
<li>Chọn một tấm ảnh muốn làm mờ.</li>
<li>Chọn độ mạnh của blur và ấn <strong>Làm mờ</strong>.</li>
<li>Chờ Backend thực thi. Sau khi thực thi xong thì sẽ có một popup Download hiện lên, lưu ảnh vào đâu đó.</li>
<li>Mở ảnh trong folder vừa lưu và xem kết quả.</li>
</ol>
</div>
`
});

// Tạo form
let form = this.utils.Element.createElement("form", {
className: "blur-image-form",
children: [strthRge, imgpkerRef, submitBtn],
eventListeners: {
"submit": function(e) {
e.preventDefault();
const formData = new FormData(e.target);
const imageFile = e.target["image"].files[0];

ImageAPIs
.convertBlurImageAsync(formData)
.then(res => {
// Chuyển binary thành base64.
const url = window.URL.createObjectURL(new Blob([res], { type: imageFile.type }));

// Tạo một thẻ a và gán base64 vào href cho thẻ a.
// Set thuộc tính download và tự động tải về.
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', imageFile.name);
document.body.appendChild(link);
link.click();
})

// return false;
}
}
});

// Tạo container
let container = this.utils.Element.createElement("div", {
className: "blur-image-container",
children: [guide, form]
});

return container;
}
}
Loading

0 comments on commit d5b26f1

Please sign in to comment.