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 * Representation of a student. Each student contains id, and a list of 016 * requests. <br> 017 * <br> 018 * Last-like semester students are mark as dummy. Dummy students have lower 019 * value and generally should not block "real" students from getting requested 020 * courses. <br> 021 * <br> 022 * 023 * @author Tomáš Müller 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 public AreaClassificationMajor getPrimaryMajor() { 303 if (iMajors == null) return null; 304 AreaClassificationMajor major = null; 305 for (AreaClassificationMajor m: iMajors) { 306 if (major == null || m.compareTo(major) < 0) 307 major = m; 308 } 309 return major; 310 } 311 312 /** 313 * List of academic area, classification, and minor codes ({@link AreaClassificationMajor}) for the given student 314 * @return list of academic area, classification, and minor codes 315 */ 316 public List<AreaClassificationMajor> getAreaClassificationMinors() { 317 return iMinors; 318 } 319 320 /** 321 * List of student's advisors 322 */ 323 public List<Instructor> getAdvisors() { 324 return iAdvisors; 325 } 326 327 /** 328 * Compare two students for equality. Two students are considered equal if 329 * they have the same id. 330 */ 331 @Override 332 public boolean equals(Object object) { 333 if (object == null || !(object instanceof Student)) 334 return false; 335 return getId() == ((Student) object).getId() && isDummy() == ((Student) object).isDummy(); 336 } 337 338 /** 339 * Hash code (base only on student id) 340 */ 341 @Override 342 public int hashCode() { 343 return (int) (iId ^ (iId >>> 32)); 344 } 345 346 /** 347 * Count number of free time slots overlapping with the given enrollment 348 * @param enrollment given enrollment 349 * @return number of slots overlapping with a free time request 350 */ 351 public int countFreeTimeOverlaps(Enrollment enrollment) { 352 if (!enrollment.isCourseRequest()) return 0; 353 int ret = 0; 354 for (Section section: enrollment.getSections()) { 355 TimeLocation time = section.getTime(); 356 if (time != null) 357 ret += countFreeTimeOverlaps(time); 358 } 359 return ret; 360 } 361 362 /** 363 * Count number of free time slots overlapping with the given time 364 * @param time given time 365 * @return number of time slots overlapping with a free time request 366 */ 367 public int countFreeTimeOverlaps(TimeLocation time) { 368 int ret = 0; 369 for (Request r: iRequests) { 370 if (r instanceof FreeTimeRequest) { 371 TimeLocation freeTime = ((FreeTimeRequest)r).getTime(); 372 if (time.hasIntersection(freeTime)) 373 ret += freeTime.nrSharedHours(time) * freeTime.nrSharedDays(time); 374 } 375 } 376 return ret; 377 } 378 379 /** 380 * Get student external id 381 * @return student external unique id 382 */ 383 public String getExternalId() { return iExternalId; } 384 /** 385 * Set student external id 386 * @param externalId student external id 387 */ 388 public void setExternalId(String externalId) { iExternalId = externalId; } 389 390 /** 391 * Get student name 392 * @return student name 393 */ 394 public String getName() { return iName; } 395 /** 396 * Set student name 397 * @param name student name 398 */ 399 public void setName(String name) { iName = name; } 400 401 /** 402 * Linked sections of this student 403 * @return linked sections of this student 404 */ 405 public List<LinkedSections> getLinkedSections() { return iLinkedSections; } 406 407 /** 408 * Get student status (online sectioning only) 409 * @return student sectioning status 410 */ 411 public String getStatus() { return iStatus; } 412 /** 413 * Set student status 414 * @param status student sectioning status 415 */ 416 public void setStatus(String status) { iStatus = status; } 417 418 /** 419 * Get last email time stamp (online sectioning only) 420 * @return student email time stamp 421 */ 422 public Long getEmailTimeStamp() { return iEmailTimeStamp; } 423 /** 424 * Set last email time stamp 425 * @param emailTimeStamp student email time stamp 426 */ 427 public void setEmailTimeStamp(Long emailTimeStamp) { iEmailTimeStamp = emailTimeStamp; } 428 429 @Override 430 public int compareTo(Student s) { 431 // priority students first, dummy students last 432 if (getPriority() != s.getPriority()) 433 return (getPriority().ordinal() < s.getPriority().ordinal() ? -1 : 1); 434 // then id 435 return Long.valueOf(getId()).compareTo(s.getId()); 436 } 437 438 /** 439 * List of student unavailabilities 440 * @return student unavailabilities 441 */ 442 public List<Unavailability> getUnavailabilities() { return iUnavailabilities; } 443 444 /** 445 * Check if student is available during the given section 446 * @param section given section 447 * @return true, if available (the section cannot overlap and there is no overlapping unavailability that cannot overlap) 448 */ 449 public boolean isAvailable(Section section) { 450 if (section.isAllowOverlap() || section.getTime() == null) return true; 451 for (Unavailability unavailability: getUnavailabilities()) 452 if (unavailability.isOverlapping(section)) return false; 453 return true; 454 } 455 456 /** 457 * Check if student is available during the given enrollment 458 * @param enrollment given enrollment 459 * @return true, if available 460 */ 461 public boolean isAvailable(Enrollment enrollment) { 462 if (enrollment != null && enrollment.isCourseRequest() && !enrollment.isAllowOverlap()) 463 for (Section section: enrollment.getSections()) 464 if (!isAvailable(section)) return false; 465 return true; 466 } 467 468 /** 469 * Return true if the student needs short distances. A different distance conflict checking is employed for such students. 470 * @return true if the student needs short distances 471 */ 472 public boolean isNeedShortDistances() { 473 return iNeedShortDistances; 474 } 475 476 /** 477 * Set true if the student needs short distances. A different distance conflict checking is employed for such students. 478 * @param needShortDistances true if the student needs short distances (default is false) 479 */ 480 public void setNeedShortDistances(boolean needShortDistances) { 481 iNeedShortDistances = needShortDistances; 482 } 483 484 /** 485 * True if student can be enrolled in disabled sections, regardless if his/her reservations 486 * @return does this student allow for disabled sections 487 */ 488 public boolean isAllowDisabled() { 489 return iAllowDisabled; 490 } 491 492 /** 493 * Set to true if student can be enrolled in disabled sections, regardless if his/her reservations 494 * @param allowDisabled does this student allow for disabled sections 495 */ 496 public void setAllowDisabled(boolean allowDisabled) { 497 iAllowDisabled = allowDisabled; 498 } 499 500 /** 501 * True if student has min credit defined 502 * @return true if min credit is set 503 */ 504 public boolean hasMinCredit() { return iMinCredit != null; } 505 506 /** 507 * Get student min credit (0 if not set) 508 * return student min credit 509 */ 510 public float getMinCredit() { return (iMinCredit == null ? 0 : iMinCredit.floatValue()); } 511 512 /** 513 * Has student any critical course requests? 514 * @return true if a student has at least one course request that is marked as critical 515 */ 516 @Deprecated 517 public boolean hasCritical() { 518 for (Request r: iRequests) 519 if (!r.isAlternative() && r.isCritical()) return true; 520 return false; 521 } 522 523 /** 524 * Has student any critical course requests? 525 * @return true if a student has at least one course request that is marked as critical 526 */ 527 public boolean hasCritical(RequestPriority rp) { 528 for (Request r: iRequests) 529 if (!r.isAlternative() && rp.isCritical(r)) return true; 530 return false; 531 } 532 533 /** 534 * Has student any unassigned critical course requests? 535 * @return true if a student has at least one not-alternative course request that is marked as critical and that is not assigned 536 */ 537 @Deprecated 538 public boolean hasUnassignedCritical(Assignment<Request, Enrollment> assignment) { 539 for (Request r: iRequests) 540 if (!r.isAlternative() && r.isCritical() && assignment.getValue(r) == null) return true; 541 return false; 542 } 543 544 /** 545 * Has student any unassigned critical course requests? 546 * @return true if a student has at least one not-alternative course request that is marked as critical and that is not assigned 547 */ 548 public boolean hasUnassignedCritical(Assignment<Request, Enrollment> assignment, RequestPriority rp) { 549 for (Request r: iRequests) 550 if (!r.isAlternative() && rp.isCritical(r) && assignment.getValue(r) == null) return true; 551 return false; 552 } 553 554 /** 555 * Set student min credit (null if not set) 556 * @param maxCredit student min credit 557 */ 558 public void setMinCredit(Float maxCredit) { iMinCredit = maxCredit; } 559 560 /** 561 * True if student has max credit defined 562 * @return true if max credit is set 563 */ 564 public boolean hasMaxCredit() { return iMaxCredit != null; } 565 566 /** 567 * Get student max credit ({@link Float#MAX_VALUE} if not set) 568 * return student max credit 569 */ 570 public float getMaxCredit() { return (iMaxCredit == null ? Float.MAX_VALUE : iMaxCredit.floatValue()); } 571 572 /** 573 * Set student max credit (null if not set) 574 * @param maxCredit student max credit 575 */ 576 public void setMaxCredit(Float maxCredit) { iMaxCredit = maxCredit; } 577 578 /** 579 * Return the number of assigned credits of the student 580 * @param assignment current assignment 581 * @return total assigned credit using {@link Enrollment#getCredit()} 582 */ 583 public float getAssignedCredit(Assignment<Request, Enrollment> assignment) { 584 float credit = 0f; 585 for (Request r: getRequests()) { 586 Enrollment e = r.getAssignment(assignment); 587 if (e != null) credit += e.getCredit(); 588 } 589 return credit; 590 } 591 592 /** 593 * Student priority level. Higher priority students are to be assigned first. 594 * The student priority is used to re-order students and assign them accoding 595 * to their priority. 596 */ 597 public static enum StudentPriority { 598 Priority("P", 1.00), 599 Senior("4", 0.70), 600 Junior("3", 0.49), 601 Sophomore("2", 0.33), 602 Freshmen("1", 0.24), 603 Normal("N", null), // this is the default priority 604 Dummy("D", null), // dummy students priority 605 ; 606 607 String iCode; 608 Double iBoost; 609 StudentPriority(String code, Double boost) { 610 iCode = code; 611 iBoost = boost; 612 } 613 public String code() { return iCode; } 614 public Double getBoost() { return iBoost; } 615 616 public boolean isSameOrHigher(Student s) { 617 return s.getPriority().ordinal() <= ordinal(); 618 } 619 public boolean isHigher(Student s) { 620 return ordinal() < s.getPriority().ordinal(); 621 } 622 public boolean isSame(Student s) { 623 return ordinal() == s.getPriority().ordinal(); 624 } 625 public static StudentPriority getPriority(String value) { 626 if ("true".equalsIgnoreCase(value)) return StudentPriority.Priority; 627 if ("false".equalsIgnoreCase(value)) return StudentPriority.Normal; 628 for (StudentPriority sp: StudentPriority.values()) { 629 if (sp.name().equalsIgnoreCase(value)) return sp; 630 } 631 return StudentPriority.Normal; 632 } 633 } 634 635 /** 636 * Check if a student has given accommodation 637 * @param code accommodation reference code 638 * @return true if present 639 */ 640 public boolean hasAccommodation(String code) { 641 return code != null && !code.isEmpty() && iAccommodations.contains(code); 642 } 643 644 public void setClassFirstDate(Integer classFirstDate) { 645 iClassFirstDate = classFirstDate; 646 } 647 648 public Integer getClassFirstDate() { 649 return iClassFirstDate; 650 } 651 652 public void setClassLastDate(Integer classLastDate) { 653 iClassLastDate = classLastDate; 654 } 655 656 public Integer getClassLastDate() { 657 return iClassLastDate; 658 } 659 660 public ModalityPreference getModalityPreference() { return iModalityPreference; } 661 public void setModalityPreference(ModalityPreference p) { iModalityPreference = p ;} 662 663 public BackToBackPreference getBackToBackPreference() { return iBackToBackPreference; } 664 public void setBackToBackPreference(BackToBackPreference p) { iBackToBackPreference = p; } 665 666 public static enum ModalityPreference { 667 NO_PREFERENCE, 668 ONLINE_PREFERRED, 669 ONILNE_DISCOURAGED, 670 ONLINE_REQUIRED, 671 } 672 673 public static enum BackToBackPreference { 674 NO_PREFERENCE, 675 BTB_PREFERRED, 676 BTB_DISCOURAGED, 677 } 678}