from datetime import datetime, timedelta
from collections import defaultdict
# 🧩 Module 1: Employee Setup
def get_employee_info():
while True:
try:
num_employees = int(input("Enter number of employees (2–50): "))
if 2 <= num_employees <= 50:
break
else:
print("Please enter a number between 2 and 50.")
except ValueError:
print("Invalid input. Please enter a number.")
employee_names = []
for i in range(num_employees):
name = input(f"Enter name for Employee {i+1}: ")
employee_names.append([Link]())
return employee_names
# 📅 Module 2: Date Range Selection
def get_date_range():
date_format = "%Y-%m-%d"
while True:
try:
start_date_str = input("Enter start date (YYYY-MM-DD): ")
end_date_str = input("Enter end date (YYYY-MM-DD): ")
start_date = [Link](start_date_str, date_format)
end_date = [Link](end_date_str, date_format)
if start_date > end_date:
print("Start date must be before end date.")
else:
break
except ValueError:
print("Invalid date format. Please use YYYY-MM-DD.")
total_days = (end_date - start_date).days + 1
return [start_date + timedelta(days=i) for i in range(total_days)]
# 🔄 Module 3: Shift Assignment with Holiday Logic + Carryover
def assign_variable_shifts(employee_names, date_list, employees_weekday,
employees_saturday, employees_holiday, raw_holidays):
schedule = {}
closed_dates = {"2025-12-25", "2025-01-01"}
# Expand public holidays with substitution logic
public_holidays = {}
for date_str, name in raw_holidays.items():
date_obj = [Link](date_str, "%Y-%m-%d")
public_holidays[date_obj] = name
if date_obj.weekday() == 6: # Sunday
substitute = date_obj + timedelta(days=1)
public_holidays[substitute] = f"{name} (Substitute)"
# Rotation setup
total_slots = len(date_list) * max(employees_weekday, employees_saturday,
employees_holiday)
rotation = (employee_names * ((total_slots // len(employee_names)) + 1))
[:total_slots]
r_index = 0
prev_day_employees = []
for date in date_list:
date_str = [Link]("%Y-%m-%d")
weekday = [Link]()
if date_str in closed_dates or weekday == 6: # Sunday or closed
prev_day_employees = []
continue
# Determine staffing count
if date in public_holidays:
count = employees_saturday
elif weekday == 5:
count = employees_saturday
else:
count = employees_weekday
# Assign one person from previous day if available
carry_over = prev_day_employees[:1] if prev_day_employees else []
remaining_count = count - len(carry_over)
assigned = carry_over + rotation[r_index : r_index + remaining_count]
r_index += remaining_count
prev_day_employees = assigned
# Save to schedule
schedule[date_str] = {
"employees": assigned,
"holiday": public_holidays.get(date, None)
}
return schedule
# 📋 Module 4a: Reference Sheet Output
def generate_reference_sheet(schedule):
print("\n Reference Sheet:\n")
print("Date | Assigned Employees | Holiday")
print("-" * 70)
for date, info in [Link]():
employee_list = ", ".join(info["employees"])
holiday_name = info["holiday"] if info["holiday"] else ""
print(f"{date} | {employee_list:<35} | {holiday_name}")
# 📆 Module 4b: Calendar View Output
def generate_calendar_view(schedule):
print("\n📆 Calendar View:\n")
week = []
for date_str in sorted([Link]()):
date_obj = [Link](date_str, "%Y-%m-%d")
day_name = date_obj.strftime("%a")
info = schedule[date_str]
employee_list = ", ".join(info["employees"])
holiday_note = f" 🎉 {info['holiday']}" if info["holiday"] else ""
entry = f"{day_name} {date_str}: {employee_list}{holiday_note}"
[Link](entry)
if len(week) == 7:
print("\n".join(week))
print("-" * 60)
week = []
if week:
print("\n".join(week))
print("-" * 60)
# 📊 Module 5: Employee Hours Audit
def audit_employee_hours(schedule):
hours = defaultdict(int)
for date_str, info in [Link]():
date_obj = [Link](date_str, "%Y-%m-%d")
weekday = date_obj.weekday()
# Determine shift length
if info["holiday"]:
shift_hours = 6
elif weekday == 5: # Saturday
shift_hours = 6
else:
shift_hours = 8
for name in info["employees"]:
hours[name] += shift_hours
print("\n📈 Employee Hours Summary:")
for name, total in sorted([Link](), key=lambda x: x[1], reverse=True):
print(f"{name}: {total} hours")
# 🚀 Main Execution
if __name__ == "__main__":
employees = get_employee_info()
dates = get_date_range()
# Staffing levels
while True:
try:
weekday_count = int(input("Employees per weekday: "))
saturday_count = int(input("Employees per Saturday: "))
holiday_count = int(input("Employees per public holiday (0 to skip):
"))
if all(0 <= x <= len(employees) for x in [weekday_count,
saturday_count, holiday_count]):
break
else:
print("Please enter valid numbers within your employee count.")
except ValueError:
print("Invalid input. Please enter numbers only.")
# 🎉 Define public holidays with names
raw_public_holidays = {
"2025-03-21": "Human Rights Day",
"2025-04-18": "Good Friday",
"2025-04-21": "Family Day",
"2025-04-27": "Freedom Day",
"2025-05-01": "Workers' Day",
"2025-06-16": "Youth Day",
"2025-08-09": "National Women's Day",
"2025-09-24": "Heritage Day",
"2025-12-16": "Day of Reconciliation",
"2025-12-26": "Day of Goodwill"
}
schedule = assign_variable_shifts(
employees,
dates,
weekday_count,
saturday_count,
holiday_count,
raw_public_holidays
)
generate_reference_sheet(schedule)
generate_calendar_view(schedule)
audit_employee_hours(schedule)