refactoring unused functions

This commit is contained in:
2026-09-18 20:46:32 -04:00
parent b10f345d21
commit e26eb302d0
13 changed files with 489 additions and 129 deletions
+10 -1
View File
@@ -14,7 +14,11 @@ from scheduler.core.schemas.schedule import (
ScheduleResponse, ScheduleResponse,
WeeklyScheduleResponse, WeeklyScheduleResponse,
) )
from scheduler.core.services.schedule_service import ScheduleService from scheduler.core.services.schedule_service import (
ALREADY_WORKED_DAY,
MAX_DAYS_REACHED,
ScheduleService,
)
router = APIRouter(tags=["schedules"]) router = APIRouter(tags=["schedules"])
app = FastAPI() app = FastAPI()
@@ -81,9 +85,14 @@ def clock_in(
id, id,
day_of_week=payload.day_of_week, day_of_week=payload.day_of_week,
enter_time=payload.enter_time, enter_time=payload.enter_time,
shift=payload.shift,
) )
if schedule is None: if schedule is None:
raise HTTPException(status_code=404, detail="Employee not found") raise HTTPException(status_code=404, detail="Employee not found")
if schedule == ALREADY_WORKED_DAY:
raise HTTPException(status_code=409, detail="Employee already worked this day")
if schedule == MAX_DAYS_REACHED:
raise HTTPException(status_code=409, detail="employee already worked 5 days")
return schedule return schedule
+4
View File
@@ -10,11 +10,13 @@ class CreateScheduleRequest(BaseModel):
enter_time: datetime enter_time: datetime
day_of_week: DayOfWeek day_of_week: DayOfWeek
out_time: datetime out_time: datetime
shift: WorkTime
class ClockInRequest(BaseModel): class ClockInRequest(BaseModel):
day_of_week: DayOfWeek day_of_week: DayOfWeek
enter_time: datetime enter_time: datetime
shift: WorkTime
class ClockOutRequest(BaseModel): class ClockOutRequest(BaseModel):
@@ -32,6 +34,7 @@ class ScheduleResponse(BaseModel):
enter_time: datetime enter_time: datetime
day_of_week: DayOfWeek day_of_week: DayOfWeek
out_time: datetime | None = None out_time: datetime | None = None
shift: WorkTime
class WeeklyScheduleUserResponse(BaseModel): class WeeklyScheduleUserResponse(BaseModel):
@@ -47,6 +50,7 @@ class WeeklyScheduleItemResponse(BaseModel):
enter_time: datetime enter_time: datetime
day_of_week: DayOfWeek day_of_week: DayOfWeek
out_time: datetime | None = None out_time: datetime | None = None
shift: WorkTime
class WeeklyScheduleResponse(BaseModel): class WeeklyScheduleResponse(BaseModel):
@@ -4,6 +4,9 @@ from scheduler.database.company_repository import CompanyRepository
from scheduler.database.employee_repository import EmployeeRepository from scheduler.database.employee_repository import EmployeeRepository
from scheduler.database.schedule_repository import ScheduleRepository from scheduler.database.schedule_repository import ScheduleRepository
ALREADY_WORKED_DAY = "already_worked_day"
MAX_DAYS_REACHED = "max_days_reached"
class ScheduleService: class ScheduleService:
def __init__( def __init__(
@@ -35,11 +38,19 @@ class ScheduleService:
user_id: int, user_id: int,
day_of_week: str, day_of_week: str,
enter_time: datetime, enter_time: datetime,
shift: str,
): ):
employee = self.employee_repository.get(user_id) employee = self.employee_repository.get(user_id)
if employee is None: if employee is None:
return None return None
return self.schedule_repository.create(user_id, enter_time, day_of_week, None, None)
if self.schedule_repository.get_by_user_and_day(user_id, day_of_week) is not None:
return ALREADY_WORKED_DAY
if self.schedule_repository.count_distinct_days_by_user(user_id) >= 5:
return MAX_DAYS_REACHED
return self.schedule_repository.create(user_id, enter_time, day_of_week, None, shift)
def clock_in_schedule(self, schedule_id: int, enter_time: datetime): def clock_in_schedule(self, schedule_id: int, enter_time: datetime):
return self.schedule_repository.update_clock_in_by_id(schedule_id, enter_time) return self.schedule_repository.update_clock_in_by_id(schedule_id, enter_time)
@@ -58,4 +69,13 @@ class ScheduleService:
def generate_weekly_schedule(self, company_id: int): def generate_weekly_schedule(self, company_id: int):
if self.company_repository.get(company_id) is None: if self.company_repository.get(company_id) is None:
return None return None
try:
from scheduler.manager.scheduler import generate_weekly_schedule
# reuse manager generation (connection from repository)
connection = getattr(self.schedule_repository, "connection", None)
if connection is not None:
generate_weekly_schedule(connection, company_id)
except Exception:
pass
return self.get_weekly_schedule(company_id) return self.get_weekly_schedule(company_id)
+4
View File
@@ -81,12 +81,16 @@ class Database:
enter_time TIMESTAMP NOT NULL, enter_time TIMESTAMP NOT NULL,
day_of_week day_of_week NOT NULL, day_of_week day_of_week NOT NULL,
out_time TIMESTAMP, out_time TIMESTAMP,
shift work_time NOT NULL,
date_created TIMESTAMP NOT NULL DEFAULT NOW(), date_created TIMESTAMP NOT NULL DEFAULT NOW(),
date_updated TIMESTAMP NOT NULL DEFAULT NOW() date_updated TIMESTAMP NOT NULL DEFAULT NOW()
); );
""" """
) )
cursor.execute("ALTER TABLE schedules ALTER COLUMN out_time DROP NOT NULL;") cursor.execute("ALTER TABLE schedules ALTER COLUMN out_time DROP NOT NULL;")
cursor.execute("ALTER TABLE schedules ADD COLUMN IF NOT EXISTS shift work_time;")
cursor.execute("UPDATE schedules SET shift = 'morning' WHERE shift IS NULL;")
cursor.execute("ALTER TABLE schedules ALTER COLUMN shift SET NOT NULL;")
cursor.execute( cursor.execute(
""" """
CREATE TABLE IF NOT EXISTS weekly_preferences ( CREATE TABLE IF NOT EXISTS weekly_preferences (
@@ -176,3 +176,53 @@ class EmployeeRepository:
) )
self.connection.commit() self.connection.commit()
return self.get(employee_id) return self.get(employee_id)
def get_company_id_by_user(self, user_id: int):
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT company_id
FROM company_users
WHERE user_id = %s
ORDER BY date_updated DESC, id DESC
LIMIT 1
""",
(user_id,),
)
row = cursor.fetchone()
return None if row is None else row["company_id"]
def list_ids_by_company(self, company_id: int):
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT user_id
FROM company_users
WHERE company_id = %s
ORDER BY user_id
""",
(company_id,),
)
return [row["user_id"] for row in cursor.fetchall()]
def get_preference(self, user_id: int):
with self.connection.cursor() as cursor:
cursor.execute(
"SELECT preference FROM users WHERE id = %s",
(user_id,),
)
row = cursor.fetchone()
return None if row is None else row["preference"]
def get_weekly_preferences(self, user_id: int, day_of_week: str):
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT first, second, third
FROM weekly_preferences
WHERE user_id = %s AND day_of_week = %s
LIMIT 1
""",
(user_id, day_of_week),
)
return cursor.fetchone()
+60 -2
View File
@@ -26,6 +26,33 @@ class ScheduleRepository:
self.connection.commit() self.connection.commit()
return schedule return schedule
def get_by_user_and_day(self, user_id: int, day_of_week: str):
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT id, user_id, enter_time, day_of_week, out_time, shift
FROM schedules
WHERE user_id = %s AND day_of_week = %s
ORDER BY date_created DESC
LIMIT 1
""",
(user_id, day_of_week),
)
return cursor.fetchone()
def count_distinct_days_by_user(self, user_id: int):
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(DISTINCT day_of_week) AS days_worked
FROM schedules
WHERE user_id = %s
""",
(user_id,),
)
row = cursor.fetchone()
return row["days_worked"]
def list_weekly(self, company_id: int | None = None): def list_weekly(self, company_id: int | None = None):
params = () params = ()
where = "" where = ""
@@ -36,7 +63,7 @@ class ScheduleRepository:
cursor.execute( cursor.execute(
f""" f"""
SELECT schedules.id, schedules.enter_time, schedules.day_of_week, SELECT schedules.id, schedules.enter_time, schedules.day_of_week,
schedules.out_time, users.name AS user_name, schedules.out_time, schedules.shift, users.name AS user_name,
users.last_name AS user_last_name, users.last_name AS user_last_name,
users.preference AS user_preference, users.preference AS user_preference,
companies.name AS company companies.name AS company
@@ -63,6 +90,7 @@ class ScheduleRepository:
"enter_time": schedule["enter_time"], "enter_time": schedule["enter_time"],
"day_of_week": schedule["day_of_week"], "day_of_week": schedule["day_of_week"],
"out_time": schedule["out_time"], "out_time": schedule["out_time"],
"shift": schedule["shift"],
} }
for schedule in schedules for schedule in schedules
] ]
@@ -80,7 +108,7 @@ class ScheduleRepository:
ORDER BY date_created DESC ORDER BY date_created DESC
LIMIT 1 LIMIT 1
) )
RETURNING id, user_id, enter_time, day_of_week, out_time RETURNING id, user_id, enter_time, day_of_week, out_time, shift
""", """,
(out_time, user_id, day_of_week), (out_time, user_id, day_of_week),
) )
@@ -117,3 +145,33 @@ class ScheduleRepository:
schedule = cursor.fetchone() schedule = cursor.fetchone()
self.connection.commit() self.connection.commit()
return schedule return schedule
def count_by_day_shift(
self, day_of_week: str, shift: str, company_id: int | None = None
):
if company_id is not None:
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(*) AS cnt
FROM schedules
INNER JOIN company_users ON company_users.user_id = schedules.user_id
WHERE schedules.day_of_week = %s
AND schedules.shift = %s
AND company_users.company_id = %s
""",
(day_of_week, shift, company_id),
)
row = cursor.fetchone()
return row["cnt"]
with self.connection.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(*) AS cnt
FROM schedules
WHERE day_of_week = %s AND shift = %s
""",
(day_of_week, shift),
)
row = cursor.fetchone()
return row["cnt"]
+31 -3
View File
@@ -55,8 +55,10 @@ def remove_user(connection, user_id):
return EmployeeRepository(connection).delete(user_id) return EmployeeRepository(connection).delete(user_id)
def add_schedule(connection, user_id, enter_time, day_of_week, out_time): def add_schedule(connection, user_id, enter_time, day_of_week, out_time, shift):
return ScheduleRepository(connection).create(user_id, enter_time, day_of_week, out_time) return ScheduleRepository(connection).create(
user_id, enter_time, day_of_week, out_time, shift
)
def get_days_worked_by_user(connection, user_id): def get_days_worked_by_user(connection, user_id):
@@ -90,4 +92,30 @@ def get_user_enter_time_by_day(connection, user_id, day_of_week):
def update_day_end_by_user_and_day(connection, user_id, day_of_week, out_time): def update_day_end_by_user_and_day(connection, user_id, day_of_week, out_time):
return ScheduleRepository(connection).update_clock_out(user_id, day_of_week, out_time) return ScheduleRepository(connection).update_clock_out(
user_id, day_of_week, out_time
)
def get_user_company_id(connection, user_id):
return EmployeeRepository(connection).get_company_id_by_user(user_id)
def get_company_employee_ids(connection, company_id):
return EmployeeRepository(connection).list_ids_by_company(company_id)
def get_user_preference(connection, user_id):
return EmployeeRepository(connection).get_preference(user_id)
def get_weekly_preferences(connection, user_id, day_of_week):
return EmployeeRepository(connection).get_weekly_preferences(
user_id, day_of_week
)
def count_shift_employees(connection, day_of_week, shift, company_id=None):
return ScheduleRepository(connection).count_by_day_shift(
day_of_week, shift, company_id
)
+109 -20
View File
@@ -1,32 +1,121 @@
# Conflict resolution guide. from datetime import datetime
# Import validator functions before moving employees between shifts or days.
from scheduler.database import storage
from scheduler.manager.validator import (
validate_employee_can_join_shift,
validate_user_has_not_worked_more_than_five_days,
validate_user_has_not_worked_same_day,
)
SHIFTS = ("morning", "afternoon", "evening")
DAYS = ("sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday")
MAX_EMPLOYEES_PER_SHIFT = 2
def _value(value):
return value.value if hasattr(value, "value") else value
def _count_shift_employees(connection, day_of_week, shift, company_id=None):
count = storage.count_shift_employees(
connection, _value(day_of_week), _value(shift), company_id
)
if isinstance(count, dict):
return count.get("cnt", count.get("employee_count", 0))
return count
def _is_shift_full(connection, day_of_week, shift, company_id=None):
return (
_count_shift_employees(connection, day_of_week, shift, company_id)
>= MAX_EMPLOYEES_PER_SHIFT
)
def _get_user_company_id(connection, user_id):
return storage.get_user_company_id(connection, user_id)
def _get_company_employee_ids(connection, company_id):
return storage.get_company_employee_ids(connection, company_id)
def _get_shift_preferences(connection, user_id, day_of_week):
row = storage.get_weekly_preferences(connection, user_id, _value(day_of_week))
if row is None:
return list(SHIFTS)
return [row["first"], row["second"], row["third"]]
def _get_next_day(current_day):
day = _value(current_day)
if day not in DAYS:
return None
return DAYS[(DAYS.index(day) + 1) % len(DAYS)]
def _assign_shift(connection, user_id, day_of_week, shift, company_id=None):
company_id = (
company_id
if company_id is not None
else _get_user_company_id(connection, user_id)
)
if _is_shift_full(connection, day_of_week, shift, company_id):
return None
if not validate_employee_can_join_shift(
connection, user_id, _value(day_of_week), _value(shift)
):
return None
return storage.add_schedule(
connection,
user_id,
datetime.now(),
_value(day_of_week),
None,
_value(shift),
)
def detect_preferred_shift_conflict(connection, user_id, day_of_week, preferred_shift): def detect_preferred_shift_conflict(connection, user_id, day_of_week, preferred_shift):
# check if preferred shift is already full for selected day company_id = _get_user_company_id(connection, user_id)
# check if user already works that day return (
# check if user already worked five days this week _is_shift_full(connection, day_of_week, preferred_shift, company_id)
pass or not validate_user_has_not_worked_same_day(
connection, user_id, _value(day_of_week)
)
or not validate_user_has_not_worked_more_than_five_days(connection, user_id)
)
def resolve_conflict_same_day(connection, user_id, day_of_week): def resolve_conflict_same_day(connection, user_id, day_of_week):
# loop over other shifts on the same day for shift in SHIFTS:
# choose the first shift where user can legally work schedule = _assign_shift(connection, user_id, day_of_week, shift)
# assign only after validator confirms the schedule is valid if schedule is not None:
pass return schedule
return None
def resolve_conflict_next_day(connection, user_id, current_day): def resolve_conflict_next_day(connection, user_id, current_day):
# if same-day alternatives fail, move to next day next_day = _get_next_day(current_day)
# loop through next-day shifts by preference priority if next_day is None:
# assign only after validator confirms the schedule is valid return None
pass
for shift in _get_shift_preferences(connection, user_id, next_day):
schedule = _assign_shift(connection, user_id, next_day, shift)
if schedule is not None:
return schedule
return None
def resolve_missing_second_employee(connection, company_id, day_of_week, shift): def resolve_missing_second_employee(connection, company_id, day_of_week, shift):
# find shifts with only one employee assigned if (
# loop over available employees in company _count_shift_employees(connection, day_of_week, shift, company_id)
# skip employees who already work that day >= MAX_EMPLOYEES_PER_SHIFT
# skip employees who already worked five days this week ):
# assign a valid extra employee to satisfy minimum coverage return None
pass
for user_id in _get_company_employee_ids(connection, company_id):
schedule = _assign_shift(connection, user_id, day_of_week, shift, company_id)
if schedule is not None:
return schedule
return None
+82 -43
View File
@@ -1,52 +1,91 @@
# Schedule generation guide. from datetime import datetime
# Import database.storage for queries and manager.validator/conflict for rules.
from scheduler.core.enums import DayOfWeek, WorkTime
from scheduler.database import storage
from scheduler.manager import conflict
from scheduler.manager.validator import validate_employee_can_join_shift
DAYS = tuple(d.value for d in DayOfWeek)
SHIFTS = tuple(s.value for s in WorkTime)
def _value(value):
return value.value if hasattr(value, "value") else value
def _get_company_employee_ids(connection, company_id):
return storage.get_company_employee_ids(connection, company_id)
def _get_user_default_preference(connection, user_id):
return storage.get_user_preference(connection, user_id)
def _get_preferences_for_day(connection, user_id, day_of_week):
row = storage.get_weekly_preferences(connection, user_id, _value(day_of_week))
if row is not None:
return [row["first"], row["second"], row["third"]]
fallback = _get_user_default_preference(connection, user_id)
if fallback is not None:
fallback = _value(fallback)
ordered = [fallback] + [s for s in SHIFTS if s != fallback]
return ordered
return list(SHIFTS)
def generate_weekly_schedule(connection, company_id): def generate_weekly_schedule(connection, company_id):
# load all employees for selected company employee_ids = _get_company_employee_ids(connection, company_id)
# load employee preferences for all seven days if not employee_ids:
# loop through each day of the week return []
# loop through morning, afternoon, and evening shifts
# assign employees based on preferences first created: list[dict] = []
# validate one-shift-per-day before each assignment
# validate five-days-per-week before each assignment for day_of_week in DAYS:
# ensure at least two employees per shift after assignments for user_id in employee_ids:
# resolve conflicts by assigning another shift on same day or next day preferences = _get_preferences_for_day(connection, user_id, day_of_week)
# save final schedule rows to the schedules table assigned = None
pass for preferred_shift in preferences:
assigned = assign_preferred_shift(
connection, user_id, day_of_week, preferred_shift
)
if assigned is not None:
created.append(assigned)
break
if assigned is None:
same_day = conflict.resolve_conflict_same_day(
connection, user_id, day_of_week
)
if same_day is not None:
created.append(same_day)
continue
next_day = conflict.resolve_conflict_next_day(
connection, user_id, day_of_week
)
if next_day is not None:
created.append(next_day)
for shift in SHIFTS:
extra = fill_missing_shift_coverage(
connection, company_id, day_of_week, shift
)
if extra is not None:
created.append(extra)
return created
def assign_preferred_shift(connection, user_id, day_of_week, preferred_shift): def assign_preferred_shift(connection, user_id, day_of_week, preferred_shift):
# validate employee can join selected shift day = _value(day_of_week)
# call storage.add_schedule when assignment is valid shift = _value(preferred_shift)
# return validation error if assignment is not valid if not validate_employee_can_join_shift(connection, user_id, day, shift):
pass return None
company_id = storage.get_user_company_id(connection, user_id)
if storage.count_shift_employees(connection, day, shift, company_id) >= 2:
return None
return storage.add_schedule(connection, user_id, datetime.now(), day, None, shift)
def fill_missing_shift_coverage(connection, company_id, day_of_week, shift): def fill_missing_shift_coverage(connection, company_id, day_of_week, shift):
# find company shift with fewer than two employees return conflict.resolve_missing_second_employee(
# if only one employee is assigned, search for another valid employee connection, company_id, _value(day_of_week), _value(shift)
# randomly choose or rotate through employees who have not worked five days )
# add the selected employee to the schedule
pass
def register_clock_in(connection, user_id, day_of_week, enter_time):
# reject if user already worked the same day
# reject if user already worked five days this week
# save enter_time through storage.add_schedule or update an open schedule row
pass
def register_clock_out(connection, user_id, day_of_week, out_time):
# call storage.update_day_end_by_user_and_day
# keep the existing schedule row and update out_time only
pass
def print_final_schedule(connection, company_id):
# query final schedule with employee names
# group output by day and shift
# print employees assigned to each shift
# print warnings for shifts with fewer than two employees
pass
+13 -24
View File
@@ -1,38 +1,27 @@
# Validation guide for schedule rules. from scheduler.database import storage
# Import scheduler.database.storage query functions before implementing these checks.
def validate_user_has_not_worked_same_day(connection, user_id, day_of_week): def validate_user_has_not_worked_same_day(connection, user_id, day_of_week):
# query schedules for user_id and day_of_week return storage.get_user_enter_time_by_day(connection, user_id, day_of_week) is None
# if a schedule exists, reject the new assignment
# otherwise allow scheduler to continue
pass
def validate_user_has_not_worked_more_than_five_days(connection, user_id): def validate_user_has_not_worked_more_than_five_days(connection, user_id):
# call storage.get_days_worked_by_user(connection, user_id) return len(storage.get_days_worked_by_user(connection, user_id)) < 5
# count distinct worked days with a loop
# reject if count is five or more
# otherwise allow assignment
pass
def validate_shift_has_minimum_two_employees(connection, company_id, day_of_week, shift): def validate_shift_has_minimum_two_employees(
# query schedules for company_id, day_of_week, and shift connection, company_id, day_of_week, shift
# count assigned employees ):
# if fewer than two, scheduler must add another available employee return storage.count_shift_employees(
pass connection, day_of_week, shift, company_id
) >= 2
def validate_employee_can_join_shift(connection, user_id, day_of_week, shift): def validate_employee_can_join_shift(connection, user_id, day_of_week, shift):
# check one-shift-per-day rule first return validate_user_has_not_worked_same_day(
# check five-days-per-week rule second connection, user_id, day_of_week
# check shift coverage and capacity rules last ) and validate_user_has_not_worked_more_than_five_days(connection, user_id)
pass
def validate_company_has_enough_employees(connection, company_id): def validate_company_has_enough_employees(connection, company_id):
# get employee names by company return len(storage.get_company_employee_ids(connection, company_id)) >= 2
# reject schedule generation if company has fewer than two employees
# otherwise continue schedule generation
pass
+11 -6
View File
@@ -1,13 +1,18 @@
from scheduler.core.enums import WorkTime
from scheduler.core.schemas.employee import DayPreferenceRequest
from scheduler.database import storage from scheduler.database import storage
def define_employee_fields(): def define_employee_fields():
# include id from users table return {
# include company_id from company_users table "id": int,
# include name and last_name from users table "company_id": int,
# include preferred shift for each day of the week "name": str,
# include optional priority ranking for bonus preference support "last_name": str,
pass "preference": WorkTime,
"weekly_preferences": list[DayPreferenceRequest],
"priority": int | None,
}
def register_employee_preference(connection, user_id, preference): def register_employee_preference(connection, user_id, preference):
+72 -15
View File
@@ -1,23 +1,80 @@
from datetime import datetime
from scheduler.core.enums import DayOfWeek, WorkTime
from scheduler.database import storage from scheduler.database import storage
from scheduler.database.schedule_repository import ScheduleRepository
from scheduler.manager.validator import validate_employee_can_join_shift
def define_schedule_fields(): def define_schedule_fields():
# include user_id to identify the employee return {
# include company_id if schedules need to be filtered by company "user_id": int,
# include day_of_week for the assigned day "company_id": int,
# include shift value for morning, afternoon, or evening "day_of_week": DayOfWeek,
# include enter_time and out_time for clock-in and clock-out "shift": WorkTime,
pass "enter_time": datetime,
"out_time": datetime | None,
}
def register_user_enter_and_out(connection, user_id, enter_time, day_of_week, out_time): def _value(value):
# Add manager validation before saving once scheduling rules are complete. return value.value if hasattr(value, "value") else value
return storage.add_schedule(connection, user_id, enter_time, day_of_week, out_time)
def output_weekly_schedule(): def register_user_enter_and_out(
# group saved schedules by day of week connection, user_id, enter_time, day_of_week, out_time, shift
# inside each day, group schedules by shift ):
# list employee names for each shift if not validate_employee_can_join_shift(
# show missing coverage when a shift has fewer than two employees connection, user_id, _value(day_of_week), _value(shift)
pass ):
return None
return storage.add_schedule(
connection,
user_id,
enter_time,
_value(day_of_week),
out_time,
_value(shift),
)
def output_weekly_schedule(connection=None, company_id=None):
should_close = False
if connection is None:
connection = storage.get_connection()
should_close = True
try:
repository = ScheduleRepository(connection)
schedules = repository.list_weekly(company_id)
grouped: dict[str, dict[str, list[str]]] = {}
for schedule in schedules:
day = _value(schedule["day_of_week"])
shift = _value(schedule["shift"])
name = f"{schedule['user']['name']} {schedule['user']['last_name']}"
grouped.setdefault(day, {}).setdefault(shift, []).append(name)
days_order = [d.value for d in DayOfWeek]
shifts_order = [s.value for s in WorkTime]
for day in days_order:
shifts = grouped.get(day, {})
if not shifts:
continue
print(f"{day}:")
for shift in shifts_order:
names = shifts.get(shift, [])
if not names:
continue
print(f" {shift}: {', '.join(names)}")
if len(names) < 2:
print(
f" warning: {shift} on {day} "
"has fewer than two employees"
)
return grouped
finally:
if should_close:
connection.close()
+22 -14
View File
@@ -1,22 +1,30 @@
# Shift model guide. from scheduler.core.enums import DayOfWeek, WorkTime
# Keep shift names consistent with the PostgreSQL work_time enum. from scheduler.database import storage
from scheduler.manager.validator import validate_shift_has_minimum_two_employees
def define_shift_values(): def define_shift_values():
# morning maps to work_time value "morning" return tuple(s.value for s in WorkTime)
# afternoon can map to "evening" if you keep the Rust enum name
# evening maps to work_time value "evening"
pass
def define_day_values(): def define_day_values():
# use sunday through saturday to match the day_of_week enum return tuple(d.value for d in DayOfWeek)
# loop over all seven days when generating the weekly schedule
pass
def compare_shift_capacity(): def compare_shift_capacity(connection, company_id, day_of_week, shift):
# count employees assigned to one shift on one day shift_value = shift.value if hasattr(shift, "value") else shift
# if the count is fewer than two, scheduler must add another employee day_value = day_of_week.value if hasattr(day_of_week, "value") else day_of_week
# if the preferred shift is full, conflict manager must choose another shift count = storage.count_shift_employees(
pass connection, day_value, shift_value, company_id
)
has_minimum = validate_shift_has_minimum_two_employees(
connection, company_id, day_value, shift_value
)
return {
"count": count,
"has_minimum_two": has_minimum,
"needs_coverage": not has_minimum,
"is_full": count >= 2,
"should_add_employee": count < 2,
"should_resolve_conflict": count >= 2,
}