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 & 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 & 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 & 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}