001package org.cpsolver.studentsct; 002 003import java.io.File; 004import java.util.ArrayList; 005import java.util.BitSet; 006import java.util.Collections; 007import java.util.Comparator; 008import java.util.HashSet; 009import java.util.HashMap; 010import java.util.Iterator; 011import java.util.List; 012import java.util.Map; 013import java.util.Set; 014 015import org.cpsolver.coursett.model.Placement; 016import org.cpsolver.coursett.model.RoomLocation; 017import org.cpsolver.coursett.model.TimeLocation; 018import org.cpsolver.ifs.assignment.Assignment; 019import org.cpsolver.ifs.model.Constraint; 020import org.cpsolver.ifs.util.DistanceMetric; 021import org.cpsolver.ifs.util.Progress; 022import org.cpsolver.studentsct.constraint.FixedAssignments; 023import org.cpsolver.studentsct.filter.StudentFilter; 024import org.cpsolver.studentsct.model.AcademicAreaCode; 025import org.cpsolver.studentsct.model.AreaClassificationMajor; 026import org.cpsolver.studentsct.model.Choice; 027import org.cpsolver.studentsct.model.Config; 028import org.cpsolver.studentsct.model.Course; 029import org.cpsolver.studentsct.model.CourseRequest; 030import org.cpsolver.studentsct.model.Enrollment; 031import org.cpsolver.studentsct.model.FreeTimeRequest; 032import org.cpsolver.studentsct.model.Instructor; 033import org.cpsolver.studentsct.model.Offering; 034import org.cpsolver.studentsct.model.Request; 035import org.cpsolver.studentsct.model.Request.RequestPriority; 036import org.cpsolver.studentsct.model.Student.StudentPriority; 037import org.cpsolver.studentsct.model.RequestGroup; 038import org.cpsolver.studentsct.model.Section; 039import org.cpsolver.studentsct.model.Student; 040import org.cpsolver.studentsct.model.Subpart; 041import org.cpsolver.studentsct.model.Unavailability; 042import org.cpsolver.studentsct.reservation.CourseReservation; 043import org.cpsolver.studentsct.reservation.CourseRestriction; 044import org.cpsolver.studentsct.reservation.CurriculumOverride; 045import org.cpsolver.studentsct.reservation.CurriculumReservation; 046import org.cpsolver.studentsct.reservation.CurriculumRestriction; 047import org.cpsolver.studentsct.reservation.DummyReservation; 048import org.cpsolver.studentsct.reservation.GroupReservation; 049import org.cpsolver.studentsct.reservation.IndividualReservation; 050import org.cpsolver.studentsct.reservation.IndividualRestriction; 051import org.cpsolver.studentsct.reservation.LearningCommunityReservation; 052import org.cpsolver.studentsct.reservation.Reservation; 053import org.cpsolver.studentsct.reservation.ReservationOverride; 054import org.cpsolver.studentsct.reservation.Restriction; 055import org.dom4j.Document; 056import org.dom4j.DocumentException; 057import org.dom4j.Element; 058import org.dom4j.io.SAXReader; 059 060/** 061 * Load student sectioning model from an XML file. 062 * 063 * <br> 064 * <br> 065 * Parameters: 066 * <table border='1' summary='Related Solver Parameters'> 067 * <tr> 068 * <th>Parameter</th> 069 * <th>Type</th> 070 * <th>Comment</th> 071 * </tr> 072 * <tr> 073 * <td>General.Input</td> 074 * <td>{@link String}</td> 075 * <td>Path of an XML file to be loaded</td> 076 * </tr> 077 * <tr> 078 * <td>Xml.LoadBest</td> 079 * <td>{@link Boolean}</td> 080 * <td>If true, load best assignments</td> 081 * </tr> 082 * <tr> 083 * <td>Xml.LoadInitial</td> 084 * <td>{@link Boolean}</td> 085 * <td>If false, load initial assignments</td> 086 * </tr> 087 * <tr> 088 * <td>Xml.LoadCurrent</td> 089 * <td>{@link Boolean}</td> 090 * <td>If true, load current assignments</td> 091 * </tr> 092 * <tr> 093 * <td>Xml.LoadOfferings</td> 094 * <td>{@link Boolean}</td> 095 * <td>If true, load offerings (and their stucture, i.e., courses, 096 * configurations, subparts and sections)</td> 097 * </tr> 098 * <tr> 099 * <td>Xml.LoadStudents</td> 100 * <td>{@link Boolean}</td> 101 * <td>If true, load students (and their requests)</td> 102 * </tr> 103 * <tr> 104 * <td>Xml.StudentFilter</td> 105 * <td>{@link StudentFilter}</td> 106 * <td>If provided, students are filtered by the given student filter</td> 107 * </tr> 108 * </table> 109 * 110 * <br> 111 * <br> 112 * Usage: 113 * <pre><code> 114 * StudentSectioningModel model = new StudentSectioningModel(cfg);<br> 115 * new StudentSectioningXMLLoader(model).load();<br> 116 * </code></pre> 117 * 118 * @version StudentSct 1.3 (Student Sectioning)<br> 119 * Copyright (C) 2007 - 2014 Tomas Muller<br> 120 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 121 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 122 * <br> 123 * This library is free software; you can redistribute it and/or modify 124 * it under the terms of the GNU Lesser General Public License as 125 * published by the Free Software Foundation; either version 3 of the 126 * License, or (at your option) any later version. <br> 127 * <br> 128 * This library is distributed in the hope that it will be useful, but 129 * WITHOUT ANY WARRANTY; without even the implied warranty of 130 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 131 * Lesser General Public License for more details. <br> 132 * <br> 133 * You should have received a copy of the GNU Lesser General Public 134 * License along with this library; if not see 135 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 136 */ 137 138public class StudentSectioningXMLLoader extends StudentSectioningLoader { 139 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger 140 .getLogger(StudentSectioningXMLLoader.class); 141 142 private File iInputFile; 143 private File iTimetableFile = null; 144 private boolean iLoadBest = false; 145 private boolean iLoadInitial = false; 146 private boolean iLoadCurrent = false; 147 private boolean iLoadOfferings = true; 148 private boolean iLoadStudents = true; 149 private StudentFilter iStudentFilter = null; 150 private boolean iWaitlistCritical = false; 151 private boolean iMoveCriticalUp = false; 152 153 /** 154 * Constructor 155 * 156 * @param model 157 * student sectioning model 158 * @param assignment current assignment 159 */ 160 public StudentSectioningXMLLoader(StudentSectioningModel model, Assignment<Request, Enrollment> assignment) { 161 super(model, assignment); 162 iInputFile = new File(getModel().getProperties().getProperty("General.Input", 163 "." + File.separator + "solution.xml")); 164 if (getModel().getProperties().getProperty("General.InputTimetable") != null) 165 iTimetableFile = new File(getModel().getProperties().getProperty("General.InputTimetable")); 166 iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true); 167 iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true); 168 iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true); 169 iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true); 170 iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true); 171 iWaitlistCritical = getModel().getProperties().getPropertyBoolean("Xml.WaitlistCritical", false); 172 iMoveCriticalUp = getModel().getProperties().getPropertyBoolean("Xml.MoveCriticalUp", false); 173 if (getModel().getProperties().getProperty("Xml.StudentFilter") != null) { 174 try { 175 iStudentFilter = (StudentFilter) Class.forName( 176 getModel().getProperties().getProperty("Xml.StudentFilter")).getConstructor(new Class[] {}) 177 .newInstance(new Object[] {}); 178 } catch (Exception e) { 179 sLogger.error("Unable to create student filter, reason: " + e.getMessage(), e); 180 } 181 } 182 } 183 184 /** Set input file (e.g., if it is not set by General.Input property) 185 * @param inputFile input file 186 **/ 187 public void setInputFile(File inputFile) { 188 iInputFile = inputFile; 189 } 190 191 /** Set student filter 192 * @param filter student filter 193 **/ 194 public void setStudentFilter(StudentFilter filter) { 195 iStudentFilter = filter; 196 } 197 198 /** Set whether to load students 199 * @param loadStudents true if students are to be loaded 200 **/ 201 public void setLoadStudents(boolean loadStudents) { 202 iLoadStudents = loadStudents; 203 } 204 205 /** Set whether to load offerings 206 * @param loadOfferings true if instructional offerings are to be loaded 207 **/ 208 public void setLoadOfferings(boolean loadOfferings) { 209 iLoadOfferings = loadOfferings; 210 } 211 212 /** Create BitSet from a bit string */ 213 private static BitSet createBitSet(String bitString) { 214 BitSet ret = new BitSet(bitString.length()); 215 for (int i = 0; i < bitString.length(); i++) 216 if (bitString.charAt(i) == '1') 217 ret.set(i); 218 return ret; 219 } 220 221 /** Load the file */ 222 @Override 223 public void load() throws Exception { 224 sLogger.debug("Reading XML data from " + iInputFile); 225 226 Document document = (new SAXReader()).read(iInputFile); 227 Element root = document.getRootElement(); 228 229 load(root); 230 } 231 232 public void load(Document document) { 233 Element root = document.getRootElement(); 234 235 if (getModel() != null && root.element("travel-times") != null) 236 loadTravelTimes(root.element("travel-times"), getModel().getDistanceMetric()); 237 238 Progress.getInstance(getModel()).load(root, true); 239 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 240 241 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 242 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 243 244 if (root.element("offerings") != null) { 245 loadOfferings(root.element("offerings"), offeringTable, courseTable, null); 246 } 247 248 List<Enrollment> bestEnrollments = new ArrayList<Enrollment>(); 249 List<Enrollment> currentEnrollments = new ArrayList<Enrollment>(); 250 if (root.element("students") != null) { 251 loadStudents(root.element("students"), offeringTable, courseTable, bestEnrollments, currentEnrollments); 252 } 253 254 if (root.element("constraints") != null) 255 loadLinkedSections(root.element("constraints"), offeringTable); 256 257 if (!bestEnrollments.isEmpty()) assignBest(bestEnrollments); 258 if (!currentEnrollments.isEmpty()) assignCurrent(currentEnrollments); 259 260 if (iMoveCriticalUp) moveCriticalRequestsUp(); 261 262 boolean hasFixed = false; 263 for (Request r: getModel().variables()) { 264 if (r instanceof CourseRequest && ((CourseRequest)r).isFixed()) { 265 hasFixed = true; break; 266 } 267 } 268 if (hasFixed) 269 getModel().addGlobalConstraint(new FixedAssignments()); 270 } 271 272 /** 273 * Load data from the given XML root 274 * @param root document root 275 * @throws DocumentException 276 */ 277 protected void load(Element root) throws DocumentException { 278 sLogger.debug("Root element: " + root.getName()); 279 if (!"sectioning".equals(root.getName())) { 280 sLogger.error("Given XML file is not student sectioning problem."); 281 return; 282 } 283 284 if (iLoadOfferings && getModel().getDistanceConflict() != null && root.element("travel-times") != null) 285 loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric()); 286 287 Map<Long, Placement> timetable = null; 288 if (iTimetableFile != null) { 289 sLogger.info("Reading timetable from " + iTimetableFile + " ..."); 290 Document timetableDocument = (new SAXReader()).read(iTimetableFile); 291 Element timetableRoot = timetableDocument.getRootElement(); 292 if (!"timetable".equals(timetableRoot.getName())) { 293 sLogger.error("Given XML file is not course timetabling problem."); 294 return; 295 } 296 timetable = loadTimetable(timetableRoot); 297 } 298 299 Progress.getInstance(getModel()).load(root, true); 300 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 301 302 if (root.attributeValue("term") != null) 303 getModel().getProperties().setProperty("Data.Term", root.attributeValue("term")); 304 if (root.attributeValue("year") != null) 305 getModel().getProperties().setProperty("Data.Year", root.attributeValue("year")); 306 if (root.attributeValue("initiative") != null) 307 getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative")); 308 309 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 310 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 311 312 if (iLoadOfferings && root.element("offerings") != null) { 313 loadOfferings(root.element("offerings"), offeringTable, courseTable, timetable); 314 } else { 315 for (Offering offering : getModel().getOfferings()) { 316 offeringTable.put(new Long(offering.getId()), offering); 317 for (Course course : offering.getCourses()) { 318 courseTable.put(new Long(course.getId()), course); 319 } 320 } 321 } 322 323 List<Enrollment> bestEnrollments = new ArrayList<Enrollment>(); 324 List<Enrollment> currentEnrollments = new ArrayList<Enrollment>(); 325 if (iLoadStudents && root.element("students") != null) { 326 loadStudents(root.element("students"), offeringTable, courseTable, bestEnrollments, currentEnrollments); 327 } 328 329 if (iLoadOfferings && root.element("constraints") != null) 330 loadLinkedSections(root.element("constraints"), offeringTable); 331 332 if (!bestEnrollments.isEmpty()) assignBest(bestEnrollments); 333 if (!currentEnrollments.isEmpty()) assignCurrent(currentEnrollments); 334 335 if (iMoveCriticalUp) moveCriticalRequestsUp(); 336 337 sLogger.debug("Model successfully loaded."); 338 } 339 340 /** 341 * Load offerings 342 * @param offeringsEl offerings element 343 * @param offeringTable offering table 344 * @param courseTable course table 345 * @param timetable provided timetable (null if to be loaded from the given document) 346 */ 347 protected void loadOfferings(Element offeringsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, Map<Long, Placement> timetable) { 348 HashMap<Long, Config> configTable = new HashMap<Long, Config>(); 349 HashMap<Long, Subpart> subpartTable = new HashMap<Long, Subpart>(); 350 HashMap<Long, Section> sectionTable = new HashMap<Long, Section>(); 351 for (Iterator<?> i = offeringsEl.elementIterator("offering"); i.hasNext();) { 352 Element offeringEl = (Element) i.next(); 353 Offering offering = new Offering( 354 Long.parseLong(offeringEl.attributeValue("id")), 355 offeringEl.attributeValue("name", "O" + offeringEl.attributeValue("id"))); 356 offeringTable.put(new Long(offering.getId()), offering); 357 getModel().addOffering(offering); 358 359 for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) { 360 Element courseEl = (Element) j.next(); 361 Course course = loadCourse(courseEl, offering); 362 courseTable.put(new Long(course.getId()), course); 363 } 364 365 for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) { 366 Element configEl = (Element) j.next(); 367 Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable); 368 configTable.put(config.getId(), config); 369 } 370 371 for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) { 372 Element reservationEl = (Element)j.next(); 373 loadReservation(reservationEl, offering, configTable, sectionTable); 374 } 375 376 for (Iterator<?> j = offeringEl.elementIterator("restriction"); j.hasNext(); ) { 377 Element restrictionEl = (Element)j.next(); 378 loadRestriction(restrictionEl, offering, configTable, sectionTable); 379 } 380 } 381 } 382 383 /** 384 * Load course 385 * @param courseEl course element 386 * @param offering parent offering 387 * @return loaded course 388 */ 389 protected Course loadCourse(Element courseEl, Offering offering) { 390 Course course = new Course( 391 Long.parseLong(courseEl.attributeValue("id")), 392 courseEl.attributeValue("subjectArea", ""), 393 courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")), 394 offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")), 395 Integer.parseInt(courseEl.attributeValue("projected", "0"))); 396 course.setCredit(courseEl.attributeValue("credit")); 397 String credits = courseEl.attributeValue("credits"); 398 if (credits != null) 399 course.setCreditValue(Float.valueOf(credits)); 400 return course; 401 } 402 403 /** 404 * Load config 405 * @param configEl config element 406 * @param offering parent offering 407 * @param subpartTable subpart table (of the offering) 408 * @param sectionTable section table (of the offering) 409 * @param timetable provided timetable 410 * @return loaded config 411 */ 412 protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 413 Config config = new Config 414 (Long.parseLong(configEl.attributeValue("id")), 415 Integer.parseInt(configEl.attributeValue("limit", "-1")), 416 configEl.attributeValue("name", "G" + configEl.attributeValue("id")), 417 offering); 418 Element imEl = configEl.element("instructional-method"); 419 if (imEl != null) { 420 config.setInstructionalMethodId(Long.parseLong(imEl.attributeValue("id"))); 421 config.setInstructionalMethodName(imEl.attributeValue("name", "M" + imEl.attributeValue("id"))); 422 config.setInstructionalMethodReference(imEl.attributeValue("reference", config.getInstructionalMethodName())); 423 } 424 for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) { 425 Element subpartEl = (Element) k.next(); 426 Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable); 427 subpartTable.put(new Long(subpart.getId()), subpart); 428 } 429 return config; 430 } 431 432 /** 433 * Load subpart 434 * @param subpartEl supart element 435 * @param config parent config 436 * @param subpartTable subpart table (of the offering) 437 * @param sectionTable section table (of the offering) 438 * @param timetable provided timetable 439 * @return loaded subpart 440 */ 441 protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 442 Subpart parentSubpart = null; 443 if (subpartEl.attributeValue("parent") != null) 444 parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent"))); 445 Subpart subpart = new Subpart( 446 Long.parseLong(subpartEl.attributeValue("id")), 447 subpartEl.attributeValue("itype"), 448 subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")), 449 config, 450 parentSubpart); 451 subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false"))); 452 subpart.setCredit(subpartEl.attributeValue("credit")); 453 String credits = subpartEl.attributeValue("credits"); 454 if (credits != null) 455 subpart.setCreditValue(Float.valueOf(credits)); 456 457 for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) { 458 Element sectionEl = (Element) l.next(); 459 Section section = loadSection(sectionEl, subpart, sectionTable, timetable); 460 sectionTable.put(new Long(section.getId()), section); 461 } 462 463 return subpart; 464 } 465 466 /** 467 * Load section 468 * @param sectionEl section element 469 * @param subpart parent subpart 470 * @param sectionTable section table (of the offering) 471 * @param timetable provided timetable 472 * @return loaded section 473 */ 474 @SuppressWarnings("deprecation") 475 protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 476 Section parentSection = null; 477 if (sectionEl.attributeValue("parent") != null) 478 parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent"))); 479 Placement placement = null; 480 if (timetable != null) { 481 placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id"))); 482 } else { 483 TimeLocation time = null; 484 Element timeEl = sectionEl.element("time"); 485 if (timeEl != null) { 486 time = new TimeLocation( 487 Integer.parseInt(timeEl.attributeValue("days"), 2), 488 Integer.parseInt(timeEl.attributeValue("start")), 489 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 490 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 491 timeEl.attributeValue("datePatternName", ""), 492 createBitSet(timeEl.attributeValue("dates")), 493 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 494 if (timeEl.attributeValue("pattern") != null) 495 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 496 } 497 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 498 for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) { 499 Element roomEl = (Element) m.next(); 500 Double posX = null, posY = null; 501 if (roomEl.attributeValue("location") != null) { 502 String loc = roomEl.attributeValue("location"); 503 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 504 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 505 } 506 RoomLocation room = new RoomLocation( 507 Long.valueOf(roomEl.attributeValue("id")), 508 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 509 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 510 0, Integer.parseInt(roomEl.attributeValue("capacity")), 511 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 512 rooms.add(room); 513 } 514 placement = (time == null ? null : new Placement(null, time, rooms)); 515 } 516 517 List<Instructor> instructors = new ArrayList<Instructor>(); 518 for (Iterator<?> m = sectionEl.elementIterator("instructor"); m.hasNext(); ) { 519 Element instructorEl = (Element)m.next(); 520 instructors.add(new Instructor(Long.parseLong(instructorEl.attributeValue("id")), instructorEl.attributeValue("externalId"), instructorEl.attributeValue("name"), instructorEl.attributeValue("email"))); 521 } 522 if (instructors.isEmpty() && sectionEl.attributeValue("instructorIds") != null) 523 instructors = Instructor.toInstructors(sectionEl.attributeValue("instructorIds"), sectionEl.attributeValue("instructorNames")); 524 Section section = new Section( 525 Long.parseLong(sectionEl.attributeValue("id")), 526 Integer.parseInt(sectionEl.attributeValue("limit")), 527 sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")), 528 subpart, placement, instructors, parentSection); 529 530 section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0"))); 531 section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0"))); 532 section.setCancelled("true".equalsIgnoreCase(sectionEl.attributeValue("cancelled", "false"))); 533 section.setEnabled("true".equalsIgnoreCase(sectionEl.attributeValue("enabled", "true"))); 534 section.setOnline("true".equalsIgnoreCase(sectionEl.attributeValue("online", "false"))); 535 for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) { 536 Element cNameEl = (Element)m.next(); 537 section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText()); 538 } 539 Element ignoreEl = sectionEl.element("no-conflicts"); 540 if (ignoreEl != null) { 541 for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); ) 542 section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id"))); 543 } 544 545 return section; 546 } 547 548 /** 549 * Load reservation 550 * @param reservationEl reservation element 551 * @param offering parent offering 552 * @param configTable config table (of the offering) 553 * @param sectionTable section table (of the offering) 554 * @return loaded reservation 555 */ 556 protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 557 Reservation r = null; 558 if ("individual".equals(reservationEl.attributeValue("type"))) { 559 Set<Long> studentIds = new HashSet<Long>(); 560 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 561 Element studentEl = (Element)k.next(); 562 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 563 } 564 r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 565 } else if ("group".equals(reservationEl.attributeValue("type"))) { 566 Set<Long> studentIds = new HashSet<Long>(); 567 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 568 Element studentEl = (Element)k.next(); 569 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 570 } 571 r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")), 572 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 573 offering, studentIds); 574 } else if ("lc".equals(reservationEl.attributeValue("type"))) { 575 Set<Long> studentIds = new HashSet<Long>(); 576 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 577 Element studentEl = (Element)k.next(); 578 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 579 } 580 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 581 for (Course course: offering.getCourses()) { 582 if (course.getId() == courseId) 583 r = new LearningCommunityReservation(Long.valueOf(reservationEl.attributeValue("id")), 584 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 585 course, studentIds); 586 } 587 } else if ("curriculum".equals(reservationEl.attributeValue("type")) || "curriculum-override".equals(reservationEl.attributeValue("type"))) { 588 List<String> classifications = new ArrayList<String>(); 589 for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) { 590 Element clasfEl = (Element)k.next(); 591 classifications.add(clasfEl.attributeValue("code")); 592 } 593 List<String> majors = new ArrayList<String>(); 594 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 595 Element majorEl = (Element)k.next(); 596 majors.add(majorEl.attributeValue("code")); 597 } 598 if ("curriculum".equals(reservationEl.attributeValue("type"))) 599 r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")), 600 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 601 offering, 602 reservationEl.attributeValue("area"), 603 classifications, majors); 604 else 605 r = new CurriculumOverride(Long.valueOf(reservationEl.attributeValue("id")), 606 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 607 offering, 608 reservationEl.attributeValue("area"), 609 classifications, majors); 610 } else if ("course".equals(reservationEl.attributeValue("type"))) { 611 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 612 for (Course course: offering.getCourses()) { 613 if (course.getId() == courseId) 614 r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course); 615 } 616 } else if ("dummy".equals(reservationEl.attributeValue("type"))) { 617 r = new DummyReservation(offering); 618 } else if ("override".equals(reservationEl.attributeValue("type"))) { 619 Set<Long> studentIds = new HashSet<Long>(); 620 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 621 Element studentEl = (Element)k.next(); 622 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 623 } 624 r = new ReservationOverride(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 625 } 626 if (r == null) { 627 sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type")); 628 return null; 629 } 630 r.setExpired("true".equals(reservationEl.attributeValue("expired", "false"))); 631 for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) { 632 Element configEl = (Element)k.next(); 633 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 634 } 635 for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) { 636 Element sectionEl = (Element)k.next(); 637 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))), false); 638 } 639 r.setPriority(Integer.parseInt(reservationEl.attributeValue("priority", String.valueOf(r.getPriority())))); 640 r.setMustBeUsed("true".equals(reservationEl.attributeValue("mustBeUsed", r.mustBeUsed() ? "true" : "false"))); 641 r.setAllowOverlap("true".equals(reservationEl.attributeValue("allowOverlap", r.isAllowOverlap() ? "true" : "false"))); 642 r.setCanAssignOverLimit("true".equals(reservationEl.attributeValue("canAssignOverLimit", r.canAssignOverLimit() ? "true" : "false"))); 643 r.setAllowDisabled("true".equals(reservationEl.attributeValue("allowDisabled", r.isAllowDisabled() ? "true" : "false"))); 644 r.setNeverIncluded("true".equals(reservationEl.attributeValue("neverIncluded", "false"))); 645 r.setBreakLinkedSections("true".equals(reservationEl.attributeValue("breakLinkedSections", "false"))); 646 return r; 647 } 648 649 /** 650 * Load restriction 651 * @param restrictionEl restriction element 652 * @param offering parent offering 653 * @param configTable config table (of the offering) 654 * @param sectionTable section table (of the offering) 655 * @return loaded restriction 656 */ 657 protected Restriction loadRestriction(Element restrictionEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 658 Restriction r = null; 659 if ("individual".equals(restrictionEl.attributeValue("type"))) { 660 Set<Long> studentIds = new HashSet<Long>(); 661 for (Iterator<?> k = restrictionEl.elementIterator("student"); k.hasNext(); ) { 662 Element studentEl = (Element)k.next(); 663 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 664 } 665 r = new IndividualRestriction(Long.valueOf(restrictionEl.attributeValue("id")), offering, studentIds); 666 } else if ("curriculum".equals(restrictionEl.attributeValue("type"))) { 667 List<String> classifications = new ArrayList<String>(); 668 for (Iterator<?> k = restrictionEl.elementIterator("classification"); k.hasNext(); ) { 669 Element clasfEl = (Element)k.next(); 670 classifications.add(clasfEl.attributeValue("code")); 671 } 672 List<String> majors = new ArrayList<String>(); 673 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 674 Element majorEl = (Element)k.next(); 675 majors.add(majorEl.attributeValue("code")); 676 } 677 r = new CurriculumRestriction(Long.valueOf(restrictionEl.attributeValue("id")), 678 offering, 679 restrictionEl.attributeValue("area"), 680 classifications, majors); 681 } else if ("course".equals(restrictionEl.attributeValue("type"))) { 682 long courseId = Long.parseLong(restrictionEl.attributeValue("course")); 683 for (Course course: offering.getCourses()) { 684 if (course.getId() == courseId) 685 r = new CourseRestriction(Long.valueOf(restrictionEl.attributeValue("id")), course); 686 } 687 } 688 if (r == null) { 689 sLogger.error("Unknown reservation type "+ restrictionEl.attributeValue("type")); 690 return null; 691 } 692 for (Iterator<?> k = restrictionEl.elementIterator("config"); k.hasNext(); ) { 693 Element configEl = (Element)k.next(); 694 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 695 } 696 for (Iterator<?> k = restrictionEl.elementIterator("section"); k.hasNext(); ) { 697 Element sectionEl = (Element)k.next(); 698 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id")))); 699 } 700 return r; 701 } 702 703 /** 704 * Load given timetable 705 * @param timetableRoot document root in the course timetabling XML format 706 * @return loaded timetable (map class id: assigned placement) 707 */ 708 protected Map<Long, Placement> loadTimetable(Element timetableRoot) { 709 Map<Long, Placement> timetable = new HashMap<Long, Placement>(); 710 HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>(); 711 for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) { 712 Element roomEl = (Element)i.next(); 713 Long roomId = Long.valueOf(roomEl.attributeValue("id")); 714 Double posX = null, posY = null; 715 if (roomEl.attributeValue("location") != null) { 716 String loc = roomEl.attributeValue("location"); 717 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 718 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 719 } 720 RoomLocation room = new RoomLocation( 721 Long.valueOf(roomEl.attributeValue("id")), 722 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 723 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 724 0, Integer.parseInt(roomEl.attributeValue("capacity")), 725 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 726 rooms.put(roomId, room); 727 } 728 for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) { 729 Element classEl = (Element)i.next(); 730 Long classId = Long.valueOf(classEl.attributeValue("id")); 731 TimeLocation time = null; 732 Element timeEl = null; 733 for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) { 734 Element e = (Element)j.next(); 735 if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; } 736 } 737 if (timeEl != null) { 738 time = new TimeLocation( 739 Integer.parseInt(timeEl.attributeValue("days"), 2), 740 Integer.parseInt(timeEl.attributeValue("start")), 741 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 742 classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")), 743 classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")), 744 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 745 if (timeEl.attributeValue("pattern") != null) 746 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 747 } 748 List<RoomLocation> room = new ArrayList<RoomLocation>(); 749 for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) { 750 Element roomEl = (Element) j.next(); 751 if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue; 752 room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id")))); 753 } 754 Placement placement = (time == null ? null : new Placement(null, time, room)); 755 if (placement != null) 756 timetable.put(classId, placement); 757 } 758 return timetable; 759 } 760 761 /** 762 * Load travel times 763 * @param travelTimesEl travel-time element 764 * @param metric distance metric to be populated 765 */ 766 protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) { 767 for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) { 768 Element travelTimeEl = (Element)i.next(); 769 metric.addTravelTime( 770 Long.valueOf(travelTimeEl.attributeValue("id1")), 771 Long.valueOf(travelTimeEl.attributeValue("id2")), 772 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 773 } 774 } 775 776 /** 777 * Load linked sections 778 * @param constraintsEl constraints element 779 * @param offeringTable offering table 780 */ 781 protected void loadLinkedSections(Element constraintsEl, Map<Long, Offering> offeringTable) { 782 for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) { 783 Element linkedEl = (Element) i.next(); 784 List<Section> sections = new ArrayList<Section>(); 785 for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) { 786 Element sectionEl = (Element) j.next(); 787 Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering"))); 788 sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id")))); 789 } 790 getModel().addLinkedSections("true".equals(linkedEl.attributeValue("mustBeUsed", "false")), sections); 791 } 792 } 793 794 /** 795 * Load students 796 * @param studentsEl students element 797 * @param offeringTable offering table 798 * @param courseTable course table 799 */ 800 protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, List<Enrollment> bestEnrollments, List<Enrollment> currentEnrollments) { 801 for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) { 802 Element studentEl = (Element) i.next(); 803 Student student = loadStudent(studentEl, offeringTable); 804 if (iStudentFilter != null && !iStudentFilter.accept(student)) 805 continue; 806 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 807 Element requestEl = (Element) j.next(); 808 Request request = loadRequest(requestEl, student, offeringTable, courseTable); 809 if (request == null) continue; 810 811 Element initialEl = requestEl.element("initial"); 812 if (iLoadInitial && initialEl != null) { 813 Enrollment enrollment = loadEnrollment(initialEl, request); 814 if (enrollment != null) 815 request.setInitialAssignment(enrollment); 816 } 817 Element currentEl = requestEl.element("current"); 818 if (iLoadCurrent && currentEl != null) { 819 Enrollment enrollment = loadEnrollment(currentEl, request); 820 if (enrollment != null) 821 currentEnrollments.add(enrollment); 822 } 823 Element bestEl = requestEl.element("best"); 824 if (iLoadBest && bestEl != null) { 825 Enrollment enrollment = loadEnrollment(bestEl, request); 826 if (enrollment != null) 827 bestEnrollments.add(enrollment); 828 } 829 Element fixedEl = requestEl.element("fixed"); 830 if (fixedEl != null && request instanceof CourseRequest) 831 ((CourseRequest)request).setFixedValue(loadEnrollment(fixedEl, request)); 832 } 833 getModel().addStudent(student); 834 } 835 } 836 837 /** 838 * Save best enrollments 839 * @param bestEnrollments best enrollments 840 */ 841 protected void assignBest(List<Enrollment> bestEnrollments) { 842 // Enrollments with a reservation must go first, enrollments with an override go last 843 for (Enrollment enrollment : bestEnrollments) { 844 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 845 if (!enrollment.getStudent().isAvailable(enrollment)) { 846 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 847 continue; 848 } 849 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 850 if (conflicts.isEmpty()) 851 getAssignment().assign(0, enrollment); 852 else 853 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 854 } 855 for (Enrollment enrollment : bestEnrollments) { 856 if (enrollment.getReservation() != null) continue; 857 if (!enrollment.getStudent().isAvailable(enrollment)) { 858 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 859 continue; 860 } 861 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 862 if (conflicts.isEmpty()) 863 getAssignment().assign(0, enrollment); 864 else 865 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 866 } 867 for (Enrollment enrollment : bestEnrollments) { 868 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 869 if (!enrollment.getStudent().isAvailable(enrollment)) { 870 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 871 continue; 872 } 873 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 874 if (conflicts.isEmpty()) 875 getAssignment().assign(0, enrollment); 876 else 877 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 878 } 879 getModel().saveBest(getAssignment()); 880 } 881 882 /** 883 * Assign current enrollments 884 * @param currentEnrollments current enrollments 885 */ 886 protected void assignCurrent(List<Enrollment> currentEnrollments) { 887 for (Request request : getModel().variables()) 888 getAssignment().unassign(0, request); 889 // Enrollments with a reservation must go first, enrollments with an override go last 890 for (Enrollment enrollment : currentEnrollments) { 891 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 892 if (!enrollment.getStudent().isAvailable(enrollment)) { 893 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 894 continue; 895 } 896 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 897 if (conflicts.isEmpty()) 898 getAssignment().assign(0, enrollment); 899 else 900 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 901 } 902 for (Enrollment enrollment : currentEnrollments) { 903 if (enrollment.getReservation() != null) continue; 904 if (!enrollment.getStudent().isAvailable(enrollment)) { 905 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 906 continue; 907 } 908 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 909 if (conflicts.isEmpty()) 910 getAssignment().assign(0, enrollment); 911 else 912 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 913 } 914 for (Enrollment enrollment : currentEnrollments) { 915 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue; 916 if (!enrollment.getStudent().isAvailable(enrollment)) { 917 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 918 continue; 919 } 920 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 921 if (conflicts.isEmpty()) 922 getAssignment().assign(0, enrollment); 923 else 924 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 925 } 926 } 927 928 /** 929 * Load student 930 * @param studentEl student element 931 * @param offeringTable offering table 932 * @return loaded student 933 */ 934 protected Student loadStudent(Element studentEl, Map<Long, Offering> offeringTable) { 935 Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy"))); 936 if (studentEl.attributeValue("priority") != null) 937 student.setPriority(StudentPriority.getPriority(studentEl.attributeValue("priority"))); 938 if ("true".equals(studentEl.attributeValue("shortDistances"))) 939 student.setNeedShortDistances(true); 940 if ("true".equals(studentEl.attributeValue("allowDisabled"))) 941 student.setAllowDisabled(true); 942 student.setExternalId(studentEl.attributeValue("externalId")); 943 student.setName(studentEl.attributeValue("name")); 944 student.setStatus(studentEl.attributeValue("status")); 945 String minCredit = studentEl.attributeValue("minCredit"); 946 if (minCredit != null) 947 student.setMinCredit(Float.parseFloat(minCredit)); 948 String maxCredit = studentEl.attributeValue("maxCredit"); 949 if (maxCredit != null) 950 student.setMaxCredit(Float.parseFloat(maxCredit)); 951 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 952 Element requestEl = (Element) j.next(); 953 if ("classification".equals(requestEl.getName())) { 954 student.getAcademicAreaClasiffications().add( 955 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label"))); 956 } else if ("major".equals(requestEl.getName())) { 957 student.getMajors().add( 958 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label"))); 959 } else if ("minor".equals(requestEl.getName())) { 960 student.getMinors().add( 961 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label"))); 962 } else if ("unavailability".equals(requestEl.getName())) { 963 Offering offering = offeringTable.get(Long.parseLong(requestEl.attributeValue("offering"))); 964 Section section = (offering == null ? null : offering.getSection(Long.parseLong(requestEl.attributeValue("section")))); 965 if (section != null) 966 new Unavailability(student, section, "true".equals(requestEl.attributeValue("allowOverlap"))); 967 } else if ("acm".equals(requestEl.getName())) { 968 student.getAreaClassificationMajors().add( 969 new AreaClassificationMajor(requestEl.attributeValue("area"), requestEl.attributeValue("classification"), requestEl.attributeValue("major"))); 970 } else if ("advisor".equals(requestEl.getName())) { 971 student.getAdvisors().add(new Instructor(0l, requestEl.attributeValue("externalId"), requestEl.attributeValue("name"), requestEl.attributeValue("email"))); 972 } 973 } 974 return student; 975 } 976 977 /** 978 * Load request 979 * @param requestEl request element 980 * @param student parent student 981 * @param offeringTable offering table 982 * @param courseTable course table 983 * @return loaded request 984 */ 985 protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 986 if ("freeTime".equals(requestEl.getName())) { 987 return loadFreeTime(requestEl, student); 988 } else if ("course".equals(requestEl.getName())) { 989 return loadCourseRequest(requestEl, student, offeringTable, courseTable); 990 } else { 991 return null; 992 } 993 } 994 995 /** 996 * Load free time request 997 * @param requestEl request element 998 * @param student parent student 999 * @return loaded free time request 1000 */ 1001 public FreeTimeRequest loadFreeTime(Element requestEl, Student student) { 1002 TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2), 1003 Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl 1004 .attributeValue("length")), 0, 0, 1005 requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl 1006 .attributeValue("datePattern")), "", createBitSet(requestEl 1007 .attributeValue("dates")), 0); 1008 FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")), 1009 Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl 1010 .attributeValue("alternative")), student, time); 1011 if (requestEl.attributeValue("weight") != null) 1012 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1013 return request; 1014 } 1015 1016 /** 1017 * Load course request 1018 * @param requestEl request element 1019 * @param student parent student 1020 * @param offeringTable offering table 1021 * @param courseTable course table 1022 * @return loaded course request 1023 */ 1024 public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1025 List<Course> courses = new ArrayList<Course>(); 1026 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 1027 for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();) 1028 courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course")))); 1029 Long timeStamp = null; 1030 if (requestEl.attributeValue("timeStamp") != null) 1031 timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp")); 1032 CourseRequest courseRequest = new CourseRequest( 1033 Long.parseLong(requestEl.attributeValue("id")), 1034 Integer.parseInt(requestEl.attributeValue("priority")), 1035 "true".equals(requestEl.attributeValue("alternative")), 1036 student, courses, 1037 "true".equals(requestEl.attributeValue("waitlist", "false")), 1038 RequestPriority.valueOf(requestEl.attributeValue("importance", 1039 "true".equals(requestEl.attributeValue("critical", "false")) ? RequestPriority.Critical.name() : RequestPriority.Normal.name())), 1040 timeStamp); 1041 if (iWaitlistCritical && RequestPriority.Critical.isCritical(courseRequest) && !courseRequest.isAlternative()) courseRequest.setWaitlist(true); 1042 if (requestEl.attributeValue("weight") != null) 1043 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1044 for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) { 1045 Element choiceEl = (Element) k.next(); 1046 courseRequest.getWaitlistedChoices().add( 1047 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1048 } 1049 for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) { 1050 Element choiceEl = (Element) k.next(); 1051 courseRequest.getSelectedChoices().add( 1052 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1053 } 1054 for (Iterator<?> k = requestEl.elementIterator("required"); k.hasNext();) { 1055 Element choiceEl = (Element) k.next(); 1056 courseRequest.getRequiredChoices().add( 1057 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1058 } 1059 groups: for (Iterator<?> k = requestEl.elementIterator("group"); k.hasNext();) { 1060 Element groupEl = (Element) k.next(); 1061 long gid = Long.parseLong(groupEl.attributeValue("id")); 1062 String gname = groupEl.attributeValue("name", "g" + gid); 1063 Course course = courseTable.get(Long.valueOf(groupEl.attributeValue("course"))); 1064 for (RequestGroup g: course.getRequestGroups()) { 1065 if (g.getId() == gid) { 1066 courseRequest.addRequestGroup(g); 1067 continue groups; 1068 } 1069 } 1070 courseRequest.addRequestGroup(new RequestGroup(gid, gname, course)); 1071 } 1072 return courseRequest; 1073 } 1074 1075 /** 1076 * Load enrollment 1077 * @param enrollmentEl enrollment element (current, best, or initial) 1078 * @param request parent request 1079 * @return loaded enrollment 1080 */ 1081 protected Enrollment loadEnrollment(Element enrollmentEl, Request request) { 1082 if (request instanceof CourseRequest) { 1083 CourseRequest courseRequest = (CourseRequest) request; 1084 HashSet<Section> sections = new HashSet<Section>(); 1085 for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) { 1086 Element sectionEl = (Element) k.next(); 1087 Section section = courseRequest.getSection(Long.parseLong(sectionEl 1088 .attributeValue("id"))); 1089 sections.add(section); 1090 } 1091 Reservation reservation = null; 1092 if (enrollmentEl.attributeValue("reservation", null) != null) { 1093 long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation")); 1094 for (Course course: courseRequest.getCourses()) 1095 for (Reservation r: course.getOffering().getReservations()) 1096 if (r.getId() == reservationId) { reservation = r; break; } 1097 } 1098 if (!sections.isEmpty()) { 1099 if (enrollmentEl.attributeValue("course") != null) { 1100 Course course = courseRequest.getCourse(Long.valueOf(enrollmentEl.attributeValue("course"))); 1101 if (course != null) 1102 return courseRequest.createEnrollment(course, sections, reservation); 1103 } 1104 return courseRequest.createEnrollment(sections, reservation); 1105 } 1106 } else if (request instanceof FreeTimeRequest) { 1107 return ((FreeTimeRequest)request).createEnrollment(); 1108 } 1109 return null; 1110 } 1111 1112 protected void moveCriticalRequestsUp() { 1113 for (Student student: getModel().getStudents()) { 1114 int assigned = 0, critical = 0; 1115 for (Request r: student.getRequests()) { 1116 if (r instanceof CourseRequest) { 1117 if (r.getInitialAssignment() != null) assigned ++; 1118 if (r.getRequestPriority() != RequestPriority.Normal) critical ++; 1119 } 1120 } 1121 if ((getModel().getKeepInitialAssignments() && assigned > 0) || critical > 0) { 1122 Collections.sort(student.getRequests(), new Comparator<Request>() { 1123 @Override 1124 public int compare(Request r1, Request r2) { 1125 if (r1.isAlternative() != r2.isAlternative()) return r1.isAlternative() ? 1 : -1; 1126 if (getModel().getKeepInitialAssignments()) { 1127 boolean a1 = (r1 instanceof CourseRequest && r1.getInitialAssignment() != null); 1128 boolean a2 = (r2 instanceof CourseRequest && r2.getInitialAssignment() != null); 1129 if (a1 != a2) return a1 ? -1 : 1; 1130 } 1131 int c1 = r1.getRequestPriority().ordinal(); 1132 int c2 = r2.getRequestPriority().ordinal(); 1133 if (c1 != c2) return c1 < c2 ? -1 : 1; 1134 return r1.getPriority() < r2.getPriority() ? -1 : 1; 1135 } 1136 }); 1137 int p = 0; 1138 for (Request r: student.getRequests()) 1139 r.setPriority(p++); 1140 } 1141 } 1142 } 1143 1144}