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