ts = pd.read_csv("timesheets.csv", parse_dates=["date"])
# columns: employee_id, date, hours, project
# No more than 16 hours in a day
bad = ts.groupby(["employee_id","date"])["hours"].sum()
bad = bad[bad > 16]
if len(bad):
print(f"⚠️ {len(bad)} employee-days over 16 hours")
print(bad)
ts["pay_period"] = ts["date"].dt.to_period("2W-SUN") # bi-weekly ending Sunday
daily = ts.groupby(["employee_id","date"])["hours"].sum().reset_index()
# Apply 40h/week overtime rule
daily["week"] = daily["date"].dt.to_period("W-SUN")
weekly = (daily.groupby(["employee_id","week"])["hours"]
.sum()
.reset_index())
weekly["regular_hrs"] = weekly["hours"].clip(upper=40)
weekly["overtime_hrs"] = (weekly["hours"] - 40).clip(lower=0)
weekly["pay_period"] = weekly["week"].apply(lambda w: pd.Period(w.start_time, freq="2W-SUN"))
payroll = (weekly.groupby(["employee_id","pay_period"])
.agg(regular = ("regular_hrs", "sum"),
overtime= ("overtime_hrs", "sum"))
.reset_index())
payroll["pay_period"] = payroll["pay_period"].astype(str)
payroll.to_csv("payroll_import.csv", index=False)
print(f"Wrote payroll for {payroll['employee_id'].nunique()} employees")
Merge the payroll output with an employees table containing hourly rates. Compute total pay (regular×rate + overtime×rate×1.5).