Compare commits

...

1 Commits

Author SHA1 Message Date
Admin 5154464646 update(ttw): run send image in backgound 2025-12-11 14:47:34 +07:00
27 changed files with 95 additions and 48 deletions

Binary file not shown.

View File

@ -1,5 +1,7 @@
Run client: npm run dev or npm run build && npm run preview Run client: npm run dev or npm run build && npm run preview
==> Build client xong => coppy file asset và index vào folder static của server => thêm prefix static vào link của assets trong file index VD: /static/assets ==> Build client xong => coppy file asset và index vào folder static của server => thêm prefix static vào link của assets trong file index VD: /camera/static/assets
Run server uvicorn main:app --reload Run server uvicorn main:app --reload
nohup uvicorn main:app --host 172.16.6.38 --port 8080 > log.log 2>&1 &

View File

@ -6,45 +6,76 @@ from fastapi import UploadFile
URL_API = "https://ms.prology.net/api/v1" URL_API = "https://ms.prology.net/api/v1"
def send_image(id, file: UploadFile, student_name: str, status: str): # def send_image(id, file: UploadFile, student_name: str, status: str):
# id = str(id)
# # Tạo folder theo ngày
# today = datetime.datetime.now().strftime("%Y_%m_%d")
# folder_path = f"./images/{today}"
# if not os.path.exists(folder_path):
# os.makedirs(folder_path)
# # Tạo file name chuẩn
# file_name = (
# f"{student_name}_"
# f"{status}_at_{datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')}.png"
# )
# file_path = os.path.join(folder_path, file_name)
# # Lưu file UploadFile xuống
# with open(file_path, "wb") as f:
# f.write(file.file.read())
# # Mở lại file để gửi API
# with open(file_path, "rb") as image_file:
# files = {"image": image_file}
# data = {"id": id, "file_name": file_name}
# try:
# response = requests.post(
# URL_API + "/admin/tracking/send-image",
# data=data,
# files=files
# )
# response.raise_for_status()
# res = response.json()
# except Exception as e:
# return {"status": False, "message": str(e)}
# return res
def send_image(id, image_bytes, student_name: str, status: str):
id = str(id) id = str(id)
# Tạo folder theo ngày
today = datetime.datetime.now().strftime("%Y_%m_%d") today = datetime.datetime.now().strftime("%Y_%m_%d")
folder_path = f"./images/{today}" folder_path = f"./images/{today}"
os.makedirs(folder_path, exist_ok=True)
if not os.path.exists(folder_path): safe_student = "".join(c for c in student_name if c.isalnum() or c in ("-", "_"))
os.makedirs(folder_path) safe_status = "".join(c for c in status if c.isalnum() or c in ("-", "_"))
timestamp = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
# Tạo file name chuẩn
file_name = (
f"{student_name}_"
f"{status}_at_{datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')}.png"
)
file_name = f"{safe_student}_{safe_status}_at_{timestamp}.png"
file_path = os.path.join(folder_path, file_name) file_path = os.path.join(folder_path, file_name)
# Lưu file UploadFile xuống # Lưu xuống
with open(file_path, "wb") as f: with open(file_path, "wb") as f:
f.write(file.file.read()) f.write(image_bytes)
# Mở lại file để gửi API # Gửi API
with open(file_path, "rb") as image_file: try:
files = {"image": image_file} with open(file_path, "rb") as image_file:
data = {"id": id, "file_name": file_name}
try:
response = requests.post( response = requests.post(
URL_API + "/admin/tracking/send-image", URL_API + "/admin/tracking/send-image",
data=data, data={"id": id, "file_name": file_name},
files=files files={"image": image_file}
) )
response.raise_for_status() response.raise_for_status()
res = response.json() except Exception as e:
except Exception as e: print("Send image failed:", e)
return {"status": False, "message": str(e)}
return res

View File

@ -1,2 +1,3 @@
VITE_API_BASE_URL = "/" VITE_API_BASE_URL = "/camera"
# VITE_API_BASE_URL = "http://127.0.0.1:8000" # VITE_API_BASE_URL = "http://127.0.0.1:8000"
VITE_API_BASE_MS = "https://ms.prology.net"

View File

@ -9,7 +9,7 @@ class MsApi {
Authorization: Authorization:
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL21zLnByb2xvZ3kubmV0L2FwaS92MS9hZG1pbi9sb2dpbiIsImlhdCI6MTc1Njg2MDQ1OSwiZXhwIjoxNzg4Mzk2NDU5LCJuYmYiOjE3NTY4NjA0NTksImp0aSI6IkRrb0NLbHBKV1pkNnZCN0QiLCJzdWIiOiIxNSIsInBydiI6ImQyZmYyOTMzOWE4YTNlODJjMzU4MmE1YThlNzM5ZGYxNzg5YmIxMmYifQ.DoHqHeAGGxpvzlNQ9dAZjZf2Yl573XCgNBT8ZiSx5N4", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL21zLnByb2xvZ3kubmV0L2FwaS92MS9hZG1pbi9sb2dpbiIsImlhdCI6MTc1Njg2MDQ1OSwiZXhwIjoxNzg4Mzk2NDU5LCJuYmYiOjE3NTY4NjA0NTksImp0aSI6IkRrb0NLbHBKV1pkNnZCN0QiLCJzdWIiOiIxNSIsInBydiI6ImQyZmYyOTMzOWE4YTNlODJjMzU4MmE1YThlNzM5ZGYxNzg5YmIxMmYifQ.DoHqHeAGGxpvzlNQ9dAZjZf2Yl573XCgNBT8ZiSx5N4",
}, },
baseURL: "https://ms.prology.net/api/v1/admin", baseURL: import.meta.env.VITE_API_BASE_MS + "/api/v1/admin",
method: "GET", method: "GET",
url: "timekeeping", url: "timekeeping",
params: { params: {

View File

@ -114,10 +114,13 @@ export default function TabFeatures() {
} catch (error) { } catch (error) {
const data = error as AxiosError; const data = error as AxiosError;
toast.error( const message =
(data.response?.data as any)?.message || (data.response?.data as any)?.message ||
"Error In Checking: " + JSON.stringify(data) "Error In Checking: " + JSON.stringify(data);
);
if ((message as string).includes("No face detected")) return;
toast.error(message);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -135,7 +138,7 @@ export default function TabFeatures() {
// ← cách đúng nhất để detect phím cách // ← cách đúng nhất để detect phím cách
e.preventDefault(); // nếu không muốn scroll e.preventDefault(); // nếu không muốn scroll
if (!loading) return; if (loading) return;
captureAndCheck(); captureAndCheck();
} }

View File

@ -17,7 +17,7 @@ import TabFeatures from "./components/tab-features";
export default function Main() { export default function Main() {
const [isSidebarOpen, setIsSidebarOpen] = useState(true); const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true); const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(false);
// const { currentUser, setCurrentUser } = useUserStore(); // const { currentUser, setCurrentUser } = useUserStore();
const { setCanvasRef, setVideoRef } = useAppStore(); const { setCanvasRef, setVideoRef } = useAppStore();
@ -139,7 +139,7 @@ export default function Main() {
</Card> </Card>
</motion.div> </motion.div>
)} )}
</AnimatePresence> */} </AnimatePresence> */}
{isCountDown && ( {isCountDown && (
<CountDown <CountDown

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

View File

@ -1,4 +1,4 @@
from fastapi import FastAPI, UploadFile, File, Form, Depends, HTTPException from fastapi import FastAPI, UploadFile, File, Form, Depends, HTTPException, BackgroundTasks
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
import face_recognition import face_recognition
@ -191,7 +191,7 @@ async def register_student(
@app.post("/checkin") @app.post("/checkin")
async def checkin(file: UploadFile = File(...), camera_id: str = Form("cam1"), db: Session = Depends(get_db)): async def checkin(background_tasks: BackgroundTasks, file: UploadFile = File(...), camera_id: str = Form("cam1"), db: Session = Depends(get_db)):
image_data = await file.read() image_data = await file.read()
path = os.path.join(UPLOAD_DIR, "checkin.jpg") path = os.path.join(UPLOAD_DIR, "checkin.jpg")
with open(path, "wb") as f: with open(path, "wb") as f:
@ -229,7 +229,7 @@ async def checkin(file: UploadFile = File(...), camera_id: str = Form("cam1"), d
"""), """),
{ {
"student_id": encoding.id, "student_id": encoding.id,
"time_threshold": now - datetime.timedelta(minutes=5) "time_threshold": now - datetime.timedelta(minutes=0.5)
} }
).fetchone() ).fetchone()
@ -258,15 +258,14 @@ async def checkin(file: UploadFile = File(...), camera_id: str = Form("cam1"), d
# reset pointer # reset pointer
file.file.seek(0) file.file.seek(0)
send_image_res = send_image( background_tasks.add_task(
id=id_log, send_image,
file=file, id_log,
student_name=encoding.name, image_data, # truyền bytes, không phải UploadFile
status=status encoding.name,
status
) )
print(id_log, send_image_res)
# Insert new checkin # Insert new checkin
db.execute( db.execute(
text(""" text("""

View File

@ -8,7 +8,7 @@
<script <script
type="module" type="module"
crossorigin crossorigin
src="/camera/static/assets/index-NzXShqcn.js" src="/camera/static/assets/index-Cs3L7CRl.js"
></script> ></script>
<link <link
rel="stylesheet" rel="stylesheet"

11
TrackingToolWeb/ultils.py Normal file
View File

@ -0,0 +1,11 @@
from api import send_image
def background_send_image(id_log, file_path, student_name, status):
with open(file_path, "rb") as f:
send_image(
id=id_log,
file=f,
student_name=student_name,
status=status
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 223 KiB