I started using OptaPy last week, I've read through the documentation and watched a few videos. I have a rough understanding of how things work in the big picture - but I would appreciate any input and tips for optimization.
I am trying to generate a schedule similar to "employee-scheduling example" on github a few different conditions. So I used this project as a baseline.
The problem is the following:
I have two locations, each with their own min and max shifts.
Location 1 - min employees per shift = 3, max employees per shift = 4
Location 2 - min employees per shift = 2, max employees per shift = 4
Each location has their own timetable of shifts.
Location 1 - Monday to Friday (08:00 to 16:00) - 5 x 2 hour shifts a day.
Location 2 - Monday to Friday (08:00 to 18:00) - 6 x 2 hour shifts a day. Saturday (08:00 to 12:00) - 2 x 2 hour shifts.
Now here is where things get interesing.
So obviously each shift should have the minimum required employees assigned to it, but it would be nice to fill the optional employees too.
Initially I thought of defining the Shift Class with a list of employees. But at the time I could not find an example of this, I only found an example of 1 employee per shift. So to accomodate this, I generate from the database for each timeslot, 4 shifts and set a employee_required boolean.
Eg. Location 1 - Monday 08:00 to 10:00 - (since location has max employees of 4 and min employees of 3) I create 4 shifts for this timeslot, 3 of them have required set to true and 1 of them required is set to false.
Currently, I am not using this information (since I don't know how to focus OptaPy on the required shifts first and the optional shifts later to get the best result/score).
As of now I have only 2 constraints.
- A employee must be available to do that shift. How I calculate this, the employee has an array of timeslot-hashes that he/she can do.
employee.timeslots_unique_hashes = ["Monday-08:00-10:00", "Thursday-12:00-14:00"]
And then I have a unique hash for the shift - "Monday-08:00-10:00". So if the shift hash is not in the employees list of hashes. I penalise based on the shift length. Below is my constraint
def required_timeslot(constraint_factory: ConstraintFactory):
return constraint_factory \
.for_each(Shift) \
.filter(lambda shift: shift.unique_hash not in shift.employee.timeslots_unique_hashes) \
.penalize("Employee missing timeslot", HardSoftScore.ONE_HARD, lambda shift: get_shift_duration_in_minutes(shift))
- A employee can only have 1 shift per day across all locations.
def one_shift_per_day(constraint_factory: ConstraintFactory):
return constraint_factory \
.for_each_unique_pair(Shift,
Joiners.equal(lambda shift: shift.employee),
Joiners.equal(lambda shift: shift.day_name)
) \
.penalize("Max one shift per day", HardSoftScore.ONE_HARD)
So where I need advice or help is in the following:
- (Optional) Implement the required boolean on a shift - focus on required shifts to be filled with less priority on the optional to improve score.
- (Required) Constraint - A employee has a array of other employee ids that are required. So there might be a husband and wife that are required to be together. Or mother and daughter. I tried writing the constraint but need help completing it.
The logic here would be to group the shifts by their unique hash, cycle through the employees and build an array of required employees for that shift. If the array employees assigned to that shift does not match the array of required employees, penalise based on the amount of employees missing.
def required_employees(constraint_factory: ConstraintFactory):
return constraint_factory \
.for_each(Shift) \
.groupBy(lambda shift: shift.unique_hash, to_list(lambda shift: shift.employee)) \
.filter(lambda shift, employees: shift_does_not_contains_all_required_employees(shift)) \
.penalize("Shift does not contain all the required employees", HardSoftScore.ONE_HARD, lambda shift: get_missing_required_employees_count(shift))
- (Required but not sure possible) Constraint - ensure that all the employees get atleast one shift. Obviously some employees have the possibility to get 1 shift per day, but I want to prioritise that everyone get atleast one shift before giving someone that already has a shift another one.
Any help/advice/suggestions would be highly appreciated. Thank you