diff --git a/JavaSource/org/unitime/timetable/gwt/client/sectioning/ChangeGradeModesDialog.java b/JavaSource/org/unitime/timetable/gwt/client/sectioning/ChangeGradeModesDialog.java index 55a8992674..3f29b4c4b3 100644 --- a/JavaSource/org/unitime/timetable/gwt/client/sectioning/ChangeGradeModesDialog.java +++ b/JavaSource/org/unitime/timetable/gwt/client/sectioning/ChangeGradeModesDialog.java @@ -369,7 +369,7 @@ public void onSuccess(RetrieveAvailableGradeModesResponse result) { new WebTable.Cell(clazz.getStartString(CONSTANTS.useAmPm())).aria(clazz.getStartStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getEndString(CONSTANTS.useAmPm())).aria(clazz.getEndStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), (vcc != null ? vcc : new WebTable.AbbvTextCell(clazz.getCredit())), new WebTable.Cell(pendingCredit == null ? "" : MESSAGES.credit(pendingCredit)), (change == null ? new WebTable.Cell("") : firstClazz ? change : new GradeModeLabel(change, gradeMode)), @@ -383,7 +383,7 @@ public void onSuccess(RetrieveAvailableGradeModesResponse result) { new WebTable.Cell(clazz.getSection()), new WebTable.Cell(MESSAGES.arrangeHours(), 3, null), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), (vcc != null ? vcc : new WebTable.AbbvTextCell(clazz.getCredit())), new WebTable.Cell(pendingCredit == null ? "" : MESSAGES.credit(pendingCredit)), (change == null ? new WebTable.Cell("") : firstClazz ? change : new GradeModeLabel(change, gradeMode)), diff --git a/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSchedule.java b/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSchedule.java index 201f14dd52..0ccdef8667 100644 --- a/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSchedule.java +++ b/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSchedule.java @@ -1063,7 +1063,7 @@ protected void fillInAssignments() { new WebTable.Cell(clazz.getStartString(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getEndString(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection()), clazz.hasNote() ? new WebTable.IconCell(RESOURCES.note(), clazz.getNote(), "") : new WebTable.Cell(""), diff --git a/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentScheduleTable.java b/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentScheduleTable.java index 86b674ccac..bf4e675822 100644 --- a/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentScheduleTable.java +++ b/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentScheduleTable.java @@ -237,7 +237,7 @@ public void populate(Collection data) { new WebTable.Cell(clazz.getLimitString()), new WebTable.Cell(clazz.getDaysString(CONSTANTS.shortDays()) + " " + clazz.getStartString(CONSTANTS.useAmPm()) + " - " + clazz.getEndString(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection()), clazz.hasNote() ? new WebTable.IconCell(RESOURCES.note(), clazz.getNote(), "") : new WebTable.Cell(""), diff --git a/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSectioningWidget.java b/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSectioningWidget.java index a2e2d57929..6aa728793b 100644 --- a/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSectioningWidget.java +++ b/JavaSource/org/unitime/timetable/gwt/client/sectioning/StudentSectioningWidget.java @@ -1765,7 +1765,7 @@ else if (clazz.hasWarn()) new WebTable.Cell(clazz.getStartString(CONSTANTS.useAmPm())).aria(clazz.getStartStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getEndString(CONSTANTS.useAmPm())).aria(clazz.getEndStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection(), clazz.getParentSection() == null || clazz.getParentSection().length() > 10), new WebTable.NoteCell(clazz.getOverlapAndNote("text-red"), clazz.getOverlapAndNote(null)), @@ -1782,7 +1782,7 @@ else if (clazz.hasWarn()) new WebTable.Cell(clazz.getLimitString()), new WebTable.Cell(MESSAGES.arrangeHours(), 3, null), new WebTable.Cell(clazz.hasDatePattern() ? clazz.getDatePattern() : ""), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection(), clazz.getParentSection() == null || clazz.getParentSection().length() > 10), new WebTable.NoteCell(clazz.getOverlapAndNote("text-red"), clazz.getOverlapAndNote(null)), @@ -2195,7 +2195,7 @@ public void onSuccess(ClassAssignmentInterface result) { new WebTable.Cell(clazz.getStartString(CONSTANTS.useAmPm())).aria(clazz.getStartStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getEndString(CONSTANTS.useAmPm())).aria(clazz.getEndStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection(), clazz.getParentSection() == null || clazz.getParentSection().length() > 10), new WebTable.NoteCell(clazz.getOverlapAndNote("text-red"), clazz.getOverlapAndNote(null)), @@ -2212,7 +2212,7 @@ public void onSuccess(ClassAssignmentInterface result) { new WebTable.Cell(clazz.getLimitString()), new WebTable.Cell(MESSAGES.arrangeHours(), 3, null), new WebTable.Cell(clazz.hasDatePattern() ? clazz.getDatePattern() : ""), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection(), clazz.getParentSection() == null || clazz.getParentSection().length() > 10), new WebTable.NoteCell(clazz.getOverlapAndNote("text-red"), clazz.getOverlapAndNote(null)), @@ -2295,7 +2295,7 @@ public void onSuccess(ClassAssignmentInterface result) { new WebTable.Cell(clazz.getStartString(CONSTANTS.useAmPm())).aria(clazz.getStartStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getEndString(CONSTANTS.useAmPm())).aria(clazz.getEndStringAria(CONSTANTS.useAmPm())), new WebTable.Cell(clazz.getDatePattern()), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection(), clazz.getParentSection() == null || clazz.getParentSection().length() > 10), new WebTable.NoteCell(clazz.getOverlapAndNote("text-red"), clazz.getOverlapAndNote(null)), @@ -2312,7 +2312,7 @@ public void onSuccess(ClassAssignmentInterface result) { new WebTable.Cell(clazz.getLimitString()), new WebTable.Cell(MESSAGES.arrangeHours(), 3, null), new WebTable.Cell(clazz.hasDatePattern() ? clazz.getDatePattern() : ""), - (clazz.hasDistanceConflict() ? new WebTable.RoomCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), + (clazz.hasDistanceConflict() ? new WebTable.RoomCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), clazz.getRooms(), ", ") : new WebTable.RoomCell(clazz.getRooms(), ", ")), new WebTable.InstructorCell(clazz.getInstructors(), clazz.getInstructorEmails(), ", "), new WebTable.Cell(clazz.getParentSection(), clazz.getParentSection() == null || clazz.getParentSection().length() > 10), new WebTable.NoteCell(clazz.getOverlapAndNote("text-red"), clazz.getOverlapAndNote(null)), diff --git a/JavaSource/org/unitime/timetable/gwt/client/sectioning/SuggestionsBox.java b/JavaSource/org/unitime/timetable/gwt/client/sectioning/SuggestionsBox.java index 6986b56fd5..00d0c03e65 100644 --- a/JavaSource/org/unitime/timetable/gwt/client/sectioning/SuggestionsBox.java +++ b/JavaSource/org/unitime/timetable/gwt/client/sectioning/SuggestionsBox.java @@ -463,7 +463,7 @@ public void showResults(Collection result) { new WebTable.Cell(compare(dates(old), dates(clazz), CmpMode.BOTH_OLD, selected, clazz == null)), new WebTable.Cell(compare(dates(old), dates(clazz), CmpMode.BOTH_NEW, selected, clazz == null)), (clazz != null && clazz.hasDistanceConflict() ? - new WebTable.IconCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), + new WebTable.IconCell(clazz.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(clazz.getBackToBackRooms(), clazz.getBackToBackDistance()), compare(room(old), room(clazz), CmpMode.BOTH_OLD, selected, clazz == null)) : new WebTable.Cell(compare(room(old), room(clazz), CmpMode.BOTH_OLD, selected, clazz == null))), new WebTable.Cell(compare(room(old), room(clazz), CmpMode.BOTH_NEW, selected, clazz == null)), @@ -511,7 +511,7 @@ public void showResults(Collection result) { new WebTable.Cell(compare(dates(old), dates(clazz), CmpMode.BOTH_OLD, false, clazz == null)), new WebTable.Cell(compare(dates(old), dates(clazz), CmpMode.BOTH_NEW, false, clazz == null)), (old != null && old.hasDistanceConflict() ? - new WebTable.IconCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(old.getBackToBackRooms(), old.getBackToBackDistance()), + new WebTable.IconCell(old.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(old.getBackToBackRooms(), old.getBackToBackDistance()), compare(room(old), room(clazz), CmpMode.BOTH_OLD, false, clazz == null)) : new WebTable.Cell(compare(room(old), room(clazz), CmpMode.BOTH_OLD, false, clazz == null))), new WebTable.Cell(compare(room(old), room(clazz), CmpMode.BOTH_NEW, false, clazz == null)), @@ -561,7 +561,7 @@ public void showResults(Collection result) { new WebTable.Cell(compare(dates(old), dates(clazz), CmpMode.BOTH_OLD, false, clazz == null)), new WebTable.Cell(compare(dates(old), dates(clazz), CmpMode.BOTH_NEW, false, clazz == null)), (old != null && old.hasDistanceConflict() ? - new WebTable.IconCell(RESOURCES.distantConflict(), MESSAGES.backToBackDistance(old.getBackToBackRooms(), old.getBackToBackDistance()), + new WebTable.IconCell(old.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict(), MESSAGES.backToBackDistance(old.getBackToBackRooms(), old.getBackToBackDistance()), compare(room(old), room(clazz), CmpMode.BOTH_OLD, false, clazz == null)) : new WebTable.Cell(compare(room(old), room(clazz), CmpMode.BOTH_OLD, false, clazz == null))), new WebTable.Cell(compare(room(old), room(clazz), CmpMode.BOTH_NEW, false, clazz == null)), diff --git a/JavaSource/org/unitime/timetable/gwt/client/sectioning/TimeGrid.java b/JavaSource/org/unitime/timetable/gwt/client/sectioning/TimeGrid.java index 8f06abdc3f..a4e86e7d50 100644 --- a/JavaSource/org/unitime/timetable/gwt/client/sectioning/TimeGrid.java +++ b/JavaSource/org/unitime/timetable/gwt/client/sectioning/TimeGrid.java @@ -461,7 +461,7 @@ public ArrayList addClass(ClassAssignmentInterface.ClassAssignment row, for (Meeting m: meetings) m.setPinned(true); if (row.hasDistanceConflict()) { for (Meeting m: meetings) { - Widget dist = new Image(RESOURCES.distantConflict()); + Widget dist = new Image(row.hasLongDistanceConflict() ? RESOURCES.longDistantConflict() : RESOURCES.distantConflict()); if (iPrint) { dist = new Label(MESSAGES.distanceConflict(row.getBackToBackDistance())); dist.setStyleName("label"); diff --git a/JavaSource/org/unitime/timetable/gwt/resources/StudentSectioningResources.java b/JavaSource/org/unitime/timetable/gwt/resources/StudentSectioningResources.java index afe544dd8c..a32ce0f893 100644 --- a/JavaSource/org/unitime/timetable/gwt/resources/StudentSectioningResources.java +++ b/JavaSource/org/unitime/timetable/gwt/resources/StudentSectioningResources.java @@ -65,6 +65,9 @@ public interface StudentSectioningResources extends ClientBundle { @Source("org/unitime/timetable/gwt/resources/icons/roadrunner16.png") ImageResource distantConflict(); + @Source("org/unitime/timetable/gwt/resources/icons/car-13.png") + ImageResource longDistantConflict(); + @Source("org/unitime/timetable/gwt/resources/icons/tick.png") ImageResource saved(); diff --git a/JavaSource/org/unitime/timetable/gwt/resources/icons/car-13.png b/JavaSource/org/unitime/timetable/gwt/resources/icons/car-13.png new file mode 100644 index 0000000000..fef59e84ea Binary files /dev/null and b/JavaSource/org/unitime/timetable/gwt/resources/icons/car-13.png differ diff --git a/JavaSource/org/unitime/timetable/gwt/shared/ClassAssignmentInterface.java b/JavaSource/org/unitime/timetable/gwt/shared/ClassAssignmentInterface.java index 2d971e8dbc..562f0c06c7 100644 --- a/JavaSource/org/unitime/timetable/gwt/shared/ClassAssignmentInterface.java +++ b/JavaSource/org/unitime/timetable/gwt/shared/ClassAssignmentInterface.java @@ -426,6 +426,7 @@ public static class ClassAssignment implements IsSerializable, Serializable { private GradeMode iGradeMode = null; private Float iCreditHour = null, iCreditMin = null, iCreditMax = null; private Boolean iCanWaitList = null; + private boolean iLongDistanceConflict = false; public ClassAssignment() {} public ClassAssignment(CourseAssignment course) { @@ -663,6 +664,9 @@ public void setAvailable(Boolean available) { public boolean hasDistanceConflict() { return iDistanceConflict; } public void setDistanceConflict(boolean distanceConflict) { iDistanceConflict = distanceConflict; } + public boolean hasLongDistanceConflict() { return iLongDistanceConflict; } + public void setLongDistanceConflict(boolean longDistanceConflict) { iLongDistanceConflict = longDistanceConflict; } + public int getBackToBackDistance() { return iBackToBackDistance; } public void setBackToBackDistance(int backToBackDistance) { iBackToBackDistance = backToBackDistance; } diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetAssignment.java b/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetAssignment.java index 861dd7e675..fc4b62fb1b 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetAssignment.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetAssignment.java @@ -38,6 +38,8 @@ import org.cpsolver.ifs.assignment.AssignmentComparator; import org.cpsolver.ifs.assignment.AssignmentMap; import org.cpsolver.ifs.util.DistanceMetric; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; +import org.cpsolver.studentsct.extension.StudentQuality; import org.cpsolver.studentsct.model.Config; import org.cpsolver.studentsct.model.Course; import org.cpsolver.studentsct.model.CourseRequest; @@ -579,6 +581,7 @@ public ClassAssignmentInterface computeAssignment(OnlineSectioningServer server, CustomClassAttendanceProvider provider = Customization.CustomClassAttendanceProvider.getProvider(); StudentClassAttendance attendance = (provider == null ? null : provider.getCustomClassAttendanceForStudent(StudentDAO.getInstance().get(student.getStudentId(), helper.getHibSession()), helper, null)); Map> wlOverlaps = null; + StudentQuality sq = new StudentQuality(server.getDistanceMetric(), server.getConfig()); float credit = 0f; if (student.getMaxCredit() != null) @@ -640,6 +643,7 @@ public int compare(Enrollment o1, Enrollment o2) { return o1.getRequest().compareTo(o2.getRequest()); } }); + TreeSet other = new TreeSet(); Hashtable> overlapingSections = new Hashtable>(); Enrollment noConfEnrl = null; int nbrEnrl = 0; @@ -659,7 +663,7 @@ public int compare(Enrollment o1, Enrollment o2) { } else if (x != null && x.getAssignments() != null && !x.getAssignments().isEmpty()) { for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(sq, a, enrl)) { overlaps = true; overlap.add(x); if (x.getRequest() instanceof CourseRequest) { @@ -672,12 +676,22 @@ public int compare(Enrollment o1, Enrollment o2) { } } } + unavailabilities: for (Unavailability unavailability: courseRequest.getStudent().getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(sq, (Section)section, unavailability)) { + overlaps = true; + String ov = MSG.teachingAssignment(unavailability.getSection().getName()); + other.add(ov); + continue unavailabilities; + } + } + } if (!overlaps && noConfEnrl == null) noConfEnrl = enrl; } if (noConfEnrl == null) { if (wlOverlaps == null) wlOverlaps = new HashMap>(); - Set overlaps = new TreeSet(); + Set overlaps = new TreeSet(other); wlOverlaps.put(c.getId(), overlaps); for (Enrollment q: overlap) { if (q.getRequest() instanceof FreeTimeRequest) { @@ -747,7 +761,7 @@ public int compare(Enrollment o1, Enrollment o2) { if (x == null || x.getAssignments() == null || x.getAssignments().isEmpty()) continue; for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(sq, a, enrl)) { overlap.add(x); if (x.getRequest() instanceof CourseRequest) { CourseRequest cr = (CourseRequest)x.getRequest(); @@ -758,6 +772,15 @@ public int compare(Enrollment o1, Enrollment o2) { } } } + unavailabilities: for (Unavailability unavailability: crq.getStudent().getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(sq, (Section)section, unavailability)) { + String ov = MSG.teachingAssignment(unavailability.getSection().getName()); + ca.addOverlap(ov); + continue unavailabilities; + } + } + } } for (Enrollment q: overlap) { if (q.getRequest() instanceof FreeTimeRequest) { @@ -775,6 +798,16 @@ public int compare(Enrollment o1, Enrollment o2) { ca.addOverlap(ov); } } + unavailabilities: for (Unavailability unavailability: crq.getStudent().getUnavailabilities()) { + for (Config config: crq.getCourse(course.getCourseId()).getOffering().getConfigs()) + for (Subpart subpart: config.getSubparts()) + for (Section section: subpart.getSections()) { + if (section.getLimit() > 0 && unavailability.isOverlapping(section)) { + ca.addOverlap(MSG.teachingAssignment(unavailability.getSection().getName())); + continue unavailabilities; + } + } + } if (avEnrls.isEmpty()) { ca.setNotAvailable(true); if (course.getLimit() >= 0) { @@ -891,8 +924,10 @@ public int compare(Enrollment o1, Enrollment o2) { for (Iterator k = otherSection.getRooms().iterator(); k.hasNext();) from += k.next().getName() + (k.hasNext() ? ", " : ""); } - if (otherSection.isDistanceConflict(student, section, m)) + if (otherSection.isDistanceConflict(student, section, m)) { a.setDistanceConflict(true); + a.setLongDistanceConflict(otherSection.isLongDistanceConflict(student, otherSection, m)); + } if (section.getTime() != null && section.getTime().hasIntersection(otherSection.getTime()) && !section.isToIgnoreStudentConflictsWith(offering.getDistributions(), otherSection.getSectionId())) { XCourse otherCourse = otherOffering.getCourse(otherEnrollment.getCourseId()); XSubpart otherSubpart = otherOffering.getSubpart(otherSection.getSubpartId()); @@ -908,9 +943,10 @@ public int compare(Enrollment o1, Enrollment o2) { } if (section.isUnavailabilityDistanceConflict(student, cs.getSection(), um)) { a.setDistanceConflict(true); + a.setLongDistanceConflict(section.isUnavailabilityLongDistanceConflict(student, cs.getSection(), um)); from = ""; - for (Iterator k = cs.getSection().getRooms().iterator(); k.hasNext();) - from += k.next().getName() + (k.hasNext() ? ", " : ""); + for (Iterator k = cs.getSection().getRooms().iterator(); k.hasNext();) + from += k.next().getName() + (k.hasNext() ? ", " : ""); dist = section.getDistanceInMinutes(um, cs.getSection().getRooms()); } } diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetRequest.java b/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetRequest.java index 005b97eb7c..005315abe1 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetRequest.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/basic/GetRequest.java @@ -29,6 +29,9 @@ import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.AssignmentComparator; import org.cpsolver.ifs.assignment.AssignmentMap; +import org.cpsolver.ifs.util.DistanceMetric; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; +import org.cpsolver.studentsct.extension.StudentQuality; import org.cpsolver.studentsct.model.Config; import org.cpsolver.studentsct.model.Course; import org.cpsolver.studentsct.model.Enrollment; @@ -201,6 +204,8 @@ else if (status == org.unitime.timetable.model.CourseRequest.CourseRequestOverri if (setInactive && server instanceof StudentSolver) setInactive = false; boolean showWaitListPosition = ApplicationProperty.OnlineSchedulingShowWaitListPosition.isTrue(); + DistanceMetric m = server.getDistanceMetric(); + StudentQuality sq = new StudentQuality(m, server.getConfig()); for (XRequest cd: student.getRequests()) { CourseRequestInterface.Request r = null; @@ -315,6 +320,7 @@ public int compare(Enrollment o1, Enrollment o2) { return o1.getRequest().compareTo(o2.getRequest()); } }); + TreeSet other = new TreeSet(); Hashtable> overlapingSections = new Hashtable>(); Enrollment noConfEnrl = null; int nbrEnrl = 0; @@ -334,7 +340,7 @@ public int compare(Enrollment o1, Enrollment o2) { } else if (x != null && x.getAssignments() != null && !x.getAssignments().isEmpty()) { for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(sq, a, enrl)) { overlaps = true; overlap.add(x); if (x.getRequest() instanceof org.cpsolver.studentsct.model.CourseRequest) { @@ -347,6 +353,16 @@ public int compare(Enrollment o1, Enrollment o2) { } } } + unavailabilities: for (Unavailability unavailability: courseRequest.getStudent().getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(sq, (Section)section, unavailability)) { + overlaps = true; + String ov = MSG.teachingAssignment(unavailability.getSection().getName()); + other.add(ov); + continue unavailabilities; + } + } + } if (!overlaps && noConfEnrl == null) noConfEnrl = enrl; } diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListCourseOfferingsByExternalId.java b/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListCourseOfferingsByExternalId.java index d5dfc3b336..aa6197007b 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListCourseOfferingsByExternalId.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListCourseOfferingsByExternalId.java @@ -55,7 +55,7 @@ protected List listCourses(OnlineSectioningServer server, Onli if (iQuery != null && iQuery.length() >= 3) { try { for (Object[] courseClassId: helper.getHibSession().createQuery( - "select distinct c.uniqueId, z.uniqueId " + + "select c.uniqueId, z.uniqueId " + "from Class_ z inner join z.schedulingSubpart.instrOfferingConfig.instructionalOffering.courseOfferings c " + "where c.subjectArea.session.uniqueId = :sessionId and c.subjectArea.department.allowStudentScheduling = true " + "and ((lower(c.subjectArea.subjectAreaAbbreviation || ' ' || c.courseNbr || ' ' || z.classSuffix) like :q || '%' and :q like lower(c.subjectArea.subjectAreaAbbreviation || ' ' || c.courseNbr || ' %')) or lower(z.classSuffix) like :q || '%') " + diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListEnrollments.java b/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListEnrollments.java index 563cea7a31..7c7f27e595 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListEnrollments.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/basic/ListEnrollments.java @@ -436,13 +436,20 @@ else if (i.hasNext()) for (Iterator k = otherSection.getRooms().iterator(); k.hasNext();) from += k.next().getName() + (k.hasNext() ? ", " : ""); } - if (otherSection.isDistanceConflict(student, section, m)) + if (otherSection.isDistanceConflict(student, section, m)) { a.setDistanceConflict(true); + a.setLongDistanceConflict(otherSection.isLongDistanceConflict(student, section, m)); + } if (section.getTime() != null && section.getTime().hasIntersection(otherSection.getTime()) && !section.isToIgnoreStudentConflictsWith(offering.getDistributions(), otherSection.getSectionId())) { XCourse otherCourse = otherOffering.getCourse(otherEnrollment.getCourseId()); XSubpart otherSubpart = otherOffering.getSubpart(otherSection.getSubpartId()); overlap.add(MSG.clazz(otherCourse.getSubjectArea(), otherCourse.getCourseNumber(), otherSubpart.getName(), otherSection.getName(otherCourse.getCourseId()))); } + if (otherSection.isHardDistanceConflict(student, section, m)) { + XCourse otherCourse = otherOffering.getCourse(otherEnrollment.getCourseId()); + XSubpart otherSubpart = otherOffering.getSubpart(otherSection.getSubpartId()); + overlap.add(MSG.clazz(otherCourse.getSubjectArea(), otherCourse.getCourseNumber(), otherSubpart.getName(), otherSection.getName(otherCourse.getCourseId()))); + } } } } diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/model/XSection.java b/JavaSource/org/unitime/timetable/onlinesectioning/model/XSection.java index 33e0cdbe97..b836342893 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/model/XSection.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/model/XSection.java @@ -465,6 +465,65 @@ public boolean isDistanceConflict(XStudent student, XSection other, DistanceMetr } return false; } + + public boolean isLongDistanceConflict(XStudent student, XSection other, DistanceMetric m) { + if (getNrRooms() == 0 || other.getNrRooms() == 0) return false; + XTime t1 = getTime(); + XTime t2 = other.getTime(); + if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) return false; + int a1 = t1.getSlot(), a2 = t2.getSlot(); + if (student.hasAccomodation(m.getShortDistanceAccommodationReference())) { + if (m.doComputeDistanceConflictsBetweenNonBTBClasses()) { + if (a1 + t1.getLength() <= a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return (dist > Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())); + } + } else { + if (a1 + t1.getLength() == a2) + return getDistanceInMinutes(m, other.getRooms()) > 0; + } + } else { + if (m.doComputeDistanceConflictsBetweenNonBTBClasses()) { + if (a1 + t1.getLength() <= a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceLongLimitInMinutes() && (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())); + } + } else { + if (a1 + t1.getLength() == a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceLongLimitInMinutes() && dist > t1.getBreakTime(); + } + } + } + return false; + } + + public boolean isHardDistanceConflict(XStudent student, XSection other, DistanceMetric m) { + if (!m.isHardDistanceConflictsEnabled()) return false; + if (getNrRooms() == 0 || other.getNrRooms() == 0) return false; + XTime t1 = getTime(); + XTime t2 = other.getTime(); + if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) return false; + int a1 = t1.getSlot(), a2 = t2.getSlot(); + if (m.doComputeDistanceConflictsBetweenNonBTBClasses()) { + if (a1 + t1.getLength() <= a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceHardLimitInMinutes() && (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()) + m.getAllowedDistanceInMinutes()); + } else if (a2 + t2.getLength() <= a1) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceHardLimitInMinutes() && (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength()) + m.getAllowedDistanceInMinutes()); + } + } else { + if (a1 + t1.getLength() == a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceHardLimitInMinutes() && dist > t1.getBreakTime() + m.getAllowedDistanceInMinutes(); + } else if (a1 == a2 + t2.getLength()) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceHardLimitInMinutes() && dist > t2.getBreakTime() + m.getAllowedDistanceInMinutes(); + } + } + return false; + } public boolean isUnavailabilityDistanceConflict(XStudent student, XSection other, DistanceMetric m) { if (getNrRooms() == 0 || other.getNrRooms() == 0) return false; @@ -481,7 +540,39 @@ public boolean isUnavailabilityDistanceConflict(XStudent student, XSection other } return false; } - + + public boolean isUnavailabilityHardDistanceConflict(XStudent student, XSection other, DistanceMetric m) { + if (!m.isHardDistanceConflictsEnabled()) return false; + if (getNrRooms() == 0 || other.getNrRooms() == 0) return false; + XTime t1 = getTime(); + XTime t2 = other.getTime(); + if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) return false; + int a1 = t1.getSlot(), a2 = t2.getSlot(); + if (a1 + t1.getLength() <= a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceHardLimitInMinutes() && (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()) + m.getAllowedDistanceInMinutes()); + } else if (a2 + t2.getLength() <= a1) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceHardLimitInMinutes() && (dist > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength()) + m.getAllowedDistanceInMinutes()); + } + return false; + } + + public boolean isUnavailabilityLongDistanceConflict(XStudent student, XSection other, DistanceMetric m) { + if (getNrRooms() == 0 || other.getNrRooms() == 0) return false; + XTime t1 = getTime(); + XTime t2 = other.getTime(); + if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) return false; + int a1 = t1.getSlot(), a2 = t2.getSlot(); + if (a1 + t1.getLength() <= a2) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceLongLimitInMinutes() && (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())); + } else if (a2 + t2.getLength() <= a1) { + int dist = getDistanceInMinutes(m, other.getRooms()); + return dist >= m.getDistanceLongLimitInMinutes() && (dist > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength())); + } + return false; + } /** Return true if overlaps are allowed, but the number of overlapping slots should be minimized. */ public boolean isAllowOverlap() { diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/solver/ComputeSuggestionsAction.java b/JavaSource/org/unitime/timetable/onlinesectioning/solver/ComputeSuggestionsAction.java index 9bd52d3ea6..10617f94f9 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/solver/ComputeSuggestionsAction.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/solver/ComputeSuggestionsAction.java @@ -35,6 +35,7 @@ import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.AssignmentComparator; import org.cpsolver.ifs.assignment.AssignmentMap; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; import org.cpsolver.studentsct.extension.StudentQuality; import org.cpsolver.studentsct.heuristics.selection.BranchBoundSelection.BranchBoundNeighbour; import org.cpsolver.studentsct.model.Config; @@ -504,6 +505,7 @@ public int compare(Enrollment o1, Enrollment o2) { CourseRequest request = (CourseRequest)selectedRequest; Course course = request.getCourses().get(0); Collection avEnrls = request.getAvaiableEnrollmentsSkipSameTime(assignment); + TreeSet overlapMessages = new TreeSet(); for (Iterator e = avEnrls.iterator(); e.hasNext();) { Enrollment enrl = e.next(); for (Request q: enrl.getStudent().getRequests()) { @@ -512,7 +514,7 @@ public int compare(Enrollment o1, Enrollment o2) { if (x == null || x.getAssignments() == null || x.getAssignments().isEmpty()) continue; for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(model.getStudentQuality(), a, enrl)) { overlap.add(x); if (x.getRequest() instanceof CourseRequest) { CourseRequest cr = (CourseRequest)x.getRequest(); @@ -523,8 +525,25 @@ public int compare(Enrollment o1, Enrollment o2) { } } } + unavailabilities: for (Unavailability unavailability: student.getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(model.getStudentQuality(), (Section)section, unavailability)) { + overlapMessages.add(unavailability.getCourseName() + " " + unavailability.getSectionName()); + continue unavailabilities; + } + } + } + } + unavailabilities: for (Unavailability unavailability: student.getUnavailabilities()) { + for (Config config: course.getOffering().getConfigs()) + for (Subpart subpart: config.getSubparts()) + for (Section section: subpart.getSections()) { + if (section.getLimit() > 0 && unavailability.isOverlapping(section)) { + overlapMessages.add(unavailability.getCourseName() + " " + unavailability.getSectionName()); + continue unavailabilities; + } + } } - TreeSet overlapMessages = new TreeSet(); for (Iterator i = overlap.iterator(); i.hasNext();) { Enrollment q = i.next(); String ov = null; diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/solver/FindAssignmentAction.java b/JavaSource/org/unitime/timetable/onlinesectioning/solver/FindAssignmentAction.java index a659d15d51..6c472edb06 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/solver/FindAssignmentAction.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/solver/FindAssignmentAction.java @@ -44,6 +44,7 @@ import org.cpsolver.ifs.util.DistanceMetric; import org.cpsolver.ifs.util.ToolBox; import org.cpsolver.studentsct.StudentSectioningModel; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; import org.cpsolver.studentsct.extension.StudentQuality; import org.cpsolver.studentsct.heuristics.selection.BranchBoundSelection.BranchBoundNeighbour; import org.cpsolver.studentsct.model.Choice; @@ -422,7 +423,7 @@ else if (server.getConfig().getPropertyBoolean("General.CheckUnavailabilitiesFro if (a.isPinned() || (getSpecialRegistration() == null && a.isSaved()) || getRequest().isNoChange()) { if (!conflict) { for (Section s: requiredOrSavedSections) - if (s.isOverlapping(section)) { conflict = true; break; } + if (s.isOverlapping(section) || HardDistanceConflicts.inConflict(model.getStudentQuality(), s, section)) { conflict = true; break; } boolean allowOverlap = false; for (Reservation rx: cr.getReservations(cr.getCourse(a.getCourseId()))) { if (rx.isAllowOverlap()) { allowOverlap = true; break; } @@ -433,7 +434,7 @@ else if (server.getConfig().getPropertyBoolean("General.CheckUnavailabilitiesFro for (Map.Entry> x: requiredOrSavedSectionsForCourse.entrySet()) { if (!allowOverlaps.contains(x.getKey())) for (Section s: x.getValue()) - if (s.isOverlapping(section)) { conflict = true; break; } + if (s.isOverlapping(section) || HardDistanceConflicts.inConflict(model.getStudentQuality(), s, section)) { conflict = true; break; } if (conflict) break; } } @@ -465,7 +466,7 @@ else if (server.getConfig().getPropertyBoolean("General.CheckUnavailabilitiesFro if (section.getLimit() == 0 && !getRequest().areSpaceConflictsAllowed()) conflict = true; for (Section s: requiredOrSavedSections) - if (s.isOverlapping(section)) { conflict = true; break; } + if (s.isOverlapping(section) || HardDistanceConflicts.inConflict(model.getStudentQuality(), s, section)) { conflict = true; break; } boolean allowOverlap = false; for (Reservation rx: cr.getReservations(cr.getCourse(a.getCourseId()))) { if (rx.isAllowOverlap()) { allowOverlap = true; break; } @@ -476,7 +477,7 @@ else if (server.getConfig().getPropertyBoolean("General.CheckUnavailabilitiesFro for (Map.Entry> x: requiredOrSavedSectionsForCourse.entrySet()) { if (!allowOverlaps.contains(x.getKey())) for (Section s: x.getValue()) - if (s.isOverlapping(section)) { conflict = true; break; } + if (s.isOverlapping(section) || HardDistanceConflicts.inConflict(model.getStudentQuality(), s, section)) { conflict = true; break; } if (conflict) break; } } @@ -1059,6 +1060,7 @@ public int compare(Enrollment e1, Enrollment e2) { return e1.getRequest().compareTo(e2.getRequest()); } }); + TreeSet other = new TreeSet(); Hashtable> overlapingSections = new Hashtable>(); Collection enrls = r.getEnrollmentsSkipSameTime(assignment); Enrollment noConfEnrl = null; @@ -1078,7 +1080,7 @@ public int compare(Enrollment e1, Enrollment e2) { for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(sq, a, enrl)) { overlaps = true; overlap.add(x); if (x.getRequest() instanceof CourseRequest) { @@ -1090,6 +1092,16 @@ public int compare(Enrollment e1, Enrollment e2) { } } } + unavailabilities: for (Unavailability unavailability: r.getStudent().getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(sq, (Section)section, unavailability)) { + overlaps = true; + String ov = MSG.teachingAssignment(unavailability.getSection().getName()); + other.add(ov); + continue unavailabilities; + } + } + } if (!overlaps && noConfEnrl == null) noConfEnrl = enrl; } @@ -1110,6 +1122,7 @@ public int compare(Enrollment e1, Enrollment e2) { ca.addOverlap(ov); } } + for (String q: other) ca.addOverlap(q); if (nbrEnrl == 0) { unavailabilities: for (Unavailability unavailability: enrollment.getStudent().getUnavailabilities()) { for (Config config: course.getOffering().getConfigs()) @@ -1163,7 +1176,7 @@ public int compare(Enrollment e1, Enrollment e2) { if (x == enrollment) continue; for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(sq, a, enrl)) { overlap.add(x); if (x.getRequest() instanceof CourseRequest) { CourseRequest cr = (CourseRequest)x.getRequest(); @@ -1174,6 +1187,14 @@ public int compare(Enrollment e1, Enrollment e2) { } } } + unavailabilities: for (Unavailability unavailability: enrollment.getStudent().getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(sq, (Section)section, unavailability)) { + ca.addOverlap(MSG.teachingAssignment(unavailability.getSection().getName())); + continue unavailabilities; + } + } + } } for (Enrollment q: overlap) { if (q.getRequest() instanceof FreeTimeRequest) { @@ -1195,7 +1216,7 @@ public int compare(Enrollment e1, Enrollment e2) { for (Config config: course.getOffering().getConfigs()) for (Subpart subpart: config.getSubparts()) for (Section section: subpart.getSections()) { - if (unavailability.isOverlapping(section)) { + if (section.getLimit() > 0 && unavailability.isOverlapping(section)) { ca.addOverlap(MSG.teachingAssignment(unavailability.getSection().getName())); continue unavailabilities; } @@ -1380,8 +1401,10 @@ public int compare(Enrollment e1, Enrollment e2) { from += k.next().getName() + (k.hasNext() ? ", " : ""); } - if (sq.hasDistanceConflict(enrollment.getStudent(), s, section) && s.getTime().getStartSlot() < section.getTime().getStartSlot()) + if (sq.hasDistanceConflict(enrollment.getStudent(), s, section) && s.getTime().getStartSlot() < section.getTime().getStartSlot()) { a.setDistanceConflict(true); + a.setLongDistanceConflict(sq.getStudentQualityContext().getDistanceInMinutes(s.getPlacement(), section.getPlacement()) >= sq.getStudentQualityContext().getDistanceMetric().getDistanceLongLimitInMinutes()); + } if (section.getTime() != null && section.getTime().hasIntersection(s.getTime()) && !section.isToIgnoreStudentConflictsWith(s.getId())) { overlap.add(MSG.clazz(x.getCourse().getSubjectArea(), x.getCourse().getCourseNumber(), s.getSubpart().getName(), s.getName(x.getCourse().getId()))); } @@ -1390,8 +1413,17 @@ public int compare(Enrollment e1, Enrollment e2) { for (Unavailability unavailability: enrollment.getStudent().getUnavailabilities()) if (section.getTime() != null && unavailability.getTime() != null && section.getTime().hasIntersection(unavailability.getTime())) overlap.add(unavailability.getSection().getName()); - else if (StudentQuality.Type.UnavailabilityDistance.inConflict(sq.getStudentQualityContext(), section, unavailability)) + else if (StudentQuality.Type.UnavailabilityDistance.inConflict(sq.getStudentQualityContext(), section, unavailability)) { a.setDistanceConflict(true); + int d = sq.getStudentQualityContext().getUnavailabilityDistanceInMinutes(section.getPlacement(), unavailability); + a.setLongDistanceConflict(d >= sq.getStudentQualityContext().getUnavailabilityDistanceMetric().getDistanceLongLimitInMinutes()); + if (d > dist) { + dist = d; + from = ""; + for (Iterator k = unavailability.getRooms().iterator(); k.hasNext();) + from += k.next().getName() + (k.hasNext() ? ", " : ""); + } + } if (!overlap.isEmpty()) { String note = null; diff --git a/JavaSource/org/unitime/timetable/onlinesectioning/status/FindEnrollmentAction.java b/JavaSource/org/unitime/timetable/onlinesectioning/status/FindEnrollmentAction.java index a18f439bda..6f291bc1dc 100644 --- a/JavaSource/org/unitime/timetable/onlinesectioning/status/FindEnrollmentAction.java +++ b/JavaSource/org/unitime/timetable/onlinesectioning/status/FindEnrollmentAction.java @@ -33,6 +33,8 @@ import org.cpsolver.ifs.assignment.AssignmentComparator; import org.cpsolver.ifs.assignment.AssignmentMap; import org.cpsolver.ifs.util.DistanceMetric; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; +import org.cpsolver.studentsct.extension.StudentQuality; import org.cpsolver.studentsct.model.Config; import org.cpsolver.studentsct.model.Course; import org.cpsolver.studentsct.model.CourseRequest; @@ -151,6 +153,7 @@ public List execute(OnlineSectioningServer if (offering == null) return ret; XEnrollments enrollments = server.getEnrollments(course.getOfferingId()); DistanceMetric m = server.getDistanceMetric(); + StudentQuality sq = new StudentQuality(m, server.getConfig()); XExpectations expectations = server.getExpectations(offering.getOfferingId()); OverExpectedCriterion overExp = server.getOverExpectedCriterion(); AcademicSessionInfo session = server.getAcademicSession(); @@ -314,6 +317,7 @@ public int compare(Enrollment o1, Enrollment o2) { return o1.getRequest().compareTo(o2.getRequest()); } }); + TreeSet other = new TreeSet(); Hashtable> overlapingSections = new Hashtable>(); Enrollment noConfEnrl = null; int nbrEnrl = 0; @@ -333,7 +337,7 @@ public int compare(Enrollment o1, Enrollment o2) { } else if (x != null && x.getAssignments() != null && !x.getAssignments().isEmpty()) { for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(sq, a, enrl)) { overlaps = true; overlap.add(x); if (x.getRequest() instanceof org.cpsolver.studentsct.model.CourseRequest) { @@ -346,11 +350,21 @@ public int compare(Enrollment o1, Enrollment o2) { } } } + unavailabilities: for (Unavailability unavailability: courseRequest.getStudent().getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(sq, (Section)section, unavailability)) { + overlaps = true; + String ov = MSG.teachingAssignment(unavailability.getSection().getName()); + other.add(ov); + continue unavailabilities; + } + } + } if (!overlaps && noConfEnrl == null) noConfEnrl = enrl; } if (noConfEnrl == null) { - Set overlaps = new TreeSet(); + Set overlaps = new TreeSet(other); for (Enrollment q: overlap) { if (q.getRequest() instanceof FreeTimeRequest) { overlaps.add(OnlineSectioningHelper.toString((FreeTimeRequest)q.getRequest())); @@ -502,13 +516,20 @@ else if (i.hasNext()) for (Iterator k = otherSection.getRooms().iterator(); k.hasNext();) from += k.next().getName() + (k.hasNext() ? ", " : ""); } - if (otherSection.isDistanceConflict(student, section, m)) + if (otherSection.isDistanceConflict(student, section, m)) { a.setDistanceConflict(true); + a.setLongDistanceConflict(otherSection.isLongDistanceConflict(student, section, m)); + } if (section.getTime() != null && section.getTime().hasIntersection(otherSection.getTime()) && !section.isToIgnoreStudentConflictsWith(offering.getDistributions(), otherSection.getSectionId())) { XCourse otherCourse = otherOffering.getCourse(otherEnrollment.getCourseId()); XSubpart otherSubpart = otherOffering.getSubpart(otherSection.getSubpartId()); overlap.add(MSG.clazz(otherCourse.getSubjectArea(), otherCourse.getCourseNumber(), otherSubpart.getName(), otherSection.getName(otherCourse.getCourseId()))); } + if (otherSection.isHardDistanceConflict(student, section, m)) { + XCourse otherCourse = otherOffering.getCourse(otherEnrollment.getCourseId()); + XSubpart otherSubpart = otherOffering.getSubpart(otherSection.getSubpartId()); + overlap.add(MSG.clazz(otherCourse.getSubjectArea(), otherCourse.getCourseNumber(), otherSubpart.getName(), otherSection.getName(otherCourse.getCourseId()))); + } } } } diff --git a/JavaSource/org/unitime/timetable/reports/studentsct/UnasignedCourseRequests.java b/JavaSource/org/unitime/timetable/reports/studentsct/UnasignedCourseRequests.java index 9c596f6a24..8b06846145 100644 --- a/JavaSource/org/unitime/timetable/reports/studentsct/UnasignedCourseRequests.java +++ b/JavaSource/org/unitime/timetable/reports/studentsct/UnasignedCourseRequests.java @@ -33,7 +33,9 @@ import org.cpsolver.ifs.util.CSVFile; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.studentsct.StudentSectioningModel; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; import org.cpsolver.studentsct.model.AreaClassificationMajor; +import org.cpsolver.studentsct.model.Config; import org.cpsolver.studentsct.model.Course; import org.cpsolver.studentsct.model.CourseRequest; import org.cpsolver.studentsct.model.Enrollment; @@ -45,6 +47,8 @@ import org.cpsolver.studentsct.model.Section; import org.cpsolver.studentsct.model.Student; import org.cpsolver.studentsct.model.StudentGroup; +import org.cpsolver.studentsct.model.Subpart; +import org.cpsolver.studentsct.model.Unavailability; import org.cpsolver.studentsct.report.AbstractStudentSectioningReport; import org.unitime.localization.impl.Localization; import org.unitime.timetable.gwt.resources.StudentSectioningMessages; @@ -151,6 +155,7 @@ public int compare(Enrollment o1, Enrollment o2) { return o1.getRequest().compareTo(o2.getRequest()); } }); + TreeSet other = new TreeSet(); Hashtable> overlapingSections = new Hashtable>(); List av = courseRequest.getAvaiableEnrollmentsSkipSameTime(assignment); RequestPriority conflictPriority = null; @@ -172,7 +177,7 @@ public int compare(Enrollment o1, Enrollment o2) { if (x == null || x.getAssignments() == null || x.getAssignments().isEmpty()) continue; for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(getModel().getStudentQuality(), a, enrl)) { overlaps.add(x); if (x.getRequest() instanceof CourseRequest) { CourseRequest cr = (CourseRequest)x.getRequest(); @@ -183,9 +188,28 @@ public int compare(Enrollment o1, Enrollment o2) { } } } + unavailabilities: for (Unavailability unavailability: student.getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(getModel().getStudentQuality(), (Section)section, unavailability)) { + other.add(unavailability.getCourseName() + " " + unavailability.getSectionName()); + continue unavailabilities; + } + } + } + } + unavailabilities: for (Unavailability unavailability: student.getUnavailabilities()) { + for (Course course: courseRequest.getCourses()) + for (Config config: course.getOffering().getConfigs()) + for (Subpart subpart: config.getSubparts()) + for (Section section: subpart.getSections()) { + if (section.getLimit() > 0 && unavailability.isOverlapping(section)) { + other.add(unavailability.getCourseName() + " " + unavailability.getSectionName()); + continue unavailabilities; + } + } } - if (!overlaps.isEmpty()) { - TreeSet ts = new TreeSet(); + if (!overlaps.isEmpty() || !other.isEmpty()) { + TreeSet ts = new TreeSet(other); for (Enrollment q: overlaps) { if (q.getRequest() instanceof FreeTimeRequest) { ts.add(OnlineSectioningHelper.toString((FreeTimeRequest)q.getRequest())); diff --git a/JavaSource/org/unitime/timetable/reports/studentsct/UnusedReservations.java b/JavaSource/org/unitime/timetable/reports/studentsct/UnusedReservations.java index 432353c19c..6acfb5a33c 100644 --- a/JavaSource/org/unitime/timetable/reports/studentsct/UnusedReservations.java +++ b/JavaSource/org/unitime/timetable/reports/studentsct/UnusedReservations.java @@ -35,7 +35,9 @@ import org.cpsolver.ifs.util.CSVFile; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.studentsct.StudentSectioningModel; +import org.cpsolver.studentsct.constraint.HardDistanceConflicts; import org.cpsolver.studentsct.model.AreaClassificationMajor; +import org.cpsolver.studentsct.model.Config; import org.cpsolver.studentsct.model.Course; import org.cpsolver.studentsct.model.CourseRequest; import org.cpsolver.studentsct.model.Enrollment; @@ -47,6 +49,8 @@ import org.cpsolver.studentsct.model.Section; import org.cpsolver.studentsct.model.Student; import org.cpsolver.studentsct.model.StudentGroup; +import org.cpsolver.studentsct.model.Subpart; +import org.cpsolver.studentsct.model.Unavailability; import org.cpsolver.studentsct.report.AbstractStudentSectioningReport; import org.cpsolver.studentsct.reservation.CourseReservation; import org.cpsolver.studentsct.reservation.CurriculumReservation; @@ -259,6 +263,7 @@ public int compare(Enrollment o1, Enrollment o2) { return o1.getRequest().compareTo(o2.getRequest()); } }); + TreeSet other = new TreeSet(); Hashtable> overlapingSections = new Hashtable>(); List av = courseRequest.getAvaiableEnrollmentsSkipSameTime(assignment); if (enrollment != null) { @@ -282,7 +287,7 @@ else if (SectioningRequest.hasInconsistentRequirements(courseRequest, null)) if (x == null || x.getAssignments() == null || x.getAssignments().isEmpty()) continue; for (Iterator i = x.getAssignments().iterator(); i.hasNext();) { SctAssignment a = i.next(); - if (a.isOverlapping(enrl.getAssignments())) { + if (a.isOverlapping(enrl.getAssignments()) || HardDistanceConflicts.inConflict(getModel().getStudentQuality(), a, enrl)) { overlaps.add(x); if (x.getRequest() instanceof CourseRequest) { CourseRequest cr = (CourseRequest)x.getRequest(); @@ -292,10 +297,28 @@ else if (SectioningRequest.hasInconsistentRequirements(courseRequest, null)) } } } + unavailabilities: for (Unavailability unavailability: student.getUnavailabilities()) { + for (SctAssignment section: enrl.getAssignments()) { + if (HardDistanceConflicts.inConflict(getModel().getStudentQuality(), (Section)section, unavailability)) { + other.add(unavailability.getCourseName() + " " + unavailability.getSectionName()); + continue unavailabilities; + } + } + } } } - if (!overlaps.isEmpty()) { - TreeSet ts = new TreeSet(); + unavailabilities: for (Unavailability unavailability: student.getUnavailabilities()) { + for (Config config: course.getOffering().getConfigs()) + for (Subpart subpart: config.getSubparts()) + for (Section section: subpart.getSections()) { + if (section.getLimit() > 0 && unavailability.isOverlapping(section)) { + other.add(unavailability.getCourseName() + " " + unavailability.getSectionName()); + continue unavailabilities; + } + } + } + if (!overlaps.isEmpty() || !other.isEmpty()) { + TreeSet ts = new TreeSet(other); for (Enrollment q: overlaps) { if (q.getRequest() instanceof FreeTimeRequest) { ts.add(OnlineSectioningHelper.toString((FreeTimeRequest)q.getRequest())); diff --git a/WebContent/help/Release-Notes.xml b/WebContent/help/Release-Notes.xml index 661320b00f..410a102623 100644 --- a/WebContent/help/Release-Notes.xml +++ b/WebContent/help/Release-Notes.xml @@ -42,6 +42,18 @@ So, in other words, a distance conflict with a travel time of 60 or more minutes will be considered hard if the student has less than 30 minutes to travel between the two classes. + + Scheduling Assistant, Dashboards: Hard/Long Distance Conflicts + + Long Distance Conflicts: A distance conflict is considered long, when the travel time is HardDistanceConflict.DistanceLongLimitInMinutes (default to 60) minutes. + Long distance conflicts show a different icon in the user interface (a red racing car instead of a road runner). + + When a student cannot be enrolled in a course due to a hard distance conflict, show a Conflicts with X warning message. + Just like when the course is conflicting with some other course X due to time overlaps. + + Allow for hard distance conflicts when student checks the "Include conflicting suggestions" toggle. + + Course Timetabling