001package org.cpsolver.studentsct.model;
002
003import java.util.ArrayList;
004import java.util.HashSet;
005import java.util.List;
006import java.util.Set;
007
008import org.cpsolver.coursett.model.TimeLocation;
009import org.cpsolver.ifs.assignment.Assignment;
010import org.cpsolver.studentsct.constraint.LinkedSections;
011import org.cpsolver.studentsct.model.Request.RequestPriority;
012
013
014
015/**
016 * Representation of a student. Each student contains id, and a list of
017 * requests. <br>
018 * <br>
019 * Last-like semester students are mark as dummy. Dummy students have lower
020 * value and generally should not block "real" students from getting requested
021 * courses. <br>
022 * <br>
023 * 
024 * @version StudentSct 1.3 (Student Sectioning)<br>
025 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
026 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
027 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
028 * <br>
029 *          This library is free software; you can redistribute it and/or modify
030 *          it under the terms of the GNU Lesser General Public License as
031 *          published by the Free Software Foundation; either version 3 of the
032 *          License, or (at your option) any later version. <br>
033 * <br>
034 *          This library is distributed in the hope that it will be useful, but
035 *          WITHOUT ANY WARRANTY; without even the implied warranty of
036 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
037 *          Lesser General Public License for more details. <br>
038 * <br>
039 *          You should have received a copy of the GNU Lesser General Public
040 *          License along with this library; if not see
041 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
042 */
043public class Student implements Comparable<Student> {
044    private long iId;
045    private String iExternalId = null, iName = null;
046    private StudentPriority iPriority = StudentPriority.Normal;
047    private List<Request> iRequests = new ArrayList<Request>();
048    private List<AreaClassificationMajor> iMajors = new ArrayList<AreaClassificationMajor>();
049    private List<AreaClassificationMajor> iMinors = new ArrayList<AreaClassificationMajor>();
050    private List<LinkedSections> iLinkedSections = new ArrayList<LinkedSections>();
051    private Set<String> iAccommodations = new HashSet<String>();
052    private List<StudentGroup> iGroups = new ArrayList<StudentGroup>();
053    private String iStatus = null;
054    private Long iEmailTimeStamp = null;
055    private List<Unavailability> iUnavailabilities = new ArrayList<Unavailability>();
056    private boolean iNeedShortDistances = false;
057    private boolean iAllowDisabled = false;
058    private Float iMinCredit = null;
059    private Float iMaxCredit = null;
060    private List<Instructor> iAdvisors = new ArrayList<Instructor>();
061    private Integer iClassFirstDate = null, iClassLastDate = null;
062    private ModalityPreference iModalityPreference = ModalityPreference.NO_PREFERENCE;
063    private BackToBackPreference iBackToBackPreference = BackToBackPreference.NO_PREFERENCE;
064
065    /**
066     * Constructor
067     * 
068     * @param id
069     *            student unique id
070     */
071    public Student(long id) {
072        iId = id;
073    }
074
075    /**
076     * Constructor
077     * 
078     * @param id
079     *            student unique id
080     * @param dummy
081     *            dummy flag
082     */
083    public Student(long id, boolean dummy) {
084        iId = id;
085        iPriority = (dummy ? StudentPriority.Dummy : StudentPriority.Normal);
086    }
087
088    /** Student unique id 
089     * @return student unique id
090     **/
091    public long getId() {
092        return iId;
093    }
094
095    /** Set student unique id 
096     * @param id student unique id
097     **/
098    public void setId(long id) {
099        iId = id;
100    }
101
102    /** Student's course and free time requests 
103     * @return student requests
104     **/
105    public List<Request> getRequests() {
106        return iRequests;
107    }
108
109    /** Number of requests (alternative requests are ignored) 
110     * @return number of non alternative student requests
111     **/
112    public int nrRequests() {
113        int ret = 0;
114        for (Request r : getRequests()) {
115            if (!r.isAlternative())
116                ret++;
117        }
118        return ret;
119    }
120
121    /** Number of alternative requests 
122     * @return number of alternative student requests 
123     **/
124    public int nrAlternativeRequests() {
125        int ret = 0;
126        for (Request r : getRequests()) {
127            if (r.isAlternative())
128                ret++;
129        }
130        return ret;
131    }
132
133    /**
134     * True if the given request can be assigned to the student. A request
135     * cannot be assigned to a student when the student already has the desired
136     * number of requests assigned (i.e., number of non-alternative course
137     * requests).
138     * @param assignment current assignment
139     * @param request given request of this student
140     * @return true if the given request can be assigned
141     **/
142    public boolean canAssign(Assignment<Request, Enrollment> assignment, Request request) {
143        if (request.isAssigned(assignment))
144            return true;
145        int alt = 0;
146        float credit = 0f;
147        boolean found = false;
148        for (Request r : getRequests()) {
149            if (r.equals(request))
150                found = true;
151            boolean assigned = (r.isAssigned(assignment) || r.equals(request));
152            boolean course = (r instanceof CourseRequest);
153            boolean waitlist = (course && ((CourseRequest) r).isWaitlist());
154            if (r.isAlternative()) {
155                if (assigned || (!found && waitlist))
156                    alt--;
157            } else {
158                if (course && !waitlist && !assigned)
159                    alt++;
160            }
161            if (r.equals(request))
162                credit += r.getMinCredit();
163            else {
164                Enrollment e = r.getAssignment(assignment);
165                if (e != null) credit += e.getCredit();
166            }
167        }
168        return (alt >= 0 && credit <= getMaxCredit());
169    }
170
171    /**
172     * True if the student has assigned the desired number of requests (i.e.,
173     * number of non-alternative course requests).
174     * @param assignment current assignment
175     * @return true if this student has a complete schedule
176     */
177    public boolean isComplete(Assignment<Request, Enrollment> assignment) {
178        int nrRequests = 0;
179        int nrAssignedRequests = 0;
180        float credit = 0f;
181        Float minCredit = null;
182        for (Request r : getRequests()) {
183            if (!(r instanceof CourseRequest))
184                continue; // ignore free times
185            if (!r.isAlternative())
186                nrRequests++;
187            if (r.isAssigned(assignment))
188                nrAssignedRequests++;
189            Enrollment e = r.getAssignment(assignment);
190            if (e != null) {
191                credit += e.getCredit();
192            } else if (r instanceof CourseRequest) {
193                minCredit = (minCredit == null ? r.getMinCredit() : Math.min(minCredit, r.getMinCredit()));
194            }
195        }
196        return nrAssignedRequests == nrRequests || credit + (minCredit == null ? 0f : minCredit.floatValue()) > getMaxCredit();
197    }
198
199    /** Number of assigned COURSE requests 
200     * @param assignment current assignment
201     * @return number of assigned course requests
202     **/
203    public int nrAssignedRequests(Assignment<Request, Enrollment> assignment) {
204        int nrAssignedRequests = 0;
205        for (Request r : getRequests()) {
206            if (!(r instanceof CourseRequest))
207                continue; // ignore free times
208            if (r.isAssigned(assignment))
209                nrAssignedRequests++;
210        }
211        return nrAssignedRequests;
212    }
213
214    @Override
215    public String toString() {
216        return (isDummy() ? "D" : "") + "S[" + getId() + "]";
217    }
218
219    /**
220     * Student's dummy flag. Dummy students have lower value and generally
221     * should not block "real" students from getting requested courses.
222     * @return true if projected student
223     */
224    public boolean isDummy() {
225        return iPriority == StudentPriority.Dummy;
226    }
227
228    /**
229     * Set student's dummy flag. Dummy students have lower value and generally
230     * should not block "real" students from getting requested courses.
231     * @param dummy projected student
232     */
233    public void setDummy(boolean dummy) {
234        if (dummy)
235            iPriority = StudentPriority.Dummy;
236        else if (iPriority == StudentPriority.Dummy)
237            iPriority = StudentPriority.Normal;
238    }
239    
240    /**
241     * Student's priority. Priority students are to be assigned first.
242     * @return student priority level
243     */
244    public StudentPriority getPriority() {
245        return iPriority;
246    }
247    
248    /**
249     * Set student's priority. Priority students are to be assigned first.
250     * @param priority student priority level
251     */
252    public void setPriority(StudentPriority priority) {
253        iPriority = priority;
254    }
255    
256    /**
257     * Set student's priority. Priority students are to be assigned first.
258     * @param priority true for priority student
259     */
260    @Deprecated
261    public void setPriority(boolean priority) {
262        if (priority)
263            iPriority = StudentPriority.Priority;
264        else if (StudentPriority.Normal.isHigher(this))
265            iPriority = StudentPriority.Normal;
266    }
267    
268    /**
269     * Student's priority. Priority students are to be assigned first.
270     * @return true if priority student
271     */
272    @Deprecated
273    public boolean isPriority() {
274        return StudentPriority.Normal.isHigher(this);
275    }
276
277
278    /**
279     * List of student groups ({@link StudentGroup}) for the given student
280     * @return list of academic area abbreviation (group type) &amp; group code pairs
281     */
282    public List<StudentGroup> getGroups() {
283        return iGroups;
284    }
285    
286    /**
287     * List student accommodations
288     * @return student accommodations
289     */
290    public Set<String> getAccommodations() {
291        return iAccommodations;
292    }
293    
294    /**
295     * List of academic area, classification, and major codes ({@link AreaClassificationMajor}) for the given student
296     * @return list of academic area, classification, and major codes
297     */
298    public List<AreaClassificationMajor> getAreaClassificationMajors() {
299        return iMajors;
300    }
301    
302    /**
303     * List of academic area, classification, and minor codes ({@link AreaClassificationMajor}) for the given student
304     * @return list of academic area, classification, and minor codes
305     */
306    public List<AreaClassificationMajor> getAreaClassificationMinors() {
307        return iMinors;
308    }
309
310    /**
311     * List of student's advisors
312     */
313    public List<Instructor> getAdvisors() {
314        return iAdvisors;
315    }
316
317    /**
318     * Compare two students for equality. Two students are considered equal if
319     * they have the same id.
320     */
321    @Override
322    public boolean equals(Object object) {
323        if (object == null || !(object instanceof Student))
324            return false;
325        return getId() == ((Student) object).getId() && isDummy() == ((Student) object).isDummy();
326    }
327
328    /**
329     * Hash code (base only on student id)
330     */
331    @Override
332    public int hashCode() {
333        return (int) (iId ^ (iId >>> 32));
334    }
335    
336    /**
337     * Count number of free time slots overlapping with the given enrollment
338     * @param enrollment given enrollment
339     * @return number of slots overlapping with a free time request
340     */
341    public int countFreeTimeOverlaps(Enrollment enrollment) {
342        if (!enrollment.isCourseRequest()) return 0;
343        int ret = 0;
344        for (Section section: enrollment.getSections()) {
345            TimeLocation time = section.getTime();
346            if (time != null)
347                ret += countFreeTimeOverlaps(time);
348        }
349        return ret;
350    }
351    
352    /**
353     * Count number of free time slots overlapping with the given time
354     * @param time given time
355     * @return number of time slots overlapping with a free time request
356     */
357    public int countFreeTimeOverlaps(TimeLocation time) {
358        int ret = 0;
359        for (Request r: iRequests) {
360            if (r instanceof FreeTimeRequest) {
361                TimeLocation freeTime = ((FreeTimeRequest)r).getTime();
362                if (time.hasIntersection(freeTime))
363                    ret += freeTime.nrSharedHours(time) * freeTime.nrSharedDays(time);
364            }
365        }
366        return ret;
367    }
368    
369    /**
370     * Get student external id
371     * @return student external unique id
372     */
373    public String getExternalId() { return iExternalId; }
374    /**
375     * Set student external id
376     * @param externalId student external id
377     */
378    public void setExternalId(String externalId) { iExternalId = externalId; }
379
380    /**
381     * Get student name
382     * @return student name
383     */
384    public String getName() { return iName; }
385    /**
386     * Set student name
387     * @param name student name
388     */
389    public void setName(String name) { iName = name; }
390    
391    /**
392     * Linked sections of this student
393     * @return linked sections of this student
394     */
395    public List<LinkedSections> getLinkedSections() { return iLinkedSections; }
396    
397    /**
398     * Get student status (online sectioning only)
399     * @return student sectioning status
400     */
401    public String getStatus() { return iStatus; }
402    /**
403     * Set student status
404     * @param status student sectioning status
405     */
406    public void setStatus(String status) { iStatus = status; }
407    
408    /**
409     * Get last email time stamp (online sectioning only)
410     * @return student email time stamp
411     */
412    public Long getEmailTimeStamp() { return iEmailTimeStamp; }
413    /**
414     * Set last email time stamp
415     * @param emailTimeStamp student email time stamp
416     */
417    public void setEmailTimeStamp(Long emailTimeStamp) { iEmailTimeStamp = emailTimeStamp; }
418
419    @Override
420    public int compareTo(Student s) {
421        // priority students first, dummy students last
422        if (getPriority() != s.getPriority())
423            return (getPriority().ordinal() < s.getPriority().ordinal() ? -1 : 1);
424        // then id
425        return Long.valueOf(getId()).compareTo(s.getId());
426    }
427    
428    /**
429     * List of student unavailabilities
430     * @return student unavailabilities
431     */
432    public List<Unavailability> getUnavailabilities() { return iUnavailabilities; }
433    
434    /**
435     * Check if student is available during the given section
436     * @param section given section
437     * @return true, if available (the section cannot overlap and there is no overlapping unavailability that cannot overlap) 
438     */
439    public boolean isAvailable(Section section) {
440        if (section.isAllowOverlap() || section.getTime() == null) return true;
441        for (Unavailability unavailability: getUnavailabilities())
442            if (unavailability.isOverlapping(section)) return false;
443        return true;
444    }
445    
446    /**
447     * Check if student is available during the given enrollment
448     * @param enrollment given enrollment
449     * @return true, if available
450     */
451    public boolean isAvailable(Enrollment enrollment) {
452        if (enrollment != null && enrollment.isCourseRequest() && !enrollment.isAllowOverlap())
453            for (Section section: enrollment.getSections())
454                if (!isAvailable(section)) return false;
455        return true;
456    }
457    
458    /**
459     * Return true if the student needs short distances. A different distance conflict checking is employed for such students.
460     * @return true if the student needs short distances
461     */
462    public boolean isNeedShortDistances() {
463        return iNeedShortDistances;
464    }
465    
466    /**
467     * Set true if the student needs short distances. A different distance conflict checking is employed for such students.
468     * @param needShortDistances true if the student needs short distances (default is false)
469     */
470    public void setNeedShortDistances(boolean needShortDistances) {
471        iNeedShortDistances = needShortDistances;
472    }
473    
474    /**
475     * True if student can be enrolled in disabled sections, regardless if his/her reservations 
476     * @return does this student allow for disabled sections
477     */
478    public boolean isAllowDisabled() {
479        return iAllowDisabled;
480    }
481    
482    /**
483     * Set to true  if student can be enrolled in disabled sections, regardless if his/her reservations
484     * @param allowDisabled does this student allow for disabled sections
485     */
486    public void setAllowDisabled(boolean allowDisabled) {
487        iAllowDisabled = allowDisabled;
488    }
489    
490    /**
491     * True if student has min credit defined
492     * @return true if min credit is set
493     */
494    public boolean hasMinCredit() { return iMinCredit != null; }
495    
496    /**
497     * Get student min credit (0 if not set)
498     * return student min credit
499     */
500    public float getMinCredit() { return (iMinCredit == null ? 0 : iMinCredit.floatValue()); }
501    
502    /**
503     * Has student any critical course requests?
504     * @return true if a student has at least one course request that is marked as critical
505     */
506    @Deprecated
507    public boolean hasCritical() {
508        for (Request r: iRequests)
509            if (!r.isAlternative() && r.isCritical()) return true;
510        return false;
511    }
512    
513    /**
514     * Has student any critical course requests?
515     * @return true if a student has at least one course request that is marked as critical
516     */
517    public boolean hasCritical(RequestPriority rp) {
518        for (Request r: iRequests)
519            if (!r.isAlternative() && rp.isCritical(r)) return true;
520        return false;
521    }
522    
523    /**
524     * Has student any unassigned critical course requests?
525     * @return true if a student has at least one not-alternative course request that is marked as critical and that is not assigned
526     */
527    @Deprecated
528    public boolean hasUnassignedCritical(Assignment<Request, Enrollment> assignment) {
529        for (Request r: iRequests)
530            if (!r.isAlternative() && r.isCritical() && assignment.getValue(r) == null) return true;
531        return false;
532    }
533    
534    /**
535     * Has student any unassigned critical course requests?
536     * @return true if a student has at least one not-alternative course request that is marked as critical and that is not assigned
537     */
538    public boolean hasUnassignedCritical(Assignment<Request, Enrollment> assignment, RequestPriority rp) {
539        for (Request r: iRequests)
540            if (!r.isAlternative() && rp.isCritical(r) && assignment.getValue(r) == null) return true;
541        return false;
542    }
543    
544    /**
545     * Set student min credit (null if not set)
546     * @param maxCredit student min credit
547     */
548    public void setMinCredit(Float maxCredit) { iMinCredit = maxCredit; }
549    
550    /**
551     * True if student has max credit defined
552     * @return true if max credit is set
553     */
554    public boolean hasMaxCredit() { return iMaxCredit != null; }
555    
556    /**
557     * Get student max credit ({@link Float#MAX_VALUE} if not set)
558     * return student max credit
559     */
560    public float getMaxCredit() { return (iMaxCredit == null ? Float.MAX_VALUE : iMaxCredit.floatValue()); }
561    
562    /**
563     * Set student max credit (null if not set)
564     * @param maxCredit student max credit
565     */
566    public void setMaxCredit(Float maxCredit) { iMaxCredit = maxCredit; }
567    
568    /**
569     * Return the number of assigned credits of the student
570     * @param assignment current assignment
571     * @return total assigned credit using {@link Enrollment#getCredit()} 
572     */
573    public float getAssignedCredit(Assignment<Request, Enrollment> assignment) {
574        float credit = 0f;
575        for (Request r: getRequests()) {
576            Enrollment e = r.getAssignment(assignment);
577            if (e != null) credit += e.getCredit();
578        }
579        return credit;
580    }
581    
582    /**
583     * Student priority level. Higher priority students are to be assigned first.
584     * The student priority is used to re-order students and assign them accoding
585     * to their priority.
586     */
587    public static enum StudentPriority {
588        Priority("P", 1.00),
589        Senior("4", 0.70),
590        Junior("3", 0.49),
591        Sophomore("2", 0.33),
592        Freshmen("1", 0.24),
593        Normal("N", null), // this is the default priority
594        Dummy("D", null), // dummy students priority
595        ;
596        
597        String iCode;
598        Double iBoost;
599        StudentPriority(String code, Double boost) {
600            iCode = code;
601            iBoost = boost;
602        }
603        public String code() { return iCode; }
604        public Double getBoost() { return iBoost; }
605        
606        public boolean isSameOrHigher(Student s) {
607            return s.getPriority().ordinal() <= ordinal();
608        }
609        public boolean isHigher(Student s) {
610            return ordinal() < s.getPriority().ordinal();
611        }
612        public boolean isSame(Student s) {
613            return ordinal() == s.getPriority().ordinal();
614        }
615        public static StudentPriority getPriority(String value) {
616            if ("true".equalsIgnoreCase(value)) return StudentPriority.Priority;
617            if ("false".equalsIgnoreCase(value)) return StudentPriority.Normal;
618            for (StudentPriority sp: StudentPriority.values()) {
619                if (sp.name().equalsIgnoreCase(value)) return sp;
620            }
621            return StudentPriority.Normal;
622        }
623    }
624    
625    /**
626     * Check if a student has given accommodation
627     * @param code accommodation reference code
628     * @return true if present
629     */
630    public boolean hasAccommodation(String code) {
631        return code != null && !code.isEmpty() && iAccommodations.contains(code);
632    }
633    
634    public void setClassFirstDate(Integer classFirstDate) {
635        iClassFirstDate = classFirstDate;
636    }
637    
638    public Integer getClassFirstDate() {
639        return iClassFirstDate;
640    }
641    
642    public void setClassLastDate(Integer classLastDate) {
643        iClassLastDate = classLastDate;
644    }
645    
646    public Integer getClassLastDate() {
647        return iClassLastDate;
648    }
649    
650    public ModalityPreference getModalityPreference() { return iModalityPreference; }
651    public void setModalityPreference(ModalityPreference p) { iModalityPreference = p ;}
652    
653    public BackToBackPreference getBackToBackPreference() { return iBackToBackPreference; }
654    public void setBackToBackPreference(BackToBackPreference p) { iBackToBackPreference = p; }
655    
656    public static enum ModalityPreference {
657        NO_PREFERENCE,
658        ONLINE_PREFERRED,
659        ONILNE_DISCOURAGED,
660        ONLINE_REQUIRED,
661    }
662    
663    public static enum BackToBackPreference {
664        NO_PREFERENCE,
665        BTB_PREFERRED,
666        BTB_DISCOURAGED,
667    }
668}