001 package net.sf.cpsolver.coursett.model;
002
003 import java.util.Collection;
004 import java.util.HashSet;
005 import java.util.HashMap;
006 import java.util.Map;
007 import java.util.Set;
008
009 import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
010 import 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 Tomas Muller<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 */
034 public 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 }