Skip to content

Commit da39257

Browse files
committed
Instructional Offering Detail, Class Detail, Instructor Detail: Instructor 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
1 parent b2ca8cf commit da39257

File tree

4 files changed

+252
-30
lines changed

4 files changed

+252
-30
lines changed

JavaSource/org/unitime/timetable/model/Class_.java

+12
Original file line numberDiff line numberDiff line change
@@ -1984,5 +1984,17 @@ public boolean hasRoomIndexedPrefs() {
19841984
if (p.getRoomIndex() != null && p.getRoomIndex() < getNbrRooms()) return true;
19851985
return false;
19861986
}
1987+
1988+
public boolean hasLeadInstructor() {
1989+
for (ClassInstructor ci: getClassInstructors())
1990+
if (ci.isLead()) return true;
1991+
return false;
1992+
}
1993+
1994+
public boolean hasLeadInstructorWithUnavailabilities() {
1995+
for (ClassInstructor ci: getClassInstructors())
1996+
if (ci.isLead() && ci.getInstructor().hasUnavailabilities()) return true;
1997+
return false;
1998+
}
19871999

19882000
}

JavaSource/org/unitime/timetable/solver/CommitedClassAssignmentProxy.java

+119-19
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,19 @@ public boolean hasConflicts(Long offeringId) {
185185
}
186186
}
187187

188+
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
189+
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
190+
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
191+
Calendar cal = Calendar.getInstance(Locale.US);
192+
cal.setTime(new Date());
193+
cal.set(Calendar.HOUR_OF_DAY, 0);
194+
cal.set(Calendar.MINUTE, 0);
195+
cal.set(Calendar.SECOND, 0);
196+
cal.set(Calendar.MILLISECOND, 0);
197+
Date today = cal.getTime();
198+
188199
if (RoomAvailability.getInstance() != null && RoomAvailability.getInstance() instanceof DefaultRoomAvailabilityService) {
189-
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
190-
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
191200
if (!changePast || ignorePast) {
192-
Calendar cal = Calendar.getInstance(Localization.getJavaLocale());
193-
cal.set(Calendar.HOUR_OF_DAY, 0);
194-
cal.set(Calendar.MINUTE, 0);
195-
cal.set(Calendar.SECOND, 0);
196-
cal.set(Calendar.MILLISECOND, 0);
197-
Date today = cal.getTime();
198201
return MeetingDAO.getInstance().getSession().createQuery(
199202
"select count(mx) from ClassEvent e inner join e.meetings m, Meeting mx inner join mx.event ex " +
200203
"where e.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.uniqueId = :offeringId and type(ex) != ClassEvent and m.approvalStatus = 1 and mx.approvalStatus = 1 and " +
@@ -210,16 +213,6 @@ public boolean hasConflicts(Long offeringId) {
210213
).setParameter("offeringId", offeringId).setCacheable(true).uniqueResult().intValue() > 0;
211214
}
212215
} else if (RoomAvailability.getInstance() != null) {
213-
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
214-
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
215-
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
216-
Calendar cal = Calendar.getInstance(Locale.US);
217-
cal.setTime(new Date());
218-
cal.set(Calendar.HOUR_OF_DAY, 0);
219-
cal.set(Calendar.MINUTE, 0);
220-
cal.set(Calendar.SECOND, 0);
221-
cal.set(Calendar.MILLISECOND, 0);
222-
Date today = cal.getTime();
223216
for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
224217
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
225218
for (Class_ clazz: subpart.getClasses()) {
@@ -252,6 +245,54 @@ public boolean hasConflicts(Long offeringId) {
252245
}
253246
}
254247

248+
for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
249+
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
250+
for (Class_ clazz: subpart.getClasses()) {
251+
if (clazz.isCancelled() || !clazz.hasLeadInstructor()) continue;
252+
if (!clazz.hasLeadInstructorWithUnavailabilities() && !ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue()) continue;
253+
Assignment assignment = getAssignment(clazz);
254+
if (assignment == null) continue;
255+
ClassTimeInfo period = new ClassTimeInfo(assignment);
256+
for (ClassInstructor ci: clazz.getClassInstructors()) {
257+
if (!ci.getLead()) continue;
258+
if (RoomAvailability.getInstance() != null) {
259+
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
260+
ci.getInstructor().getUniqueId(),
261+
bounds[0], bounds[1],
262+
RoomAvailabilityInterface.sClassType);
263+
if (times != null && !times.isEmpty()) {
264+
Collection<TimeBlock> timesToCheck = null;
265+
if (!changePast || ignorePast) {
266+
timesToCheck = new Vector();
267+
for (TimeBlock time: times) {
268+
if (!time.getEndTime().before(today))
269+
timesToCheck.add(time);
270+
}
271+
} else {
272+
timesToCheck = times;
273+
}
274+
if (period.overlaps(timesToCheck) != null) return true;
275+
}
276+
}
277+
if (ci.getInstructor().hasUnavailabilities()) {
278+
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
279+
if (times != null && !times.isEmpty()) {
280+
Collection<TimeBlock> timesToCheck = null;
281+
if (!changePast || ignorePast) {
282+
timesToCheck = new Vector();
283+
for (TimeBlock time: times) {
284+
if (!time.getEndTime().before(today))
285+
timesToCheck.add(time);
286+
}
287+
} else {
288+
timesToCheck = times;
289+
}
290+
if (period.overlaps(timesToCheck) != null) return true;
291+
}
292+
}
293+
}
294+
}
295+
255296
return false;
256297
}
257298

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

352394
Assignment assignment = getAssignment(clazz);
353395
Set<Long> ignorePermIds = new HashSet<Long>();
@@ -367,6 +409,7 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
367409
for (Location room : assignment.getRooms()) {
368410
if (room.isIgnoreRoomCheck()) {
369411
ignorePermIds.add(room.getPermanentId());
412+
} else if (defaultRoomAvailability) {
370413
} else {
371414
Collection<TimeBlock> times = RoomAvailability.getInstance().getRoomAvailability(
372415
room.getUniqueId(),
@@ -391,7 +434,64 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
391434
}
392435
}
393436

394-
if (RoomAvailability.getInstance() != null && RoomAvailability.getInstance() instanceof DefaultRoomAvailabilityService) {
437+
if (assignment != null && clazz.hasLeadInstructor() &&
438+
(clazz.hasLeadInstructorWithUnavailabilities() || ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue())) {
439+
Date[] bounds = DatePattern.getBounds(clazz.getSessionId());
440+
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
441+
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
442+
Calendar cal = Calendar.getInstance(Locale.US);
443+
cal.setTime(new Date());
444+
cal.set(Calendar.HOUR_OF_DAY, 0);
445+
cal.set(Calendar.MINUTE, 0);
446+
cal.set(Calendar.SECOND, 0);
447+
cal.set(Calendar.MILLISECOND, 0);
448+
Date today = cal.getTime();
449+
ClassTimeInfo period = new ClassTimeInfo(assignment);
450+
for (ClassInstructor ci: clazz.getClassInstructors()) {
451+
if (!ci.getLead()) continue;
452+
if (RoomAvailability.getInstance() != null) {
453+
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
454+
ci.getInstructor().getUniqueId(),
455+
bounds[0], bounds[1],
456+
RoomAvailabilityInterface.sClassType);
457+
if (times != null && !times.isEmpty()) {
458+
Collection<TimeBlock> timesToCheck = null;
459+
if (!changePast || ignorePast) {
460+
timesToCheck = new Vector();
461+
for (TimeBlock time: times) {
462+
if (!time.getEndTime().before(today))
463+
timesToCheck.add(time);
464+
}
465+
} else {
466+
timesToCheck = times;
467+
}
468+
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
469+
if (overlaps != null)
470+
conflicts.addAll(overlaps);
471+
}
472+
}
473+
if (ci.getInstructor().hasUnavailabilities()) {
474+
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
475+
if (times != null && !times.isEmpty()) {
476+
Collection<TimeBlock> timesToCheck = null;
477+
if (!changePast || ignorePast) {
478+
timesToCheck = new Vector();
479+
for (TimeBlock time: times) {
480+
if (!time.getEndTime().before(today))
481+
timesToCheck.add(time);
482+
}
483+
} else {
484+
timesToCheck = times;
485+
}
486+
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
487+
if (overlaps != null)
488+
conflicts.addAll(overlaps);
489+
}
490+
}
491+
}
492+
}
493+
494+
if (defaultRoomAvailability) {
395495
EventDateMapping.Class2EventDateMap class2eventDateMap = EventDateMapping.getMapping(clazz.getManagingDept().getSessionId());
396496
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
397497
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();

JavaSource/org/unitime/timetable/solver/SolutionClassAssignmentProxy.java

+116-10
Original file line numberDiff line numberDiff line change
@@ -202,18 +202,19 @@ public boolean hasConflicts(Long offeringId) {
202202
}
203203
}
204204
}
205+
206+
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
207+
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
208+
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
209+
Calendar cal = Calendar.getInstance(Locale.US);
210+
cal.setTime(new Date());
211+
cal.set(Calendar.HOUR_OF_DAY, 0);
212+
cal.set(Calendar.MINUTE, 0);
213+
cal.set(Calendar.SECOND, 0);
214+
cal.set(Calendar.MILLISECOND, 0);
215+
Date today = cal.getTime();
205216

206217
if (RoomAvailability.getInstance() != null) {
207-
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
208-
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
209-
Date[] bounds = DatePattern.getBounds(offering.getSessionId());
210-
Calendar cal = Calendar.getInstance(Locale.US);
211-
cal.setTime(new Date());
212-
cal.set(Calendar.HOUR_OF_DAY, 0);
213-
cal.set(Calendar.MINUTE, 0);
214-
cal.set(Calendar.SECOND, 0);
215-
cal.set(Calendar.MILLISECOND, 0);
216-
Date today = cal.getTime();
217218
for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
218219
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
219220
for (Class_ clazz: subpart.getClasses()) {
@@ -245,6 +246,54 @@ public boolean hasConflicts(Long offeringId) {
245246
}
246247
}
247248

249+
for (InstrOfferingConfig config: offering.getInstrOfferingConfigs())
250+
for (SchedulingSubpart subpart: config.getSchedulingSubparts())
251+
for (Class_ clazz: subpart.getClasses()) {
252+
if (clazz.isCancelled() || !clazz.hasLeadInstructor()) continue;
253+
if (!clazz.hasLeadInstructorWithUnavailabilities() && !ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue()) continue;
254+
Assignment assignment = getAssignment(clazz);
255+
if (assignment == null) continue;
256+
ClassTimeInfo period = new ClassTimeInfo(assignment);
257+
for (ClassInstructor ci: clazz.getClassInstructors()) {
258+
if (!ci.getLead()) continue;
259+
if (RoomAvailability.getInstance() != null) {
260+
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
261+
ci.getInstructor().getUniqueId(),
262+
bounds[0], bounds[1],
263+
RoomAvailabilityInterface.sClassType);
264+
if (times != null && !times.isEmpty()) {
265+
Collection<TimeBlock> timesToCheck = null;
266+
if (!changePast || ignorePast) {
267+
timesToCheck = new Vector();
268+
for (TimeBlock time: times) {
269+
if (!time.getEndTime().before(today))
270+
timesToCheck.add(time);
271+
}
272+
} else {
273+
timesToCheck = times;
274+
}
275+
if (period.overlaps(timesToCheck) != null) return true;
276+
}
277+
}
278+
if (ci.getInstructor().hasUnavailabilities()) {
279+
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
280+
if (times != null && !times.isEmpty()) {
281+
Collection<TimeBlock> timesToCheck = null;
282+
if (!changePast || ignorePast) {
283+
timesToCheck = new Vector();
284+
for (TimeBlock time: times) {
285+
if (!time.getEndTime().before(today))
286+
timesToCheck.add(time);
287+
}
288+
} else {
289+
timesToCheck = times;
290+
}
291+
if (period.overlaps(timesToCheck) != null) return true;
292+
}
293+
}
294+
}
295+
}
296+
248297
return false;
249298
}
250299

@@ -383,6 +432,63 @@ public Set<TimeBlock> getConflictingTimeBlocks(Long classId) {
383432
}
384433
}
385434

435+
if (assignment != null && clazz.hasLeadInstructor() &&
436+
(clazz.hasLeadInstructorWithUnavailabilities() || ApplicationProperty.RoomAvailabilityIncludeInstructors.isTrue())) {
437+
Date[] bounds = DatePattern.getBounds(clazz.getSessionId());
438+
boolean changePast = ApplicationProperty.ClassAssignmentChangePastMeetings.isTrue();
439+
boolean ignorePast = ApplicationProperty.ClassAssignmentIgnorePastMeetings.isTrue();
440+
Calendar cal = Calendar.getInstance(Locale.US);
441+
cal.setTime(new Date());
442+
cal.set(Calendar.HOUR_OF_DAY, 0);
443+
cal.set(Calendar.MINUTE, 0);
444+
cal.set(Calendar.SECOND, 0);
445+
cal.set(Calendar.MILLISECOND, 0);
446+
Date today = cal.getTime();
447+
ClassTimeInfo period = new ClassTimeInfo(assignment);
448+
for (ClassInstructor ci: clazz.getClassInstructors()) {
449+
if (!ci.getLead()) continue;
450+
if (RoomAvailability.getInstance() != null) {
451+
Collection<TimeBlock> times = RoomAvailability.getInstance().getInstructorAvailability(
452+
ci.getInstructor().getUniqueId(),
453+
bounds[0], bounds[1],
454+
RoomAvailabilityInterface.sClassType);
455+
if (times != null && !times.isEmpty()) {
456+
Collection<TimeBlock> timesToCheck = null;
457+
if (!changePast || ignorePast) {
458+
timesToCheck = new Vector();
459+
for (TimeBlock time: times) {
460+
if (!time.getEndTime().before(today))
461+
timesToCheck.add(time);
462+
}
463+
} else {
464+
timesToCheck = times;
465+
}
466+
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
467+
if (overlaps != null)
468+
conflicts.addAll(overlaps);
469+
}
470+
}
471+
if (ci.getInstructor().hasUnavailabilities()) {
472+
Collection<TimeBlock> times = ci.getInstructor().listUnavailableDays();
473+
if (times != null && !times.isEmpty()) {
474+
Collection<TimeBlock> timesToCheck = null;
475+
if (!changePast || ignorePast) {
476+
timesToCheck = new Vector();
477+
for (TimeBlock time: times) {
478+
if (!time.getEndTime().before(today))
479+
timesToCheck.add(time);
480+
}
481+
} else {
482+
timesToCheck = times;
483+
}
484+
List<TimeBlock> overlaps = period.allOverlaps(timesToCheck);
485+
if (overlaps != null)
486+
conflicts.addAll(overlaps);
487+
}
488+
}
489+
}
490+
}
491+
386492
return conflicts;
387493
}
388494
}

WebContent/help/Release-Notes.xml

+5-1
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,17 @@
6565
<item>
6666
<name>Instructor Unavailable Dates</name>
6767
<description>
68-
<line>Course Timetabling Solver: consider instructor's department when checking for the unavailable dates.
68+
<line>Course Timetabling Solver: Consider instructor's department when checking for the unavailable dates.
6969
<line>This is to allow for an instructor to have different unavailable dates on each of the departments they are teaching.</line>
7070
</line>
7171
<line>Class Assignment page: Do not list class times during which the instructor is not available.
7272
<line>Either because they have Instructor Unavailable Dates set.</line>
7373
<line>Or because instructor event availability is enabled (unitime.events.instructorUnavailability=true).</line>
7474
</line>
75+
<line>Instructional Offering Detail, Class Detail, Instructor Detail: Include instructor availability in the conflict checking.
76+
<line>That is, show a class conflicting warning when the class is placed at a time during which the instructor is not available
77+
due to an event, or unavailable dates.</line>
78+
</line>
7579
</description>
7680
</item>
7781
</category>

0 commit comments

Comments
 (0)