001package org.cpsolver.studentsct; 002 003import java.io.File; 004import java.util.ArrayList; 005import java.util.BitSet; 006import java.util.HashSet; 007import java.util.HashMap; 008import java.util.Iterator; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012 013import org.cpsolver.coursett.model.Placement; 014import org.cpsolver.coursett.model.RoomLocation; 015import org.cpsolver.coursett.model.TimeLocation; 016import org.cpsolver.ifs.assignment.Assignment; 017import org.cpsolver.ifs.model.Constraint; 018import org.cpsolver.ifs.util.DistanceMetric; 019import org.cpsolver.ifs.util.Progress; 020import org.cpsolver.studentsct.filter.StudentFilter; 021import org.cpsolver.studentsct.model.AcademicAreaCode; 022import org.cpsolver.studentsct.model.Choice; 023import org.cpsolver.studentsct.model.Config; 024import org.cpsolver.studentsct.model.Course; 025import org.cpsolver.studentsct.model.CourseRequest; 026import org.cpsolver.studentsct.model.Enrollment; 027import org.cpsolver.studentsct.model.FreeTimeRequest; 028import org.cpsolver.studentsct.model.Instructor; 029import org.cpsolver.studentsct.model.Offering; 030import org.cpsolver.studentsct.model.Request; 031import org.cpsolver.studentsct.model.RequestGroup; 032import org.cpsolver.studentsct.model.Section; 033import org.cpsolver.studentsct.model.Student; 034import org.cpsolver.studentsct.model.Subpart; 035import org.cpsolver.studentsct.reservation.CourseReservation; 036import org.cpsolver.studentsct.reservation.CurriculumReservation; 037import org.cpsolver.studentsct.reservation.DummyReservation; 038import org.cpsolver.studentsct.reservation.GroupReservation; 039import org.cpsolver.studentsct.reservation.IndividualReservation; 040import org.cpsolver.studentsct.reservation.Reservation; 041import org.cpsolver.studentsct.reservation.ReservationOverride; 042import org.dom4j.Document; 043import org.dom4j.DocumentException; 044import org.dom4j.Element; 045import org.dom4j.io.SAXReader; 046 047/** 048 * Load student sectioning model from an XML file. 049 * 050 * <br> 051 * <br> 052 * Parameters: 053 * <table border='1' summary='Related Solver Parameters'> 054 * <tr> 055 * <th>Parameter</th> 056 * <th>Type</th> 057 * <th>Comment</th> 058 * </tr> 059 * <tr> 060 * <td>General.Input</td> 061 * <td>{@link String}</td> 062 * <td>Path of an XML file to be loaded</td> 063 * </tr> 064 * <tr> 065 * <td>Xml.LoadBest</td> 066 * <td>{@link Boolean}</td> 067 * <td>If true, load best assignments</td> 068 * </tr> 069 * <tr> 070 * <td>Xml.LoadInitial</td> 071 * <td>{@link Boolean}</td> 072 * <td>If false, load initial assignments</td> 073 * </tr> 074 * <tr> 075 * <td>Xml.LoadCurrent</td> 076 * <td>{@link Boolean}</td> 077 * <td>If true, load current assignments</td> 078 * </tr> 079 * <tr> 080 * <td>Xml.LoadOfferings</td> 081 * <td>{@link Boolean}</td> 082 * <td>If true, load offerings (and their stucture, i.e., courses, 083 * configurations, subparts and sections)</td> 084 * </tr> 085 * <tr> 086 * <td>Xml.LoadStudents</td> 087 * <td>{@link Boolean}</td> 088 * <td>If true, load students (and their requests)</td> 089 * </tr> 090 * <tr> 091 * <td>Xml.StudentFilter</td> 092 * <td>{@link StudentFilter}</td> 093 * <td>If provided, students are filtered by the given student filter</td> 094 * </tr> 095 * </table> 096 * 097 * <br> 098 * <br> 099 * Usage: 100 * <pre><code> 101 * StudentSectioningModel model = new StudentSectioningModel(cfg);<br> 102 * new StudentSectioningXMLLoader(model).load();<br> 103 * </code></pre> 104 * 105 * @version StudentSct 1.3 (Student Sectioning)<br> 106 * Copyright (C) 2007 - 2014 Tomas Muller<br> 107 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 108 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 109 * <br> 110 * This library is free software; you can redistribute it and/or modify 111 * it under the terms of the GNU Lesser General Public License as 112 * published by the Free Software Foundation; either version 3 of the 113 * License, or (at your option) any later version. <br> 114 * <br> 115 * This library is distributed in the hope that it will be useful, but 116 * WITHOUT ANY WARRANTY; without even the implied warranty of 117 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 118 * Lesser General Public License for more details. <br> 119 * <br> 120 * You should have received a copy of the GNU Lesser General Public 121 * License along with this library; if not see 122 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 123 */ 124 125public class StudentSectioningXMLLoader extends StudentSectioningLoader { 126 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger 127 .getLogger(StudentSectioningXMLLoader.class); 128 129 private File iInputFile; 130 private File iTimetableFile = null; 131 private boolean iLoadBest = false; 132 private boolean iLoadInitial = false; 133 private boolean iLoadCurrent = false; 134 private boolean iLoadOfferings = true; 135 private boolean iLoadStudents = true; 136 private StudentFilter iStudentFilter = null; 137 138 /** 139 * Constructor 140 * 141 * @param model 142 * student sectioning model 143 * @param assignment current assignment 144 */ 145 public StudentSectioningXMLLoader(StudentSectioningModel model, Assignment<Request, Enrollment> assignment) { 146 super(model, assignment); 147 iInputFile = new File(getModel().getProperties().getProperty("General.Input", 148 "." + File.separator + "solution.xml")); 149 if (getModel().getProperties().getProperty("General.InputTimetable") != null) 150 iTimetableFile = new File(getModel().getProperties().getProperty("General.InputTimetable")); 151 iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true); 152 iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true); 153 iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true); 154 iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true); 155 iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true); 156 if (getModel().getProperties().getProperty("Xml.StudentFilter") != null) { 157 try { 158 iStudentFilter = (StudentFilter) Class.forName( 159 getModel().getProperties().getProperty("Xml.StudentFilter")).getConstructor(new Class[] {}) 160 .newInstance(new Object[] {}); 161 } catch (Exception e) { 162 sLogger.error("Unable to create student filter, reason: " + e.getMessage(), e); 163 } 164 } 165 } 166 167 /** Set input file (e.g., if it is not set by General.Input property) 168 * @param inputFile input file 169 **/ 170 public void setInputFile(File inputFile) { 171 iInputFile = inputFile; 172 } 173 174 /** Set student filter 175 * @param filter student filter 176 **/ 177 public void setStudentFilter(StudentFilter filter) { 178 iStudentFilter = filter; 179 } 180 181 /** Set whether to load students 182 * @param loadStudents true if students are to be loaded 183 **/ 184 public void setLoadStudents(boolean loadStudents) { 185 iLoadStudents = loadStudents; 186 } 187 188 /** Set whether to load offerings 189 * @param loadOfferings true if instructional offerings are to be loaded 190 **/ 191 public void setLoadOfferings(boolean loadOfferings) { 192 iLoadOfferings = loadOfferings; 193 } 194 195 /** Create BitSet from a bit string */ 196 private static BitSet createBitSet(String bitString) { 197 BitSet ret = new BitSet(bitString.length()); 198 for (int i = 0; i < bitString.length(); i++) 199 if (bitString.charAt(i) == '1') 200 ret.set(i); 201 return ret; 202 } 203 204 /** Load the file */ 205 @Override 206 public void load() throws Exception { 207 sLogger.debug("Reading XML data from " + iInputFile); 208 209 Document document = (new SAXReader()).read(iInputFile); 210 Element root = document.getRootElement(); 211 212 load(root); 213 } 214 215 public void load(Document document) { 216 Element root = document.getRootElement(); 217 218 if (getModel().getDistanceConflict() != null && root.element("travel-times") != null) 219 loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric()); 220 221 Progress.getInstance(getModel()).load(root, true); 222 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 223 224 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 225 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 226 227 if (root.element("offerings") != null) { 228 loadOfferings(root.element("offerings"), offeringTable, courseTable, null); 229 } 230 231 if (root.element("students") != null) { 232 loadStudents(root.element("students"), offeringTable, courseTable); 233 } 234 235 if (root.element("constraints") != null) 236 loadLinkedSections(root.element("constraints"), offeringTable); 237 } 238 239 /** 240 * Load data from the given XML root 241 * @param root document root 242 * @throws DocumentException 243 */ 244 protected void load(Element root) throws DocumentException { 245 sLogger.debug("Root element: " + root.getName()); 246 if (!"sectioning".equals(root.getName())) { 247 sLogger.error("Given XML file is not student sectioning problem."); 248 return; 249 } 250 251 if (iLoadOfferings && getModel().getDistanceConflict() != null && root.element("travel-times") != null) 252 loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric()); 253 254 Map<Long, Placement> timetable = null; 255 if (iTimetableFile != null) { 256 sLogger.info("Reading timetable from " + iTimetableFile + " ..."); 257 Document timetableDocument = (new SAXReader()).read(iTimetableFile); 258 Element timetableRoot = timetableDocument.getRootElement(); 259 if (!"timetable".equals(timetableRoot.getName())) { 260 sLogger.error("Given XML file is not course timetabling problem."); 261 return; 262 } 263 timetable = loadTimetable(timetableRoot); 264 } 265 266 Progress.getInstance(getModel()).load(root, true); 267 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 268 269 if (root.attributeValue("term") != null) 270 getModel().getProperties().setProperty("Data.Term", root.attributeValue("term")); 271 if (root.attributeValue("year") != null) 272 getModel().getProperties().setProperty("Data.Year", root.attributeValue("year")); 273 if (root.attributeValue("initiative") != null) 274 getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative")); 275 276 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 277 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 278 279 if (iLoadOfferings && root.element("offerings") != null) { 280 loadOfferings(root.element("offerings"), offeringTable, courseTable, timetable); 281 } else { 282 for (Offering offering : getModel().getOfferings()) { 283 offeringTable.put(new Long(offering.getId()), offering); 284 for (Course course : offering.getCourses()) { 285 courseTable.put(new Long(course.getId()), course); 286 } 287 } 288 } 289 290 if (iLoadStudents && root.element("students") != null) { 291 loadStudents(root.element("students"), offeringTable, courseTable); 292 } 293 294 if (iLoadOfferings && root.element("constraints") != null) 295 loadLinkedSections(root.element("constraints"), offeringTable); 296 297 sLogger.debug("Model successfully loaded."); 298 } 299 300 /** 301 * Load offerings 302 * @param offeringsEl offerings element 303 * @param offeringTable offering table 304 * @param courseTable course table 305 * @param timetable provided timetable (null if to be loaded from the given document) 306 */ 307 protected void loadOfferings(Element offeringsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, Map<Long, Placement> timetable) { 308 HashMap<Long, Config> configTable = new HashMap<Long, Config>(); 309 HashMap<Long, Subpart> subpartTable = new HashMap<Long, Subpart>(); 310 HashMap<Long, Section> sectionTable = new HashMap<Long, Section>(); 311 for (Iterator<?> i = offeringsEl.elementIterator("offering"); i.hasNext();) { 312 Element offeringEl = (Element) i.next(); 313 Offering offering = new Offering( 314 Long.parseLong(offeringEl.attributeValue("id")), 315 offeringEl.attributeValue("name", "O" + offeringEl.attributeValue("id"))); 316 offeringTable.put(new Long(offering.getId()), offering); 317 getModel().addOffering(offering); 318 319 for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) { 320 Element courseEl = (Element) j.next(); 321 Course course = loadCourse(courseEl, offering); 322 courseTable.put(new Long(course.getId()), course); 323 } 324 325 for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) { 326 Element configEl = (Element) j.next(); 327 Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable); 328 configTable.put(config.getId(), config); 329 } 330 331 for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) { 332 Element reservationEl = (Element)j.next(); 333 loadReservation(reservationEl, offering, configTable, sectionTable); 334 } 335 } 336 } 337 338 /** 339 * Load course 340 * @param courseEl course element 341 * @param offering parent offering 342 * @return loaded course 343 */ 344 protected Course loadCourse(Element courseEl, Offering offering) { 345 Course course = new Course( 346 Long.parseLong(courseEl.attributeValue("id")), 347 courseEl.attributeValue("subjectArea", ""), 348 courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")), 349 offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")), 350 Integer.parseInt(courseEl.attributeValue("projected", "0"))); 351 course.setCredit(courseEl.attributeValue("credit")); 352 return course; 353 } 354 355 /** 356 * Load config 357 * @param configEl config element 358 * @param offering parent offering 359 * @param subpartTable subpart table (of the offering) 360 * @param sectionTable section table (of the offering) 361 * @param timetable provided timetable 362 * @return loaded config 363 */ 364 protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 365 Config config = new Config 366 (Long.parseLong(configEl.attributeValue("id")), 367 Integer.parseInt(configEl.attributeValue("limit", "-1")), 368 configEl.attributeValue("name", "G" + configEl.attributeValue("id")), 369 offering); 370 Element imEl = configEl.element("instructional-method"); 371 if (imEl != null) { 372 config.setInstructionalMethodId(Long.parseLong(imEl.attributeValue("id"))); 373 config.setInstructionalMethodName(imEl.attributeValue("name", "M" + imEl.attributeValue("id"))); 374 } 375 for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) { 376 Element subpartEl = (Element) k.next(); 377 Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable); 378 subpartTable.put(new Long(subpart.getId()), subpart); 379 } 380 return config; 381 } 382 383 /** 384 * Load subpart 385 * @param subpartEl supart element 386 * @param config parent config 387 * @param subpartTable subpart table (of the offering) 388 * @param sectionTable section table (of the offering) 389 * @param timetable provided timetable 390 * @return loaded subpart 391 */ 392 protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 393 Subpart parentSubpart = null; 394 if (subpartEl.attributeValue("parent") != null) 395 parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent"))); 396 Subpart subpart = new Subpart( 397 Long.parseLong(subpartEl.attributeValue("id")), 398 subpartEl.attributeValue("itype"), 399 subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")), 400 config, 401 parentSubpart); 402 subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false"))); 403 subpart.setCredit(subpartEl.attributeValue("credit")); 404 405 for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) { 406 Element sectionEl = (Element) l.next(); 407 Section section = loadSection(sectionEl, subpart, sectionTable, timetable); 408 sectionTable.put(new Long(section.getId()), section); 409 } 410 411 return subpart; 412 } 413 414 /** 415 * Load section 416 * @param sectionEl section element 417 * @param subpart parent subpart 418 * @param sectionTable section table (of the offering) 419 * @param timetable provided timetable 420 * @return loaded section 421 */ 422 @SuppressWarnings("deprecation") 423 protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 424 Section parentSection = null; 425 if (sectionEl.attributeValue("parent") != null) 426 parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent"))); 427 Placement placement = null; 428 if (timetable != null) { 429 placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id"))); 430 } else { 431 TimeLocation time = null; 432 Element timeEl = sectionEl.element("time"); 433 if (timeEl != null) { 434 time = new TimeLocation( 435 Integer.parseInt(timeEl.attributeValue("days"), 2), 436 Integer.parseInt(timeEl.attributeValue("start")), 437 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 438 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 439 timeEl.attributeValue("datePatternName", ""), 440 createBitSet(timeEl.attributeValue("dates")), 441 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 442 if (timeEl.attributeValue("pattern") != null) 443 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 444 } 445 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 446 for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) { 447 Element roomEl = (Element) m.next(); 448 Double posX = null, posY = null; 449 if (roomEl.attributeValue("location") != null) { 450 String loc = roomEl.attributeValue("location"); 451 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 452 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 453 } 454 RoomLocation room = new RoomLocation( 455 Long.valueOf(roomEl.attributeValue("id")), 456 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 457 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 458 0, Integer.parseInt(roomEl.attributeValue("capacity")), 459 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 460 rooms.add(room); 461 } 462 placement = (time == null ? null : new Placement(null, time, rooms)); 463 } 464 465 List<Instructor> instructors = new ArrayList<Instructor>(); 466 for (Iterator<?> m = sectionEl.elementIterator("instructor"); m.hasNext(); ) { 467 Element instructorEl = (Element)m.next(); 468 instructors.add(new Instructor(Long.parseLong(instructorEl.attributeValue("id")), instructorEl.attributeValue("externalId"), instructorEl.attributeValue("name"), instructorEl.attributeValue("email"))); 469 } 470 if (instructors.isEmpty() && sectionEl.attributeValue("instructorIds") != null) 471 instructors = Instructor.toInstructors(sectionEl.attributeValue("instructorIds"), sectionEl.attributeValue("instructorNames")); 472 Section section = new Section( 473 Long.parseLong(sectionEl.attributeValue("id")), 474 Integer.parseInt(sectionEl.attributeValue("limit")), 475 sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")), 476 subpart, placement, instructors, parentSection); 477 478 section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0"))); 479 section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0"))); 480 section.setCancelled("true".equalsIgnoreCase(sectionEl.attributeValue("cancelled", "false"))); 481 for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) { 482 Element cNameEl = (Element)m.next(); 483 section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText()); 484 } 485 Element ignoreEl = sectionEl.element("no-conflicts"); 486 if (ignoreEl != null) { 487 for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); ) 488 section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id"))); 489 } 490 491 return section; 492 } 493 494 /** 495 * Load reservation 496 * @param reservationEl reservation element 497 * @param offering parent offering 498 * @param configTable config table (of the offering) 499 * @param sectionTable section table (of the offering) 500 * @return loaded reservation 501 */ 502 protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 503 Reservation r = null; 504 if ("individual".equals(reservationEl.attributeValue("type"))) { 505 Set<Long> studentIds = new HashSet<Long>(); 506 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 507 Element studentEl = (Element)k.next(); 508 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 509 } 510 r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 511 } else if ("group".equals(reservationEl.attributeValue("type"))) { 512 Set<Long> studentIds = new HashSet<Long>(); 513 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 514 Element studentEl = (Element)k.next(); 515 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 516 } 517 r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")), 518 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 519 offering, studentIds); 520 } else if ("curriculum".equals(reservationEl.attributeValue("type"))) { 521 List<String> classifications = new ArrayList<String>(); 522 for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) { 523 Element clasfEl = (Element)k.next(); 524 classifications.add(clasfEl.attributeValue("code")); 525 } 526 List<String> majors = new ArrayList<String>(); 527 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 528 Element majorEl = (Element)k.next(); 529 majors.add(majorEl.attributeValue("code")); 530 } 531 r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")), 532 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 533 offering, 534 reservationEl.attributeValue("area"), 535 classifications, majors); 536 } else if ("course".equals(reservationEl.attributeValue("type"))) { 537 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 538 for (Course course: offering.getCourses()) { 539 if (course.getId() == courseId) 540 r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course); 541 } 542 } else if ("dummy".equals(reservationEl.attributeValue("type"))) { 543 r = new DummyReservation(offering); 544 } else if ("override".equals(reservationEl.attributeValue("type"))) { 545 Set<Long> studentIds = new HashSet<Long>(); 546 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 547 Element studentEl = (Element)k.next(); 548 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 549 } 550 r = new ReservationOverride(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 551 } 552 if (r == null) { 553 sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type")); 554 return null; 555 } 556 r.setExpired("true".equals(reservationEl.attributeValue("expired", "false"))); 557 for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) { 558 Element configEl = (Element)k.next(); 559 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 560 } 561 for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) { 562 Element sectionEl = (Element)k.next(); 563 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id")))); 564 } 565 r.setPriority(Integer.parseInt(reservationEl.attributeValue("priority", String.valueOf(r.getPriority())))); 566 r.setMustBeUsed("true".equals(reservationEl.attributeValue("mustBeUsed", r.mustBeUsed() ? "true" : "false"))); 567 r.setAllowOverlap("true".equals(reservationEl.attributeValue("allowOverlap", r.isAllowOverlap() ? "true" : "false"))); 568 r.setCanAssignOverLimit("true".equals(reservationEl.attributeValue("canAssignOverLimit", r.canAssignOverLimit() ? "true" : "false"))); 569 return r; 570 } 571 572 /** 573 * Load given timetable 574 * @param timetableRoot document root in the course timetabling XML format 575 * @return loaded timetable (map class id: assigned placement) 576 */ 577 protected Map<Long, Placement> loadTimetable(Element timetableRoot) { 578 Map<Long, Placement> timetable = new HashMap<Long, Placement>(); 579 HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>(); 580 for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) { 581 Element roomEl = (Element)i.next(); 582 Long roomId = Long.valueOf(roomEl.attributeValue("id")); 583 Double posX = null, posY = null; 584 if (roomEl.attributeValue("location") != null) { 585 String loc = roomEl.attributeValue("location"); 586 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 587 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 588 } 589 RoomLocation room = new RoomLocation( 590 Long.valueOf(roomEl.attributeValue("id")), 591 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 592 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 593 0, Integer.parseInt(roomEl.attributeValue("capacity")), 594 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 595 rooms.put(roomId, room); 596 } 597 for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) { 598 Element classEl = (Element)i.next(); 599 Long classId = Long.valueOf(classEl.attributeValue("id")); 600 TimeLocation time = null; 601 Element timeEl = null; 602 for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) { 603 Element e = (Element)j.next(); 604 if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; } 605 } 606 if (timeEl != null) { 607 time = new TimeLocation( 608 Integer.parseInt(timeEl.attributeValue("days"), 2), 609 Integer.parseInt(timeEl.attributeValue("start")), 610 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 611 classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")), 612 classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")), 613 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 614 if (timeEl.attributeValue("pattern") != null) 615 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 616 } 617 List<RoomLocation> room = new ArrayList<RoomLocation>(); 618 for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) { 619 Element roomEl = (Element) j.next(); 620 if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue; 621 room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id")))); 622 } 623 Placement placement = (time == null ? null : new Placement(null, time, room)); 624 if (placement != null) 625 timetable.put(classId, placement); 626 } 627 return timetable; 628 } 629 630 /** 631 * Load travel times 632 * @param travelTimesEl travel-time element 633 * @param metric distance metric to be populated 634 */ 635 protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) { 636 for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) { 637 Element travelTimeEl = (Element)i.next(); 638 metric.addTravelTime( 639 Long.valueOf(travelTimeEl.attributeValue("id1")), 640 Long.valueOf(travelTimeEl.attributeValue("id2")), 641 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 642 } 643 } 644 645 /** 646 * Load linked sections 647 * @param constraintsEl constraints element 648 * @param offeringTable offering table 649 */ 650 protected void loadLinkedSections(Element constraintsEl, Map<Long, Offering> offeringTable) { 651 for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) { 652 Element linkedEl = (Element) i.next(); 653 List<Section> sections = new ArrayList<Section>(); 654 for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) { 655 Element sectionEl = (Element) j.next(); 656 Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering"))); 657 sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id")))); 658 } 659 getModel().addLinkedSections("true".equals(linkedEl.attributeValue("mustBeUsed", "false")), sections); 660 } 661 } 662 663 /** 664 * Load students 665 * @param studentsEl students element 666 * @param offeringTable offering table 667 * @param courseTable course table 668 */ 669 protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 670 List<Enrollment> bestEnrollments = new ArrayList<Enrollment>(); 671 List<Enrollment> currentEnrollments = new ArrayList<Enrollment>(); 672 for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) { 673 Element studentEl = (Element) i.next(); 674 Student student = loadStudent(studentEl); 675 if (iStudentFilter != null && !iStudentFilter.accept(student)) 676 continue; 677 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 678 Element requestEl = (Element) j.next(); 679 Request request = loadRequest(requestEl, student, offeringTable, courseTable); 680 if (request == null) continue; 681 682 Element initialEl = requestEl.element("initial"); 683 if (iLoadInitial && initialEl != null) { 684 Enrollment enrollment = loadEnrollment(initialEl, request); 685 if (enrollment != null) 686 request.setInitialAssignment(enrollment); 687 } 688 Element currentEl = requestEl.element("current"); 689 if (iLoadCurrent && currentEl != null) { 690 Enrollment enrollment = loadEnrollment(currentEl, request); 691 if (enrollment != null) 692 currentEnrollments.add(enrollment); 693 } 694 Element bestEl = requestEl.element("best"); 695 if (iLoadBest && bestEl != null) { 696 Enrollment enrollment = loadEnrollment(bestEl, request); 697 if (enrollment != null) 698 bestEnrollments.add(enrollment); 699 } 700 } 701 getModel().addStudent(student); 702 } 703 704 if (!bestEnrollments.isEmpty()) { 705 // Enrollments with a reservation must go first 706 for (Enrollment enrollment : bestEnrollments) { 707 if (enrollment.getReservation() == null) continue; 708 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 709 if (conflicts.isEmpty()) 710 getAssignment().assign(0, enrollment); 711 else 712 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 713 } 714 for (Enrollment enrollment : bestEnrollments) { 715 if (enrollment.getReservation() != null) continue; 716 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 717 if (conflicts.isEmpty()) 718 getAssignment().assign(0, enrollment); 719 else 720 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 721 } 722 getModel().saveBest(getAssignment()); 723 } 724 725 if (!currentEnrollments.isEmpty()) { 726 for (Request request : getModel().variables()) 727 getAssignment().unassign(0, request); 728 // Enrollments with a reservation must go first 729 for (Enrollment enrollment : currentEnrollments) { 730 if (enrollment.getReservation() == null) continue; 731 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 732 if (conflicts.isEmpty()) 733 getAssignment().assign(0, enrollment); 734 else 735 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 736 } 737 for (Enrollment enrollment : currentEnrollments) { 738 if (enrollment.getReservation() != null) continue; 739 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment); 740 if (conflicts.isEmpty()) 741 getAssignment().assign(0, enrollment); 742 else 743 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 744 } 745 } 746 } 747 748 /** 749 * Load student 750 * @param studentEl student element 751 * @return loaded student 752 */ 753 protected Student loadStudent(Element studentEl) { 754 Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy"))); 755 student.setExternalId(studentEl.attributeValue("externalId")); 756 student.setName(studentEl.attributeValue("name")); 757 student.setStatus(studentEl.attributeValue("status")); 758 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 759 Element requestEl = (Element) j.next(); 760 if ("classification".equals(requestEl.getName())) { 761 student.getAcademicAreaClasiffications().add( 762 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"))); 763 } else if ("major".equals(requestEl.getName())) { 764 student.getMajors().add( 765 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"))); 766 } else if ("minor".equals(requestEl.getName())) { 767 student.getMinors().add( 768 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"))); 769 } 770 } 771 return student; 772 } 773 774 /** 775 * Load request 776 * @param requestEl request element 777 * @param student parent student 778 * @param offeringTable offering table 779 * @param courseTable course table 780 * @return loaded request 781 */ 782 protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 783 if ("freeTime".equals(requestEl.getName())) { 784 return loadFreeTime(requestEl, student); 785 } else if ("course".equals(requestEl.getName())) { 786 return loadCourseRequest(requestEl, student, offeringTable, courseTable); 787 } else { 788 return null; 789 } 790 } 791 792 /** 793 * Load free time request 794 * @param requestEl request element 795 * @param student parent student 796 * @return loaded free time request 797 */ 798 public FreeTimeRequest loadFreeTime(Element requestEl, Student student) { 799 TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2), 800 Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl 801 .attributeValue("length")), 0, 0, 802 requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl 803 .attributeValue("datePattern")), "", createBitSet(requestEl 804 .attributeValue("dates")), 0); 805 FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")), 806 Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl 807 .attributeValue("alternative")), student, time); 808 if (requestEl.attributeValue("weight") != null) 809 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 810 return request; 811 } 812 813 /** 814 * Load course request 815 * @param requestEl request element 816 * @param student parent student 817 * @param offeringTable offering table 818 * @param courseTable course table 819 * @return loaded course request 820 */ 821 public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 822 List<Course> courses = new ArrayList<Course>(); 823 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 824 for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();) 825 courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course")))); 826 Long timeStamp = null; 827 if (requestEl.attributeValue("timeStamp") != null) 828 timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp")); 829 CourseRequest courseRequest = new CourseRequest( 830 Long.parseLong(requestEl.attributeValue("id")), 831 Integer.parseInt(requestEl.attributeValue("priority")), 832 "true".equals(requestEl.attributeValue("alternative")), 833 student, courses, 834 "true".equals(requestEl.attributeValue("waitlist", "false")), timeStamp); 835 if (requestEl.attributeValue("weight") != null) 836 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 837 for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) { 838 Element choiceEl = (Element) k.next(); 839 courseRequest.getWaitlistedChoices().add( 840 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 841 } 842 for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) { 843 Element choiceEl = (Element) k.next(); 844 courseRequest.getSelectedChoices().add( 845 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 846 } 847 groups: for (Iterator<?> k = requestEl.elementIterator("group"); k.hasNext();) { 848 Element groupEl = (Element) k.next(); 849 long gid = Long.parseLong(groupEl.attributeValue("id")); 850 String gname = groupEl.attributeValue("name", "g" + gid); 851 Course course = courseTable.get(Long.valueOf(groupEl.attributeValue("course"))); 852 for (RequestGroup g: course.getRequestGroups()) { 853 if (g.getId() == gid) { 854 courseRequest.addRequestGroup(g); 855 continue groups; 856 } 857 } 858 courseRequest.addRequestGroup(new RequestGroup(gid, gname, course)); 859 } 860 return courseRequest; 861 } 862 863 /** 864 * Load enrollment 865 * @param enrollmentEl enrollment element (current, best, or initial) 866 * @param request parent request 867 * @return loaded enrollment 868 */ 869 protected Enrollment loadEnrollment(Element enrollmentEl, Request request) { 870 if (request instanceof CourseRequest) { 871 CourseRequest courseRequest = (CourseRequest) request; 872 HashSet<Section> sections = new HashSet<Section>(); 873 for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) { 874 Element sectionEl = (Element) k.next(); 875 Section section = courseRequest.getSection(Long.parseLong(sectionEl 876 .attributeValue("id"))); 877 sections.add(section); 878 } 879 Reservation reservation = null; 880 if (enrollmentEl.attributeValue("reservation", null) != null) { 881 long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation")); 882 for (Course course: courseRequest.getCourses()) 883 for (Reservation r: course.getOffering().getReservations()) 884 if (r.getId() == reservationId) { reservation = r; break; } 885 } 886 if (!sections.isEmpty()) 887 return courseRequest.createEnrollment(sections, reservation); 888 } else if (request instanceof FreeTimeRequest) { 889 return ((FreeTimeRequest)request).createEnrollment(); 890 } 891 return null; 892 } 893 894 895}