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