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