|
@@ -13,7 +13,8 @@ import org.optaplanner.core.api.score.stream.uni.UniConstraintStream;
|
|
|
|
|
|
|
|
public class PlanningConstraintProvider implements ConstraintProvider {
|
|
public class PlanningConstraintProvider implements ConstraintProvider {
|
|
|
|
|
|
|
|
- private static UniConstraintStream<Assignement> getAssignedSlotStream(ConstraintFactory constraintFactory) {
|
|
|
|
|
|
|
+ private static UniConstraintStream<Assignement> getAssignedSlotStream(
|
|
|
|
|
+ ConstraintFactory constraintFactory) {
|
|
|
return constraintFactory.fromUnfiltered(Assignement.class) // To match DRL
|
|
return constraintFactory.fromUnfiltered(Assignement.class) // To match DRL
|
|
|
.filter(shift -> shift.getVolonteer() != null);
|
|
.filter(shift -> shift.getVolonteer() != null);
|
|
|
}
|
|
}
|
|
@@ -28,16 +29,18 @@ public class PlanningConstraintProvider implements ConstraintProvider {
|
|
|
// Soft constraints are only implemented in the "complete"
|
|
// Soft constraints are only implemented in the "complete"
|
|
|
// implementation
|
|
// implementation
|
|
|
volunteerMinRestTime(constraintFactory), competencyTeachingEffort(constraintFactory),
|
|
volunteerMinRestTime(constraintFactory), competencyTeachingEffort(constraintFactory),
|
|
|
- preferenceApplication(constraintFactory), balanceLoad(constraintFactory) };
|
|
|
|
|
|
|
+ preferenceApplication(constraintFactory), balanceLoad(constraintFactory)};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Constraint completeAllTimeslot(ConstraintFactory constraintFactory) {
|
|
private Constraint completeAllTimeslot(ConstraintFactory constraintFactory) {
|
|
|
- return constraintFactory.from(Assignement.class).filter((assignment) -> assignment.getVolonteer() == null)
|
|
|
|
|
|
|
+ return constraintFactory.from(Assignement.class)
|
|
|
|
|
+ .filter((assignment) -> assignment.getVolonteer() == null)
|
|
|
.penalize("Timeslot not initialized", HardMediumSoftScore.ONE_HARD);
|
|
.penalize("Timeslot not initialized", HardMediumSoftScore.ONE_HARD);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Constraint feedAllVolunteer(ConstraintFactory constraintFactory) {
|
|
private Constraint feedAllVolunteer(ConstraintFactory constraintFactory) {
|
|
|
- return constraintFactory.from(MealAssignement.class).filter((assignment) -> assignment.getMealSlot() == null)
|
|
|
|
|
|
|
+ return constraintFactory.from(MealAssignement.class)
|
|
|
|
|
+ .filter((assignment) -> assignment.getMealSlot() == null)
|
|
|
.penalize("Meal not initialized", HardMediumSoftScore.ONE_HARD);
|
|
.penalize("Meal not initialized", HardMediumSoftScore.ONE_HARD);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -94,22 +97,22 @@ public class PlanningConstraintProvider implements ConstraintProvider {
|
|
|
|
|
|
|
|
private Constraint competencyConflict(ConstraintFactory constraintFactory) {
|
|
private Constraint competencyConflict(ConstraintFactory constraintFactory) {
|
|
|
// a volonteer must match required competencies.
|
|
// a volonteer must match required competencies.
|
|
|
- return getAssignedSlotStream(constraintFactory).penalize("Competence conflict", HardMediumSoftScore.ONE_HARD,
|
|
|
|
|
- Assignement::getCompetenceLackScore);
|
|
|
|
|
|
|
+ return getAssignedSlotStream(constraintFactory).penalize("Competence conflict",
|
|
|
|
|
+ HardMediumSoftScore.ONE_HARD, Assignement::getCompetenceLackScore);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Constraint competencyTeachingEffort(ConstraintFactory constraintFactory) {
|
|
private Constraint competencyTeachingEffort(ConstraintFactory constraintFactory) {
|
|
|
// We favor volonteer that already have required competencies.
|
|
// We favor volonteer that already have required competencies.
|
|
|
- // TODO: Idealy you should group by per volonteer and computed missing teachable
|
|
|
|
|
- // competencies
|
|
|
|
|
- return getAssignedSlotStream(constraintFactory).penalize("Avoid teaching competences to volunteer",
|
|
|
|
|
- HardMediumSoftScore.ONE_SOFT, Assignement::getSoftCompetenceLackScore);
|
|
|
|
|
|
|
+ // TODO: Idealy you should group by per volonteer and computed missing teachable competencies
|
|
|
|
|
+ return getAssignedSlotStream(constraintFactory).penalize(
|
|
|
|
|
+ "Avoid teaching competences to volunteer", HardMediumSoftScore.ONE_SOFT,
|
|
|
|
|
+ Assignement::getSoftCompetenceLackScore);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Constraint preferenceApplication(ConstraintFactory constraintFactory) {
|
|
private Constraint preferenceApplication(ConstraintFactory constraintFactory) {
|
|
|
// The system should favor timeslots selected by user.
|
|
// The system should favor timeslots selected by user.
|
|
|
- return getAssignedSlotStream(constraintFactory).reward("Preference application", HardMediumSoftScore.ONE_SOFT,
|
|
|
|
|
- Assignement::getPreferenceScore);
|
|
|
|
|
|
|
+ return getAssignedSlotStream(constraintFactory).reward("Preference application",
|
|
|
|
|
+ HardMediumSoftScore.ONE_SOFT, Assignement::getPreferenceScore);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Constraint balanceLoad(ConstraintFactory constraintFactory) {
|
|
private Constraint balanceLoad(ConstraintFactory constraintFactory) {
|
|
@@ -125,11 +128,13 @@ public class PlanningConstraintProvider implements ConstraintProvider {
|
|
|
|
|
|
|
|
private Constraint mealMaxAttendee(ConstraintFactory constraintFactory) {
|
|
private Constraint mealMaxAttendee(ConstraintFactory constraintFactory) {
|
|
|
// Each Meal slot can only accomodate a maximum of attendee
|
|
// Each Meal slot can only accomodate a maximum of attendee
|
|
|
- return constraintFactory.from(MealAssignement.class).filter(assignement -> assignement.getMealSlot() != null)
|
|
|
|
|
|
|
+ return constraintFactory.from(MealAssignement.class)
|
|
|
|
|
+ .filter(assignement -> assignement.getMealSlot() != null)
|
|
|
.groupBy(MealAssignement::getMealSlot, ConstraintCollectors.count())
|
|
.groupBy(MealAssignement::getMealSlot, ConstraintCollectors.count())
|
|
|
.filter((slot, volonteerCount) -> slot.getMaxAttendee() < volonteerCount)
|
|
.filter((slot, volonteerCount) -> slot.getMaxAttendee() < volonteerCount)
|
|
|
// then penalize each pair with a hard weight.
|
|
// then penalize each pair with a hard weight.
|
|
|
- .penalize("Meal max attendee", HardMediumSoftScore.ONE_HARD, (slot,
|
|
|
|
|
- volonteerCount) -> (slot.getMaxAttendee() - volonteerCount) * (slot.getMaxAttendee() - volonteerCount));
|
|
|
|
|
|
|
+ .penalize("Meal max attendee", HardMediumSoftScore.ONE_HARD,
|
|
|
|
|
+ (slot, volonteerCount) -> (slot.getMaxAttendee() - volonteerCount)
|
|
|
|
|
+ * (slot.getMaxAttendee() - volonteerCount));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|