swagtha01
swagtha01

Reputation: 13

OptaPlanner Custom Employee Rostering - Issues with WeeklyHoursLimit in the Constraint Provider

I have been following the employee rostering example to create a custom employee scheduling service. I am trying to implement the weekly hours limit. However, I have not been able to achieve the desired constraint of a maximum of 40 hours worked per week per Employee. I've tried to replicate the OptaPlanner Employee Rostering Solution. This is what I've come up with, but the solution returns no matches no matter what input I try.

 Constraint weeklyHoursUpperLimit(ConstraintFactory constraintFactory) {
    return constraintFactory.from(Employee.class)
            .join(Schedule.class, equal(Function.identity(), Schedule::getEmployee))
            .groupBy((employee, schedule) -> employee,
                    ((employee, schedule) -> ZonedDateTime.parse(schedule.getTimeslot().getStart()).toLocalDate()
                            .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY))),
                    sumDuration((employee, schedule) -> Duration.between(ZonedDateTime.parse(schedule.getTimeslot().getStart()),
                            ZonedDateTime.parse(schedule.getTimeslot().getEnd()))))
            .filter((practitioner, day, time) -> time.toHoursPart() > 40)
            .penalize("Practitioners cannot work more than 40 hours per week", HardMediumSoftScore.ONE_HARD);

The solution, a list of type Schedule containing an Employee and the corresponding timeslot, does not seem to ever match the desired behavior.

Using the following input:

{
    "employees": [
        {
            "id": "0458d419-da18-4d86-82de-31a2272f018a"
        }
    ],
    "timeslots": [
        {
            "start": "2021-05-24T00:00:00-04:00",
            "end": "2021-05-24T12:00:00-04:00",
            "slotId": 1
        },
        {
            "start": "2021-05-24T12:00:00-04:00",
            "end": "2021-05-25T00:00:00-04:00",
            "slotId": 7
        },
        {
            "start": "2021-05-25T12:00:00-04:00",
            "end": "2021-05-26T00:00:00-04:00",
            "slotId": 4
        },
        {
            "start": "2021-05-27T12:00:00-04:00",
            "end": "2021-05-28T00:00:00-04:00",
            "slotId": 5
        }
    ]
}

The ScoreExplanation says this: constraint (Practitioners cannot work more than 40 hours per week) has 0 matches.

What it should say: constraint (Practitioners cannot work more than 40 hours per week) has 1 matches.

With this input, the only possible solution must match this constraint. Other constraints are detected and penalized, so I am wondering why this one never returns any matches.

Upvotes: 1

Views: 307

Answers (1)

Radovan Synek
Radovan Synek

Reputation: 1029

I suspect the problem is in the filter: .filter((practitioner, day, time) -> time.toHoursPart() > 40)

Duration#toHoursPart() returns a remainder after dividing total hours by hours in a day, which is between 0 and 23. The filter should probably use Duration#toHours() instead.

Upvotes: 1

Related Questions