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 offering.setDummy("true".equals(offeringEl.attributeValue("dummy", "false"))); 359 offeringTable.put(Long.valueOf(offering.getId()), offering); 360 getModel().addOffering(offering); 361 362 for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) { 363 Element courseEl = (Element) j.next(); 364 Course course = loadCourse(courseEl, offering); 365 courseTable.put(Long.valueOf(course.getId()), course); 366 } 367 368 for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) { 369 Element configEl = (Element) j.next(); 370 Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable); 371 configTable.put(config.getId(), config); 372 } 373 374 for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) { 375 Element reservationEl = (Element)j.next(); 376 loadReservation(reservationEl, offering, configTable, sectionTable); 377 } 378 379 for (Iterator<?> j = offeringEl.elementIterator("restriction"); j.hasNext(); ) { 380 Element restrictionEl = (Element)j.next(); 381 loadRestriction(restrictionEl, offering, configTable, sectionTable); 382 } 383 } 384 } 385 386 /** 387 * Load course 388 * @param courseEl course element 389 * @param offering parent offering 390 * @return loaded course 391 */ 392 protected Course loadCourse(Element courseEl, Offering offering) { 393 Course course = new Course( 394 Long.parseLong(courseEl.attributeValue("id")), 395 courseEl.attributeValue("subjectArea", ""), 396 courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")), 397 offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")), 398 Integer.parseInt(courseEl.attributeValue("projected", "0"))); 399 course.setCredit(courseEl.attributeValue("credit")); 400 String credits = courseEl.attributeValue("credits"); 401 if (credits != null) 402 course.setCreditValue(Float.valueOf(credits)); 403 course.setNote(courseEl.attributeValue("note")); 404 course.setType(courseEl.attributeValue("type")); 405 course.setTitle(courseEl.attributeValue("title")); 406 return course; 407 } 408 409 /** 410 * Load config 411 * @param configEl config element 412 * @param offering parent offering 413 * @param subpartTable subpart table (of the offering) 414 * @param sectionTable section table (of the offering) 415 * @param timetable provided timetable 416 * @return loaded config 417 */ 418 protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 419 Config config = new Config 420 (Long.parseLong(configEl.attributeValue("id")), 421 Integer.parseInt(configEl.attributeValue("limit", "-1")), 422 configEl.attributeValue("name", "G" + configEl.attributeValue("id")), 423 offering); 424 Element imEl = configEl.element("instructional-method"); 425 if (imEl != null) { 426 config.setInstructionalMethodId(Long.parseLong(imEl.attributeValue("id"))); 427 config.setInstructionalMethodName(imEl.attributeValue("name", "M" + imEl.attributeValue("id"))); 428 config.setInstructionalMethodReference(imEl.attributeValue("reference", config.getInstructionalMethodName())); 429 } 430 for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) { 431 Element subpartEl = (Element) k.next(); 432 Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable); 433 subpartTable.put(Long.valueOf(subpart.getId()), subpart); 434 } 435 return config; 436 } 437 438 /** 439 * Load subpart 440 * @param subpartEl supart element 441 * @param config parent config 442 * @param subpartTable subpart table (of the offering) 443 * @param sectionTable section table (of the offering) 444 * @param timetable provided timetable 445 * @return loaded subpart 446 */ 447 protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 448 Subpart parentSubpart = null; 449 if (subpartEl.attributeValue("parent") != null) 450 parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent"))); 451 Subpart subpart = new Subpart( 452 Long.parseLong(subpartEl.attributeValue("id")), 453 subpartEl.attributeValue("itype"), 454 subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")), 455 config, 456 parentSubpart); 457 subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false"))); 458 subpart.setCredit(subpartEl.attributeValue("credit")); 459 String credits = subpartEl.attributeValue("credits"); 460 if (credits != null) 461 subpart.setCreditValue(Float.valueOf(credits)); 462 463 for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) { 464 Element sectionEl = (Element) l.next(); 465 Section section = loadSection(sectionEl, subpart, sectionTable, timetable); 466 sectionTable.put(Long.valueOf(section.getId()), section); 467 } 468 469 return subpart; 470 } 471 472 /** 473 * Load section 474 * @param sectionEl section element 475 * @param subpart parent subpart 476 * @param sectionTable section table (of the offering) 477 * @param timetable provided timetable 478 * @return loaded section 479 */ 480 @SuppressWarnings("deprecation") 481 protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 482 Section parentSection = null; 483 if (sectionEl.attributeValue("parent") != null) 484 parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent"))); 485 Placement placement = null; 486 if (timetable != null) { 487 placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id"))); 488 } else { 489 TimeLocation time = null; 490 Element timeEl = sectionEl.element("time"); 491 if (timeEl != null) { 492 time = new TimeLocation( 493 Integer.parseInt(timeEl.attributeValue("days"), 2), 494 Integer.parseInt(timeEl.attributeValue("start")), 495 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 496 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 497 timeEl.attributeValue("datePatternName", ""), 498 createBitSet(timeEl.attributeValue("dates")), 499 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 500 if (timeEl.attributeValue("pattern") != null) 501 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 502 } 503 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 504 for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) { 505 Element roomEl = (Element) m.next(); 506 Double posX = null, posY = null; 507 if (roomEl.attributeValue("location") != null) { 508 String loc = roomEl.attributeValue("location"); 509 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 510 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 511 } 512 RoomLocation room = new RoomLocation( 513 Long.valueOf(roomEl.attributeValue("id")), 514 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 515 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 516 0, Integer.parseInt(roomEl.attributeValue("capacity")), 517 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 518 rooms.add(room); 519 } 520 placement = (time == null ? null : new Placement(null, time, rooms)); 521 } 522 523 List<Instructor> instructors = new ArrayList<Instructor>(); 524 for (Iterator<?> m = sectionEl.elementIterator("instructor"); m.hasNext(); ) { 525 Element instructorEl = (Element)m.next(); 526 instructors.add(new Instructor(Long.parseLong(instructorEl.attributeValue("id")), instructorEl.attributeValue("externalId"), instructorEl.attributeValue("name"), instructorEl.attributeValue("email"))); 527 } 528 if (instructors.isEmpty() && sectionEl.attributeValue("instructorIds") != null) 529 instructors = Instructor.toInstructors(sectionEl.attributeValue("instructorIds"), sectionEl.attributeValue("instructorNames")); 530 Section section = new Section( 531 Long.parseLong(sectionEl.attributeValue("id")), 532 Integer.parseInt(sectionEl.attributeValue("limit")), 533 sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")), 534 subpart, placement, instructors, parentSection); 535 536 section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0"))); 537 section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0"))); 538 section.setCancelled("true".equalsIgnoreCase(sectionEl.attributeValue("cancelled", "false"))); 539 section.setEnabled("true".equalsIgnoreCase(sectionEl.attributeValue("enabled", "true"))); 540 section.setOnline("true".equalsIgnoreCase(sectionEl.attributeValue("online", "false"))); 541 section.setPast("true".equalsIgnoreCase(sectionEl.attributeValue("past", "false"))); 542 for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) { 543 Element cNameEl = (Element)m.next(); 544 section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText()); 545 } 546 Element ignoreEl = sectionEl.element("no-conflicts"); 547 if (ignoreEl != null) { 548 for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); ) 549 section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id"))); 550 } 551 552 return section; 553 } 554 555 /** 556 * Load reservation 557 * @param reservationEl reservation element 558 * @param offering parent offering 559 * @param configTable config table (of the offering) 560 * @param sectionTable section table (of the offering) 561 * @return loaded reservation 562 */ 563 protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 564 Reservation r = null; 565 if ("individual".equals(reservationEl.attributeValue("type"))) { 566 Set<Long> studentIds = new HashSet<Long>(); 567 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 568 Element studentEl = (Element)k.next(); 569 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 570 } 571 r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 572 } else if ("group".equals(reservationEl.attributeValue("type"))) { 573 Set<Long> studentIds = new HashSet<Long>(); 574 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 575 Element studentEl = (Element)k.next(); 576 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 577 } 578 r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")), 579 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 580 offering, studentIds); 581 } else if ("lc".equals(reservationEl.attributeValue("type"))) { 582 Set<Long> studentIds = new HashSet<Long>(); 583 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 584 Element studentEl = (Element)k.next(); 585 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 586 } 587 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 588 for (Course course: offering.getCourses()) { 589 if (course.getId() == courseId) 590 r = new LearningCommunityReservation(Long.valueOf(reservationEl.attributeValue("id")), 591 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 592 course, studentIds); 593 } 594 } else if ("curriculum".equals(reservationEl.attributeValue("type")) || "curriculum-override".equals(reservationEl.attributeValue("type"))) { 595 List<String> acadAreas = new ArrayList<String>(); 596 for (Iterator<?> k = reservationEl.elementIterator("area"); k.hasNext(); ) { 597 Element areaEl = (Element)k.next(); 598 acadAreas.add(areaEl.attributeValue("code")); 599 } 600 if (acadAreas.isEmpty() && reservationEl.attributeValue("area") != null) 601 acadAreas.add(reservationEl.attributeValue("area")); 602 List<String> classifications = new ArrayList<String>(); 603 for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) { 604 Element clasfEl = (Element)k.next(); 605 classifications.add(clasfEl.attributeValue("code")); 606 } 607 List<String> majors = new ArrayList<String>(); 608 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 609 Element majorEl = (Element)k.next(); 610 majors.add(majorEl.attributeValue("code")); 611 } 612 List<String> minors = new ArrayList<String>(); 613 for (Iterator<?> k = reservationEl.elementIterator("minor"); k.hasNext(); ) { 614 Element minorEl = (Element)k.next(); 615 minors.add(minorEl.attributeValue("code")); 616 } 617 if ("curriculum".equals(reservationEl.attributeValue("type"))) 618 r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")), 619 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 620 offering, 621 acadAreas, classifications, majors, minors); 622 else 623 r = new CurriculumOverride(Long.valueOf(reservationEl.attributeValue("id")), 624 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 625 offering, 626 acadAreas, classifications, majors, minors); 627 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 628 Element majorEl = (Element)k.next(); 629 for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) { 630 Element concentrationEl = (Element)l.next(); 631 ((CurriculumReservation)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code")); 632 } 633 } 634 } else if ("course".equals(reservationEl.attributeValue("type"))) { 635 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 636 for (Course course: offering.getCourses()) { 637 if (course.getId() == courseId) 638 r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course); 639 } 640 } else if ("dummy".equals(reservationEl.attributeValue("type"))) { 641 r = new DummyReservation(offering); 642 } else if ("universal".equals(reservationEl.attributeValue("type"))) { 643 r = new UniversalOverride( 644 Long.valueOf(reservationEl.attributeValue("id")), 645 "true".equals(reservationEl.attributeValue("override", "false")), 646 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 647 offering, 648 reservationEl.attributeValue("filter")); 649 } else if ("override".equals(reservationEl.attributeValue("type"))) { 650 Set<Long> studentIds = new HashSet<Long>(); 651 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 652 Element studentEl = (Element)k.next(); 653 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 654 } 655 r = new ReservationOverride(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 656 } 657 if (r == null) { 658 sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type")); 659 return null; 660 } 661 r.setExpired("true".equals(reservationEl.attributeValue("expired", "false"))); 662 for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) { 663 Element configEl = (Element)k.next(); 664 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 665 } 666 for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) { 667 Element sectionEl = (Element)k.next(); 668 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))), false); 669 } 670 r.setPriority(Integer.parseInt(reservationEl.attributeValue("priority", String.valueOf(r.getPriority())))); 671 r.setMustBeUsed("true".equals(reservationEl.attributeValue("mustBeUsed", r.mustBeUsed() ? "true" : "false"))); 672 r.setAllowOverlap("true".equals(reservationEl.attributeValue("allowOverlap", r.isAllowOverlap() ? "true" : "false"))); 673 r.setCanAssignOverLimit("true".equals(reservationEl.attributeValue("canAssignOverLimit", r.canAssignOverLimit() ? "true" : "false"))); 674 r.setAllowDisabled("true".equals(reservationEl.attributeValue("allowDisabled", r.isAllowDisabled() ? "true" : "false"))); 675 r.setNeverIncluded("true".equals(reservationEl.attributeValue("neverIncluded", "false"))); 676 r.setBreakLinkedSections("true".equals(reservationEl.attributeValue("breakLinkedSections", "false"))); 677 return r; 678 } 679 680 /** 681 * Load restriction 682 * @param restrictionEl restriction element 683 * @param offering parent offering 684 * @param configTable config table (of the offering) 685 * @param sectionTable section table (of the offering) 686 * @return loaded restriction 687 */ 688 protected Restriction loadRestriction(Element restrictionEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 689 Restriction r = null; 690 if ("individual".equals(restrictionEl.attributeValue("type"))) { 691 Set<Long> studentIds = new HashSet<Long>(); 692 for (Iterator<?> k = restrictionEl.elementIterator("student"); k.hasNext(); ) { 693 Element studentEl = (Element)k.next(); 694 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 695 } 696 r = new IndividualRestriction(Long.valueOf(restrictionEl.attributeValue("id")), offering, studentIds); 697 } else if ("curriculum".equals(restrictionEl.attributeValue("type"))) { 698 List<String> acadAreas = new ArrayList<String>(); 699 for (Iterator<?> k = restrictionEl.elementIterator("area"); k.hasNext(); ) { 700 Element areaEl = (Element)k.next(); 701 acadAreas.add(areaEl.attributeValue("code")); 702 } 703 if (acadAreas.isEmpty() && restrictionEl.attributeValue("area") != null) 704 acadAreas.add(restrictionEl.attributeValue("area")); 705 List<String> classifications = new ArrayList<String>(); 706 for (Iterator<?> k = restrictionEl.elementIterator("classification"); k.hasNext(); ) { 707 Element clasfEl = (Element)k.next(); 708 classifications.add(clasfEl.attributeValue("code")); 709 } 710 List<String> majors = new ArrayList<String>(); 711 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 712 Element majorEl = (Element)k.next(); 713 majors.add(majorEl.attributeValue("code")); 714 } 715 List<String> minors = new ArrayList<String>(); 716 for (Iterator<?> k = restrictionEl.elementIterator("minor"); k.hasNext(); ) { 717 Element minorEl = (Element)k.next(); 718 minors.add(minorEl.attributeValue("code")); 719 } 720 r = new CurriculumRestriction(Long.valueOf(restrictionEl.attributeValue("id")), 721 offering, 722 acadAreas, classifications, majors, minors); 723 for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) { 724 Element majorEl = (Element)k.next(); 725 for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) { 726 Element concentrationEl = (Element)l.next(); 727 ((CurriculumRestriction)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code")); 728 } 729 } 730 } else if ("course".equals(restrictionEl.attributeValue("type"))) { 731 long courseId = Long.parseLong(restrictionEl.attributeValue("course")); 732 for (Course course: offering.getCourses()) { 733 if (course.getId() == courseId) 734 r = new CourseRestriction(Long.valueOf(restrictionEl.attributeValue("id")), course); 735 } 736 } 737 if (r == null) { 738 sLogger.error("Unknown reservation type "+ restrictionEl.attributeValue("type")); 739 return null; 740 } 741 for (Iterator<?> k = restrictionEl.elementIterator("config"); k.hasNext(); ) { 742 Element configEl = (Element)k.next(); 743 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 744 } 745 for (Iterator<?> k = restrictionEl.elementIterator("section"); k.hasNext(); ) { 746 Element sectionEl = (Element)k.next(); 747 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id")))); 748 } 749 return r; 750 } 751 752 /** 753 * Load given timetable 754 * @param timetableRoot document root in the course timetabling XML format 755 * @return loaded timetable (map class id: assigned placement) 756 */ 757 protected Map<Long, Placement> loadTimetable(Element timetableRoot) { 758 Map<Long, Placement> timetable = new HashMap<Long, Placement>(); 759 HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>(); 760 for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) { 761 Element roomEl = (Element)i.next(); 762 Long roomId = Long.valueOf(roomEl.attributeValue("id")); 763 Double posX = null, posY = null; 764 if (roomEl.attributeValue("location") != null) { 765 String loc = roomEl.attributeValue("location"); 766 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 767 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 768 } 769 RoomLocation room = new RoomLocation( 770 Long.valueOf(roomEl.attributeValue("id")), 771 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 772 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 773 0, Integer.parseInt(roomEl.attributeValue("capacity")), 774 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 775 rooms.put(roomId, room); 776 } 777 for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) { 778 Element classEl = (Element)i.next(); 779 Long classId = Long.valueOf(classEl.attributeValue("id")); 780 TimeLocation time = null; 781 Element timeEl = null; 782 for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) { 783 Element e = (Element)j.next(); 784 if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; } 785 } 786 if (timeEl != null) { 787 time = new TimeLocation( 788 Integer.parseInt(timeEl.attributeValue("days"), 2), 789 Integer.parseInt(timeEl.attributeValue("start")), 790 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 791 classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")), 792 classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")), 793 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 794 if (timeEl.attributeValue("pattern") != null) 795 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 796 } 797 List<RoomLocation> room = new ArrayList<RoomLocation>(); 798 for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) { 799 Element roomEl = (Element) j.next(); 800 if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue; 801 room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id")))); 802 } 803 Placement placement = (time == null ? null : new Placement(null, time, room)); 804 if (placement != null) 805 timetable.put(classId, placement); 806 } 807 return timetable; 808 } 809 810 /** 811 * Load travel times 812 * @param travelTimesEl travel-time element 813 * @param metric distance metric to be populated 814 */ 815 protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) { 816 for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) { 817 Element travelTimeEl = (Element)i.next(); 818 metric.addTravelTime( 819 Long.valueOf(travelTimeEl.attributeValue("id1")), 820 Long.valueOf(travelTimeEl.attributeValue("id2")), 821 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 822 } 823 } 824 825 /** 826 * Load linked sections 827 * @param constraintsEl constraints element 828 * @param offeringTable offering table 829 */ 830 protected void loadLinkedSections(Element constraintsEl, Map<Long, Offering> offeringTable) { 831 for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) { 832 Element linkedEl = (Element) i.next(); 833 List<Section> sections = new ArrayList<Section>(); 834 for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) { 835 Element sectionEl = (Element) j.next(); 836 Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering"))); 837 sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id")))); 838 } 839 getModel().addLinkedSections("true".equals(linkedEl.attributeValue("mustBeUsed", "false")), sections); 840 } 841 } 842 843 /** 844 * Load students 845 * @param studentsEl students element 846 * @param offeringTable offering table 847 * @param courseTable course table 848 */ 849 protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, List<Enrollment> bestEnrollments, List<Enrollment> currentEnrollments) { 850 for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) { 851 Element studentEl = (Element) i.next(); 852 Student student = loadStudent(studentEl, offeringTable); 853 if (iStudentFilter != null && !iStudentFilter.accept(student)) 854 continue; 855 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 856 Element requestEl = (Element) j.next(); 857 Request request = loadRequest(requestEl, student, offeringTable, courseTable); 858 if (request == null) continue; 859 860 Element initialEl = requestEl.element("initial"); 861 if (iLoadInitial && initialEl != null) { 862 Enrollment enrollment = loadEnrollment(initialEl, request); 863 if (enrollment != null) 864 request.setInitialAssignment(enrollment); 865 } 866 Element currentEl = requestEl.element("current"); 867 if (iLoadCurrent && currentEl != null) { 868 Enrollment enrollment = loadEnrollment(currentEl, request); 869 if (enrollment != null) 870 currentEnrollments.add(enrollment); 871 } 872 Element bestEl = requestEl.element("best"); 873 if (iLoadBest && bestEl != null) { 874 Enrollment enrollment = loadEnrollment(bestEl, request); 875 if (enrollment != null) 876 bestEnrollments.add(enrollment); 877 } 878 Element fixedEl = requestEl.element("fixed"); 879 if (fixedEl != null && request instanceof CourseRequest) 880 ((CourseRequest)request).setFixedValue(loadEnrollment(fixedEl, request)); 881 } 882 getModel().addStudent(student); 883 } 884 } 885 886 /** 887 * Save best enrollments 888 * @param bestEnrollments best enrollments 889 */ 890 protected void assignBest(List<Enrollment> bestEnrollments) { 891 // Enrollments with a reservation must go first, enrollments with an override go last 892 for (Enrollment enrollment : bestEnrollments) { 893 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 894 if (!enrollment.getStudent().isAvailable(enrollment)) { 895 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 896 continue; 897 } 898 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 899 if (conflicts.isEmpty()) 900 getAssignment().assign(0, enrollment); 901 else 902 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 903 } 904 for (Enrollment enrollment : bestEnrollments) { 905 if (enrollment.getReservation() != null) continue; 906 if (!enrollment.getStudent().isAvailable(enrollment)) { 907 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 908 continue; 909 } 910 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 911 if (conflicts.isEmpty()) 912 getAssignment().assign(0, enrollment); 913 else 914 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 915 } 916 for (Enrollment enrollment : bestEnrollments) { 917 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue; 918 if (!enrollment.getStudent().isAvailable(enrollment)) { 919 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 920 continue; 921 } 922 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 923 if (conflicts.isEmpty()) 924 getAssignment().assign(0, enrollment); 925 else 926 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 927 } 928 getModel().saveBest(getAssignment()); 929 } 930 931 /** 932 * Assign current enrollments 933 * @param currentEnrollments current enrollments 934 */ 935 protected void assignCurrent(List<Enrollment> currentEnrollments) { 936 for (Request request : getModel().variables()) 937 getAssignment().unassign(0, request); 938 // Enrollments with a reservation must go first, enrollments with an override go last 939 for (Enrollment enrollment : currentEnrollments) { 940 if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue; 941 if (!enrollment.getStudent().isAvailable(enrollment)) { 942 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 943 continue; 944 } 945 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 946 if (conflicts.isEmpty()) 947 getAssignment().assign(0, enrollment); 948 else 949 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 950 } 951 for (Enrollment enrollment : currentEnrollments) { 952 if (enrollment.getReservation() != null) continue; 953 if (!enrollment.getStudent().isAvailable(enrollment)) { 954 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 955 continue; 956 } 957 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 958 if (conflicts.isEmpty()) 959 getAssignment().assign(0, enrollment); 960 else 961 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 962 } 963 for (Enrollment enrollment : currentEnrollments) { 964 if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue; 965 if (!enrollment.getStudent().isAvailable(enrollment)) { 966 sLogger.warn("Enrollment " + enrollment + " is conflicting: student not available."); 967 continue; 968 } 969 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 970 if (conflicts.isEmpty()) 971 getAssignment().assign(0, enrollment); 972 else 973 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 974 } 975 } 976 977 /** 978 * Load student 979 * @param studentEl student element 980 * @param offeringTable offering table 981 * @return loaded student 982 */ 983 protected Student loadStudent(Element studentEl, Map<Long, Offering> offeringTable) { 984 Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy"))); 985 if (studentEl.attributeValue("priority") != null) 986 student.setPriority(StudentPriority.getPriority(studentEl.attributeValue("priority"))); 987 if ("true".equals(studentEl.attributeValue("shortDistances"))) 988 student.setNeedShortDistances(true); 989 if ("true".equals(studentEl.attributeValue("allowDisabled"))) 990 student.setAllowDisabled(true); 991 student.setExternalId(studentEl.attributeValue("externalId")); 992 student.setName(studentEl.attributeValue("name")); 993 student.setStatus(studentEl.attributeValue("status")); 994 String minCredit = studentEl.attributeValue("minCredit"); 995 if (minCredit != null) 996 student.setMinCredit(Float.parseFloat(minCredit)); 997 String maxCredit = studentEl.attributeValue("maxCredit"); 998 if (maxCredit != null) 999 student.setMaxCredit(Float.parseFloat(maxCredit)); 1000 String classFirstDate = studentEl.attributeValue("classFirstDate"); 1001 if (classFirstDate != null) 1002 student.setClassFirstDate(Integer.parseInt(classFirstDate)); 1003 String classLastDate = studentEl.attributeValue("classLastDate"); 1004 if (classLastDate != null) 1005 student.setClassLastDate(Integer.parseInt(classLastDate)); 1006 String modality = studentEl.attributeValue("modality"); 1007 if (modality != null) 1008 student.setModalityPreference(ModalityPreference.valueOf(modality)); 1009 String btb = studentEl.attributeValue("btb"); 1010 if (btb != null) 1011 student.setBackToBackPreference(BackToBackPreference.valueOf(btb)); 1012 1013 List<String[]> clasf = new ArrayList<String[]>(); 1014 List<String[]> major = new ArrayList<String[]>(); 1015 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 1016 Element requestEl = (Element) j.next(); 1017 if ("classification".equals(requestEl.getName())) { 1018 clasf.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")}); 1019 } else if ("major".equals(requestEl.getName())) { 1020 major.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")}); 1021 } else if ("minor".equals(requestEl.getName())) { 1022 if ("A".equals(requestEl.attributeValue("area"))) 1023 student.getAccommodations().add(requestEl.attributeValue("code")); 1024 else 1025 student.getGroups().add(new StudentGroup(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label"))); 1026 } else if ("unavailability".equals(requestEl.getName())) { 1027 Offering offering = offeringTable.get(Long.parseLong(requestEl.attributeValue("offering"))); 1028 Section section = (offering == null ? null : offering.getSection(Long.parseLong(requestEl.attributeValue("section")))); 1029 if (section != null) { 1030 Unavailability ua = new Unavailability(student, section, "true".equals(requestEl.attributeValue("allowOverlap"))); 1031 ua.setTeachingAssignment("true".equals(requestEl.attributeValue("ta", "false"))); 1032 if (requestEl.attributeValue("course") != null) 1033 ua.setCourseId(Long.valueOf(requestEl.attributeValue("course"))); 1034 } 1035 } else if ("acm".equals(requestEl.getName())) { 1036 if (requestEl.attributeValue("minor") != null) 1037 student.getAreaClassificationMinors().add(new AreaClassificationMajor( 1038 requestEl.attributeValue("area"), requestEl.attributeValue("areaName"), 1039 requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"), 1040 requestEl.attributeValue("minor"), requestEl.attributeValue("minorName"), 1041 requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"), 1042 requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"), 1043 requestEl.attributeValue("program"), requestEl.attributeValue("programName"), 1044 requestEl.attributeValue("campus"), requestEl.attributeValue("campusName"), 1045 requestEl.attributeValue("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight")))); 1046 else 1047 student.getAreaClassificationMajors().add(new AreaClassificationMajor( 1048 requestEl.attributeValue("area"), requestEl.attributeValue("areaName"), 1049 requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"), 1050 requestEl.attributeValue("major"), requestEl.attributeValue("majorName"), 1051 requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"), 1052 requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"), 1053 requestEl.attributeValue("program"), requestEl.attributeValue("programName"), 1054 requestEl.attributeValue("campus"), requestEl.attributeValue("campusName"), 1055 requestEl.attributeValue("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight")))); 1056 } else if ("group".equals(requestEl.getName())) { 1057 student.getGroups().add(new StudentGroup(requestEl.attributeValue("type"), requestEl.attributeValue("reference"), requestEl.attributeValue("name"))); 1058 } else if ("accommodation".equals(requestEl.getName())) { 1059 student.getAccommodations().add(requestEl.attributeValue("reference")); 1060 } else if ("advisor".equals(requestEl.getName())) { 1061 student.getAdvisors().add(new Instructor(0l, requestEl.attributeValue("externalId"), requestEl.attributeValue("name"), requestEl.attributeValue("email"))); 1062 } 1063 } 1064 for (int i = 0; i < Math.min(clasf.size(), major.size()); i++) { 1065 student.getAreaClassificationMajors().add(new AreaClassificationMajor(clasf.get(i)[0],clasf.get(i)[1],major.get(i)[1])); 1066 } 1067 return student; 1068 } 1069 1070 /** 1071 * Load request 1072 * @param requestEl request element 1073 * @param student parent student 1074 * @param offeringTable offering table 1075 * @param courseTable course table 1076 * @return loaded request 1077 */ 1078 protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1079 if ("freeTime".equals(requestEl.getName())) { 1080 return loadFreeTime(requestEl, student); 1081 } else if ("course".equals(requestEl.getName())) { 1082 return loadCourseRequest(requestEl, student, offeringTable, courseTable); 1083 } else { 1084 return null; 1085 } 1086 } 1087 1088 /** 1089 * Load free time request 1090 * @param requestEl request element 1091 * @param student parent student 1092 * @return loaded free time request 1093 */ 1094 public FreeTimeRequest loadFreeTime(Element requestEl, Student student) { 1095 TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2), 1096 Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl 1097 .attributeValue("length")), 0, 0, 1098 requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl 1099 .attributeValue("datePattern")), "", createBitSet(requestEl 1100 .attributeValue("dates")), 0); 1101 FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")), 1102 Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl 1103 .attributeValue("alternative")), student, time); 1104 if (requestEl.attributeValue("weight") != null) 1105 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1106 return request; 1107 } 1108 1109 /** 1110 * Load course request 1111 * @param requestEl request element 1112 * @param student parent student 1113 * @param offeringTable offering table 1114 * @param courseTable course table 1115 * @return loaded course request 1116 */ 1117 public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 1118 List<Course> courses = new ArrayList<Course>(); 1119 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 1120 for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();) 1121 courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course")))); 1122 Long timeStamp = null; 1123 if (requestEl.attributeValue("timeStamp") != null) 1124 timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp")); 1125 CourseRequest courseRequest = new CourseRequest( 1126 Long.parseLong(requestEl.attributeValue("id")), 1127 Integer.parseInt(requestEl.attributeValue("priority")), 1128 "true".equals(requestEl.attributeValue("alternative")), 1129 student, courses, 1130 "true".equals(requestEl.attributeValue("waitlist", "false")), 1131 RequestPriority.valueOf(requestEl.attributeValue("importance", 1132 "true".equals(requestEl.attributeValue("critical", "false")) ? RequestPriority.Critical.name() : RequestPriority.Normal.name())), 1133 timeStamp); 1134 if (iWaitlistCritical && RequestPriority.Critical.isCritical(courseRequest) && !courseRequest.isAlternative()) courseRequest.setWaitlist(true); 1135 if (requestEl.attributeValue("weight") != null) 1136 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 1137 for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) { 1138 Element choiceEl = (Element) k.next(); 1139 courseRequest.getWaitlistedChoices().add( 1140 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1141 } 1142 for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) { 1143 Element choiceEl = (Element) k.next(); 1144 courseRequest.getSelectedChoices().add( 1145 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1146 } 1147 for (Iterator<?> k = requestEl.elementIterator("required"); k.hasNext();) { 1148 Element choiceEl = (Element) k.next(); 1149 courseRequest.getRequiredChoices().add( 1150 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 1151 } 1152 groups: for (Iterator<?> k = requestEl.elementIterator("group"); k.hasNext();) { 1153 Element groupEl = (Element) k.next(); 1154 long gid = Long.parseLong(groupEl.attributeValue("id")); 1155 String gname = groupEl.attributeValue("name", "g" + gid); 1156 Course course = courseTable.get(Long.valueOf(groupEl.attributeValue("course"))); 1157 for (RequestGroup g: course.getRequestGroups()) { 1158 if (g.getId() == gid) { 1159 courseRequest.addRequestGroup(g); 1160 continue groups; 1161 } 1162 } 1163 courseRequest.addRequestGroup(new RequestGroup(gid, gname, course)); 1164 } 1165 return courseRequest; 1166 } 1167 1168 /** 1169 * Load enrollment 1170 * @param enrollmentEl enrollment element (current, best, or initial) 1171 * @param request parent request 1172 * @return loaded enrollment 1173 */ 1174 protected Enrollment loadEnrollment(Element enrollmentEl, Request request) { 1175 if (request instanceof CourseRequest) { 1176 CourseRequest courseRequest = (CourseRequest) request; 1177 HashSet<Section> sections = new HashSet<Section>(); 1178 for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) { 1179 Element sectionEl = (Element) k.next(); 1180 Section section = courseRequest.getSection(Long.parseLong(sectionEl 1181 .attributeValue("id"))); 1182 sections.add(section); 1183 } 1184 Reservation reservation = null; 1185 if (enrollmentEl.attributeValue("reservation", null) != null) { 1186 long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation")); 1187 for (Course course: courseRequest.getCourses()) 1188 for (Reservation r: course.getOffering().getReservations()) 1189 if (r.getId() == reservationId) { reservation = r; break; } 1190 } 1191 if (!sections.isEmpty()) { 1192 if (enrollmentEl.attributeValue("course") != null) { 1193 Course course = courseRequest.getCourse(Long.valueOf(enrollmentEl.attributeValue("course"))); 1194 if (course != null) 1195 return courseRequest.createEnrollment(course, sections, reservation); 1196 } 1197 return courseRequest.createEnrollment(sections, reservation); 1198 } 1199 } else if (request instanceof FreeTimeRequest) { 1200 return ((FreeTimeRequest)request).createEnrollment(); 1201 } 1202 return null; 1203 } 1204 1205 protected void moveCriticalRequestsUp() { 1206 for (Student student: getModel().getStudents()) { 1207 int assigned = 0, critical = 0; 1208 for (Request r: student.getRequests()) { 1209 if (r instanceof CourseRequest) { 1210 if (r.getInitialAssignment() != null) assigned ++; 1211 if (r.getRequestPriority() != RequestPriority.Normal) critical ++; 1212 } 1213 } 1214 if ((getModel().getKeepInitialAssignments() && assigned > 0) || critical > 0) { 1215 Collections.sort(student.getRequests(), new Comparator<Request>() { 1216 @Override 1217 public int compare(Request r1, Request r2) { 1218 if (r1.isAlternative() != r2.isAlternative()) return r1.isAlternative() ? 1 : -1; 1219 if (getModel().getKeepInitialAssignments()) { 1220 boolean a1 = (r1 instanceof CourseRequest && r1.getInitialAssignment() != null); 1221 boolean a2 = (r2 instanceof CourseRequest && r2.getInitialAssignment() != null); 1222 if (a1 != a2) return a1 ? -1 : 1; 1223 } 1224 int c1 = r1.getRequestPriority().ordinal(); 1225 int c2 = r2.getRequestPriority().ordinal(); 1226 if (c1 != c2) return c1 < c2 ? -1 : 1; 1227 return r1.getPriority() < r2.getPriority() ? -1 : 1; 1228 } 1229 }); 1230 int p = 0; 1231 for (Request r: student.getRequests()) 1232 r.setPriority(p++); 1233 } 1234 } 1235 } 1236 1237}