javaspringtimefold

How to use the sum aggregate function in TimeFold Constraints rule


The question is for the TimeFold employee scheduling sample customization. I have a Shift class:

    public class Shift {
    @PlanningId
    private String id;

    private LocalDateTime start;
    private LocalDateTime end;

    private String location;
    private String requiredSkill;

    @PlanningVariable(allowsUnassigned = true)
    private Employee employee;
    private HashMap<String, HardMediumSoftScore> scoreMap = new HashMap<String, HardMediumSoftScore>();
}

I have a Employee class:

public class Employee {
    @PlanningId
    private String name;

    private Set<String> skills;
    private Contract contracts;
}

I have to add a rule when the same employee is assigned multiple shifts on the same date. Then the shift duration in minutes has to be summed up. I have tried like follows:

    constraintFactory.forEach(Shift.class)
              .join(Shift.class)
              .filter((shift1, shift2) -> shift1.getEmployee() != null && shift2.getEmployee() != null)
              .filter((shift1, shift2) -> shift1.getEmployee() == shift2.getEmployee())
              .filter((shift1, shift2)-> shift1.getStartDate() == shift2.getStartDate())
              .groupBy((shift1, shift2)->sum(shift1.getDuration()))
              // The above line not working giving an error
              
              .penalize(HardMediumSoftScore.ONE_MEDIUM) // Here I have to put the calculated sum value.
              .indictWith((shift1, shift2)->List.of(new RuleShiftKey(shift1.getId(), "Rule1"))) 
// Here in the above line, I need the shift object to get the shift ID.
              .asConstraint("Daywise score filter");

Please help to rewrite the rule.


Solution

  • I would use the employee and date as keys in the groupby:

    constraintFactory.forEach(Shift.class) // For each shift
         // Group shifts that...
        .groupBy(Shift::getEmployee,  // ...has the same employee
                 Shift::getStartDate, // ...on the same day
                 // and accumulate their results into
                 //...the number of shifts the employee has on that day
                 ConstraintCollectors.count(),
                 //...the total duration worked by the employee on that day
                 ConstraintCollectors.sumDuration(Shift::getDuration))
        // Include only days where the employee worked more that one shift
        .filter((employee, date, count, totalDuration) -> count >= 2)
        // Penalize by the duration worked by the employee that day, in minutes
        .penalize(HardMediumSoftScore.ONE_MEDIUM,
                  (employee, date, count, totalDuration) -> (int) totalDuration.toMinutes())
        .indictWith((employee, date, count, totalDuration) -> List.of(new RuleShiftKey(date, "Rule1")))
        .asConstraint("Daywise score filter");