Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62e48ef205 | ||
|
|
b98c28401f | ||
|
|
94b921192f |
+6
-2
@@ -4,7 +4,7 @@ import datetime
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from rota_generator.shifts import MaxShiftsPerWeekConstraint, NoWorkers, PostShiftConstraint, PreShiftConstraint, RotaBuilder, SingleShift, WorkerRequirement, days, RotaConstraintOptions
|
from rota_generator.shifts import MaxShiftsPerWeekConstraint, NoWorkers, PostShiftConstraint, PreShiftConstraint, RotaBuilder, SingleShift, WorkerRequirement, days, RotaConstraintOptions, MaxShiftsPerWeekBlockConstraint
|
||||||
import typer
|
import typer
|
||||||
import subprocess
|
import subprocess
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -404,6 +404,9 @@ def load_workers():
|
|||||||
previous_shifts=priors_map.get(initial, {}),
|
previous_shifts=priors_map.get(initial, {}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if initial == "LB":
|
||||||
|
w.start_date = datetime.date(2026, 6, 15)
|
||||||
|
|
||||||
#if initial == "L1":
|
#if initial == "L1":
|
||||||
# w.oop = [OutOfProgramme(
|
# w.oop = [OutOfProgramme(
|
||||||
# start_date="2025-09-15",
|
# start_date="2025-09-15",
|
||||||
@@ -482,7 +485,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 feb 2026 run 2",
|
name="cons rota feb 2026 run 5",
|
||||||
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,
|
||||||
@@ -551,6 +554,7 @@ def main(
|
|||||||
constraints=[
|
constraints=[
|
||||||
PreShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"], exclude_days=("Sat", "Sun")),
|
PreShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"], exclude_days=("Sat", "Sun")),
|
||||||
MaxShiftsPerWeekConstraint(max_shifts=1),
|
MaxShiftsPerWeekConstraint(max_shifts=1),
|
||||||
|
MaxShiftsPerWeekBlockConstraint(week_block=3,max_shifts=1)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
+79
-78
@@ -2,7 +2,20 @@ import datetime
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from rota_generator.shifts import NoWorkers, RotaBuilder, SingleShift, WorkerRequirement, days
|
from rota_generator.shifts import (
|
||||||
|
NoWorkers,
|
||||||
|
RotaBuilder,
|
||||||
|
SingleShift,
|
||||||
|
WorkerRequirement,
|
||||||
|
days,
|
||||||
|
PreShiftConstraint,
|
||||||
|
PostShiftConstraint,
|
||||||
|
NightConstraint,
|
||||||
|
RequireRemoteSitePresenceConstraint,
|
||||||
|
LimitGradeNumberConstraint,
|
||||||
|
MinimumGradeNumberConstraint,
|
||||||
|
MaxShiftsPerWeekConstraint,
|
||||||
|
)
|
||||||
import typer
|
import typer
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@@ -35,8 +48,8 @@ def main(
|
|||||||
solve: bool = True,
|
solve: bool = True,
|
||||||
time_to_run: int = 60 * 60 * 4,
|
time_to_run: int = 60 * 60 * 4,
|
||||||
ratio: float = 0.001,
|
ratio: float = 0.001,
|
||||||
start_date: datetime.datetime = "2025-09-01",
|
start_date: datetime.datetime = "2026-03-02",
|
||||||
weeks: int = 16,
|
weeks: int = 12,
|
||||||
bom: int = 2,
|
bom: int = 2,
|
||||||
):
|
):
|
||||||
rota_start_date = start_date.date()
|
rota_start_date = start_date.date()
|
||||||
@@ -81,12 +94,7 @@ def main(
|
|||||||
length=12.5,
|
length=12.5,
|
||||||
days=days[:5],
|
days=days[:5],
|
||||||
balance_offset=4,
|
balance_offset=4,
|
||||||
constraint=[
|
constraints=[MaxShiftsPerWeekConstraint(max_shifts=2)],
|
||||||
{
|
|
||||||
"name": "max_shifts_per_week",
|
|
||||||
"options": 2,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
sites=(
|
sites=(
|
||||||
@@ -122,7 +130,7 @@ def main(
|
|||||||
days=days[:5],
|
days=days[:5],
|
||||||
balance_offset=4,
|
balance_offset=4,
|
||||||
workers_required=1,
|
workers_required=1,
|
||||||
constraint=[{"name": "max_shifts_per_week", "options": 2}],
|
constraints=[MaxShiftsPerWeekConstraint(max_shifts=2)],
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
sites=(
|
sites=(
|
||||||
@@ -130,6 +138,7 @@ def main(
|
|||||||
"exeter twilights and weekends",
|
"exeter twilights and weekends",
|
||||||
"exeter no nights",
|
"exeter no nights",
|
||||||
"exeter weekends",
|
"exeter weekends",
|
||||||
|
"exeter twilights",
|
||||||
),
|
),
|
||||||
name="weekend_exeter",
|
name="weekend_exeter",
|
||||||
length=12.5,
|
length=12.5,
|
||||||
@@ -137,7 +146,7 @@ def main(
|
|||||||
balance_offset=3,
|
balance_offset=3,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
force_as_block=True,
|
force_as_block=True,
|
||||||
constraint=[{"name": "post", "options": 2}, {"name": "pre", "options": 2}],
|
constraints=[PostShiftConstraint(days=2), PreShiftConstraint(days=2)],
|
||||||
# constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2},],
|
# constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2},],
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
@@ -155,7 +164,7 @@ def main(
|
|||||||
# rota_on_nwds=True,
|
# rota_on_nwds=True,
|
||||||
# force_as_block=True,
|
# force_as_block=True,
|
||||||
assign_as_block=True,
|
assign_as_block=True,
|
||||||
constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2}],
|
constraints=[PreShiftConstraint(days=2), PostShiftConstraint(days=2)],
|
||||||
# force_as_block_unless_nwd=True
|
# force_as_block_unless_nwd=True
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
@@ -166,7 +175,7 @@ def main(
|
|||||||
balance_offset=3,
|
balance_offset=3,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
force_as_block=True,
|
force_as_block=True,
|
||||||
constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2}],
|
constraints=[PreShiftConstraint(days=2), PostShiftConstraint(days=2)],
|
||||||
# force_as_block_unless_nwd=True
|
# force_as_block_unless_nwd=True
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
@@ -183,7 +192,7 @@ def main(
|
|||||||
workers_required=1,
|
workers_required=1,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
force_as_block=True,
|
force_as_block=True,
|
||||||
constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2}],
|
constraints=[PreShiftConstraint(days=2), PostShiftConstraint(days=2)],
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
sites=(
|
sites=(
|
||||||
@@ -198,7 +207,7 @@ def main(
|
|||||||
workers_required=1,
|
workers_required=1,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
force_as_block=True,
|
force_as_block=True,
|
||||||
constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2}],
|
constraints=[PreShiftConstraint(days=2), PostShiftConstraint(days=2)],
|
||||||
),
|
),
|
||||||
SingleShift(
|
SingleShift(
|
||||||
sites=(
|
sites=(
|
||||||
@@ -227,16 +236,13 @@ def main(
|
|||||||
workers_required=4,
|
workers_required=4,
|
||||||
force_as_block=True,
|
force_as_block=True,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
constraint=[
|
constraints=[
|
||||||
{"name": "night"},
|
NightConstraint(),
|
||||||
{"name": "pre", "options": 2},
|
PreShiftConstraint(days=2),
|
||||||
{"name": "post", "options": 2},
|
PostShiftConstraint(days=2),
|
||||||
{
|
RequireRemoteSitePresenceConstraint(site="plymouth", required_number=1),
|
||||||
"name": "require_remote_site_presence_week",
|
LimitGradeNumberConstraint(grade=2, max_number=1),
|
||||||
"options": ("plymouth", 1),
|
MinimumGradeNumberConstraint(grade=4, min_number=1),
|
||||||
},
|
|
||||||
{"name": "limit_grade_number", "options": {2: 1}},
|
|
||||||
{"name": "minimum_grade_number", "options": (4, 1)},
|
|
||||||
],
|
],
|
||||||
#end_date=datetime.datetime.strptime("2025-06-01", "%Y-%m-%d").date(),
|
#end_date=datetime.datetime.strptime("2025-06-01", "%Y-%m-%d").date(),
|
||||||
),
|
),
|
||||||
@@ -257,16 +263,13 @@ def main(
|
|||||||
workers_required=4,
|
workers_required=4,
|
||||||
force_as_block=True,
|
force_as_block=True,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
constraint=[
|
constraints=[
|
||||||
{"name": "night"},
|
NightConstraint(),
|
||||||
{"name": "pre", "options": 2},
|
PreShiftConstraint(days=2),
|
||||||
{"name": "post", "options": 3},
|
PostShiftConstraint(days=3),
|
||||||
{
|
RequireRemoteSitePresenceConstraint(site="plymouth", required_number=1),
|
||||||
"name": "require_remote_site_presence_week",
|
LimitGradeNumberConstraint(grade=2, max_number=2),
|
||||||
"options": ("plymouth", 1),
|
MinimumGradeNumberConstraint(grade=4, min_number=1),
|
||||||
},
|
|
||||||
{"name": "limit_grade_number", "options": {2: 2}},
|
|
||||||
{"name": "minimum_grade_number", "options": (4, 1)},
|
|
||||||
],
|
],
|
||||||
#end_date=datetime.datetime.strptime("2025-06-01", "%Y-%m-%d").date(),
|
#end_date=datetime.datetime.strptime("2025-06-01", "%Y-%m-%d").date(),
|
||||||
),
|
),
|
||||||
@@ -301,13 +304,18 @@ def main(
|
|||||||
site = w["site"]
|
site = w["site"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print(f"Worker {worker} has no site")
|
print(f"Worker {worker} has no site")
|
||||||
raise KeyError
|
print(w)
|
||||||
|
sys.exit(1)
|
||||||
grade = w["grade"]
|
grade = w["grade"]
|
||||||
try:
|
try:
|
||||||
fte = float(w["fte"]) * 100
|
fte = float(w["fte"]) * 100
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(f"{worker} has invalid fte: {fte}")
|
print(f"{worker} has invalid fte: {fte}")
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
except KeyError:
|
||||||
|
print(f"Worker {worker} has no FTE")
|
||||||
|
print(w)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
nwd = w["nwd"]
|
nwd = w["nwd"]
|
||||||
end_date = w["end_date"]
|
end_date = w["end_date"]
|
||||||
@@ -349,41 +357,36 @@ def main(
|
|||||||
if oop:
|
if oop:
|
||||||
formatted_oops = []
|
formatted_oops = []
|
||||||
for dates in oop.split(","):
|
for dates in oop.split(","):
|
||||||
print(dates)
|
raw = dates.strip()
|
||||||
if "-" in dates:
|
# strip surrounding brackets/parentheses/quotes
|
||||||
s, e = dates.split("-")
|
raw = raw.strip("()[]\"' ")
|
||||||
elif "to" in dates:
|
if "-" in raw:
|
||||||
s, e = dates.split(" to ")
|
s, e = raw.split("-", 1)
|
||||||
|
elif " to " in raw:
|
||||||
|
s, e = raw.split(" to ", 1)
|
||||||
|
elif "to" in raw:
|
||||||
|
s, e = raw.split("to", 1)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Cannot parse OOP dates: '{oop}' for {worker}")
|
raise ValueError(f"Cannot parse OOP dates: '{oop}' for {worker}")
|
||||||
try:
|
|
||||||
formatted_oops.append(
|
s = s.strip().strip("()[]\"' ")
|
||||||
{
|
e = e.strip().strip("()[]\"' ")
|
||||||
"start_date": datetime.datetime.strptime(
|
|
||||||
s.strip(), "%d/%m/%Y"
|
parsed = False
|
||||||
).date(),
|
for fmt in ("%d/%m/%Y", "%d/%m/%y"):
|
||||||
"end_date": datetime.datetime.strptime(
|
|
||||||
e.strip(), "%d/%m/%Y"
|
|
||||||
).date(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
try:
|
try:
|
||||||
formatted_oops.append(
|
start_dt = datetime.datetime.strptime(s, fmt).date()
|
||||||
{
|
end_dt = datetime.datetime.strptime(e, fmt).date()
|
||||||
"start_date": datetime.datetime.strptime(
|
formatted_oops.append({"start_date": start_dt, "end_date": end_dt})
|
||||||
s.strip(), "%d/%m/%y"
|
parsed = True
|
||||||
).date(),
|
break
|
||||||
"end_date": datetime.datetime.strptime(
|
except ValueError:
|
||||||
e.strip(), "%d/%m/%y"
|
continue
|
||||||
).date(),
|
|
||||||
}
|
if not parsed:
|
||||||
)
|
print("WORKER", worker)
|
||||||
except ValueError as e:
|
print("DATES", s, e)
|
||||||
print(e)
|
raise ValueError(f"Cannot parse OOP dates: '{oop}' for {worker}")
|
||||||
print("WORKER", worker)
|
|
||||||
print("DATES", s, e)
|
|
||||||
raise
|
|
||||||
|
|
||||||
oop = formatted_oops
|
oop = formatted_oops
|
||||||
else:
|
else:
|
||||||
@@ -423,21 +426,19 @@ def main(
|
|||||||
|
|
||||||
shift_fte_overrides = {}
|
shift_fte_overrides = {}
|
||||||
|
|
||||||
#if worker_name == "Ben Kemp":
|
if worker_name == "Hadi Mohamed":
|
||||||
# shift_fte_overrides = {
|
shift_fte_overrides = {
|
||||||
# "plymouth_twilight": 100,
|
"weekend_exeter": 50,
|
||||||
# "weekend_plymouth1": 50,
|
}
|
||||||
# "weekend_plymouth2": 50,
|
|
||||||
# }
|
|
||||||
#elif worker_name == "Joel Lim":
|
#elif worker_name == "Joel Lim":
|
||||||
# shift_fte_overrides = {
|
# shift_fte_overrides = {
|
||||||
# "plymouth_twilight": 100,
|
# "plymouth_twilight": 100,
|
||||||
# "weekend_exeter": 50,
|
# "weekend_exeter": 50,
|
||||||
# }
|
# }
|
||||||
if worker_name == "Nang Thiriphoo":
|
#if worker_name == "Nang Thiriphoo":
|
||||||
shift_fte_overrides = {
|
# shift_fte_overrides = {
|
||||||
"weekend_exeter": 40,
|
# "weekend_exeter": 40,
|
||||||
}
|
# }
|
||||||
|
|
||||||
w = Worker(
|
w = Worker(
|
||||||
name=worker_name,
|
name=worker_name,
|
||||||
@@ -1,41 +1,49 @@
|
|||||||
import csv
|
import csv
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
from requests import Session
|
from requests import Session
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from rich.pretty import pprint
|
from rich.pretty import pprint
|
||||||
|
|
||||||
|
|
||||||
from rota_generator.workers import Worker, NotAvailableToWork, NonWorkingDays, WorkRequests, PreferenceNotToWork, OutOfProgramme
|
from rota_generator.workers import (
|
||||||
|
Worker,
|
||||||
|
NotAvailableToWork,
|
||||||
|
NonWorkingDays,
|
||||||
|
WorkRequests,
|
||||||
|
PreferenceNotToWork,
|
||||||
|
OutOfProgramme,
|
||||||
|
)
|
||||||
|
|
||||||
date_re = r"[\d]{1,2}\/[\d]{1,2}\/[\d]{2}"
|
date_re = r"[\d]{1,2}\/[\d]{1,2}\/[\d]{2}"
|
||||||
|
|
||||||
|
|
||||||
live_rota = True
|
live_rota = True
|
||||||
|
|
||||||
def load_leave(Rota):
|
|
||||||
|
|
||||||
|
def load_leave(Rota):
|
||||||
with Session() as s:
|
with Session() as s:
|
||||||
shifts = Rota.get_shift_names()
|
shifts = Rota.get_shift_names()
|
||||||
|
|
||||||
workers = {}
|
workers = {}
|
||||||
|
|
||||||
headers = {
|
headers = {"Cache-Control": "no-cache", "Pragma": "no-cache"}
|
||||||
"Cache-Control": "no-cache",
|
|
||||||
"Pragma": "no-cache"
|
|
||||||
}
|
|
||||||
|
|
||||||
if live_rota:
|
if live_rota:
|
||||||
download = s.get("https://docs.google.com/spreadsheets/d/e/2PACX-1vSfcQi5Qs__A8hE2CVvUA_6ULNJALrtZOBHPUZ3xyrxPif9obtQF2IqhioT_4nebjHV1Ac5iqhNtuq4/pub?gid=2024511103&single=true&output=csv", headers=headers)
|
download = s.get(
|
||||||
#download = s.get("https://docs.google.com/spreadsheets/d/e/2PACX-1vSRx9VWXSlRubPyA0RhiI-Oqf5eHNYYEc6rFzlraDbR5_8qqr5g13-4uV-gn4u-TjZxiSMv1fBUaESq/pub?gid=814517272&single=true&output=csv", headers=headers)
|
"https://docs.google.com/spreadsheets/d/e/2PACX-1vQtNrp9F1fjtAh8vFX-W_R3RlD9H-2P_vCLMoHrVKR0e24yabbsdrD65VjwxoHlV1Qnatmw1NeghQHG/pub?gid=1199196967&single=true&output=csv",
|
||||||
decoded_content = download.content.decode('utf-8')
|
headers=headers,
|
||||||
|
)
|
||||||
|
# download = s.get("https://docs.google.com/spreadsheets/d/e/2PACX-1vSRx9VWXSlRubPyA0RhiI-Oqf5eHNYYEc6rFzlraDbR5_8qqr5g13-4uV-gn4u-TjZxiSMv1fBUaESq/pub?gid=814517272&single=true&output=csv", headers=headers)
|
||||||
|
decoded_content = download.content.decode("utf-8")
|
||||||
|
|
||||||
reader = csv.reader(decoded_content.splitlines(), delimiter=',')
|
reader = csv.reader(decoded_content.splitlines(), delimiter=",")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
with open("/home/ross/Downloads/PROC Draft RK version.csv") as f:
|
with open("/home/ross/Downloads/PROC Draft RK version.csv") as f:
|
||||||
reader = csv.reader(f.read().splitlines(), delimiter=',')
|
reader = csv.reader(f.read().splitlines(), delimiter=",")
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
leave = {}
|
leave = {}
|
||||||
@@ -48,7 +56,7 @@ def load_leave(Rota):
|
|||||||
for row in reader:
|
for row in reader:
|
||||||
if n < 10:
|
if n < 10:
|
||||||
print(row)
|
print(row)
|
||||||
row_title = row[1]
|
row_title = row[0]
|
||||||
print(row_title)
|
print(row_title)
|
||||||
row_date = row[0]
|
row_date = row[0]
|
||||||
r = row[2:]
|
r = row[2:]
|
||||||
@@ -80,44 +88,81 @@ def load_leave(Rota):
|
|||||||
|
|
||||||
worker = workers[a]
|
worker = workers[a]
|
||||||
|
|
||||||
lower_item = r[i].lower()
|
lower_item = r[i].lower().strip()
|
||||||
if lower_item == "derriford":
|
if lower_item == "derriford":
|
||||||
lower_item = "plymouth"
|
lower_item = "plymouth"
|
||||||
|
|
||||||
if lower_item == "derriford twilights":
|
if lower_item == "derriford twilights":
|
||||||
lower_item = "plymouth_twilights"
|
lower_item = "plymouth_twilights"
|
||||||
|
|
||||||
if "PROC site" in row_title or row_title == "PROC nights from" or "proc nights" in row_title.lower():
|
if (
|
||||||
|
"PROC site" in row_title
|
||||||
|
or row_title == "PROC nights from"
|
||||||
|
or "proc nights" in row_title.lower()
|
||||||
|
):
|
||||||
worker["site_pref"] = lower_item
|
worker["site_pref"] = lower_item
|
||||||
|
|
||||||
elif row_title in ("Rotation", "Site", "Placement", "Placement location"):
|
elif row_title in (
|
||||||
|
"Rotation",
|
||||||
|
"Site",
|
||||||
|
"Placement",
|
||||||
|
"Placement location",
|
||||||
|
"Placement location",
|
||||||
|
):
|
||||||
worker["site"] = lower_item
|
worker["site"] = lower_item
|
||||||
|
|
||||||
elif row_title in ("Grade", "Grade (ST)") or "Year of training" in row_title:
|
elif (
|
||||||
|
row_title in ("Grade", "Grade (ST)")
|
||||||
|
or "Year of training" in row_title
|
||||||
|
):
|
||||||
worker["grade"] = lower_item.lstrip("ST")
|
worker["grade"] = lower_item.lstrip("ST")
|
||||||
|
|
||||||
elif row_title in ("%FTE", "%FTE on-call", "FTE", "FTE on-call") or "on-call commitment" in row_title:
|
elif (
|
||||||
if lower_item.endswith("%"):
|
row_title
|
||||||
lower_item = float(lower_item.rstrip("%")) / 100
|
in (
|
||||||
worker["fte"] = lower_item
|
"%FTE",
|
||||||
|
"%FTE on-call",
|
||||||
|
"FTE",
|
||||||
|
"FTE on-call",
|
||||||
|
"Full-time equivalent (FTE)",
|
||||||
|
)
|
||||||
|
or "on-call commitment" in row_title
|
||||||
|
):
|
||||||
|
if lower_item:
|
||||||
|
if lower_item.endswith("%"):
|
||||||
|
lower_item = float(lower_item.rstrip("%")) / 100
|
||||||
|
worker["fte"] = lower_item
|
||||||
|
|
||||||
elif "NWD" in row_title:
|
elif (
|
||||||
|
"NWD" in row_title or row_title == "If <100% FTE, specify days off:"
|
||||||
|
):
|
||||||
worker["nwd"] = lower_item
|
worker["nwd"] = lower_item
|
||||||
|
|
||||||
elif row_title == "Flexible NWD":
|
elif row_title == "Flexible NWD":
|
||||||
worker["flexible_nwd"] = lower_item
|
worker["flexible_nwd"] = lower_item
|
||||||
|
|
||||||
elif row_title in ("End Date", "CCT date"):
|
elif row_title in ("End Date", "CCT date", "CCT Date"):
|
||||||
if lower_item:
|
if lower_item:
|
||||||
try:
|
try:
|
||||||
date = datetime.strptime(lower_item, "%d/%m/%y").date()
|
date = datetime.strptime(lower_item, "%d/%m/%y").date()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
date = datetime.strptime(lower_item, "%d/%m/%Y").date()
|
try:
|
||||||
|
date = datetime.strptime(lower_item, "%d-%m-%y").date()
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
date = datetime.strptime(
|
||||||
|
lower_item, "%d/%m/%Y"
|
||||||
|
).date()
|
||||||
|
except ValueError:
|
||||||
|
print(
|
||||||
|
f"Cannot parse date: '{lower_item}' (n: {n}, row: {row})"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
worker["end_date"] = date
|
worker["end_date"] = date
|
||||||
else:
|
else:
|
||||||
worker["end_date"] = None
|
worker["end_date"] = None
|
||||||
|
|
||||||
|
|
||||||
elif row_title in ("Start date",):
|
elif row_title in ("Start date",):
|
||||||
if lower_item:
|
if lower_item:
|
||||||
try:
|
try:
|
||||||
@@ -125,13 +170,22 @@ def load_leave(Rota):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
date = datetime.strptime(lower_item, "%d/%m/%Y").date()
|
date = datetime.strptime(lower_item, "%d/%m/%Y").date()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.warning(f"Cannot parse date: {lower_item} (n: {n}, row: {row})")
|
logger.warning(
|
||||||
|
f"Cannot parse date: {lower_item} (n: {n}, row: {row})"
|
||||||
|
)
|
||||||
raise ValueError(f"Cannot parse date: {lower_item}")
|
raise ValueError(f"Cannot parse date: {lower_item}")
|
||||||
worker["start_date"] = date
|
worker["start_date"] = date
|
||||||
else:
|
else:
|
||||||
worker["start_date"] = None
|
worker["start_date"] = None
|
||||||
|
|
||||||
elif "OOP" in row_title or "out of programme" in row_title.lower():
|
elif "OOP" in row_title or "out of programme" in row_title.lower():
|
||||||
|
if lower_item in (
|
||||||
|
"OOPT 1 START",
|
||||||
|
"OOPT 1 END",
|
||||||
|
"OOPT 2 START",
|
||||||
|
"OOPT 2 END",
|
||||||
|
):
|
||||||
|
continue
|
||||||
worker["oop"] = lower_item
|
worker["oop"] = lower_item
|
||||||
|
|
||||||
elif row_title == "Group":
|
elif row_title == "Group":
|
||||||
@@ -157,7 +211,6 @@ def load_leave(Rota):
|
|||||||
worker["bank_holiday_extra"] = int(lower_item)
|
worker["bank_holiday_extra"] = int(lower_item)
|
||||||
|
|
||||||
elif re.match(date_re, row_date) is not None:
|
elif re.match(date_re, row_date) is not None:
|
||||||
|
|
||||||
if lower_item != "":
|
if lower_item != "":
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
@@ -167,29 +220,41 @@ def load_leave(Rota):
|
|||||||
|
|
||||||
# This may be easier to do as a dict
|
# This may be easier to do as a dict
|
||||||
if lower_item in shifts:
|
if lower_item in shifts:
|
||||||
worker["requests"].append(WorkRequests(date=date, shift=lower_item))
|
worker["requests"].append(
|
||||||
|
WorkRequests(date=date, shift=lower_item)
|
||||||
|
)
|
||||||
elif lower_item in ("nights only"):
|
elif lower_item in ("nights only"):
|
||||||
if date.weekday() < 5:
|
if date.weekday() < 5:
|
||||||
worker["requests"].append(WorkRequests(date=date, shift="night_weekday"))
|
worker["requests"].append(
|
||||||
|
WorkRequests(date=date, shift="night_weekday")
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
worker["requests"].append(WorkRequests(date=date, shift="night_weekend"))
|
worker["requests"].append(
|
||||||
|
WorkRequests(date=date, shift="night_weekend")
|
||||||
elif lower_item in ("work_request", "happy to work", "offered", "may be", "volunteered"):
|
)
|
||||||
worker["requests"].append(WorkRequests(date=date, shift="*"))
|
|
||||||
|
|
||||||
|
elif lower_item in (
|
||||||
|
"work_request",
|
||||||
|
"happy to work",
|
||||||
|
"offered",
|
||||||
|
"may be",
|
||||||
|
"volunteered",
|
||||||
|
):
|
||||||
|
worker["requests"].append(
|
||||||
|
WorkRequests(date=date, shift="*")
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
worker["leave"].append(NotAvailableToWork(date=date, reason=lower_item))
|
worker["leave"].append(
|
||||||
|
NotAvailableToWork(date=date, reason=lower_item)
|
||||||
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"Error with: {lower_item}")
|
print(f"Error with: {lower_item}")
|
||||||
print(f"{row=}")
|
print(f"{row=}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
n = n + 1
|
n = n + 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return workers
|
return workers
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user