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.AreaClassificationMajor; 025import org.cpsolver.studentsct.model.Choice; 026import org.cpsolver.studentsct.model.Config; 027import org.cpsolver.studentsct.model.Course; 028import org.cpsolver.studentsct.model.CourseRequest; 029import org.cpsolver.studentsct.model.Enrollment; 030import org.cpsolver.studentsct.model.FreeTimeRequest; 031import org.cpsolver.studentsct.model.Instructor; 032import org.cpsolver.studentsct.model.Offering; 033import org.cpsolver.studentsct.model.Request; 034import org.cpsolver.studentsct.model.Request.RequestPriority; 035import org.cpsolver.studentsct.model.Student.StudentPriority; 036import org.cpsolver.studentsct.model.RequestGroup; 037import org.cpsolver.studentsct.model.Section; 038import org.cpsolver.studentsct.model.Student; 039import org.cpsolver.studentsct.model.StudentGroup; 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 Tomáš Müller<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.logging.log4j.Logger sLogger = org.apache.logging.log4j.LogManager.getLogger(StudentSectioningXMLLoader.class); 140 141 private File iInputFile; 142 private File iTimetableFile = null; 143 private boolean iLoadBest = false; 144 private boolean iLoadInitial = false; 145 private boolean iLoadCurrent = false; 146 private boolean iLoadOfferings = true; 147 private boolean iLoadStudents = true; 148 private StudentFilter iStudentFilter = null; 149 private boolean iWaitlistCritical = false; 150 private boolean iMoveCriticalUp = false; 151 152 /** 153 * Constructor 154 * 155 * @param model 156 * student sectioning model 157 * @param assignment current assignment 158 */ 159 public StudentSectioningXMLLoader(StudentSectioningModel model, Assignment<Request, Enrollment> assignment) { 160 super(model, assignment); 161 iInputFile = new File(getModel().getProperties().getProperty("General.Input", 162 "." + File.separator + "solution.xml")); 163 if (getModel().getProperties().getProperty("General.InputTimetable") != null) 164 iTimetableFile = new File(getModel().getProperties().getProperty("General.InputTimetable")); 165 iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true); 166 iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true); 167 iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true); 168 iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true); 169 iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true); 170 iWaitlistCritical = getModel().getProperties().getPropertyBoolean("Xml.WaitlistCritical", false); 171 iMoveCriticalUp = getModel().getProperties().getPropertyBoolean("Xml.MoveCriticalUp", false); 172 if (getModel().getProperties().getProperty("Xml.StudentFilter") != null) { 173 try { 174 iStudentFilter = (StudentFilter) Class.forName( 175 getModel().getProperties().getProperty("Xml.StudentFilter")).getConstructor(new Class[] {}) 176 .newInstance(new Object[] {}); 177 } catch (Exception e) { 178 sLogger.error("Unable to create student filter, reason: " + e.getMessage(), e); 179 } 180 } 181 } 182 183 /** Set input file (e.g., if it is not set by General.Input property) 184 * @param inputFile input file 185 **/ 186 public void setInputFile(File inputFile) { 187 iInputFile = inputFile; 188 } 189 190 /** Set student filter 191 * @param filter student filter 192 **/ 193 public void setStudentFilter(StudentFilter filter) { 194 iStudentFilter = filter; 195 } 196 197 /** Set whether to load students 198 * @param loadStudents true if students are to be loaded 199 **/ 200 public void setLoadStudents(boolean loadStudents) { 201 iLoadStudents = loadStudents; 202 } 203 204 /** Set whether to load offerings 205 * @param loadOfferings true if instructional offerings are to be loaded 206 **/ 207 public void setLoadOfferings(boolean loadOfferings) { 208 iLoadOfferings = loadOfferings; 209 } 210 211 /** Create BitSet from a bit string */ 212 private static BitSet createBitSet(String bitString) { 213 BitSet ret = new BitSet(bitString.length()); 214 for (int i = 0; i < bitString.length(); i++) 215 if (bitString.charAt(i) == '1') 216 ret.set(i); 217 return ret; 218 } 219 220 /** Load the file */ 221 @Override 222 public void load() throws Exception { 223 sLogger.debug("Reading XML data from " + iInputFile); 224 225 Document document = (new SAXReader()).read(iInputFile); 226 Element root = document.getRootElement(); 227 228 load(root); 229 } 230 231 public void load(Document document) { 232 Element root = document.getRootElement(); 233 234 if (getModel() != null && root.element("travel-times") != null) 235 loadTravelTimes(root.element("travel-times"), getModel().getDistanceMetric()); 236 237 Progress.getInstance(getModel()).load(root, true); 238 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 239 240 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 241 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 242 243 if (root.element("offerings") != null) { 244 loadOfferings(root.element("offerings"), offeringTable, courseTable, null); 245 } 246 247 List<Enrollment> bestEnrollments = new ArrayList<Enrollment>(); 248 List<Enrollment> currentEnrollments = new ArrayList<Enrollment>(); 249 if (root.element("students") != null) { 250 loadStudents(root.element("students"), offeringTable, courseTable, bestEnrollments, currentEnrollments); 251 } 252 253 if (root.element("constraints") != null) 254 loadLinkedSections(root.element("constraints"), offeringTable); 255 256 if (!bestEnrollments.isEmpty()) assignBest(bestEnrollments); 257 if (!currentEnrollments.isEmpty()) assignCurrent(currentEnrollments); 258 259 if (iMoveCriticalUp) moveCriticalRequestsUp(); 260 261 boolean hasFixed = false; 262 for (Request r: getModel().variables()) { 263 if (r instanceof CourseRequest && ((CourseRequest)r).isFixed()) { 264 hasFixed = true; break; 265 } 266 } 267 if (hasFixed) 268 getModel().addGlobalConstraint(new FixedAssignments()); 269 } 270 271 /** 272 * Load data from the given XML root 273 * @param root document root 274 * @throws DocumentException 275 */ 276 protected void load(Element root) throws DocumentException { 277 sLogger.debug("Root element: " + root.getName()); 278 if (!"sectioning".equals(root.getName())) { 279 sLogger.error("Given XML file is not student sectioning problem."); 280 return; 281 } 282 283 if (iLoadOfferings && getModel().getDistanceConflict() != null && root.element("travel-times") != null) 284 loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric()); 285 286 Map<Long, Placement> timetable = null; 287 if (iTimetableFile != null) { 288 sLogger.info("Reading timetable from " + iTimetableFile + " ..."); 289 Document timetableDocument = (new SAXReader()).read(iTimetableFile); 290 Element timetableRoot = timetableDocument.getRootElement(); 291 if (!"timetable".equals(timetableRoot.getName())) { 292 sLogger.error("Given XML file is not course timetabling problem."); 293 return; 294 } 295 timetable = loadTimetable(timetableRoot); 296 } 297 298 Progress.getInstance(getModel()).load(root, true); 299 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 300 301 if (root.attributeValue("term") != null) 302 getModel().getProperties().setProperty("Data.Term", root.attributeValue("term")); 303 if (root.attributeValue("year") != null) 304 getModel().getProperties().setProperty("Data.Year", root.attributeValue("year")); 305 if (root.attributeValue("initiative") != null) 306 getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative")); 307 308 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 309 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 310 311 if (iLoadOfferings && root.element("offerings") != null) { 312 loadOfferings(root.element("offerings"), offeringTable, courseTable, timetable); 313 } else { 314 for (Offering offering : getModel().getOfferings()) { 315 offeringTable.put(new Long(offering.getId()), offering); 316 for (Course course : offering.getCourses()) { 317 courseTable.put(new Long(course.getId()), course); 318 } 319 } 320 } 321 322 List<Enrollment> bestEnrollments = new ArrayList<Enrollment>(); 323 List<Enrollment> currentEnrollments = new ArrayList<Enrollment>(); 324 if (iLoadStudents && root.element("students") != null) { 325 loadStudents(root.element("students"), offeringTable, courseTable, bestEnrollments, currentEnrollments); 326 } 327 328 if (iLoadOfferings && root.element("constraints") != null) 329 loadLinkedSections(root.element("constraints"), offeringTable); 330 331 if (!bestEnrollments.isEmpty()) assignBest(bestEnrollments); 332 if (!currentEnrollments.isEmpty()) assignCurrent(currentEnrollments); 333 334 if (iMoveCriticalUp) moveCriticalRequestsUp(); 335 336 sLogger.debug("Model successfully loaded."); 337 } 338 339 /** 340 * Load offerings 341 * @param offeringsEl offerings element 342 * @param offeringTable offering table 343 * @param courseTable course table 344 * @param timetable provided timetable (null if to be loaded from the given document) 345 */ 346 protected void loadOfferings(Element offeringsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, Map<Long, Placement> timetable) { 347 HashMap<Long, Config> configTable = new HashMap<Long, Config>(); 348 HashMap<Long, Subpart> subpartTable = new HashMap<Long, Subpart>(); 349 HashMap<Long, Section> sectionTable = new HashMap<Long, Section>(); 350 for (Iterator<?> i = offeringsEl.elementIterator("offering"); i.hasNext();) { 351 Element offeringEl = (Element) i.next(); 352 Offering offering = new Offering( 353 Long.parseLong(offeringEl.attributeValue("id")), 354 offeringEl.attributeValue("name", "O" + offeringEl.attributeValue("id"))); 355 offeringTable.put(new Long(offering.getId()), offering); 356 getModel().addOffering(offering); 357 358 for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) { 359 Element courseEl = (Element) j.next(); 360 Course course = loadCourse(courseEl, offering); 361 courseTable.put(new Long(course.getId()), course); 362 } 363 364 for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) { 365 Element configEl = (Element) j.next(); 366 Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable); 367 configTable.put(config.getId(), config); 368 } 369 370 for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) { 371 Element reservationEl = (Element)j.next(); 372 loadReservation(reservationEl, offering, configTable, sectionTable); 373 } 374 375 for (Iterator<?> j = offeringEl.elementIterator("restriction"); j.hasNext(); ) { 376 Element restrictionEl = (Element)j.next(); 377 loadRestriction(restrictionEl, offering, configTable, sectionTable); 378 } 379 } 380 } 381 382 /** 383 * Load course 384 * @param courseEl course element 385 * @param offering parent offering 386 * @return loaded course 387 */ 388 protected Course loadCourse(Element courseEl, Offering offering) { 389 Course course = new Course( 390 Long.parseLong(courseEl.attributeValue("id")), 391 courseEl.attributeValue("subjectArea", ""), 392 courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")), 393 offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")), 394 Integer.parseInt(courseEl.attributeValue("projected", "0"))); 395 course.setCredit(courseEl.attributeValue("credit")); 396 String credits = courseEl.attributeValue("credits"); 397 if (credits != null) 398 course.setCreditValue(Float.valueOf(credits)); 399 return course; 400 } 401 402 /** 403 * Load config 404 * @param configEl config element 405 * @param offering parent offering 406 * @param subpartTable subpart table (of the offering) 407 * @param sectionTable section table (of the offering) 408 * @param timetable provided timetable 409 * @return loaded config 410 */ 411 protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 412 Config config = new Config 413 (Long.parseLong(configEl.attributeValue("id")), 414 Integer.parseInt(configEl.attributeValue("limit", "-1")), 415 configEl.attributeValue("name", "G" + configEl.attributeValue("id")), 416 offering); 417 Element imEl = configEl.element("instructional-method"); 418 if (imEl != null) { 419 config.setInstructionalMethodId(Long.parseLong(imEl.attributeValue("id"))); 420 config.setInstructionalMethodName(imEl.attributeValue("name", "M" + imEl.attributeValue("id"))); 421 config.setInstructionalMethodReference(imEl.attributeValue("reference", config.getInstructionalMethodName())); 422 } 423 for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) { 424 Element subpartEl = (Element) k.next(); 425 Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable); 426 subpartTable.put(new Long(subpart.getId()), subpart); 427 } 428 return config; 429 } 430 431 /** 432 * Load subpart 433 * @param subpartEl supart element 434 * @param config parent config 435 * @param subpartTable subpart table (of the offering) 436 * @param sectionTable section table (of the offering) 437 * @param timetable provided timetable 438 * @return loaded subpart 439 */ 440 protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 441 Subpart parentSubpart = null; 442 if (subpartEl.attributeValue("parent") != null) 443 parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent"))); 444 Subpart subpart = new Subpart( 445 Long.parseLong(subpartEl.attributeValue("id")), 446 subpartEl.attributeValue("itype"), 447 subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")), 448 config, 449 parentSubpart); 450 subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false"))); 451 subpart.setCredit(subpartEl.attributeValue("credit")); 452 String credits = subpartEl.attributeValue("credits"); 453 if (credits != null) 454 subpart.setCreditValue(Float.valueOf(credits)); 455 456 for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) { 457 Element sectionEl = (Element) l.next(); 458 Section section = loadSection(sectionEl, subpart, sectionTable, timetable); 459 sectionTable.put(new Long(section.getId()), section); 460 } 461 462 return subpart; 463 } 464 465 /** 466 * Load section 467 * @param sectionEl section element 468 * @param subpart parent subpart 469 * @param sectionTable section table (of the offering) 470 * @param timetable provided timetable 471 * @return loaded section 472 */ 473 @SuppressWarnings("deprecation") 474 protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 475 Section parentSection = null; 476 if (sectionEl.attributeValue("parent") != null) 477 parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent"))); 478 Placement placement = null; 479 if (timetable != null) { 480 placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id"))); 481 } else { 482 TimeLocation time = null; 483 Element timeEl = sectionEl.element("time"); 484 if (timeEl != null) { 485 time = new TimeLocation( 486 Integer.parseInt(timeEl.attributeValue("days"), 2), 487 Integer.parseInt(timeEl.attributeValue("start")), 488 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 489 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 490 timeEl.attributeValue("datePatternName", ""), 491 createBitSet(timeEl.attributeValue("dates")), 492 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 493 if (timeEl.attributeValue("pattern") != null) 494 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 495 } 496 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 497 for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) { 498 Element roomEl = (Element) m.next(); 499 Double posX = null, posY = null; 500 if (roomEl.attributeValue("location") != null) { 501 String loc = roomEl.attributeValue("location"); 502 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 503 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 504 } 505 RoomLocation room = new RoomLocation( 506 Long.valueOf(roomEl.attributeValue("id")), 507 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 508 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 509 0, Integer.parseInt(roomEl.attributeValue("capacity")), 510 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 511 rooms.add(room); 512 } 513 placement = (time == null ? null : new Placement(null, time, rooms)); 514 } 515 516 List<Instructor> instructors = new ArrayList<Instructor>(); 517 for (Iterator<?> m = sectionEl.elementIterator("instructor"); m.hasNext(); ) { 518 Element instructorEl = (Element)m.next(); 519 instructors.add(new Instructor(Long.parseLong(instructorEl.attributeValue("id")), instructorEl.attributeValue("externalId"), instructorEl.attributeValue("name"), instructorEl.attributeValue("email"))); 520 } 521 if (instructors.isEmpty() && sectionEl.attributeValue("instructorIds") != null) 522 instructors = Instructor.toInstructors(sectionEl.attributeValue("instructorIds"), sectionEl.attributeValue("instructorNames")); 523 Section section = new Section( 524 Long.parseLong(sectionEl.attributeValue("id")), 525 Integer.parseInt(sectionEl.attributeValue("limit")), 526 sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")), 527 subpart, placement, instructors, parentSection); 528 529 section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0"))); 530 section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0"))); 531 section.setCancelled("true".equalsIgnoreCase(sectionEl.attributeValue("cancelled", "false"))); 532 section.setEnabled("true".equalsIgnoreCase(sectionEl.attributeValue("enabled", "true"))); 533 section.setOnline("true".equalsIgnoreCase(sectionEl.attributeValue("online", "false"))); 534 section.setPast("true".equalsIgnoreCase(sectionEl.attributeValue("past", "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> acadAreas = new ArrayList<String>(); 589 for (Iterator<?> k = reservationEl.elementIterator("area"); k.hasNext(); ) { 590 Element areaEl = (Element)k.next(); 591 acadAreas.add(areaEl.attributeValue("code")); 592 } 593 if (acadAreas.isEmpty() && reservationEl.attributeValue("area") != null) 594 acadAreas.add(reservationEl.attributeValue("area")); 595 List<String> classifications = new ArrayList<String>(); 596 for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) { 597 Element clasfEl = (Element)k.next(); 598 classifications.add(clasfEl.attributeValue("code")); 599 } 600 List<String> majors = new ArrayList<String>(); 601 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 602 Element majorEl = (Element)k.next(); 603 majors.add(majorEl.attributeValue("code")); 604 } 605 List<String> minors = new ArrayList<String>(); 606 for (Iterator<?> k = reservationEl.elementIterator("minor"); k.hasNext(); ) { 607 Element minorEl = (Element)k.next(); 608 minors.add(minorEl.attributeValue("code")); 609 } 610 if ("curriculum".equals(reservationEl.attributeValue("type"))) 611 r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")), 612 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 613 offering, 614 acadAreas, classifications, majors, minors); 615 else 616 r = new CurriculumOverride(Long.valueOf(reservationEl.attributeValue("id")), 617 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 618 offering, 619 acadAreas, classifications, majors, minors); 620 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 621 Element majorEl = (Element)k.next(); 622 for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) { 623 Element concentrationEl = (Element)l.next(); 624 ((CurriculumReservation)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code")); 625 } 626 } 627 } else if ("course".equals(reservationEl.attributeValue("type"))) { 628 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 629 for (Course course: offering.getCourses()) { 630 if (course.getId() == courseId) 631 r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course); 632 } 633 } else if ("dummy".equals(reservationEl.attributeValue("type"))) { 634 r = new DummyReservation(offering); 635 } else if ("override".equals(reservationEl.attributeValue("type"))) { 636 Set<Long> studentIds = new HashSet<Long>(); 637 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 638 Element studentEl = (Element)k.next(); 639 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 640 } 641 r = new ReservationOverride(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 642 } 643 if (r == null) { 644 sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type")); 645 return null; 646 } 647 r.setExpired("true".equals(reservationEl.attributeValue("expired", "false"))); 648 for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) { 649 Element configEl = (Element)k.next(); 650 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 651 } 652 for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) { 653 Element sectionEl = (Element)k.next(); 654 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))), false); 655 } 656 r.setPriority(Integer.parseInt(reservationEl.attributeValue("priority", String.valueOf(r.getPriority())))); 657 r.setMustBeUsed("true".equals(reservationEl.attributeValue("mustBeUsed", r.mustBeUsed() ? "true" : "false"))); 658 r.setAllowOverlap("true".equals(reservationEl.attributeValue("allowOverlap", r.isAllowOverlap() ? "true" : "false"))); 659 r.setCanAssignOverLimit("true".equals(reservationEl.attributeValue("canAssignOverLimit", r.canAssignOverLimit() ? "true" : "false"))); 660 r.setAllowDisabled("true".equals(reservationEl.attributeValue("allowDisabled", r.isAllowDisabled() ? "true" : "false"))); 661 r.setNeverIncluded("true".equals(reservationEl.attributeValue("neverIncluded", "false"))); 662 r.setBreakLinkedSections("true".equals(reservationEl.attributeValue("breakLinkedSections", "false"))); 663 return r; 664 } 665 666 /** 667 * Load restriction 668 * @param restrictionEl restriction element 669 * @param offering parent offering 670 * @param configTable config table (of the offering) 671 * @param sectionTable section table (of the offering) 672 * @return loaded restriction 673 */ 674 protected Restriction loadRestriction(Element restrictionEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 675 Restriction r = null; 676 if ("individual".equals(restrictionEl.attributeValue("type"))) { 677 Set<Long> studentIds = new HashSet<Long>(); 678 for (Iterator<?> k = restrictionEl.elementIterator("student"); k.hasNext(); ) { 679 Element studentEl = (Element)k.next(); 680 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 681 } 682 r = new IndividualRestriction(Long.valueOf(restrictionEl.attributeValue("id")), offering, studentIds); 683 } else if ("curriculum".equals(restrictionEl.attributeValue("type"))) { 684 List<String> acadAreas = new ArrayList<String>(); 685 for (Iterator<?> k = restrictionEl.elementIterator("area"); k.hasNext(); ) { 686 Element areaEl = (Element)k.next(); 687 acadAreas.add(areaEl.attributeValue("code")); 688 } 689 if (acadAreas.isEmpty() && restrictionEl.attributeValue("area") != null) 690 acadAreas.add(restrictionEl.attributeValue("area")); 691 List<String> classifications = new ArrayList<String>(); 692 for (Iterator<?> k = restrictionEl.elementIterator("classification"); k.hasNext(); ) { 693 Element clasfEl = (Element)k.next(); 694 classifications.add(clasfEl.attributeValue("code")); 695 } 696 List<String> majors = new ArrayList<String>(); 697 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 698 Element majorEl = (Element)k.next(); 699 majors.add(majorEl.attributeValue("code")); 700 } 701 List<String> minors = new ArrayList<String>(); 702 for (Iterator<?> k = restrictionEl.elementIterator("minor"); k.hasNext(); ) { 703 Element minorEl = (Element)k.next(); 704 minors.add(minorEl.attributeValue("code")); 705 } 706 r = new CurriculumRestriction(Long.valueOf(restrictionEl.attributeValue("id")), 707 offering, 708 acadAreas, classifications, majors, minors); 709 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 710 Element majorEl = (Element)k.next(); 711 for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) { 712 Element concentrationEl = (Element)l.next(); 713 ((CurriculumRestriction)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code")); 714 } 715 } 716 } else if ("course".equals(restrictionEl.attributeValue("type"))) { 717 long courseId = Long.parseLong(restrictionEl.attributeValue("course")); 718 for (Course course: offering.getCourses()) { 719 if (course.getId() == courseId) 720 r = new CourseRestriction(Long.valueOf(restrictionEl.attributeValue("id")), course); 721 } 722 } 723 if (r == null) { 724 sLogger.error("Unknown reservation type "+ restrictionEl.attributeValue("type")); 725 return null; 726 } 727 for (Iterator<?> k = restrictionEl.elementIterator("config"); k.hasNext(); ) { 728 Element configEl = (Element)k.next(); 729 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 730 } 731 for (Iterator<?> k = restrictionEl.elementIterator("section"); k.hasNext(); ) { 732 Element sectionEl = (Element)k.next(); 733 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id")))); 734 } 735 return r; 736 } 737 738 /** 739 * Load given timetable 740 * @param timetableRoot document root in the course timetabling XML format 741 * @return loaded timetable (map class id: assigned placement) 742 */ 743 protected Map<Long, Placement> loadTimetable(Element timetableRoot) { 744 Map<Long, Placement> timetable = new HashMap<Long, Placement>(); 745 HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>(); 746 for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) { 747 Element roomEl = (Element)i.next(); 748 Long roomId = Long.valueOf(roomEl.attributeValue("id")); 749 Double posX = null, posY = null; 750 if (roomEl.attributeValue("location") != null) { 751 String loc = roomEl.attributeValue("location"); 752 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 753 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 754 } 755 RoomLocation room = new RoomLocation( 756 Long.valueOf(roomEl.attributeValue("id")), 757 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 758 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 759 0, Integer.parseInt(roomEl.attributeValue("capacity")), 760 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 761 rooms.put(roomId, room); 762 } 763 for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) { 764 Element classEl = (Element)i.next(); 765 Long classId = Long.valueOf(classEl.attributeValue("id")); 766 TimeLocation time = null; 767 Element timeEl = null; 768 for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) { 769 Element e = (Element)j.next(); 770 if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; } 771 } 772 if (timeEl != null) { 773 time = new TimeLocation( 774 Integer.parseInt(timeEl.attributeValue("days"), 2), 775 Integer.parseInt(timeEl.attributeValue("start")), 776 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 777 classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")), 778 classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")), 779 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 780 if (timeEl.attributeValue("pattern") != null) 781 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 782 } 783 List<RoomLocation> room = new ArrayList<RoomLocation>(); 784 for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) { 785 Element roomEl = (Element) j.next(); 786 if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue; 787 room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id")))); 788 } 789 Placement placement = (time == null ? null : new Placement(null, time, room)); 790 if (placement != null) 791 timetable.put(classId, placement); 792 } 793 return timetable; 794 } 795 796 /** 797 * Load travel times 798 * @param travelTimesEl travel-time element 799 * @param metric distance metric to be populated 800 */ 801 protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) { 802 for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) { 803 Element travelTimeEl = (Element)i.next(); 804 metric.addTravelTime( 805 Long.valueOf(travelTimeEl.attributeValue("id1")), 806 Long.valueOf(travelTimeEl.attributeValue("id2")), 807 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 808 } 809 } 810 811 /** 812 * Load linked sections 813 * @param constraintsEl constraints element 814 * @param offeringTable offering table 815 */ 816 protected void loadLinkedSections(Element constraintsEl, Map<Long, Offering> offeringTable) { 817 for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) { 818 Element linkedEl = (Element) i.next(); 819 List<Section> sections = new ArrayList<Section>(); 820 for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) { 821 Element sectionEl = (Element) j.next(); 822 Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering"))); 823 sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id")))); 824 } 825 getModel().addLinkedSections("true".equals(linkedEl.attributeValue("mustBeUsed", "false")), sections); 826 } 827 } 828 829 /** 830 * Load students 831 * @param studentsEl students element 832 * @param offeringTable offering table 833 * @param courseTable course table 834 */ 835 protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, List<Enrollment> bestEnrollments, List<Enrollment> currentEnrollments) { 836 for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) { 837 Element studentEl = (Element) i.next(); 838 Student student = loadStudent(studentEl, offeringTable); 839 if (iStudentFilter != null && !iStudentFilter.accept(student)) 840 continue; 841 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 842 Element requestEl = (Element) j.next(); 843 Request request = loadRequest(requestEl, student, offeringTable, courseTable); 844 if (request == null) continue; 845 846 Element initialEl = requestEl.element("initial"); 847 if (iLoadInitial && initialEl != null) { 848 Enrollment enrollment = loadEnrollment(initialEl, request); 849 if (enrollment != null) 850 request.setInitialAssignment(enrollment); 851 } 852 Element currentEl = requestEl.element("current"); 853 if (iLoadCurrent && currentEl != null) { 854 Enrollment enrollment = loadEnrollment(currentEl, request); 855 if (enrollment != null) 856 currentEnrollments.add(enrollment); 857 } 858 Element bestEl = requestEl.element("best"); 859 if (iLoadBest && bestEl != null) { 860 Enrollment enrollment = loadEnrollment(bestEl, request); 861 if (enrollment != null) 862 bestEnrollments.add(enrollment); 863 } 864 Element fixedEl = requestEl.element("fixed"); 865 if (fixedEl != null && request instanceof CourseRequest) 866 ((CourseRequest)request).setFixedValue(loadEnrollment(fixedEl, request)); 867 } 868 getModel().addStudent(student); 869 } 870 } 871 872 /** 873 * Save best enrollments 874 * @param bestEnrollments best enrollments 875 */ 876 protected void assignBest(List<Enrollment> bestEnrollments) { 877 // Enrollments with a reservation must go first, enrollments with an override go last 878 for (Enrollment enrollment : bestEnrollments) { 879 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 880 if (!enrollment.getStudent().isAvailable(enrollment)) { 881 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 882 continue; 883 } 884 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 885 if (conflicts.isEmpty()) 886 getAssignment().assign(0, enrollment); 887 else 888 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 889 } 890 for (Enrollment enrollment : bestEnrollments) { 891 if (enrollment.getReservation() != null) 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 : bestEnrollments) { 903 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) 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 getModel().saveBest(getAssignment()); 915 } 916 917 /** 918 * Assign current enrollments 919 * @param currentEnrollments current enrollments 920 */ 921 protected void assignCurrent(List<Enrollment> currentEnrollments) { 922 for (Request request : getModel().variables()) 923 getAssignment().unassign(0, request); 924 // Enrollments with a reservation must go first, enrollments with an override go last 925 for (Enrollment enrollment : currentEnrollments) { 926 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 927 if (!enrollment.getStudent().isAvailable(enrollment)) { 928 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 929 continue; 930 } 931 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 932 if (conflicts.isEmpty()) 933 getAssignment().assign(0, enrollment); 934 else 935 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 936 } 937 for (Enrollment enrollment : currentEnrollments) { 938 if (enrollment.getReservation() != null) continue; 939 if (!enrollment.getStudent().isAvailable(enrollment)) { 940 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 941 continue; 942 } 943 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 944 if (conflicts.isEmpty()) 945 getAssignment().assign(0, enrollment); 946 else 947 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 948 } 949 for (Enrollment enrollment : currentEnrollments) { 950 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue; 951 if (!enrollment.getStudent().isAvailable(enrollment)) { 952 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 953 continue; 954 } 955 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 956 if (conflicts.isEmpty()) 957 getAssignment().assign(0, enrollment); 958 else 959 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 960 } 961 } 962 963 /** 964 * Load student 965 * @param studentEl student element 966 * @param offeringTable offering table 967 * @return loaded student 968 */ 969 protected Student loadStudent(Element studentEl, Map<Long, Offering> offeringTable) { 970 Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy"))); 971 if (studentEl.attributeValue("priority") != null) 972 student.setPriority(StudentPriority.getPriority(studentEl.attributeValue("priority"))); 973 if ("true".equals(studentEl.attributeValue("shortDistances"))) 974 student.setNeedShortDistances(true); 975 if ("true".equals(studentEl.attributeValue("allowDisabled"))) 976 student.setAllowDisabled(true); 977 student.setExternalId(studentEl.attributeValue("externalId")); 978 student.setName(studentEl.attributeValue("name")); 979 student.setStatus(studentEl.attributeValue("status")); 980 String minCredit = studentEl.attributeValue("minCredit"); 981 if (minCredit != null) 982 student.setMinCredit(Float.parseFloat(minCredit)); 983 String maxCredit = studentEl.attributeValue("maxCredit"); 984 if (maxCredit != null) 985 student.setMaxCredit(Float.parseFloat(maxCredit)); 986 List<String[]> clasf = new ArrayList<String[]>(); 987 List<String[]> major = new ArrayList<String[]>(); 988 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 989 Element requestEl = (Element) j.next(); 990 if ("classification".equals(requestEl.getName())) { 991 clasf.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")}); 992 } else if ("major".equals(requestEl.getName())) { 993 major.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")}); 994 } else if ("minor".equals(requestEl.getName())) { 995 if ("A".equals(requestEl.attributeValue("area"))) 996 student.getAccommodations().add(requestEl.attributeValue("code")); 997 else 998 student.getGroups().add(new StudentGroup(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label"))); 999 } else if ("unavailability".equals(requestEl.getName())) { 1000 Offering offering = offeringTable.get(Long.parseLong(requestEl.attributeValue("offering"))); 1001 Section section = (offering == null ? null : offering.getSection(Long.parseLong(requestEl.attributeValue("section")))); 1002 if (section != null) 1003 new Unavailability(student, section, "true".equals(requestEl.attributeValue("allowOverlap"))); 1004 } else if ("acm".equals(requestEl.getName())) { 1005 if (requestEl.attributeValue("minor") != null) 1006 student.getAreaClassificationMinors().add(new AreaClassificationMajor( 1007 requestEl.attributeValue("area"), requestEl.attributeValue("areaName"), 1008 requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"), 1009 requestEl.attributeValue("minor"), requestEl.attributeValue("minorName"), 1010 requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"), 1011 requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"), 1012 requestEl.attributeValue("program"), requestEl.attributeValue("programName"), 1013 requestEl.attributeValue("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight")))); 1014 else 1015 student.getAreaClassificationMajors().add(new AreaClassificationMajor( 1016 requestEl.attributeValue("area"), requestEl.attributeValue("areaName"), 1017 requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"), 1018 requestEl.attributeValue("major"), requestEl.attributeValue("majorName"), 1019 requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"), 1020 requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"), 1021 requestEl.attributeValue("program"), requestEl.attributeValue("programName"), 1022 requestEl.attributeValue("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight")))); 1023 } else if ("group".equals(requestEl.getName())) { 1024 student.getGroups().add(new StudentGroup(requestEl.attributeValue("type"), requestEl.attributeValue("reference"), requestEl.attributeValue("name"))); 1025 } else if ("accommodation".equals(requestEl.getName())) { 1026 student.getAccommodations().add(requestEl.attributeValue("reference")); 1027 } else if ("advisor".equals(requestEl.getName())) { 1028 student.getAdvisors().add(new Instructor(0l, requestEl.attributeValue("externalId"), requestEl.attributeValue("name"), requestEl.attributeValue("email"))); 1029 } 1030 } 1031 for (int i = 0; i < Math.min(clasf.size(), major.size()); i++) { 1032 student.getAreaClassificationMajors().add(new AreaClassificationMajor(clasf.get(i)[0],clasf.get(i)[1],major.get(i)[1])); 1033 } 1034 return student; 1035 } 1036 1037 /** 1038 * Load request 1039 * @param requestEl request element 1040 * @param student parent student 1041 * @param offeringTable offering table 1042 * @param courseTable course table 1043 * @return loaded request 1044 */ 1045 protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1046 if ("freeTime".equals(requestEl.getName())) { 1047 return loadFreeTime(requestEl, student); 1048 } else if ("course".equals(requestEl.getName())) { 1049 return loadCourseRequest(requestEl, student, offeringTable, courseTable); 1050 } else { 1051 return null; 1052 } 1053 } 1054 1055 /** 1056 * Load free time request 1057 * @param requestEl request element 1058 * @param student parent student 1059 * @return loaded free time request 1060 */ 1061 public FreeTimeRequest loadFreeTime(Element requestEl, Student student) { 1062 TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2), 1063 Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl 1064 .attributeValue("length")), 0, 0, 1065 requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl 1066 .attributeValue("datePattern")), "", createBitSet(requestEl 1067 .attributeValue("dates")), 0); 1068 FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")), 1069 Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl 1070 .attributeValue("alternative")), student, time); 1071 if (requestEl.attributeValue("weight") != null) 1072 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1073 return request; 1074 } 1075 1076 /** 1077 * Load course request 1078 * @param requestEl request element 1079 * @param student parent student 1080 * @param offeringTable offering table 1081 * @param courseTable course table 1082 * @return loaded course request 1083 */ 1084 public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1085 List<Course> courses = new ArrayList<Course>(); 1086 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 1087 for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();) 1088 courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course")))); 1089 Long timeStamp = null; 1090 if (requestEl.attributeValue("timeStamp") != null) 1091 timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp")); 1092 CourseRequest courseRequest = new CourseRequest( 1093 Long.parseLong(requestEl.attributeValue("id")), 1094 Integer.parseInt(requestEl.attributeValue("priority")), 1095 "true".equals(requestEl.attributeValue("alternative")), 1096 student, courses, 1097 "true".equals(requestEl.attributeValue("waitlist", "false")), 1098 RequestPriority.valueOf(requestEl.attributeValue("importance", 1099 "true".equals(requestEl.attributeValue("critical", "false")) ? RequestPriority.Critical.name() : RequestPriority.Normal.name())), 1100 timeStamp); 1101 if (iWaitlistCritical && RequestPriority.Critical.isCritical(courseRequest) && !courseRequest.isAlternative()) courseRequest.setWaitlist(true); 1102 if (requestEl.attributeValue("weight") != null) 1103 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1104 for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) { 1105 Element choiceEl = (Element) k.next(); 1106 courseRequest.getWaitlistedChoices().add( 1107 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1108 } 1109 for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) { 1110 Element choiceEl = (Element) k.next(); 1111 courseRequest.getSelectedChoices().add( 1112 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1113 } 1114 for (Iterator<?> k = requestEl.elementIterator("required"); k.hasNext();) { 1115 Element choiceEl = (Element) k.next(); 1116 courseRequest.getRequiredChoices().add( 1117 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1118 } 1119 groups: for (Iterator<?> k = requestEl.elementIterator("group"); k.hasNext();) { 1120 Element groupEl = (Element) k.next(); 1121 long gid = Long.parseLong(groupEl.attributeValue("id")); 1122 String gname = groupEl.attributeValue("name", "g" + gid); 1123 Course course = courseTable.get(Long.valueOf(groupEl.attributeValue("course"))); 1124 for (RequestGroup g: course.getRequestGroups()) { 1125 if (g.getId() == gid) { 1126 courseRequest.addRequestGroup(g); 1127 continue groups; 1128 } 1129 } 1130 courseRequest.addRequestGroup(new RequestGroup(gid, gname, course)); 1131 } 1132 return courseRequest; 1133 } 1134 1135 /** 1136 * Load enrollment 1137 * @param enrollmentEl enrollment element (current, best, or initial) 1138 * @param request parent request 1139 * @return loaded enrollment 1140 */ 1141 protected Enrollment loadEnrollment(Element enrollmentEl, Request request) { 1142 if (request instanceof CourseRequest) { 1143 CourseRequest courseRequest = (CourseRequest) request; 1144 HashSet<Section> sections = new HashSet<Section>(); 1145 for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) { 1146 Element sectionEl = (Element) k.next(); 1147 Section section = courseRequest.getSection(Long.parseLong(sectionEl 1148 .attributeValue("id"))); 1149 sections.add(section); 1150 } 1151 Reservation reservation = null; 1152 if (enrollmentEl.attributeValue("reservation", null) != null) { 1153 long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation")); 1154 for (Course course: courseRequest.getCourses()) 1155 for (Reservation r: course.getOffering().getReservations()) 1156 if (r.getId() == reservationId) { reservation = r; break; } 1157 } 1158 if (!sections.isEmpty()) { 1159 if (enrollmentEl.attributeValue("course") != null) { 1160 Course course = courseRequest.getCourse(Long.valueOf(enrollmentEl.attributeValue("course"))); 1161 if (course != null) 1162 return courseRequest.createEnrollment(course, sections, reservation); 1163 } 1164 return courseRequest.createEnrollment(sections, reservation); 1165 } 1166 } else if (request instanceof FreeTimeRequest) { 1167 return ((FreeTimeRequest)request).createEnrollment(); 1168 } 1169 return null; 1170 } 1171 1172 protected void moveCriticalRequestsUp() { 1173 for (Student student: getModel().getStudents()) { 1174 int assigned = 0, critical = 0; 1175 for (Request r: student.getRequests()) { 1176 if (r instanceof CourseRequest) { 1177 if (r.getInitialAssignment() != null) assigned ++; 1178 if (r.getRequestPriority() != RequestPriority.Normal) critical ++; 1179 } 1180 } 1181 if ((getModel().getKeepInitialAssignments() && assigned > 0) || critical > 0) { 1182 Collections.sort(student.getRequests(), new Comparator<Request>() { 1183 @Override 1184 public int compare(Request r1, Request r2) { 1185 if (r1.isAlternative() != r2.isAlternative()) return r1.isAlternative() ? 1 : -1; 1186 if (getModel().getKeepInitialAssignments()) { 1187 boolean a1 = (r1 instanceof CourseRequest && r1.getInitialAssignment() != null); 1188 boolean a2 = (r2 instanceof CourseRequest && r2.getInitialAssignment() != null); 1189 if (a1 != a2) return a1 ? -1 : 1; 1190 } 1191 int c1 = r1.getRequestPriority().ordinal(); 1192 int c2 = r2.getRequestPriority().ordinal(); 1193 if (c1 != c2) return c1 < c2 ? -1 : 1; 1194 return r1.getPriority() < r2.getPriority() ? -1 : 1; 1195 } 1196 }); 1197 int p = 0; 1198 for (Request r: student.getRequests()) 1199 r.setPriority(p++); 1200 } 1201 } 1202 } 1203 1204}