.
This commit is contained in:
+96
@@ -259,6 +259,100 @@ def load_workers():
|
||||
assignments, assignments_by_worker = extract_shift_assignments_from_rota(rota_df)
|
||||
print(assignments)
|
||||
|
||||
# --- Load per-day prior worked data (Sat/Sun) from sep2025priordays.csv for Rota A workers ---
|
||||
# Build set of initials who are on Rota A
|
||||
rota_a_initials = set()
|
||||
for wd in workers_days:
|
||||
name = wd["name"]
|
||||
initial = wd["initial"]
|
||||
if rota_data.get(name, "") == "rota a":
|
||||
rota_a_initials.add(initial)
|
||||
|
||||
priordays_path = "cons/sep2025priordays.csv"
|
||||
try:
|
||||
import csv
|
||||
|
||||
perday_counts = defaultdict(lambda: defaultdict(int))
|
||||
with open(priordays_path, newline="", encoding="utf-8") as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) < 4:
|
||||
continue
|
||||
day = row[2].strip()
|
||||
if day not in ("Sat", "Sun"):
|
||||
continue
|
||||
# fields from index 3 onwards are assignments; some may be empty
|
||||
for field in row[3:]:
|
||||
if not field:
|
||||
continue
|
||||
val = field.strip()
|
||||
if not val:
|
||||
continue
|
||||
# entries may be like: RK (oncall, weekend) or CK (weekend b)
|
||||
import re
|
||||
|
||||
m = re.match(r"\s*([A-Za-z0-9]+)\s*\(([^)]*)\)", val)
|
||||
if not m:
|
||||
# sometimes multiple assignments are concatenated in a single cell separated by commas
|
||||
parts = [p.strip() for p in re.split(r",\s*", val) if p.strip()]
|
||||
for p in parts:
|
||||
m2 = re.match(r"\s*([A-Za-z0-9]+)\s*\(([^)]*)\)", p)
|
||||
if m2:
|
||||
initial = m2.group(1).strip()
|
||||
paren = m2.group(2).lower()
|
||||
if initial in rota_a_initials and "weekend" in paren:
|
||||
perday_counts[initial][day] += 1
|
||||
continue
|
||||
|
||||
initial = m.group(1).strip()
|
||||
paren = m.group(2).lower()
|
||||
if initial in rota_a_initials and "weekend" in paren:
|
||||
perday_counts[initial][day] += 1
|
||||
|
||||
# Merge per-day counts into priors_map under 'weekend' per-day keys
|
||||
for initial, daymap in perday_counts.items():
|
||||
prev = priors_map.get(initial, {})
|
||||
existing = prev.get("weekend")
|
||||
# If existing is a tuple, convert to dict preserving overall tuple under '__all__'
|
||||
if existing and not isinstance(existing, dict):
|
||||
prev["weekend"] = {"__all__": existing}
|
||||
existing = prev["weekend"]
|
||||
|
||||
for day, worked_count in daymap.items():
|
||||
# Determine allocated value for this per-day entry
|
||||
allocated_day = worked_count
|
||||
if existing:
|
||||
# prefer an explicit per-day allocated if present
|
||||
if isinstance(existing, dict) and day in existing:
|
||||
try:
|
||||
allocated_day = float(existing[day][1])
|
||||
except Exception:
|
||||
allocated_day = worked_count
|
||||
elif isinstance(existing, dict) and "__all__" in existing:
|
||||
try:
|
||||
overall_alloc = float(existing["__all__"][1])
|
||||
allocated_day = overall_alloc / 2.0
|
||||
except Exception:
|
||||
allocated_day = worked_count
|
||||
elif isinstance(existing, tuple):
|
||||
try:
|
||||
allocated_day = float(existing[1]) / 2.0
|
||||
except Exception:
|
||||
allocated_day = worked_count
|
||||
|
||||
# ensure structure
|
||||
if "weekend" not in prev or not isinstance(prev["weekend"], dict):
|
||||
prev.setdefault("weekend", {})
|
||||
|
||||
prev["weekend"][day] = (float(worked_count), float(allocated_day))
|
||||
|
||||
if prev:
|
||||
priors_map[initial] = prev
|
||||
except FileNotFoundError:
|
||||
print(f"Per-day prior file not found: {priordays_path}")
|
||||
except Exception as e:
|
||||
print(f"Error reading per-day priors: {e}")
|
||||
|
||||
workers = []
|
||||
for worker_day in workers_days:
|
||||
worker = worker_day["name"]
|
||||
@@ -393,6 +487,8 @@ def main(
|
||||
max_days_per_week_block=[(4, 2), (4, 4)],
|
||||
balance_weekend_days=True,
|
||||
#weekend_day_hard_max_deviation=2,
|
||||
balance_weekend_days_use_previous_shifts=True,
|
||||
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user