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