Skip to content

Commit

Permalink
Instructional Offering Detail, Class Detail, Instructor Detail: Instr…
Browse files Browse the repository at this point in the history
…uctor Unavailability

- include instructor availability in the conflict checking
- that is, show a class conflicting warning when the class is placed at a time during which the instructor is not available due to an event, or unavailable dates
  • Loading branch information
tomas-muller committed Jan 9, 2025
1 parent b2ca8cf commit da39257
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 30 deletions.
12 changes: 12 additions & 0 deletions JavaSource/org/unitime/timetable/model/Class_.java
Original file line number Diff line number Diff line change
Expand Up @@ -1984,5 +1984,17 @@ public boolean hasRoomIndexedPrefs() {
if (p.getRoomIndex() != null && p.getRoomIndex() < getNbrRooms()) return true;
return false;
}

public boolean hasLeadInstructor() {
for (ClassInstructor ci: getClassInstructors())
if (ci.isLead()) return true;
return false;
}

public boolean hasLeadInstructorWithUnavailabilities() {
for (ClassInstructor ci: getClassInstructors())
if (ci.isLead() && ci.getInstructor().hasUnavailabilities()) return true;
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,19 @@ public boolean hasConflicts(Long offeringId) {
}
}

Date[] bounds = DatePattern.getBounds(offering.getSessionId());
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(new Date());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();

if (RoomAvailability.getInstance() != null && RoomAvailability.getInstance() instanceof DefaultRoomAvailabilityService) {
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
if (!changePast || ignorePast) {
Calendar cal = Calendar.getInstance(Localization.getJavaLocale());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();
return MeetingDAO.getInstance().getSession().createQuery(
"select count(mx) from ClassEvent e inner join e.meetings m, Meeting mx inner join mx.event ex " +
"where e.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.uniqueId = :offeringId and type(ex) != ClassEvent and m.approvalStatus = 1 and mx.approvalStatus = 1 and " +
Expand All @@ -210,16 +213,6 @@ public boolean hasConflicts(Long offeringId) {
).setParameter("offeringId", offeringId).setCacheable(true).uniqueResult().intValue() > 0;
}
} else if (RoomAvailability.getInstance() != null) {
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(new Date());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();
for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
for (Class_ clazz: subpart.getClasses()) {
Expand Down Expand Up @@ -252,6 +245,54 @@ public boolean hasConflicts(Long offeringId) {
}
}

for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
for (Class_ clazz: subpart.getClasses()) {
if (clazz.isCancelled() || !clazz.hasLeadInstructor()) continue;
if (!clazz.hasLeadInstructorWithUnavailabilities() && !ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue()) continue;
Assignment assignment = getAssignment(clazz);
if (assignment == null) continue;
ClassTimeInfo period = new ClassTimeInfo(assignment);
for (ClassInstructor ci: clazz.getClassInstructors()) {
if (!ci.getLead()) continue;
if (RoomAvailability.getInstance() != null) {
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
ci.getInstructor().getUniqueId(),
bounds[0], bounds[1],
RoomAvailabilityInterface.sClassType);
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
if (period.overlaps(timesToCheck) != null) return true;
}
}
if (ci.getInstructor().hasUnavailabilities()) {
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
if (period.overlaps(timesToCheck) != null) return true;
}
}
}
}

return false;
}

Expand Down Expand Up @@ -348,6 +389,7 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
Class_ clazz = Class_DAO.getInstance().get(classId);
if (clazz == null || clazz.isCancelled()) return null;
Set<TimeBlock> conflicts = new TreeSet<TimeBlock>(new TimeBlockComparator());
boolean defaultRoomAvailability = (RoomAvailability.getInstance() != null && RoomAvailability.getInstance() instanceof DefaultRoomAvailabilityService);

Assignment assignment = getAssignment(clazz);
Set<Long> ignorePermIds = new HashSet<Long>();
Expand All @@ -367,6 +409,7 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
for (Location room : assignment.getRooms()) {
if (room.isIgnoreRoomCheck()) {
ignorePermIds.add(room.getPermanentId());
} else if (defaultRoomAvailability) {
} else {
Collection<TimeBlock> times = RoomAvailability.getInstance().getRoomAvailability(
room.getUniqueId(),
Expand All @@ -391,7 +434,64 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
}
}

if (RoomAvailability.getInstance() != null && RoomAvailability.getInstance() instanceof DefaultRoomAvailabilityService) {
if (assignment != null && clazz.hasLeadInstructor() &&
(clazz.hasLeadInstructorWithUnavailabilities() || ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue())) {
Date[] bounds = DatePattern.getBounds(clazz.getSessionId());
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(new Date());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();
ClassTimeInfo period = new ClassTimeInfo(assignment);
for (ClassInstructor ci: clazz.getClassInstructors()) {
if (!ci.getLead()) continue;
if (RoomAvailability.getInstance() != null) {
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
ci.getInstructor().getUniqueId(),
bounds[0], bounds[1],
RoomAvailabilityInterface.sClassType);
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
if (overlaps != null)
conflicts.addAll(overlaps);
}
}
if (ci.getInstructor().hasUnavailabilities()) {
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
if (overlaps != null)
conflicts.addAll(overlaps);
}
}
}
}

if (defaultRoomAvailability) {
EventDateMapping.Class2EventDateMap class2eventDateMap = EventDateMapping.getMapping(clazz.getManagingDept().getSessionId());
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,19 @@ public boolean hasConflicts(Long offeringId) {
}
}
}

boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(new Date());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();

if (RoomAvailability.getInstance() != null) {
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(new Date());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();
for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
for (Class_ clazz: subpart.getClasses()) {
Expand Down Expand Up @@ -245,6 +246,54 @@ public boolean hasConflicts(Long offeringId) {
}
}

for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
for (Class_ clazz: subpart.getClasses()) {
if (clazz.isCancelled() || !clazz.hasLeadInstructor()) continue;
if (!clazz.hasLeadInstructorWithUnavailabilities() && !ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue()) continue;
Assignment assignment = getAssignment(clazz);
if (assignment == null) continue;
ClassTimeInfo period = new ClassTimeInfo(assignment);
for (ClassInstructor ci: clazz.getClassInstructors()) {
if (!ci.getLead()) continue;
if (RoomAvailability.getInstance() != null) {
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
ci.getInstructor().getUniqueId(),
bounds[0], bounds[1],
RoomAvailabilityInterface.sClassType);
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
if (period.overlaps(timesToCheck) != null) return true;
}
}
if (ci.getInstructor().hasUnavailabilities()) {
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
if (period.overlaps(timesToCheck) != null) return true;
}
}
}
}

return false;
}

Expand Down Expand Up @@ -383,6 +432,63 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
}
}

if (assignment != null && clazz.hasLeadInstructor() &&
(clazz.hasLeadInstructorWithUnavailabilities() || ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue())) {
Date[] bounds = DatePattern.getBounds(clazz.getSessionId());
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(new Date());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date today = cal.getTime();
ClassTimeInfo period = new ClassTimeInfo(assignment);
for (ClassInstructor ci: clazz.getClassInstructors()) {
if (!ci.getLead()) continue;
if (RoomAvailability.getInstance() != null) {
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
ci.getInstructor().getUniqueId(),
bounds[0], bounds[1],
RoomAvailabilityInterface.sClassType);
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
if (overlaps != null)
conflicts.addAll(overlaps);
}
}
if (ci.getInstructor().hasUnavailabilities()) {
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
if (times != null && !times.isEmpty()) {
Collection<TimeBlock> timesToCheck = null;
if (!changePast || ignorePast) {
timesToCheck = new Vector();
for (TimeBlock time: times) {
if (!time.getEndTime().before(today))
timesToCheck.add(time);
}
} else {
timesToCheck = times;
}
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
if (overlaps != null)
conflicts.addAll(overlaps);
}
}
}
}

return conflicts;
}
}
6 changes: 5 additions & 1 deletion WebContent/help/Release-Notes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,17 @@
<item>
<name>Instructor Unavailable Dates</name>
<description>
<line>Course Timetabling Solver: consider instructor's department when checking for the unavailable dates.
<line>Course Timetabling Solver: Consider instructor's department when checking for the unavailable dates.
<line>This is to allow for an instructor to have different unavailable dates on each of the departments they are teaching.</line>
</line>
<line>Class Assignment page: Do not list class times during which the instructor is not available.
<line>Either because they have Instructor Unavailable Dates set.</line>
<line>Or because instructor event availability is enabled (unitime.events.instructorUnavailability=true).</line>
</line>
<line>Instructional Offering Detail, Class Detail, Instructor Detail: Include instructor availability in the conflict checking.
<line>That is, show a class conflicting warning when the class is placed at a time during which the instructor is not available
due to an event, or unavailable dates.</line>
</line>
</description>
</item>
</category>
Expand Down

0 comments on commit da39257

Please sign in to comment.