Commit ·
f64ea82
1
Parent(s): 13bd96e
vfdhbvdjh
Browse files- controllers/main.py +59 -4
controllers/main.py
CHANGED
|
@@ -3798,6 +3798,39 @@ def _get_admin_from_token(authorization: str | None) -> dict:
|
|
| 3798 |
return auth["user"]
|
| 3799 |
|
| 3800 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3801 |
def _serialize_notification_rows(rows: list[dict]) -> list[dict]:
|
| 3802 |
for row in rows:
|
| 3803 |
for field in ("attendance_time", "created_at", "read_at"):
|
|
@@ -3925,6 +3958,9 @@ def save_log_to_db(log_queries: list) -> None:
|
|
| 3925 |
log_queries,
|
| 3926 |
)
|
| 3927 |
conn.commit()
|
|
|
|
|
|
|
|
|
|
| 3928 |
|
| 3929 |
notification_queries = []
|
| 3930 |
for log_id, person_id, status, confidence, camera, action in log_queries:
|
|
@@ -4667,6 +4703,10 @@ async def employee_me(authorization: str | None = Header(default=None)):
|
|
| 4667 |
@app.get("/api/employee/notifications")
|
| 4668 |
async def employee_notifications(authorization: str | None = Header(default=None)):
|
| 4669 |
employee = _get_employee_from_token(authorization)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4670 |
conn = cursor = None
|
| 4671 |
try:
|
| 4672 |
conn = get_db_connection()
|
|
@@ -4678,7 +4718,7 @@ async def employee_notifications(authorization: str | None = Header(default=None
|
|
| 4678 |
order="DESC",
|
| 4679 |
)
|
| 4680 |
unread = sum(1 for row in rows if row.get("status") == "unread")
|
| 4681 |
-
return {"success": True, "data": rows, "unread": unread}
|
| 4682 |
except Exception as e:
|
| 4683 |
logger.error(f"[Employee Notifications] {employee.get('person_id')}: {e}", exc_info=True)
|
| 4684 |
return {
|
|
@@ -4707,6 +4747,11 @@ async def employee_attendance_calendar(
|
|
| 4707 |
if target_month < 1 or target_month > 12:
|
| 4708 |
return JSONResponse(status_code=400, content={"success": False, "error": "Thang khong hop le"})
|
| 4709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4710 |
start_date = date(target_year, target_month, 1)
|
| 4711 |
if target_month == 12:
|
| 4712 |
end_date = date(target_year + 1, 1, 1)
|
|
@@ -4725,7 +4770,7 @@ async def employee_attendance_calendar(
|
|
| 4725 |
include_day_key=True,
|
| 4726 |
order="ASC",
|
| 4727 |
)
|
| 4728 |
-
return {"success": True, "data": rows, "year": target_year, "month": target_month}
|
| 4729 |
except Exception as e:
|
| 4730 |
logger.error(f"[Employee Attendance] {employee.get('person_id')}: {e}", exc_info=True)
|
| 4731 |
return {
|
|
@@ -4775,6 +4820,7 @@ async def mark_employee_notifications_read(
|
|
| 4775 |
)
|
| 4776 |
|
| 4777 |
conn.commit()
|
|
|
|
| 4778 |
return {"success": True, "updated": cursor.rowcount}
|
| 4779 |
finally:
|
| 4780 |
if cursor:
|
|
@@ -4878,6 +4924,10 @@ async def admin_employee_notifications(
|
|
| 4878 |
authorization: str | None = Header(default=None),
|
| 4879 |
):
|
| 4880 |
_get_admin_from_token(authorization)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4881 |
conn = cursor = None
|
| 4882 |
try:
|
| 4883 |
conn = get_db_connection()
|
|
@@ -4889,7 +4939,7 @@ async def admin_employee_notifications(
|
|
| 4889 |
order="DESC",
|
| 4890 |
)
|
| 4891 |
unread = sum(1 for row in rows if row.get("status") == "unread")
|
| 4892 |
-
return {"success": True, "data": rows, "unread": unread}
|
| 4893 |
except Exception as e:
|
| 4894 |
logger.error(f"[Admin Employee Notifications] {person_id}: {e}", exc_info=True)
|
| 4895 |
return {
|
|
@@ -4919,6 +4969,11 @@ async def admin_employee_attendance_calendar(
|
|
| 4919 |
if target_month < 1 or target_month > 12:
|
| 4920 |
return JSONResponse(status_code=400, content={"success": False, "error": "Thang khong hop le"})
|
| 4921 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4922 |
start_date = date(target_year, target_month, 1)
|
| 4923 |
end_date = date(target_year + 1, 1, 1) if target_month == 12 else date(target_year, target_month + 1, 1)
|
| 4924 |
|
|
@@ -4934,7 +4989,7 @@ async def admin_employee_attendance_calendar(
|
|
| 4934 |
include_day_key=True,
|
| 4935 |
order="ASC",
|
| 4936 |
)
|
| 4937 |
-
return {"success": True, "data": rows, "year": target_year, "month": target_month}
|
| 4938 |
except Exception as e:
|
| 4939 |
logger.error(f"[Admin Employee Attendance] {person_id}: {e}", exc_info=True)
|
| 4940 |
return {
|
|
|
|
| 3798 |
return auth["user"]
|
| 3799 |
|
| 3800 |
|
| 3801 |
+
ATTENDANCE_CACHE_TTL_SECONDS = 15
|
| 3802 |
+
_attendance_cache: dict[str, tuple[float, dict]] = {}
|
| 3803 |
+
|
| 3804 |
+
|
| 3805 |
+
def _attendance_cache_get(key: str) -> dict | None:
|
| 3806 |
+
cached = _attendance_cache.get(key)
|
| 3807 |
+
if not cached:
|
| 3808 |
+
return None
|
| 3809 |
+
expires_at, payload = cached
|
| 3810 |
+
if expires_at <= time.time():
|
| 3811 |
+
_attendance_cache.pop(key, None)
|
| 3812 |
+
return None
|
| 3813 |
+
return payload
|
| 3814 |
+
|
| 3815 |
+
|
| 3816 |
+
def _attendance_cache_set(key: str, payload: dict, ttl: int = ATTENDANCE_CACHE_TTL_SECONDS) -> dict:
|
| 3817 |
+
_attendance_cache[key] = (time.time() + ttl, payload)
|
| 3818 |
+
if len(_attendance_cache) > 500:
|
| 3819 |
+
now = time.time()
|
| 3820 |
+
for item_key, (expires_at, _) in list(_attendance_cache.items()):
|
| 3821 |
+
if expires_at <= now:
|
| 3822 |
+
_attendance_cache.pop(item_key, None)
|
| 3823 |
+
return payload
|
| 3824 |
+
|
| 3825 |
+
|
| 3826 |
+
def _attendance_cache_clear_person(person_id: str) -> None:
|
| 3827 |
+
marker = f":{person_id}:"
|
| 3828 |
+
suffix = f":{person_id}"
|
| 3829 |
+
for key in list(_attendance_cache.keys()):
|
| 3830 |
+
if marker in key or key.endswith(suffix):
|
| 3831 |
+
_attendance_cache.pop(key, None)
|
| 3832 |
+
|
| 3833 |
+
|
| 3834 |
def _serialize_notification_rows(rows: list[dict]) -> list[dict]:
|
| 3835 |
for row in rows:
|
| 3836 |
for field in ("attendance_time", "created_at", "read_at"):
|
|
|
|
| 3958 |
log_queries,
|
| 3959 |
)
|
| 3960 |
conn.commit()
|
| 3961 |
+
for _, person_id, status, *_ in log_queries:
|
| 3962 |
+
if person_id and status == "success":
|
| 3963 |
+
_attendance_cache_clear_person(person_id)
|
| 3964 |
|
| 3965 |
notification_queries = []
|
| 3966 |
for log_id, person_id, status, confidence, camera, action in log_queries:
|
|
|
|
| 4703 |
@app.get("/api/employee/notifications")
|
| 4704 |
async def employee_notifications(authorization: str | None = Header(default=None)):
|
| 4705 |
employee = _get_employee_from_token(authorization)
|
| 4706 |
+
cache_key = f"employee_notifications:{employee['person_id']}"
|
| 4707 |
+
cached = _attendance_cache_get(cache_key)
|
| 4708 |
+
if cached:
|
| 4709 |
+
return cached
|
| 4710 |
conn = cursor = None
|
| 4711 |
try:
|
| 4712 |
conn = get_db_connection()
|
|
|
|
| 4718 |
order="DESC",
|
| 4719 |
)
|
| 4720 |
unread = sum(1 for row in rows if row.get("status") == "unread")
|
| 4721 |
+
return _attendance_cache_set(cache_key, {"success": True, "data": rows, "unread": unread})
|
| 4722 |
except Exception as e:
|
| 4723 |
logger.error(f"[Employee Notifications] {employee.get('person_id')}: {e}", exc_info=True)
|
| 4724 |
return {
|
|
|
|
| 4747 |
if target_month < 1 or target_month > 12:
|
| 4748 |
return JSONResponse(status_code=400, content={"success": False, "error": "Thang khong hop le"})
|
| 4749 |
|
| 4750 |
+
cache_key = f"employee_attendance:{employee['person_id']}:{target_year}:{target_month}"
|
| 4751 |
+
cached = _attendance_cache_get(cache_key)
|
| 4752 |
+
if cached:
|
| 4753 |
+
return cached
|
| 4754 |
+
|
| 4755 |
start_date = date(target_year, target_month, 1)
|
| 4756 |
if target_month == 12:
|
| 4757 |
end_date = date(target_year + 1, 1, 1)
|
|
|
|
| 4770 |
include_day_key=True,
|
| 4771 |
order="ASC",
|
| 4772 |
)
|
| 4773 |
+
return _attendance_cache_set(cache_key, {"success": True, "data": rows, "year": target_year, "month": target_month})
|
| 4774 |
except Exception as e:
|
| 4775 |
logger.error(f"[Employee Attendance] {employee.get('person_id')}: {e}", exc_info=True)
|
| 4776 |
return {
|
|
|
|
| 4820 |
)
|
| 4821 |
|
| 4822 |
conn.commit()
|
| 4823 |
+
_attendance_cache_clear_person(employee["person_id"])
|
| 4824 |
return {"success": True, "updated": cursor.rowcount}
|
| 4825 |
finally:
|
| 4826 |
if cursor:
|
|
|
|
| 4924 |
authorization: str | None = Header(default=None),
|
| 4925 |
):
|
| 4926 |
_get_admin_from_token(authorization)
|
| 4927 |
+
cache_key = f"admin_notifications:{person_id}"
|
| 4928 |
+
cached = _attendance_cache_get(cache_key)
|
| 4929 |
+
if cached:
|
| 4930 |
+
return cached
|
| 4931 |
conn = cursor = None
|
| 4932 |
try:
|
| 4933 |
conn = get_db_connection()
|
|
|
|
| 4939 |
order="DESC",
|
| 4940 |
)
|
| 4941 |
unread = sum(1 for row in rows if row.get("status") == "unread")
|
| 4942 |
+
return _attendance_cache_set(cache_key, {"success": True, "data": rows, "unread": unread})
|
| 4943 |
except Exception as e:
|
| 4944 |
logger.error(f"[Admin Employee Notifications] {person_id}: {e}", exc_info=True)
|
| 4945 |
return {
|
|
|
|
| 4969 |
if target_month < 1 or target_month > 12:
|
| 4970 |
return JSONResponse(status_code=400, content={"success": False, "error": "Thang khong hop le"})
|
| 4971 |
|
| 4972 |
+
cache_key = f"admin_attendance:{person_id}:{target_year}:{target_month}"
|
| 4973 |
+
cached = _attendance_cache_get(cache_key)
|
| 4974 |
+
if cached:
|
| 4975 |
+
return cached
|
| 4976 |
+
|
| 4977 |
start_date = date(target_year, target_month, 1)
|
| 4978 |
end_date = date(target_year + 1, 1, 1) if target_month == 12 else date(target_year, target_month + 1, 1)
|
| 4979 |
|
|
|
|
| 4989 |
include_day_key=True,
|
| 4990 |
order="ASC",
|
| 4991 |
)
|
| 4992 |
+
return _attendance_cache_set(cache_key, {"success": True, "data": rows, "year": target_year, "month": target_month})
|
| 4993 |
except Exception as e:
|
| 4994 |
logger.error(f"[Admin Employee Attendance] {person_id}: {e}", exc_info=True)
|
| 4995 |
return {
|