001package net.sf.cpsolver.coursett.model; 002 003import java.util.Collection; 004import java.util.HashSet; 005import java.util.HashMap; 006import java.util.Map; 007import java.util.Set; 008 009import net.sf.cpsolver.coursett.constraint.InstructorConstraint; 010import net.sf.cpsolver.coursett.constraint.JenrlConstraint; 011 012/** 013 * Student. 014 * 015 * @version CourseTT 1.2 (University Course Timetabling)<br> 016 * Copyright (C) 2006 - 2010 Tomáš Müller<br> 017 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 018 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 019 * <br> 020 * This library is free software; you can redistribute it and/or modify 021 * it under the terms of the GNU Lesser General Public License as 022 * published by the Free Software Foundation; either version 3 of the 023 * License, or (at your option) any later version. <br> 024 * <br> 025 * This library is distributed in the hope that it will be useful, but 026 * WITHOUT ANY WARRANTY; without even the implied warranty of 027 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 028 * Lesser General Public License for more details. <br> 029 * <br> 030 * You should have received a copy of the GNU Lesser General Public 031 * License along with this library; if not see 032 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 033 */ 034public class Student implements Comparable<Student> { 035 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Student.class); 036 public static boolean USE_DISTANCE_CACHE = false; 037 Long iStudentId = null; 038 HashMap<Long, Double> iOfferings = new HashMap<Long, Double>(); 039 Set<Lecture> iLectures = new HashSet<Lecture>(); 040 Set<Configuration> iConfigurations = new HashSet<Configuration>(); 041 HashMap<Long, Set<Lecture>> iCanNotEnrollSections = null; 042 HashMap<Student, Double> iDistanceCache = null; 043 HashSet<Placement> iCommitedPlacements = null; 044 private String iAcademicArea = null, iAcademicClassification = null, iMajor = null, iCurriculum = null; 045 HashMap<Long, Double> iOfferingPriority = new HashMap<Long, Double>(); 046 private InstructorConstraint iInstructor = null; 047 048 public Student(Long studentId) { 049 iStudentId = studentId; 050 } 051 052 public void addOffering(Long offeringId, double weight, Double priority) { 053 iOfferings.put(offeringId, weight); 054 if (priority != null) iOfferingPriority.put(offeringId, priority); 055 } 056 057 public void addOffering(Long offeringId, double weight) { 058 addOffering(offeringId, weight, null); 059 } 060 061 public Map<Long, Double> getOfferingsMap() { 062 return iOfferings; 063 } 064 065 public Set<Long> getOfferings() { 066 return iOfferings.keySet(); 067 } 068 069 public boolean hasOffering(Long offeringId) { 070 return iOfferings.containsKey(offeringId); 071 } 072 073 public InstructorConstraint getInstructor() { return iInstructor; } 074 075 public void setInstructor(InstructorConstraint instructor) { iInstructor = instructor; } 076 077 /** 078 * Priority of an offering (for the student). Null if not used, or between 079 * zero (no priority) and one (highest priority) 080 */ 081 public Double getPriority(Long offeringId) { 082 return offeringId == null ? null : iOfferingPriority.get(offeringId); 083 } 084 085 public Double getPriority(Configuration configuration) { 086 return configuration == null ? null : getPriority(configuration.getOfferingId()); 087 } 088 089 public Double getPriority(Lecture lecture) { 090 return lecture == null ? null : getPriority(lecture.getConfiguration()); 091 } 092 093 public Double getConflictingPriorty(Lecture l1, Lecture l2) { 094 // Conflicting priority is the lower of the two priorities 095 Double p1 = getPriority(l1); 096 Double p2 = getPriority(l2); 097 return p1 == null ? null : p2 == null ? null : Math.min(p1, p2); 098 } 099 100 public double getOfferingWeight(Configuration configuration) { 101 if (configuration == null) 102 return 1.0; 103 return getOfferingWeight(configuration.getOfferingId()); 104 } 105 106 public double getOfferingWeight(Long offeringId) { 107 Double weight = iOfferings.get(offeringId); 108 return (weight == null ? 0.0 : weight.doubleValue()); 109 } 110 111 public boolean canUnenroll(Lecture lecture) { 112 if (getInstructor() != null) 113 return !getInstructor().variables().contains(lecture); 114 return true; 115 } 116 117 public boolean canEnroll(Lecture lecture) { 118 if (iCanNotEnrollSections != null) { 119 Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId()); 120 return canEnroll(canNotEnrollLectures, lecture, true); 121 } 122 return true; 123 } 124 125 private boolean canEnroll(Set<Lecture> canNotEnrollLectures, Lecture lecture, boolean checkParents) { 126 if (canNotEnrollLectures == null) 127 return true; 128 if (canNotEnrollLectures.contains(lecture)) 129 return false; 130 if (checkParents) { 131 Lecture parent = lecture.getParent(); 132 while (parent != null) { 133 if (canNotEnrollLectures.contains(parent)) 134 return false; 135 parent = parent.getParent(); 136 } 137 } 138 if (lecture.hasAnyChildren()) { 139 for (Long subpartId: lecture.getChildrenSubpartIds()) { 140 boolean canEnrollChild = false; 141 for (Lecture childLecture : lecture.getChildren(subpartId)) { 142 if (canEnroll(canNotEnrollLectures, childLecture, false)) { 143 canEnrollChild = true; 144 break; 145 } 146 } 147 if (!canEnrollChild) 148 return false; 149 } 150 } 151 return true; 152 } 153 154 public void addCanNotEnroll(Lecture lecture) { 155 if (iCanNotEnrollSections == null) 156 iCanNotEnrollSections = new HashMap<Long, Set<Lecture>>(); 157 if (lecture.getConfiguration() == null) { 158 sLogger.warn("Student.addCanNotEnroll(" + lecture 159 + ") -- given lecture has no configuration associated with."); 160 return; 161 } 162 Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId()); 163 if (canNotEnrollLectures == null) { 164 canNotEnrollLectures = new HashSet<Lecture>(); 165 iCanNotEnrollSections.put(lecture.getConfiguration().getOfferingId(), canNotEnrollLectures); 166 } 167 canNotEnrollLectures.add(lecture); 168 } 169 170 public void addCanNotEnroll(Long offeringId, Collection<Lecture> lectures) { 171 if (lectures == null || lectures.isEmpty()) 172 return; 173 if (iCanNotEnrollSections == null) 174 iCanNotEnrollSections = new HashMap<Long, Set<Lecture>>(); 175 Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(offeringId); 176 if (canNotEnrollLectures == null) { 177 canNotEnrollLectures = new HashSet<Lecture>(); 178 iCanNotEnrollSections.put(offeringId, canNotEnrollLectures); 179 } 180 canNotEnrollLectures.addAll(lectures); 181 } 182 183 public Map<Long, Set<Lecture>> canNotEnrollSections() { 184 return iCanNotEnrollSections; 185 } 186 187 public void addLecture(Lecture lecture) { 188 iLectures.add(lecture); 189 } 190 191 public void removeLecture(Lecture lecture) { 192 iLectures.remove(lecture); 193 } 194 195 public Set<Lecture> getLectures() { 196 return iLectures; 197 } 198 199 public void addConfiguration(Configuration config) { 200 iConfigurations.add(config); 201 } 202 203 public void removeConfiguration(Configuration config) { 204 iConfigurations.remove(config); 205 } 206 207 public Set<Configuration> getConfigurations() { 208 return iConfigurations; 209 } 210 211 public Long getId() { 212 return iStudentId; 213 } 214 215 public double getDistance(Student student) { 216 Double dist = (USE_DISTANCE_CACHE && iDistanceCache != null ? iDistanceCache.get(student) : null); 217 if (dist == null) { 218 int same = 0; 219 for (Long o : getOfferings()) { 220 if (student.getOfferings().contains(o)) 221 same++; 222 } 223 double all = student.getOfferings().size() + getOfferings().size(); 224 double dif = all - 2.0 * same; 225 dist = new Double(dif / all); 226 if (USE_DISTANCE_CACHE) { 227 if (iDistanceCache == null) 228 iDistanceCache = new HashMap<Student, Double>(); 229 iDistanceCache.put(student, dist); 230 } 231 } 232 return dist.doubleValue(); 233 } 234 235 public void clearDistanceCache() { 236 if (USE_DISTANCE_CACHE && iDistanceCache != null) 237 iDistanceCache.clear(); 238 } 239 240 @Override 241 public String toString() { 242 return String.valueOf(getId()); 243 } 244 245 @Override 246 public int hashCode() { 247 return getId().hashCode(); 248 } 249 250 @Override 251 public int compareTo(Student s) { 252 return getId().compareTo(s.getId()); 253 } 254 255 @Override 256 public boolean equals(Object o) { 257 if (o == null || !(o instanceof Student)) 258 return false; 259 return getId().equals(((Student) o).getId()); 260 } 261 262 public void addCommitedPlacement(Placement placement) { 263 if (iCommitedPlacements == null) 264 iCommitedPlacements = new HashSet<Placement>(); 265 iCommitedPlacements.add(placement); 266 } 267 268 public Set<Placement> getCommitedPlacements() { 269 return iCommitedPlacements; 270 } 271 272 public Set<Placement> conflictPlacements(Placement placement) { 273 if (iCommitedPlacements == null) 274 return null; 275 Set<Placement> ret = new HashSet<Placement>(); 276 Lecture lecture = placement.variable(); 277 for (Placement commitedPlacement : iCommitedPlacements) { 278 Lecture commitedLecture = commitedPlacement.variable(); 279 if (lecture.getSchedulingSubpartId() != null 280 && lecture.getSchedulingSubpartId().equals(commitedLecture.getSchedulingSubpartId())) 281 continue; 282 if (lecture.isToIgnoreStudentConflictsWith(commitedLecture)) continue; 283 if (JenrlConstraint.isInConflict(commitedPlacement, placement, ((TimetableModel)placement.variable().getModel()).getDistanceMetric())) 284 ret.add(commitedPlacement); 285 } 286 return ret; 287 } 288 289 public int countConflictPlacements(Placement placement) { 290 Set<Placement> conflicts = conflictPlacements(placement); 291 double w = getOfferingWeight((placement.variable()).getConfiguration()); 292 return (int) Math.round(conflicts == null ? 0 : avg(w, 1.0) * conflicts.size()); 293 } 294 295 public double getJenrlWeight(Lecture l1, Lecture l2) { 296 if (getInstructor() != null && (getInstructor().variables().contains(l1) || getInstructor().variables().contains(l2))) 297 return 1.0; 298 return avg(getOfferingWeight(l1.getConfiguration()), getOfferingWeight(l2.getConfiguration())); 299 } 300 301 public double avg(double w1, double w2) { 302 return Math.sqrt(w1 * w2); 303 } 304 305 public String getAcademicArea() { 306 return iAcademicArea; 307 } 308 309 public void setAcademicArea(String acadArea) { 310 iAcademicArea = acadArea; 311 } 312 313 public String getAcademicClassification() { 314 return iAcademicClassification; 315 } 316 317 public void setAcademicClassification(String acadClasf) { 318 iAcademicClassification = acadClasf; 319 } 320 321 public String getMajor() { 322 return iMajor; 323 } 324 325 public void setMajor(String major) { 326 iMajor = major; 327 } 328 329 public String getCurriculum() { 330 return iCurriculum; 331 } 332 333 public void setCurriculum(String curriculum) { 334 iCurriculum = curriculum; 335 } 336}