add workers check
This commit is contained in:
+73
-3
@@ -31,6 +31,44 @@ ROTA_START_DATE = "2026-08-03"
|
|||||||
sites = ("rota a", "rota b", "rota c")
|
sites = ("rota a", "rota b", "rota c")
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_worker_key(value):
|
||||||
|
text = str(value or "").strip()
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
return re.sub(r"\s+", " ", text).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_worker_alignment(base_workers, other_workers, base_label, other_label):
|
||||||
|
"""Validate that two worker collections contain the same members.
|
||||||
|
|
||||||
|
Comparison is case-insensitive and whitespace-normalized.
|
||||||
|
Raises ValueError with a clear diff when the sets do not match.
|
||||||
|
"""
|
||||||
|
base_clean = [str(w).strip() for w in base_workers if str(w).strip()]
|
||||||
|
other_clean = [str(w).strip() for w in other_workers if str(w).strip()]
|
||||||
|
|
||||||
|
base_norm_to_raw = {_normalize_worker_key(w): w for w in base_clean}
|
||||||
|
other_norm_to_raw = {_normalize_worker_key(w): w for w in other_clean}
|
||||||
|
|
||||||
|
base_norm = set(base_norm_to_raw.keys())
|
||||||
|
other_norm = set(other_norm_to_raw.keys())
|
||||||
|
|
||||||
|
only_in_base_norm = sorted(base_norm - other_norm)
|
||||||
|
only_in_other_norm = sorted(other_norm - base_norm)
|
||||||
|
|
||||||
|
if not only_in_base_norm and not only_in_other_norm:
|
||||||
|
return
|
||||||
|
|
||||||
|
only_in_base = [base_norm_to_raw[k] for k in only_in_base_norm]
|
||||||
|
only_in_other = [other_norm_to_raw[k] for k in only_in_other_norm]
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
"Worker mismatch detected between sources. "
|
||||||
|
f"{base_label} only: {only_in_base}. "
|
||||||
|
f"{other_label} only: {only_in_other}."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _parse_sheet_date(value):
|
def _parse_sheet_date(value):
|
||||||
if pd.isna(value):
|
if pd.isna(value):
|
||||||
return None
|
return None
|
||||||
@@ -185,7 +223,7 @@ def extract_leave_and_rota_from_calender(calender_df):
|
|||||||
|
|
||||||
|
|
||||||
#print(leave_requests)
|
#print(leave_requests)
|
||||||
return leave_requests, work_requests#, rota_data
|
return leave_requests, work_requests, worker_names
|
||||||
|
|
||||||
def extract_shift_assignments_from_rota(rota_df):
|
def extract_shift_assignments_from_rota(rota_df):
|
||||||
"""
|
"""
|
||||||
@@ -319,11 +357,43 @@ def load_workers():
|
|||||||
workers_days.append({"initial": initial, "name": name, "availability": availability, "requests": requests})
|
workers_days.append({"initial": initial, "name": name, "availability": availability, "requests": requests})
|
||||||
|
|
||||||
|
|
||||||
leave_requests, work_requests = extract_leave_and_rota_from_calender(calender_df)
|
leave_requests, work_requests, calendar_workers = extract_leave_and_rota_from_calender(calender_df)
|
||||||
|
|
||||||
assignments, assignments_by_worker = extract_shift_assignments_from_rota(rota_df)
|
assignments, assignments_by_worker = extract_shift_assignments_from_rota(rota_df)
|
||||||
print(assignments)
|
print(assignments)
|
||||||
|
|
||||||
|
# Validate cross-source worker matching before constructing Worker objects.
|
||||||
|
days_workers = [w["name"] for w in workers_days]
|
||||||
|
days_initials = [w["initial"] for w in workers_days]
|
||||||
|
|
||||||
|
_validate_worker_alignment(
|
||||||
|
base_workers=days_workers,
|
||||||
|
other_workers=calendar_workers,
|
||||||
|
base_label="Days tab worker names",
|
||||||
|
other_label="Calendar tab worker names",
|
||||||
|
)
|
||||||
|
|
||||||
|
_validate_worker_alignment(
|
||||||
|
base_workers=days_initials,
|
||||||
|
other_workers=list(assignments_by_worker.keys()),
|
||||||
|
base_label="Days tab worker initials",
|
||||||
|
other_label="Rota tab assigned initials",
|
||||||
|
)
|
||||||
|
|
||||||
|
_validate_worker_alignment(
|
||||||
|
base_workers=days_initials,
|
||||||
|
other_workers=list(rota_b_assignments.keys()),
|
||||||
|
base_label="Days tab worker initials",
|
||||||
|
other_label="Rota B assignments initials",
|
||||||
|
)
|
||||||
|
|
||||||
|
_validate_worker_alignment(
|
||||||
|
base_workers=days_initials,
|
||||||
|
other_workers=list(priors_map.keys()),
|
||||||
|
base_label="Days tab worker initials",
|
||||||
|
other_label="Running totals worker initials",
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug("Using Running totals priors for oncall/twilight/weekend A only")
|
logger.debug("Using Running totals priors for oncall/twilight/weekend A only")
|
||||||
|
|
||||||
workers = []
|
workers = []
|
||||||
@@ -455,7 +525,7 @@ def main(
|
|||||||
weeks_to_rota=weeks,
|
weeks_to_rota=weeks,
|
||||||
balance_offset_modifier=bom,
|
balance_offset_modifier=bom,
|
||||||
use_previous_shifts=True,
|
use_previous_shifts=True,
|
||||||
name="cons rota 2026 july",
|
name="cons rota test",
|
||||||
allow_force_assignment_with_leave_conflict=True,
|
allow_force_assignment_with_leave_conflict=True,
|
||||||
constraint_options=RotaConstraintOptions(
|
constraint_options=RotaConstraintOptions(
|
||||||
balance_weekends=True,
|
balance_weekends=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user