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