Java tutorial
/* * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. * * The Apereo Foundation licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. * */ package org.unitime.timetable.solver; import java.util.ArrayList; import java.util.BitSet; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cpsolver.coursett.TimetableLoader; import org.cpsolver.coursett.constraint.ClassLimitConstraint; import org.cpsolver.coursett.constraint.DepartmentSpreadConstraint; import org.cpsolver.coursett.constraint.DiscouragedRoomConstraint; import org.cpsolver.coursett.constraint.GroupConstraint; import org.cpsolver.coursett.constraint.IgnoreStudentConflictsConstraint; import org.cpsolver.coursett.constraint.InstructorConstraint; import org.cpsolver.coursett.constraint.JenrlConstraint; import org.cpsolver.coursett.constraint.MinimizeNumberOfUsedGroupsOfTime; import org.cpsolver.coursett.constraint.MinimizeNumberOfUsedRoomsConstraint; import org.cpsolver.coursett.constraint.RoomConstraint; import org.cpsolver.coursett.constraint.SpreadConstraint; import org.cpsolver.coursett.constraint.FlexibleConstraint.FlexibleConstraintType; import org.cpsolver.coursett.model.Configuration; import org.cpsolver.coursett.model.Lecture; import org.cpsolver.coursett.model.Placement; import org.cpsolver.coursett.model.RoomLocation; import org.cpsolver.coursett.model.Student; import org.cpsolver.coursett.model.TimeLocation; import org.cpsolver.coursett.model.TimetableModel; import org.cpsolver.coursett.preference.MinMaxPreferenceCombination; import org.cpsolver.coursett.preference.PreferenceCombination; import org.cpsolver.coursett.preference.SumPreferenceCombination; import org.cpsolver.ifs.model.BinaryConstraint; import org.cpsolver.ifs.model.Constraint; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.ifs.util.Progress; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LazyInitializationException; import org.hibernate.Query; import org.hibernate.Transaction; import org.unitime.timetable.defaults.ApplicationProperty; import org.unitime.timetable.interfaces.RoomAvailabilityInterface; import org.unitime.timetable.interfaces.RoomAvailabilityInterface.TimeBlock; import org.unitime.timetable.model.AcademicClassification; import org.unitime.timetable.model.Assignment; import org.unitime.timetable.model.Building; import org.unitime.timetable.model.BuildingPref; import org.unitime.timetable.model.ClassDurationType; import org.unitime.timetable.model.ClassInstructor; import org.unitime.timetable.model.Class_; import org.unitime.timetable.model.CourseOffering; import org.unitime.timetable.model.CourseReservation; import org.unitime.timetable.model.CurriculumReservation; import org.unitime.timetable.model.DatePattern; import org.unitime.timetable.model.DatePatternPref; import org.unitime.timetable.model.Department; import org.unitime.timetable.model.DepartmentalInstructor; import org.unitime.timetable.model.DistributionObject; import org.unitime.timetable.model.DistributionPref; import org.unitime.timetable.model.ExactTimeMins; import org.unitime.timetable.model.InstrOfferingConfig; import org.unitime.timetable.model.InstructionalOffering; import org.unitime.timetable.model.Location; import org.unitime.timetable.model.PosMajor; import org.unitime.timetable.model.PreferenceLevel; import org.unitime.timetable.model.Reservation; import org.unitime.timetable.model.Room; import org.unitime.timetable.model.RoomFeature; import org.unitime.timetable.model.RoomFeaturePref; import org.unitime.timetable.model.RoomGroupPref; import org.unitime.timetable.model.RoomPref; import org.unitime.timetable.model.RoomSharingModel; import org.unitime.timetable.model.SchedulingSubpart; import org.unitime.timetable.model.Session; import org.unitime.timetable.model.Solution; import org.unitime.timetable.model.SolverGroup; import org.unitime.timetable.model.TimePattern; import org.unitime.timetable.model.TimePatternModel; import org.unitime.timetable.model.TimePref; import org.unitime.timetable.model.TravelTime; import org.unitime.timetable.model.DistributionPref.Structure; import org.unitime.timetable.model.comparators.ClassComparator; import org.unitime.timetable.model.comparators.InstrOfferingConfigComparator; import org.unitime.timetable.model.dao.AssignmentDAO; import org.unitime.timetable.model.dao.SchedulingSubpartDAO; import org.unitime.timetable.model.dao.SessionDAO; import org.unitime.timetable.model.dao.SolutionDAO; import org.unitime.timetable.model.dao.SolverGroupDAO; import org.unitime.timetable.model.dao.TimetableManagerDAO; import org.unitime.timetable.solver.course.weights.ClassWeightProvider; import org.unitime.timetable.solver.course.weights.DefaultClassWeights; import org.unitime.timetable.solver.curricula.LastLikeStudentCourseDemands; import org.unitime.timetable.solver.curricula.StudentCourseDemands; import org.unitime.timetable.solver.curricula.StudentCourseDemands.WeightedCourseOffering; import org.unitime.timetable.solver.curricula.StudentCourseDemands.WeightedStudentId; import org.unitime.timetable.solver.jgroups.SolverServerImplementation; import org.unitime.timetable.util.Constants; import org.unitime.timetable.util.DateUtils; import org.unitime.timetable.util.Formats; import org.unitime.timetable.util.RoomAvailability; import org.unitime.timetable.util.duration.DurationModel; /** * @author Tomas Muller */ public class TimetableDatabaseLoader extends TimetableLoader { private static Log sLog = LogFactory.getLog(TimetableDatabaseLoader.class); private Session iSession; private Long iSessionId; private Long[] iSolverGroupId; private String iSolverGroupIds; private String iDepartmentIds = null; private SolverGroup[] iSolverGroup; private Long[] iSolutionId; private long iFakeLectureId = 0; private Hashtable<Long, RoomConstraint> iRooms = new Hashtable<Long, RoomConstraint>(); private Hashtable<Object, InstructorConstraint> iInstructors = new Hashtable<Object, InstructorConstraint>(); private Hashtable<Long, Lecture> iLectures = new Hashtable<Long, Lecture>(); private Hashtable<Long, SchedulingSubpart> iSubparts = new Hashtable<Long, SchedulingSubpart>(); private Hashtable<Long, Student> iStudents = new Hashtable<Long, Student>(); private Hashtable<Long, String> iDeptNames = new Hashtable<Long, String>(); private Hashtable<Long, Class_> iClasses = new Hashtable<Long, Class_>(); private Set<DatePattern> iAllUsedDatePatterns = new HashSet<DatePattern>(); private Set<Class_> iAllClasses = null; private Hashtable<InstructionalOffering, List<Configuration>> iAltConfigurations = new Hashtable<InstructionalOffering, List<Configuration>>(); private Hashtable<InstructionalOffering, Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>>> iOfferings = new Hashtable<InstructionalOffering, Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>>>(); private Hashtable<CourseOffering, Set<Student>> iCourse2students = new Hashtable<CourseOffering, Set<Student>>(); private boolean iDeptBalancing = true; private boolean iSubjectBalancing = false; private boolean iMppAssignment = true; private boolean iInteractiveMode = false; private boolean iSpread = true; private boolean iAutoSameStudents = true; private String iAutoPrecedence = null; private boolean iLoadCommittedAssignments = false; private double iFewerSeatsDisouraged = 0.01; private double iFewerSeatsStronglyDisouraged = 0.02; private double iNormalizedPrefDecreaseFactor = TimePatternModel.sDefaultDecreaseFactor; private double iAlterTimePatternWeight = 0.0; private double iAlterDatePatternWeight = 1.0; private TimePatternModel iAlterTimePatternModel = (TimePatternModel) TimePattern.getDefaultRequiredTimeTable() .getModel(); private boolean iWeakenTimePreferences = false; private Progress iProgress = null; private boolean iLoadStudentEnrlsFromSolution = false; private boolean iFixMinPerWeek = false; private boolean iAssignSingleton = true; private boolean iIgnoreRoomSharing = false; private boolean iLoadStudentInstructorConflicts = false; private int iMaxRoomCombinations = 100; private String iAutoSameStudentsConstraint = "SAME_STUDENTS"; private String iInstructorFormat = null; private boolean iRoomAvailabilityTimeStampIsSet = false; private CommittedStudentConflictsMode iCommittedStudentConflictsMode = CommittedStudentConflictsMode.Load; private StudentCourseDemands iStudentCourseDemands = null; private ClassWeightProvider iClassWeightProvider = null; private boolean iUseAmPm = true; public static enum CommittedStudentConflictsMode { Ignore, Load, Compute }; public TimetableDatabaseLoader(TimetableModel model, org.cpsolver.ifs.assignment.Assignment<Lecture, Placement> assignment) { super(model, assignment); Progress.sTraceEnabled = false; iProgress = Progress.getInstance(model); iSessionId = model.getProperties().getPropertyLong("General.SessionId", (Long) null); iSolverGroupId = model.getProperties().getPropertyLongArry("General.SolverGroupId", null); iSolutionId = model.getProperties().getPropertyLongArry("General.SolutionId", null); iIgnoreRoomSharing = model.getProperties().getPropertyBoolean("General.IgnoreRoomSharing", iIgnoreRoomSharing); iSolverGroupIds = ""; if (iSolverGroupId != null) { for (int i = 0; i < iSolverGroupId.length; i++) { if (i > 0) iSolverGroupIds += ","; iSolverGroupIds += iSolverGroupId[i].toString(); } } iDeptBalancing = getModel().getProperties().getPropertyBoolean("General.DeptBalancing", iDeptBalancing); iSubjectBalancing = getModel().getProperties().getPropertyBoolean("General.SubjectBalancing", iSubjectBalancing); iSpread = getModel().getProperties().getPropertyBoolean("General.Spread", iSpread); iAutoSameStudents = getModel().getProperties().getPropertyBoolean("General.AutoSameStudents", iAutoSameStudents); iAutoPrecedence = getModel().getProperties().getProperty("General.AutoPrecedence"); iMppAssignment = getModel().getProperties().getPropertyBoolean("General.MPP", iMppAssignment); iInteractiveMode = getModel().getProperties().getPropertyBoolean("General.InteractiveMode", iInteractiveMode); iAssignSingleton = getModel().getProperties().getPropertyBoolean("General.AssignSingleton", iAssignSingleton); iMaxRoomCombinations = getModel().getProperties().getPropertyInt("General.MaxRoomCombinations", iMaxRoomCombinations); iFewerSeatsDisouraged = getModel().getProperties().getPropertyDouble("Global.FewerSeatsDisouraged", iFewerSeatsDisouraged); iFewerSeatsStronglyDisouraged = getModel().getProperties() .getPropertyDouble("Global.FewerSeatsStronglyDisouraged", iFewerSeatsStronglyDisouraged); iNormalizedPrefDecreaseFactor = getModel().getProperties() .getPropertyDouble("General.NormalizedPrefDecreaseFactor", iNormalizedPrefDecreaseFactor); iLoadStudentEnrlsFromSolution = getModel().getProperties() .getPropertyBoolean("Global.LoadStudentEnrlsFromSolution", iLoadStudentEnrlsFromSolution); iLoadStudentInstructorConflicts = getModel().getProperties() .getPropertyBoolean("Global.LoadStudentInstructorConflicts", iLoadStudentInstructorConflicts); iFixMinPerWeek = getModel().getProperties().getPropertyBoolean("Global.FixMinPerWeek", iFixMinPerWeek); iAlterTimePatternWeight = getModel().getProperties().getPropertyDouble("TimePreferences.Weight", iAlterTimePatternWeight); iAlterDatePatternWeight = getModel().getProperties() .getPropertyDouble("General.AlternativeDatePatternWeight", iAlterDatePatternWeight); iAlterTimePatternModel.setPreferences(getModel().getProperties().getProperty("TimePreferences.Pref", null)); iWeakenTimePreferences = getModel().getProperties().getPropertyBoolean("TimePreferences.Weaken", iWeakenTimePreferences); iAutoSameStudentsConstraint = getModel().getProperties().getProperty("General.AutoSameStudentsConstraint", iAutoSameStudentsConstraint); iInstructorFormat = getModel().getProperties().getProperty("General.InstructorFormat", DepartmentalInstructor.sNameFormatLastFist); try { String studentCourseDemandsClassName = getModel().getProperties().getProperty( "Curriculum.StudentCourseDemadsClass", LastLikeStudentCourseDemands.class.getName()); if (studentCourseDemandsClassName.indexOf(' ') >= 0) studentCourseDemandsClassName = studentCourseDemandsClassName.replace(" ", ""); if (studentCourseDemandsClassName.indexOf('.') < 0) studentCourseDemandsClassName = "org.unitime.timetable.solver.curricula." + studentCourseDemandsClassName; Class studentCourseDemandsClass = Class.forName(studentCourseDemandsClassName); iStudentCourseDemands = (StudentCourseDemands) studentCourseDemandsClass .getConstructor(DataProperties.class).newInstance(getModel().getProperties()); } catch (Exception e) { iProgress.message(msglevel("badStudentCourseDemands", Progress.MSGLEVEL_WARN), "Failed to load custom student course demands class, using last-like course demands instead.", e); iStudentCourseDemands = new LastLikeStudentCourseDemands(getModel().getProperties()); } getModel().getProperties().setProperty("General.SaveStudentEnrollments", iStudentCourseDemands.isMakingUpStudents() ? "false" : "true"); getModel().getProperties().setProperty("General.WeightStudents", iStudentCourseDemands.isWeightStudentsToFillUpOffering() ? "true" : "false"); iCommittedStudentConflictsMode = CommittedStudentConflictsMode.valueOf(getModel().getProperties() .getProperty("General.CommittedStudentConflicts", iCommittedStudentConflictsMode.name())); iLoadCommittedAssignments = getModel().getProperties() .getPropertyBoolean("General.LoadCommittedAssignments", iLoadCommittedAssignments); if (iCommittedStudentConflictsMode == CommittedStudentConflictsMode.Load && iStudentCourseDemands.isMakingUpStudents()) { iCommittedStudentConflictsMode = CommittedStudentConflictsMode.Compute; getModel().getProperties().setProperty("General.CommittedStudentConflicts", iCommittedStudentConflictsMode.name()); } if (iLoadStudentEnrlsFromSolution && iStudentCourseDemands.isMakingUpStudents()) { iLoadStudentEnrlsFromSolution = false; getModel().getProperties().setProperty("Global.LoadStudentEnrlsFromSolution", "false"); } if (iLoadStudentInstructorConflicts && iStudentCourseDemands.isMakingUpStudents()) { iLoadStudentInstructorConflicts = false; getModel().getProperties().setProperty("Global.LoadStudentInstructorConflicts", "false"); } try { String classWeightProviderClassName = getModel().getProperties() .getProperty("ClassWeightProvider.Class", DefaultClassWeights.class.getName()); if (classWeightProviderClassName.indexOf(' ') >= 0) classWeightProviderClassName = classWeightProviderClassName.replace(" ", ""); if (classWeightProviderClassName.indexOf('.') < 0) classWeightProviderClassName = "org.unitime.timetable.solver.course.weights." + classWeightProviderClassName; Class classWeightProviderClass = Class.forName(classWeightProviderClassName); iClassWeightProvider = (ClassWeightProvider) classWeightProviderClass .getConstructor(DataProperties.class).newInstance(getModel().getProperties()); } catch (Exception e) { iProgress.message(msglevel("badClassWeightProvider", Progress.MSGLEVEL_WARN), "Failed to load custom class weight provider, using the default one instead.", e); iClassWeightProvider = new DefaultClassWeights(getModel().getProperties()); } iUseAmPm = getModel().getProperties().getPropertyBoolean("General.UseAmPm", iUseAmPm); } public int msglevel(String type, int defaultLevel) { String level = ApplicationProperty.SolverLogLevel.value(type); if (level == null) return defaultLevel; if ("warn".equalsIgnoreCase(level)) return Progress.MSGLEVEL_WARN; if ("error".equalsIgnoreCase(level)) return Progress.MSGLEVEL_ERROR; if ("fatal".equalsIgnoreCase(level)) return Progress.MSGLEVEL_FATAL; if ("info".equalsIgnoreCase(level)) return Progress.MSGLEVEL_INFO; if ("debug".equalsIgnoreCase(level)) return Progress.MSGLEVEL_DEBUG; if ("trace".equalsIgnoreCase(level)) return Progress.MSGLEVEL_TRACE; return defaultLevel; } private String getClassLabel(Class_ clazz) { return "<A href='classDetail.do?cid=" + clazz.getUniqueId() + "'>" + clazz.getClassLabel() + "</A>"; } private String getClassLabel(Lecture lecture) { return "<A href='classDetail.do?cid=" + lecture.getClassId() + "'>" + lecture.getName() + "</A>"; } private String getOfferingLabel(InstructionalOffering offering) { return "<A href='instructionalOfferingDetail.do?io=" + offering.getUniqueId() + "'>" + offering.getCourseName() + "</A>"; } private String getOfferingLabel(CourseOffering offering) { return "<A href='instructionalOfferingDetail.do?io=" + offering.getInstructionalOffering().getUniqueId() + "'>" + offering.getCourseName() + "</A>"; } private String getSubpartLabel(SchedulingSubpart subpart) { String suffix = subpart.getSchedulingSubpartSuffix(); return "<A href='schedulingSubpartDetail.do?ssuid=" + subpart.getUniqueId() + "'>" + subpart.getCourseName() + " " + subpart.getItypeDesc().trim() + (suffix == null || suffix.length() == 0 ? "" : " (" + suffix + ")") + "</A>"; } private Hashtable iRoomPreferences = null; private PreferenceLevel getRoomPreference(Long deptId, Long locationId) { if (iRoomPreferences == null) { iRoomPreferences = new Hashtable(); for (int i = 0; i < iSolverGroup.length; i++) { for (Iterator j = iSolverGroup[i].getDepartments().iterator(); j.hasNext();) { Department department = (Department) j.next(); Hashtable roomPreferencesThisDept = new Hashtable(); iRoomPreferences.put(department.getUniqueId(), roomPreferencesThisDept); for (Iterator k = department.getPreferences(RoomPref.class).iterator(); k.hasNext();) { RoomPref pref = (RoomPref) k.next(); roomPreferencesThisDept.put(pref.getRoom().getUniqueId(), pref.getPrefLevel()); } } } } Hashtable roomPreferencesThisDept = (Hashtable) iRoomPreferences.get(deptId); if (roomPreferencesThisDept == null) return null; return (PreferenceLevel) roomPreferencesThisDept.get(locationId); } public static List<RoomLocation> computeRoomLocations(Class_ clazz) { return computeRoomLocations(clazz, false, 0.01, 0.02); } public static List<RoomLocation> computeRoomLocations(Class_ clazz, boolean interactiveMode, double fewerSeatsDisouraged, double fewerSeatsStronglyDisouraged) { int minClassLimit = clazz.getExpectedCapacity().intValue(); int maxClassLimit = clazz.getMaxExpectedCapacity().intValue(); if (maxClassLimit < minClassLimit) maxClassLimit = minClassLimit; float room2limitRatio = clazz.getRoomRatio().floatValue(); int roomCapacity = Math.round(minClassLimit <= 0 ? room2limitRatio : room2limitRatio * minClassLimit); int discouragedCapacity = (int) Math.round((1.0 - fewerSeatsStronglyDisouraged) * roomCapacity); int stronglyDiscouragedCapacity = (int) Math.round((1.0 - fewerSeatsStronglyDisouraged) * roomCapacity); List<RoomLocation> roomLocations = new ArrayList<RoomLocation>(); boolean reqRoom = false; boolean reqBldg = false; boolean reqGroup = false; Set allRooms = clazz.getAvailableRooms(); Set groupPrefs = clazz.effectivePreferences(RoomGroupPref.class); Set roomPrefs = clazz.effectivePreferences(RoomPref.class); Set bldgPrefs = clazz.effectivePreferences(BuildingPref.class); Set featurePrefs = clazz.effectivePreferences(RoomFeaturePref.class); for (Iterator i1 = allRooms.iterator(); i1.hasNext();) { Location room = (Location) i1.next(); boolean add = true; PreferenceCombination pref = new SumPreferenceCombination(); // --- group preference ---------- PreferenceCombination groupPref = PreferenceCombination.getDefault(); for (Iterator i2 = groupPrefs.iterator(); i2.hasNext();) { RoomGroupPref p = (RoomGroupPref) i2.next(); if (p.getRoomGroup().getRooms().contains(room)) groupPref.addPreferenceProlog(p.getPrefLevel().getPrefProlog()); } if (groupPref.getPreferenceProlog().equals(PreferenceLevel.sProhibited)) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (reqGroup && !groupPref.getPreferenceProlog().equals(PreferenceLevel.sRequired)) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (!reqGroup && (groupPref.getPreferenceProlog().equals(PreferenceLevel.sRequired))) { reqGroup = true; if (interactiveMode) { for (RoomLocation r : roomLocations) { r.setPreference(r.getPreference() + 100); } } else roomLocations.clear(); } if (!groupPref.getPreferenceProlog().equals(PreferenceLevel.sProhibited) && !groupPref.getPreferenceProlog().equals(PreferenceLevel.sRequired)) pref.addPreferenceProlog(groupPref.getPreferenceProlog()); // --- room preference ------------ String roomPref = null; PreferenceLevel roomPreference = null; for (Iterator k = clazz.getManagingDept().getPreferences(RoomPref.class).iterator(); k.hasNext();) { RoomPref x = (RoomPref) k.next(); if (room.equals(x.getRoom())) roomPreference = x.getPrefLevel(); } if (roomPreference != null) { roomPref = roomPreference.getPrefProlog(); if (PreferenceLevel.sProhibited.equals(roomPref)) { add = false; } if (PreferenceLevel.sStronglyDiscouraged.equals(roomPref)) { roomPref = PreferenceLevel.sProhibited; } } for (Iterator i2 = roomPrefs.iterator(); i2.hasNext();) { RoomPref p = (RoomPref) i2.next(); if (room.equals(p.getRoom())) { roomPref = p.getPrefLevel().getPrefProlog(); break; } } if (roomPref != null && roomPref.equals(PreferenceLevel.sProhibited)) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (reqRoom && (roomPref == null || !roomPref.equals(PreferenceLevel.sRequired))) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (!reqRoom && (roomPref != null && roomPref.equals(PreferenceLevel.sRequired))) { reqRoom = true; if (interactiveMode) { for (RoomLocation r : roomLocations) { r.setPreference(r.getPreference() + 100); } } else roomLocations.clear(); } if (roomPref != null && !roomPref.equals(PreferenceLevel.sProhibited) && !roomPref.equals(PreferenceLevel.sRequired)) pref.addPreferenceProlog(roomPref); // --- building preference ------------ Building bldg = (room instanceof Room ? ((Room) room).getBuilding() : null); String bldgPref = null; for (Iterator i2 = bldgPrefs.iterator(); i2.hasNext();) { BuildingPref p = (BuildingPref) i2.next(); if (bldg != null && bldg.equals(p.getBuilding())) { bldgPref = p.getPrefLevel().getPrefProlog(); break; } } if (bldgPref != null && bldgPref.equals(PreferenceLevel.sProhibited)) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (reqBldg && (bldgPref == null || !bldgPref.equals(PreferenceLevel.sRequired))) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (!reqBldg && (bldgPref != null && bldgPref.equals(PreferenceLevel.sRequired))) { reqBldg = true; if (interactiveMode) { for (RoomLocation r : roomLocations) { r.setPreference(r.getPreference() + 100); } } else roomLocations.clear(); } if (bldgPref != null && !bldgPref.equals(PreferenceLevel.sProhibited) && !bldgPref.equals(PreferenceLevel.sRequired)) pref.addPreferenceProlog(bldgPref); // --- room features preference -------- boolean acceptableFeatures = true; PreferenceCombination featurePref = new MinMaxPreferenceCombination(); for (Iterator i2 = featurePrefs.iterator(); i2.hasNext();) { RoomFeaturePref roomFeaturePref = (RoomFeaturePref) i2.next(); RoomFeature feature = roomFeaturePref.getRoomFeature(); String p = roomFeaturePref.getPrefLevel().getPrefProlog(); boolean hasFeature = feature.getRooms().contains(room); if (p.equals(PreferenceLevel.sProhibited) && hasFeature) { acceptableFeatures = false; } if (p.equals(PreferenceLevel.sRequired) && !hasFeature) { acceptableFeatures = false; } if (p != null && hasFeature && !p.equals(PreferenceLevel.sProhibited) && !p.equals(PreferenceLevel.sRequired)) featurePref.addPreferenceProlog(p); } pref.addPreferenceInt(featurePref.getPreferenceInt()); if (!acceptableFeatures) { if (interactiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } // --- room size ----------------- if (room.getCapacity().intValue() < stronglyDiscouragedCapacity) { if (interactiveMode) pref.addPreferenceInt(1000); else add = false; } else if (room.getCapacity().intValue() < discouragedCapacity) { pref.addPreferenceProlog(PreferenceLevel.sStronglyDiscouraged); } else if (room.getCapacity().intValue() < roomCapacity) { pref.addPreferenceProlog(PreferenceLevel.sDiscouraged); } int prefInt = pref.getPreferenceInt(); if (!add) continue; roomLocations.add(new RoomLocation(room.getUniqueId(), room.getLabel(), (bldg == null ? null : bldg.getUniqueId()), prefInt, room.getCapacity().intValue(), room.getCoordinateX(), room.getCoordinateY(), (room.isIgnoreTooFar() == null ? false : room.isIgnoreTooFar().booleanValue()), null)); } return roomLocations; } private Lecture loadClass(Class_ clazz, org.hibernate.Session hibSession) { if (clazz.isCancelled()) { iProgress.message(msglevel("cancelledClass", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " is cancelled (class not loaded)."); return null; } List<TimeLocation> timeLocations = new ArrayList<TimeLocation>(); List<RoomLocation> roomLocations = new ArrayList<RoomLocation>(); iProgress.message(msglevel("loadingClass", Progress.MSGLEVEL_DEBUG), "loading class " + getClassLabel(clazz)); Department dept = clazz.getControllingDept(); iDeptNames.put(dept.getUniqueId(), dept.getShortLabel()); iProgress.trace("department: " + dept.getName() + " (id:" + dept.getUniqueId() + ")"); int minClassLimit = clazz.getExpectedCapacity().intValue(); int maxClassLimit = clazz.getMaxExpectedCapacity().intValue(); if (maxClassLimit < minClassLimit) maxClassLimit = minClassLimit; if (clazz.getSchedulingSubpart().getInstrOfferingConfig().isUnlimitedEnrollment()) minClassLimit = maxClassLimit = Integer.MAX_VALUE; float room2limitRatio = clazz.getRoomRatio().floatValue(); int roomCapacity = Math.round(minClassLimit <= 0 ? room2limitRatio : room2limitRatio * minClassLimit); iProgress.trace("class limit: [" + minClassLimit + "," + maxClassLimit + "]"); iProgress.trace("room2limitRatio: " + room2limitRatio); iProgress.trace("room capacity: " + roomCapacity); int discouragedCapacity = (int) Math.round((1.0 - iFewerSeatsDisouraged) * roomCapacity); iProgress.trace("discouraged capacity: " + discouragedCapacity); int stronglyDiscouragedCapacity = (int) Math.round((1.0 - iFewerSeatsStronglyDisouraged) * roomCapacity); iProgress.trace("strongly discouraged capacity: " + stronglyDiscouragedCapacity); Set timePrefs = clazz.effectivePreferences(TimePref.class); if (timePrefs.isEmpty()) { if (clazz.getSchedulingSubpart().getMinutesPerWk().intValue() != 0) { DurationModel dm = clazz.getSchedulingSubpart().getInstrOfferingConfig().getDurationModel(); iProgress.message(msglevel("noTimePattern", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " has no time pattern selected (class not loaded). <i>If not changed, this class will be treated as Arrange " + dm.getArrangedHours(clazz.getSchedulingSubpart().getMinutesPerWk(), clazz.effectiveDatePattern()) + " Hours.</i>"); } return null; } Set patterns = new HashSet(); DatePattern datePattern = clazz.effectiveDatePattern(); if (datePattern == null) { iProgress.message(msglevel("noDatePattern", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " has no date pattern selected (class not loaded)."); return null; } iAllUsedDatePatterns.add(datePattern); int nrRooms = (clazz.getNbrRooms() == null ? 1 : clazz.getNbrRooms().intValue()); Set groupPrefs = clazz.effectivePreferences(RoomGroupPref.class); Set roomPrefs = clazz.effectivePreferences(RoomPref.class); Set bldgPrefs = clazz.effectivePreferences(BuildingPref.class); Set featurePrefs = clazz.effectivePreferences(RoomFeaturePref.class); if (nrRooms > 0) { boolean reqRoom = false; boolean reqBldg = false; boolean reqGroup = false; Set allRooms = clazz.getAvailableRooms(); for (Iterator i1 = allRooms.iterator(); i1.hasNext();) { Location room = (Location) i1.next(); iProgress.trace("checking room " + room.getLabel() + " ..."); boolean add = true; PreferenceCombination pref = new SumPreferenceCombination(); // --- group preference ---------- PreferenceCombination groupPref = PreferenceCombination.getDefault(); for (Iterator i2 = groupPrefs.iterator(); i2.hasNext();) { RoomGroupPref p = (RoomGroupPref) i2.next(); if (p.getRoomGroup().getRooms().contains(room)) groupPref.addPreferenceProlog(p.getPrefLevel().getPrefProlog()); } if (groupPref.getPreferenceProlog().equals(PreferenceLevel.sProhibited)) { iProgress.trace("group is prohibited :-("); if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (reqGroup && !groupPref.getPreferenceProlog().equals(PreferenceLevel.sRequired)) { iProgress.trace("building is not required :-("); if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (!reqGroup && (groupPref.getPreferenceProlog().equals(PreferenceLevel.sRequired))) { iProgress.trace("group is required, removing all previous rooms (they are not required)"); reqGroup = true; if (iInteractiveMode) { for (RoomLocation r : roomLocations) { r.setPreference(r.getPreference() + 100); } } else roomLocations.clear(); } if (!groupPref.getPreferenceProlog().equals(PreferenceLevel.sProhibited) && !groupPref.getPreferenceProlog().equals(PreferenceLevel.sRequired)) pref.addPreferenceProlog(groupPref.getPreferenceProlog()); // --- room preference ------------ String roomPref = null; PreferenceLevel roomPreference = getRoomPreference(clazz.getManagingDept().getUniqueId(), room.getUniqueId()); if (roomPreference != null) { roomPref = roomPreference.getPrefProlog(); if (!iInteractiveMode && PreferenceLevel.sProhibited.equals(roomPref)) { iProgress.trace("room is prohibited (on room level) :-("); add = false; } if (PreferenceLevel.sStronglyDiscouraged.equals(roomPref)) { roomPref = PreferenceLevel.sProhibited; } } for (Iterator i2 = roomPrefs.iterator(); i2.hasNext();) { RoomPref p = (RoomPref) i2.next(); if (room.equals(p.getRoom())) { roomPref = p.getPrefLevel().getPrefProlog(); iProgress.trace("room preference is " + p.getPrefLevel().getPrefName()); break; } } if (roomPref != null && roomPref.equals(PreferenceLevel.sProhibited)) { iProgress.trace("room is prohibited :-("); if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (reqRoom && (roomPref == null || !roomPref.equals(PreferenceLevel.sRequired))) { iProgress.trace("room is not required :-("); if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (!reqRoom && (roomPref != null && roomPref.equals(PreferenceLevel.sRequired))) { iProgress.trace("room is required, removing all previous rooms (they are not required)"); reqRoom = true; if (iInteractiveMode) { for (RoomLocation r : roomLocations) { r.setPreference(r.getPreference() + 100); } } else roomLocations.clear(); } if (roomPref != null && !roomPref.equals(PreferenceLevel.sProhibited) && !roomPref.equals(PreferenceLevel.sRequired)) pref.addPreferenceProlog(roomPref); // --- building preference ------------ Building bldg = (room instanceof Room ? ((Room) room).getBuilding() : null); String bldgPref = null; for (Iterator i2 = bldgPrefs.iterator(); i2.hasNext();) { BuildingPref p = (BuildingPref) i2.next(); if (bldg != null && bldg.equals(p.getBuilding())) { bldgPref = p.getPrefLevel().getPrefProlog(); iProgress.trace("building preference is " + p.getPrefLevel().getPrefName()); break; } } if (bldgPref != null && bldgPref.equals(PreferenceLevel.sProhibited)) { iProgress.trace("building is prohibited :-("); if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (reqBldg && (bldgPref == null || !bldgPref.equals(PreferenceLevel.sRequired))) { iProgress.trace("building is not required :-("); if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } if (!reqBldg && (bldgPref != null && bldgPref.equals(PreferenceLevel.sRequired))) { iProgress.trace("building is required, removing all previous rooms (they are not required)"); reqBldg = true; if (iInteractiveMode) { for (RoomLocation r : roomLocations) { r.setPreference(r.getPreference() + 100); } } else roomLocations.clear(); } if (bldgPref != null && !bldgPref.equals(PreferenceLevel.sProhibited) && !bldgPref.equals(PreferenceLevel.sRequired)) pref.addPreferenceProlog(bldgPref); // --- room features preference -------- boolean acceptableFeatures = true; PreferenceCombination featurePref = new MinMaxPreferenceCombination(); for (Iterator i2 = featurePrefs.iterator(); i2.hasNext();) { RoomFeaturePref roomFeaturePref = (RoomFeaturePref) i2.next(); RoomFeature feature = roomFeaturePref.getRoomFeature(); String p = roomFeaturePref.getPrefLevel().getPrefProlog(); boolean hasFeature = feature.getRooms().contains(room); if (p.equals(PreferenceLevel.sProhibited) && hasFeature) { iProgress.trace("present feature " + feature.getLabel() + " is prohibited :-("); acceptableFeatures = false; } if (p.equals(PreferenceLevel.sRequired) && !hasFeature) { iProgress.trace("not present feature " + feature.getLabel() + " is required :-("); acceptableFeatures = false; } if (p != null && hasFeature && !p.equals(PreferenceLevel.sProhibited) && !p.equals(PreferenceLevel.sRequired)) featurePref.addPreferenceProlog(p); } pref.addPreferenceInt(featurePref.getPreferenceInt()); if (!acceptableFeatures) { if (iInteractiveMode) pref.addPreferenceProlog(PreferenceLevel.sProhibited); else add = false; } // --- room size ----------------- if (room.getCapacity().intValue() < stronglyDiscouragedCapacity) { iProgress.trace("too small :-("); if (iInteractiveMode) pref.addPreferenceInt(1000); else add = false; } else if (room.getCapacity().intValue() < discouragedCapacity) { iProgress.trace("room of strongly discouraged size"); pref.addPreferenceProlog(PreferenceLevel.sStronglyDiscouraged); } else if (room.getCapacity().intValue() < roomCapacity) { iProgress.trace("room of discouraged size"); pref.addPreferenceProlog(PreferenceLevel.sDiscouraged); } int prefInt = pref.getPreferenceInt(); iProgress.trace("room preference is " + prefInt); if (!add) continue; roomLocations.add(new RoomLocation(room.getUniqueId(), room.getLabel(), (bldg == null ? null : bldg.getUniqueId()), prefInt, room.getCapacity().intValue(), room.getCoordinateX(), room.getCoordinateY(), (room.isIgnoreTooFar() == null ? false : room.isIgnoreTooFar().booleanValue()), getRoomConstraint(clazz.getManagingDept(), room, hibSession))); } if (roomLocations.isEmpty() || roomLocations.size() < (clazz.getNbrRooms() == null ? 1 : clazz.getNbrRooms().intValue())) { iProgress.message(msglevel("noRoom", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " has no available room" + (clazz.getNbrRooms() != null && clazz.getNbrRooms().intValue() > 1 ? "s" : "") + " (class not loaded)."); return null; } } else { if (!groupPrefs.isEmpty() || !roomPrefs.isEmpty() || !bldgPrefs.isEmpty() || !featurePrefs.isEmpty()) iProgress.message(msglevel("zeroRoomsButPref", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " requires no room (number of rooms is set to zero), but it contains some room preferences."); } int minPerWeek = clazz.getSchedulingSubpart().getMinutesPerWk().intValue(); boolean onlyReq = false; for (Iterator i1 = timePrefs.iterator(); i1.hasNext();) { TimePref timePref = (TimePref) i1.next(); TimePatternModel pattern = timePref.getTimePatternModel(); if (pattern.isExactTime() || pattern.countPreferences(PreferenceLevel.sRequired) > 0) onlyReq = true; } if (onlyReq) { iProgress.trace("time pattern has requred times"); } ClassDurationType dtype = clazz.getSchedulingSubpart().getInstrOfferingConfig().getEffectiveDurationType(); DurationModel dm = clazz.getSchedulingSubpart().getInstrOfferingConfig().getDurationModel(); for (Iterator i1 = timePrefs.iterator(); i1.hasNext();) { TimePref timePref = (TimePref) i1.next(); TimePatternModel pattern = timePref.getTimePatternModel(); if (pattern.isExactTime()) { if (datePattern.getType() == DatePattern.sTypePatternSet) { Set<DatePatternPref> datePatternPrefs = (Set<DatePatternPref>) clazz .effectivePreferences(DatePatternPref.class); boolean hasReq = false; for (DatePatternPref p : datePatternPrefs) { if (PreferenceLevel.sRequired.equals(p.getPrefLevel().getPrefProlog())) { hasReq = true; break; } } for (DatePattern child : datePattern.findChildren()) { int minsPerMeeting = dm.getExactTimeMinutesPerMeeting( clazz.getSchedulingSubpart().getMinutesPerWk(), child, pattern.getExactDays()); int length = ExactTimeMins.getNrSlotsPerMtg(minsPerMeeting); int breakTime = ExactTimeMins.getBreakTime(minsPerMeeting); String pr = PreferenceLevel.sNeutral; for (DatePatternPref p : datePatternPrefs) { if (p.getDatePattern().equals(child)) pr = p.getPrefLevel().getPrefProlog(); } int prVal = 0; if (!PreferenceLevel.sNeutral.equals(pr) && !PreferenceLevel.sRequired.equals(pr)) { prVal = PreferenceLevel.prolog2int(pr); } if (iInteractiveMode) { if (hasReq && !PreferenceLevel.sRequired.equals(pr)) prVal += 100; if (PreferenceLevel.sProhibited.equals(pr)) prVal += 100; } else { if (hasReq && !PreferenceLevel.sRequired.equals(pr)) continue; if (PreferenceLevel.sProhibited.equals(pr)) continue; } TimeLocation loc = new TimeLocation(pattern.getExactDays(), pattern.getExactStartSlot(), length, PreferenceLevel.sIntLevelNeutral, 0, PreferenceLevel.prolog2int(pr), child.getUniqueId(), child.getName(), child.getPatternBitSet(), breakTime); loc.setTimePatternId(pattern.getTimePattern().getUniqueId()); if (!PreferenceLevel.sNeutral.equals(pr) && !PreferenceLevel.sRequired.equals(pr)) { loc.setNormalizedPreference(iAlterDatePatternWeight * prVal); } timeLocations.add(loc); } } else { int minsPerMeeting = dm.getExactTimeMinutesPerMeeting( clazz.getSchedulingSubpart().getMinutesPerWk(), datePattern, pattern.getExactDays()); int length = ExactTimeMins.getNrSlotsPerMtg(minsPerMeeting); int breakTime = ExactTimeMins.getBreakTime(minsPerMeeting); TimeLocation loc = new TimeLocation(pattern.getExactDays(), pattern.getExactStartSlot(), length, PreferenceLevel.sIntLevelNeutral, 0, datePattern.getUniqueId(), datePattern.getName(), datePattern.getPatternBitSet(), breakTime); loc.setTimePatternId(pattern.getTimePattern().getUniqueId()); timeLocations.add(loc); } continue; } patterns.add(pattern.getTimePattern()); if (iWeakenTimePreferences) { pattern.weakenHardPreferences(); onlyReq = false; } if (!dm.isValidCombination(clazz.getSchedulingSubpart().getMinutesPerWk(), datePattern, timePref.getTimePattern())) { iProgress.message(msglevel("noTimePattern", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " has " + clazz.getSchedulingSubpart().getMinutesPerWk() + " " + (dtype == null ? "minutes per week" : dtype.getLabel()) + " , but " + pattern.getName() + " time pattern selected."); minPerWeek = pattern.getMinPerMtg() * pattern.getNrMeetings(); if (iFixMinPerWeek) clazz.getSchedulingSubpart().setMinutesPerWk(new Integer(minPerWeek)); } for (int time = 0; time < pattern.getNrTimes(); time++) { if (pattern.getStartSlot(time) + pattern.getSlotsPerMtg() > Constants.SLOTS_PER_DAY) { iProgress.message(msglevel("timeOverMidnight", Progress.MSGLEVEL_WARN), "Time pattern " + pattern.getName() + " that is used by " + getClassLabel(clazz) + " has a time that goes over midnight. " + "This is not allowed and the time " + pattern.getStartTime(time) + " will be ignored."); continue; } for (int day = 0; day < pattern.getNrDays(); day++) { String pref = pattern.getPreference(day, time); iProgress.trace("checking time " + pattern.getDayHeader(day) + " " + pattern.getTimeHeaderShort(time) + " (" + pref + ")"); if (!dm.isValidSelection(clazz.getSchedulingSubpart().getMinutesPerWk(), datePattern, timePref.getTimePattern(), pattern.getDayCode(day))) { iProgress.trace("time is not valid :-("); continue; } if (!iInteractiveMode && pref.equals(PreferenceLevel.sProhibited)) { iProgress.trace("time is prohibited :-("); continue; } if (!iInteractiveMode && onlyReq && !pref.equals(PreferenceLevel.sRequired)) { iProgress.trace("time is not required :-("); continue; } if (datePattern.getType() == DatePattern.sTypePatternSet) { Set<DatePatternPref> datePatternPrefs = (Set<DatePatternPref>) clazz .effectivePreferences(DatePatternPref.class); boolean hasReq = false; for (DatePatternPref p : datePatternPrefs) { if (PreferenceLevel.sRequired.equals(p.getPrefLevel().getPrefProlog())) { hasReq = true; break; } } for (DatePattern child : datePattern.findChildren()) { if (!dm.isValidSelection(clazz.getSchedulingSubpart().getMinutesPerWk(), child, timePref.getTimePattern(), pattern.getDayCode(day))) continue; String pr = PreferenceLevel.sNeutral; for (DatePatternPref p : datePatternPrefs) { if (p.getDatePattern().equals(child)) pr = p.getPrefLevel().getPrefProlog(); } int prVal = 0; if (!PreferenceLevel.sNeutral.equals(pr) && !PreferenceLevel.sRequired.equals(pr)) { prVal = PreferenceLevel.prolog2int(pr); } if (iInteractiveMode) { if (hasReq && !PreferenceLevel.sRequired.equals(pr)) prVal += 100; if (PreferenceLevel.sProhibited.equals(pr)) prVal += 100; } else { if (hasReq && !PreferenceLevel.sRequired.equals(pr)) continue; if (PreferenceLevel.sProhibited.equals(pr)) continue; } TimeLocation loc = new TimeLocation(pattern.getDayCode(day), pattern.getStartSlot(time), pattern.getSlotsPerMtg(), PreferenceLevel.prolog2int(pattern.getPreference(day, time)), pattern.getNormalizedPreference(day, time, iNormalizedPrefDecreaseFactor), PreferenceLevel.prolog2int(pr), child.getUniqueId(), child.getName(), child.getPatternBitSet(), pattern.getBreakTime()); loc.setTimePatternId(pattern.getTimePattern().getUniqueId()); if (iAlterTimePatternWeight != 0.0) { String altPref = iAlterTimePatternModel.getCombinedPreference(loc.getDayCode(), loc.getStartSlot(), loc.getLength(), TimePatternModel.sMixAlgMinMax); if (!altPref.equals(PreferenceLevel.sNeutral)) { loc.setNormalizedPreference(loc.getNormalizedPreference() + iAlterTimePatternWeight * PreferenceLevel.prolog2int(altPref)); } } if (iInteractiveMode && onlyReq && !pref.equals(PreferenceLevel.sRequired)) { loc.setPreference(PreferenceLevel.sIntLevelProhibited); loc.setNormalizedPreference(PreferenceLevel.sIntLevelProhibited); } if (!PreferenceLevel.sNeutral.equals(pr) && !PreferenceLevel.sRequired.equals(pr)) { loc.setNormalizedPreference( loc.getNormalizedPreference() + iAlterDatePatternWeight * prVal); } timeLocations.add(loc); } } else { TimeLocation loc = new TimeLocation(pattern.getDayCode(day), pattern.getStartSlot(time), pattern.getSlotsPerMtg(), PreferenceLevel.prolog2int(pattern.getPreference(day, time)), pattern.getNormalizedPreference(day, time, iNormalizedPrefDecreaseFactor), datePattern.getUniqueId(), datePattern.getName(), datePattern.getPatternBitSet(), pattern.getBreakTime()); loc.setTimePatternId(pattern.getTimePattern().getUniqueId()); if (iAlterTimePatternWeight != 0.0) { String altPref = iAlterTimePatternModel.getCombinedPreference(loc.getDayCode(), loc.getStartSlot(), loc.getLength(), TimePatternModel.sMixAlgMinMax); if (!altPref.equals(PreferenceLevel.sNeutral)) { loc.setNormalizedPreference(loc.getNormalizedPreference() + iAlterTimePatternWeight * PreferenceLevel.prolog2int(altPref)); } } if (iInteractiveMode && onlyReq && !pref.equals(PreferenceLevel.sRequired)) { loc.setPreference(PreferenceLevel.sIntLevelProhibited); loc.setNormalizedPreference(PreferenceLevel.sIntLevelProhibited); } timeLocations.add(loc); } } } } if (iInteractiveMode) { for (TimePattern pattern : TimePattern.findApplicable(iSession, false, false, false, minPerWeek, datePattern, dm, clazz.getManagingDept())) { if (patterns.contains(pattern)) continue; TimePatternModel model = pattern.getTimePatternModel(); iProgress.trace("adding prohibited pattern " + model.getName()); for (int time = 0; time < model.getNrTimes(); time++) { for (int day = 0; day < model.getNrDays(); day++) { if (datePattern.getType() == DatePattern.sTypePatternSet) { Set<DatePatternPref> datePatternPrefs = (Set<DatePatternPref>) clazz .effectivePreferences(DatePatternPref.class); for (DatePattern child : datePattern.findChildren()) { if (!dm.isValidSelection(clazz.getSchedulingSubpart().getMinutesPerWk(), child, pattern, model.getDayCode(day))) continue; String pr = PreferenceLevel.sNeutral; for (DatePatternPref p : datePatternPrefs) if (p.getDatePattern().equals(child)) pr = p.getPrefLevel().getPrefProlog(); TimeLocation loc = new TimeLocation(model.getDayCode(day), model.getStartSlot(time), model.getSlotsPerMtg(), PreferenceLevel.prolog2int(model.getPreference(day, time)), model.getNormalizedPreference(day, time, iNormalizedPrefDecreaseFactor), PreferenceLevel.prolog2int(pr), child.getUniqueId(), child.getName(), child.getPatternBitSet(), model.getBreakTime()); loc.setTimePatternId(model.getTimePattern().getUniqueId()); loc.setPreference(1000); loc.setNormalizedPreference(1000.0); timeLocations.add(loc); } } else { if (!dm.isValidSelection(clazz.getSchedulingSubpart().getMinutesPerWk(), datePattern, pattern, model.getDayCode(day))) continue; TimeLocation loc = new TimeLocation(model.getDayCode(day), model.getStartSlot(time), model.getSlotsPerMtg(), PreferenceLevel.prolog2int(model.getPreference(day, time)), model.getNormalizedPreference(day, time, iNormalizedPrefDecreaseFactor), datePattern.getUniqueId(), datePattern.getName(), datePattern.getPatternBitSet(), model.getBreakTime()); loc.setTimePatternId(model.getTimePattern().getUniqueId()); loc.setPreference(1000); loc.setNormalizedPreference(1000.0); timeLocations.add(loc); } } } } } if (timeLocations.isEmpty()) { iProgress.message(msglevel("noTime", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(clazz) + " has no available time (class not loaded)."); return null; } List<DepartmentalInstructor> instructors = clazz.getLeadInstructors(); String className = clazz.getClassLabel(); Lecture lecture = new Lecture(clazz.getUniqueId(), clazz.getManagingDept().getSolverGroup().getUniqueId(), clazz.getSchedulingSubpart().getUniqueId(), className, timeLocations, roomLocations, nrRooms, null, minClassLimit, maxClassLimit, room2limitRatio); lecture.setNote(clazz.getNotes()); if (clazz.getManagingDept() != null) lecture.setScheduler(clazz.getManagingDept().getUniqueId()); lecture.setDepartment(dept.getUniqueId()); for (DepartmentalInstructor instructor : instructors) { getInstructorConstraint(instructor, hibSession).addVariable(lecture); } if (nrRooms > 1) lecture.setMaxRoomCombinations(iMaxRoomCombinations); long estNrValues = lecture.nrValues(); if (estNrValues > 1000000) { iProgress.message(msglevel("hugeDomain", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(lecture) + " has too many possible placements (" + estNrValues + "). " + "The class was not loaded in order to prevent out of memory exception. " + "Please restrict the number of available rooms and/or times for this class."); for (DepartmentalInstructor instructor : instructors) { getInstructorConstraint(instructor, hibSession).removeVariable(lecture); } return null; } else if (estNrValues > 10000) { iProgress.message(msglevel("bigDomain", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(lecture) + " has quite a lot of possible placements (" + estNrValues + "). " + "Solver may run too slow. " + "If possible, please restrict the number of available rooms and/or times for this class."); } if (lecture.values(getAssignment()).isEmpty()) { if (!iInteractiveMode) { iProgress.message(msglevel("noPlacement", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(lecture) + " has no available placement (class not loaded)."); for (DepartmentalInstructor instructor : instructors) { getInstructorConstraint(instructor, hibSession).removeVariable(lecture); } return null; } else iProgress.message(msglevel("noPlacement", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(lecture) + " has no available placement."); } if (iClassWeightProvider != null) lecture.setWeight(iClassWeightProvider.getWeight(lecture)); iLectures.put(clazz.getUniqueId(), lecture); getModel().addVariable(lecture); for (RoomLocation r : roomLocations) { r.getRoomConstraint().addVariable(lecture); } return lecture; } private void assignCommited() { if (!getModel().hasConstantVariables()) return; iProgress.setPhase("Assigning committed classes ...", getModel().constantVariables().size()); for (Lecture lecture : getModel().constantVariables()) { iProgress.incProgress(); if (getAssignment().getValue(lecture) != null) continue; Placement placement = (Placement) lecture.getInitialAssignment(); getModel().weaken(getAssignment(), placement); Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel() .conflictConstraints(getAssignment(), placement); if (conflictConstraints.isEmpty()) { getAssignment().assign(0, placement); } else { String warn = "Unable to assign committed class " + getClassLabel(lecture) + " ← " + placement.getLongName(iUseAmPm); warn += "<br> Reason:"; for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { Set<Placement> vals = conflictConstraints.get(c); for (Placement v : vals) { warn += "<br> " + getClassLabel(v.variable()) + " = " + v.getLongName(iUseAmPm); } warn += "<br> in constraint " + c; iProgress.message(msglevel("cannotAssignCommitted", Progress.MSGLEVEL_WARN), warn); } } } } private void purgeInvalidValues() { iProgress.setPhase("Purging invalid placements ...", getModel().variables().size()); for (Lecture lecture : new ArrayList<Lecture>(getModel().variables())) { List<Placement> oldValues = new ArrayList<Placement>(lecture.values(getAssignment())); lecture.purgeInvalidValues(iInteractiveMode); if (lecture.values(getAssignment()).isEmpty()) { String warn = "Class " + getClassLabel(lecture) + " has no available placement (after enforcing consistency between the problem and committed solutions" + (iInteractiveMode ? "" : ", class not loaded") + ")."; for (Placement p : oldValues) { warn += "<br> " + p.getNotValidReason(getAssignment(), iUseAmPm); } iProgress.message(msglevel("noPlacementAfterCommit", Progress.MSGLEVEL_WARN), warn); if (!iInteractiveMode) { getModel().removeVariable(lecture); for (Constraint c : new ArrayList<Constraint>(lecture.constraints())) { c.removeVariable(lecture); if (c.variables().isEmpty() || c instanceof BinaryConstraint) getModel().removeConstraint(c); } for (Iterator i = lecture.students().iterator(); i.hasNext();) { Student s = (Student) i.next(); s.getLectures().remove(lecture); } } } iProgress.incProgress(); } } private void loadAssignment(Assignment assignment) { Lecture lecture = (Lecture) iLectures.get(assignment.getClazz().getUniqueId()); int dayCode = assignment.getDays().intValue(); int startSlot = assignment.getStartSlot().intValue(); Long patternId = assignment.getTimePattern().getUniqueId(); DatePattern datePattern = assignment.getDatePattern(); Set<Location> rooms = assignment.getRooms(); if (lecture == null) return; Placement initialPlacement = null; for (Iterator i2 = lecture.values(getAssignment()).iterator(); i2.hasNext();) { Placement placement = (Placement) i2.next(); if (placement.getTimeLocation().getDayCode() != dayCode) continue; if (placement.getTimeLocation().getStartSlot() != startSlot) continue; if (!placement.getTimeLocation().getTimePatternId().equals(patternId)) continue; if (datePattern != null && !placement.getTimeLocation().getDatePatternId().equals(datePattern.getUniqueId())) continue; if (rooms.size() != placement.getNrRooms()) continue; boolean sameRooms = true; for (Iterator i = rooms.iterator(); sameRooms && i.hasNext();) { Location r = (Location) i.next(); if (!placement.hasRoomLocation(r.getUniqueId())) sameRooms = false; } if (!sameRooms) continue; initialPlacement = placement; break; } if (initialPlacement == null) { TimeLocation timeLocation = null; for (TimeLocation t : lecture.timeLocations()) { if (t.getDayCode() != dayCode) continue; if (t.getStartSlot() != startSlot) continue; if (!t.getTimePatternId().equals(patternId)) continue; if (datePattern != null && !t.getDatePatternId().equals(datePattern.getUniqueId())) continue; timeLocation = t; break; } List<RoomLocation> roomLocations = new ArrayList<RoomLocation>(); for (Location room : rooms) { for (RoomLocation r : lecture.roomLocations()) { if (r.getId().equals(room.getUniqueId())) roomLocations.add(r); } } if (timeLocation != null && roomLocations.size() == lecture.getNrRooms()) { initialPlacement = new Placement(lecture, timeLocation, roomLocations); } } if (initialPlacement == null) { StringBuffer sb = new StringBuffer(assignment.getTimeLocation().getLongName(iUseAmPm) + " "); for (Iterator<Location> i = rooms.iterator(); i.hasNext();) { sb.append(i.next().getLabel()); if (i.hasNext()) sb.append(", "); } if (!assignment.getInstructors().isEmpty()) { sb.append(" "); for (Iterator i = assignment.getInstructors().iterator(); i.hasNext();) { sb.append(((DepartmentalInstructor) i.next()).getName(iInstructorFormat)); if (i.hasNext()) sb.append(", "); } } iProgress.message(msglevel("placementNotValid", Progress.MSGLEVEL_WARN), "Unable to assign " + getClassLabel(lecture) + " ← " + sb + " (placement not valid)"); return; } if (!initialPlacement.isValid()) { String reason = ""; for (InstructorConstraint ic : lecture.getInstructorConstraints()) { if (!ic.isAvailable(lecture, initialPlacement)) reason += "<br> instructor " + ic.getName() + " not available"; } if (lecture.getNrRooms() > 0) { if (initialPlacement.isMultiRoom()) { for (RoomLocation roomLocation : initialPlacement.getRoomLocations()) { if (!roomLocation.getRoomConstraint().isAvailable(lecture, initialPlacement.getTimeLocation(), lecture.getScheduler())) reason += "<br> room " + roomLocation.getName() + " not available"; } } else { if (!initialPlacement.getRoomLocation().getRoomConstraint().isAvailable(lecture, initialPlacement.getTimeLocation(), lecture.getScheduler())) reason += "<br> room " + initialPlacement.getRoomLocation().getName() + " not available"; } } Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel() .conflictConstraints(getAssignment(), initialPlacement); if (!conflictConstraints.isEmpty()) { for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { Set<Placement> vals = conflictConstraints.get(c); for (Placement p : vals) { Lecture l = p.variable(); if (l.isCommitted()) reason += "<br> conflict with committed assignment " + getClassLabel(l) + " = " + p.getLongName(iUseAmPm) + " (in constraint " + c + ")"; if (p.equals(initialPlacement)) reason += "<br> constraint " + c; } } } iProgress.message(msglevel("cannotAssign", Progress.MSGLEVEL_WARN), "Unable to assign " + getClassLabel(lecture) + " ← " + initialPlacement.getLongName(iUseAmPm) + (reason.length() == 0 ? "." : ":" + reason)); } else { if (iMppAssignment) lecture.setInitialAssignment(initialPlacement); getModel().weaken(getAssignment(), initialPlacement); Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel() .conflictConstraints(getAssignment(), initialPlacement); if (conflictConstraints.isEmpty()) { getAssignment().assign(0, initialPlacement); } else { String warn = "Unable to assign " + getClassLabel(lecture) + " ← " + initialPlacement.getLongName(iUseAmPm); warn += "<br> Reason:"; for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { Set<Placement> vals = conflictConstraints.get(c); for (Placement v : vals) { warn += "<br> " + getClassLabel(v.variable()) + " = " + v.getLongName(iUseAmPm); } warn += "<br> in constraint " + c; iProgress.message(msglevel("cannotAssign", Progress.MSGLEVEL_WARN), warn); } } } } private RoomConstraint getRoomConstraint(Department dept, Location location, org.hibernate.Session hibSession) { RoomConstraint rc = (RoomConstraint) iRooms.get(location.getUniqueId()); if (rc == null) { PreferenceLevel roomPreference = getRoomPreference(dept.getUniqueId(), location.getUniqueId()); boolean discouraged = (!iInteractiveMode && roomPreference != null && (roomPreference.getPrefProlog().equals(PreferenceLevel.sProhibited) || roomPreference.getPrefProlog().equals(PreferenceLevel.sStronglyDiscouraged) || roomPreference.getPrefProlog().equals(PreferenceLevel.sDiscouraged))); RoomSharingModel sharingModel = location.getRoomSharingModel(); if (sharingModel != null && iIgnoreRoomSharing) { for (int d = 0; d < sharingModel.getNrDays(); d++) for (int t = 0; t < sharingModel.getNrTimes(); t++) if (!String.valueOf(RoomSharingModel.sNotAvailablePref) .equals(sharingModel.getPreference(d, t))) sharingModel.setPreference(d, t, String.valueOf(RoomSharingModel.sFreeForAllPref)); } if (sharingModel != null && sharingModel.allAvailable(null)) sharingModel = null; Long buildingId = null; if (location instanceof Room) { buildingId = ((Room) location).getBuilding().getUniqueId(); } rc = (discouraged ? new DiscouragedRoomConstraint(getModel().getProperties(), location.getUniqueId(), location.getLabel(), buildingId, location.getCapacity().intValue(), sharingModel, location.getCoordinateX(), location.getCoordinateY(), (location.isIgnoreTooFar() == null ? false : location.isIgnoreTooFar().booleanValue()), (location.isIgnoreRoomCheck() == null ? true : !location.isIgnoreRoomCheck().booleanValue())) : new RoomConstraint(location.getUniqueId(), location.getLabel(), buildingId, location.getCapacity().intValue(), sharingModel, location.getCoordinateX(), location.getCoordinateY(), (location.isIgnoreTooFar() == null ? false : location.isIgnoreTooFar().booleanValue()), (location.isIgnoreRoomCheck() == null ? true : !location.isIgnoreRoomCheck().booleanValue()))); rc.setType(location instanceof Room ? ((Room) location).getRoomType().getUniqueId() : null); //loadRoomAvailability(location, rc, hibSession); getModel().addConstraint(rc); iRooms.put(location.getUniqueId(), rc); } return rc; } private InstructorConstraint getInstructorConstraint(DepartmentalInstructor instructor, org.hibernate.Session hibSession) { if (instructor.getExternalUniqueId() != null && instructor.getExternalUniqueId().length() > 0) { InstructorConstraint ic = (InstructorConstraint) iInstructors.get(instructor.getExternalUniqueId()); if (ic != null) return ic; } InstructorConstraint ic = (InstructorConstraint) iInstructors.get(instructor.getUniqueId()); if (ic == null) { boolean ignDist = (instructor.isIgnoreToFar() != null && instructor.isIgnoreToFar().booleanValue()); ic = new InstructorConstraint(instructor.getUniqueId(), instructor.getExternalUniqueId(), instructor.getName(iInstructorFormat), ignDist); ic.setType(instructor.getPositionType() == null ? new Long(Long.MAX_VALUE) : new Long(instructor.getPositionType().getSortOrder())); //loadInstructorAvailability(instructor, ic, hibSession); getModel().addConstraint(ic); iInstructors.put(instructor.getUniqueId(), ic); if (instructor.getExternalUniqueId() != null && instructor.getExternalUniqueId().length() > 0) iInstructors.put(instructor.getExternalUniqueId(), ic); } return ic; } private void loadInstructorAvailabilities(org.hibernate.Session hibSession, String puids) { Query q = hibSession.createQuery( "select distinct i.externalUniqueId, a from ClassInstructor ci inner join ci.instructor i inner join ci.classInstructing.assignments a " + "where ci.lead = true and i.externalUniqueId in (" + puids + ") and a.solution.owner.session.uniqueId=:sessionId and a.solution.commited=true and a.solution.owner.uniqueId not in (" + iSolverGroupIds + ")"); q.setLong("sessionId", iSessionId.longValue()); for (Iterator i = q.iterate(); i.hasNext();) { Object[] x = (Object[]) i.next(); String puid = (String) x[0]; Assignment a = (Assignment) x[1]; InstructorConstraint ic = (InstructorConstraint) iInstructors.get(puid); Placement p = a.getPlacement(); ic.setNotAvailable(p); if (!iLectures.containsKey(a.getClassId())) { iLectures.put(a.getClassId(), p.variable()); getModel().addVariable(p.variable()); } } } private void loadInstructorAvailabilities(org.hibernate.Session hibSession) { iProgress.setPhase("Loading instructor availabilities ...", 1); StringBuffer puids = new StringBuffer(); int idx = 0; for (Enumeration e = iInstructors.elements(); e.hasMoreElements();) { InstructorConstraint ic = (InstructorConstraint) e.nextElement(); if (ic.getPuid() == null) continue; if (puids.length() > 0) puids.append(","); puids.append("'" + ic.getPuid() + "'"); idx++; if (idx == 100) { loadInstructorAvailabilities(hibSession, puids.toString()); puids = new StringBuffer(); idx = 0; } } if (puids.length() > 0) loadInstructorAvailabilities(hibSession, puids.toString()); iProgress.incProgress(); } private void loadInstructorStudentConflicts(org.hibernate.Session hibSession, String puids) { for (Object[] x : (List<Object[]>) hibSession .createQuery("select s.uniqueId, s.externalUniqueId from Student s " + "where s.session.uniqueId = :sessionId and s.externalUniqueId in (" + puids + ")") .setLong("sessionId", iSessionId.longValue()).list()) { Long studentId = (Long) x[0]; String puid = (String) x[1]; InstructorConstraint ic = iInstructors.get(puid); Student s = iStudents.get(studentId); if (s != null && ic != null) { iProgress.debug("Instructor " + puid + " mapped with student " + s.getId()); s.setInstructor(ic); for (Lecture lecture : ic.variables()) { s.addLecture(lecture); lecture.addStudent(getAssignment(), s); } } } } private void loadInstructorStudentConflicts(org.hibernate.Session hibSession) { iProgress.setPhase("Loading instructor student conflicts ...", 1); StringBuffer puids = new StringBuffer(); int idx = 0; for (InstructorConstraint ic : iInstructors.values()) { if (ic.getPuid() == null) continue; if (puids.length() > 0) puids.append(","); puids.append("'" + ic.getPuid() + "'"); idx++; if (idx == 100) { loadInstructorStudentConflicts(hibSession, puids.toString()); puids = new StringBuffer(); idx = 0; } } if (puids.length() > 0) loadInstructorStudentConflicts(hibSession, puids.toString()); iProgress.incProgress(); } private void loadRoomAvailabilities(org.hibernate.Session hibSession, String roomids) { Query q = hibSession .createQuery("select distinct r.uniqueId, a from Location r inner join r.assignments as a " + "where r.uniqueId in (" + roomids + ") and a.solution.owner.session.uniqueId=:sessionId and a.solution.commited=true and a.solution.owner.uniqueId not in (" + iSolverGroupIds + ") and r.ignoreRoomCheck = false"); q.setLong("sessionId", iSessionId.longValue()); for (Iterator i = q.iterate(); i.hasNext();) { Object[] x = (Object[]) i.next(); Long roomId = (Long) x[0]; Assignment a = (Assignment) x[1]; Placement p = a.getPlacement(); RoomConstraint rc = (RoomConstraint) iRooms.get(roomId); rc.setNotAvailable(p); if (!iLectures.containsKey(a.getClassId())) { iLectures.put(a.getClassId(), p.variable()); getModel().addVariable(p.variable()); } } } private void loadRoomAvailabilities(org.hibernate.Session hibSession) { iProgress.setPhase("Loading room availabilities ...", 1); StringBuffer roomids = new StringBuffer(); int idx = 0; for (Enumeration e = iRooms.elements(); e.hasMoreElements();) { RoomConstraint rc = (RoomConstraint) e.nextElement(); if (roomids.length() > 0) roomids.append(","); roomids.append(rc.getResourceId()); idx++; if (idx == 100) { loadRoomAvailabilities(hibSession, roomids.toString()); roomids = new StringBuffer(); idx = 0; } } if (roomids.length() > 0) loadRoomAvailabilities(hibSession, roomids.toString()); iProgress.incProgress(); } private Constraint createGroupConstraint(DistributionPref pref) { Constraint gc = null; if (pref.getDistributionType().getReference().matches("_(.+)_")) { for (FlexibleConstraintType fcType : FlexibleConstraintType.values()) { if (pref.getDistributionType().getReference().matches(fcType.getPattern())) { try { gc = fcType.create(pref.getUniqueId(), pref.getOwner().toString(), pref.getPrefLevel().getPrefProlog(), pref.getDistributionType().getReference()); } catch (IllegalArgumentException e) { iProgress.warn("Constraint " + pref.getDistributionType().getReference() + " was not loaded. Inconsistent values.", e); } } } if (gc == null) { iProgress.warn("Constraint " + pref.getDistributionType().getReference() + " was not recognized."); return null; } } else if ("SAME_INSTR".equals(pref.getDistributionType().getReference())) { if (PreferenceLevel.sRequired.equals(pref.getPrefLevel().getPrefProlog())) gc = new InstructorConstraint(new Long(-(int) pref.getUniqueId().longValue()), null, pref.getDistributionType().getLabel(), false); } else if ("SPREAD".equals(pref.getDistributionType().getReference())) { gc = new SpreadConstraint(getModel().getProperties(), "spread"); } else if ("MIN_ROOM_USE".equals(pref.getDistributionType().getReference())) { if (!iInteractiveMode) gc = new MinimizeNumberOfUsedRoomsConstraint(getModel().getProperties()); else iProgress.message(msglevel("constraintNotUsed", Progress.MSGLEVEL_INFO), "Minimize number of used rooms constraint not loaded due to the interactive mode of the solver."); } else if ("MIN_GRUSE(10x1h)".equals(pref.getDistributionType().getReference())) { if (!iInteractiveMode) gc = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "10x1h", MinimizeNumberOfUsedGroupsOfTime.sGroups10of1h); else iProgress.message(msglevel("constraintNotUsed", Progress.MSGLEVEL_INFO), "Minimize number of used groups of time constraint not loaded due to the interactive mode of the solver."); } else if ("MIN_GRUSE(5x2h)".equals(pref.getDistributionType().getReference())) { if (!iInteractiveMode) gc = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "5x2h", MinimizeNumberOfUsedGroupsOfTime.sGroups5of2h); else iProgress.message(msglevel("constraintNotUsed", Progress.MSGLEVEL_INFO), "Minimize number of used groups of time constraint not loaded due to the interactive mode of the solver."); } else if ("MIN_GRUSE(3x3h)".equals(pref.getDistributionType().getReference())) { if (!iInteractiveMode) gc = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "3x3h", MinimizeNumberOfUsedGroupsOfTime.sGroups3of3h); else iProgress.message(msglevel("constraintNotUsed", Progress.MSGLEVEL_INFO), "Minimize number of used groups of time constraint not loaded due to the interactive mode of the solver."); } else if ("MIN_GRUSE(2x5h)".equals(pref.getDistributionType().getReference())) { if (!iInteractiveMode) gc = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "2x5h", MinimizeNumberOfUsedGroupsOfTime.sGroups2of5h); else iProgress.message(msglevel("constraintNotUsed", Progress.MSGLEVEL_INFO), "Minimize number of used groups of time constraint not loaded due to the interactive mode of the solver."); } else if (IgnoreStudentConflictsConstraint.REFERENCE.equals(pref.getDistributionType().getReference())) { if (PreferenceLevel.sRequired.equals(pref.getPrefLevel().getPrefProlog())) gc = new IgnoreStudentConflictsConstraint(); } else { GroupConstraint.ConstraintType type = GroupConstraint.ConstraintType .get(pref.getDistributionType().getReference()); if (type == null) { iProgress.error("Distribution constraint " + pref.getDistributionType().getReference() + " is not implemented."); return null; } gc = new GroupConstraint(pref.getUniqueId(), type, pref.getPrefLevel().getPrefProlog()); } return gc; } private void errorAddGroupConstraintNotFound(DistributionPref pref, Class_ clazz) { if (pref.getOwner() != null && pref.getOwner() instanceof DepartmentalInstructor) iProgress.message(msglevel("notLoadedInInstrPref", Progress.MSGLEVEL_INFO), "Lecture " + getClassLabel(clazz) + " not found/loaded, but used in distribution preference " + pref.getLabel()); else iProgress.message(msglevel("notLoadedInDistPref", Progress.MSGLEVEL_WARN), "Lecture " + getClassLabel(clazz) + " not found/loaded, but used in distribution preference " + pref.getLabel()); } private Lecture getLecture(Class_ clazz) { Lecture lecture = (Lecture) iLectures.get(clazz.getUniqueId()); if (lecture != null) return lecture; if (iAllClasses.contains(clazz)) return null; try { Assignment assignment = clazz.getCommittedAssignment(); if (assignment != null) { Placement placement = assignment.getPlacement(); lecture = (Lecture) placement.variable(); getModel().addVariable(lecture); iLectures.put(clazz.getUniqueId(), lecture); } } catch (LazyInitializationException e) { Assignment assignment = (new AssignmentDAO()).get(clazz.getCommittedAssignment().getUniqueId()); if (assignment != null) { Placement placement = assignment.getPlacement(); lecture = (Lecture) placement.variable(); getModel().addVariable(lecture); iLectures.put(clazz.getUniqueId(), lecture); } } return lecture; } private void addGroupConstraint(Constraint<Lecture, Placement> gc) { if (gc.variables().isEmpty()) return; boolean allVariablesAreCommitted = true; for (Lecture lecture : gc.variables()) { if (!lecture.isCommitted()) { allVariablesAreCommitted = false; break; } } if (allVariablesAreCommitted) { iProgress.debug("Not created constraint " + gc.getName() + " between " + gc.variables() + " (all variables are committed)"); for (Lecture lecture : new ArrayList<Lecture>(gc.variables())) { gc.removeVariable(lecture); } return; } getModel().addConstraint(gc); iProgress.trace("Added constraint " + gc.getName() + " between " + gc.variables()); } private void loadGroupConstraint(DistributionPref pref) { Structure structure = pref.getStructure(); if (structure == null) structure = DistributionPref.Structure.AllClasses; if (structure == DistributionPref.Structure.Progressive) { int maxSize = 0; for (Iterator i = pref.getOrderedSetOfDistributionObjects().iterator(); i.hasNext();) { DistributionObject distributionObject = (DistributionObject) i.next(); if (distributionObject.getPrefGroup() instanceof Class_) maxSize = Math.max(maxSize, 1); else if (distributionObject.getPrefGroup() instanceof SchedulingSubpart) maxSize = Math.max(maxSize, ((SchedulingSubpart) distributionObject.getPrefGroup()).getClasses().size()); } Constraint gc[] = new Constraint[maxSize]; Set<Class_> gcClasses[] = new Set[maxSize]; for (int i = 0; i < gc.length; i++) { gc[i] = createGroupConstraint(pref); if (gc[i] == null) return; gcClasses[i] = new HashSet<Class_>(); } List<Lecture> allLectureOfCorrectOrder = new ArrayList<Lecture>(); for (Iterator i = pref.getOrderedSetOfDistributionObjects().iterator(); i.hasNext();) { DistributionObject distributionObject = (DistributionObject) i.next(); if (distributionObject.getPrefGroup() instanceof Class_) { Class_ clazz = (Class_) distributionObject.getPrefGroup(); Lecture lecture = (Lecture) getLecture(clazz); if (lecture != null) allLectureOfCorrectOrder.add(lecture); } else if (distributionObject.getPrefGroup() instanceof SchedulingSubpart) { SchedulingSubpart subpart = (SchedulingSubpart) distributionObject.getPrefGroup(); List<Class_> classes = new ArrayList<Class_>(subpart.getClasses()); Collections.sort(classes, new ClassComparator(ClassComparator.COMPARE_BY_HIERARCHY)); for (Class_ clazz : classes) { Lecture lecture = getLecture(clazz); if (lecture != null) allLectureOfCorrectOrder.add(lecture); } } } List<DistributionObject> distributionObjects = new ArrayList<DistributionObject>( pref.getDistributionObjects()); Collections.sort(distributionObjects, new ChildrenFirstDistributionObjectComparator()); for (DistributionObject distributionObject : distributionObjects) { if (distributionObject.getPrefGroup() instanceof Class_) { Class_ clazz = (Class_) distributionObject.getPrefGroup(); Lecture lecture = (Lecture) getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } for (int j = 0; j < gc.length; j++) { gc[j].addVariable(lecture); gcClasses[j].add(clazz); } } else if (distributionObject.getPrefGroup() instanceof SchedulingSubpart) { SchedulingSubpart subpart = (SchedulingSubpart) distributionObject.getPrefGroup(); List<Class_> classes = new ArrayList<Class_>(subpart.getClasses()); Collections.sort(classes, new ClassComparator(ClassComparator.COMPARE_BY_HIERARCHY)); if (classes.isEmpty()) { iProgress.message(msglevel("badDistributionObj", Progress.MSGLEVEL_WARN), "Distribution preference " + pref.getLabel() + " refers to a scheduling subpart " + getSubpartLabel(subpart) + " with no classes."); continue; } for (int j = 0; j < gc.length; j++) { Class_ clazz = null; for (Iterator<Class_> k = gcClasses[j].iterator(); k.hasNext() && clazz == null;) { clazz = getParentClass(k.next(), subpart); } if (clazz == null) { // no children in the list, filter out classes with wrong parent (there is a subpart parent but no class parent) List<Class_> adepts = new ArrayList<Class_>(); adepts: for (Class_ adept : classes) { for (Class_ other : gcClasses[j]) if (shareParent(other.getSchedulingSubpart(), adept.getSchedulingSubpart()) && !shareParent(other, adept)) continue adepts; adepts.add(adept); } if (!adepts.isEmpty() && adepts.size() < classes.size()) { // there are adepts, but some classes have been filtered out -> we may need to skip some previous choices in the adept index int k = 0; for (int i = 0; i < j; i++) { adepts: for (Class_ adept : adepts) { for (Class_ other : gcClasses[i]) if (shareParent(other.getSchedulingSubpart(), adept.getSchedulingSubpart()) && !shareParent(other, adept)) continue adepts; // adept found for group i -> include it in the adept index k++; break; } } clazz = adepts.get(k % adepts.size()); } } if (clazz == null) { // fallback -> take j-th class clazz = (Class_) classes.get(j % classes.size()); } Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } gc[j].addVariable(lecture); gcClasses[j].add(clazz); } } else { iProgress.message(msglevel("badDistributionObj", Progress.MSGLEVEL_WARN), "Distribution preference " + pref.getLabel() + " refers to unsupported object " + distributionObject.getPrefGroup()); } } for (int i = 0; i < gc.length; i++) { Comparator cmp = new ObjectsByGivenOrderComparator(allLectureOfCorrectOrder); if (!gc[i].variables().isEmpty()) { Collections.sort(gc[i].variables(), cmp); addGroupConstraint(gc[i]); } } } else if (structure == DistributionPref.Structure.Pairwise) { List<Lecture> lectures = new ArrayList<Lecture>(); for (Iterator i = pref.getOrderedSetOfDistributionObjects().iterator(); i.hasNext();) { DistributionObject distributionObject = (DistributionObject) i.next(); if (distributionObject.getPrefGroup() instanceof Class_) { Class_ clazz = (Class_) distributionObject.getPrefGroup(); Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } lectures.add(lecture); } else if (distributionObject.getPrefGroup() instanceof SchedulingSubpart) { SchedulingSubpart subpart = (SchedulingSubpart) distributionObject.getPrefGroup(); List<Class_> classes = new ArrayList<Class_>(subpart.getClasses()); Collections.sort(classes, new ClassComparator(ClassComparator.COMPARE_BY_HIERARCHY)); for (Class_ clazz : classes) { Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } lectures.add(lecture); } } else { iProgress.message(msglevel("badDistributionObj", Progress.MSGLEVEL_WARN), "Distribution preference " + pref.getLabel() + " refers to unsupported object " + distributionObject.getPrefGroup()); } } if (lectures.size() < 2) { iProgress.message(msglevel("distrPrefIncomplete", Progress.MSGLEVEL_WARN), "Distribution preference " + pref.getLabel() + " refers to less than two classes"); } else { for (int idx1 = 0; idx1 < lectures.size() - 1; idx1++) { Lecture l1 = lectures.get(idx1); for (int idx2 = idx1 + 1; idx2 < lectures.size(); idx2++) { Lecture l2 = lectures.get(idx2); Constraint gc = createGroupConstraint(pref); if (gc == null) return; gc.addVariable(l1); gc.addVariable(l2); addGroupConstraint(gc); } } } } else if (structure == DistributionPref.Structure.OneOfEach) { List<Lecture> lectures = new ArrayList<Lecture>(); List<Integer> counts = new ArrayList<Integer>(); for (Iterator i = pref.getOrderedSetOfDistributionObjects().iterator(); i.hasNext();) { DistributionObject distributionObject = (DistributionObject) i.next(); int count = 0; if (distributionObject.getPrefGroup() instanceof Class_) { Class_ clazz = (Class_) distributionObject.getPrefGroup(); Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } lectures.add(lecture); count++; } else if (distributionObject.getPrefGroup() instanceof SchedulingSubpart) { SchedulingSubpart subpart = (SchedulingSubpart) distributionObject.getPrefGroup(); List<Class_> classes = new ArrayList<Class_>(subpart.getClasses()); Collections.sort(classes, new ClassComparator(ClassComparator.COMPARE_BY_HIERARCHY)); for (Class_ clazz : classes) { Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } lectures.add(lecture); count++; } } else { iProgress.message(msglevel("badDistributionObj", Progress.MSGLEVEL_WARN), "Distribution preference " + pref.getLabel() + " refers to unsupported object " + distributionObject.getPrefGroup()); } if (count > 0) counts.add(count); } if (counts.size() > 1) { for (Enumeration<List<Lecture>> e = DistributionPref.permutations(lectures, counts); e .hasMoreElements();) { Collection<Lecture> perm = e.nextElement(); Constraint gc = createGroupConstraint(pref); if (gc == null) return; for (Lecture lecture : perm) gc.addVariable(lecture); addGroupConstraint(gc); iProgress.debug("Posted " + gc.getName() + " between " + gc.variables()); } } } else { Integer grouping = null; switch (structure) { case GroupsOfTwo: grouping = 2; break; case GroupsOfThree: grouping = 3; break; case GroupsOfFour: grouping = 4; break; case GroupsOfFive: grouping = 5; break; } Constraint gc = createGroupConstraint(pref); if (gc == null) return; for (Iterator i = pref.getOrderedSetOfDistributionObjects().iterator(); i.hasNext();) { DistributionObject distributionObject = (DistributionObject) i.next(); if (distributionObject.getPrefGroup() instanceof Class_) { Class_ clazz = (Class_) distributionObject.getPrefGroup(); Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } gc.addVariable(lecture); if (grouping != null && gc.variables().size() == grouping) { addGroupConstraint(gc); gc = createGroupConstraint(pref); if (gc == null) return; } } else if (distributionObject.getPrefGroup() instanceof SchedulingSubpart) { SchedulingSubpart subpart = (SchedulingSubpart) distributionObject.getPrefGroup(); List<Class_> classes = new ArrayList<Class_>(subpart.getClasses()); Collections.sort(classes, new ClassComparator(ClassComparator.COMPARE_BY_HIERARCHY)); for (Class_ clazz : classes) { Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } gc.addVariable(lecture); if (grouping != null && gc.variables().size() == grouping) { addGroupConstraint(gc); gc = createGroupConstraint(pref); if (gc == null) return; } } } else { iProgress.message(msglevel("badDistributionObj", Progress.MSGLEVEL_WARN), "Distribution preference " + pref.getLabel() + " refers to unsupported object " + distributionObject.getPrefGroup()); } } addGroupConstraint(gc); } } private void loadInstructorGroupConstraint(DepartmentalInstructor instructor, DistributionPref pref) { Constraint gc = createGroupConstraint(pref); if (gc == null) return; boolean loadConstraint = false; for (Iterator i = instructor.getClasses().iterator(); i.hasNext();) { ClassInstructor classInstructor = (ClassInstructor) i.next(); Class_ clazz = (Class_) classInstructor.getClassInstructing(); if (classInstructor.isLead() && clazz.getManagingDept().isInheritInstructorPreferences()) { loadConstraint = true; break; } } if (!loadConstraint) return; for (Iterator i = instructor.getClasses().iterator(); i.hasNext();) { ClassInstructor classInstructor = (ClassInstructor) i.next(); if (!classInstructor.isLead()) continue; Class_ clazz = (Class_) classInstructor.getClassInstructing(); Lecture lecture = getLecture(clazz); if (lecture == null) { errorAddGroupConstraintNotFound(pref, clazz); continue; } gc.addVariable(lecture); } addGroupConstraint(gc); } private void loadInstructorGroupConstraints(DepartmentalInstructor instructor, Set<Long> checkedDistPrefIds) { Set prefs = instructor.getPreferences(DistributionPref.class); if (prefs == null || prefs.isEmpty()) return; for (Iterator i = prefs.iterator(); i.hasNext();) { DistributionPref pref = (DistributionPref) i.next(); if (checkedDistPrefIds.add(pref.getUniqueId())) loadInstructorGroupConstraint(instructor, pref); } } private void loadInstructorGroupConstraints(Department department, Set<Long> checkedDistPrefIds, org.hibernate.Session hibSession) { if (!department.isInheritInstructorPreferences()) return; List instructors = null; if (department.isExternalManager()) { instructors = hibSession .createQuery("select distinct i.instructor from Class_ as c inner join c.classInstructors i " + "where i.lead = true and (c.managingDept=:deptId or (c.managingDept is null and c.controllingDept=:deptId))") .setLong("deptId", department.getUniqueId()).list(); } else { instructors = hibSession.createQuery( "select distinct di from DepartmentalInstructor di inner join di.department d where d.uniqueId=:deptId") .setLong("deptId", department.getUniqueId()).list(); } if (instructors == null || instructors.isEmpty()) return; iProgress.setPhase("Loading instructor distr. constraints for " + department.getShortLabel() + " ...", instructors.size()); for (Iterator i = instructors.iterator(); i.hasNext();) { DepartmentalInstructor instructor = (DepartmentalInstructor) i.next(); loadInstructorGroupConstraints(instructor, checkedDistPrefIds); iProgress.incProgress(); } } private static Class_ getParentClass(Class_ clazz, SchedulingSubpart parentSubpart) { if (clazz == null) return null; if (parentSubpart.getClasses().contains(clazz)) return clazz; return getParentClass(clazz.getParentClass(), parentSubpart); } private static boolean shareParent(Class_ c1, Class_ c2) { if (c1.getParentClass() == null) return false; for (Class_ p = c2.getParentClass(); p != null; p = p.getParentClass()) if (c1.getParentClass().equals(p)) return true; return shareParent(c1.getParentClass(), c2); } private static boolean shareParent(SchedulingSubpart s1, SchedulingSubpart s2) { if (s1.getParentSubpart() == null) return false; for (SchedulingSubpart p = s2.getParentSubpart(); p != null; p = p.getParentSubpart()) if (s1.getParentSubpart().equals(p)) return true; return shareParent(s1.getParentSubpart(), s2); } public static class ChildrenFirstDistributionObjectComparator implements Comparator<DistributionObject> { public int compare(DistributionObject d1, DistributionObject d2) { if (d1.getPrefGroup() instanceof Class_) { if (d2.getPrefGroup() instanceof Class_) return d1.compareTo(d2); else return 1; //classes last } else if (d2.getPrefGroup() instanceof Class_) return -1; if (!(d1.getPrefGroup() instanceof SchedulingSubpart) || !(d2.getPrefGroup() instanceof SchedulingSubpart)) return d1.compareTo(d2); //should not happen SchedulingSubpart s1 = (SchedulingSubpart) d1.getPrefGroup(); SchedulingSubpart s2 = (SchedulingSubpart) d2.getPrefGroup(); if (s1.getClasses().size() <= 1) { if (s2.getClasses().size() <= 1) return d1.compareTo(d2); else return 1; //singleton last } else if (s2.getClasses().size() <= 1) return -1; if (getParentClass((Class_) s1.getClasses().iterator().next(), s2) != null) return -1; //c1 is child, c2 is parent if (getParentClass((Class_) s2.getClasses().iterator().next(), s1) != null) return 1; //c2 is child, c1 is parent // distribution objects share a parent, return the one with more classes first if (shareParent(s1, s2)) { if (s1.getClasses().size() < s2.getClasses().size()) return 1; else if (s1.getClasses().size() > s2.getClasses().size()) return -1; } return d1.compareTo(d2); } } private String getClassLimitConstraitName(SchedulingSubpart subpart) { if (subpart == null) return "class-limit"; String name = subpart.getCourseName() + " " + subpart.getItypeDesc().trim(); String sufix = subpart.getSchedulingSubpartSuffix(); if (sufix != null && sufix.length() > 0) name += " (" + sufix + ")"; return name; } private String getClassLimitConstraitName(Lecture lecture) { SchedulingSubpart subpart = iSubparts.get(lecture.getSchedulingSubpartId()); if (subpart == null) subpart = SchedulingSubpartDAO.getInstance().get(lecture.getSchedulingSubpartId()); return getClassLimitConstraitName(subpart); } private void createChildrenClassLimitConstraits(Lecture parentLecture) { if (!parentLecture.hasAnyChildren()) return; for (Long subpartId : parentLecture.getChildrenSubpartIds()) { List<Lecture> children = parentLecture.getChildren(subpartId); ClassLimitConstraint clc = new ClassLimitConstraint(parentLecture, getClassLimitConstraitName(parentLecture)); boolean isMakingSense = false; for (Lecture lecture : children) { createChildrenClassLimitConstraits(lecture); if (!lecture.isCommitted() && lecture.minClassLimit() != lecture.maxClassLimit()) isMakingSense = true; } if (!isMakingSense) continue; for (Lecture lecture : children) { if (!lecture.isCommitted()) clc.addVariable(lecture); else clc.setClassLimitDelta( clc.getClassLimitDelta() - iClasses.get(lecture.getClassId()).getClassLimit()); } if (clc.variables().isEmpty()) continue; SchedulingSubpart subpart = iSubparts.get(subpartId); if (subpart == null) subpart = SchedulingSubpartDAO.getInstance().get(subpartId); for (Class_ clazz : subpart.getClasses()) { if (iLectures.get(clazz.getUniqueId()) == null) clc.setClassLimitDelta(clc.getClassLimitDelta() - clazz.getClassLimit()); } iProgress.trace("Added constraint " + clc.getName() + " between " + clc.variables()); getModel().addConstraint(clc); } } public void load() { org.hibernate.Session hibSession = null; Transaction tx = null; try { hibSession = TimetableManagerDAO.getInstance().getSession(); hibSession.setCacheMode(CacheMode.IGNORE); hibSession.setFlushMode(FlushMode.COMMIT); tx = hibSession.beginTransaction(); load(hibSession); tx.commit(); } catch (Exception e) { iProgress.message(msglevel("loadFailed", Progress.MSGLEVEL_FATAL), "Unable to load input data, reason:" + e.getMessage(), e); tx.rollback(); } finally { // here we need to close the session since this code may run in a separate thread if (hibSession != null && hibSession.isOpen()) hibSession.close(); } } private boolean postSameStudentConstraint(Class_ clazz, String type) { boolean posted = false; if (!clazz.getChildClasses().isEmpty()) { for (Iterator i = clazz.getChildClasses().iterator(); i.hasNext();) { Class_ c = (Class_) i.next(); if (postSameStudentConstraint(c, type)) posted = true; } } if (posted) return true; Lecture lecture = getLecture(clazz); if (lecture == null) return false; List<Lecture> variables = new ArrayList<Lecture>(); variables.add(lecture); Class_ parent = clazz; while ((parent = parent.getParentClass()) != null) { Lecture parentLecture = getLecture(parent); if (parentLecture != null) variables.add(parentLecture); } for (Iterator i = clazz.getSchedulingSubpart().getInstrOfferingConfig().getSchedulingSubparts() .iterator(); i.hasNext();) { SchedulingSubpart subpart = (SchedulingSubpart) i.next(); if (subpart.getParentSubpart() != null || subpart.getClasses().size() != 1) continue; Class_ singleClazz = (Class_) subpart.getClasses().iterator().next(); Lecture singleLecture = getLecture(singleClazz); if (singleLecture != null && !variables.contains(singleLecture)) variables.add(singleLecture); } if (variables.size() == 1) return false; GroupConstraint gc = new GroupConstraint(null, GroupConstraint.ConstraintType.get(type), PreferenceLevel.sRequired); for (Lecture var : variables) gc.addVariable(var); addGroupConstraint(gc); return true; } private boolean postPrecedenceConstraint(Class_ clazz, String preference) { boolean posted = false; if (!clazz.getChildClasses().isEmpty()) { for (Iterator i = clazz.getChildClasses().iterator(); i.hasNext();) { Class_ c = (Class_) i.next(); if (postPrecedenceConstraint(c, preference)) posted = true; } } if (posted) return true; Lecture lecture = getLecture(clazz); if (lecture == null) return false; List<Lecture> variables = new ArrayList<Lecture>(); variables.add(lecture); Set<Integer> itypes = new HashSet<Integer>(); itypes.add(clazz.getSchedulingSubpart().getItype().getItype()); Class_ parent = clazz; while ((parent = parent.getParentClass()) != null) { Lecture parentLecture = getLecture(parent); if (parentLecture != null) { variables.add(0, parentLecture); itypes.add(parent.getSchedulingSubpart().getItype().getItype()); } } if (variables.size() <= 1 || itypes.size() <= 1) return false; GroupConstraint gc = new GroupConstraint(null, GroupConstraint.ConstraintType.PRECEDENCE, preference); String info = ""; for (Lecture var : variables) { gc.addVariable(var); if (!info.isEmpty()) info += ", "; info += getClassLabel(var); } iProgress.info("Posted precedence constraint between " + info + " (" + PreferenceLevel.prolog2string(preference) + ")"); addGroupConstraint(gc); return true; } private void propagateCommittedAssignment(HashSet students, Assignment assignment) { Class_ clazz = assignment.getClazz(); Lecture parentLecture = null; Class_ c = clazz; while ((parentLecture == null || parentLecture.isCommitted()) && c.getParentClass() != null) { c = c.getParentClass(); parentLecture = (Lecture) iLectures.get(c.getUniqueId()); } if (parentLecture != null && !parentLecture.isCommitted()) { for (Lecture lecture : parentLecture.sameSubpartLectures()) { if (!lecture.equals(parentLecture) && !lecture.isCommitted()) { //iProgress.debug("[A] Students "+students+" cannot enroll "+lecture.getName()+" due to the enrollment of "+clazz.getClassLabel()); for (Iterator i = students.iterator(); i.hasNext();) { Student student = (Student) i.next(); student.addCanNotEnroll(lecture); } } } } if (!clazz.getSchedulingSubpart().getChildSubparts().isEmpty()) { for (Iterator i = clazz.getSchedulingSubpart().getChildSubparts().iterator(); i.hasNext();) { SchedulingSubpart subpart = (SchedulingSubpart) i.next(); for (Iterator j = subpart.getClasses().iterator(); j.hasNext();) { Class_ child = (Class_) j.next(); if (!clazz.equals(child.getParentClass())) propagateCommittedAssignment(students, clazz, child); } } } } private void propagateCommittedAssignment(HashSet students, Class_ parent, Class_ clazz) { Lecture lecture = (Lecture) iLectures.get(clazz.getUniqueId()); if (lecture != null && !lecture.isCommitted()) { //iProgress.debug("[B] Students "+students+" cannot enroll "+lecture.getName()+" due to the enrollment of "+parent.getClassLabel()); for (Iterator i = students.iterator(); i.hasNext();) { Student student = (Student) i.next(); student.addCanNotEnroll(lecture); } } else { for (Iterator i = clazz.getChildClasses().iterator(); i.hasNext();) { Class_ child = (Class_) i.next(); propagateCommittedAssignment(students, parent, child); } } } private void loadCommittedStudentConflicts(org.hibernate.Session hibSession, Set<Long> offeringsToAvoid) { //Load all committed assignment - student relations that may be relevant List<Object[]> assignmentEnrollments = (List<Object[]>) hibSession .createQuery("select distinct a, e.studentId, io.uniqueId from " + "Solution s inner join s.assignments a inner join s.studentEnrollments e inner join a.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering io " + "where " + "s.commited=true and s.owner.session.uniqueId=:sessionId and s.owner not in (" + iSolverGroupIds + ") and " + "a.clazz=e.clazz") .setLong("sessionId", iSessionId.longValue()).list(); // Filter out relevant relations (relations that are for loaded students) Hashtable<Assignment, Set<Student>> assignments = new Hashtable<Assignment, Set<Student>>(); for (Object[] result : assignmentEnrollments) { Assignment assignment = (Assignment) result[0]; Long studentId = (Long) result[1]; Long offeringId = (Long) result[2]; if (offeringsToAvoid.contains(offeringId)) continue; Student student = (Student) iStudents.get(studentId); if (student != null) { Set<Student> students = assignments.get(assignment); if (students == null) { students = new HashSet<Student>(); assignments.put(assignment, students); } students.add(student); } } // Ensure no assignment-class relation is got from the cache for (Iterator i1 = assignmentEnrollments.iterator(); i1.hasNext();) { Object[] result = (Object[]) i1.next(); Assignment assignment = (Assignment) result[0]; if (!assignments.containsKey(assignment)) hibSession.evict(assignment); } // Make up the appropriate committed placements and propagate those through the course structure iProgress.setPhase("Loading student conflicts with commited solutions ...", assignments.size()); for (Iterator i1 = assignments.entrySet().iterator(); i1.hasNext();) { Map.Entry entry = (Map.Entry) i1.next(); Assignment assignment = (Assignment) entry.getKey(); HashSet students = (HashSet) entry.getValue(); Placement committedPlacement = assignment.getPlacement(); for (Iterator i2 = students.iterator(); i2.hasNext();) { Student student = (Student) i2.next(); student.addCommitedPlacement(committedPlacement); } if (!iLectures.containsKey(assignment.getClassId())) { iLectures.put(assignment.getClassId(), committedPlacement.variable()); getModel().addVariable(committedPlacement.variable()); } propagateCommittedAssignment(students, assignment); iProgress.incProgress(); } } private boolean somehowEnroll(Student student, CourseOffering course, float weight, Double priority) { if (course.getInstructionalOffering().isNotOffered()) return false; boolean hasSomethingCommitted = false; config: for (InstrOfferingConfig config : course.getInstructionalOffering().getInstrOfferingConfigs()) { for (SchedulingSubpart subpart : config.getSchedulingSubparts()) { for (Class_ clazz : subpart.getClasses()) { if (clazz.getCommittedAssignment() != null) { hasSomethingCommitted = true; break config; } } } } if (!hasSomethingCommitted) return false; if (!iOfferings.containsKey(course.getInstructionalOffering())) iOfferings.put(course.getInstructionalOffering(), loadOffering(course.getInstructionalOffering(), true)); student.addOffering(course.getInstructionalOffering().getUniqueId(), weight, priority); Set<Student> students = iCourse2students.get(course); if (students == null) { students = new HashSet<Student>(); iCourse2students.put(course, students); } students.add(student); return true; } private void makeupCommittedStudentConflicts(Set<Long> offeringsToAvoid) { iProgress.setPhase("Creating student conflicts with commited solutions ...", iStudents.size()); for (Student student : iStudents.values()) { Set<WeightedCourseOffering> courses = iStudentCourseDemands.getCourses(student.getId()); iProgress.incProgress(); if (courses == null) continue; for (WeightedCourseOffering course : courses) { if (offeringsToAvoid.contains(course.getCourseOffering().getInstructionalOffering().getUniqueId())) continue; if (!somehowEnroll(student, course.getCourseOffering(), course.getWeight(), iStudentCourseDemands.getEnrollmentPriority(student.getId(), course.getCourseOfferingId()))) offeringsToAvoid.add(course.getCourseOffering().getInstructionalOffering().getUniqueId()); } } } private void propagateReservedClasses(Class_ clazz, Set<Long> reservedClasses) { reservedClasses.add(clazz.getUniqueId()); for (Class_ child : clazz.getChildClasses()) propagateReservedClasses(child, reservedClasses); } private boolean canAttend(Set<Lecture> cannotAttendLectures, Collection<Lecture> lectures) { for (Iterator e = lectures.iterator(); e.hasNext();) { Lecture lecture = (Lecture) e.next(); if (cannotAttendLectures.contains(lecture)) continue; boolean canAttend = true; if (lecture.hasAnyChildren()) { for (Long subpartId : lecture.getChildrenSubpartIds()) { if (!canAttend(cannotAttendLectures, lecture.getChildren(subpartId))) { canAttend = false; break; } } } if (canAttend) return true; } return false; } private boolean canAttendConfigurations(Set<Lecture> cannotAttendLectures, List<Configuration> configurations) { for (Configuration cfg : configurations) { boolean canAttend = true; for (Long subpartId : cfg.getTopSubpartIds()) { if (!canAttend(cannotAttendLectures, cfg.getTopLectures(subpartId))) { canAttend = false; break; } } if (canAttend) return true; } return false; } private void checkReservation(CourseOffering course, Set<Lecture> cannotAttendLectures, List<Configuration> configurations) { if (canAttendConfigurations(cannotAttendLectures, configurations)) return; iProgress.message(msglevel("badCourseReservation", Progress.MSGLEVEL_WARN), "Inconsistent course reservations for course " + getOfferingLabel(course)); } private Collection<InstrOfferingConfig> sortedConfigs(InstructionalOffering offering) { if (offering.getInstrOfferingConfigs().size() <= 1) return offering.getInstrOfferingConfigs(); TreeSet<InstrOfferingConfig> configs = new TreeSet<InstrOfferingConfig>(new InstrOfferingConfigComparator( offering.getControllingCourseOffering().getSubjectArea().getUniqueId())); configs.addAll(offering.getInstrOfferingConfigs()); return configs; } private Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>> loadOffering(InstructionalOffering offering, boolean assignCommitted) { // solver group ids for fast check HashSet<Long> solverGroupIds = new HashSet<Long>(); for (Long solverGroupId : iSolverGroupId) solverGroupIds.add(solverGroupId); Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>> cfg2topSubparts = new Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>>(); // alternative configurations List<Configuration> altCfgs = new ArrayList<Configuration>(); iAltConfigurations.put(offering, altCfgs); for (InstrOfferingConfig config : sortedConfigs(offering)) { // create a configuration, set alternative configurations Configuration cfg = new Configuration(offering.getUniqueId(), config.getUniqueId(), config.getLimit().intValue()); Set<SchedulingSubpart> topSubparts = new HashSet<SchedulingSubpart>(); for (SchedulingSubpart subpart : config.getSchedulingSubparts()) { for (Class_ clazz : subpart.getClasses()) { Lecture lecture = iLectures.get(clazz.getUniqueId()); if (lecture == null) { if (clazz.getManagingDept().getSolverGroup() == null) { iProgress.message(msglevel("noSolverGroup", Progress.MSGLEVEL_WARN), "Manager of class " + getClassLabel(clazz) + " has no solver group (" + clazz.getManagingDept().getManagingDeptAbbv() + ")."); continue; } if (solverGroupIds.contains(clazz.getManagingDept().getSolverGroup().getUniqueId())) continue; // only classes of other problems if (clazz.getCommittedAssignment() == null) continue; // only committed classes if (iLectures.containsKey(clazz.getUniqueId())) continue; // already loaded Placement committedPlacement = clazz.getCommittedAssignment().getPlacement(); lecture = committedPlacement.variable(); iLectures.put(clazz.getUniqueId(), lecture); iClasses.put(lecture.getClassId(), clazz); iSubparts.put(subpart.getUniqueId(), subpart); getModel().addVariable(lecture); if (assignCommitted) { getModel().weaken(getAssignment(), committedPlacement); Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel() .conflictConstraints(getAssignment(), committedPlacement); if (conflictConstraints.isEmpty()) { getAssignment().assign(0, committedPlacement); } else { String warn = "Unable to assign committed class " + getClassLabel(lecture) + " ← " + committedPlacement.getLongName(iUseAmPm); warn += "<br> Reason:"; for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { Set<Placement> vals = conflictConstraints.get(c); for (Placement v : vals) { warn += "<br> " + getClassLabel(v.variable()) + " = " + v.getLongName(iUseAmPm); } warn += "<br> in constraint " + c; iProgress.message(msglevel("cannotAssignCommitted", Progress.MSGLEVEL_WARN), warn); } } } } } } for (SchedulingSubpart subpart : config.getSchedulingSubparts()) { List<Lecture> sameSubpart = new ArrayList<Lecture>(); for (Class_ clazz : subpart.getClasses()) { Lecture lecture = iLectures.get(clazz.getUniqueId()); if (lecture == null) continue; // set parent lecture Class_ parentClazz = clazz.getParentClass(); if (parentClazz != null) { Lecture parentLecture = null; Class_ c = clazz; while (parentLecture == null && c.getParentClass() != null) { c = c.getParentClass(); parentLecture = iLectures.get(c.getUniqueId()); } if (parentLecture != null) lecture.setParent(parentLecture); } // set same subpart lectures sameSubpart.add(lecture); lecture.setSameSubpartLectures(sameSubpart); if (lecture.getParent() == null) { // set configuration lecture.setConfiguration(cfg); // top subparts topSubparts.add(subpart); } } } if (!cfg.getTopLectures().isEmpty()) { // skip empty configurations altCfgs.add(cfg); cfg.setAltConfigurations(altCfgs); cfg2topSubparts.put(config, topSubparts); } } return cfg2topSubparts; } private void load(org.hibernate.Session hibSession) throws Exception { iProgress.setStatus("Loading input data ..."); TravelTime.populateTravelTimes(getModel().getDistanceMetric(), iSessionId, hibSession); iSolverGroup = null; iSession = null; if (iSolverGroup == null) { iSolverGroup = new SolverGroup[iSolverGroupId.length]; for (int i = 0; i < iSolverGroupId.length; i++) { iSolverGroup[i] = SolverGroupDAO.getInstance().get(iSolverGroupId[i], hibSession); if (iSolverGroup[i] == null) { iProgress.message(msglevel("loadFailed", Progress.MSGLEVEL_FATAL), "Unable to load solver group " + iSolverGroupId[i] + "."); return; } iProgress.debug("solver group[" + (i + 1) + "]: " + iSolverGroup[i].getName()); } } if (iSolverGroup == null || iSolverGroup.length == 0) { iProgress.message(msglevel("loadFailed", Progress.MSGLEVEL_FATAL), "No solver group loaded."); return; } iDepartmentIds = ""; for (int j = 0; j < iSolverGroup.length; j++) { for (Iterator i = iSolverGroup[j].getDepartments().iterator(); i.hasNext();) { Department d = (Department) i.next(); if (iDepartmentIds.length() > 0) iDepartmentIds += ","; iDepartmentIds += d.getUniqueId().toString(); } } getModel().getProperties().setProperty("General.DepartmentIds", iDepartmentIds); Hashtable<Long, Solution> solutions = null; if (iSolutionId != null && iSolutionId.length > 0) { solutions = new Hashtable<Long, Solution>(); String note = ""; for (int i = 0; i < iSolutionId.length; i++) { Solution solution = (new SolutionDAO()).get(iSolutionId[i], hibSession); if (solution == null) { iProgress.message(msglevel("loadFailed", Progress.MSGLEVEL_FATAL), "Unable to load solution " + iSolutionId[i] + "."); return; } iProgress.debug("solution[" + (i + 1) + "] version: " + solution.getUniqueId() + " (created " + solution.getCreated() + ", solver group " + solution.getOwner().getName() + ")"); if (solution.getNote() != null) { if (note.length() > 0) note += "\n"; note += solution.getNote(); } solutions.put(solution.getOwner().getUniqueId(), solution); } getModel().getProperties().setProperty("General.Note", note); String solutionIdStr = ""; for (int i = 0; i < iSolverGroupId.length; i++) { Solution solution = solutions.get(iSolverGroupId[i]); if (solution != null) { if (solutionIdStr.length() > 0) solutionIdStr += ","; solutionIdStr += solution.getUniqueId().toString(); } } getModel().getProperties().setProperty("General.SolutionId", solutionIdStr); } if (iSession == null) iSession = (new SessionDAO()).get(iSessionId, hibSession); if (iSession == null) { iProgress.message(msglevel("loadFailed", Progress.MSGLEVEL_FATAL), "No session loaded."); return; } iProgress.debug("session: " + iSession.getLabel()); getModel().getProperties().setProperty("Data.Term", iSession.getAcademicYearTerm()); getModel().getProperties().setProperty("Data.Initiative", iSession.getAcademicInitiative()); getModel().setYear(iSession.getSessionStartYear()); getModel().getProperties().setProperty("DatePattern.DayOfWeekOffset", String.valueOf(Constants.getDayOfWeek( DateUtils.getDate(1, iSession.getPatternStartMonth(), iSession.getSessionStartYear())))); if (iSession.getDefaultDatePattern() != null) { BitSet pattern = iSession.getDefaultDatePattern().getPatternBitSet(); String patternStr = ""; for (int i = 0; i < pattern.length(); i++) patternStr += (pattern.get(i) ? "1" : "0"); getModel().getProperties().setProperty("DatePattern.Default", patternStr); } iAllClasses = new TreeSet(new ClassComparator(ClassComparator.COMPARE_BY_HIERARCHY)); for (int i = 0; i < iSolverGroup.length; i++) { for (Iterator j = iSolverGroup[i].getDepartments().iterator(); j.hasNext();) { Department d = (Department) j.next(); iAllClasses.addAll(d.getClassesFetchWithStructure()); } } if (iAllClasses == null || iAllClasses.isEmpty()) { iProgress.message(msglevel("noClasses", Progress.MSGLEVEL_FATAL), "No classes to load."); return; } iProgress.debug("classes to load: " + iAllClasses.size()); iProgress.setPhase("Loading classes ...", iAllClasses.size()); int ord = 0; HashSet<SchedulingSubpart> subparts = new HashSet<SchedulingSubpart>(); for (Iterator i1 = iAllClasses.iterator(); i1.hasNext();) { Class_ clazz = (Class_) i1.next(); Lecture lecture = loadClass(clazz, hibSession); subparts.add(clazz.getSchedulingSubpart()); if (lecture != null) lecture.setOrd(ord++); iClasses.put(clazz.getUniqueId(), clazz); iProgress.incProgress(); } loadInstructorAvailabilities(hibSession); loadRoomAvailabilities(hibSession); iProgress.setPhase("Loading offerings ...", iAllClasses.size()); Set<Long> loadedOfferings = new HashSet<Long>(); for (Class_ clazz : iAllClasses) { Lecture lecture = (Lecture) iLectures.get(clazz.getUniqueId()); iProgress.incProgress(); if (lecture == null) continue; //skip classes that were not loaded InstructionalOffering offering = clazz.getSchedulingSubpart().getInstrOfferingConfig() .getInstructionalOffering(); if (!loadedOfferings.add(offering.getUniqueId())) continue; // already loaded iOfferings.put(offering, loadOffering(offering, false)); } List<DistributionPref> distPrefs = new ArrayList<DistributionPref>(); for (int i = 0; i < iSolverGroup.length; i++) { distPrefs.addAll(iSolverGroup[i].getDistributionPreferences()); } iProgress.setPhase("Loading distribution preferences ...", distPrefs.size()); for (Iterator i = distPrefs.iterator(); i.hasNext();) { DistributionPref distributionPref = (DistributionPref) i.next(); if (!PreferenceLevel.sNeutral.equals(distributionPref.getPrefLevel().getPrefProlog())) loadGroupConstraint(distributionPref); iProgress.incProgress(); } Set<Long> checkedDistPrefIds = new HashSet<Long>(); for (int i = 0; i < iSolverGroup.length; i++) { for (Iterator j = iSolverGroup[i].getDepartments().iterator(); j.hasNext();) { loadInstructorGroupConstraints((Department) j.next(), checkedDistPrefIds, hibSession); } } if (iAutoSameStudents) { iProgress.setPhase("Posting automatic same_students constraints ...", iAllClasses.size()); for (Iterator i1 = iAllClasses.iterator(); i1.hasNext();) { Class_ clazz = (Class_) i1.next(); Lecture lecture = (Lecture) iLectures.get(clazz.getUniqueId()); if (lecture == null) continue; if (!lecture.hasAnyChildren()) postSameStudentConstraint(clazz, iAutoSameStudentsConstraint); iProgress.incProgress(); } } if (iAutoPrecedence != null) { PreferenceLevel pref = PreferenceLevel.getPreferenceLevel(iAutoPrecedence); if (pref == null) { // Lookup preference if needed for (PreferenceLevel p : PreferenceLevel.getPreferenceLevelList()) if (iAutoPrecedence.equalsIgnoreCase(p.getPrefProlog()) || iAutoPrecedence.equalsIgnoreCase(p.getPrefName()) || iAutoPrecedence.equals(p.getAbbreviation())) { pref = p; break; } } if (pref == null) { iProgress.message(msglevel("autoPrecedence", Progress.MSGLEVEL_WARN), "Preference " + iAutoPrecedence + " not recognized."); } else if (!PreferenceLevel.sNeutral.equals(pref.getPrefProlog())) { iProgress.setPhase("Posting automatic precedence constraints ...", iAllClasses.size()); for (Iterator i1 = iAllClasses.iterator(); i1.hasNext();) { Class_ clazz = (Class_) i1.next(); Lecture lecture = (Lecture) iLectures.get(clazz.getUniqueId()); if (lecture == null) continue; if (!lecture.hasAnyChildren()) postPrecedenceConstraint(clazz, pref.getPrefProlog()); iProgress.incProgress(); } } } postAutomaticHierarchicalConstraints(); assignCommited(); iProgress.setPhase("Posting class limit constraints ...", iOfferings.size()); for (Map.Entry<InstructionalOffering, Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>>> entry : iOfferings .entrySet()) { Hashtable<InstrOfferingConfig, Set<SchedulingSubpart>> topSubparts = entry.getValue(); for (Map.Entry<InstrOfferingConfig, Set<SchedulingSubpart>> subpartEntry : topSubparts.entrySet()) { InstrOfferingConfig config = subpartEntry.getKey(); Set<SchedulingSubpart> topSubpartsThisConfig = subpartEntry.getValue(); for (SchedulingSubpart subpart : topSubpartsThisConfig) { boolean isMakingSense = false; for (Class_ clazz : subpart.getClasses()) { Lecture lecture = iLectures.get(clazz.getUniqueId()); if (lecture == null) continue; createChildrenClassLimitConstraits(lecture); if (!lecture.isCommitted() && lecture.minClassLimit() != lecture.maxClassLimit()) isMakingSense = true; } if (!isMakingSense) continue; if (subpart.getParentSubpart() == null) { ClassLimitConstraint clc = new ClassLimitConstraint(config.getLimit(), getClassLimitConstraitName(subpart)); for (Class_ clazz : subpart.getClasses()) { Lecture lecture = iLectures.get(clazz.getUniqueId()); if (lecture == null || lecture.isCommitted()) { clc.setClassLimitDelta(clc.getClassLimitDelta() - clazz.getClassLimit()); continue; } clc.addVariable(lecture); } if (clc.variables().isEmpty()) continue; iProgress.trace("Added constraint " + clc.getName() + " between " + clc.variables()); getModel().addConstraint(clc); } else { Hashtable<Long, ClassLimitConstraint> clcs = new Hashtable<Long, ClassLimitConstraint>(); for (Class_ clazz : subpart.getClasses()) { Lecture lecture = iLectures.get(clazz.getUniqueId()); Class_ parentClazz = clazz.getParentClass(); ClassLimitConstraint clc = clcs.get(parentClazz.getUniqueId()); if (clc == null) { clc = new ClassLimitConstraint(parentClazz.getClassLimit(), parentClazz.getClassLabel()); clcs.put(parentClazz.getUniqueId(), clc); } if (lecture == null || lecture.isCommitted()) { clc.setClassLimitDelta(clc.getClassLimitDelta() - clazz.getClassLimit()); } else { clc.addVariable(lecture); } } for (ClassLimitConstraint clc : clcs.values()) { if (!clc.variables().isEmpty()) { iProgress .trace("Added constraint " + clc.getName() + " between " + clc.variables()); getModel().addConstraint(clc); } } } } } iProgress.incProgress(); } iStudentCourseDemands.init(hibSession, iProgress, iSession, iOfferings.keySet()); iProgress.setPhase("Loading students ...", iOfferings.size()); for (InstructionalOffering offering : iOfferings.keySet()) { boolean unlimitedOffering = false; int offeringLimit = 0; for (InstrOfferingConfig config : offering.getInstrOfferingConfigs()) if (config.isUnlimitedEnrollment()) unlimitedOffering = true; else offeringLimit += config.getLimit(); Double factor = null; if (!unlimitedOffering) { int totalCourseLimit = 0; for (CourseOffering course : offering.getCourseOfferings()) { int courseLimit = -1; if (course.getReservation() != null) courseLimit = course.getReservation(); if (courseLimit < 0) { if (offering.getCourseOfferings().size() == 1) courseLimit = offeringLimit; else { iProgress.message(msglevel("crossListWithoutReservation", Progress.MSGLEVEL_INFO), "Cross-listed course " + getOfferingLabel(course) + " does not have any course reservation."); if (course.getProjectedDemand() != null && offering.getProjectedDemand() > 0) courseLimit = course.getProjectedDemand(); else if (course.getDemand() != null && offering.getDemand() > 0) courseLimit = course.getDemand(); else courseLimit = offeringLimit / offering.getCourseOfferings().size(); } } totalCourseLimit += courseLimit; } if (totalCourseLimit < offeringLimit) iProgress.message( msglevel("courseReservationsBelowLimit", totalCourseLimit == 0 ? Progress.MSGLEVEL_INFO : Progress.MSGLEVEL_WARN), "Total number of course reservations is below the offering limit for instructional offering " + getOfferingLabel(offering) + " (" + totalCourseLimit + "<" + offeringLimit + ")."); if (totalCourseLimit > offeringLimit) iProgress.message(msglevel("courseReservationsOverLimit", Progress.MSGLEVEL_INFO), "Total number of course reservations exceeds the offering limit for instructional offering " + getOfferingLabel(offering) + " (" + totalCourseLimit + ">" + offeringLimit + ")."); if (totalCourseLimit == 0) continue; if (totalCourseLimit != offeringLimit) factor = new Double(((double) offeringLimit) / totalCourseLimit); } for (CourseOffering course : offering.getCourseOfferings()) { Set<WeightedStudentId> studentIds = iStudentCourseDemands.getDemands(course); float studentWeight = 0.0f; if (studentIds != null) for (WeightedStudentId studentId : studentIds) studentWeight += studentId.getWeight(); int courseLimit = -1; if (course.getReservation() != null) courseLimit = course.getReservation(); if (courseLimit < 0) { if (offering.getCourseOfferings().size() == 1 && !unlimitedOffering) courseLimit = offeringLimit; else { courseLimit = Math.round(studentWeight); } } if (factor != null) courseLimit = (int) Math.round(courseLimit * factor); if (studentIds == null || studentIds.isEmpty()) { iProgress.message(msglevel("offeringWithoutDemand", Progress.MSGLEVEL_INFO), "No student enrollments for course " + getOfferingLabel(course) + "."); continue; } if (courseLimit == 0 && offering.getCourseOfferings().size() > 1) { iProgress.message(msglevel("noCourseReservation", Progress.MSGLEVEL_WARN), "No reserved space for students of course " + getOfferingLabel(course) + "."); } double weight = (iStudentCourseDemands.isWeightStudentsToFillUpOffering() && courseLimit != 0 ? (double) courseLimit / studentWeight : 1.0); Set<Lecture> cannotAttendLectures = null; if (offering.getCourseOfferings().size() > 1) { Set<Long> reservedClasses = new HashSet<Long>(); int limit = 0; boolean unlimited = false; for (Reservation r : offering.getReservations()) { if (r instanceof CourseReservation && course.equals(((CourseReservation) r).getCourse())) { for (Class_ clazz : r.getClasses()) { limit += clazz.getMaxExpectedCapacity(); propagateReservedClasses(clazz, reservedClasses); Class_ parent = clazz.getParentClass(); while (parent != null) { reservedClasses.add(parent.getUniqueId()); parent = parent.getParentClass(); } } for (InstrOfferingConfig config : r.getConfigurations()) { if (config.isUnlimitedEnrollment()) unlimited = true; else limit += config.getLimit(); for (SchedulingSubpart subpart : config.getSchedulingSubparts()) for (Class_ clazz : subpart.getClasses()) reservedClasses.add(clazz.getUniqueId()); } } } if (!reservedClasses.isEmpty()) { iProgress.debug("Course requests for course " + getOfferingLabel(course) + " are " + reservedClasses); if (!unlimited && courseLimit > limit) iProgress.message(msglevel("insufficientCourseReservation", Progress.MSGLEVEL_WARN), "Too little space reserved in for course " + getOfferingLabel(course) + " (" + limit + "<" + courseLimit + ")."); cannotAttendLectures = new HashSet<Lecture>(); for (InstrOfferingConfig config : course.getInstructionalOffering() .getInstrOfferingConfigs()) { boolean hasConfigReservation = false; subparts: for (SchedulingSubpart subpart : config.getSchedulingSubparts()) for (Class_ clazz : subpart.getClasses()) if (reservedClasses.contains(clazz.getUniqueId())) { hasConfigReservation = true; break subparts; } for (SchedulingSubpart subpart : config.getSchedulingSubparts()) { boolean hasSubpartReservation = false; for (Class_ clazz : subpart.getClasses()) if (reservedClasses.contains(clazz.getUniqueId())) { hasSubpartReservation = true; break; } // !hasConfigReservation >> all lectures are cannot attend (there is a reservation on a different config) // otherwise if !hasSubpartReservation >> there is reservation on some other subpoart --> can attend any of the classes of this subpart if (!hasConfigReservation || hasSubpartReservation) for (Class_ clazz : subpart.getClasses()) { if (reservedClasses.contains(clazz.getUniqueId())) continue; Lecture lecture = iLectures.get(clazz.getUniqueId()); if (lecture != null && !lecture.isCommitted()) cannotAttendLectures.add(lecture); } } } if (!cannotAttendLectures.isEmpty()) { iProgress.debug("Prohibited lectures for course " + getOfferingLabel(course) + " are " + cannotAttendLectures); checkReservation(course, cannotAttendLectures, iAltConfigurations.get(offering)); } } } for (WeightedStudentId studentId : studentIds) { Student student = iStudents.get(studentId.getStudentId()); if (student == null) { student = new Student(studentId.getStudentId()); student.setAcademicArea(studentId.getArea()); student.setAcademicClassification(studentId.getClasf()); student.setMajor(studentId.getMajor()); student.setCurriculum(studentId.getCurriculum()); getModel().addStudent(student); iStudents.put(studentId.getStudentId(), student); } student.addOffering(offering.getUniqueId(), weight * studentId.getWeight(), iStudentCourseDemands.getEnrollmentPriority(studentId.getStudentId(), course.getUniqueId())); Set<Student> students = iCourse2students.get(course); if (students == null) { students = new HashSet<Student>(); iCourse2students.put(course, students); } students.add(student); student.addCanNotEnroll(offering.getUniqueId(), cannotAttendLectures); Set<Long> reservedClasses = new HashSet<Long>(); for (Reservation reservation : offering.getReservations()) { if (reservation.getClasses().isEmpty() && reservation.getConfigurations().isEmpty()) continue; if (reservation instanceof CourseReservation) continue; if (reservation instanceof CurriculumReservation) { CurriculumReservation cr = (CurriculumReservation) reservation; if (studentId.getArea() == null) continue; if (!studentId.hasArea(cr.getArea().getAcademicAreaAbbreviation())) continue; if (!cr.getClassifications().isEmpty()) { boolean match = false; for (AcademicClassification clasf : cr.getClassifications()) { if (studentId.hasClassification(cr.getArea().getAcademicAreaAbbreviation(), clasf.getCode())) { match = true; break; } } if (!match) continue; } if (!cr.getMajors().isEmpty()) { if (studentId.getMajor() == null) continue; boolean match = false; for (PosMajor major : cr.getMajors()) { if (studentId.hasMajor(cr.getArea().getAcademicAreaAbbreviation(), major.getCode())) { match = true; break; } } if (!match) continue; } } else continue; for (Class_ clazz : reservation.getClasses()) { propagateReservedClasses(clazz, reservedClasses); Class_ parent = clazz.getParentClass(); while (parent != null) { reservedClasses.add(parent.getUniqueId()); parent = parent.getParentClass(); } } for (InstrOfferingConfig config : reservation.getConfigurations()) { for (SchedulingSubpart subpart : config.getSchedulingSubparts()) for (Class_ clazz : subpart.getClasses()) reservedClasses.add(clazz.getUniqueId()); } } if (!reservedClasses.isEmpty()) { iProgress.debug(course.getCourseName() + ": Student " + student.getId() + " has reserved classes " + reservedClasses); Set<Lecture> prohibited = new HashSet<Lecture>(); for (InstrOfferingConfig config : course.getInstructionalOffering() .getInstrOfferingConfigs()) { boolean hasConfigReservation = false; subparts: for (SchedulingSubpart subpart : config.getSchedulingSubparts()) for (Class_ clazz : subpart.getClasses()) if (reservedClasses.contains(clazz.getUniqueId())) { hasConfigReservation = true; break subparts; } for (SchedulingSubpart subpart : config.getSchedulingSubparts()) { boolean hasSubpartReservation = false; for (Class_ clazz : subpart.getClasses()) if (reservedClasses.contains(clazz.getUniqueId())) { hasSubpartReservation = true; break; } // !hasConfigReservation >> all lectures are cannot attend (there is a reservation on a different config) // otherwise if !hasSubpartReservation >> there is reservation on some other subpoart --> can attend any of the classes of this subpart if (!hasConfigReservation || hasSubpartReservation) for (Class_ clazz : subpart.getClasses()) { if (reservedClasses.contains(clazz.getUniqueId())) continue; Lecture lecture = iLectures.get(clazz.getUniqueId()); if (lecture != null && !lecture.isCommitted()) prohibited.add(lecture); } } } iProgress.debug(course.getCourseName() + ": Student " + student.getId() + " cannot attend classes " + prohibited); student.addCanNotEnroll(offering.getUniqueId(), prohibited); } } } iProgress.incProgress(); } iProgress.debug(iStudents.size() + " students loaded."); if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); if (iCommittedStudentConflictsMode == CommittedStudentConflictsMode.Load && !iStudentCourseDemands.isMakingUpStudents()) loadCommittedStudentConflicts(hibSession, loadedOfferings); else if (iCommittedStudentConflictsMode != CommittedStudentConflictsMode.Ignore) makeupCommittedStudentConflicts(loadedOfferings); if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); Hashtable<Student, Set<Lecture>> iPreEnrollments = new Hashtable<Student, Set<Lecture>>(); if (iLoadStudentEnrlsFromSolution) { if (iStudentCourseDemands.canUseStudentClassEnrollmentsAsSolution()) { // Load real student enrollments (not saved last-like) List<Object[]> enrollments = (List<Object[]>) hibSession .createQuery("select distinct e.student.uniqueId, e.clazz.uniqueId from " + "StudentClassEnrollment e, Class_ c where " + "e.courseOffering.instructionalOffering = c.schedulingSubpart.instrOfferingConfig.instructionalOffering and " + "c.managingDept.solverGroup.uniqueId in (" + iSolverGroupIds + ")") .list(); iProgress.setPhase("Loading current student enrolments ...", enrollments.size()); int totalEnrollments = 0; for (Object[] o : enrollments) { Long studentId = (Long) o[0]; Long clazzId = (Long) o[1]; Student student = (Student) iStudents.get(studentId); if (student == null) continue; Lecture lecture = (Lecture) iLectures.get(clazzId); if (lecture != null) { Set<Lecture> preEnrollments = iPreEnrollments.get(student); if (preEnrollments == null) { preEnrollments = new HashSet<Lecture>(); iPreEnrollments.put(student, preEnrollments); } preEnrollments.add(lecture); if (student.hasOffering(lecture.getConfiguration().getOfferingId()) && student.canEnroll(lecture)) { student.addLecture(lecture); lecture.addStudent(getAssignment(), student); totalEnrollments++; } } iProgress.incProgress(); } iProgress.message(msglevel("enrollmentsLoaded", Progress.MSGLEVEL_INFO), "Loaded " + totalEnrollments + " enrollments of " + iPreEnrollments.size() + " students."); } else { // Load enrollments from selected / committed solutions for (int idx = 0; idx < iSolverGroupId.length; idx++) { Solution solution = (solutions == null ? null : solutions.get(iSolverGroupId[idx])); List studentEnrls = null; if (solution != null) { studentEnrls = hibSession.createQuery( "select distinct e.studentId, e.clazz.uniqueId from StudentEnrollment e where e.solution.uniqueId=:solutionId") .setLong("solutionId", solution.getUniqueId()).list(); } else { studentEnrls = hibSession.createQuery( "select distinct e.studentId, e.clazz.uniqueId from StudentEnrollment e where e.solution.owner.uniqueId=:sovlerGroupId and e.solution.commited = true") .setLong("sovlerGroupId", iSolverGroupId[idx]).list(); } iProgress.setPhase("Loading student enrolments [" + (idx + 1) + "] ...", studentEnrls.size()); for (Iterator i1 = studentEnrls.iterator(); i1.hasNext();) { Object o[] = (Object[]) i1.next(); Long studentId = (Long) o[0]; Long clazzId = (Long) o[1]; Student student = (Student) iStudents.get(studentId); if (student == null) continue; Lecture lecture = (Lecture) iLectures.get(clazzId); if (lecture != null && lecture.getConfiguration() != null) { Set<Lecture> preEnrollments = iPreEnrollments.get(student); if (preEnrollments == null) { preEnrollments = new HashSet<Lecture>(); iPreEnrollments.put(student, preEnrollments); } preEnrollments.add(lecture); if (student.hasOffering(lecture.getConfiguration().getOfferingId()) && student.canEnroll(lecture)) { student.addLecture(lecture); lecture.addStudent(getAssignment(), student); } } iProgress.incProgress(); } } if (getModel().getProperties().getPropertyBoolean("Global.LoadOtherCommittedStudentEnrls", true)) { // Other committed enrollments List<Object[]> enrollments = (List<Object[]>) hibSession .createQuery("select distinct e.studentId, e.clazz.uniqueId from " + "StudentEnrollment e, Class_ c where " + "e.solution.commited = true and e.solution.owner.uniqueId not in (" + iSolverGroupIds + ") and " + "e.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering = c.schedulingSubpart.instrOfferingConfig.instructionalOffering and " + "c.managingDept.solverGroup.uniqueId in (" + iSolverGroupIds + ")") .list(); iProgress.setPhase("Loading other committed student enrolments ...", enrollments.size()); for (Object[] o : enrollments) { Long studentId = (Long) o[0]; Long clazzId = (Long) o[1]; Student student = (Student) iStudents.get(studentId); if (student == null) continue; Lecture lecture = (Lecture) iLectures.get(clazzId); if (lecture != null && lecture.getConfiguration() != null) { Set<Lecture> preEnrollments = iPreEnrollments.get(student); if (preEnrollments == null) { preEnrollments = new HashSet<Lecture>(); iPreEnrollments.put(student, preEnrollments); } preEnrollments.add(lecture); if (student.hasOffering(lecture.getConfiguration().getOfferingId()) && student.canEnroll(lecture)) { student.addLecture(lecture); lecture.addStudent(getAssignment(), student); } } iProgress.incProgress(); } } } } if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); RoomAvailabilityInterface availability = null; if (SolverServerImplementation.getInstance() != null) availability = SolverServerImplementation.getInstance().getRoomAvailability(); else availability = RoomAvailability.getInstance(); if (availability != null) { Date[] startEnd = initializeRoomAvailability(availability); if (startEnd != null) { loadRoomAvailability(availability, startEnd); loadInstructorAvailability(availability, startEnd); } } if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); iProgress.setPhase("Initial sectioning ...", iOfferings.size()); for (InstructionalOffering offering : iOfferings.keySet()) { Set<Student> students = new HashSet<Student>(); for (CourseOffering course : offering.getCourseOfferings()) { Set<Student> courseStudents = iCourse2students.get(course); if (courseStudents != null) students.addAll(courseStudents); } if (students.isEmpty()) continue; getModel().getStudentSectioning().initialSectioning(getAssignment(), offering.getUniqueId(), offering.getCourseName(), students, iAltConfigurations.get(offering)); iProgress.incProgress(); } for (Enumeration e = iStudents.elements(); e.hasMoreElements();) { ((Student) e.nextElement()).clearDistanceCache(); } if (!iPreEnrollments.isEmpty()) { iProgress.setPhase("Checking loaded enrollments ....", iPreEnrollments.size()); for (Map.Entry<Student, Set<Lecture>> entry : iPreEnrollments.entrySet()) { iProgress.incProgress(); Student student = entry.getKey(); Set<Lecture> lectures = entry.getValue(); for (Lecture lecture : lectures) { if (!lecture.students().contains(student)) { iProgress.message(msglevel("studentNotEnrolled", Progress.MSGLEVEL_WARN), "Student " + student.getId() + " is supposed to be enrolled to " + getClassLabel(lecture)); } } for (Lecture lecture : student.getLectures()) { if (!lectures.contains(lecture)) { Lecture instead = null; if (lecture.sameStudentsLectures() != null) { for (Lecture other : lecture.sameStudentsLectures()) { if (lectures.contains(other)) instead = other; } } if (instead != null) iProgress.message(msglevel("studentEnrolled", Progress.MSGLEVEL_WARN), "Student " + student.getId() + " is NOT supposed to be enrolled to " + getClassLabel(lecture) + ", he/she should have " + getClassLabel(instead) + " instead."); else iProgress.message(msglevel("studentEnrolled", Progress.MSGLEVEL_INFO), "Student " + student.getId() + " is NOT supposed to be enrolled to " + getClassLabel(lecture) + "."); } } } } if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); if (iLoadStudentInstructorConflicts) loadInstructorStudentConflicts(hibSession); iProgress.setPhase("Computing jenrl ...", iStudents.size()); Hashtable jenrls = new Hashtable(); for (Iterator i1 = iStudents.values().iterator(); i1.hasNext();) { Student st = (Student) i1.next(); for (Iterator i2 = st.getLectures().iterator(); i2.hasNext();) { Lecture l1 = (Lecture) i2.next(); for (Iterator i3 = st.getLectures().iterator(); i3.hasNext();) { Lecture l2 = (Lecture) i3.next(); if (l1.getId() >= l2.getId()) continue; Hashtable x = (Hashtable) jenrls.get(l1); if (x == null) { x = new Hashtable(); jenrls.put(l1, x); } JenrlConstraint jenrl = (JenrlConstraint) x.get(l2); if (jenrl == null) { jenrl = new JenrlConstraint(); getModel().addConstraint(jenrl); jenrl.addVariable(l1); jenrl.addVariable(l2); x.put(l2, jenrl); } jenrl.incJenrl(getAssignment(), st); } } iProgress.incProgress(); } if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); if (solutions != null) { for (int idx = 0; idx < iSolverGroupId.length; idx++) { Solution solution = (Solution) solutions.get(iSolverGroupId[idx]); if (solution == null) continue; iProgress.setPhase("Creating initial assignment [" + (idx + 1) + "] ...", solution.getAssignments().size()); for (Iterator i1 = solution.getAssignments().iterator(); i1.hasNext();) { Assignment assignment = (Assignment) i1.next(); loadAssignment(assignment); iProgress.incProgress(); } } } else if (iLoadCommittedAssignments) { iProgress.setPhase("Creating initial assignment ...", getModel().variables().size()); for (Lecture lecture : getModel().variables()) { if (lecture.isCommitted()) continue; Class_ clazz = iClasses.get(lecture.getClassId()); if (clazz != null && clazz.getCommittedAssignment() != null) loadAssignment(clazz.getCommittedAssignment()); iProgress.incProgress(); } } if (!hibSession.isOpen()) iProgress.message(msglevel("hibernateFailure", Progress.MSGLEVEL_FATAL), "Hibernate session not open."); if (iSpread) { iProgress.setPhase("Posting automatic spread constraints ...", subparts.size()); for (SchedulingSubpart subpart : subparts) { if (subpart.getClasses().size() <= 1) { iProgress.incProgress(); continue; } if (!subpart.isAutoSpreadInTime().booleanValue()) { iProgress.debug("Automatic spread constraint disabled for " + getSubpartLabel(subpart)); iProgress.incProgress(); continue; } SpreadConstraint spread = new SpreadConstraint(getModel().getProperties(), subpart.getCourseName() + " " + subpart.getItypeDesc().trim()); for (Iterator i2 = subpart.getClasses().iterator(); i2.hasNext();) { Class_ clazz = (Class_) i2.next(); Lecture lecture = (Lecture) getLecture(clazz); if (lecture == null) continue; spread.addVariable(lecture); } if (spread.variables().isEmpty()) iProgress.message(msglevel("courseWithNoClasses", Progress.MSGLEVEL_WARN), "No class for course " + getSubpartLabel(subpart)); else getModel().addConstraint(spread); iProgress.incProgress(); } } if (iDeptBalancing) { iProgress.setPhase("Creating dept. spread constraints ...", getModel().variables().size()); Hashtable<Long, DepartmentSpreadConstraint> depSpreadConstraints = new Hashtable<Long, DepartmentSpreadConstraint>(); for (Lecture lecture : getModel().variables()) { if (lecture.getDepartment() == null) continue; DepartmentSpreadConstraint deptConstr = (DepartmentSpreadConstraint) depSpreadConstraints .get(lecture.getDepartment()); if (deptConstr == null) { deptConstr = new DepartmentSpreadConstraint(getModel().getProperties(), lecture.getDepartment(), (String) iDeptNames.get(lecture.getDepartment())); depSpreadConstraints.put(lecture.getDepartment(), deptConstr); getModel().addConstraint(deptConstr); } deptConstr.addVariable(lecture); iProgress.incProgress(); } } if (iSubjectBalancing) { iProgress.setPhase("Creating subject spread constraints ...", getModel().variables().size()); Hashtable<Long, SpreadConstraint> subjectSpreadConstraints = new Hashtable<Long, SpreadConstraint>(); for (Lecture lecture : getModel().variables()) { Class_ clazz = iClasses.get(lecture.getClassId()); if (clazz == null) continue; for (CourseOffering co : clazz.getSchedulingSubpart().getInstrOfferingConfig() .getInstructionalOffering().getCourseOfferings()) { Long subject = co.getSubjectArea().getUniqueId(); SpreadConstraint subjectSpreadConstr = subjectSpreadConstraints.get(subject); if (subjectSpreadConstr == null) { subjectSpreadConstr = new SpreadConstraint(getModel().getProperties(), co.getSubjectArea().getSubjectAreaAbbreviation()); subjectSpreadConstraints.put(subject, subjectSpreadConstr); getModel().addConstraint(subjectSpreadConstr); } subjectSpreadConstr.addVariable(lecture); } iProgress.incProgress(); } } if (getModel().getProperties().getPropertyBoolean("General.PurgeInvalidPlacements", true)) purgeInvalidValues(); /* for (Constraint c: getModel().constraints()) { if (c instanceof SpreadConstraint) ((SpreadConstraint)c).init(); if (c instanceof DiscouragedRoomConstraint) ((DiscouragedRoomConstraint)c).setEnabled(true); if (c instanceof MinimizeNumberOfUsedRoomsConstraint) ((MinimizeNumberOfUsedRoomsConstraint)c).setEnabled(true); if (c instanceof MinimizeNumberOfUsedGroupsOfTime) ((MinimizeNumberOfUsedGroupsOfTime)c).setEnabled(true); } */ iProgress.setPhase("Checking for inconsistencies...", getModel().variables().size()); for (Lecture lecture : getModel().variables()) { iProgress.incProgress(); for (Iterator i = lecture.students().iterator(); i.hasNext();) { Student s = (Student) i.next(); if (!s.canEnroll(lecture)) iProgress.message(msglevel("badStudentEnrollment", Progress.MSGLEVEL_INFO), "Invalid student enrollment of student " + s.getId() + " in class " + getClassLabel(lecture) + " found."); } //check same instructor constraint if (!lecture.values(getAssignment()).isEmpty() && lecture.timeLocations().size() == 1 && !lecture.getInstructorConstraints().isEmpty()) { for (Lecture other : getModel().variables()) { if (other.values(getAssignment()).isEmpty() || other.timeLocations().size() != 1 || lecture.getClassId().compareTo(other.getClassId()) <= 0) continue; Placement p1 = lecture.values(getAssignment()).get(0); Placement p2 = other.values(getAssignment()).get(0); if (!other.getInstructorConstraints().isEmpty()) { for (InstructorConstraint ic : lecture.getInstructorConstraints()) { if (!other.getInstructorConstraints().contains(ic)) continue; if (p1.canShareRooms(p2) && p1.sameRooms(p2)) continue; if (p1.getTimeLocation().hasIntersection(p2.getTimeLocation())) { iProgress.message(msglevel("reqInstructorOverlap", Progress.MSGLEVEL_WARN), "Same instructor and overlapping time required:" + "<br> " + getClassLabel(lecture) + " ← " + p1.getLongName(iUseAmPm) + "<br> " + getClassLabel(other) + " ← " + p2.getLongName(iUseAmPm)); } else if (ic.getDistancePreference(p1, p2) == PreferenceLevel.sIntLevelProhibited && lecture.roomLocations().size() == 1 && other.roomLocations().size() == 1) { iProgress.message(msglevel("reqInstructorBackToBack", Progress.MSGLEVEL_WARN), "Same instructor, back-to-back time and rooms too far (distance=" + Math.round(10.0 * Placement.getDistanceInMeters( getModel().getDistanceMetric(), p1, p2)) + "m) required:" + "<br> " + getClassLabel(lecture) + " ← " + p1.getLongName(iUseAmPm) + "<br> " + getClassLabel(other) + " ← " + p2.getLongName(iUseAmPm)); } } } } } if (!lecture.isSingleton()) continue; for (Lecture other : getModel().variables()) { if (!other.isSingleton() || lecture.getClassId().compareTo(other.getClassId()) <= 0) continue; Placement p1 = new Placement(lecture, lecture.timeLocations().get(0), lecture.roomLocations()); Placement p2 = new Placement(other, other.timeLocations().get(0), other.roomLocations()); if (p1.shareRooms(p2) && p1.getTimeLocation().hasIntersection(p2.getTimeLocation()) && !p1.canShareRooms(p2)) { iProgress.message(msglevel("reqRoomOverlap", Progress.MSGLEVEL_WARN), "Same room and overlapping time required:" + "<br> " + getClassLabel(lecture) + " ← " + p1.getLongName(iUseAmPm) + "<br> " + getClassLabel(other) + " ← " + p2.getLongName(iUseAmPm)); } } if (getAssignment().getValue(lecture) == null) { Placement placement = new Placement(lecture, lecture.timeLocations().get(0), lecture.roomLocations()); if (!placement.isValid()) { String reason = ""; for (InstructorConstraint ic : lecture.getInstructorConstraints()) { if (!ic.isAvailable(lecture, placement)) reason += "<br> instructor " + ic.getName() + " not available"; } if (lecture.getNrRooms() > 0) { if (placement.isMultiRoom()) { for (RoomLocation roomLocation : placement.getRoomLocations()) { if (!roomLocation.getRoomConstraint().isAvailable(lecture, placement.getTimeLocation(), lecture.getScheduler())) reason += "<br> room " + roomLocation.getName() + " not available"; } } else { if (!placement.getRoomLocation().getRoomConstraint().isAvailable(lecture, placement.getTimeLocation(), lecture.getScheduler())) reason += "<br> room " + placement.getRoomLocation().getName() + " not available"; } } Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel() .conflictConstraints(getAssignment(), placement); if (!conflictConstraints.isEmpty()) { for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { Set<Placement> vals = conflictConstraints.get(c); for (Placement p : vals) { Lecture l = p.variable(); if (l.isCommitted()) reason += "<br> conflict with committed assignment " + getClassLabel(l) + " = " + p.getLongName(iUseAmPm) + " (in constraint " + c + ")"; if (p.equals(placement)) reason += "<br> constraint " + c; } } } iProgress.message(msglevel("reqInvalidPlacement", Progress.MSGLEVEL_WARN), "Class " + getClassLabel(lecture) + " requires an invalid placement " + placement.getLongName(iUseAmPm) + (reason.length() == 0 ? "." : ":" + reason)); } else if (iAssignSingleton && getModel().conflictValues(getAssignment(), placement).isEmpty()) getAssignment().assign(0, placement); } } getModel().createAssignmentContexts(getAssignment(), true); if (getModel().getProperties().getPropertyBoolean("General.EnrollmentCheck", true)) new EnrollmentCheck(getModel(), getAssignment(), msglevel("enrollmentCheck", Progress.MSGLEVEL_WARN)) .checkStudentEnrollments(iProgress); if (getModel().getProperties().getPropertyBoolean("General.SwitchStudents", true) && getAssignment().nrAssignedVariables() != 0 && !iLoadStudentEnrlsFromSolution) getModel().switchStudents(getAssignment()); iProgress.setPhase("Done", 1); iProgress.incProgress(); iProgress.message(msglevel("allDone", Progress.MSGLEVEL_INFO), "Model successfully loaded."); } public static class ObjectsByGivenOrderComparator implements Comparator { List<?> iOrderedSet = null; public ObjectsByGivenOrderComparator(List<?> orderedSetOfLectures) { iOrderedSet = orderedSetOfLectures; } public int compare(Object o1, Object o2) { int idx1 = iOrderedSet.indexOf(o1); int idx2 = iOrderedSet.indexOf(o2); int cmp = Double.compare(idx1, idx2); if (cmp != 0) return cmp; return ((Comparable) o1).compareTo(o2); } } public void roomAvailabilityActivate(RoomAvailabilityInterface availability, Date startTime, Date endTime) { try { availability.activate(new SessionDAO().get(iSessionId), startTime, endTime, RoomAvailabilityInterface.sClassType, ApplicationProperty.RoomAvailabilitySolverWaitForSync.isTrue()); } catch (Exception e) { sLog.error(e.getMessage(), e); iProgress.message(msglevel("roomAvailabilityFailure", Progress.MSGLEVEL_WARN), "Unable to access room availability service, reason:" + e.getMessage()); } } public Date[] initializeRoomAvailability(RoomAvailabilityInterface availability) { Date startDate = null, endDate = null; for (Iterator i = iAllUsedDatePatterns.iterator(); i.hasNext();) { DatePattern dp = (DatePattern) i.next(); if (startDate == null || startDate.compareTo(dp.getStartDate()) > 0) startDate = dp.getStartDate(); if (endDate == null || endDate.compareTo(dp.getEndDate()) < 0) endDate = dp.getEndDate(); } if (startDate == null || endDate == null) { iProgress.message(msglevel("roomAvailabilityFailure", Progress.MSGLEVEL_WARN), "Unable to load room availability, reason: no dates"); return null; } Calendar startDateCal = Calendar.getInstance(Locale.US); startDateCal.setTime(startDate); startDateCal.set(Calendar.HOUR_OF_DAY, 0); startDateCal.set(Calendar.MINUTE, 0); startDateCal.set(Calendar.SECOND, 0); Calendar endDateCal = Calendar.getInstance(Locale.US); endDateCal.setTime(endDate); endDateCal.set(Calendar.HOUR_OF_DAY, 23); endDateCal.set(Calendar.MINUTE, 59); endDateCal.set(Calendar.SECOND, 59); roomAvailabilityActivate(availability, startDateCal.getTime(), endDateCal.getTime()); return new Date[] { startDateCal.getTime(), endDateCal.getTime() }; } public void loadRoomAvailability(RoomAvailabilityInterface availability, Date[] startEnd) { iProgress.setPhase("Loading room availability...", iRooms.size()); int firstDOY = iSession.getDayOfYear(1, iSession.getPatternStartMonth()); int lastDOY = iSession.getDayOfYear(0, iSession.getPatternEndMonth() + 1); int size = lastDOY - firstDOY; Calendar c = Calendar.getInstance(Locale.US); Formats.Format<Date> df = Formats.getDateFormat(Formats.Pattern.DATE_PATTERN); int sessionYear = iSession.getSessionStartYear(); for (Enumeration e = iRooms.elements(); e.hasMoreElements();) { RoomConstraint room = (RoomConstraint) e.nextElement(); iProgress.incProgress(); if (!room.getConstraint()) continue; Collection<TimeBlock> times = getRoomAvailability(availability, room, startEnd[0], startEnd[1]); if (times == null) continue; for (TimeBlock time : times) { iProgress.debug(room.getName() + " not available due to " + time); int dayCode = 0; c.setTime(time.getStartTime()); int m = c.get(Calendar.MONTH); int d = c.get(Calendar.DAY_OF_MONTH); if (c.get(Calendar.YEAR) < sessionYear) m -= (12 * (sessionYear - c.get(Calendar.YEAR))); if (c.get(Calendar.YEAR) > sessionYear) m += (12 * (c.get(Calendar.YEAR) - sessionYear)); BitSet weekCode = new BitSet(size); int offset = iSession.getDayOfYear(d, m) - firstDOY; if (offset < 0 || offset >= size) continue; weekCode.set(offset); switch (c.get(Calendar.DAY_OF_WEEK)) { case Calendar.MONDAY: dayCode = Constants.DAY_CODES[Constants.DAY_MON]; break; case Calendar.TUESDAY: dayCode = Constants.DAY_CODES[Constants.DAY_TUE]; break; case Calendar.WEDNESDAY: dayCode = Constants.DAY_CODES[Constants.DAY_WED]; break; case Calendar.THURSDAY: dayCode = Constants.DAY_CODES[Constants.DAY_THU]; break; case Calendar.FRIDAY: dayCode = Constants.DAY_CODES[Constants.DAY_FRI]; break; case Calendar.SATURDAY: dayCode = Constants.DAY_CODES[Constants.DAY_SAT]; break; case Calendar.SUNDAY: dayCode = Constants.DAY_CODES[Constants.DAY_SUN]; break; } int startSlot = (c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE) - Constants.FIRST_SLOT_TIME_MIN) / Constants.SLOT_LENGTH_MIN; c.setTime(time.getEndTime()); int endSlot = (c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE) - Constants.FIRST_SLOT_TIME_MIN) / Constants.SLOT_LENGTH_MIN; if (endSlot == 0 && c.get(Calendar.DAY_OF_MONTH) != d) endSlot = 288; // next day midnight int length = endSlot - startSlot; if (length <= 0) continue; TimeLocation timeLocation = new TimeLocation(dayCode, startSlot, length, 0, 0, null, df.format(time.getStartTime()), weekCode, 0); List<TimeLocation> timeLocations = new ArrayList<TimeLocation>(1); timeLocations.add(timeLocation); RoomLocation roomLocation = new RoomLocation(room.getResourceId(), room.getName(), room.getBuildingId(), 0, room.getCapacity(), room.getPosX(), room.getPosY(), room.getIgnoreTooFar(), room); List<RoomLocation> roomLocations = new ArrayList<RoomLocation>(1); roomLocations.add(roomLocation); Lecture lecture = new Lecture(new Long(--iFakeLectureId), null, null, time.getEventName(), timeLocations, roomLocations, 1, new Placement(null, timeLocation, roomLocations), 0, 0, 1.0); lecture.setNote(time.getEventType()); Placement p = (Placement) lecture.getInitialAssignment(); lecture.setBestAssignment(p, 0); lecture.setCommitted(true); room.setNotAvailable(p); getModel().addVariable(p.variable()); } } } public Collection<TimeBlock> getRoomAvailability(RoomAvailabilityInterface availability, RoomConstraint room, Date startTime, Date endTime) { Collection<TimeBlock> ret = null; String ts = null; try { ret = availability.getRoomAvailability(room.getResourceId(), startTime, endTime, RoomAvailabilityInterface.sClassType); if (!iRoomAvailabilityTimeStampIsSet) ts = availability.getTimeStamp(startTime, endTime, RoomAvailabilityInterface.sClassType); } catch (Exception e) { sLog.error(e.getMessage(), e); iProgress.message(msglevel("roomAvailabilityFailure", Progress.MSGLEVEL_WARN), "Unable to access room availability service, reason:" + e.getMessage()); } if (!iRoomAvailabilityTimeStampIsSet) { iRoomAvailabilityTimeStampIsSet = true; if (ts != null) { getModel().getProperties().setProperty("RoomAvailability.TimeStamp", ts); iProgress.message(msglevel("roomAvailabilityUpdated", Progress.MSGLEVEL_INFO), "Using room availability that was updated on " + ts + "."); } else { iProgress.message(msglevel("roomAvailabilityFailure", Progress.MSGLEVEL_ERROR), "Room availability is not available."); } } return ret; } public void loadInstructorAvailability(RoomAvailabilityInterface availability, Date[] startEnd) { iProgress.setPhase("Loading instructor availability...", getModel().getInstructorConstraints().size()); int firstDOY = iSession.getDayOfYear(1, iSession.getPatternStartMonth()); int lastDOY = iSession.getDayOfYear(0, iSession.getPatternEndMonth() + 1); int size = lastDOY - firstDOY; Calendar c = Calendar.getInstance(Locale.US); Formats.Format<Date> df = Formats.getDateFormat(Formats.Pattern.DATE_PATTERN); int sessionYear = iSession.getSessionStartYear(); for (InstructorConstraint instructor : getModel().getInstructorConstraints()) { iProgress.incProgress(); Collection<TimeBlock> times = getInstructorAvailability(availability, instructor, startEnd[0], startEnd[1]); if (times == null) continue; for (TimeBlock time : times) { iProgress.debug(instructor.getName() + " not available due to " + time); int dayCode = 0; c.setTime(time.getStartTime()); int m = c.get(Calendar.MONTH); int d = c.get(Calendar.DAY_OF_MONTH); if (c.get(Calendar.YEAR) < sessionYear) m -= (12 * (sessionYear - c.get(Calendar.YEAR))); if (c.get(Calendar.YEAR) > sessionYear) m += (12 * (c.get(Calendar.YEAR) - sessionYear)); BitSet weekCode = new BitSet(size); int offset = iSession.getDayOfYear(d, m) - firstDOY; if (offset < 0 || offset >= size) continue; weekCode.set(offset); switch (c.get(Calendar.DAY_OF_WEEK)) { case Calendar.MONDAY: dayCode = Constants.DAY_CODES[Constants.DAY_MON]; break; case Calendar.TUESDAY: dayCode = Constants.DAY_CODES[Constants.DAY_TUE]; break; case Calendar.WEDNESDAY: dayCode = Constants.DAY_CODES[Constants.DAY_WED]; break; case Calendar.THURSDAY: dayCode = Constants.DAY_CODES[Constants.DAY_THU]; break; case Calendar.FRIDAY: dayCode = Constants.DAY_CODES[Constants.DAY_FRI]; break; case Calendar.SATURDAY: dayCode = Constants.DAY_CODES[Constants.DAY_SAT]; break; case Calendar.SUNDAY: dayCode = Constants.DAY_CODES[Constants.DAY_SUN]; break; } int startSlot = (c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE) - Constants.FIRST_SLOT_TIME_MIN) / Constants.SLOT_LENGTH_MIN; c.setTime(time.getEndTime()); int endSlot = (c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE) - Constants.FIRST_SLOT_TIME_MIN) / Constants.SLOT_LENGTH_MIN; if (endSlot == 0 && c.get(Calendar.DAY_OF_MONTH) != d) endSlot = 288; // next day midnight int length = endSlot - startSlot; if (length <= 0) continue; TimeLocation timeLocation = new TimeLocation(dayCode, startSlot, length, 0, 0, null, df.format(time.getStartTime()), weekCode, 0); List<TimeLocation> timeLocations = new ArrayList<TimeLocation>(1); timeLocations.add(timeLocation); Lecture lecture = new Lecture(new Long(--iFakeLectureId), null, null, time.getEventName(), timeLocations, new ArrayList<RoomLocation>(), 0, new Placement(null, timeLocation, (RoomLocation) null), 0, 0, 1.0); lecture.setNote(time.getEventType()); Placement p = (Placement) lecture.getInitialAssignment(); lecture.setBestAssignment(p, 0); lecture.setCommitted(true); instructor.setNotAvailable(p); getModel().addVariable(p.variable()); } } } public Collection<TimeBlock> getInstructorAvailability(RoomAvailabilityInterface availability, InstructorConstraint instructor, Date startTime, Date endTime) { Collection<TimeBlock> ret = null; String ts = null; try { ret = availability.getInstructorAvailability(instructor.getResourceId(), startTime, endTime, RoomAvailabilityInterface.sClassType); if (!iRoomAvailabilityTimeStampIsSet) ts = availability.getTimeStamp(startTime, endTime, RoomAvailabilityInterface.sClassType); } catch (Exception e) { sLog.error(e.getMessage(), e); iProgress.message(msglevel("roomAvailabilityFailure", Progress.MSGLEVEL_WARN), "Unable to access room availability service, reason:" + e.getMessage()); } if (!iRoomAvailabilityTimeStampIsSet) { iRoomAvailabilityTimeStampIsSet = true; if (ts != null) { getModel().getProperties().setProperty("RoomAvailability.TimeStamp", ts); iProgress.message(msglevel("roomAvailabilityUpdated", Progress.MSGLEVEL_INFO), "Using room availability that was updated on " + ts + "."); } else { iProgress.message(msglevel("roomAvailabilityFailure", Progress.MSGLEVEL_ERROR), "Room availability is not available."); } } return ret; } protected void postAutomaticHierarchicalConstraints() { String automaticHierarchicalConstraints = getModel().getProperties() .getProperty("General.AutomaticHierarchicalConstraints"); while (automaticHierarchicalConstraints != null && !automaticHierarchicalConstraints.isEmpty()) { while (automaticHierarchicalConstraints.startsWith(" ") || automaticHierarchicalConstraints.startsWith(",") || automaticHierarchicalConstraints.startsWith(";")) automaticHierarchicalConstraints = automaticHierarchicalConstraints.substring(1); if (automaticHierarchicalConstraints.isEmpty()) break; PreferenceLevel pref = null; for (PreferenceLevel p : PreferenceLevel.getPreferenceLevelList()) { if (automaticHierarchicalConstraints.toLowerCase() .startsWith(p.getPrefName().toLowerCase() + " ")) { pref = p; automaticHierarchicalConstraints = automaticHierarchicalConstraints .substring(p.getPrefName().length() + 1); break; } else if (automaticHierarchicalConstraints.startsWith(p.getPrefProlog() + " ")) { pref = p; automaticHierarchicalConstraints = automaticHierarchicalConstraints .substring(p.getPrefProlog().length() + 1); break; } } if (pref == null) { iProgress.message(msglevel("automaticHierarchicalConstraints", Progress.MSGLEVEL_WARN), "Failed to parse automatic hierarchical constraint preference " + automaticHierarchicalConstraints); break; } while (automaticHierarchicalConstraints.startsWith(" ") || automaticHierarchicalConstraints.startsWith(":")) automaticHierarchicalConstraints = automaticHierarchicalConstraints.substring(1); GroupConstraint.ConstraintType type = null; for (GroupConstraint.ConstraintType t : GroupConstraint.ConstraintType.values()) { if (automaticHierarchicalConstraints.toLowerCase().startsWith(t.getName().toLowerCase() + " ") || automaticHierarchicalConstraints.toLowerCase() .startsWith(t.getName().toLowerCase() + ",") || automaticHierarchicalConstraints.toLowerCase() .startsWith(t.getName().toLowerCase() + ";")) { type = t; automaticHierarchicalConstraints = automaticHierarchicalConstraints .substring(t.getName().length() + 1); break; } else if (automaticHierarchicalConstraints.toLowerCase().startsWith(t.name().toLowerCase() + " ") || automaticHierarchicalConstraints.toLowerCase().startsWith(t.name().toLowerCase() + ",") || automaticHierarchicalConstraints.toLowerCase() .startsWith(t.name().toLowerCase() + ";")) { type = t; automaticHierarchicalConstraints = automaticHierarchicalConstraints .substring(t.getName().length() + 1); break; } else if (automaticHierarchicalConstraints.equalsIgnoreCase(t.getName()) || automaticHierarchicalConstraints.equalsIgnoreCase(t.name())) { type = t; automaticHierarchicalConstraints = ""; break; } } if (type == null) { iProgress.message(msglevel("automaticHierarchicalConstraints", Progress.MSGLEVEL_WARN), "Failed to parse automatic hierarchical constraint type " + automaticHierarchicalConstraints); break; } DatePattern pattern = null; for (DatePattern p : (Set<DatePattern>) DatePattern.findAllUsed(iSessionId)) { if (automaticHierarchicalConstraints.toLowerCase().startsWith(p.getName().toLowerCase() + " ") || automaticHierarchicalConstraints.toLowerCase() .startsWith(p.getName().toLowerCase() + ",") || automaticHierarchicalConstraints.toLowerCase() .startsWith(p.getName().toLowerCase() + ";")) { automaticHierarchicalConstraints = automaticHierarchicalConstraints .substring(p.getName().length() + 1); pattern = p; break; } else if (automaticHierarchicalConstraints.equalsIgnoreCase(p.getName())) { automaticHierarchicalConstraints = ""; pattern = p; break; } } iProgress.setPhase( "Posting automatic " + pref.getPrefName() + " " + type.getName() + " constraints" + (pattern == null ? "" : " between classes of pattern " + pattern.getName()) + "...", iAllClasses.size()); for (Iterator i1 = iAllClasses.iterator(); i1.hasNext();) { Class_ clazz = (Class_) i1.next(); Lecture lecture = (Lecture) iLectures.get(clazz.getUniqueId()); if (lecture == null) continue; if (!lecture.hasAnyChildren()) postAutomaticHierarchicalConstraint(clazz, type, pref.getPrefProlog(), pattern); iProgress.incProgress(); } } } protected boolean postAutomaticHierarchicalConstraint(Class_ clazz, GroupConstraint.ConstraintType type, String preference, DatePattern pattern) { boolean posted = false; if (!clazz.getChildClasses().isEmpty()) { for (Iterator i = clazz.getChildClasses().iterator(); i.hasNext();) { Class_ c = (Class_) i.next(); if (postAutomaticHierarchicalConstraint(c, type, preference, pattern)) posted = true; } } if (posted) return true; if (getLecture(clazz) == null) return false; if (pattern != null && !pattern.equals(clazz.effectiveDatePattern())) return false; List<Lecture> variables = new ArrayList<Lecture>(); Class_ parent = clazz; while (parent != null) { if (pattern == null || pattern.equals(parent.effectiveDatePattern())) { Lecture lecture = getLecture(parent); if (lecture != null) variables.add(0, lecture); } parent = parent.getParentClass(); } if (variables.size() <= 1) return false; GroupConstraint gc = new GroupConstraint(null, type, preference); String info = ""; for (Lecture var : variables) { gc.addVariable(var); if (!info.isEmpty()) info += ", "; info += getClassLabel(var); } iProgress.info("Posted " + gc.getName() + " constraint between " + info + " (" + PreferenceLevel.prolog2string(preference) + ")"); addGroupConstraint(gc); return true; } }