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