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.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 section.setPast("true".equalsIgnoreCase(sectionEl.attributeValue("past", "false"))); 536 for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) { 537 Element cNameEl = (Element)m.next(); 538 section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText()); 539 } 540 Element ignoreEl = sectionEl.element("no-conflicts"); 541 if (ignoreEl != null) { 542 for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); ) 543 section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id"))); 544 } 545 546 return section; 547 } 548 549 /** 550 * Load reservation 551 * @param reservationEl reservation element 552 * @param offering parent offering 553 * @param configTable config table (of the offering) 554 * @param sectionTable section table (of the offering) 555 * @return loaded reservation 556 */ 557 protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 558 Reservation r = null; 559 if ("individual".equals(reservationEl.attributeValue("type"))) { 560 Set<Long> studentIds = new HashSet<Long>(); 561 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 562 Element studentEl = (Element)k.next(); 563 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 564 } 565 r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 566 } else if ("group".equals(reservationEl.attributeValue("type"))) { 567 Set<Long> studentIds = new HashSet<Long>(); 568 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 569 Element studentEl = (Element)k.next(); 570 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 571 } 572 r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")), 573 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 574 offering, studentIds); 575 } else if ("lc".equals(reservationEl.attributeValue("type"))) { 576 Set<Long> studentIds = new HashSet<Long>(); 577 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 578 Element studentEl = (Element)k.next(); 579 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 580 } 581 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 582 for (Course course: offering.getCourses()) { 583 if (course.getId() == courseId) 584 r = new LearningCommunityReservation(Long.valueOf(reservationEl.attributeValue("id")), 585 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 586 course, studentIds); 587 } 588 } else if ("curriculum".equals(reservationEl.attributeValue("type")) || "curriculum-override".equals(reservationEl.attributeValue("type"))) { 589 List<String> acadAreas = new ArrayList<String>(); 590 for (Iterator<?> k = reservationEl.elementIterator("area"); k.hasNext(); ) { 591 Element areaEl = (Element)k.next(); 592 acadAreas.add(areaEl.attributeValue("code")); 593 } 594 if (acadAreas.isEmpty() && reservationEl.attributeValue("area") != null) 595 acadAreas.add(reservationEl.attributeValue("area")); 596 List<String> classifications = new ArrayList<String>(); 597 for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) { 598 Element clasfEl = (Element)k.next(); 599 classifications.add(clasfEl.attributeValue("code")); 600 } 601 List<String> majors = new ArrayList<String>(); 602 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 603 Element majorEl = (Element)k.next(); 604 majors.add(majorEl.attributeValue("code")); 605 } 606 List<String> minors = new ArrayList<String>(); 607 for (Iterator<?> k = reservationEl.elementIterator("minor"); k.hasNext(); ) { 608 Element minorEl = (Element)k.next(); 609 minors.add(minorEl.attributeValue("code")); 610 } 611 if ("curriculum".equals(reservationEl.attributeValue("type"))) 612 r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")), 613 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 614 offering, 615 acadAreas, classifications, majors, minors); 616 else 617 r = new CurriculumOverride(Long.valueOf(reservationEl.attributeValue("id")), 618 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 619 offering, 620 acadAreas, classifications, majors, minors); 621 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 622 Element majorEl = (Element)k.next(); 623 for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) { 624 Element concentrationEl = (Element)l.next(); 625 ((CurriculumReservation)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code")); 626 } 627 } 628 } else if ("course".equals(reservationEl.attributeValue("type"))) { 629 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 630 for (Course course: offering.getCourses()) { 631 if (course.getId() == courseId) 632 r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course); 633 } 634 } else if ("dummy".equals(reservationEl.attributeValue("type"))) { 635 r = new DummyReservation(offering); 636 } else if ("override".equals(reservationEl.attributeValue("type"))) { 637 Set<Long> studentIds = new HashSet<Long>(); 638 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 639 Element studentEl = (Element)k.next(); 640 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 641 } 642 r = new ReservationOverride(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 643 } 644 if (r == null) { 645 sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type")); 646 return null; 647 } 648 r.setExpired("true".equals(reservationEl.attributeValue("expired", "false"))); 649 for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) { 650 Element configEl = (Element)k.next(); 651 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 652 } 653 for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) { 654 Element sectionEl = (Element)k.next(); 655 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))), false); 656 } 657 r.setPriority(Integer.parseInt(reservationEl.attributeValue("priority", String.valueOf(r.getPriority())))); 658 r.setMustBeUsed("true".equals(reservationEl.attributeValue("mustBeUsed", r.mustBeUsed() ? "true" : "false"))); 659 r.setAllowOverlap("true".equals(reservationEl.attributeValue("allowOverlap", r.isAllowOverlap() ? "true" : "false"))); 660 r.setCanAssignOverLimit("true".equals(reservationEl.attributeValue("canAssignOverLimit", r.canAssignOverLimit() ? "true" : "false"))); 661 r.setAllowDisabled("true".equals(reservationEl.attributeValue("allowDisabled", r.isAllowDisabled() ? "true" : "false"))); 662 r.setNeverIncluded("true".equals(reservationEl.attributeValue("neverIncluded", "false"))); 663 r.setBreakLinkedSections("true".equals(reservationEl.attributeValue("breakLinkedSections", "false"))); 664 return r; 665 } 666 667 /** 668 * Load restriction 669 * @param restrictionEl restriction element 670 * @param offering parent offering 671 * @param configTable config table (of the offering) 672 * @param sectionTable section table (of the offering) 673 * @return loaded restriction 674 */ 675 protected Restriction loadRestriction(Element restrictionEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 676 Restriction r = null; 677 if ("individual".equals(restrictionEl.attributeValue("type"))) { 678 Set<Long> studentIds = new HashSet<Long>(); 679 for (Iterator<?> k = restrictionEl.elementIterator("student"); k.hasNext(); ) { 680 Element studentEl = (Element)k.next(); 681 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 682 } 683 r = new IndividualRestriction(Long.valueOf(restrictionEl.attributeValue("id")), offering, studentIds); 684 } else if ("curriculum".equals(restrictionEl.attributeValue("type"))) { 685 List<String> acadAreas = new ArrayList<String>(); 686 for (Iterator<?> k = restrictionEl.elementIterator("area"); k.hasNext(); ) { 687 Element areaEl = (Element)k.next(); 688 acadAreas.add(areaEl.attributeValue("code")); 689 } 690 if (acadAreas.isEmpty() && restrictionEl.attributeValue("area") != null) 691 acadAreas.add(restrictionEl.attributeValue("area")); 692 List<String> classifications = new ArrayList<String>(); 693 for (Iterator<?> k = restrictionEl.elementIterator("classification"); k.hasNext(); ) { 694 Element clasfEl = (Element)k.next(); 695 classifications.add(clasfEl.attributeValue("code")); 696 } 697 List<String> majors = new ArrayList<String>(); 698 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 699 Element majorEl = (Element)k.next(); 700 majors.add(majorEl.attributeValue("code")); 701 } 702 List<String> minors = new ArrayList<String>(); 703 for (Iterator<?> k = restrictionEl.elementIterator("minor"); k.hasNext(); ) { 704 Element minorEl = (Element)k.next(); 705 minors.add(minorEl.attributeValue("code")); 706 } 707 r = new CurriculumRestriction(Long.valueOf(restrictionEl.attributeValue("id")), 708 offering, 709 acadAreas, classifications, majors, minors); 710 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 711 Element majorEl = (Element)k.next(); 712 for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) { 713 Element concentrationEl = (Element)l.next(); 714 ((CurriculumRestriction)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code")); 715 } 716 } 717 } else if ("course".equals(restrictionEl.attributeValue("type"))) { 718 long courseId = Long.parseLong(restrictionEl.attributeValue("course")); 719 for (Course course: offering.getCourses()) { 720 if (course.getId() == courseId) 721 r = new CourseRestriction(Long.valueOf(restrictionEl.attributeValue("id")), course); 722 } 723 } 724 if (r == null) { 725 sLogger.error("Unknown reservation type "+ restrictionEl.attributeValue("type")); 726 return null; 727 } 728 for (Iterator<?> k = restrictionEl.elementIterator("config"); k.hasNext(); ) { 729 Element configEl = (Element)k.next(); 730 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 731 } 732 for (Iterator<?> k = restrictionEl.elementIterator("section"); k.hasNext(); ) { 733 Element sectionEl = (Element)k.next(); 734 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id")))); 735 } 736 return r; 737 } 738 739 /** 740 * Load given timetable 741 * @param timetableRoot document root in the course timetabling XML format 742 * @return loaded timetable (map class id: assigned placement) 743 */ 744 protected Map<Long, Placement> loadTimetable(Element timetableRoot) { 745 Map<Long, Placement> timetable = new HashMap<Long, Placement>(); 746 HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>(); 747 for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) { 748 Element roomEl = (Element)i.next(); 749 Long roomId = Long.valueOf(roomEl.attributeValue("id")); 750 Double posX = null, posY = null; 751 if (roomEl.attributeValue("location") != null) { 752 String loc = roomEl.attributeValue("location"); 753 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 754 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 755 } 756 RoomLocation room = new RoomLocation( 757 Long.valueOf(roomEl.attributeValue("id")), 758 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 759 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 760 0, Integer.parseInt(roomEl.attributeValue("capacity")), 761 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 762 rooms.put(roomId, room); 763 } 764 for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) { 765 Element classEl = (Element)i.next(); 766 Long classId = Long.valueOf(classEl.attributeValue("id")); 767 TimeLocation time = null; 768 Element timeEl = null; 769 for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) { 770 Element e = (Element)j.next(); 771 if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; } 772 } 773 if (timeEl != null) { 774 time = new TimeLocation( 775 Integer.parseInt(timeEl.attributeValue("days"), 2), 776 Integer.parseInt(timeEl.attributeValue("start")), 777 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 778 classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")), 779 classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")), 780 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 781 if (timeEl.attributeValue("pattern") != null) 782 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 783 } 784 List<RoomLocation> room = new ArrayList<RoomLocation>(); 785 for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) { 786 Element roomEl = (Element) j.next(); 787 if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue; 788 room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id")))); 789 } 790 Placement placement = (time == null ? null : new Placement(null, time, room)); 791 if (placement != null) 792 timetable.put(classId, placement); 793 } 794 return timetable; 795 } 796 797 /** 798 * Load travel times 799 * @param travelTimesEl travel-time element 800 * @param metric distance metric to be populated 801 */ 802 protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) { 803 for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) { 804 Element travelTimeEl = (Element)i.next(); 805 metric.addTravelTime( 806 Long.valueOf(travelTimeEl.attributeValue("id1")), 807 Long.valueOf(travelTimeEl.attributeValue("id2")), 808 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 809 } 810 } 811 812 /** 813 * Load linked sections 814 * @param constraintsEl constraints element 815 * @param offeringTable offering table 816 */ 817 protected void loadLinkedSections(Element constraintsEl, Map<Long, Offering> offeringTable) { 818 for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) { 819 Element linkedEl = (Element) i.next(); 820 List<Section> sections = new ArrayList<Section>(); 821 for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) { 822 Element sectionEl = (Element) j.next(); 823 Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering"))); 824 sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id")))); 825 } 826 getModel().addLinkedSections("true".equals(linkedEl.attributeValue("mustBeUsed", "false")), sections); 827 } 828 } 829 830 /** 831 * Load students 832 * @param studentsEl students element 833 * @param offeringTable offering table 834 * @param courseTable course table 835 */ 836 protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, List<Enrollment> bestEnrollments, List<Enrollment> currentEnrollments) { 837 for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) { 838 Element studentEl = (Element) i.next(); 839 Student student = loadStudent(studentEl, offeringTable); 840 if (iStudentFilter != null && !iStudentFilter.accept(student)) 841 continue; 842 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 843 Element requestEl = (Element) j.next(); 844 Request request = loadRequest(requestEl, student, offeringTable, courseTable); 845 if (request == null) continue; 846 847 Element initialEl = requestEl.element("initial"); 848 if (iLoadInitial && initialEl != null) { 849 Enrollment enrollment = loadEnrollment(initialEl, request); 850 if (enrollment != null) 851 request.setInitialAssignment(enrollment); 852 } 853 Element currentEl = requestEl.element("current"); 854 if (iLoadCurrent && currentEl != null) { 855 Enrollment enrollment = loadEnrollment(currentEl, request); 856 if (enrollment != null) 857 currentEnrollments.add(enrollment); 858 } 859 Element bestEl = requestEl.element("best"); 860 if (iLoadBest && bestEl != null) { 861 Enrollment enrollment = loadEnrollment(bestEl, request); 862 if (enrollment != null) 863 bestEnrollments.add(enrollment); 864 } 865 Element fixedEl = requestEl.element("fixed"); 866 if (fixedEl != null && request instanceof CourseRequest) 867 ((CourseRequest)request).setFixedValue(loadEnrollment(fixedEl, request)); 868 } 869 getModel().addStudent(student); 870 } 871 } 872 873 /** 874 * Save best enrollments 875 * @param bestEnrollments best enrollments 876 */ 877 protected void assignBest(List<Enrollment> bestEnrollments) { 878 // Enrollments with a reservation must go first, enrollments with an override go last 879 for (Enrollment enrollment : bestEnrollments) { 880 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 881 if (!enrollment.getStudent().isAvailable(enrollment)) { 882 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 883 continue; 884 } 885 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 886 if (conflicts.isEmpty()) 887 getAssignment().assign(0, enrollment); 888 else 889 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 890 } 891 for (Enrollment enrollment : bestEnrollments) { 892 if (enrollment.getReservation() != null) continue; 893 if (!enrollment.getStudent().isAvailable(enrollment)) { 894 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 895 continue; 896 } 897 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 898 if (conflicts.isEmpty()) 899 getAssignment().assign(0, enrollment); 900 else 901 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 902 } 903 for (Enrollment enrollment : bestEnrollments) { 904 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue; 905 if (!enrollment.getStudent().isAvailable(enrollment)) { 906 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 907 continue; 908 } 909 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 910 if (conflicts.isEmpty()) 911 getAssignment().assign(0, enrollment); 912 else 913 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 914 } 915 getModel().saveBest(getAssignment()); 916 } 917 918 /** 919 * Assign current enrollments 920 * @param currentEnrollments current enrollments 921 */ 922 protected void assignCurrent(List<Enrollment> currentEnrollments) { 923 for (Request request : getModel().variables()) 924 getAssignment().unassign(0, request); 925 // Enrollments with a reservation must go first, enrollments with an override go last 926 for (Enrollment enrollment : currentEnrollments) { 927 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 928 if (!enrollment.getStudent().isAvailable(enrollment)) { 929 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 930 continue; 931 } 932 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 933 if (conflicts.isEmpty()) 934 getAssignment().assign(0, enrollment); 935 else 936 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 937 } 938 for (Enrollment enrollment : currentEnrollments) { 939 if (enrollment.getReservation() != null) continue; 940 if (!enrollment.getStudent().isAvailable(enrollment)) { 941 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 942 continue; 943 } 944 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 945 if (conflicts.isEmpty()) 946 getAssignment().assign(0, enrollment); 947 else 948 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 949 } 950 for (Enrollment enrollment : currentEnrollments) { 951 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue; 952 if (!enrollment.getStudent().isAvailable(enrollment)) { 953 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 954 continue; 955 } 956 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 957 if (conflicts.isEmpty()) 958 getAssignment().assign(0, enrollment); 959 else 960 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 961 } 962 } 963 964 /** 965 * Load student 966 * @param studentEl student element 967 * @param offeringTable offering table 968 * @return loaded student 969 */ 970 protected Student loadStudent(Element studentEl, Map<Long, Offering> offeringTable) { 971 Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy"))); 972 if (studentEl.attributeValue("priority") != null) 973 student.setPriority(StudentPriority.getPriority(studentEl.attributeValue("priority"))); 974 if ("true".equals(studentEl.attributeValue("shortDistances"))) 975 student.setNeedShortDistances(true); 976 if ("true".equals(studentEl.attributeValue("allowDisabled"))) 977 student.setAllowDisabled(true); 978 student.setExternalId(studentEl.attributeValue("externalId")); 979 student.setName(studentEl.attributeValue("name")); 980 student.setStatus(studentEl.attributeValue("status")); 981 String minCredit = studentEl.attributeValue("minCredit"); 982 if (minCredit != null) 983 student.setMinCredit(Float.parseFloat(minCredit)); 984 String maxCredit = studentEl.attributeValue("maxCredit"); 985 if (maxCredit != null) 986 student.setMaxCredit(Float.parseFloat(maxCredit)); 987 List<String[]> clasf = new ArrayList<String[]>(); 988 List<String[]> major = new ArrayList<String[]>(); 989 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 990 Element requestEl = (Element) j.next(); 991 if ("classification".equals(requestEl.getName())) { 992 clasf.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")}); 993 } else if ("major".equals(requestEl.getName())) { 994 major.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")}); 995 } else if ("minor".equals(requestEl.getName())) { 996 if ("A".equals(requestEl.attributeValue("area"))) 997 student.getAccommodations().add(requestEl.attributeValue("code")); 998 else 999 student.getGroups().add(new StudentGroup(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label"))); 1000 } else if ("unavailability".equals(requestEl.getName())) { 1001 Offering offering = offeringTable.get(Long.parseLong(requestEl.attributeValue("offering"))); 1002 Section section = (offering == null ? null : offering.getSection(Long.parseLong(requestEl.attributeValue("section")))); 1003 if (section != null) 1004 new Unavailability(student, section, "true".equals(requestEl.attributeValue("allowOverlap"))); 1005 } else if ("acm".equals(requestEl.getName())) { 1006 if (requestEl.attributeValue("minor") != null) 1007 student.getAreaClassificationMinors().add(new AreaClassificationMajor( 1008 requestEl.attributeValue("area"), requestEl.attributeValue("areaName"), 1009 requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"), 1010 requestEl.attributeValue("minor"), requestEl.attributeValue("minorName"), 1011 requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"), 1012 requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"), 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("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight")))); 1022 } else if ("group".equals(requestEl.getName())) { 1023 student.getGroups().add(new StudentGroup(requestEl.attributeValue("type"), requestEl.attributeValue("reference"), requestEl.attributeValue("name"))); 1024 } else if ("accommodation".equals(requestEl.getName())) { 1025 student.getAccommodations().add(requestEl.attributeValue("reference")); 1026 } else if ("advisor".equals(requestEl.getName())) { 1027 student.getAdvisors().add(new Instructor(0l, requestEl.attributeValue("externalId"), requestEl.attributeValue("name"), requestEl.attributeValue("email"))); 1028 } 1029 } 1030 for (int i = 0; i < Math.min(clasf.size(), major.size()); i++) { 1031 student.getAreaClassificationMajors().add(new AreaClassificationMajor(clasf.get(i)[0],clasf.get(i)[1],major.get(i)[1])); 1032 } 1033 return student; 1034 } 1035 1036 /** 1037 * Load request 1038 * @param requestEl request element 1039 * @param student parent student 1040 * @param offeringTable offering table 1041 * @param courseTable course table 1042 * @return loaded request 1043 */ 1044 protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1045 if ("freeTime".equals(requestEl.getName())) { 1046 return loadFreeTime(requestEl, student); 1047 } else if ("course".equals(requestEl.getName())) { 1048 return loadCourseRequest(requestEl, student, offeringTable, courseTable); 1049 } else { 1050 return null; 1051 } 1052 } 1053 1054 /** 1055 * Load free time request 1056 * @param requestEl request element 1057 * @param student parent student 1058 * @return loaded free time request 1059 */ 1060 public FreeTimeRequest loadFreeTime(Element requestEl, Student student) { 1061 TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2), 1062 Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl 1063 .attributeValue("length")), 0, 0, 1064 requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl 1065 .attributeValue("datePattern")), "", createBitSet(requestEl 1066 .attributeValue("dates")), 0); 1067 FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")), 1068 Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl 1069 .attributeValue("alternative")), student, time); 1070 if (requestEl.attributeValue("weight") != null) 1071 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1072 return request; 1073 } 1074 1075 /** 1076 * Load course request 1077 * @param requestEl request element 1078 * @param student parent student 1079 * @param offeringTable offering table 1080 * @param courseTable course table 1081 * @return loaded course request 1082 */ 1083 public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1084 List<Course> courses = new ArrayList<Course>(); 1085 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 1086 for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();) 1087 courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course")))); 1088 Long timeStamp = null; 1089 if (requestEl.attributeValue("timeStamp") != null) 1090 timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp")); 1091 CourseRequest courseRequest = new CourseRequest( 1092 Long.parseLong(requestEl.attributeValue("id")), 1093 Integer.parseInt(requestEl.attributeValue("priority")), 1094 "true".equals(requestEl.attributeValue("alternative")), 1095 student, courses, 1096 "true".equals(requestEl.attributeValue("waitlist", "false")), 1097 RequestPriority.valueOf(requestEl.attributeValue("importance", 1098 "true".equals(requestEl.attributeValue("critical", "false")) ? RequestPriority.Critical.name() : RequestPriority.Normal.name())), 1099 timeStamp); 1100 if (iWaitlistCritical && RequestPriority.Critical.isCritical(courseRequest) && !courseRequest.isAlternative()) courseRequest.setWaitlist(true); 1101 if (requestEl.attributeValue("weight") != null) 1102 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1103 for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) { 1104 Element choiceEl = (Element) k.next(); 1105 courseRequest.getWaitlistedChoices().add( 1106 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1107 } 1108 for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) { 1109 Element choiceEl = (Element) k.next(); 1110 courseRequest.getSelectedChoices().add( 1111 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1112 } 1113 for (Iterator<?> k = requestEl.elementIterator("required"); k.hasNext();) { 1114 Element choiceEl = (Element) k.next(); 1115 courseRequest.getRequiredChoices().add( 1116 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1117 } 1118 groups: for (Iterator<?> k = requestEl.elementIterator("group"); k.hasNext();) { 1119 Element groupEl = (Element) k.next(); 1120 long gid = Long.parseLong(groupEl.attributeValue("id")); 1121 String gname = groupEl.attributeValue("name", "g" + gid); 1122 Course course = courseTable.get(Long.valueOf(groupEl.attributeValue("course"))); 1123 for (RequestGroup g: course.getRequestGroups()) { 1124 if (g.getId() == gid) { 1125 courseRequest.addRequestGroup(g); 1126 continue groups; 1127 } 1128 } 1129 courseRequest.addRequestGroup(new RequestGroup(gid, gname, course)); 1130 } 1131 return courseRequest; 1132 } 1133 1134 /** 1135 * Load enrollment 1136 * @param enrollmentEl enrollment element (current, best, or initial) 1137 * @param request parent request 1138 * @return loaded enrollment 1139 */ 1140 protected Enrollment loadEnrollment(Element enrollmentEl, Request request) { 1141 if (request instanceof CourseRequest) { 1142 CourseRequest courseRequest = (CourseRequest) request; 1143 HashSet<Section> sections = new HashSet<Section>(); 1144 for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) { 1145 Element sectionEl = (Element) k.next(); 1146 Section section = courseRequest.getSection(Long.parseLong(sectionEl 1147 .attributeValue("id"))); 1148 sections.add(section); 1149 } 1150 Reservation reservation = null; 1151 if (enrollmentEl.attributeValue("reservation", null) != null) { 1152 long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation")); 1153 for (Course course: courseRequest.getCourses()) 1154 for (Reservation r: course.getOffering().getReservations()) 1155 if (r.getId() == reservationId) { reservation = r; break; } 1156 } 1157 if (!sections.isEmpty()) { 1158 if (enrollmentEl.attributeValue("course") != null) { 1159 Course course = courseRequest.getCourse(Long.valueOf(enrollmentEl.attributeValue("course"))); 1160 if (course != null) 1161 return courseRequest.createEnrollment(course, sections, reservation); 1162 } 1163 return courseRequest.createEnrollment(sections, reservation); 1164 } 1165 } else if (request instanceof FreeTimeRequest) { 1166 return ((FreeTimeRequest)request).createEnrollment(); 1167 } 1168 return null; 1169 } 1170 1171 protected void moveCriticalRequestsUp() { 1172 for (Student student: getModel().getStudents()) { 1173 int assigned = 0, critical = 0; 1174 for (Request r: student.getRequests()) { 1175 if (r instanceof CourseRequest) { 1176 if (r.getInitialAssignment() != null) assigned ++; 1177 if (r.getRequestPriority() != RequestPriority.Normal) critical ++; 1178 } 1179 } 1180 if ((getModel().getKeepInitialAssignments() && assigned > 0) || critical > 0) { 1181 Collections.sort(student.getRequests(), new Comparator<Request>() { 1182 @Override 1183 public int compare(Request r1, Request r2) { 1184 if (r1.isAlternative() != r2.isAlternative()) return r1.isAlternative() ? 1 : -1; 1185 if (getModel().getKeepInitialAssignments()) { 1186 boolean a1 = (r1 instanceof CourseRequest && r1.getInitialAssignment() != null); 1187 boolean a2 = (r2 instanceof CourseRequest && r2.getInitialAssignment() != null); 1188 if (a1 != a2) return a1 ? -1 : 1; 1189 } 1190 int c1 = r1.getRequestPriority().ordinal(); 1191 int c2 = r2.getRequestPriority().ordinal(); 1192 if (c1 != c2) return c1 < c2 ? -1 : 1; 1193 return r1.getPriority() < r2.getPriority() ? -1 : 1; 1194 } 1195 }); 1196 int p = 0; 1197 for (Request r: student.getRequests()) 1198 r.setPriority(p++); 1199 } 1200 } 1201 } 1202 1203}