Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -36,35 +36,21 @@ HF_API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
|
|
| 36 |
|
| 37 |
# Salesforce configuration
|
| 38 |
SF_CREDENTIALS = {
|
| 39 |
-
"username":
|
| 40 |
-
"password":
|
| 41 |
-
"security_token":
|
| 42 |
-
"domain": "login"
|
| 43 |
}
|
| 44 |
|
| 45 |
-
|
| 46 |
@retry(stop_max_attempt_number=3, wait_fixed=2000)
|
| 47 |
def connect_to_salesforce():
|
| 48 |
try:
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
username=SF_CREDENTIALS["username"],
|
| 52 |
-
password=SF_CREDENTIALS["password"],
|
| 53 |
-
security_token=SF_CREDENTIALS["security_token"],
|
| 54 |
-
instance=SF_CREDENTIALS["instance"]
|
| 55 |
-
)
|
| 56 |
-
else:
|
| 57 |
-
sf = Salesforce(
|
| 58 |
-
username=SF_CREDENTIALS["username"],
|
| 59 |
-
password=SF_CREDENTIALS["password"],
|
| 60 |
-
security_token=SF_CREDENTIALS["security_token"],
|
| 61 |
-
domain=SF_CREDENTIALS["domain"]
|
| 62 |
-
)
|
| 63 |
-
logger.info("Connected to Salesforce successfully")
|
| 64 |
sf.describe()
|
| 65 |
return sf
|
| 66 |
except Exception as e:
|
| 67 |
-
logger.error(f"Salesforce connection failed: {e}
|
| 68 |
raise
|
| 69 |
|
| 70 |
class AttendanceSystem:
|
|
@@ -82,13 +68,12 @@ class AttendanceSystem:
|
|
| 82 |
self.recognition_cooldown = 5 # seconds
|
| 83 |
self.video_file_path = None
|
| 84 |
self.video_processing = False
|
| 85 |
-
self.min_face_size = (100, 100) # Minimum face size for registration/recognition
|
| 86 |
|
| 87 |
# Initialize Salesforce
|
| 88 |
try:
|
| 89 |
self.sf = connect_to_salesforce()
|
| 90 |
except Exception as e:
|
| 91 |
-
logger.error(f"
|
| 92 |
self.sf = None
|
| 93 |
|
| 94 |
# Create directories
|
|
@@ -110,6 +95,21 @@ class AttendanceSystem:
|
|
| 110 |
if os.path.exists("data/attendance.json"):
|
| 111 |
with open("data/attendance.json", "r") as f:
|
| 112 |
self.attendance_records = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
except Exception as e:
|
| 114 |
logger.error(f"Error loading data: {e}")
|
| 115 |
self.known_face_embeddings = []
|
|
@@ -192,25 +192,24 @@ class AttendanceSystem:
|
|
| 192 |
image_array = np.array(image)
|
| 193 |
|
| 194 |
try:
|
| 195 |
-
# Verify face and size
|
| 196 |
face_analysis = DeepFace.analyze(img_path=image_array, actions=['emotion'], enforce_detection=True, detector_backend='opencv')
|
| 197 |
-
faces = DeepFace.extract_faces(img_path=image_array, target_size=(160, 160), enforce_detection=False, detector_backend='opencv')
|
| 198 |
-
if not faces or faces[0]['facial_area']['w'] < self.min_face_size[0] or faces[0]['facial_area']['h'] < self.min_face_size[1]:
|
| 199 |
-
return "β Face too small or not clear enough (must be at least 100x100 pixels)!", self.get_registered_workers_info()
|
| 200 |
|
| 201 |
embedding = DeepFace.represent(img_path=image_array, model_name='Facenet')[0]['embedding']
|
| 202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
# Check for duplicate face
|
| 204 |
if len(self.known_face_embeddings) > 0:
|
| 205 |
distances = [np.linalg.norm(np.array(embedding) - np.array(known_embedding))
|
| 206 |
for known_embedding in self.known_face_embeddings]
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
return f"β {name} is already registered!", self.get_registered_workers_info()
|
| 214 |
|
| 215 |
worker_id = f"W{self.next_worker_id:04d}"
|
| 216 |
|
|
@@ -241,7 +240,6 @@ class AttendanceSystem:
|
|
| 241 |
logger.warning("Image URL not set due to upload failure")
|
| 242 |
except Exception as e:
|
| 243 |
logger.error(f"Error saving to Salesforce: {e}")
|
| 244 |
-
return f"β Error saving to Salesforce: {e}. Check connection and permissions.", self.get_registered_workers_info()
|
| 245 |
|
| 246 |
self.save_data()
|
| 247 |
|
|
@@ -254,23 +252,18 @@ class AttendanceSystem:
|
|
| 254 |
except Exception as e:
|
| 255 |
return f"β Error during registration: {str(e)}", self.get_registered_workers_info()
|
| 256 |
|
| 257 |
-
def register_worker_auto(self, face_image: np.ndarray
|
| 258 |
"""Automatic worker registration for unrecognized faces"""
|
| 259 |
try:
|
| 260 |
-
# Verify face size
|
| 261 |
-
if face_area['w'] < self.min_face_size[0] or face_area['h'] < self.min_face_size[1]:
|
| 262 |
-
logger.info("Face too small for registration (must be at least 100x100 pixels)")
|
| 263 |
-
return None, None
|
| 264 |
-
|
| 265 |
embedding = DeepFace.represent(img_path=face_image, model_name='Facenet')[0]['embedding']
|
| 266 |
|
| 267 |
# Check for duplicate face
|
| 268 |
if len(self.known_face_embeddings) > 0:
|
| 269 |
distances = [np.linalg.norm(np.array(embedding) - np.array(known_embedding))
|
| 270 |
for known_embedding in self.known_face_embeddings]
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
return self.known_face_ids[best_match_index], self.known_face_names[best_match_index]
|
| 275 |
|
| 276 |
worker_id = f"W{self.next_worker_id:04d}"
|
|
@@ -367,10 +360,6 @@ class AttendanceSystem:
|
|
| 367 |
face_area = face_obj['facial_area']
|
| 368 |
x, y, w, h = face_area['x'], face_area['y'], face_area['w'], face_area['h']
|
| 369 |
|
| 370 |
-
# Check face size
|
| 371 |
-
if w < self.min_face_size[0] or h < self.min_face_size[1]:
|
| 372 |
-
continue
|
| 373 |
-
|
| 374 |
face_image = frame[y:y+h, x:x+w]
|
| 375 |
|
| 376 |
try:
|
|
@@ -398,7 +387,7 @@ class AttendanceSystem:
|
|
| 398 |
self.last_recognition_time[worker_id] = current_time
|
| 399 |
else:
|
| 400 |
if face_image.size > 0:
|
| 401 |
-
new_id, new_name = self.register_worker_auto(face_image
|
| 402 |
if new_id:
|
| 403 |
worker_id = new_id
|
| 404 |
worker_name = new_name
|
|
@@ -527,7 +516,7 @@ class AttendanceSystem:
|
|
| 527 |
|
| 528 |
def get_registered_workers_info(self) -> str:
|
| 529 |
if not self.sf:
|
| 530 |
-
return "β Salesforce connection not established.
|
| 531 |
|
| 532 |
try:
|
| 533 |
workers = self.sf.query_all("SELECT Name, Worker_ID__c, Image_Caption__c, Image_URL__c FROM Worker__c")['records']
|
|
@@ -555,7 +544,7 @@ class AttendanceSystem:
|
|
| 555 |
|
| 556 |
def get_today_attendance(self) -> str:
|
| 557 |
if not self.sf:
|
| 558 |
-
return "β Salesforce connection not established.
|
| 559 |
|
| 560 |
today = date.today().isoformat()
|
| 561 |
try:
|
|
@@ -599,7 +588,7 @@ class AttendanceSystem:
|
|
| 599 |
return "Invalid date format. Please use YYYY-MM-DD."
|
| 600 |
|
| 601 |
if not self.sf:
|
| 602 |
-
return "β Salesforce connection not established.
|
| 603 |
|
| 604 |
try:
|
| 605 |
records = self.sf.query_all(
|
|
@@ -705,7 +694,7 @@ def create_interface():
|
|
| 705 |
## π **Key Features:**
|
| 706 |
- **π₯ Live Camera Recognition** - Real-time face detection from camera/CCTV
|
| 707 |
- **πΉ Video File Processing** - Process pre-recorded videos for attendance
|
| 708 |
-
- **π€ Automatic Worker Registration** - Auto-register unknown faces with unique IDs
|
| 709 |
- **π€ Manual Registration** - Register workers manually with photos and AI-generated captions
|
| 710 |
- **π
24-Hour Attendance Rule** - One attendance mark per worker per day
|
| 711 |
- **π Advanced Analytics** - Detailed reports and data export
|
|
|
|
| 36 |
|
| 37 |
# Salesforce configuration
|
| 38 |
SF_CREDENTIALS = {
|
| 39 |
+
"username": "smartlabour@attendance.system",
|
| 40 |
+
"password": "#Prashanth@123",
|
| 41 |
+
"security_token": "pasQDqmWApzD0skgbv76gVgIs",
|
| 42 |
+
"domain": "login"
|
| 43 |
}
|
| 44 |
|
|
|
|
| 45 |
@retry(stop_max_attempt_number=3, wait_fixed=2000)
|
| 46 |
def connect_to_salesforce():
|
| 47 |
try:
|
| 48 |
+
sf = Salesforce(**SF_CREDENTIALS)
|
| 49 |
+
logger.info("Connected to Salesforce")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
sf.describe()
|
| 51 |
return sf
|
| 52 |
except Exception as e:
|
| 53 |
+
logger.error(f"Salesforce connection failed: {e}")
|
| 54 |
raise
|
| 55 |
|
| 56 |
class AttendanceSystem:
|
|
|
|
| 68 |
self.recognition_cooldown = 5 # seconds
|
| 69 |
self.video_file_path = None
|
| 70 |
self.video_processing = False
|
|
|
|
| 71 |
|
| 72 |
# Initialize Salesforce
|
| 73 |
try:
|
| 74 |
self.sf = connect_to_salesforce()
|
| 75 |
except Exception as e:
|
| 76 |
+
logger.error(f"Error connecting to Salesforce: {e}")
|
| 77 |
self.sf = None
|
| 78 |
|
| 79 |
# Create directories
|
|
|
|
| 95 |
if os.path.exists("data/attendance.json"):
|
| 96 |
with open("data/attendance.json", "r") as f:
|
| 97 |
self.attendance_records = json.load(f)
|
| 98 |
+
|
| 99 |
+
# Load embeddings from Salesforce for duplicate checks
|
| 100 |
+
if self.sf:
|
| 101 |
+
try:
|
| 102 |
+
workers = self.sf.query_all("SELECT Worker_ID__c, Name, Face_Embedding__c FROM Worker__c")['records']
|
| 103 |
+
for worker in workers:
|
| 104 |
+
if worker['Face_Embedding__c']:
|
| 105 |
+
embedding = json.loads(worker['Face_Embedding__c'])
|
| 106 |
+
if worker['Worker_ID__c'] not in self.known_face_ids:
|
| 107 |
+
self.known_face_embeddings.append(embedding)
|
| 108 |
+
self.known_face_names.append(worker['Name'])
|
| 109 |
+
self.known_face_ids.append(worker['Worker_ID__c'])
|
| 110 |
+
self.next_worker_id = max(self.next_worker_id, int(worker['Worker_ID__c'][1:]) + 1)
|
| 111 |
+
except Exception as e:
|
| 112 |
+
logger.error(f"Error loading embeddings from Salesforce: {e}")
|
| 113 |
except Exception as e:
|
| 114 |
logger.error(f"Error loading data: {e}")
|
| 115 |
self.known_face_embeddings = []
|
|
|
|
| 192 |
image_array = np.array(image)
|
| 193 |
|
| 194 |
try:
|
|
|
|
| 195 |
face_analysis = DeepFace.analyze(img_path=image_array, actions=['emotion'], enforce_detection=True, detector_backend='opencv')
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
embedding = DeepFace.represent(img_path=image_array, model_name='Facenet')[0]['embedding']
|
| 198 |
|
| 199 |
+
name = name.strip().title()
|
| 200 |
+
if name in self.known_face_names:
|
| 201 |
+
return f"β {name} is already registered!", self.get_registered_workers_info()
|
| 202 |
+
|
| 203 |
# Check for duplicate face
|
| 204 |
if len(self.known_face_embeddings) > 0:
|
| 205 |
distances = [np.linalg.norm(np.array(embedding) - np.array(known_embedding))
|
| 206 |
for known_embedding in self.known_face_embeddings]
|
| 207 |
+
min_distance = min(distances)
|
| 208 |
+
if min_distance < 10:
|
| 209 |
+
best_match_index = distances.index(min_distance)
|
| 210 |
+
matched_name = self.known_face_names[best_match_index]
|
| 211 |
+
matched_id = self.known_face_ids[best_match_index]
|
| 212 |
+
return f"β Face matches existing worker: {matched_name} ({matched_id})!", self.get_registered_workers_info()
|
|
|
|
| 213 |
|
| 214 |
worker_id = f"W{self.next_worker_id:04d}"
|
| 215 |
|
|
|
|
| 240 |
logger.warning("Image URL not set due to upload failure")
|
| 241 |
except Exception as e:
|
| 242 |
logger.error(f"Error saving to Salesforce: {e}")
|
|
|
|
| 243 |
|
| 244 |
self.save_data()
|
| 245 |
|
|
|
|
| 252 |
except Exception as e:
|
| 253 |
return f"β Error during registration: {str(e)}", self.get_registered_workers_info()
|
| 254 |
|
| 255 |
+
def register_worker_auto(self, face_image: np.ndarray) -> Tuple[Optional[str], Optional[str]]:
|
| 256 |
"""Automatic worker registration for unrecognized faces"""
|
| 257 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
embedding = DeepFace.represent(img_path=face_image, model_name='Facenet')[0]['embedding']
|
| 259 |
|
| 260 |
# Check for duplicate face
|
| 261 |
if len(self.known_face_embeddings) > 0:
|
| 262 |
distances = [np.linalg.norm(np.array(embedding) - np.array(known_embedding))
|
| 263 |
for known_embedding in self.known_face_embeddings]
|
| 264 |
+
min_distance = min(distances)
|
| 265 |
+
if min_distance < 10:
|
| 266 |
+
best_match_index = distances.index(min_distance)
|
| 267 |
return self.known_face_ids[best_match_index], self.known_face_names[best_match_index]
|
| 268 |
|
| 269 |
worker_id = f"W{self.next_worker_id:04d}"
|
|
|
|
| 360 |
face_area = face_obj['facial_area']
|
| 361 |
x, y, w, h = face_area['x'], face_area['y'], face_area['w'], face_area['h']
|
| 362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
face_image = frame[y:y+h, x:x+w]
|
| 364 |
|
| 365 |
try:
|
|
|
|
| 387 |
self.last_recognition_time[worker_id] = current_time
|
| 388 |
else:
|
| 389 |
if face_image.size > 0:
|
| 390 |
+
new_id, new_name = self.register_worker_auto(face_image)
|
| 391 |
if new_id:
|
| 392 |
worker_id = new_id
|
| 393 |
worker_name = new_name
|
|
|
|
| 516 |
|
| 517 |
def get_registered_workers_info(self) -> str:
|
| 518 |
if not self.sf:
|
| 519 |
+
return "β Salesforce connection not established."
|
| 520 |
|
| 521 |
try:
|
| 522 |
workers = self.sf.query_all("SELECT Name, Worker_ID__c, Image_Caption__c, Image_URL__c FROM Worker__c")['records']
|
|
|
|
| 544 |
|
| 545 |
def get_today_attendance(self) -> str:
|
| 546 |
if not self.sf:
|
| 547 |
+
return "β Salesforce connection not established."
|
| 548 |
|
| 549 |
today = date.today().isoformat()
|
| 550 |
try:
|
|
|
|
| 588 |
return "Invalid date format. Please use YYYY-MM-DD."
|
| 589 |
|
| 590 |
if not self.sf:
|
| 591 |
+
return "β Salesforce connection not established."
|
| 592 |
|
| 593 |
try:
|
| 594 |
records = self.sf.query_all(
|
|
|
|
| 694 |
## π **Key Features:**
|
| 695 |
- **π₯ Live Camera Recognition** - Real-time face detection from camera/CCTV
|
| 696 |
- **πΉ Video File Processing** - Process pre-recorded videos for attendance
|
| 697 |
+
- **π€ Automatic Worker Registration** - Auto-register unknown faces with unique IDs
|
| 698 |
- **π€ Manual Registration** - Register workers manually with photos and AI-generated captions
|
| 699 |
- **π
24-Hour Attendance Rule** - One attendance mark per worker per day
|
| 700 |
- **π Advanced Analytics** - Detailed reports and data export
|