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