upgrade send report

This commit is contained in:
JOSEPH LE 2024-06-15 11:36:36 +07:00
parent 084ae2c823
commit d9d4bbfdb8
98 changed files with 588 additions and 45 deletions

View File

@ -3,15 +3,20 @@
namespace Modules\Admin\app\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Mail\WorklogReport;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use App\Traits\HasSearchRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Services\JiraService;
use Carbon\Carbon;
use DateTime;
use DateTimeZone;
use GuzzleHttp\Promise\Utils;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Mail;
class JiraController extends Controller
{
@ -91,7 +96,7 @@ class JiraController extends Controller
public function fetchIssuesByProject(Request $request)
{
$project = ['key'=>$request->key, 'name'=> $request->name];
$project = ['key' => $request->key, 'name' => $request->name];
$allIssues = [];
if ($project['key'] !== 'GCT') {
@ -102,27 +107,27 @@ class JiraController extends Controller
$checked = true;
// while ($checked) {
$response = $this->jiraService->getIssues($project['key'], $startAt);
$total = $response['total'];
$issueLength = count($response['issues']);
$response = $this->jiraService->getIssues($project['key'], $startAt);
$total = $response['total'];
$issueLength = count($response['issues']);
foreach ($response['issues'] as $issue) {
$issues[] = [
'summary' => $issue['fields']['summary'] ?? null,
'desc' => $issue['fields']['description']['content'][0] ?? null,
'assignee' => $issue['fields']['assignee']['displayName'] ?? null,
'status' => $issue['fields']['status']['name'] ?? null,
'worklogs' => json_encode(array_map(function ($log) {
return [
'author' => $log['author']['displayName'] ?? null,
'timeSpent' => $log['timeSpent'] ?? null,
'started' => $log['started'] ?? null,
'updated' => $log['updated'] ?? null,
];
}, $issue['fields']['worklog']['worklogs'] ?? []), JSON_PRETTY_PRINT),
'originalEstimate' => isset($issue['fields']['timeoriginalestimate']) ? $issue['fields']['timeoriginalestimate'] / 60 / 60 : null,
'timeSpent' => isset($issue['fields']['timespent']) ? $issue['fields']['timespent'] / 60 / 60 : null
];
foreach ($response['issues'] as $issue) {
$issues[] = [
'summary' => $issue['fields']['summary'] ?? null,
'desc' => $issue['fields']['description']['content'][0] ?? null,
'assignee' => $issue['fields']['assignee']['displayName'] ?? null,
'status' => $issue['fields']['status']['name'] ?? null,
'worklogs' => json_encode(array_map(function ($log) {
return [
'author' => $log['author']['displayName'] ?? null,
'timeSpent' => $log['timeSpent'] ?? null,
'started' => $log['started'] ?? null,
'updated' => $log['updated'] ?? null,
];
}, $issue['fields']['worklog']['worklogs'] ?? []), JSON_PRETTY_PRINT),
'originalEstimate' => isset($issue['fields']['timeoriginalestimate']) ? $issue['fields']['timeoriginalestimate'] / 60 / 60 : null,
'timeSpent' => isset($issue['fields']['timespent']) ? $issue['fields']['timespent'] / 60 / 60 : null
];
// }
// if (($startAt + $issueLength >= $total && $total !== 0) || $total === 0) {
@ -147,6 +152,7 @@ class JiraController extends Controller
'status' => false
], 500);
}
public function exportToExcel()
{
$allIssues = $this->fetchAllIssues()->original;
@ -169,4 +175,76 @@ class JiraController extends Controller
return response()->json(['error' => $e->getMessage()], 500);
}
}
public function sendReport()
{
$now = new DateTime('now', new DateTimeZone('Asia/Bangkok'));
$dateFormatted = $now->format('Y-m-d');
$workLogs = $this->jiraService->getAllUserWorkLogs('2024-06-14', '2024-06-14');
// $tasks = [
// [
// 'status' => 'Done',
// 'summary' => '[LT-37] TEST TỔNG THỂ & BUILD PRODUCTION GD 1',
// 'estimate' => '8H',
// 'total_time_spent' => '8.5H',
// 'time_spent' => '2H',
// 'start_date' => '00:30 2024/05/28',
// 'comment' => 'Check on stage'
// ],
// [
// 'status' => 'In Progress',
// 'summary' => '[IPSPro-826] TEST TASK ERP',
// 'estimate' => '0H',
// 'total_time_spent' => '13H',
// 'time_spent' => '1H',
// 'start_date' => '03:30 2024/05/28',
// 'comment' => 'check ERP update Incoming & PO add SN'
// ]
// ];
$tasksByUser = $this->formatWorkLogsByUser($workLogs);
Mail::to('luanlt632000@gmail.com')->send(new WorklogReport($tasksByUser));
// return "Email sent successfully!";
return response()->json([
'data' => $tasksByUser,
'status' => true
], 200);
}
private function formatWorkLogsByUser($workLogs)
{
// Assuming each workLog has a 'user' field
$tasksByUser = [];
foreach ($workLogs as $log) {
$user = $log['username'];
if (!isset($tasksByUser[$user])) {
$tasksByUser[$user] = [];
}
foreach ($log['information']['issues'] as $issue) {
// $today = Carbon::today('Asia/Ho_Chi_Minh');
$today = Carbon::create('2024','6','14');
$filteredWorklogs = [];
foreach ($issue['fields']['worklog']['worklogs'] as $worklog) {
$started = Carbon::parse($worklog['started']);
if ($started->isSameDay($today) && $worklog['updateAuthor']['displayName'] == $user) {
$filteredWorklogs[] = $worklog;
}
}
$tasksByUser[$user][] = [
'status' => $issue['fields']['status']['name'],
'summary' => $issue['fields']['summary'],
'estimate' => $issue['fields']['timeoriginalestimate'],
'time_spent' => $issue['fields']['timespent'],
'worklogs' => $filteredWorklogs,
];
}
}
return $tasksByUser;
}
}

View File

@ -97,34 +97,38 @@ Route::middleware('api')
'prefix' => 'jira',
], function () {
Route::get('/fetch-issues', [JiraController::class, 'fetchAllIssues']);
Route::get('/export-issues', [JiraController::class, 'exportToExcel']);
Route::get('/all-project', [JiraController::class, 'getAllProject']);
Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']);
Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs'])->middleware('check.permission:admin');
});
Route::group([
'prefix' => 'timekeeping',
], function () {
Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff');
});
Route::group([
'prefix' => 'tracking',
], function () {
Route::post('/create', [TrackingController::class, 'create'])->middleware('check.permission:admin.hr');
});
Route::group([
'prefix' => 'timekeeping',
], function () {
Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff');
});
Route::group([
'prefix' => 'tracking',
], function () {
Route::post('/create', [TrackingController::class, 'create'])->middleware('check.permission:admin.hr');
Route::post('/update', [TrackingController::class, 'update'])->middleware('check.permission:admin.hr');
Route::get('/delete', [TrackingController::class, 'delete'])->middleware('check.permission:admin.hr');
});
});
});
});
});
Route::group([
'prefix' => 'v1/admin/tracking',
], function () {
Route::get('/', [TrackingController::class, 'get'])->middleware('check.permission:admin.hr.staff');
Route::post('/scan-create', [TrackingController::class, 'create']);
Route::post('/send-image', [TrackingController::class, 'saveImage']);
// Route::get('/clear-cache', [SettingController::class, 'clearCache']);
});
Route::group([
'prefix' => 'v1/admin/tracking',
], function () {
Route::get('/', [TrackingController::class, 'get'])->middleware('check.permission:admin.hr.staff');
Route::post('/scan-create', [TrackingController::class, 'create']);
Route::post('/send-image', [TrackingController::class, 'saveImage']);
// Route::get('/clear-cache', [SettingController::class, 'clearCache']);
});
'prefix' => 'v1/admin/jira',
], function () {
Route::get('/send-worklog-report', [JiraController::class, 'sendReport']);
});

View File

@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class DailyAPICall extends Command
{
protected $signature = 'daily:api-call';
protected $description = 'Call API daily and store results';
public function handle()
{
// Gọi API
$response = Http::get(env('APP_URL').'/api/v1/admin/jira/send-worklog-report');
// Xử lý dữ liệu và lưu vào cơ sở dữ liệu (ví dụ)
// $data = $response->json();
// Nếu cần lưu dữ liệu vào database, bạn có thể sử dụng Eloquent
// Ghi log hoặc thông báo khi cần thiết
$this->info('API called successfully!');
}
}

View File

@ -13,6 +13,9 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void
{
$schedule->command('clean_logs')->daily();
// Chạy command 'daily:api-call' vào mỗi ngày lúc 9h sáng
// $schedule->command('daily:api-call')
// ->dailyAt('18:00');
}
/**

View File

@ -0,0 +1,29 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class WorklogReport extends Mailable
{
use Queueable, SerializesModels;
public $tasksByUser;
public function __construct($tasksByUser)
{
$this->tasksByUser = $tasksByUser;
}
public function build()
{
return $this->markdown('email.worklog')
->subject('Worklog Report')
->with('tasksByUser', $this->tasksByUser);
}
}

View File

@ -0,0 +1,99 @@
@component('mail::message')
<div id="main">
<div id="list">
@foreach($tasksByUser as $user => $tasks)
<a href="#{{str_replace(' ', '', $user)}}">{{ $user }}</a><br>
@endforeach
</div>
<div id="list-user">
<strong>Worklog Report</strong>
@foreach($tasksByUser as $user => $tasks)
<div class="user" id="{{str_replace(' ', '', $user)}}">
<strong style="margin-bottom: 15px;">{{ $user }}</strong>
@foreach($tasks as $task)
<div class="task {{ strtolower(str_replace(' ', '', $task['status'])) }}">
<strong>{{ $task['summary'] }}</strong><br>
Status: {{ $task['status'] }}<br>
Estimate: {{ (int)$task['estimate']/60/60 }}h<br>
@foreach($task['worklogs'] as $worklog)
<div class="worklog">
Time spent: {{ $worklog['timeSpent'] }}<br>
Start date: {{ $worklog['started'] }}<br>
{{ isset($worklog['comment']) && isset($worklog['comment']['content'][0]['content'][0])&& isset($worklog['comment']['content'][0]['content'][0]['text']) ? 'Comment: ' . $worklog['comment']['content'][0]['content'][0]['text'] : "" }}
</div>
<br>
@endforeach
</div>
@endforeach
</div>
@endforeach
</div>
</div>
Thanks,<br>
{{ config('app.name') }}
@endcomponent
<style>
#main{
position: relative;
display: flex;
width: 1000px;
}
#list{
position: sticky;
top: 10px;
left: 10px;
z-index: 1000;
width: 20%;
}
#list-user{
z-index: 100;
width: 80%;
max-height: 700px;
overflow: auto;
}
.user{
border: solid 2px gray;
border-radius: 5px;
padding: 10px;
margin-bottom: 10px;
}
.task{
margin-bottom: 5px;
}
.task.done {
background-color: #e0ffe0;
padding: 10px;
border-radius: 5px;
}
.task.inprogress {
background-color: #fff8e0;
padding: 10px;
border-radius: 5px;
}
.task.backlog {
background-color: #EEEEEE;
padding: 10px;
border-radius: 5px;
}
.task.todo {
background-color: #ADD8E6;
padding: 10px;
border-radius: 5px;
}
.worklog{
border: solid 1px gray;
border-radius: 5px;
padding: 10px;
font-size: 13px;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

4
TrackingTool/README.md Normal file
View File

@ -0,0 +1,4 @@
## Create QR code for users
1. Add users to the listUser.xlsx file.
2. Run script file createQR.py
3. The code will be saved in the QRCode folder

45
TrackingTool/createQR.py Normal file
View File

@ -0,0 +1,45 @@
import tkinter as tk
from tkinter import messagebox
import qrcode
import openpyxl
def create_qr_codes():
# Đọc dữ liệu từ tệp Excel
wb = openpyxl.load_workbook('listUser.xlsx')
sheet = wb.active
first_row_skipped = False
# Duyệt qua từng dòng trong tệp Excel và tạo mã QR code
for row in sheet.iter_rows(values_only=True):
name, role = row[0], row[1]
# Bỏ qua dòng đầu tiên
if not first_row_skipped:
first_row_skipped = True
continue
if name != None and role != None:
# Tạo nội dung cho mã QR code
qr_data = f"{name}\n{role}\n\n"
# Tạo mã QR code
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(qr_data)
qr.make(fit=True)
# Lấy hình ảnh mã QR code
img = qr.make_image(fill_color="black", back_color="white")
# Lưu hình ảnh mã QR code vào tệp
img_file_name = f"./QRCode/{name}_{role}_qr_code.png"
img.save(img_file_name)
print(f"QR code created for {name}, {role}")
messagebox.showinfo("Success", "QR codes created successfully.")
create_qr_codes()

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

BIN
TrackingTool/listUser.xlsx Normal file

Binary file not shown.

6
TrackingTool/package.txt Normal file
View File

@ -0,0 +1,6 @@
pyttsx3
openpyxl
pyzbar
opencv-python
qrcode
pyautogui

228
TrackingTool/tracking.py Normal file
View File

@ -0,0 +1,228 @@
import os
import cv2 as cv
import openpyxl
import pyttsx3
import pyautogui
from pyzbar import pyzbar
from datetime import datetime
import requests
# Khởi tạo danh sách rỗng để lưu trữ thông tin người dùng
user_data = []
history = []
screen_width = 1250
screen_height = 1100
WINDOW_QR_CODE = "QR Code"
WINDOW_TRACKING = "Tracking"
WINDOW_HISTORY = "History"
URL_API = "http://localhost:8000/api/v1"
data = [0]
# Hàm thông báo bằng giọng nói
def speak(text):
engine = pyttsx3.init()
engine.say(text)
engine.runAndWait()
def send_image(id, file_name):
# Ensure id is a string if it's an integer
id = str(id)
today_date = datetime.now().strftime("%Y_%m_%d")
folder_path = f"./images/{today_date}"
# Ensure the directory exists
if not os.path.exists(folder_path):
os.makedirs(folder_path)
# Ensure the full file path is correct
file_path = os.path.join(folder_path, file_name)
# Check if the file exists
if not os.path.isfile(file_path):
print(f"Error: The file {file_path} does not exist.")
return {"status": False, "message": f"The file {file_path} does not exist."}
with open(file_path, 'rb') as image_file:
files = {'image': image_file}
# Correct payload for the data parameter
data = {'id': id, 'file_name': file_name}
print(files)
try:
response = requests.post(URL_API + "/admin/tracking/send-image", data=data, files=files)
response.raise_for_status()
res = response.json()
except requests.exceptions.RequestException as e:
print(f"HTTP Request failed: {e}")
return {"status": False, "message": str(e)}
# Check if the request was successful
if res.get('status') == True:
# Process the returned data
print(res)
return res
else:
return res
def create_history(frame, data):
# Gửi yêu cầu POST với dữ liệu đã chỉ định
response = requests.post(URL_API+"/admin/tracking/scan-create", data=data)
res = response.json()
# Kiểm tra xem gửi yêu cầu có thành công hay không
if res.get('status') == True:
# Xử lý dữ liệu trả về
print(res)
return res
else:
display_text(frame, res.get('data'), (25, 25), 0.7, (6, 6, 255), 2)
speak(res.get('data'))
return res
# Hàm để ghi thông tin vào tệp Excel
def write_to_excel(name, time, check_type):
try:
# Mở workbook hiện có
workbook = openpyxl.load_workbook("./data/"+time.strftime("%Y_%m")+"_check_in_out.xlsx")
sheet = workbook.active
except FileNotFoundError:
# Tạo workbook mới nếu tệp không tồn tại
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.append(["Name", "Role", "Time", "Check Type"])
# Ghi thông tin vào các ô trong tệp Excel
sheet.append([name.split('\n')[0].strip(), name.split('\n')[1].strip(), time, check_type])
# Lưu tệp Excel
workbook.save("./data/"+time.strftime("%Y_%m")+"_check_in_out.xlsx")
def check_response(res, frame, name, timestamp, text):
if res.get('status'):
display_text(frame, f"{text.split('\n')[0].strip()} {res.get('check_status')}", (25, 25), 0.7, (0, 255, 10), 2)
display_text(frame, f"{text.split('\n')[1]}", (25, 50), 0.7, (0, 255, 10), 2)
display_text(frame, f"{datetime.now()}", (25, 75), 0.7, (0, 255, 10), 2)
display_text(frame, f"Sucessful", (25, 100), 0.7, (0, 255, 10), 2)
write_to_excel(name, timestamp, res.get('check_status'))
speak(res.get('check_status') + " success")
return True
else:
display_text(frame, f"Call API fail", (25, 50), 0.7, (6, 6, 255), 2)
speak("Call API fail")
cv.waitKey(2000)
return False
# Hàm để thêm thông tin mới vào danh sách
def check_in(name, frame, text):
timestamp = datetime.now()
user_data.append({"name": name, "check_in_time": timestamp})
res = create_history(frame, {"name": name.split('\n')[0], "time_string": f"{datetime.now()}", "status": "check in"})
result = check_response(res, frame, name, timestamp, text)
return res
# cv.waitKey(5000)
# Hàm để xóa thông tin khi check out
def check_out(name, frame, text):
for user in user_data:
if user["name"] == name:
timestamp = datetime.now()
user["check_out_time"] = timestamp
print(f"{name} đã check out lúc {timestamp}")
user_data.remove(user)
res = create_history(frame, {"name": name.split('\n')[0], "time_string": f"{datetime.now()}", "status": "check out"})
result = check_response(res, frame, name, timestamp, text)
if result:
return res
# Hàm để hiển thị văn bản lên hình ảnh
def display_text(frame, text, position, font_scale, color, thickness):
cv.putText(frame, text, position, cv.FONT_HERSHEY_COMPLEX, font_scale, color, thickness)
def screenshot_window(save_path):
today_date = datetime.now().strftime("%Y_%m_%d")
folder_path = f"./images/{today_date}"
# Kiểm tra xem thư mục đã tồn tại chưa
if not os.path.exists(folder_path):
# Nếu thư mục chưa tồn tại, tạo mới
os.makedirs(folder_path)
print(f"Folder '{today_date}' created successfully!")
screenshot = pyautogui.screenshot(region=(10, 10, screen_width, screen_height))
screenshot.save(folder_path+"/"+save_path)
print("Screenshot saved successfully!")
# Hàm để xử lý quá trình quét mã QR code
def process_qr_code(frame):
decoded_objects = pyzbar.decode(frame)
for obj in decoded_objects:
(x, y, w, h) = obj.rect
cv.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 255), 2)
text = obj.data.decode('utf-8')
file_name = ""
status = ""
id_log = 0
if text.endswith("\n\n"):
if text not in [user["name"] for user in user_data]:
print(f"{text} đã check in lúc {datetime.now()}")
status += "check in"
file_name+=text.split('\n')[0]+"_"+f"{status}_at_{datetime.now().strftime("%Y_%m_%d_%H_%M_%S")}.png"
# screenshot_window(file_name)
res = check_in(text, frame, text)
id_log = res.get('data').get('id')
else:
print(f"{text} đã check out lúc {datetime.now()}")
status += "check out"
file_name+=text.split('\n')[0]+"_"+f"{status}_at_{datetime.now().strftime("%Y_%m_%d_%H_%M_%S")}.png"
# screenshot_window(file_name)
res = check_out(text, frame, text)
id_log = res.get('data').get('id')
cv.namedWindow(WINDOW_QR_CODE, cv.WINDOW_NORMAL)
cv.resizeWindow(WINDOW_QR_CODE, screen_width, screen_height)
cv.imshow(WINDOW_QR_CODE, frame)
cv.moveWindow(WINDOW_QR_CODE, 10, 10)
cv.waitKey(5000) # Chờ 5 giây
screenshot_window(file_name)
send_image(id_log, file_name)
cv.destroyWindow(WINDOW_QR_CODE)
else:
display_text(frame, f"QR invalid", (25, 25), 0.7, (6, 6, 255), 2)
display_text(frame, f"Failed", (25, 50), 0.7, (6, 6, 255), 2)
speak("Failed")
cv.namedWindow(WINDOW_QR_CODE, cv.WINDOW_NORMAL)
cv.resizeWindow(WINDOW_QR_CODE, screen_width, screen_height)
cv.imshow(WINDOW_QR_CODE, frame)
cv.moveWindow(WINDOW_QR_CODE, 10, 10)
cv.waitKey(2000)
cv.destroyWindow(WINDOW_QR_CODE)
return frame
# Khởi tạo camera
def main():
cap = cv.VideoCapture(0)
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml')
cv.namedWindow(WINDOW_TRACKING, cv.WINDOW_NORMAL)
cv.resizeWindow(WINDOW_TRACKING, screen_width, screen_height)
while True:
ret, frame = cap.read()
if not ret:
break
# Convert the frame to grayscale
gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# Detect faces in the frame
faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=25, minSize=(30, 30))
# Draw rectangles around the faces
if len(faces) == 1:
for (x, y, w, h) in faces:
cv.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
display_text(frame, f"Face detected", (430, 25), 0.7, (0, 255, 0), 2)
frame = process_qr_code(frame)
else:
display_text(frame, f"Face not found", (430, 25), 0.7, (6, 6, 255), 2)
cv.imshow(WINDOW_TRACKING, frame)
cv.moveWindow(WINDOW_TRACKING, 10, 10)
if cv.waitKey(1) == ord('q'):
break
cap.release()
cv.destroyAllWindows()
main()

View File

@ -0,0 +1,21 @@
const express = require('express');
const app = express();
const port = 3000;
// Middleware to parse JSON bodies
app.use(express.json());
// Define a simple route
app.get('/', (req, res) => {
res.send('Hello, World!');
});
// Define another route
app.get('/webhooks/webhook1', (req, res) => {
console.log(req.body)
});
// Start the server
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});