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