Compare commits
1 Commits
master
...
zelda.upda
| Author | SHA1 | Date |
|---|---|---|
|
|
5154464646 |
|
|
@ -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 &
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
|
@ -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("""
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 223 KiB |