matrice-analytics 0.1.70__py3-none-any.whl → 0.1.96__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matrice_analytics/post_processing/__init__.py +8 -2
- matrice_analytics/post_processing/config.py +4 -2
- matrice_analytics/post_processing/core/base.py +1 -1
- matrice_analytics/post_processing/core/config.py +40 -3
- matrice_analytics/post_processing/face_reg/face_recognition.py +1014 -201
- matrice_analytics/post_processing/face_reg/face_recognition_client.py +171 -29
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +19 -0
- matrice_analytics/post_processing/post_processor.py +4 -0
- matrice_analytics/post_processing/usecases/__init__.py +4 -1
- matrice_analytics/post_processing/usecases/advanced_customer_service.py +913 -500
- matrice_analytics/post_processing/usecases/color_detection.py +19 -18
- matrice_analytics/post_processing/usecases/customer_service.py +356 -9
- matrice_analytics/post_processing/usecases/fire_detection.py +241 -23
- matrice_analytics/post_processing/usecases/footfall.py +750 -0
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +638 -40
- matrice_analytics/post_processing/usecases/people_counting.py +66 -33
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +35 -34
- matrice_analytics/post_processing/usecases/weapon_detection.py +2 -1
- matrice_analytics/post_processing/utils/alert_instance_utils.py +1018 -0
- matrice_analytics/post_processing/utils/business_metrics_manager_utils.py +1338 -0
- matrice_analytics/post_processing/utils/incident_manager_utils.py +1754 -0
- {matrice_analytics-0.1.70.dist-info → matrice_analytics-0.1.96.dist-info}/METADATA +1 -1
- {matrice_analytics-0.1.70.dist-info → matrice_analytics-0.1.96.dist-info}/RECORD +26 -22
- {matrice_analytics-0.1.70.dist-info → matrice_analytics-0.1.96.dist-info}/WHEEL +0 -0
- {matrice_analytics-0.1.70.dist-info → matrice_analytics-0.1.96.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_analytics-0.1.70.dist-info → matrice_analytics-0.1.96.dist-info}/top_level.txt +0 -0
|
@@ -7,6 +7,7 @@ in the post-processing pipeline using Matrice Session.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
+
import re
|
|
10
11
|
import base64
|
|
11
12
|
import logging
|
|
12
13
|
import httpx
|
|
@@ -14,6 +15,7 @@ import urllib
|
|
|
14
15
|
import urllib.request
|
|
15
16
|
from typing import List, Dict, Any, Optional
|
|
16
17
|
from datetime import datetime, timezone
|
|
18
|
+
from pathlib import Path
|
|
17
19
|
|
|
18
20
|
# Import matrice session
|
|
19
21
|
try:
|
|
@@ -29,6 +31,78 @@ class FacialRecognitionClient:
|
|
|
29
31
|
Simplified Face Recognition Client using Matrice Session.
|
|
30
32
|
All API calls are made through the Matrice session RPC interface.
|
|
31
33
|
"""
|
|
34
|
+
|
|
35
|
+
# Pattern for matching action IDs (hex strings of at least 8 characters)
|
|
36
|
+
ACTION_ID_PATTERN = re.compile(r"^[0-9a-f]{8,}$", re.IGNORECASE)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def _discover_action_id(cls) -> Optional[str]:
|
|
40
|
+
"""Discover action_id from current working directory name (and parents)."""
|
|
41
|
+
candidates: List[str] = []
|
|
42
|
+
try:
|
|
43
|
+
cwd = Path.cwd()
|
|
44
|
+
candidates.append(cwd.name)
|
|
45
|
+
for parent in cwd.parents:
|
|
46
|
+
candidates.append(parent.name)
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
usr_src = Path("/usr/src")
|
|
52
|
+
if usr_src.exists():
|
|
53
|
+
for child in usr_src.iterdir():
|
|
54
|
+
if child.is_dir():
|
|
55
|
+
candidates.append(child.name)
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
for candidate in candidates:
|
|
60
|
+
if candidate and len(candidate) >= 8 and cls.ACTION_ID_PATTERN.match(candidate):
|
|
61
|
+
return candidate
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def _fetch_project_id_from_action(self) -> Optional[str]:
|
|
65
|
+
"""
|
|
66
|
+
Fetch project ID from action details using discovered action ID.
|
|
67
|
+
|
|
68
|
+
This method discovers the action ID from the working directory name,
|
|
69
|
+
fetches action details from the API, and extracts the _idProject field.
|
|
70
|
+
If successful, it also updates the MATRICE_PROJECT_ID environment variable.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
The project ID string if found, None otherwise.
|
|
74
|
+
"""
|
|
75
|
+
action_id = self._discover_action_id()
|
|
76
|
+
if not action_id:
|
|
77
|
+
self.logger.warning("[PROJECT_ID] Could not discover action_id from folder name")
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
self.logger.info(f"[PROJECT_ID] Discovered action_id from folder: {action_id}")
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
url = f"/v1/actions/action/{action_id}/details"
|
|
84
|
+
self.logger.info(f"[PROJECT_ID] Fetching action details from: {url}")
|
|
85
|
+
response = self.session.rpc.get(url)
|
|
86
|
+
|
|
87
|
+
if response and response.get("success", False) and response.get("code") == 200:
|
|
88
|
+
data = response.get("data", {})
|
|
89
|
+
project_id = data.get("_idProject", "")
|
|
90
|
+
|
|
91
|
+
if project_id:
|
|
92
|
+
self.logger.info(f"[PROJECT_ID] Successfully fetched project ID from action details: {project_id}")
|
|
93
|
+
# Update environment variable so other components can use it
|
|
94
|
+
os.environ["MATRICE_PROJECT_ID"] = project_id
|
|
95
|
+
self.logger.info(f"[PROJECT_ID] Updated MATRICE_PROJECT_ID environment variable: {project_id}")
|
|
96
|
+
return project_id
|
|
97
|
+
else:
|
|
98
|
+
self.logger.warning(f"[PROJECT_ID] _idProject not found in action details for action_id={action_id}")
|
|
99
|
+
else:
|
|
100
|
+
error_msg = response.get('message', 'Unknown error') if response else 'Empty response'
|
|
101
|
+
self.logger.warning(f"[PROJECT_ID] Failed to fetch action details: {error_msg}")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
self.logger.error(f"[PROJECT_ID] Error fetching action details for action_id={action_id}: {e}", exc_info=True)
|
|
104
|
+
|
|
105
|
+
return None
|
|
32
106
|
|
|
33
107
|
def __init__(self, account_number: str = "", access_key: str = "", secret_key: str = "",
|
|
34
108
|
project_id: str = "", server_id: str = "", session=None):
|
|
@@ -75,6 +149,23 @@ class FacialRecognitionClient:
|
|
|
75
149
|
except Exception as e:
|
|
76
150
|
self.logger.error(f"Failed to initialize Matrice session: {e}", exc_info=True)
|
|
77
151
|
raise
|
|
152
|
+
|
|
153
|
+
# If project_id is still empty, try to fetch from action details
|
|
154
|
+
if not self.project_id:
|
|
155
|
+
self.logger.info("[PROJECT_ID] Project ID is empty, attempting to fetch from action details...")
|
|
156
|
+
fetched_project_id = self._fetch_project_id_from_action()
|
|
157
|
+
if fetched_project_id:
|
|
158
|
+
self.project_id = fetched_project_id
|
|
159
|
+
self.logger.info(f"[PROJECT_ID] Successfully set project_id from action details: {self.project_id}")
|
|
160
|
+
# Update session with the new project_id if possible
|
|
161
|
+
if hasattr(self.session, 'update'):
|
|
162
|
+
try:
|
|
163
|
+
self.session.update(self.project_id)
|
|
164
|
+
self.logger.info(f"[PROJECT_ID] Updated session with project_id: {self.project_id}")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
self.logger.warning(f"[PROJECT_ID] Failed to update session with project_id: {e}")
|
|
167
|
+
else:
|
|
168
|
+
self.logger.warning("[PROJECT_ID] Could not fetch project_id from action details")
|
|
78
169
|
|
|
79
170
|
# Fetch server connection info if server_id is provided
|
|
80
171
|
if self.server_id:
|
|
@@ -93,12 +184,29 @@ class FacialRecognitionClient:
|
|
|
93
184
|
self.server_base_url = f"http://{server_host}:{server_port}"
|
|
94
185
|
self.logger.warning(f"Facial recognition server base URL: {self.server_base_url}")
|
|
95
186
|
|
|
96
|
-
|
|
97
|
-
|
|
187
|
+
# Update project_id from server_info if available and current project_id is empty
|
|
188
|
+
server_project_id = self.server_info.get('projectID', '')
|
|
189
|
+
if server_project_id:
|
|
190
|
+
if not self.project_id:
|
|
191
|
+
self.project_id = server_project_id
|
|
192
|
+
self.logger.info(f"[PROJECT_ID] Set project_id from server_info: {self.project_id}")
|
|
193
|
+
# Update environment variable
|
|
194
|
+
os.environ["MATRICE_PROJECT_ID"] = self.project_id
|
|
195
|
+
self.logger.info(f"[PROJECT_ID] Updated MATRICE_PROJECT_ID env var from server_info: {self.project_id}")
|
|
196
|
+
self.session.update(server_project_id)
|
|
197
|
+
self.logger.info(f"Updated Matrice session with project ID: {server_project_id}")
|
|
198
|
+
else:
|
|
199
|
+
self.logger.warning("[PROJECT_ID] server_info.projectID is empty")
|
|
98
200
|
else:
|
|
99
201
|
self.logger.warning("Failed to fetch facial recognition server connection info")
|
|
100
202
|
except Exception as e:
|
|
101
203
|
self.logger.error(f"Error fetching facial recognition server connection info: {e}", exc_info=True)
|
|
204
|
+
|
|
205
|
+
# Final check: log the project_id status
|
|
206
|
+
if self.project_id:
|
|
207
|
+
self.logger.info(f"[PROJECT_ID] Final project_id: {self.project_id}")
|
|
208
|
+
else:
|
|
209
|
+
self.logger.error("[PROJECT_ID] WARNING: project_id is still empty after all initialization attempts!")
|
|
102
210
|
|
|
103
211
|
def _get_public_ip(self) -> str:
|
|
104
212
|
"""Get the public IP address of this machine."""
|
|
@@ -286,6 +394,8 @@ class FacialRecognitionClient:
|
|
|
286
394
|
employee_id: Optional[str] = None,
|
|
287
395
|
timestamp: str = datetime.now(timezone.utc).isoformat(),
|
|
288
396
|
image_data: Optional[str] = None,
|
|
397
|
+
camera_name: Optional[str] = None,
|
|
398
|
+
camera_id: Optional[str] = None,
|
|
289
399
|
) -> Dict[str, Any]:
|
|
290
400
|
"""
|
|
291
401
|
Store people activity data with optional image data
|
|
@@ -310,6 +420,8 @@ class FacialRecognitionClient:
|
|
|
310
420
|
"timestamp": timestamp,
|
|
311
421
|
"bbox": bbox,
|
|
312
422
|
"location": location,
|
|
423
|
+
"camera_name": camera_name,
|
|
424
|
+
"camera_id": camera_id,
|
|
313
425
|
}
|
|
314
426
|
|
|
315
427
|
# Add optional fields if provided based on API spec
|
|
@@ -322,8 +434,8 @@ class FacialRecognitionClient:
|
|
|
322
434
|
if image_data:
|
|
323
435
|
activity_request["imageData"] = image_data
|
|
324
436
|
|
|
325
|
-
self.logger.info(f"API REQUEST: Storing people activity - type={detection_type}, staff_id={staff_id}, location={location}, has_image={bool(image_data)}")
|
|
326
|
-
self.logger.debug(f"Activity request payload: bbox={bbox}, employee_id={employee_id}")
|
|
437
|
+
self.logger.info(f"API REQUEST: Storing people activity - type={detection_type}, staff_id={staff_id}, location={location}, camera_name={camera_name}, camera_id={camera_id}, has_image={bool(image_data)}")
|
|
438
|
+
self.logger.debug(f"Activity request payload: bbox={bbox}, employee_id={employee_id}, camera_name={camera_name}, camera_id={camera_id}")
|
|
327
439
|
|
|
328
440
|
try:
|
|
329
441
|
response = await self.session.rpc.async_send_request(
|
|
@@ -474,11 +586,11 @@ class FacialRecognitionClient:
|
|
|
474
586
|
except Exception as e:
|
|
475
587
|
self.logger.error(f"API ERROR: Get all staff embeddings request failed - {e}", exc_info=True)
|
|
476
588
|
return {"success": False, "error": str(e)}
|
|
477
|
-
|
|
478
|
-
async def
|
|
479
|
-
"""Update deployment
|
|
589
|
+
|
|
590
|
+
async def update_deployment_action(self, deployment_id: str) -> Dict[str, Any]:
|
|
591
|
+
"""Update deployment action in backend
|
|
480
592
|
|
|
481
|
-
API: PUT /v1/
|
|
593
|
+
API: PUT /internal/v1/actions/update_facial_recognition_deployment/:server_id?app_deployment_id=:deployment_id
|
|
482
594
|
|
|
483
595
|
Args:
|
|
484
596
|
deployment_id: The deployment ID to update
|
|
@@ -487,34 +599,34 @@ class FacialRecognitionClient:
|
|
|
487
599
|
Dict containing response data
|
|
488
600
|
"""
|
|
489
601
|
if not deployment_id:
|
|
490
|
-
self.logger.warning("No deployment_id provided for
|
|
602
|
+
self.logger.warning("No deployment_id provided for update_deployment_action")
|
|
491
603
|
return {"success": False, "error": "deployment_id is required"}
|
|
492
604
|
|
|
493
|
-
self.logger.info(f"API REQUEST: Updating deployment - deployment_id={deployment_id}")
|
|
605
|
+
self.logger.info(f"API REQUEST: Updating deployment action - deployment_id={deployment_id}")
|
|
494
606
|
|
|
495
|
-
# Use Matrice session for async RPC call
|
|
607
|
+
# Use Matrice session for async RPC call to backend (not facial recognition server).
|
|
496
608
|
try:
|
|
497
609
|
response = await self.session.rpc.async_send_request(
|
|
498
610
|
method="PUT",
|
|
499
|
-
path=f"/v1/
|
|
611
|
+
path=f"/v1/actions/update_facial_recognition_deployment/{self.server_id}?app_deployment_id={deployment_id}",
|
|
500
612
|
payload={},
|
|
501
|
-
base_url=
|
|
613
|
+
base_url="https://prod.backend.app.matrice.ai"
|
|
502
614
|
)
|
|
503
615
|
|
|
504
616
|
if response.get('success', False):
|
|
505
|
-
self.logger.info(f"API RESPONSE: Deployment updated successfully - deployment_id={deployment_id}")
|
|
617
|
+
self.logger.info(f"API RESPONSE: Deployment action updated successfully - deployment_id={deployment_id}")
|
|
506
618
|
else:
|
|
507
|
-
self.logger.warning(f"Failed to update deployment for deployment_id={deployment_id}: {response.get('error', 'Unknown error')}")
|
|
619
|
+
self.logger.warning(f"Failed to update deployment action for deployment_id={deployment_id}: {response.get('error', 'Unknown error')}")
|
|
508
620
|
|
|
509
621
|
return self._handle_response(response)
|
|
510
622
|
except Exception as e:
|
|
511
|
-
self.logger.error(f"API ERROR: Update deployment request failed - deployment_id={deployment_id} - {e}", exc_info=True)
|
|
623
|
+
self.logger.error(f"API ERROR: Update deployment action request failed - deployment_id={deployment_id} - {e}", exc_info=True)
|
|
512
624
|
return {"success": False, "error": str(e)}
|
|
513
625
|
|
|
514
|
-
async def
|
|
515
|
-
"""Update deployment
|
|
626
|
+
async def update_deployment(self, deployment_id: str) -> Dict[str, Any]:
|
|
627
|
+
"""Update deployment to notify facial recognition server
|
|
516
628
|
|
|
517
|
-
API: PUT /
|
|
629
|
+
API: PUT /v1/facial_recognition/update_deployment/:deployment_id
|
|
518
630
|
|
|
519
631
|
Args:
|
|
520
632
|
deployment_id: The deployment ID to update
|
|
@@ -523,28 +635,28 @@ class FacialRecognitionClient:
|
|
|
523
635
|
Dict containing response data
|
|
524
636
|
"""
|
|
525
637
|
if not deployment_id:
|
|
526
|
-
self.logger.warning("No deployment_id provided for
|
|
638
|
+
self.logger.warning("No deployment_id provided for update_deployment")
|
|
527
639
|
return {"success": False, "error": "deployment_id is required"}
|
|
528
640
|
|
|
529
|
-
self.logger.info(f"API REQUEST: Updating deployment
|
|
641
|
+
self.logger.info(f"API REQUEST: Updating deployment - deployment_id={deployment_id}")
|
|
530
642
|
|
|
531
|
-
# Use Matrice session for async RPC call
|
|
643
|
+
# Use Matrice session for async RPC call
|
|
532
644
|
try:
|
|
533
645
|
response = await self.session.rpc.async_send_request(
|
|
534
646
|
method="PUT",
|
|
535
|
-
path=f"/
|
|
647
|
+
path=f"/v1/facial_recognition/update_deployment/{deployment_id}",
|
|
536
648
|
payload={},
|
|
537
|
-
base_url=
|
|
649
|
+
base_url=self.server_base_url
|
|
538
650
|
)
|
|
539
651
|
|
|
540
652
|
if response.get('success', False):
|
|
541
|
-
self.logger.info(f"API RESPONSE: Deployment
|
|
653
|
+
self.logger.info(f"API RESPONSE: Deployment updated successfully - deployment_id={deployment_id}")
|
|
542
654
|
else:
|
|
543
|
-
self.logger.warning(f"Failed to update deployment
|
|
655
|
+
self.logger.warning(f"Failed to update deployment for deployment_id={deployment_id}: {response.get('error', 'Unknown error')}")
|
|
544
656
|
|
|
545
657
|
return self._handle_response(response)
|
|
546
658
|
except Exception as e:
|
|
547
|
-
self.logger.error(f"API ERROR: Update deployment
|
|
659
|
+
self.logger.error(f"API ERROR: Update deployment request failed - deployment_id={deployment_id} - {e}", exc_info=True)
|
|
548
660
|
return {"success": False, "error": str(e)}
|
|
549
661
|
|
|
550
662
|
async def enroll_unknown_person(self, embedding: List[float], image_source: str = None, timestamp: str = None, location: str = None, employee_id: str = None) -> Dict[str, Any]:
|
|
@@ -602,17 +714,47 @@ class FacialRecognitionClient:
|
|
|
602
714
|
payload={},
|
|
603
715
|
base_url=self.server_base_url
|
|
604
716
|
)
|
|
605
|
-
|
|
717
|
+
|
|
606
718
|
if response.get('success', False):
|
|
607
719
|
self.logger.info(f"API RESPONSE: Service is healthy")
|
|
608
720
|
else:
|
|
609
721
|
self.logger.warning(f"Health check failed: {response.get('error', 'Unknown error')}")
|
|
610
|
-
|
|
722
|
+
|
|
611
723
|
return self._handle_response(response)
|
|
612
724
|
except Exception as e:
|
|
613
725
|
self.logger.error(f"API ERROR: Health check request failed - {e}", exc_info=True)
|
|
614
726
|
return {"success": False, "error": str(e)}
|
|
615
727
|
|
|
728
|
+
async def get_redis_details(self) -> Dict[str, Any]:
|
|
729
|
+
"""Get Redis connection details from facial recognition server
|
|
730
|
+
|
|
731
|
+
API: GET /v1/facial_recognition/get_redis_details
|
|
732
|
+
|
|
733
|
+
Returns:
|
|
734
|
+
Dict containing Redis connection details (REDIS_IP, REDIS_PORT, REDIS_PASSWORD)
|
|
735
|
+
"""
|
|
736
|
+
|
|
737
|
+
self.logger.info(f"API REQUEST: Getting Redis connection details")
|
|
738
|
+
|
|
739
|
+
# Use Matrice session for async RPC call
|
|
740
|
+
try:
|
|
741
|
+
response = await self.session.rpc.async_send_request(
|
|
742
|
+
method="GET",
|
|
743
|
+
path=f"/v1/facial_recognition/get_redis_details",
|
|
744
|
+
payload={},
|
|
745
|
+
base_url=self.server_base_url
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
if response.get('success', False):
|
|
749
|
+
self.logger.info(f"API RESPONSE: Redis details retrieved successfully")
|
|
750
|
+
else:
|
|
751
|
+
self.logger.warning(f"Failed to get Redis details: {response.get('error', 'Unknown error')}")
|
|
752
|
+
|
|
753
|
+
return self._handle_response(response)
|
|
754
|
+
except Exception as e:
|
|
755
|
+
self.logger.error(f"API ERROR: Get Redis details request failed - {e}", exc_info=True)
|
|
756
|
+
return {"success": False, "error": str(e)}
|
|
757
|
+
|
|
616
758
|
def _handle_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
|
|
617
759
|
"""Handle RPC response and errors"""
|
|
618
760
|
try:
|
|
@@ -4,6 +4,7 @@ import time
|
|
|
4
4
|
import threading
|
|
5
5
|
import queue
|
|
6
6
|
import base64
|
|
7
|
+
import os
|
|
7
8
|
from typing import Dict, Optional, Set
|
|
8
9
|
import numpy as np
|
|
9
10
|
import cv2
|
|
@@ -18,6 +19,15 @@ class PeopleActivityLogging:
|
|
|
18
19
|
self.face_client = face_client
|
|
19
20
|
self.logger = logging.getLogger(__name__)
|
|
20
21
|
|
|
22
|
+
# Log project ID information for observability and debugging
|
|
23
|
+
face_client_project_id = getattr(self.face_client, "project_id", None) if self.face_client else None
|
|
24
|
+
env_project_id = os.getenv("MATRICE_PROJECT_ID", "")
|
|
25
|
+
self.logger.info(
|
|
26
|
+
"[PROJECT_ID] PeopleActivityLogging initialized "
|
|
27
|
+
f"with face_client.project_id='{face_client_project_id}', "
|
|
28
|
+
f"MATRICE_PROJECT_ID env='{env_project_id}'"
|
|
29
|
+
)
|
|
30
|
+
|
|
21
31
|
# Use thread-safe queue for cross-thread communication (Python 3.8 compatibility)
|
|
22
32
|
self.activity_queue = queue.Queue()
|
|
23
33
|
|
|
@@ -95,6 +105,8 @@ class PeopleActivityLogging:
|
|
|
95
105
|
detection: Dict,
|
|
96
106
|
current_frame: Optional[np.ndarray] = None,
|
|
97
107
|
location: str = "",
|
|
108
|
+
camera_name: str = "",
|
|
109
|
+
camera_id: str = "",
|
|
98
110
|
):
|
|
99
111
|
"""Enqueue a detection for background processing"""
|
|
100
112
|
try:
|
|
@@ -103,6 +115,8 @@ class PeopleActivityLogging:
|
|
|
103
115
|
"detection": detection,
|
|
104
116
|
"current_frame": current_frame,
|
|
105
117
|
"location": location,
|
|
118
|
+
"camera_name": camera_name,
|
|
119
|
+
"camera_id": camera_id,
|
|
106
120
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
107
121
|
"employee_id": detection.get("employee_id", None),
|
|
108
122
|
"staff_id": detection.get("person_id")
|
|
@@ -178,7 +192,10 @@ class PeopleActivityLogging:
|
|
|
178
192
|
location = activity_data["location"]
|
|
179
193
|
staff_id = activity_data["staff_id"]
|
|
180
194
|
timestamp = activity_data["timestamp"]
|
|
195
|
+
camera_name = activity_data.get("camera_name", "")
|
|
196
|
+
camera_id = activity_data.get("camera_id", "")
|
|
181
197
|
|
|
198
|
+
self.logger.debug(f"Processing activity - location: '{location}', camera_name: '{camera_name}', camera_id: '{camera_id}'")
|
|
182
199
|
try:
|
|
183
200
|
if not self.face_client:
|
|
184
201
|
self.logger.warning("Face client not available for activity logging")
|
|
@@ -211,6 +228,8 @@ class PeopleActivityLogging:
|
|
|
211
228
|
employee_id=employee_id,
|
|
212
229
|
timestamp=timestamp,
|
|
213
230
|
image_data=image_data,
|
|
231
|
+
camera_name=camera_name,
|
|
232
|
+
camera_id=camera_id,
|
|
214
233
|
)
|
|
215
234
|
|
|
216
235
|
if response and response.get("success", False):
|
|
@@ -99,6 +99,7 @@ from .usecases import (
|
|
|
99
99
|
UndergroundPipelineDefectUseCase,
|
|
100
100
|
SusActivityUseCase,
|
|
101
101
|
NaturalDisasterUseCase,
|
|
102
|
+
FootFallUseCase,
|
|
102
103
|
# Put all IMAGE based usecases here
|
|
103
104
|
BloodCancerDetectionUseCase,
|
|
104
105
|
SkinCancerClassificationUseCase,
|
|
@@ -569,6 +570,9 @@ class PostProcessor:
|
|
|
569
570
|
registry.register_use_case(
|
|
570
571
|
"environmental", "natural_disaster_detection", NaturalDisasterUseCase
|
|
571
572
|
)
|
|
573
|
+
registry.register_use_case(
|
|
574
|
+
"retail", "footfall", FootFallUseCase
|
|
575
|
+
)
|
|
572
576
|
|
|
573
577
|
# Put all IMAGE based usecases here
|
|
574
578
|
registry.register_use_case(
|
|
@@ -85,6 +85,8 @@ from .pcb_defect_detection import PCBDefectConfig, PCBDefectUseCase
|
|
|
85
85
|
from .underground_pipeline_defect_detection import UndergroundPipelineDefectConfig,UndergroundPipelineDefectUseCase
|
|
86
86
|
from .suspicious_activity_detection import SusActivityConfig, SusActivityUseCase
|
|
87
87
|
from .natural_disaster import NaturalDisasterConfig, NaturalDisasterUseCase
|
|
88
|
+
from .footfall import FootFallConfig, FootFallUseCase
|
|
89
|
+
|
|
88
90
|
|
|
89
91
|
#Put all IMAGE based usecases here
|
|
90
92
|
from .blood_cancer_detection_img import BloodCancerDetectionConfig, BloodCancerDetectionUseCase
|
|
@@ -172,6 +174,7 @@ __all__ = [
|
|
|
172
174
|
'UndergroundPipelineDefectUseCase',
|
|
173
175
|
'SusActivityUseCase',
|
|
174
176
|
'NaturalDisasterUseCase',
|
|
177
|
+
'FootFallUseCase',
|
|
175
178
|
|
|
176
179
|
#Put all IMAGE based usecases here
|
|
177
180
|
'BloodCancerDetectionUseCase',
|
|
@@ -254,7 +257,7 @@ __all__ = [
|
|
|
254
257
|
'PCBDefectConfig',
|
|
255
258
|
'SusActivityConfig',
|
|
256
259
|
'NaturalDisasterConfig',
|
|
257
|
-
|
|
260
|
+
'FootFallConfig',
|
|
258
261
|
#Put all IMAGE based usecase CONFIGS here
|
|
259
262
|
'BloodCancerDetectionConfig',
|
|
260
263
|
'SkinCancerClassificationConfig',
|