rairo commited on
Commit
d842a03
·
verified ·
1 Parent(s): d36c26a

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +89 -58
main.py CHANGED
@@ -254,7 +254,7 @@ def assign_roster(job_id):
254
  job_ref = db.reference(f"jobs/{job_id}")
255
  job_data = job_ref.get()
256
 
257
- if not job_data: # Fixed the condition: job_data is None, not job_id
258
  logger.error(f"Job {job_id} not found")
259
  return
260
 
@@ -276,6 +276,32 @@ def assign_roster(job_id):
276
  logger.warning(f"No members assigned to job {job_id}")
277
  return
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  # Get current assignments and rotation history
280
  current_assignments = job_data.get("assignments", [])
281
  rotation_history = job_data.get("rotation_history", {})
@@ -298,8 +324,8 @@ def assign_roster(job_id):
298
  if special_assignment:
299
  logger.info(f"Applying special case assignment for job {job_id}, shift {shift_number}")
300
  # Apply special case assignment
301
- assigned_point = next((p for p in guarding_points if p["id"] == special_assignment["point_id"]), None)
302
- assigned_member = next((m for m in assigned_members if m["id"] == special_assignment["member_id"]), None)
303
 
304
  if assigned_point and assigned_member:
305
  shift_assignments.append({
@@ -309,15 +335,17 @@ def assign_roster(job_id):
309
  "special_case_reason": special_assignment.get("reason", "Special Assignment")
310
  })
311
  else:
312
- logger.warning(f"Special case assignment failed: Point or Member not found for job {job_id}")
 
313
  else:
314
  logger.info(f"Applying standard rotation algorithm for job {job_id}, shift {shift_number}")
315
  # Standard rotation algorithm
316
- # Create point-specific rotation history
317
- # point_assignments = {} # This variable was defined but not used, removed.
318
-
319
  for point in guarding_points:
320
- point_id = point["id"]
 
 
 
 
321
  point_history = rotation_history.get(point_id, [])
322
 
323
  # Find member who hasn't been assigned to this point recently
@@ -325,12 +353,12 @@ def assign_roster(job_id):
325
 
326
  # Remove recently assigned members (no immediate repetition)
327
  recently_assigned_member_ids = set()
328
- lookback_window = min(len(guarding_points), len(assigned_members)) # Prevent index error
329
- for recent_assignment in point_history[-lookback_window:]: # Look back N assignments
330
- recently_assigned_member_ids.add(recent_assignment["member_id"])
331
 
332
  # Filter available members
333
- available_members = [m for m in available_members if m["id"] not in recently_assigned_member_ids]
334
 
335
  # If all members have been recently assigned, use all members
336
  if not available_members:
@@ -340,21 +368,21 @@ def assign_roster(job_id):
340
  # Select next member (simple round-robin from available)
341
  selected_member = None
342
  if point_history and available_members:
343
- last_assigned_member_id = point_history[-1]["member_id"]
344
- last_member_index = next((i for i, m in enumerate(available_members) if m["id"] == last_assigned_member_id), -1)
345
- if last_member_index != -1: # Only proceed if last member was in available list
346
  next_member_index = (last_member_index + 1) % len(available_members)
347
  else:
348
  # Last assigned member is not in available list, pick first available
349
  next_member_index = 0
350
  selected_member = available_members[next_member_index]
351
  elif available_members:
352
- # No history or last member not in available list, pick first
353
- selected_member = available_members[0]
354
  else:
355
- # This shouldn't happen due to the fallback, but log if it does
356
- logger.error(f"No available members for point {point_id} in job {job_id}")
357
- continue # Skip this point
358
 
359
  if selected_member:
360
  # Record assignment
@@ -366,53 +394,56 @@ def assign_roster(job_id):
366
 
367
  # Update rotation history
368
  point_history.append({
369
- "member_id": selected_member["id"],
370
  "member_name": selected_member.get("name", "Unknown"),
371
  "assigned_at": datetime.datetime.now().isoformat(),
372
  "shift_number": shift_number
373
  })
374
  rotation_history[point_id] = point_history
375
  else:
376
- logger.warning(f"Could not select a member for point {point_id} in job {job_id}")
377
 
378
- # Update rotation history in database
 
379
  job_ref.update({"rotation_history": rotation_history})
380
 
381
- # Create shift record
382
- shift_record = {
383
- "shift_number": shift_number,
384
- "assigned_at": datetime.datetime.now().isoformat(),
385
- "assignments": shift_assignments,
386
- "shift_id": str(uuid.uuid4())
387
- }
388
-
389
- # Add to current assignments
390
- current_assignments.append(shift_record)
391
-
392
- # Update job with new assignment
393
- job_ref.update({
394
- "assignments": current_assignments,
395
- "last_updated": datetime.datetime.now().isoformat()
396
- })
397
-
398
- logger.info(f"Shift {shift_number} assigned for job {job_id} with {len(shift_assignments)} assignments")
399
-
400
- # Send email notification to admins
401
- send_rotation_notification(job_id, shift_record)
402
-
403
- # Schedule next rotation based on job's rotation period
404
- rotation_period = job_data.get("rotation_period", 28800) # Default 8 hours
405
- next_run_time = datetime.datetime.now() + datetime.timedelta(seconds=rotation_period)
406
-
407
- scheduler.add_job(
408
- func=assign_roster,
409
- trigger="date",
410
- run_date=next_run_time,
411
- args=[job_id],
412
- id=f"rotate_{job_id}_{uuid.uuid4().hex[:8]}"
413
- )
414
-
415
- logger.info(f"Scheduled next rotation for job {job_id} in {rotation_period} seconds")
 
 
416
 
417
  except Exception as e:
418
  logger.error(f"Error in assign_roster for job {job_id}: {e}", exc_info=True)
 
254
  job_ref = db.reference(f"jobs/{job_id}")
255
  job_data = job_ref.get()
256
 
257
+ if not job_data:
258
  logger.error(f"Job {job_id} not found")
259
  return
260
 
 
276
  logger.warning(f"No members assigned to job {job_id}")
277
  return
278
 
279
+ # CRITICAL FIX: Ensure all guarding points have IDs
280
+ points_updated = False
281
+ for i, point in enumerate(guarding_points):
282
+ if "id" not in point or not point["id"]:
283
+ point["id"] = f"point_{i+1}"
284
+ points_updated = True
285
+ logger.warning(f"Point missing ID in job {job_id}, assigned: point_{i+1}")
286
+
287
+ # Update database if points were missing IDs
288
+ if points_updated:
289
+ job_ref.update({"guarding_points": guarding_points})
290
+ logger.info(f"Updated guarding points with missing IDs for job {job_id}")
291
+
292
+ # CRITICAL FIX: Ensure all assigned members have IDs
293
+ members_updated = False
294
+ for i, member in enumerate(assigned_members):
295
+ if "id" not in member or not member["id"]:
296
+ member["id"] = f"member_{i+1}_{uuid.uuid4().hex[:6]}"
297
+ members_updated = True
298
+ logger.warning(f"Member missing ID in job {job_id}, assigned: {member['id']}")
299
+
300
+ # Update database if members were missing IDs
301
+ if members_updated:
302
+ job_ref.update({"assigned_members": assigned_members})
303
+ logger.info(f"Updated assigned members with missing IDs for job {job_id}")
304
+
305
  # Get current assignments and rotation history
306
  current_assignments = job_data.get("assignments", [])
307
  rotation_history = job_data.get("rotation_history", {})
 
324
  if special_assignment:
325
  logger.info(f"Applying special case assignment for job {job_id}, shift {shift_number}")
326
  # Apply special case assignment
327
+ assigned_point = next((p for p in guarding_points if p.get("id") == special_assignment.get("point_id")), None)
328
+ assigned_member = next((m for m in assigned_members if m.get("id") == special_assignment.get("member_id")), None)
329
 
330
  if assigned_point and assigned_member:
331
  shift_assignments.append({
 
335
  "special_case_reason": special_assignment.get("reason", "Special Assignment")
336
  })
337
  else:
338
+ logger.warning(f"Special case assignment failed: Point or Member not found for job {job_id}")
339
+ logger.debug(f"Looking for point_id: {special_assignment.get('point_id')}, member_id: {special_assignment.get('member_id')}")
340
  else:
341
  logger.info(f"Applying standard rotation algorithm for job {job_id}, shift {shift_number}")
342
  # Standard rotation algorithm
 
 
 
343
  for point in guarding_points:
344
+ point_id = point.get("id")
345
+ if not point_id:
346
+ logger.error(f"Point still missing ID after fix attempt: {point}")
347
+ continue
348
+
349
  point_history = rotation_history.get(point_id, [])
350
 
351
  # Find member who hasn't been assigned to this point recently
 
353
 
354
  # Remove recently assigned members (no immediate repetition)
355
  recently_assigned_member_ids = set()
356
+ lookback_window = min(len(guarding_points), len(assigned_members))
357
+ for recent_assignment in point_history[-lookback_window:]:
358
+ recently_assigned_member_ids.add(recent_assignment.get("member_id"))
359
 
360
  # Filter available members
361
+ available_members = [m for m in available_members if m.get("id") not in recently_assigned_member_ids]
362
 
363
  # If all members have been recently assigned, use all members
364
  if not available_members:
 
368
  # Select next member (simple round-robin from available)
369
  selected_member = None
370
  if point_history and available_members:
371
+ last_assigned_member_id = point_history[-1].get("member_id")
372
+ last_member_index = next((i for i, m in enumerate(available_members) if m.get("id") == last_assigned_member_id), -1)
373
+ if last_member_index != -1:
374
  next_member_index = (last_member_index + 1) % len(available_members)
375
  else:
376
  # Last assigned member is not in available list, pick first available
377
  next_member_index = 0
378
  selected_member = available_members[next_member_index]
379
  elif available_members:
380
+ # No history or last member not in available list, pick first
381
+ selected_member = available_members[0]
382
  else:
383
+ # This shouldn't happen due to the fallback, but log if it does
384
+ logger.error(f"No available members for point {point_id} in job {job_id}")
385
+ continue
386
 
387
  if selected_member:
388
  # Record assignment
 
394
 
395
  # Update rotation history
396
  point_history.append({
397
+ "member_id": selected_member.get("id"),
398
  "member_name": selected_member.get("name", "Unknown"),
399
  "assigned_at": datetime.datetime.now().isoformat(),
400
  "shift_number": shift_number
401
  })
402
  rotation_history[point_id] = point_history
403
  else:
404
+ logger.warning(f"Could not select a member for point {point_id} in job {job_id}")
405
 
406
+ # Update rotation history in database only if we have assignments
407
+ if shift_assignments:
408
  job_ref.update({"rotation_history": rotation_history})
409
 
410
+ # Create shift record
411
+ shift_record = {
412
+ "shift_number": shift_number,
413
+ "assigned_at": datetime.datetime.now().isoformat(),
414
+ "assignments": shift_assignments,
415
+ "shift_id": str(uuid.uuid4())
416
+ }
417
+
418
+ # Add to current assignments
419
+ current_assignments.append(shift_record)
420
+
421
+ # Update job with new assignment
422
+ job_ref.update({
423
+ "assignments": current_assignments,
424
+ "last_updated": datetime.datetime.now().isoformat()
425
+ })
426
+
427
+ logger.info(f"Shift {shift_number} assigned for job {job_id} with {len(shift_assignments)} assignments")
428
+
429
+ # Send email notification to admins
430
+ send_rotation_notification(job_id, shift_record)
431
+
432
+ # Schedule next rotation based on job's rotation period
433
+ rotation_period = job_data.get("rotation_period", 28800) # Default 8 hours
434
+ next_run_time = datetime.datetime.now() + datetime.timedelta(seconds=rotation_period)
435
+
436
+ scheduler.add_job(
437
+ func=assign_roster,
438
+ trigger="date",
439
+ run_date=next_run_time,
440
+ args=[job_id],
441
+ id=f"rotate_{job_id}_{uuid.uuid4().hex[:8]}"
442
+ )
443
+
444
+ logger.info(f"Scheduled next rotation for job {job_id} in {rotation_period} seconds")
445
+ else:
446
+ logger.error(f"No assignments created for job {job_id}, shift {shift_number}")
447
 
448
  except Exception as e:
449
  logger.error(f"Error in assign_roster for job {job_id}: {e}", exc_info=True)