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