From 47bfb16891ef6e2b21c05126ea0965bea0349cd5 Mon Sep 17 00:00:00 2001 From: Ross Date: Wed, 17 Sep 2025 21:21:23 +0100 Subject: [PATCH] Refactor logging implementation to use Loguru for improved logging management and add warnings for unknown leave values and worker site validation. --- gen_cons.py | 3 +++ rota/shifts.py | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/gen_cons.py b/gen_cons.py index e527a8c..de13146 100644 --- a/gen_cons.py +++ b/gen_cons.py @@ -17,6 +17,7 @@ from rota.workers import ( OutOfProgramme, ) +from loguru import logger app = typer.Typer() @@ -68,6 +69,8 @@ def extract_leave_and_rota_from_calender(calender_df): "date": date, "worker": worker, }) + else: + logger.warning(f"Unknown leave value for {worker} on {date}: {leave_val}") #print(leave_requests) return leave_requests, work_requests, rota_data diff --git a/rota/shifts.py b/rota/shifts.py index 1b61033..0af3e08 100644 --- a/rota/shifts.py +++ b/rota/shifts.py @@ -34,14 +34,9 @@ from rich.progress import track from rich.panel import Panel from rich import print -import logging +from loguru import logger -logging.basicConfig( - filename="rota.log", - filemode="w", - format="%(name)s - %(levelname)s - %(message)s", - level=logging.DEBUG, -) +logger.add("rota.log", rotation="1 MB", level="DEBUG") ShiftName = str DayStr = str @@ -413,7 +408,7 @@ class RotaBuilder(object): "Shift/invalid start date", "Shift/invalid end date", "Locum/no locum availability", - "Force assignment/leave conflict", # Currently will always be infeasible + #"Force assignment/leave conflict", # Currently will always be infeasible ] self.results = None @@ -1515,7 +1510,7 @@ class RotaBuilder(object): console.rule( f"Generate worker constraints: [bold blue]{worker.name}[/bold blue]" ) - logging.debug(f"Generate worker constraints: {worker.name}") + logger.debug(f"Generate worker constraints: {worker.name}") for week, day in self.get_week_day_combinations(): shifts_today = self.get_shift_names_by_week_day(week, day) @@ -3312,6 +3307,7 @@ class RotaBuilder(object): if worker.site not in self.sites: message = f"Worker with name '{worker.name}' ({worker.id}) has no valid shifts (site: {worker.site})" + logger.warning(message) self.add_warning("Worker/no valid shifts", message) @@ -4540,8 +4536,32 @@ class RotaBuilder(object): f"Shift balance extra: {getattr(worker, 'shift_balance_extra', '')}", f"Non-working days: {getattr(worker, 'non_working_day_list', '')}", f"OOP: {getattr(worker, 'oop', '')}", + f"Unavailable: {''}", f"Shift targets: {json.dumps(getattr(worker, 'shift_target_number', {}))}", ] + # Build unavailability list for this worker + unavail_list = [] + for entry in sorted(self.unavailable_to_work): + wid, wk, d = entry + if wid != worker.id: + continue + date = self.get_date_by_week_day(wk, d) + reason = self.unavailable_to_work_reason.get((wid, wk, d), "") + if reason: + unavail_list.append(f"{date.isoformat()} ({d}) [week {wk}]: {reason}") + else: + # date may be a string if fallback used + if isinstance(date, str): + unavail_list.append(f"{date} ({d}) [week {wk}]") + else: + unavail_list.append(f"{date.isoformat()} ({d}) [week {wk}]") + + # replace placeholder Unavailable entry + details = [line for line in details if not line.startswith("Unavailable:")] + details.insert( + -1, # before Shift targets + f"Unavailable: {', '.join(unavail_list) if unavail_list else ''}", + ) worker_details_human.append("\n".join(details)) worker_details_human_str = "\n\n".join(worker_details_human)