Skip to content

Rostering

Rostering is the final planning step: assign named resources (specific people) to days and shifts, respecting individual rules and preferences.

MinHoursRoster builds a resource-level roster that covers the shift requirements with the minimum scheduled hours, optionally rewarding shift preferences.

Inputs

ArgumentDescription
num_daysNumber of days to schedule
resourcesList of unique resource names
shiftsList of shift names
shifts_hoursDuration of each shift (same order as shifts)
min_working_hoursMinimum hours per resource over the horizon
max_restingMaximum resting days per resource (must be < num_days)
required_resources{shift_name: [per-day requirement, …]}
banned_shiftsShifts a resource cannot work on a given day
non_sequential_shiftsShift pairs that cannot follow one another
resources_preferencesPreferred shifts per resource
resources_prioritizationRelative weight of each resource's preferences

Validation

MinHoursRoster checks its inputs up front: resources must be unique, shifts and shifts_hours must have the same length, required_resources must contain every shift with one entry per day, and max_resting must be smaller than num_days. Each failure raises a clear ValueError.

Example

python
from pyworkforce.rostering import MinHoursRoster

roster = MinHoursRoster(
    num_days=2,
    resources=["e.johnston@co", "m.lee@co", "a.smith@co"],
    shifts=["Morning", "Night"],
    shifts_hours=[8, 8],
    min_working_hours=8,
    max_resting=1,
    required_resources={"Morning": [1, 1], "Night": [1, 1]},
    banned_shifts=[{"resource": "e.johnston@co", "shift": "Night", "day": 0}],
    non_sequential_shifts=[{"origin": "Night", "destination": "Morning"}],
    resources_preferences=[{"resource": "m.lee@co", "shift": "Morning"}],
    resources_prioritization=[{"resource": "m.lee@co", "weight": 2}],
)

solution = roster.solve()
print(solution["status"])

The solution

solve() returns a dictionary (also stored as solution_) describing both who works and who rests:

python
{
  "status": "OPTIMAL",
  "cost": ...,
  "shifted_hours": ...,
  "total_resources": 3,
  "total_shifts": ...,
  "resting_days": ...,
  "resource_shifts": [{"resource": "m.lee@co", "day": 0, "shift": "Morning"}, ...],
  "resting_resource": [{"resource": "a.smith@co", "day": 1}, ...],
}

Rules in detail

  • banned_shifts — each entry {"resource": ..., "shift": ..., "day": ...} forbids that exact assignment.
  • non_sequential_shifts — each entry {"origin": ..., "destination": ...} prevents destination from being worked the day after origin (e.g. no Morning right after a Night).
  • resources_preferences — each entry {"resource": ..., "shift": ...} marks a preferred shift; preferences reduce the effective cost of those assignments.
  • resources_prioritization — each entry {"resource": ..., "weight": ...} scales how strongly a resource's preferences are honored.

Released under the MIT License.