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