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