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