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