from datetime import datetime 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): company_id = _get_user_company_id(connection, user_id) return ( _is_shift_full(connection, day_of_week, preferred_shift, company_id) 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): for shift in SHIFTS: schedule = _assign_shift(connection, user_id, day_of_week, shift) if schedule is not None: return schedule return None def resolve_conflict_next_day(connection, user_id, current_day): next_day = _get_next_day(current_day) if next_day is None: return None 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): if ( _count_shift_employees(connection, day_of_week, shift, company_id) >= MAX_EMPLOYEES_PER_SHIFT ): return None 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