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    }