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