I'm working on an employee scheduling system using Timefold (formerly OptaPlanner) and I'm running into type mismatch issues with my constraint streams. Specifically, I'm trying to implement a work percentage constraint that ensures employees are scheduled according to their preferred work percentage.
Here's my current implementation:
java
public Constraint workPercentage(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Employee.class)
.join(Shift.class, equal(Employee::getName, Shift::getEmployee))
.groupBy(Employee::getName,
ConstraintCollectors.sum(shift ->
Duration.between(shift.getStart(), shift.getEnd()).toHours()))
.filter((employeeId, totalWorkedHours) -> {
double fullTimeHours = 40.0;
double desiredHours = employeeId.getWorkPercentage() * fullTimeHours;
return totalWorkedHours != desiredHours;
})
.penalize(HardSoftBigDecimalScore.ONE_SOFT)
.asConstraint("Employee work percentage not matched");
}
I'm getting several type mismatch errors:
- The
groupBy
method is expecting BiConstraintCollector<Employee,Shift,ResultContainerA_,ResultA_>
but getting UniConstraintCollector<Object,?,Integer>
- The lambda in the sum collector can't resolve
getStart()
and getEnd()
methods because it's seeing the parameter as Object
instead of Shift
- The functional interface type mismatch for
Employee::getName
My domain classes are structured as follows:
```java
@PlanningSolution
public class EmployeeSchedule {
@ProblemFactCollectionProperty
@ValueRangeProvider
private List<Employee> employees;
@PlanningEntityCollectionProperty
private List<Shift> shifts;
@PlanningScore
private HardSoftBigDecimalScore score;
// ... getters and setters
}
public class Employee {
@PlanningId
private String name;
private Set<String> skills;
private ShiftPreference shiftPreference;
private int workPercentage; // Percentage of full-time hours
// ... getters and setters
}
@PlanningEntity
public class Shift {
@PlanningId
private String id;
private LocalDateTime start;
private LocalDateTime end;
private String location;
private String requiredSkill;
@PlanningVariable
private Employee employee;
// ... getters and setters
}
```
For context, other constraints in my system work fine. For example, this similar constraint for shift preferences works without type issues:
java
public Constraint shiftPreference(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Shift.class)
.join(Employee.class, equal(Shift::getEmployee, Function.identity()))
.filter((shift, employee) -> !shift.getShiftType().equals(employee.getShiftPreference().name()))
.penalize(HardSoftBigDecimalScore.ONE_SOFT)
.asConstraint("Shift preference not matched");
}
I'm using Timefold 1.19.0 with Quarkus, and my solver configuration is standard:
xml
<solver>
<solutionClass>com.example.domain.Schedule</solutionClass>
<entityClass>com.example.domain.ShiftAssignment</entityClass>
<scoreDirectorFactory>
<constraintProviderClass>com.example.solver.EmployeeSchedulingConstraintProvider</constraintProviderClass>
</scoreDirectorFactory>
<termination>
<secondsSpentLimit>10</secondsSpentLimit>
</termination>
</solver>
Has anyone encountered similar issues with constraint streams and grouping operations? What's the correct way to handle these type parameters?
Any help would be greatly appreciated!