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