Skip to content

Commit

Permalink
Planner 1227 post publish constraints (#434)
Browse files Browse the repository at this point in the history
* Add publishedTimeslot and publishedRoom fields to class Talk

* Add test cases for publishedTimeslot and PublishedRoom

* Implement publishedTimeslot and publishedRoom constraints

* Implement room stability constraint

* Fix paste erros in drl, add publish button and generate examples files

* Fix scoreCorruption in Talk prereq talks rule
  • Loading branch information
MusaTalluzi authored and ge0ffrey committed Sep 24, 2018
1 parent e13111a commit 712e113
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 48 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Expand Up @@ -56,6 +56,9 @@ public class ConferenceParametrization extends AbstractPersistable {
public static final String SAME_DAY_TALKS = "Same day talks";
public static final String POPULAR_TALKS = "Popular talks";
public static final String CROWD_CONTROL = "Crowd control";
public static final String PUBLISHED_TIMESLOT = "Published timeslot";
public static final String PUBLISHED_ROOM = "Published room";
public static final String ROOM_STABILITY = "Room stability";

private int talkTypeOfTimeslot = 10000;
private int talkTypeOfRoom = 10000;
Expand All @@ -71,9 +74,11 @@ public class ConferenceParametrization extends AbstractPersistable {
private int speakerProhibitedRoomTags = 1;
private int talkRequiredRoomTags = 1;
private int talkProhibitedRoomTags = 1;
private int talkMutuallyExclusiveTalksTags = 1;
private int talkPrerequisiteTalks = 1;

private int talkMutuallyExclusiveTalksTags = 1;
private int publishedTimeslot = 10;

private int themeTrackConflict = 10;
private int sectorConflict = 10;
private int audienceTypeDiversity = 1;
Expand All @@ -93,6 +98,8 @@ public class ConferenceParametrization extends AbstractPersistable {
private int sameDayTalks = 10;
private int popularTalks = 10;
private int crowdControl = 10;
private int publishedRoom = 10;
private int roomStability = 10;

public ConferenceParametrization() {
}
Expand Down Expand Up @@ -217,6 +224,22 @@ public void setTalkProhibitedRoomTags(int talkProhibitedRoomTags) {
this.talkProhibitedRoomTags = talkProhibitedRoomTags;
}

public int getTalkMutuallyExclusiveTalksTags() {
return talkMutuallyExclusiveTalksTags;
}

public void setTalkMutuallyExclusiveTalksTags(int talkMutuallyExclusiveTalksTags) {
this.talkMutuallyExclusiveTalksTags = talkMutuallyExclusiveTalksTags;
}

public int getPublishedTimeslot() {
return publishedTimeslot;
}

public void setPublishedTimeslot(int publishedTimeslot) {
this.publishedTimeslot = publishedTimeslot;
}

public int getThemeTrackConflict() {
return themeTrackConflict;
}
Expand Down Expand Up @@ -345,14 +368,6 @@ public void setTalkUndesiredRoomTags(int talkUndesiredRoomTags) {
this.talkUndesiredRoomTags = talkUndesiredRoomTags;
}

public int getTalkMutuallyExclusiveTalksTags() {
return talkMutuallyExclusiveTalksTags;
}

public void setTalkMutuallyExclusiveTalksTags(int talkMutuallyExclusiveTalksTags) {
this.talkMutuallyExclusiveTalksTags = talkMutuallyExclusiveTalksTags;
}

public int getTalkPrerequisiteTalks() {
return talkPrerequisiteTalks;
}
Expand Down Expand Up @@ -384,4 +399,20 @@ public int getCrowdControl() {
public void setCrowdControl(int crowdControl) {
this.crowdControl = crowdControl;
}
}

public int getPublishedRoom() {
return publishedRoom;
}

public void setPublishedRoom(int publishedRoom) {
this.publishedRoom = publishedRoom;
}

public int getRoomStability() {
return roomStability;
}

public void setRoomStability(int roomStability) {
this.roomStability = roomStability;
}
}
Expand Up @@ -50,6 +50,8 @@ public class Talk extends AbstractPersistable {
private Set<Talk> prerequisiteTalkSet;
private int favoriteCount;
private int crowdControlRisk;
private Timeslot publishedTimeslot;
private Room publishedRoom;

@PlanningPin
private boolean pinnedByUser = false;
Expand Down Expand Up @@ -455,6 +457,22 @@ public void setCrowdControlRisk(int crowdControlRisk) {
this.crowdControlRisk = crowdControlRisk;
}

public Timeslot getPublishedTimeslot() {
return publishedTimeslot;
}

public void setPublishedTimeslot(Timeslot publishedTimeslot) {
this.publishedTimeslot = publishedTimeslot;
}

public Room getPublishedRoom() {
return publishedRoom;
}

public void setPublishedRoom(Room publishedRoom) {
this.publishedRoom = publishedRoom;
}

// ************************************************************************
// With methods
// ************************************************************************
Expand Down
Expand Up @@ -177,9 +177,15 @@ private void readConfiguration() {
"Soft penalty per 2 talks where the less popular one (has lower favorite count) is assigned a larger room than the more popular talk");
readIntConstraintLine(CROWD_CONTROL, parametrization::setCrowdControl,
"Soft penalty per talks with crowd control risk greater than zero that are not in pairs");
readIntConstraintLine(PUBLISHED_ROOM, parametrization::setPublishedRoom,
"Soft penalty per talk scheduled at a different room than its published one");
readIntConstraintLine(ROOM_STABILITY, parametrization::setRoomStability,
"Soft penalty per two talks with the same track scheduled in the same day but at different rooms");

readIntConstraintLine(TALK_MUTUALLY_EXCLUSIVE_TALKS_TAGS, parametrization::setTalkMutuallyExclusiveTalksTags,
"Medium penalty per two talks that share the same Mutually exclusive talks tag that are scheduled in overlapping timeslots");
readIntConstraintLine(PUBLISHED_TIMESLOT, parametrization::setPublishedTimeslot,
"Medium penalty per talk scheduled at a different timeslot than its published one");

readIntConstraintLine(TALK_TYPE_OF_TIMESLOT, parametrization::setTalkTypeOfTimeslot,
"Hard penalty per talk in a timeslot with another talk type");
Expand Down Expand Up @@ -463,6 +469,10 @@ private void readTalkList() {
readHeaderCell("Start");
readHeaderCell("End");
readHeaderCell("Room");
readHeaderCell("Published Timeslot");
readHeaderCell("Published Start");
readHeaderCell("Published End");
readHeaderCell("Published Room");
List<Talk> talkList = new ArrayList<>(currentSheet.getLastRowNum() - 1);
long id = 0L;
Map<Pair<LocalDateTime, LocalDateTime>, Timeslot> timeslotMap = solution.getTimeslotList().stream().collect(
Expand Down Expand Up @@ -564,48 +574,67 @@ private void readTalkList() {
talk.setFavoriteCount(getNextPositiveIntegerCell("talk with code (" + talk.getCode(), "a Favorite count"));
talk.setCrowdControlRisk(getNextPositiveIntegerCell("talk with code (" + talk.getCode(), "a crowd control risk"));
talk.setPinnedByUser(nextBooleanCell().getBooleanCellValue());
String dateString = nextStringCell().getStringCellValue();
String startTimeString = nextStringCell().getStringCellValue();
String endTimeString = nextStringCell().getStringCellValue();
if (!dateString.isEmpty() || !startTimeString.isEmpty() || !endTimeString.isEmpty()) {
LocalDateTime startDateTime;
LocalDateTime endDateTime;
try {
startDateTime = LocalDateTime.of(LocalDate.parse(dateString, DAY_FORMATTER),
LocalTime.parse(startTimeString, TIME_FORMATTER));
endDateTime = LocalDateTime.of(LocalDate.parse(dateString, DAY_FORMATTER),
LocalTime.parse(endTimeString, TIME_FORMATTER));
} catch (DateTimeParseException e) {
throw new IllegalStateException(currentPosition() + ": The talk with code (" + talk.getCode()
+ ") has a timeslot date (" + dateString
+ "), startTime (" + startTimeString + ") and endTime (" + endTimeString
+ ") that doesn't parse as a date or time.", e);
}
Timeslot timeslot = timeslotMap.get(Pair.of(startDateTime, endDateTime));
if (timeslot == null) {
throw new IllegalStateException(currentPosition() + ": The talk with code (" + talk.getCode()
+ ") has a timeslot date (" + dateString
+ "), startTime (" + startTimeString + ") and endTime (" + endTimeString
+ ") that doesn't exist in the other sheet (Timeslots).");
}
talk.setTimeslot(timeslot);
}
String roomName = nextStringCell().getStringCellValue();
if (!roomName.isEmpty()) {
Room room = roomMap.get(roomName);
if (room == null) {
throw new IllegalStateException(currentPosition() + ": The talk with code (" + talk.getCode()
+ ") has a roomName (" + roomName
+ ") that doesn't exist in the other sheet (Rooms).");
}
talk.setRoom(room);
}
talk.setTimeslot(extractTimeslot(timeslotMap, talk));
talk.setRoom(extractRoom(roomMap, talk));
talk.setPublishedTimeslot(extractTimeslot(timeslotMap, talk));
talk.setPublishedRoom(extractRoom(roomMap, talk));

talkList.add(talk);
}

setPrerequisiteTalkSets(talkToPrerequisiteTalkSetMap);
solution.setTalkList(talkList);
}

private Timeslot extractTimeslot(Map<Pair<LocalDateTime, LocalDateTime>, Timeslot> timeslotMap, Talk talk) {
Timeslot assignedTimeslot;
String dateString = nextStringCell().getStringCellValue();
String startTimeString = nextStringCell().getStringCellValue();
String endTimeString = nextStringCell().getStringCellValue();
if (!dateString.isEmpty() || !startTimeString.isEmpty() || !endTimeString.isEmpty()) {
LocalDateTime startDateTime;
LocalDateTime endDateTime;
try {
startDateTime = LocalDateTime.of(LocalDate.parse(dateString, DAY_FORMATTER),
LocalTime.parse(startTimeString, TIME_FORMATTER));
endDateTime = LocalDateTime.of(LocalDate.parse(dateString, DAY_FORMATTER),
LocalTime.parse(endTimeString, TIME_FORMATTER));
} catch (DateTimeParseException e) {
throw new IllegalStateException(currentPosition() + ": The talk with code (" + talk.getCode()
+ ") has a timeslot date (" + dateString
+ "), startTime (" + startTimeString + ") and endTime (" + endTimeString
+ ") that doesn't parse as a date or time.", e);
}

assignedTimeslot = timeslotMap.get(Pair.of(startDateTime, endDateTime));
if (assignedTimeslot == null) {
throw new IllegalStateException(currentPosition() + ": The talk with code (" + talk.getCode()
+ ") has a timeslot date (" + dateString
+ "), startTime (" + startTimeString + ") and endTime (" + endTimeString
+ ") that doesn't exist in the other sheet (Timeslots).");
}

return assignedTimeslot;
}

return null;
}

private Room extractRoom(Map<String, Room> roomMap, Talk talk) {
String roomName = nextStringCell().getStringCellValue();
if (!roomName.isEmpty()) {
Room room = roomMap.get(roomName);
if (room == null) {
throw new IllegalStateException(currentPosition() + ": The talk with code (" + talk.getCode()
+ ") has a roomName (" + roomName
+ ") that doesn't exist in the other sheet (Rooms).");
}
return room;
}

return null;
}

private int getNextStrictlyPositiveIntegerCell(String classSpecifier, String columnName) {
double cellValueDouble = nextNumericCell().getNumericCellValue();
if (strict && (cellValueDouble <= 0 || cellValueDouble != Math.floor(cellValueDouble))) {
Expand Down Expand Up @@ -784,10 +813,16 @@ private void writeConfiguration() {
"Soft penalty per 2 talks where the less popular one (has lower favorite count) is assigned a larger room than the more popular talk");
writeIntConstraintLine(CROWD_CONTROL, parametrization::getCrowdControl,
"Soft penalty per talks with crowd control risk greater than zero that are not in pairs");
writeIntConstraintLine(PUBLISHED_ROOM, parametrization::getPublishedRoom,
"Soft penalty per talk scheduled at a different room than its published one");
writeIntConstraintLine(ROOM_STABILITY, parametrization::getRoomStability,
"Soft penalty per two talks with the same track scheduled in the same day but at different rooms");

nextRow();
writeIntConstraintLine(TALK_MUTUALLY_EXCLUSIVE_TALKS_TAGS, parametrization::getTalkMutuallyExclusiveTalksTags,
"Medium penalty per two talks that share the same Mutually exclusive talks tag that are scheduled in overlapping timeslots");
writeIntConstraintLine(PUBLISHED_TIMESLOT, parametrization::getPublishedTimeslot,
"Medium penalty per talk scheduled at a different timeslot than its published one");

nextRow();
writeIntConstraintLine(TALK_TYPE_OF_TIMESLOT, parametrization::getTalkTypeOfTimeslot,
Expand Down Expand Up @@ -943,6 +978,11 @@ private void writeTalkList() {
nextHeaderCell("Start");
nextHeaderCell("End");
nextHeaderCell("Room");
nextHeaderCell("Published Timeslot");
nextHeaderCell("Published Start");
nextHeaderCell("Published End");
nextHeaderCell("Published Room");

for (Talk talk : solution.getTalkList()) {
nextRow();
nextCell().setCellValue(talk.getCode());
Expand Down Expand Up @@ -973,6 +1013,10 @@ private void writeTalkList() {
nextCell().setCellValue(talk.getTimeslot() == null ? "" : TIME_FORMATTER.format(talk.getTimeslot().getStartDateTime()));
nextCell().setCellValue(talk.getTimeslot() == null ? "" : TIME_FORMATTER.format(talk.getTimeslot().getEndDateTime()));
nextCell().setCellValue(talk.getRoom() == null ? "" : talk.getRoom().getName());
nextCell().setCellValue(talk.getPublishedTimeslot() == null ? "" : DAY_FORMATTER.format(talk.getPublishedTimeslot().getDate()));
nextCell().setCellValue(talk.getPublishedTimeslot() == null ? "" : TIME_FORMATTER.format(talk.getPublishedTimeslot().getStartDateTime()));
nextCell().setCellValue(talk.getPublishedTimeslot() == null ? "" : TIME_FORMATTER.format(talk.getPublishedTimeslot().getEndDateTime()));
nextCell().setCellValue(talk.getPublishedRoom() == null ? "" : talk.getPublishedRoom().getName());
}
autoSizeColumnsWithHeader();
}
Expand Down
Expand Up @@ -40,6 +40,14 @@ public ConferenceSchedulingPanel() {
solutionBusiness.setSolution(conferenceSchedulingImporter.importSolution());
});

JButton publishButton = new JButton("Publish");
publishButton.addActionListener(actionEvent -> {
solutionBusiness.getSolution().getTalkList().stream().forEach(talk -> {
talk.setPublishedTimeslot(talk.getTimeslot());
talk.setPublishedRoom(talk.getRoom());
});
});

JButton button = new JButton("Show in LibreOffice or Excel");
button.addActionListener(event -> {
SolutionFileIO<ConferenceSolution> solutionFileIO = new ConferenceSchedulingXlsxFileIO();
Expand All @@ -57,6 +65,7 @@ public ConferenceSchedulingPanel() {
}
});
add(importConferenceButton);
add(publishButton);
add(button);
add(new JLabel("Changes to that file are ignored unless you explicitly save it there and open it here."));
}
Expand Down
Expand Up @@ -156,9 +156,14 @@ end
rule "Talk prerequisite talks"
when
ConferenceParametrization($weight : talkPrerequisiteTalks != 0)
$talk : Talk(timeslot != null, missingPrerequisiteCount() > 0)
$talk : Talk(timeslot != null, getPrerequisiteTalkSet().size() > 0)
$count : Number(this > 0) from accumulate(
$reqTalk : Talk($talk.getPrerequisiteTalkSet().contains(this), timeslot == null ||
!timeslot.endsBefore($talk.getTimeslot())),
count($reqTalk)
)
then
scoreHolder.addHardConstraintMatch(kcontext, - $weight * $talk.missingPrerequisiteCount());
scoreHolder.addHardConstraintMatch(kcontext, - $weight * $count.intValue());
end

// ############################################################################
Expand All @@ -181,6 +186,14 @@ rule "Talk mutually-exclusive-talks tags"
}
end

rule "Published timeslot"
when
ConferenceParametrization($weight : publishedTimeslot != 0)
Talk(publishedTimeslot != null, timeslot != publishedTimeslot)
then
scoreHolder.addMediumConstraintMatch(kcontext, - $weight);
end

// ############################################################################
// Soft constraints
// ############################################################################
Expand Down Expand Up @@ -387,4 +400,21 @@ rule "Crowd control"
)
then
scoreHolder.addSoftConstraintMatch(kcontext, - $weight);
end

rule "Published room"
when
ConferenceParametrization($weight : publishedRoom != 0)
Talk(publishedRoom != null, room != publishedRoom)
then
scoreHolder.addSoftConstraintMatch(kcontext, - $weight);
end

rule "Room stability"
when
ConferenceParametrization($weight : roomStability != 0)
$talk : Talk(timeslot != null, $timeslot : timeslot, $room : room, $id : id)
Talk(timeslot != null, overlappingThemeTrackCount($talk) > 0, $id < id, timeslot.isOnSameDayAs($timeslot), $room != room)
then
scoreHolder.addSoftConstraintMatch(kcontext, - $weight);
end
Binary file not shown.

0 comments on commit 712e113

Please sign in to comment.