001package net.sf.cpsolver.studentsct.model;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import net.sf.cpsolver.coursett.model.TimeLocation;
007import net.sf.cpsolver.studentsct.constraint.LinkedSections;
008
009
010/**
011 * Representation of a student. Each student contains id, and a list of
012 * requests. <br>
013 * <br>
014 * Last-like semester students are mark as dummy. Dummy students have lower
015 * value and generally should not block "real" students from getting requested
016 * courses. <br>
017 * <br>
018 * 
019 * @version StudentSct 1.2 (Student Sectioning)<br>
020 *          Copyright (C) 2007 - 2010 Tomáš Müller<br>
021 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
022 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
023 * <br>
024 *          This library is free software; you can redistribute it and/or modify
025 *          it under the terms of the GNU Lesser General Public License as
026 *          published by the Free Software Foundation; either version 3 of the
027 *          License, or (at your option) any later version. <br>
028 * <br>
029 *          This library is distributed in the hope that it will be useful, but
030 *          WITHOUT ANY WARRANTY; without even the implied warranty of
031 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
032 *          Lesser General Public License for more details. <br>
033 * <br>
034 *          You should have received a copy of the GNU Lesser General Public
035 *          License along with this library; if not see
036 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
037 */
038public class Student implements Comparable<Student> {
039    private long iId;
040    private String iExternalId = null, iName = null;
041    private boolean iDummy = false;
042    private List<Request> iRequests = new ArrayList<Request>();
043    private List<AcademicAreaCode> iAcadAreaClassifs = new ArrayList<AcademicAreaCode>();
044    private List<AcademicAreaCode> iMajors = new ArrayList<AcademicAreaCode>();
045    private List<AcademicAreaCode> iMinors = new ArrayList<AcademicAreaCode>();
046    private List<LinkedSections> iLinkedSections = new ArrayList<LinkedSections>();
047    private String iStatus = null;
048    private Long iEmailTimeStamp = null;
049
050    /**
051     * Constructor
052     * 
053     * @param id
054     *            student unique id
055     */
056    public Student(long id) {
057        iId = id;
058    }
059
060    /**
061     * Constructor
062     * 
063     * @param id
064     *            student unique id
065     * @param dummy
066     *            dummy flag
067     */
068    public Student(long id, boolean dummy) {
069        iId = id;
070        iDummy = dummy;
071    }
072
073    /** Student unique id */
074    public long getId() {
075        return iId;
076    }
077
078    /** Set student unique id */
079    public void setId(long id) {
080        iId = id;
081    }
082
083    /** Student's course and free time requests */
084    public List<Request> getRequests() {
085        return iRequests;
086    }
087
088    /** Number of requests (alternative requests are ignored) */
089    public int nrRequests() {
090        int ret = 0;
091        for (Request r : getRequests()) {
092            if (!r.isAlternative())
093                ret++;
094        }
095        return ret;
096    }
097
098    /** Number of alternative requests */
099    public int nrAlternativeRequests() {
100        int ret = 0;
101        for (Request r : getRequests()) {
102            if (r.isAlternative())
103                ret++;
104        }
105        return ret;
106    }
107
108    /**
109     * True if the given request can be assigned to the student. A request
110     * cannot be assigned to a student when the student already has the desired
111     * number of requests assigned (i.e., number of non-alternative course
112     * requests).
113     **/
114    public boolean canAssign(Request request) {
115        if (request.isAssigned())
116            return true;
117        int alt = 0;
118        boolean found = false;
119        for (Request r : getRequests()) {
120            if (r.equals(request))
121                found = true;
122            boolean assigned = (r.isAssigned() || r.equals(request));
123            boolean course = (r instanceof CourseRequest);
124            boolean waitlist = (course && ((CourseRequest) r).isWaitlist());
125            if (r.isAlternative()) {
126                if (assigned || (!found && waitlist))
127                    alt--;
128            } else {
129                if (course && !waitlist && !assigned)
130                    alt++;
131            }
132        }
133        return (alt >= 0);
134    }
135
136    /**
137     * True if the student has assigned the desired number of requests (i.e.,
138     * number of non-alternative course requests).
139     */
140    public boolean isComplete() {
141        int nrRequests = 0;
142        int nrAssignedRequests = 0;
143        for (Request r : getRequests()) {
144            if (!(r instanceof CourseRequest))
145                continue; // ignore free times
146            if (!r.isAlternative())
147                nrRequests++;
148            if (r.isAssigned())
149                nrAssignedRequests++;
150        }
151        return nrAssignedRequests == nrRequests;
152    }
153
154    /** Number of assigned COURSE requests */
155    public int nrAssignedRequests() {
156        int nrAssignedRequests = 0;
157        for (Request r : getRequests()) {
158            if (!(r instanceof CourseRequest))
159                continue; // ignore free times
160            if (r.isAssigned())
161                nrAssignedRequests++;
162        }
163        return nrAssignedRequests;
164    }
165
166    @Override
167    public String toString() {
168        return (isDummy() ? "D" : "") + "S[" + getId() + "]";
169    }
170
171    /**
172     * Student's dummy flag. Dummy students have lower value and generally
173     * should not block "real" students from getting requested courses.
174     */
175    public boolean isDummy() {
176        return iDummy;
177    }
178
179    /**
180     * Set student's dummy flag. Dummy students have lower value and generally
181     * should not block "real" students from getting requested courses.
182     */
183    public void setDummy(boolean dummy) {
184        iDummy = dummy;
185    }
186
187    /**
188     * List of academic area - classification codes ({@link AcademicAreaCode})
189     * for the given student
190     */
191    public List<AcademicAreaCode> getAcademicAreaClasiffications() {
192        return iAcadAreaClassifs;
193    }
194
195    /**
196     * List of major codes ({@link AcademicAreaCode}) for the given student
197     */
198    public List<AcademicAreaCode> getMajors() {
199        return iMajors;
200    }
201
202    /**
203     * List of major codes ({@link AcademicAreaCode}) for the given student
204     */
205    public List<AcademicAreaCode> getMinors() {
206        return iMinors;
207    }
208
209    /**
210     * Compare two students for equality. Two students are considered equal if
211     * they have the same id.
212     */
213    @Override
214    public boolean equals(Object object) {
215        if (object == null || !(object instanceof Student))
216            return false;
217        return getId() == ((Student) object).getId() && isDummy() == ((Student) object).isDummy();
218    }
219
220    /**
221     * Hash code (base only on student id)
222     */
223    @Override
224    public int hashCode() {
225        return (int) (iId ^ (iId >>> 32));
226    }
227    
228    /**
229     * Count number of free time slots overlapping with the given enrollment
230     */
231    public int countFreeTimeOverlaps(Enrollment enrollment) {
232        if (!enrollment.isCourseRequest()) return 0;
233        int ret = 0;
234        for (Section section: enrollment.getSections()) {
235            TimeLocation time = section.getTime();
236            if (time != null)
237                ret += countFreeTimeOverlaps(time);
238        }
239        return ret;
240    }
241    
242    /**
243     * Count number of free time slots overlapping with the given time
244     */
245    public int countFreeTimeOverlaps(TimeLocation time) {
246        int ret = 0;
247        for (Request r: iRequests) {
248            if (r instanceof FreeTimeRequest) {
249                TimeLocation freeTime = ((FreeTimeRequest)r).getTime();
250                if (time.hasIntersection(freeTime))
251                    ret += freeTime.nrSharedHours(time) * freeTime.nrSharedDays(time);
252            }
253        }
254        return ret;
255    }
256    
257    /**
258     * Get student external id
259     */
260    public String getExternalId() { return iExternalId; }
261    /**
262     * Set student external id
263     */
264    public void setExternalId(String externalId) { iExternalId = externalId; }
265
266    /**
267     * Get student name
268     */
269    public String getName() { return iName; }
270    /**
271     * Set student name
272     */
273    public void setName(String name) { iName = name; }
274    
275    /**
276     * Linked sections of this student
277     */
278    public List<LinkedSections> getLinkedSections() { return iLinkedSections; }
279    
280    /**
281     * Get student status (online sectioning only)
282     */
283    public String getStatus() { return iStatus; }
284    /**
285     * Set student status
286     */
287    public void setStatus(String status) { iStatus = status; }
288    
289    /**
290     * Get last email time stamp (online sectioning only)
291     */
292    public Long getEmailTimeStamp() { return iEmailTimeStamp; }
293    /**
294     * Set last email time stamp
295     */
296    public void setEmailTimeStamp(Long emailTimeStamp) { iEmailTimeStamp = emailTimeStamp; }
297
298    @Override
299    public int compareTo(Student s) {
300        // real students first
301        if (isDummy()) {
302            if (!s.isDummy()) return 1;
303        } else if (s.isDummy()) return -1;
304        // then id
305        return new Long(getId()).compareTo(s.getId());
306    }
307}