001package org.cpsolver.studentsct; 002 003import java.io.File; 004import java.io.FileOutputStream; 005import java.io.IOException; 006import java.math.RoundingMode; 007import java.text.DecimalFormat; 008import java.text.DecimalFormatSymbols; 009import java.util.BitSet; 010import java.util.Date; 011import java.util.Locale; 012import java.util.Map; 013import java.util.Set; 014import java.util.TreeSet; 015 016import org.cpsolver.coursett.IdConvertor; 017import org.cpsolver.coursett.model.RoomLocation; 018import org.cpsolver.coursett.model.TimeLocation; 019import org.cpsolver.ifs.solver.Solver; 020import org.cpsolver.ifs.util.Progress; 021import org.cpsolver.studentsct.constraint.LinkedSections; 022import org.cpsolver.studentsct.model.AcademicAreaCode; 023import org.cpsolver.studentsct.model.Choice; 024import org.cpsolver.studentsct.model.Config; 025import org.cpsolver.studentsct.model.Course; 026import org.cpsolver.studentsct.model.CourseRequest; 027import org.cpsolver.studentsct.model.Enrollment; 028import org.cpsolver.studentsct.model.FreeTimeRequest; 029import org.cpsolver.studentsct.model.Instructor; 030import org.cpsolver.studentsct.model.Offering; 031import org.cpsolver.studentsct.model.Request; 032import org.cpsolver.studentsct.model.RequestGroup; 033import org.cpsolver.studentsct.model.Section; 034import org.cpsolver.studentsct.model.Student; 035import org.cpsolver.studentsct.model.Subpart; 036import org.cpsolver.studentsct.reservation.CourseReservation; 037import org.cpsolver.studentsct.reservation.CurriculumReservation; 038import org.cpsolver.studentsct.reservation.DummyReservation; 039import org.cpsolver.studentsct.reservation.GroupReservation; 040import org.cpsolver.studentsct.reservation.IndividualReservation; 041import org.cpsolver.studentsct.reservation.Reservation; 042import org.cpsolver.studentsct.reservation.ReservationOverride; 043import org.dom4j.Document; 044import org.dom4j.DocumentHelper; 045import org.dom4j.Element; 046import org.dom4j.io.OutputFormat; 047import org.dom4j.io.XMLWriter; 048 049 050/** 051 * Save student sectioning solution into an XML file. 052 * 053 * <br> 054 * <br> 055 * Parameters: 056 * <table border='1' summary='Related Solver Parameters'> 057 * <tr> 058 * <th>Parameter</th> 059 * <th>Type</th> 060 * <th>Comment</th> 061 * </tr> 062 * <tr> 063 * <td>General.Output</td> 064 * <td>{@link String}</td> 065 * <td>Folder with the output solution in XML format (solution.xml)</td> 066 * </tr> 067 * <tr> 068 * <td>Xml.ConvertIds</td> 069 * <td>{@link Boolean}</td> 070 * <td>If true, ids are converted (to be able to make input data public)</td> 071 * </tr> 072 * <tr> 073 * <td>Xml.ShowNames</td> 074 * <td>{@link Boolean}</td> 075 * <td>If false, names are not exported (to be able to make input data public)</td> 076 * </tr> 077 * <tr> 078 * <td>Xml.SaveBest</td> 079 * <td>{@link Boolean}</td> 080 * <td>If true, best solution is saved.</td> 081 * </tr> 082 * <tr> 083 * <td>Xml.SaveInitial</td> 084 * <td>{@link Boolean}</td> 085 * <td>If true, initial solution is saved.</td> 086 * </tr> 087 * <tr> 088 * <td>Xml.SaveCurrent</td> 089 * <td>{@link Boolean}</td> 090 * <td>If true, current solution is saved.</td> 091 * </tr> 092 * <tr> 093 * <td>Xml.SaveOnlineSectioningInfo</td> 094 * <td>{@link Boolean}</td> 095 * <td>If true, save online sectioning info (i.e., expected and held space of 096 * each section)</td> 097 * </tr> 098 * <tr> 099 * <td>Xml.SaveStudentInfo</td> 100 * <td>{@link Boolean}</td> 101 * <td>If true, save student information (i.e., academic area classification, 102 * major, minor)</td> 103 * </tr> 104 * </table> 105 * <br> 106 * <br> 107 * Usage: 108 * <pre><code> 109 * new StudentSectioningXMLSaver(solver).save(new File("solution.xml")); 110 * </code></pre> 111 * 112 * @version StudentSct 1.3 (Student Sectioning)<br> 113 * Copyright (C) 2007 - 2014 Tomas Muller<br> 114 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 115 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 116 * <br> 117 * This library is free software; you can redistribute it and/or modify 118 * it under the terms of the GNU Lesser General Public License as 119 * published by the Free Software Foundation; either version 3 of the 120 * License, or (at your option) any later version. <br> 121 * <br> 122 * This library is distributed in the hope that it will be useful, but 123 * WITHOUT ANY WARRANTY; without even the implied warranty of 124 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 125 * Lesser General Public License for more details. <br> 126 * <br> 127 * You should have received a copy of the GNU Lesser General Public 128 * License along with this library; if not see 129 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 130 */ 131 132public class StudentSectioningXMLSaver extends StudentSectioningSaver { 133 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(StudentSectioningXMLSaver.class); 134 private static DecimalFormat[] sDF = { new DecimalFormat(""), new DecimalFormat("0"), new DecimalFormat("00"), 135 new DecimalFormat("000"), new DecimalFormat("0000"), new DecimalFormat("00000"), 136 new DecimalFormat("000000"), new DecimalFormat("0000000") }; 137 private static DecimalFormat sStudentWeightFormat = new DecimalFormat("0.0000", new DecimalFormatSymbols(Locale.US)); 138 private File iOutputFolder = null; 139 140 private boolean iSaveBest = false; 141 private boolean iSaveInitial = false; 142 private boolean iSaveCurrent = false; 143 private boolean iSaveOnlineSectioningInfo = false; 144 private boolean iSaveStudentInfo = true; 145 146 private boolean iConvertIds = false; 147 private boolean iShowNames = false; 148 149 static { 150 sStudentWeightFormat.setRoundingMode(RoundingMode.DOWN); 151 } 152 153 /** 154 * Constructor 155 * 156 * @param solver 157 * student sectioning solver 158 */ 159 public StudentSectioningXMLSaver(Solver<Request, Enrollment> solver) { 160 super(solver); 161 iOutputFolder = new File(getModel().getProperties().getProperty("General.Output", 162 "." + File.separator + "output")); 163 iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", true); 164 iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", true); 165 iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", false); 166 iSaveOnlineSectioningInfo = getModel().getProperties().getPropertyBoolean("Xml.SaveOnlineSectioningInfo", true); 167 iSaveStudentInfo = getModel().getProperties().getPropertyBoolean("Xml.SaveStudentInfo", true); 168 iShowNames = getModel().getProperties().getPropertyBoolean("Xml.ShowNames", true); 169 iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds", false); 170 } 171 172 /** Convert bitset to a bit string */ 173 private static String bitset2string(BitSet b) { 174 StringBuffer sb = new StringBuffer(); 175 for (int i = 0; i < b.length(); i++) 176 sb.append(b.get(i) ? "1" : "0"); 177 return sb.toString(); 178 } 179 180 /** Generate id for given object with the given id */ 181 private String getId(String type, String id) { 182 if (!iConvertIds) 183 return id.toString(); 184 return IdConvertor.getInstance().convert(type, id); 185 } 186 187 /** Generate id for given object with the given id */ 188 private String getId(String type, Number id) { 189 return getId(type, id.toString()); 190 } 191 192 /** Generate id for given object with the given id */ 193 private String getId(String type, long id) { 194 return getId(type, String.valueOf(id)); 195 } 196 197 /** Save an XML file */ 198 @Override 199 public void save() throws Exception { 200 save(null); 201 } 202 203 /** 204 * Save an XML file 205 * 206 * @param outFile 207 * output file 208 * @throws Exception thrown when the save fails 209 */ 210 public void save(File outFile) throws Exception { 211 if (outFile == null) { 212 outFile = new File(iOutputFolder, "solution.xml"); 213 } else if (outFile.getParentFile() != null) { 214 outFile.getParentFile().mkdirs(); 215 } 216 sLogger.debug("Writting XML data to:" + outFile); 217 218 Document document = DocumentHelper.createDocument(); 219 document.addComment("Student Sectioning"); 220 221 populate(document); 222 223 FileOutputStream fos = null; 224 try { 225 fos = new FileOutputStream(outFile); 226 (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(document); 227 fos.flush(); 228 fos.close(); 229 fos = null; 230 } finally { 231 try { 232 if (fos != null) 233 fos.close(); 234 } catch (IOException e) { 235 } 236 } 237 238 if (iConvertIds) 239 IdConvertor.getInstance().save(); 240 } 241 242 public Document saveDocument() { 243 Document document = DocumentHelper.createDocument(); 244 document.addComment("Student Sectioning"); 245 246 populate(document); 247 248 return document; 249 } 250 251 /** 252 * Fill in all the data into the given document 253 * @param document document to be populated 254 */ 255 protected void populate(Document document) { 256 if (iSaveCurrent || iSaveBest) { 257 StringBuffer comments = new StringBuffer("Solution Info:\n"); 258 Map<String, String> solutionInfo = (getSolution() == null ? getModel().getExtendedInfo(getAssignment()) : getSolution().getExtendedInfo()); 259 for (String key : new TreeSet<String>(solutionInfo.keySet())) { 260 String value = solutionInfo.get(key); 261 comments.append(" " + key + ": " + value + "\n"); 262 } 263 document.addComment(comments.toString()); 264 } 265 266 Element root = document.addElement("sectioning"); 267 root.addAttribute("version", "1.0"); 268 root.addAttribute("initiative", getModel().getProperties().getProperty("Data.Initiative")); 269 root.addAttribute("term", getModel().getProperties().getProperty("Data.Term")); 270 root.addAttribute("year", getModel().getProperties().getProperty("Data.Year")); 271 root.addAttribute("created", String.valueOf(new Date())); 272 273 saveOfferings(root); 274 275 saveStudents(root); 276 277 saveLinkedSections(root); 278 279 saveTravelTimes(root); 280 281 if (iShowNames) { 282 Progress.getInstance(getModel()).save(root); 283 } 284 } 285 286 /** 287 * Save offerings 288 * @param root document root 289 */ 290 protected void saveOfferings(Element root) { 291 Element offeringsEl = root.addElement("offerings"); 292 for (Offering offering : getModel().getOfferings()) { 293 Element offeringEl = offeringsEl.addElement("offering"); 294 saveOffering(offeringEl, offering); 295 saveReservations(offeringEl, offering); 296 } 297 } 298 299 /** 300 * Save given offering 301 * @param offeringEl offering element to be populated 302 * @param offering offering to be saved 303 */ 304 protected void saveOffering(Element offeringEl, Offering offering) { 305 offeringEl.addAttribute("id", getId("offering", offering.getId())); 306 if (iShowNames) 307 offeringEl.addAttribute("name", offering.getName()); 308 for (Course course : offering.getCourses()) { 309 Element courseEl = offeringEl.addElement("course"); 310 saveCourse(courseEl, course); 311 } 312 for (Config config : offering.getConfigs()) { 313 Element configEl = offeringEl.addElement("config"); 314 saveConfig(configEl, config); 315 } 316 } 317 318 /** 319 * Save given course 320 * @param courseEl course element to be populated 321 * @param course course to be saved 322 */ 323 protected void saveCourse(Element courseEl, Course course) { 324 courseEl.addAttribute("id", getId("course", course.getId())); 325 if (iShowNames) 326 courseEl.addAttribute("subjectArea", course.getSubjectArea()); 327 if (iShowNames) 328 courseEl.addAttribute("courseNbr", course.getCourseNumber()); 329 if (iShowNames && course.getLimit() >= 0) 330 courseEl.addAttribute("limit", String.valueOf(course.getLimit())); 331 if (iShowNames && course.getProjected() != 0) 332 courseEl.addAttribute("projected", String.valueOf(course.getProjected())); 333 if (iShowNames && course.getCredit() != null) 334 courseEl.addAttribute("credit", course.getCredit()); 335 } 336 337 /** 338 * Save given config 339 * @param configEl config element to be populated 340 * @param config config to be saved 341 */ 342 protected void saveConfig(Element configEl, Config config) { 343 configEl.addAttribute("id", getId("config", config.getId())); 344 if (config.getLimit() >= 0) 345 configEl.addAttribute("limit", String.valueOf(config.getLimit())); 346 if (iShowNames) 347 configEl.addAttribute("name", config.getName()); 348 for (Subpart subpart : config.getSubparts()) { 349 Element subpartEl = configEl.addElement("subpart"); 350 saveSubpart(subpartEl, subpart); 351 } 352 if (config.getInstructionalMethodId() != null) { 353 Element imEl = configEl.addElement("instructional-method"); 354 imEl.addAttribute("id", getId("instructional-method", config.getInstructionalMethodId())); 355 if (iShowNames && config.getInstructionalMethodName() != null) 356 imEl.addAttribute("name", config.getInstructionalMethodName()); 357 } 358 } 359 360 /** 361 * Save scheduling subpart 362 * @param subpartEl subpart element to be populated 363 * @param subpart subpart to be saved 364 */ 365 protected void saveSubpart(Element subpartEl, Subpart subpart) { 366 subpartEl.addAttribute("id", getId("subpart", subpart.getId())); 367 subpartEl.addAttribute("itype", subpart.getInstructionalType()); 368 if (subpart.getParent() != null) 369 subpartEl.addAttribute("parent", getId("subpart", subpart.getParent().getId())); 370 if (iShowNames) { 371 subpartEl.addAttribute("name", subpart.getName()); 372 if (subpart.getCredit() != null) 373 subpartEl.addAttribute("credit", subpart.getCredit()); 374 } 375 if (subpart.isAllowOverlap()) 376 subpartEl.addAttribute("allowOverlap", "true"); 377 for (Section section : subpart.getSections()) { 378 Element sectionEl = subpartEl.addElement("section"); 379 saveSection(sectionEl, section); 380 } 381 } 382 383 /** 384 * Save section 385 * @param sectionEl section element to be populated 386 * @param section section to be saved 387 */ 388 protected void saveSection(Element sectionEl, Section section) { 389 sectionEl.addAttribute("id", getId("section", section.getId())); 390 sectionEl.addAttribute("limit", String.valueOf(section.getLimit())); 391 if (section.isCancelled()) 392 sectionEl.addAttribute("cancelled", "true"); 393 if (section.getNameByCourse() != null) 394 for (Map.Entry<Long, String> entry: section.getNameByCourse().entrySet()) 395 sectionEl.addElement("cname").addAttribute("id", entry.getKey().toString()).setText(entry.getValue()); 396 if (section.getParent() != null) 397 sectionEl.addAttribute("parent", getId("section", section.getParent().getId())); 398 if (section.hasInstructors()) { 399 for (Instructor instructor: section.getInstructors()) { 400 Element instructorEl = sectionEl.addElement("instructor"); 401 instructorEl.addAttribute("id", getId("instructor", instructor.getId())); 402 if (iShowNames && instructor.getName() != null) 403 instructorEl.addAttribute("name", instructor.getName()); 404 if (iShowNames && instructor.getExternalId() != null) 405 instructorEl.addAttribute("externalId", instructor.getExternalId()); 406 if (iShowNames && instructor.getEmail() != null) 407 instructorEl.addAttribute("email", instructor.getExternalId()); 408 } 409 } 410 if (iShowNames) 411 sectionEl.addAttribute("name", section.getName()); 412 if (section.getPlacement() != null) { 413 TimeLocation tl = section.getPlacement().getTimeLocation(); 414 if (tl != null) { 415 Element timeLocationEl = sectionEl.addElement("time"); 416 timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer 417 .toBinaryString(tl.getDayCode())))); 418 timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 419 timeLocationEl.addAttribute("length", String.valueOf(tl.getLength())); 420 if (tl.getBreakTime() != 0) 421 timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 422 if (iShowNames && tl.getTimePatternId() != null) 423 timeLocationEl.addAttribute("pattern", getId("timePattern", tl.getTimePatternId())); 424 if (iShowNames && tl.getDatePatternId() != null) 425 timeLocationEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 426 if (iShowNames && tl.getDatePatternName() != null 427 && tl.getDatePatternName().length() > 0) 428 timeLocationEl.addAttribute("datePatternName", tl.getDatePatternName()); 429 timeLocationEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 430 if (iShowNames) 431 timeLocationEl.setText(tl.getLongName(true)); 432 } 433 for (RoomLocation rl : section.getRooms()) { 434 Element roomLocationEl = sectionEl.addElement("room"); 435 roomLocationEl.addAttribute("id", getId("room", rl.getId())); 436 if (iShowNames && rl.getBuildingId() != null) 437 roomLocationEl.addAttribute("building", getId("building", rl.getBuildingId())); 438 if (iShowNames && rl.getName() != null) 439 roomLocationEl.addAttribute("name", rl.getName()); 440 roomLocationEl.addAttribute("capacity", String.valueOf(rl.getRoomSize())); 441 if (rl.getPosX() != null && rl.getPosY() != null) 442 roomLocationEl.addAttribute("location", rl.getPosX() + "," + rl.getPosY()); 443 if (rl.getIgnoreTooFar()) 444 roomLocationEl.addAttribute("ignoreTooFar", "true"); 445 } 446 } 447 if (iSaveOnlineSectioningInfo) { 448 if (section.getSpaceHeld() != 0.0) 449 sectionEl.addAttribute("hold", sStudentWeightFormat.format(section.getSpaceHeld())); 450 if (section.getSpaceExpected() != 0.0) 451 sectionEl.addAttribute("expect", sStudentWeightFormat 452 .format(section.getSpaceExpected())); 453 } 454 if (section.getIgnoreConflictWithSectionIds() != null && !section.getIgnoreConflictWithSectionIds().isEmpty()) { 455 Element ignoreEl = sectionEl.addElement("no-conflicts"); 456 for (Long sectionId: section.getIgnoreConflictWithSectionIds()) 457 ignoreEl.addElement("section").addAttribute("id", getId("section", sectionId)); 458 } 459 } 460 461 /** 462 * Save reservations of the given offering 463 * @param offeringEl offering element to be populated with reservations 464 * @param offering offering which reservations are to be saved 465 */ 466 protected void saveReservations(Element offeringEl, Offering offering) { 467 if (!offering.getReservations().isEmpty()) { 468 for (Reservation r: offering.getReservations()) { 469 saveReservation(offeringEl.addElement("reservation"), r); 470 } 471 } 472 } 473 474 /** 475 * Save reservation 476 * @param reservationEl reservation element to be populated 477 * @param reservation reservation to be saved 478 */ 479 protected void saveReservation(Element reservationEl, Reservation reservation) { 480 reservationEl.addAttribute("id", getId("reservation", reservation.getId())); 481 reservationEl.addAttribute("expired", reservation.isExpired() ? "true" : "false"); 482 if (reservation instanceof GroupReservation) { 483 GroupReservation gr = (GroupReservation)reservation; 484 reservationEl.addAttribute("type", "group"); 485 for (Long studentId: gr.getStudentIds()) 486 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 487 if (gr.getReservationLimit() >= 0.0) 488 reservationEl.addAttribute("limit", String.valueOf(gr.getReservationLimit())); 489 } else if (reservation instanceof ReservationOverride) { 490 reservationEl.addAttribute("type", "override"); 491 ReservationOverride o = (ReservationOverride)reservation; 492 for (Long studentId: o.getStudentIds()) 493 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 494 } else if (reservation instanceof IndividualReservation) { 495 reservationEl.addAttribute("type", "individual"); 496 for (Long studentId: ((IndividualReservation)reservation).getStudentIds()) 497 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 498 } else if (reservation instanceof CurriculumReservation) { 499 reservationEl.addAttribute("type", "curriculum"); 500 CurriculumReservation cr = (CurriculumReservation)reservation; 501 if (cr.getReservationLimit() >= 0.0) 502 reservationEl.addAttribute("limit", String.valueOf(cr.getReservationLimit())); 503 reservationEl.addAttribute("area", cr.getAcademicArea()); 504 for (String clasf: cr.getClassifications()) 505 reservationEl.addElement("classification").addAttribute("code", clasf); 506 for (String major: cr.getMajors()) 507 reservationEl.addElement("major").addAttribute("code", major); 508 } else if (reservation instanceof CourseReservation) { 509 reservationEl.addAttribute("type", "course"); 510 CourseReservation cr = (CourseReservation)reservation; 511 reservationEl.addAttribute("course", getId("course",cr.getCourse().getId())); 512 } else if (reservation instanceof DummyReservation) { 513 reservationEl.addAttribute("type", "dummy"); 514 } 515 reservationEl.addAttribute("priority", String.valueOf(reservation.getPriority())); 516 reservationEl.addAttribute("mustBeUsed", reservation.mustBeUsed() ? "true" : "false"); 517 reservationEl.addAttribute("allowOverlap", reservation.isAllowOverlap() ? "true" : "false"); 518 reservationEl.addAttribute("canAssignOverLimit", reservation.canAssignOverLimit() ? "true" : "false"); 519 for (Config config: reservation.getConfigs()) 520 reservationEl.addElement("config").addAttribute("id", getId("config", config.getId())); 521 for (Map.Entry<Subpart, Set<Section>> entry: reservation.getSections().entrySet()) { 522 for (Section section: entry.getValue()) { 523 reservationEl.addElement("section").addAttribute("id", getId("section", section.getId())); 524 } 525 } 526 } 527 528 /** 529 * Save students 530 * @param root document root 531 */ 532 protected void saveStudents(Element root) { 533 Element studentsEl = root.addElement("students"); 534 for (Student student : getModel().getStudents()) { 535 Element studentEl = studentsEl.addElement("student"); 536 saveStudent(studentEl, student); 537 for (Request request : student.getRequests()) { 538 saveRequest(studentEl, request); 539 } 540 } 541 } 542 543 /** 544 * Save student 545 * @param studentEl student element to be populated 546 * @param student student to be saved 547 */ 548 protected void saveStudent(Element studentEl, Student student) { 549 studentEl.addAttribute("id", getId("student", student.getId())); 550 if (iShowNames) { 551 if (student.getExternalId() != null && !student.getExternalId().isEmpty()) 552 studentEl.addAttribute("externalId", student.getExternalId()); 553 if (student.getName() != null && !student.getName().isEmpty()) 554 studentEl.addAttribute("name", student.getName()); 555 if (student.getStatus() != null && !student.getStatus().isEmpty()) 556 studentEl.addAttribute("status", student.getStatus()); 557 } 558 if (student.isDummy()) 559 studentEl.addAttribute("dummy", "true"); 560 if (iSaveStudentInfo) { 561 for (AcademicAreaCode aac : student.getAcademicAreaClasiffications()) { 562 Element aacEl = studentEl.addElement("classification"); 563 if (aac.getArea() != null) 564 aacEl.addAttribute("area", aac.getArea()); 565 if (aac.getCode() != null) 566 aacEl.addAttribute("code", aac.getCode()); 567 } 568 for (AcademicAreaCode aac : student.getMajors()) { 569 Element aacEl = studentEl.addElement("major"); 570 if (aac.getArea() != null) 571 aacEl.addAttribute("area", aac.getArea()); 572 if (aac.getCode() != null) 573 aacEl.addAttribute("code", aac.getCode()); 574 } 575 for (AcademicAreaCode aac : student.getMinors()) { 576 Element aacEl = studentEl.addElement("minor"); 577 if (aac.getArea() != null) 578 aacEl.addAttribute("area", aac.getArea()); 579 if (aac.getCode() != null) 580 aacEl.addAttribute("code", aac.getCode()); 581 } 582 } 583 } 584 585 /** 586 * Save request 587 * @param studentEl student element to be populated 588 * @param request request to be saved 589 */ 590 protected void saveRequest(Element studentEl, Request request) { 591 if (request instanceof FreeTimeRequest) { 592 saveFreeTimeRequest(studentEl.addElement("freeTime"), (FreeTimeRequest) request); 593 } else if (request instanceof CourseRequest) { 594 saveCourseRequest(studentEl.addElement("course"), (CourseRequest) request); 595 } 596 } 597 598 /** 599 * Save free time request 600 * @param requestEl request element to be populated 601 * @param request free time request to be saved 602 */ 603 protected void saveFreeTimeRequest(Element requestEl, FreeTimeRequest request) { 604 requestEl.addAttribute("id", getId("request", request.getId())); 605 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 606 if (request.isAlternative()) 607 requestEl.addAttribute("alternative", "true"); 608 if (request.getWeight() != 1.0) 609 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 610 TimeLocation tl = request.getTime(); 611 if (tl != null) { 612 requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl 613 .getDayCode())))); 614 requestEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 615 requestEl.addAttribute("length", String.valueOf(tl.getLength())); 616 if (iShowNames && tl.getDatePatternId() != null) 617 requestEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 618 requestEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 619 if (iShowNames) 620 requestEl.setText(tl.getLongName(true)); 621 } 622 if (iSaveInitial && request.getInitialAssignment() != null) { 623 requestEl.addElement("initial"); 624 } 625 if (iSaveCurrent && getAssignment().getValue(request) != null) { 626 requestEl.addElement("current"); 627 } 628 if (iSaveBest && request.getBestAssignment() != null) { 629 requestEl.addElement("best"); 630 } 631 } 632 633 /** 634 * Save course request 635 * @param requestEl request element to be populated 636 * @param request course request to be saved 637 */ 638 protected void saveCourseRequest(Element requestEl, CourseRequest request) { 639 requestEl.addAttribute("id", getId("request", request.getId())); 640 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 641 if (request.isAlternative()) 642 requestEl.addAttribute("alternative", "true"); 643 if (request.getWeight() != 1.0) 644 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 645 requestEl.addAttribute("waitlist", request.isWaitlist() ? "true" : "false"); 646 if (request.getTimeStamp() != null) 647 requestEl.addAttribute("timeStamp", request.getTimeStamp().toString()); 648 boolean first = true; 649 for (Course course : request.getCourses()) { 650 if (first) 651 requestEl.addAttribute("course", getId("course", course.getId())); 652 else 653 requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId())); 654 first = false; 655 } 656 for (Choice choice : request.getWaitlistedChoices()) { 657 Element choiceEl = requestEl.addElement("waitlisted"); 658 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 659 choiceEl.setText(choice.getId()); 660 } 661 for (Choice choice : request.getSelectedChoices()) { 662 Element choiceEl = requestEl.addElement("selected"); 663 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 664 choiceEl.setText(choice.getId()); 665 } 666 if (iSaveInitial && request.getInitialAssignment() != null) { 667 saveEnrollment(requestEl.addElement("initial"), request.getInitialAssignment()); 668 } 669 if (iSaveCurrent && getAssignment().getValue(request) != null) { 670 saveEnrollment(requestEl.addElement("current"), getAssignment().getValue(request)); 671 } 672 if (iSaveBest && request.getBestAssignment() != null) { 673 saveEnrollment(requestEl.addElement("best"), request.getBestAssignment()); 674 } 675 for (RequestGroup g: request.getRequestGroups()) { 676 Element groupEl = requestEl.addElement("group").addAttribute("id", getId("group", g.getId())).addAttribute("course", getId("course", g.getCourse().getId())); 677 if (iShowNames) 678 groupEl.addAttribute("name", g.getName()); 679 } 680 } 681 682 /** 683 * Save enrollment 684 * @param assignmentEl assignment element to be populated 685 * @param enrollment enrollment to be saved 686 */ 687 protected void saveEnrollment(Element assignmentEl, Enrollment enrollment) { 688 if (enrollment.getReservation() != null) 689 assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId())); 690 for (Section section : enrollment.getSections()) { 691 Element sectionEl = assignmentEl.addElement("section").addAttribute("id", 692 getId("section", section.getId())); 693 if (iShowNames) 694 sectionEl.setText(section.getName() + " " + 695 (section.getTime() == null ? " Arr Hrs" : " " + section.getTime().getLongName(true)) + 696 (section.getNrRooms() == 0 ? "" : " " + section.getPlacement().getRoomName(",")) + 697 (section.hasInstructors() ? " " + section.getInstructorNames(",") : "")); 698 } 699 } 700 701 /** 702 * Save linked sections 703 * @param root document root 704 */ 705 protected void saveLinkedSections(Element root) { 706 Element constrainstEl = root.addElement("constraints"); 707 for (LinkedSections linkedSections: getModel().getLinkedSections()) { 708 Element linkEl = constrainstEl.addElement("linked-sections"); 709 linkEl.addAttribute("mustBeUsed", linkedSections.isMustBeUsed() ? "true" : "false"); 710 for (Offering offering: linkedSections.getOfferings()) 711 for (Subpart subpart: linkedSections.getSubparts(offering)) 712 for (Section section: linkedSections.getSections(subpart)) 713 linkEl.addElement("section") 714 .addAttribute("offering", getId("offering", offering.getId())) 715 .addAttribute("id", getId("section", section.getId())); 716 } 717 } 718 719 /** 720 * Save travel times 721 * @param root document root 722 */ 723 protected void saveTravelTimes(Element root) { 724 if (getModel().getDistanceConflict() != null) { 725 Map<Long, Map<Long, Integer>> travelTimes = getModel().getDistanceConflict().getDistanceMetric().getTravelTimes(); 726 if (travelTimes != null) { 727 Element travelTimesEl = root.addElement("travel-times"); 728 for (Map.Entry<Long, Map<Long, Integer>> e1: travelTimes.entrySet()) 729 for (Map.Entry<Long, Integer> e2: e1.getValue().entrySet()) 730 travelTimesEl.addElement("travel-time") 731 .addAttribute("id1", getId("room", e1.getKey().toString())) 732 .addAttribute("id2", getId("room", e2.getKey().toString())) 733 .addAttribute("minutes", e2.getValue().toString()); 734 } 735 } 736 } 737}