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