matrice-analytics 0.1.3__py3-none-any.whl → 0.1.32__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.

Potentially problematic release.


This version of matrice-analytics might be problematic. Click here for more details.

Files changed (61) hide show
  1. matrice_analytics/post_processing/advanced_tracker/matching.py +3 -3
  2. matrice_analytics/post_processing/advanced_tracker/strack.py +1 -1
  3. matrice_analytics/post_processing/config.py +4 -0
  4. matrice_analytics/post_processing/core/config.py +115 -12
  5. matrice_analytics/post_processing/face_reg/compare_similarity.py +5 -5
  6. matrice_analytics/post_processing/face_reg/embedding_manager.py +109 -8
  7. matrice_analytics/post_processing/face_reg/face_recognition.py +157 -61
  8. matrice_analytics/post_processing/face_reg/face_recognition_client.py +339 -88
  9. matrice_analytics/post_processing/face_reg/people_activity_logging.py +67 -29
  10. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
  11. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
  12. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
  13. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
  14. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
  15. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
  16. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
  17. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
  18. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
  19. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
  20. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
  21. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
  22. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
  23. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
  24. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
  25. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
  26. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
  27. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
  28. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
  29. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
  30. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
  31. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
  32. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
  33. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
  34. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
  35. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
  36. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
  37. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
  38. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
  39. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
  40. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
  41. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
  42. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
  43. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
  44. matrice_analytics/post_processing/ocr/postprocessing.py +0 -1
  45. matrice_analytics/post_processing/post_processor.py +32 -11
  46. matrice_analytics/post_processing/usecases/color/clip.py +42 -8
  47. matrice_analytics/post_processing/usecases/color/color_mapper.py +2 -2
  48. matrice_analytics/post_processing/usecases/color_detection.py +50 -129
  49. matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +41 -386
  50. matrice_analytics/post_processing/usecases/flare_analysis.py +1 -56
  51. matrice_analytics/post_processing/usecases/license_plate_detection.py +476 -202
  52. matrice_analytics/post_processing/usecases/license_plate_monitoring.py +351 -26
  53. matrice_analytics/post_processing/usecases/people_counting.py +408 -1431
  54. matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
  55. matrice_analytics/post_processing/usecases/vehicle_monitoring.py +39 -10
  56. matrice_analytics/post_processing/utils/__init__.py +8 -8
  57. {matrice_analytics-0.1.3.dist-info → matrice_analytics-0.1.32.dist-info}/METADATA +1 -1
  58. {matrice_analytics-0.1.3.dist-info → matrice_analytics-0.1.32.dist-info}/RECORD +61 -26
  59. {matrice_analytics-0.1.3.dist-info → matrice_analytics-0.1.32.dist-info}/WHEEL +0 -0
  60. {matrice_analytics-0.1.3.dist-info → matrice_analytics-0.1.32.dist-info}/licenses/LICENSE.txt +0 -0
  61. {matrice_analytics-0.1.3.dist-info → matrice_analytics-0.1.32.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,8 @@ import os
10
10
  import base64
11
11
  import logging
12
12
  import httpx
13
+ import urllib
14
+ import urllib.request
13
15
  from typing import List, Dict, Any, Optional
14
16
  from datetime import datetime, timezone
15
17
 
@@ -38,6 +40,10 @@ class FacialRecognitionClient:
38
40
  if not self.server_id:
39
41
  raise ValueError("Server ID is required for Face Recognition Client")
40
42
 
43
+ self.server_info = None
44
+ self.server_base_url = None
45
+ self.public_ip = self._get_public_ip()
46
+
41
47
  # Use existing session if provided, otherwise create new one
42
48
  if session is not None:
43
49
  self.session = session
@@ -70,6 +76,60 @@ class FacialRecognitionClient:
70
76
  self.logger.error(f"Failed to initialize Matrice session: {e}", exc_info=True)
71
77
  raise
72
78
 
79
+ # Fetch server connection info if server_id is provided
80
+ if self.server_id:
81
+ try:
82
+ self.server_info = self.get_server_connection_info()
83
+ if self.server_info:
84
+ self.logger.info(f"Successfully fetched facial recognition server info: {self.server_info.get('name', 'Unknown')}")
85
+ # Compare server host with public IP to determine if it's localhost
86
+ server_host = self.server_info.get('host', 'localhost')
87
+ server_port = self.server_info.get('port', 8081)
88
+
89
+ if server_host == self.public_ip:
90
+ self.server_base_url = f"http://localhost:{server_port}"
91
+ self.logger.warning(f"Server host matches public IP, using localhost: {self.server_base_url}")
92
+ else:
93
+ self.server_base_url = f"https://{server_host}:{server_port}"
94
+ self.logger.warning(f"Facial recognition server base URL: {self.server_base_url}")
95
+
96
+ self.session.update(self.server_info.get('projectID', ''))
97
+ self.logger.info(f"Updated Matrice session with project ID: {self.server_info.get('projectID', '')}")
98
+ else:
99
+ self.logger.warning("Failed to fetch facial recognition server connection info")
100
+ except Exception as e:
101
+ self.logger.error(f"Error fetching facial recognition server connection info: {e}", exc_info=True)
102
+
103
+ def _get_public_ip(self) -> str:
104
+ """Get the public IP address of this machine."""
105
+ try:
106
+ public_ip = urllib.request.urlopen("https://v4.ident.me", timeout=120).read().decode("utf8").strip()
107
+ self.logger.warning(f"Successfully fetched external IP: {public_ip}")
108
+ return public_ip
109
+ except Exception as e:
110
+ self.logger.error(f"Error fetching external IP: {e}", exc_info=True)
111
+ return "localhost"
112
+
113
+ def get_server_connection_info(self) -> Optional[Dict[str, Any]]:
114
+ """Fetch server connection info from RPC."""
115
+ if not self.server_id:
116
+ return None
117
+
118
+ try:
119
+ response = self.session.rpc.get(f"/v1/actions/get_facial_recognition_server/{self.server_id}")
120
+ if response.get("success", False) and response.get("code") == 200:
121
+ # Response format:
122
+ # {'success': True, 'code': 200, 'message': 'Success', 'serverTime': '2025-10-21T09:56:14Z',
123
+ # 'data': {'id': '68f28be1f74ae116727448c4', 'name': 'Local Server', 'host': '68.36.82.163', 'port': 8081, 'status': 'active', 'accountNumber': '3823255831182978487149732',
124
+ # 'projectID': '68aff0bbce98491879437909', 'region': 'United States', 'isShared': False}}
125
+ return response.get("data", {})
126
+ else:
127
+ self.logger.warning(f"Failed to fetch server info: {response.get('message', 'Unknown error')}")
128
+ return None
129
+ except Exception as e:
130
+ self.logger.error(f"Exception while fetching server connection info: {e}", exc_info=True)
131
+ return None
132
+
73
133
  async def enroll_staff(self, staff_data: Dict[str, Any], image_paths: List[str]) -> Dict[str, Any]:
74
134
  """
75
135
  Enroll a new staff member with face images
@@ -95,21 +155,40 @@ class FacialRecognitionClient:
95
155
  return await self.enroll_staff_base64(staff_data, base64_images)
96
156
 
97
157
  async def enroll_staff_base64(self, staff_data: Dict[str, Any], base64_images: List[str]) -> Dict[str, Any]:
98
- """Enroll staff with base64 encoded images"""
158
+ """Enroll staff with base64 encoded images
159
+
160
+ API: POST /v1/facial_recognition/staff/enroll?projectId={projectId}&serverID={serverID}
161
+ """
99
162
 
100
- # Prepare enrollment request
163
+ # Prepare enrollment request matching API spec
101
164
  enrollment_request = {
102
- "staff_info": staff_data,
165
+ "staffId": staff_data.get("staffId", ""),
166
+ "firstName": staff_data.get("firstName", ""),
167
+ "lastName": staff_data.get("lastName", ""),
168
+ "email": staff_data.get("email", ""),
169
+ "position": staff_data.get("position", ""),
170
+ "department": staff_data.get("department", ""),
103
171
  "images": base64_images
104
172
  }
105
173
 
174
+ self.logger.info(f"API REQUEST: Enrolling staff with {len(base64_images)} images - Staff ID: {staff_data.get('staffId', 'N/A')}")
175
+ self.logger.debug(f"Enrollment request payload: {list(enrollment_request.keys())}, num_images={len(base64_images)}")
176
+
106
177
  # Use Matrice session for async RPC call
107
- response = await self.session.rpc.async_send_request(
108
- method="POST",
109
- path=f"/v1/actions/facial_recognition/staff/enroll?serverID={self.server_id}",
110
- payload=enrollment_request
111
- )
112
- return self._handle_response(response)
178
+ try:
179
+ response = await self.session.rpc.async_send_request(
180
+ method="POST",
181
+ path=f"/v1/facial_recognition/staff/enroll?projectId={self.project_id}&serverID={self.server_id}",
182
+ payload=enrollment_request,
183
+ base_url=self.server_base_url
184
+ )
185
+ self.logger.info(f"API RESPONSE: Staff enrollment completed - Success: {response.get('success', False)}")
186
+ if not response.get('success', False):
187
+ self.logger.warning(f"Staff enrollment failed: {response.get('error', 'Unknown error')}")
188
+ return self._handle_response(response)
189
+ except Exception as e:
190
+ self.logger.error(f"API ERROR: Staff enrollment request failed - {e}", exc_info=True)
191
+ return {"success": False, "error": str(e)}
113
192
 
114
193
  async def search_similar_faces(self, face_embedding: List[float],
115
194
  threshold: float = 0.3, limit: int = 10,
@@ -119,6 +198,8 @@ class FacialRecognitionClient:
119
198
  """
120
199
  Search for staff members by face embedding vector
121
200
 
201
+ API: POST /v1/facial_recognition/search/similar?projectId={projectId}&serverID={serverID}
202
+
122
203
  Args:
123
204
  face_embedding: Face embedding vector
124
205
  collection: Vector collection name
@@ -134,29 +215,67 @@ class FacialRecognitionClient:
134
215
  "embedding": face_embedding,
135
216
  "collection": collection,
136
217
  "threshold": threshold,
137
- "limit": limit,
138
- "location": location,
139
- "timestamp": timestamp
218
+ "limit": limit
140
219
  }
220
+
221
+ # Add optional fields only if provided
222
+ if location:
223
+ search_request["location"] = location
224
+ if timestamp:
225
+ search_request["timestamp"] = timestamp
226
+
227
+ self.logger.debug(f"API REQUEST: Searching similar faces - threshold={threshold}, limit={limit}, collection={collection}, location={location}")
141
228
 
142
229
  # Use Matrice session for async RPC call
143
- response = await self.session.rpc.async_send_request(
144
- method="POST",
145
- path=f"/v1/actions/facial_recognition/search/similar?serverID={self.server_id}",
146
- payload=search_request
147
- )
148
- return self._handle_response(response)
230
+ try:
231
+ response = await self.session.rpc.async_send_request(
232
+ method="POST",
233
+ path=f"/v1/facial_recognition/search/similar?projectId={self.project_id}&serverID={self.server_id}",
234
+ payload=search_request,
235
+ base_url=self.server_base_url
236
+ )
237
+
238
+ results_count = 0
239
+ if response.get('success', False):
240
+ data = response.get('data', [])
241
+ results_count = len(data) if isinstance(data, list) else 0
242
+ self.logger.info(f"API RESPONSE: Face search completed - Found {results_count} matches")
243
+ if results_count > 0:
244
+ self.logger.debug(f"Top match: staff_id={data[0].get('staffId', 'N/A')}, score={data[0].get('score', 0):.3f}")
245
+ else:
246
+ self.logger.warning(f"Face search failed: {response.get('error', 'Unknown error')}")
247
+
248
+ return self._handle_response(response)
249
+ except Exception as e:
250
+ self.logger.error(f"API ERROR: Face search request failed - {e}", exc_info=True)
251
+ return {"success": False, "error": str(e)}
149
252
 
150
253
  async def get_staff_details(self, staff_id: str) -> Dict[str, Any]:
151
- """Get full staff details by staff ID"""
254
+ """Get full staff details by staff ID
255
+
256
+ API: GET /v1/facial_recognition/staff/:staffId?projectId={projectId}&serverID={serverID}
257
+ """
258
+
259
+ self.logger.debug(f"API REQUEST: Getting staff details - staff_id={staff_id}")
152
260
 
153
261
  # Use Matrice session for async RPC call
154
- response = await self.session.rpc.async_send_request(
155
- method="GET",
156
- path=f"/v1/actions/facial_recognition/staff/{staff_id}?serverID={self.server_id}",
157
- payload={}
158
- )
159
- return self._handle_response(response)
262
+ try:
263
+ response = await self.session.rpc.async_send_request(
264
+ method="GET",
265
+ path=f"/v1/facial_recognition/staff/{staff_id}?projectId={self.project_id}&serverID={self.server_id}",
266
+ payload={},
267
+ base_url=self.server_base_url
268
+ )
269
+
270
+ if response.get('success', False):
271
+ self.logger.info(f"API RESPONSE: Staff details retrieved successfully - staff_id={staff_id}")
272
+ else:
273
+ self.logger.warning(f"Failed to get staff details for staff_id={staff_id}: {response.get('error', 'Unknown error')}")
274
+
275
+ return self._handle_response(response)
276
+ except Exception as e:
277
+ self.logger.error(f"API ERROR: Get staff details request failed for staff_id={staff_id} - {e}", exc_info=True)
278
+ return {"success": False, "error": str(e)}
160
279
 
161
280
  async def store_people_activity(self,
162
281
  staff_id: str,
@@ -165,9 +284,12 @@ class FacialRecognitionClient:
165
284
  location: str,
166
285
  employee_id: Optional[str] = None,
167
286
  timestamp: str = datetime.now(timezone.utc).isoformat(),
168
- ) -> str:
287
+ image_data: Optional[str] = None,
288
+ ) -> Dict[str, Any]:
169
289
  """
170
- Store people activity data and return response with potential upload URLs
290
+ Store people activity data with optional image data
291
+
292
+ API: POST /v1/facial_recognition/store_people_activity?projectId={projectId}&serverID={serverID}
171
293
 
172
294
  Args:
173
295
  staff_id: Staff identifier (empty for unknown faces)
@@ -176,10 +298,10 @@ class FacialRecognitionClient:
176
298
  location: Location identifier
177
299
  employee_id: Employee ID (for unknown faces, this will be generated)
178
300
  timestamp: Timestamp in ISO format
301
+ image_data: Base64-encoded JPEG image data (optional)
179
302
 
180
303
  Returns:
181
- Dict containing response data including uploadUrl and employeeId for unknown faces,
182
- or None if the request failed
304
+ Dict containing response data with success status
183
305
  """
184
306
  activity_request = {
185
307
  "staff_id": staff_id,
@@ -189,97 +311,201 @@ class FacialRecognitionClient:
189
311
  "location": location,
190
312
  }
191
313
 
192
- # Add optional fields if provided
193
- if detection_type == "unknown":
194
- if employee_id:
195
- activity_request["anonymous_id"] = employee_id
314
+ # Add optional fields if provided based on API spec
315
+ if detection_type == "unknown" and employee_id:
316
+ activity_request["anonymous_id"] = employee_id
196
317
  elif detection_type == "known" and employee_id:
197
318
  activity_request["employee_id"] = employee_id
198
- response = await self.session.rpc.async_send_request(
199
- method="POST",
200
- path=f"/v1/actions/facial_recognition/store_people_activity?serverID={self.server_id}",
201
- payload=activity_request
202
- )
203
- handled_response = self._handle_response(response)
204
- if handled_response.get("success", False):
205
- data = handled_response.get("data", {})
206
- self.logger.debug(f"Successfully stored {detection_type} activity")
207
- if not data:
208
- self.logger.warning("No data returned form store people activity")
209
- return None
210
- return data
211
- else:
212
- self.logger.error(f"Failed to store {detection_type} activity: {handled_response.get('error', 'Unknown error')}")
213
- return None
319
+
320
+ # Add image data if provided
321
+ if image_data:
322
+ activity_request["imageData"] = image_data
323
+
324
+ self.logger.info(f"API REQUEST: Storing people activity - type={detection_type}, staff_id={staff_id}, location={location}, has_image={bool(image_data)}")
325
+ self.logger.debug(f"Activity request payload: bbox={bbox}, employee_id={employee_id}")
326
+
327
+ try:
328
+ response = await self.session.rpc.async_send_request(
329
+ method="POST",
330
+ path=f"/v1/facial_recognition/store_people_activity?projectId={self.project_id}&serverID={self.server_id}",
331
+ payload=activity_request,
332
+ base_url=self.server_base_url
333
+ )
334
+ handled_response = self._handle_response(response)
335
+
336
+ if handled_response.get("success", False):
337
+ self.logger.info(f"API RESPONSE: Successfully stored {detection_type} activity for staff_id={staff_id}")
338
+ return handled_response
339
+ else:
340
+ self.logger.warning(f"Failed to store {detection_type} activity: {handled_response.get('error', 'Unknown error')}")
341
+ return handled_response
342
+ except Exception as e:
343
+ self.logger.error(f"API ERROR: Store people activity request failed - type={detection_type}, staff_id={staff_id} - {e}", exc_info=True)
344
+ return {"success": False, "error": str(e)}
214
345
 
215
346
  async def update_staff_images(self, image_url: str, employee_id: str) -> Dict[str, Any]:
216
- """Update staff images with uploaded image URL"""
347
+ """Update staff images with uploaded image URL
348
+
349
+ API: PUT /v1/facial_recognition/staff/update_images?projectId={projectId}&serverID={serverID}
350
+ """
217
351
 
218
352
  update_request = {
219
353
  "imageUrl": image_url,
220
354
  "employeeId": employee_id
221
355
  }
222
356
 
357
+ self.logger.info(f"API REQUEST: Updating staff images - employee_id={employee_id}")
358
+ self.logger.debug(f"Update request: image_url={image_url[:50]}...")
359
+
223
360
  # Use Matrice session for async RPC call
224
- response = await self.session.rpc.async_send_request(
225
- method="PUT",
226
- path=f"/v1/actions/facial_recognition/update_staff_images?serverID={self.server_id}",
227
- payload=update_request
228
- )
229
- return self._handle_response(response)
361
+ try:
362
+ response = await self.session.rpc.async_send_request(
363
+ method="PUT",
364
+ path=f"/v1/facial_recognition/staff/update_images?projectId={self.project_id}&serverID={self.server_id}",
365
+ payload=update_request,
366
+ base_url=self.server_base_url
367
+ )
368
+
369
+ if response.get('success', False):
370
+ self.logger.info(f"API RESPONSE: Staff images updated successfully - employee_id={employee_id}")
371
+ else:
372
+ self.logger.warning(f"Failed to update staff images for employee_id={employee_id}: {response.get('error', 'Unknown error')}")
373
+
374
+ return self._handle_response(response)
375
+ except Exception as e:
376
+ self.logger.error(f"API ERROR: Update staff images request failed - employee_id={employee_id} - {e}", exc_info=True)
377
+ return {"success": False, "error": str(e)}
230
378
 
231
379
  async def upload_image_to_url(self, image_bytes: bytes, upload_url: str) -> bool:
232
380
  """Upload image bytes to the provided URL"""
233
381
  try:
382
+ self.logger.info(f"API REQUEST: Uploading image to URL - size={len(image_bytes)} bytes")
383
+ self.logger.debug(f"Upload URL: {upload_url[:100]}...")
384
+
234
385
  # Upload the image to the signed URL using async httpx
235
386
  headers = {'Content-Type': 'image/jpeg'}
236
387
  async with httpx.AsyncClient() as client:
237
388
  response = await client.put(upload_url, content=image_bytes, headers=headers)
238
389
 
239
390
  if response.status_code in [200, 201]:
240
- self.logger.debug(f"Successfully uploaded image to URL")
391
+ self.logger.info(f"API RESPONSE: Successfully uploaded image - status={response.status_code}")
241
392
  return True
242
393
  else:
243
- self.logger.error(f"Failed to upload image: {response.status_code} - {response.text}")
394
+ self.logger.error(f"API ERROR: Failed to upload image - status={response.status_code}, response={response.text[:200]}")
244
395
  return False
245
396
 
246
397
  except Exception as e:
247
- self.logger.error(f"Error uploading image to URL: {e}", exc_info=True)
398
+ self.logger.error(f"API ERROR: Exception during image upload - {e}", exc_info=True)
248
399
  return False
249
400
 
250
401
  async def shutdown_service(self, action_record_id: Optional[str] = None) -> Dict[str, Any]:
251
- """Gracefully shutdown the service"""
402
+ """Gracefully shutdown the service
403
+
404
+ API: DELETE /v1/facial_recognition/shutdown?projectId={projectId}&serverID={serverID}
405
+ """
252
406
 
253
407
  payload = {} if not action_record_id else {"actionRecordId": action_record_id}
254
408
 
409
+ self.logger.info(f"API REQUEST: Shutting down service - action_record_id={action_record_id}")
410
+
255
411
  # Use Matrice session for async RPC call
256
- response = await self.session.rpc.async_send_request(
257
- method="DELETE",
258
- path=f"/v1/actions/facial_recognition/shutdown?serverID={self.server_id}",
259
- payload=payload
260
- )
261
- return self._handle_response(response)
412
+ try:
413
+ response = await self.session.rpc.async_send_request(
414
+ method="DELETE",
415
+ path=f"/v1/facial_recognition/shutdown?projectId={self.project_id}&serverID={self.server_id}",
416
+ payload=payload,
417
+ base_url=self.server_base_url
418
+ )
419
+
420
+ if response.get('success', False):
421
+ self.logger.info(f"API RESPONSE: Service shutdown successful")
422
+ else:
423
+ self.logger.warning(f"Service shutdown failed: {response.get('error', 'Unknown error')}")
424
+
425
+ return self._handle_response(response)
426
+ except Exception as e:
427
+ self.logger.error(f"API ERROR: Shutdown service request failed - {e}", exc_info=True)
428
+ return {"success": False, "error": str(e)}
262
429
 
263
430
  async def get_all_staff_embeddings(self) -> Dict[str, Any]:
264
- """Get all staff embeddings"""
431
+ """Get all staff embeddings
432
+
433
+ API: GET /v1/facial_recognition/get_all_staff_embeddings?projectId={projectId}&serverID={serverID}
434
+ """
265
435
 
266
436
  payload = {}
267
437
 
438
+ self.logger.info(f"API REQUEST: Getting all staff embeddings")
439
+
440
+ # Use Matrice session for async RPC call
441
+ try:
442
+ response = await self.session.rpc.async_send_request(
443
+ method="GET",
444
+ path=f"/v1/facial_recognition/get_all_staff_embeddings?projectId={self.project_id}&serverID={self.server_id}",
445
+ payload=payload,
446
+ base_url=self.server_base_url
447
+ )
448
+
449
+ embeddings_count = 0
450
+ if response.get('success', False):
451
+ data = response.get('data', [])
452
+ embeddings_count = len(data) if isinstance(data, list) else 0
453
+ self.logger.info(f"API RESPONSE: Retrieved {embeddings_count} staff embeddings")
454
+ else:
455
+ self.logger.warning(f"Failed to get staff embeddings: {response.get('error', 'Unknown error')}")
456
+
457
+ return self._handle_response(response)
458
+ except Exception as e:
459
+ self.logger.error(f"API ERROR: Get all staff embeddings request failed - {e}", exc_info=True)
460
+ return {"success": False, "error": str(e)}
461
+
462
+ async def update_deployment(self, deployment_id: str) -> Dict[str, Any]:
463
+ """Update deployment to notify facial recognition server
464
+
465
+ API: PUT /v1/facial_recognition/update_deployment/:deployment_id
466
+
467
+ Args:
468
+ deployment_id: The deployment ID to update
469
+
470
+ Returns:
471
+ Dict containing response data
472
+ """
473
+ if not deployment_id:
474
+ self.logger.warning("No deployment_id provided for update_deployment")
475
+ return {"success": False, "error": "deployment_id is required"}
476
+
477
+ self.logger.info(f"API REQUEST: Updating deployment - deployment_id={deployment_id}")
478
+
268
479
  # Use Matrice session for async RPC call
269
- response = await self.session.rpc.async_send_request(
270
- method="GET",
271
- path=f"/v1/actions/facial_recognition/get_all_staff_embeddings?serverID={self.server_id}",
272
- payload=payload,
273
- )
274
- return self._handle_response(response)
480
+ try:
481
+ response = await self.session.rpc.async_send_request(
482
+ method="PUT",
483
+ path=f"/v1/facial_recognition/update_deployment/{deployment_id}",
484
+ payload={},
485
+ base_url=self.server_base_url
486
+ )
487
+
488
+ if response.get('success', False):
489
+ self.logger.info(f"API RESPONSE: Deployment updated successfully - deployment_id={deployment_id}")
490
+ else:
491
+ self.logger.warning(f"Failed to update deployment for deployment_id={deployment_id}: {response.get('error', 'Unknown error')}")
492
+
493
+ return self._handle_response(response)
494
+ except Exception as e:
495
+ self.logger.error(f"API ERROR: Update deployment request failed - deployment_id={deployment_id} - {e}", exc_info=True)
496
+ return {"success": False, "error": str(e)}
275
497
 
276
498
  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]:
277
- """Enroll an unknown person"""
499
+ """Enroll an unknown person
500
+
501
+ API: POST /v1/facial_recognition/enroll_unknown_person?projectId={projectId}&serverID={serverID}
502
+ """
278
503
 
279
504
  payload = {
280
505
  "embedding": embedding
281
506
  }
282
507
 
508
+ # Add optional fields based on API spec
283
509
  if image_source:
284
510
  payload["imageSource"] = image_source
285
511
  if timestamp:
@@ -288,27 +514,52 @@ class FacialRecognitionClient:
288
514
  payload["timestamp"] = datetime.now(timezone.utc).isoformat()
289
515
  if location:
290
516
  payload["location"] = location
291
- if employee_id:
292
- payload["employeeId"] = employee_id
517
+
518
+ self.logger.info(f"API REQUEST: Enrolling unknown person - location={location}")
519
+ self.logger.debug(f"Unknown enrollment payload: has_embedding={bool(embedding)}, has_image_source={bool(image_source)}")
293
520
 
294
521
  # Use Matrice session for async RPC call
295
- response = await self.session.rpc.async_send_request(
296
- method="POST",
297
- path=f"/v1/actions/facial_recognition/enroll_unknown_person?serverID={self.server_id}",
298
- payload=payload,
299
- )
300
- return self._handle_response(response)
522
+ try:
523
+ response = await self.session.rpc.async_send_request(
524
+ method="POST",
525
+ path=f"/v1/facial_recognition/enroll_unknown_person?projectId={self.project_id}&serverID={self.server_id}",
526
+ payload=payload,
527
+ base_url=self.server_base_url
528
+ )
529
+
530
+ if response.get('success', False):
531
+ self.logger.info(f"API RESPONSE: Unknown person enrolled successfully")
532
+ else:
533
+ self.logger.warning(f"Failed to enroll unknown person: {response.get('error', 'Unknown error')}")
534
+
535
+ return self._handle_response(response)
536
+ except Exception as e:
537
+ self.logger.error(f"API ERROR: Enroll unknown person request failed - {e}", exc_info=True)
538
+ return {"success": False, "error": str(e)}
301
539
 
302
540
  async def health_check(self) -> Dict[str, Any]:
303
541
  """Check if the facial recognition service is healthy"""
304
542
 
543
+ self.logger.debug(f"API REQUEST: Health check")
544
+
305
545
  # Use Matrice session for async RPC call
306
- response = await self.session.rpc.async_send_request(
307
- method="GET",
308
- path=f"/v1/actions/facial_recognition/health?serverID={self.server_id}",
309
- payload={}
310
- )
311
- return self._handle_response(response)
546
+ try:
547
+ response = await self.session.rpc.async_send_request(
548
+ method="GET",
549
+ path=f"/v1/facial_recognition/health?serverID={self.server_id}",
550
+ payload={},
551
+ base_url=self.server_base_url
552
+ )
553
+
554
+ if response.get('success', False):
555
+ self.logger.info(f"API RESPONSE: Service is healthy")
556
+ else:
557
+ self.logger.warning(f"Health check failed: {response.get('error', 'Unknown error')}")
558
+
559
+ return self._handle_response(response)
560
+ except Exception as e:
561
+ self.logger.error(f"API ERROR: Health check request failed - {e}", exc_info=True)
562
+ return {"success": False, "error": str(e)}
312
563
 
313
564
  def _handle_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
314
565
  """Handle RPC response and errors"""