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<AreaClassificationMajor> iACM = new ArrayList<AreaClassificationMajor>();
049    private List<LinkedSections> iLinkedSections = new ArrayList<LinkedSections>();
050    private String iStatus = null;
051    private Long iEmailTimeStamp = null;
052    private List<Unavailability> iUnavailabilities = new ArrayList<Unavailability>();
053    private boolean iNeedShortDistances = false;
054
055    /**
056     * Constructor
057     * 
058     * @param id
059     *            student unique id
060     */
061    public Student(long id) {
062        iId = id;
063    }
064
065    /**
066     * Constructor
067     * 
068     * @param id
069     *            student unique id
070     * @param dummy
071     *            dummy flag
072     */
073    public Student(long id, boolean dummy) {
074        iId = id;
075        iDummy = dummy;
076    }
077
078    /** Student unique id 
079     * @return student unique id
080     **/
081    public long getId() {
082        return iId;
083    }
084
085    /** Set student unique id 
086     * @param id student unique id
087     **/
088    public void setId(long id) {
089        iId = id;
090    }
091
092    /** Student's course and free time requests 
093     * @return student requests
094     **/
095    public List<Request> getRequests() {
096        return iRequests;
097    }
098
099    /** Number of requests (alternative requests are ignored) 
100     * @return number of non alternative student requests
101     **/
102    public int nrRequests() {
103        int ret = 0;
104        for (Request r : getRequests()) {
105            if (!r.isAlternative())
106                ret++;
107        }
108        return ret;
109    }
110
111    /** Number of alternative requests 
112     * @return number of alternative student requests 
113     **/
114    public int nrAlternativeRequests() {
115        int ret = 0;
116        for (Request r : getRequests()) {
117            if (r.isAlternative())
118                ret++;
119        }
120        return ret;
121    }
122
123    /**
124     * True if the given request can be assigned to the student. A request
125     * cannot be assigned to a student when the student already has the desired
126     * number of requests assigned (i.e., number of non-alternative course
127     * requests).
128     * @param assignment current assignment
129     * @param request given request of this student
130     * @return true if the given request can be assigned
131     **/
132    public boolean canAssign(Assignment<Request, Enrollment> assignment, Request request) {
133        if (request.isAssigned(assignment))
134            return true;
135        int alt = 0;
136        boolean found = false;
137        for (Request r : getRequests()) {
138            if (r.equals(request))
139                found = true;
140            boolean assigned = (r.isAssigned(assignment) || r.equals(request));
141            boolean course = (r instanceof CourseRequest);
142            boolean waitlist = (course && ((CourseRequest) r).isWaitlist());
143            if (r.isAlternative()) {
144                if (assigned || (!found && waitlist))
145                    alt--;
146            } else {
147                if (course && !waitlist && !assigned)
148                    alt++;
149            }
150        }
151        return (alt >= 0);
152    }
153
154    /**
155     * True if the student has assigned the desired number of requests (i.e.,
156     * number of non-alternative course requests).
157     * @param assignment current assignment
158     * @return true if this student has a complete schedule
159     */
160    public boolean isComplete(Assignment<Request, Enrollment> assignment) {
161        int nrRequests = 0;
162        int nrAssignedRequests = 0;
163        for (Request r : getRequests()) {
164            if (!(r instanceof CourseRequest))
165                continue; // ignore free times
166            if (!r.isAlternative())
167                nrRequests++;
168            if (r.isAssigned(assignment))
169                nrAssignedRequests++;
170        }
171        return nrAssignedRequests == nrRequests;
172    }
173
174    /** Number of assigned COURSE requests 
175     * @param assignment current assignment
176     * @return number of assigned course requests
177     **/
178    public int nrAssignedRequests(Assignment<Request, Enrollment> assignment) {
179        int nrAssignedRequests = 0;
180        for (Request r : getRequests()) {
181            if (!(r instanceof CourseRequest))
182                continue; // ignore free times
183            if (r.isAssigned(assignment))
184                nrAssignedRequests++;
185        }
186        return nrAssignedRequests;
187    }
188
189    @Override
190    public String toString() {
191        return (isDummy() ? "D" : "") + "S[" + getId() + "]";
192    }
193
194    /**
195     * Student's dummy flag. Dummy students have lower value and generally
196     * should not block "real" students from getting requested courses.
197     * @return true if projected student
198     */
199    public boolean isDummy() {
200        return iDummy;
201    }
202
203    /**
204     * Set student's dummy flag. Dummy students have lower value and generally
205     * should not block "real" students from getting requested courses.
206     * @param dummy projected student
207     */
208    public void setDummy(boolean dummy) {
209        iDummy = dummy;
210    }
211
212    /**
213     * List of academic area - classification codes ({@link AcademicAreaCode})
214     * for the given student
215     * @return list of academic area abbreviation &amp; classification code pairs
216     */
217    public List<AcademicAreaCode> getAcademicAreaClasiffications() {
218        return iAcadAreaClassifs;
219    }
220
221    /**
222     * List of major codes ({@link AcademicAreaCode}) for the given student
223     * @return list of academic area abbreviation &amp; major code pairs
224     */
225    public List<AcademicAreaCode> getMajors() {
226        return iMajors;
227    }
228
229    /**
230     * List of major codes ({@link AcademicAreaCode}) for the given student
231     * @return list of academic area abbreviation &amp; minor code pairs
232     */
233    public List<AcademicAreaCode> getMinors() {
234        return iMinors;
235    }
236    
237    /**
238     * List of academic area, classification, and major codes ({@link AreaClassificationMajor}) for the given student
239     * @return list of academic area, classification, and major codes
240     */
241    public List<AreaClassificationMajor> getAreaClassificationMajors() {
242        return iACM;
243    }
244
245    /**
246     * Compare two students for equality. Two students are considered equal if
247     * they have the same id.
248     */
249    @Override
250    public boolean equals(Object object) {
251        if (object == null || !(object instanceof Student))
252            return false;
253        return getId() == ((Student) object).getId() && isDummy() == ((Student) object).isDummy();
254    }
255
256    /**
257     * Hash code (base only on student id)
258     */
259    @Override
260    public int hashCode() {
261        return (int) (iId ^ (iId >>> 32));
262    }
263    
264    /**
265     * Count number of free time slots overlapping with the given enrollment
266     * @param enrollment given enrollment
267     * @return number of slots overlapping with a free time request
268     */
269    public int countFreeTimeOverlaps(Enrollment enrollment) {
270        if (!enrollment.isCourseRequest()) return 0;
271        int ret = 0;
272        for (Section section: enrollment.getSections()) {
273            TimeLocation time = section.getTime();
274            if (time != null)
275                ret += countFreeTimeOverlaps(time);
276        }
277        return ret;
278    }
279    
280    /**
281     * Count number of free time slots overlapping with the given time
282     * @param time given time
283     * @return number of time slots overlapping with a free time request
284     */
285    public int countFreeTimeOverlaps(TimeLocation time) {
286        int ret = 0;
287        for (Request r: iRequests) {
288            if (r instanceof FreeTimeRequest) {
289                TimeLocation freeTime = ((FreeTimeRequest)r).getTime();
290                if (time.hasIntersection(freeTime))
291                    ret += freeTime.nrSharedHours(time) * freeTime.nrSharedDays(time);
292            }
293        }
294        return ret;
295    }
296    
297    /**
298     * Get student external id
299     * @return student external unique id
300     */
301    public String getExternalId() { return iExternalId; }
302    /**
303     * Set student external id
304     * @param externalId student external id
305     */
306    public void setExternalId(String externalId) { iExternalId = externalId; }
307
308    /**
309     * Get student name
310     * @return student name
311     */
312    public String getName() { return iName; }
313    /**
314     * Set student name
315     * @param name student name
316     */
317    public void setName(String name) { iName = name; }
318    
319    /**
320     * Linked sections of this student
321     * @return linked sections of this student
322     */
323    public List<LinkedSections> getLinkedSections() { return iLinkedSections; }
324    
325    /**
326     * Get student status (online sectioning only)
327     * @return student sectioning status
328     */
329    public String getStatus() { return iStatus; }
330    /**
331     * Set student status
332     * @param status student sectioning status
333     */
334    public void setStatus(String status) { iStatus = status; }
335    
336    /**
337     * Get last email time stamp (online sectioning only)
338     * @return student email time stamp
339     */
340    public Long getEmailTimeStamp() { return iEmailTimeStamp; }
341    /**
342     * Set last email time stamp
343     * @param emailTimeStamp student email time stamp
344     */
345    public void setEmailTimeStamp(Long emailTimeStamp) { iEmailTimeStamp = emailTimeStamp; }
346
347    @Override
348    public int compareTo(Student s) {
349        // real students first
350        if (isDummy()) {
351            if (!s.isDummy()) return 1;
352        } else if (s.isDummy()) return -1;
353        // then id
354        return new Long(getId()).compareTo(s.getId());
355    }
356    
357    /**
358     * List of student unavailabilities
359     * @return student unavailabilities
360     */
361    public List<Unavailability> getUnavailabilities() { return iUnavailabilities; }
362    
363    /**
364     * Check if student is available during the given section
365     * @param section given section
366     * @return true, if available (the section cannot overlap and there is no overlapping unavailability that cannot overlap) 
367     */
368    public boolean isAvailable(Section section) {
369        if (section.isAllowOverlap() || section.getTime() == null) return true;
370        for (Unavailability unavailability: getUnavailabilities())
371            if (unavailability.isOverlapping(section)) return false;
372        return true;
373    }
374    
375    /**
376     * Check if student is available during the given enrollment
377     * @param enrollment given enrollment
378     * @return true, if available
379     */
380    public boolean isAvailable(Enrollment enrollment) {
381        if (enrollment != null && enrollment.isCourseRequest() && !enrollment.isAllowOverlap())
382            for (Section section: enrollment.getSections())
383                if (!isAvailable(section)) return false;
384        return true;
385    }
386    
387    /**
388     * Return true if the student needs short distances. A different distance conflict checking is employed for such students.
389     * @return true if the student needs short distances
390     */
391    public boolean isNeedShortDistances() {
392        return iNeedShortDistances;
393    }
394    
395    /**
396     * Set true if the student needs short distances. A different distance conflict checking is employed for such students.
397     * @param needShortDistances true if the student needs short distances (default is false)
398     */
399    public void setNeedShortDistances(boolean needShortDistances) {
400        iNeedShortDistances = needShortDistances;
401    }
402}