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 } 351 352 /** 353 * Save given config 354 * @param configEl config element to be populated 355 * @param config config to be saved 356 */ 357 protected void saveConfig(Element configEl, Config config) { 358 configEl.addAttribute("id", getId("config", config.getId())); 359 if (config.getLimit() >= 0) 360 configEl.addAttribute("limit", String.valueOf(config.getLimit())); 361 if (iShowNames) 362 configEl.addAttribute("name", config.getName()); 363 for (Subpart subpart : config.getSubparts()) { 364 Element subpartEl = configEl.addElement("subpart"); 365 saveSubpart(subpartEl, subpart); 366 } 367 if (config.getInstructionalMethodId() != null) { 368 Element imEl = configEl.addElement("instructional-method"); 369 imEl.addAttribute("id", getId("instructional-method", config.getInstructionalMethodId())); 370 if (iShowNames && config.getInstructionalMethodName() != null) 371 imEl.addAttribute("name", config.getInstructionalMethodName()); 372 if (iShowNames && config.getInstructionalMethodReference() != null) 373 imEl.addAttribute("reference", config.getInstructionalMethodReference()); 374 } 375 } 376 377 /** 378 * Save scheduling subpart 379 * @param subpartEl subpart element to be populated 380 * @param subpart subpart to be saved 381 */ 382 protected void saveSubpart(Element subpartEl, Subpart subpart) { 383 subpartEl.addAttribute("id", getId("subpart", subpart.getId())); 384 subpartEl.addAttribute("itype", subpart.getInstructionalType()); 385 if (subpart.getParent() != null) 386 subpartEl.addAttribute("parent", getId("subpart", subpart.getParent().getId())); 387 if (iShowNames) { 388 subpartEl.addAttribute("name", subpart.getName()); 389 if (subpart.getCredit() != null) 390 subpartEl.addAttribute("credit", subpart.getCredit()); 391 if (subpart.hasCreditValue()) 392 subpartEl.addAttribute("credits", subpart.getCreditValue().toString()); 393 } 394 if (subpart.isAllowOverlap()) 395 subpartEl.addAttribute("allowOverlap", "true"); 396 for (Section section : subpart.getSections()) { 397 Element sectionEl = subpartEl.addElement("section"); 398 saveSection(sectionEl, section); 399 } 400 } 401 402 /** 403 * Save section 404 * @param sectionEl section element to be populated 405 * @param section section to be saved 406 */ 407 protected void saveSection(Element sectionEl, Section section) { 408 sectionEl.addAttribute("id", getId("section", section.getId())); 409 sectionEl.addAttribute("limit", String.valueOf(section.getLimit())); 410 if (section.isCancelled()) 411 sectionEl.addAttribute("cancelled", "true"); 412 if (!section.isEnabled()) 413 sectionEl.addAttribute("enabled", "false"); 414 if (section.isOnline()) 415 sectionEl.addAttribute("online", "true"); 416 if (section.isPast()) 417 sectionEl.addAttribute("past", "true"); 418 if (iShowNames && section.getNameByCourse() != null) 419 for (Map.Entry<Long, String> entry: section.getNameByCourse().entrySet()) 420 sectionEl.addElement("cname").addAttribute("id", getId("course", entry.getKey())).setText(entry.getValue()); 421 if (section.getParent() != null) 422 sectionEl.addAttribute("parent", getId("section", section.getParent().getId())); 423 if (section.hasInstructors()) { 424 for (Instructor instructor: section.getInstructors()) { 425 Element instructorEl = sectionEl.addElement("instructor"); 426 instructorEl.addAttribute("id", getId("instructor", instructor.getId())); 427 if (iShowNames && instructor.getName() != null) 428 instructorEl.addAttribute("name", instructor.getName()); 429 if (iShowNames && instructor.getExternalId() != null) 430 instructorEl.addAttribute("externalId", instructor.getExternalId()); 431 if (iShowNames && instructor.getEmail() != null) 432 instructorEl.addAttribute("email", instructor.getExternalId()); 433 } 434 } 435 if (iShowNames) 436 sectionEl.addAttribute("name", section.getName()); 437 if (section.getPlacement() != null) { 438 TimeLocation tl = section.getPlacement().getTimeLocation(); 439 if (tl != null) { 440 Element timeLocationEl = sectionEl.addElement("time"); 441 timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer 442 .toBinaryString(tl.getDayCode())))); 443 timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 444 timeLocationEl.addAttribute("length", String.valueOf(tl.getLength())); 445 if (tl.getBreakTime() != 0) 446 timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 447 if (iShowNames && tl.getTimePatternId() != null) 448 timeLocationEl.addAttribute("pattern", getId("timePattern", tl.getTimePatternId())); 449 if (iShowNames && tl.getDatePatternId() != null) 450 timeLocationEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 451 if (iShowNames && tl.getDatePatternName() != null 452 && tl.getDatePatternName().length() > 0) 453 timeLocationEl.addAttribute("datePatternName", tl.getDatePatternName()); 454 timeLocationEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 455 if (iShowNames) 456 timeLocationEl.setText(tl.getLongName(true)); 457 } 458 for (RoomLocation rl : section.getRooms()) { 459 Element roomLocationEl = sectionEl.addElement("room"); 460 roomLocationEl.addAttribute("id", getId("room", rl.getId())); 461 if (iShowNames && rl.getBuildingId() != null) 462 roomLocationEl.addAttribute("building", getId("building", rl.getBuildingId())); 463 if (iShowNames && rl.getName() != null) 464 roomLocationEl.addAttribute("name", rl.getName()); 465 roomLocationEl.addAttribute("capacity", String.valueOf(rl.getRoomSize())); 466 if (rl.getPosX() != null && rl.getPosY() != null) 467 roomLocationEl.addAttribute("location", rl.getPosX() + "," + rl.getPosY()); 468 if (rl.getIgnoreTooFar()) 469 roomLocationEl.addAttribute("ignoreTooFar", "true"); 470 } 471 } 472 if (iSaveOnlineSectioningInfo) { 473 if (section.getSpaceHeld() != 0.0) 474 sectionEl.addAttribute("hold", sStudentWeightFormat.format(section.getSpaceHeld())); 475 if (section.getSpaceExpected() != 0.0) 476 sectionEl.addAttribute("expect", sStudentWeightFormat 477 .format(section.getSpaceExpected())); 478 } 479 if (section.getIgnoreConflictWithSectionIds() != null && !section.getIgnoreConflictWithSectionIds().isEmpty()) { 480 Element ignoreEl = sectionEl.addElement("no-conflicts"); 481 for (Long sectionId: section.getIgnoreConflictWithSectionIds()) 482 ignoreEl.addElement("section").addAttribute("id", getId("section", sectionId)); 483 } 484 } 485 486 /** 487 * Save reservations of the given offering 488 * @param offeringEl offering element to be populated with reservations 489 * @param offering offering which reservations are to be saved 490 */ 491 protected void saveReservations(Element offeringEl, Offering offering) { 492 if (!offering.getReservations().isEmpty()) { 493 for (Reservation r: offering.getReservations()) { 494 saveReservation(offeringEl.addElement("reservation"), r); 495 } 496 } 497 } 498 499 /** 500 * Save reservation 501 * @param reservationEl reservation element to be populated 502 * @param reservation reservation to be saved 503 */ 504 protected void saveReservation(Element reservationEl, Reservation reservation) { 505 reservationEl.addAttribute("id", getId("reservation", reservation.getId())); 506 reservationEl.addAttribute("expired", reservation.isExpired() ? "true" : "false"); 507 if (reservation instanceof LearningCommunityReservation) { 508 LearningCommunityReservation lc = (LearningCommunityReservation)reservation; 509 reservationEl.addAttribute("type", "lc"); 510 for (Long studentId: lc.getStudentIds()) 511 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 512 if (lc.getReservationLimit() >= 0.0) 513 reservationEl.addAttribute("limit", String.valueOf(lc.getReservationLimit())); 514 reservationEl.addAttribute("course", getId("course",lc.getCourse().getId())); 515 } else if (reservation instanceof GroupReservation) { 516 GroupReservation gr = (GroupReservation)reservation; 517 reservationEl.addAttribute("type", "group"); 518 for (Long studentId: gr.getStudentIds()) 519 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 520 if (gr.getReservationLimit() >= 0.0) 521 reservationEl.addAttribute("limit", String.valueOf(gr.getReservationLimit())); 522 } else if (reservation instanceof ReservationOverride) { 523 reservationEl.addAttribute("type", "override"); 524 ReservationOverride o = (ReservationOverride)reservation; 525 for (Long studentId: o.getStudentIds()) 526 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 527 } else if (reservation instanceof IndividualReservation) { 528 reservationEl.addAttribute("type", "individual"); 529 for (Long studentId: ((IndividualReservation)reservation).getStudentIds()) 530 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 531 } else if (reservation instanceof CurriculumReservation) { 532 reservationEl.addAttribute("type", (reservation instanceof CurriculumOverride ? "curriculum-override" : "curriculum")); 533 CurriculumReservation cr = (CurriculumReservation)reservation; 534 if (cr.getReservationLimit() >= 0.0) 535 reservationEl.addAttribute("limit", String.valueOf(cr.getReservationLimit())); 536 if (cr.getAcademicAreas().size() == 1) 537 reservationEl.addAttribute("area", cr.getAcademicAreas().iterator().next()); 538 else { 539 for (String area: cr.getAcademicAreas()) 540 reservationEl.addElement("area").addAttribute("code", area); 541 } 542 for (String clasf: cr.getClassifications()) 543 reservationEl.addElement("classification").addAttribute("code", clasf); 544 for (String major: cr.getMajors()) { 545 Element majorEl = reservationEl.addElement("major").addAttribute("code", major); 546 Set<String> concentrations = cr.getConcentrations(major); 547 if (concentrations != null) 548 for (String conc: concentrations) 549 majorEl.addElement("concentration").addAttribute("code", conc); 550 } 551 for (String minor: cr.getMinors()) 552 reservationEl.addElement("minor").addAttribute("code", minor); 553 } else if (reservation instanceof CourseReservation) { 554 reservationEl.addAttribute("type", "course"); 555 CourseReservation cr = (CourseReservation)reservation; 556 reservationEl.addAttribute("course", getId("course",cr.getCourse().getId())); 557 } else if (reservation instanceof DummyReservation) { 558 reservationEl.addAttribute("type", "dummy"); 559 } 560 reservationEl.addAttribute("priority", String.valueOf(reservation.getPriority())); 561 reservationEl.addAttribute("mustBeUsed", reservation.mustBeUsed() ? "true" : "false"); 562 reservationEl.addAttribute("allowOverlap", reservation.isAllowOverlap() ? "true" : "false"); 563 reservationEl.addAttribute("canAssignOverLimit", reservation.canAssignOverLimit() ? "true" : "false"); 564 reservationEl.addAttribute("allowDisabled", reservation.isAllowDisabled() ? "true" : "false"); 565 if (reservation.neverIncluded()) reservationEl.addAttribute("neverIncluded", "true"); 566 if (reservation.canBreakLinkedSections()) reservationEl.addAttribute("breakLinkedSections", "true"); 567 for (Config config: reservation.getConfigs()) 568 reservationEl.addElement("config").addAttribute("id", getId("config", config.getId())); 569 for (Map.Entry<Subpart, Set<Section>> entry: reservation.getSections().entrySet()) { 570 for (Section section: entry.getValue()) { 571 reservationEl.addElement("section").addAttribute("id", getId("section", section.getId())); 572 } 573 } 574 } 575 576 /** 577 * Save restrictions of the given offering 578 * @param offeringEl offering element to be populated with restrictions 579 * @param offering offering which restrictions are to be saved 580 */ 581 protected void saveRestrictions(Element offeringEl, Offering offering) { 582 if (!offering.getRestrictions().isEmpty()) { 583 for (Restriction r: offering.getRestrictions()) { 584 saveRestriction(offeringEl.addElement("restriction"), r); 585 } 586 } 587 } 588 589 /** 590 * Save restriction 591 * @param restrictionEl restriction element to be populated 592 * @param restriction restriction to be saved 593 */ 594 protected void saveRestriction(Element restrictionEl, Restriction restriction) { 595 restrictionEl.addAttribute("id", getId("restriction", restriction.getId())); 596 if (restriction instanceof IndividualRestriction) { 597 restrictionEl.addAttribute("type", "individual"); 598 for (Long studentId: ((IndividualRestriction)restriction).getStudentIds()) 599 restrictionEl.addElement("student").addAttribute("id", getId("student", studentId)); 600 } else if (restriction instanceof CurriculumRestriction) { 601 restrictionEl.addAttribute("type", "curriculum"); 602 CurriculumRestriction cr = (CurriculumRestriction)restriction; 603 if (cr.getAcademicAreas().size() == 1) 604 restrictionEl.addAttribute("area", cr.getAcademicAreas().iterator().next()); 605 else { 606 for (String area: cr.getAcademicAreas()) 607 restrictionEl.addElement("area").addAttribute("code", area); 608 } 609 for (String clasf: cr.getClassifications()) 610 restrictionEl.addElement("classification").addAttribute("code", clasf); 611 for (String major: cr.getMajors()) { 612 Element majorEl = restrictionEl.addElement("major").addAttribute("code", major); 613 Set<String> concentrations = cr.getConcentrations(major); 614 if (concentrations != null) 615 for (String conc: concentrations) 616 majorEl.addElement("concentration").addAttribute("code", conc); 617 } 618 for (String minor: cr.getMinors()) 619 restrictionEl.addElement("minor").addAttribute("code", minor); 620 } else if (restriction instanceof CourseRestriction) { 621 restrictionEl.addAttribute("type", "course"); 622 CourseRestriction cr = (CourseRestriction)restriction; 623 restrictionEl.addAttribute("course", getId("course",cr.getCourse().getId())); 624 } 625 for (Config config: restriction.getConfigs()) 626 restrictionEl.addElement("config").addAttribute("id", getId("config", config.getId())); 627 for (Map.Entry<Subpart, Set<Section>> entry: restriction.getSections().entrySet()) { 628 for (Section section: entry.getValue()) { 629 restrictionEl.addElement("section").addAttribute("id", getId("section", section.getId())); 630 } 631 } 632 } 633 634 /** 635 * Save students 636 * @param root document root 637 */ 638 protected void saveStudents(Element root) { 639 Element studentsEl = root.addElement("students"); 640 for (Student student : getModel().getStudents()) { 641 Element studentEl = studentsEl.addElement("student"); 642 saveStudent(studentEl, student); 643 for (Request request : student.getRequests()) { 644 saveRequest(studentEl, request); 645 } 646 } 647 } 648 649 /** 650 * Save student 651 * @param studentEl student element to be populated 652 * @param student student to be saved 653 */ 654 protected void saveStudent(Element studentEl, Student student) { 655 studentEl.addAttribute("id", getId("student", student.getId())); 656 if (iShowNames) { 657 if (student.getExternalId() != null && !student.getExternalId().isEmpty()) 658 studentEl.addAttribute("externalId", student.getExternalId()); 659 if (student.getName() != null && !student.getName().isEmpty()) 660 studentEl.addAttribute("name", student.getName()); 661 if (student.getStatus() != null && !student.getStatus().isEmpty()) 662 studentEl.addAttribute("status", student.getStatus()); 663 } 664 if (student.isDummy()) 665 studentEl.addAttribute("dummy", "true"); 666 if (student.getPriority().ordinal() < StudentPriority.Normal.ordinal()) 667 studentEl.addAttribute("priority", student.getPriority().name()); 668 if (student.isNeedShortDistances()) 669 studentEl.addAttribute("shortDistances", "true"); 670 if (student.isAllowDisabled()) 671 studentEl.addAttribute("allowDisabled", "true"); 672 if (student.hasMinCredit()) 673 studentEl.addAttribute("minCredit", String.valueOf(student.getMinCredit())); 674 if (student.hasMaxCredit()) 675 studentEl.addAttribute("maxCredit", String.valueOf(student.getMaxCredit())); 676 if (student.getClassFirstDate() != null) 677 studentEl.addAttribute("classFirstDate", String.valueOf(student.getClassFirstDate())); 678 if (student.getClassLastDate() != null) 679 studentEl.addAttribute("classLastDate", String.valueOf(student.getClassLastDate())); 680 if (student.getModalityPreference() != null && student.getModalityPreference() != ModalityPreference.NO_PREFERENCE) 681 studentEl.addAttribute("modality", student.getModalityPreference().name()); 682 if (student.getBackToBackPreference() != null && student.getBackToBackPreference() != BackToBackPreference.NO_PREFERENCE) 683 studentEl.addAttribute("btb", student.getBackToBackPreference().name()); 684 if (iSaveStudentInfo) { 685 for (AreaClassificationMajor acm : student.getAreaClassificationMajors()) { 686 Element acmEl = studentEl.addElement("acm"); 687 if (acm.getArea() != null) 688 acmEl.addAttribute("area", acm.getArea()); 689 if (acm.getClassification() != null) 690 acmEl.addAttribute("classification", acm.getClassification()); 691 if (acm.getMajor() != null) 692 acmEl.addAttribute("major", acm.getMajor()); 693 if (acm.getConcentration() != null) 694 acmEl.addAttribute("concentration", acm.getConcentration()); 695 if (acm.getDegree() != null) 696 acmEl.addAttribute("degree", acm.getDegree()); 697 if (acm.getProgram() != null) 698 acmEl.addAttribute("program", acm.getProgram()); 699 if (acm.getAreaName() != null && iShowNames) 700 acmEl.addAttribute("areaName", acm.getAreaName()); 701 if (acm.getClassificationName() != null && iShowNames) 702 acmEl.addAttribute("classificationName", acm.getClassificationName()); 703 if (acm.getMajorName() != null && iShowNames) 704 acmEl.addAttribute("majorName", acm.getMajorName()); 705 if (acm.getConcentrationName() != null && iShowNames) 706 acmEl.addAttribute("concentrationName", acm.getConcentrationName()); 707 if (acm.getDegreeName() != null && iShowNames) 708 acmEl.addAttribute("degreeName", acm.getDegreeName()); 709 if (acm.getProgramName() != null && iShowNames) 710 acmEl.addAttribute("programName", acm.getProgramName()); 711 if (acm.getWeight() != 1.0) 712 acmEl.addAttribute("weight", String.valueOf(acm.getWeight())); 713 } 714 for (AreaClassificationMajor acm : student.getAreaClassificationMinors()) { 715 Element acmEl = studentEl.addElement("acm"); 716 if (acm.getArea() != null) 717 acmEl.addAttribute("area", acm.getArea()); 718 if (acm.getClassification() != null) 719 acmEl.addAttribute("classification", acm.getClassification()); 720 if (acm.getMajor() != null) 721 acmEl.addAttribute("minor", acm.getMajor()); 722 if (acm.getConcentration() != null) 723 acmEl.addAttribute("concentration", acm.getConcentration()); 724 if (acm.getDegree() != null) 725 acmEl.addAttribute("degree", acm.getDegree()); 726 if (acm.getProgram() != null) 727 acmEl.addAttribute("program", acm.getProgram()); 728 if (acm.getAreaName() != null && iShowNames) 729 acmEl.addAttribute("areaName", acm.getAreaName()); 730 if (acm.getClassificationName() != null && iShowNames) 731 acmEl.addAttribute("classificationName", acm.getClassificationName()); 732 if (acm.getMajorName() != null && iShowNames) 733 acmEl.addAttribute("minorName", acm.getMajorName()); 734 if (acm.getConcentrationName() != null && iShowNames) 735 acmEl.addAttribute("concentrationName", acm.getConcentrationName()); 736 if (acm.getDegreeName() != null && iShowNames) 737 acmEl.addAttribute("degreeName", acm.getDegreeName()); 738 if (acm.getProgramName() != null && iShowNames) 739 acmEl.addAttribute("programName", acm.getProgramName()); 740 if (acm.getWeight() != 1.0) 741 acmEl.addAttribute("weight", String.valueOf(acm.getWeight())); 742 } 743 for (StudentGroup g : student.getGroups()) { 744 Element grEl = studentEl.addElement("group"); 745 if (g.getType() != null && !g.getType().isEmpty()) 746 grEl.addAttribute("type", g.getType()); 747 if (g.getReference() != null) 748 grEl.addAttribute("reference", g.getReference()); 749 if (g.getName() != null) 750 grEl.addAttribute("name", g.getName()); 751 } 752 for (String acc: student.getAccommodations()) 753 studentEl.addElement("accommodation").addAttribute("reference", acc); 754 } 755 if (iShowNames && iSaveStudentInfo) { 756 for (Instructor adv: student.getAdvisors()) { 757 Element advEl = studentEl.addElement("advisor"); 758 if (adv.getExternalId() != null) 759 advEl.addAttribute("externalId", adv.getExternalId()); 760 if (adv.getName() != null) 761 advEl.addAttribute("name", adv.getName()); 762 if (adv.getEmail() != null) 763 advEl.addAttribute("email", adv.getEmail()); 764 } 765 } 766 for (Unavailability unavailability: student.getUnavailabilities()) { 767 Element unavEl = studentEl.addElement("unavailability"); 768 unavEl.addAttribute("offering", getId("offering", unavailability.getSection().getSubpart().getConfig().getOffering().getId())); 769 unavEl.addAttribute("section", getId("section", unavailability.getSection().getId())); 770 if (unavailability.isAllowOverlap()) unavEl.addAttribute("allowOverlap", "true"); 771 } 772 } 773 774 /** 775 * Save request 776 * @param studentEl student element to be populated 777 * @param request request to be saved 778 */ 779 protected void saveRequest(Element studentEl, Request request) { 780 if (request instanceof FreeTimeRequest) { 781 saveFreeTimeRequest(studentEl.addElement("freeTime"), (FreeTimeRequest) request); 782 } else if (request instanceof CourseRequest) { 783 saveCourseRequest(studentEl.addElement("course"), (CourseRequest) request); 784 } 785 } 786 787 /** 788 * Save free time request 789 * @param requestEl request element to be populated 790 * @param request free time request to be saved 791 */ 792 protected void saveFreeTimeRequest(Element requestEl, FreeTimeRequest request) { 793 requestEl.addAttribute("id", getId("request", request.getId())); 794 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 795 if (request.isAlternative()) 796 requestEl.addAttribute("alternative", "true"); 797 if (request.getWeight() != 1.0) 798 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 799 TimeLocation tl = request.getTime(); 800 if (tl != null) { 801 requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl 802 .getDayCode())))); 803 requestEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 804 requestEl.addAttribute("length", String.valueOf(tl.getLength())); 805 if (iShowNames && tl.getDatePatternId() != null) 806 requestEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 807 requestEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 808 if (iShowNames) 809 requestEl.setText(tl.getLongName(true)); 810 } 811 if (iSaveInitial && request.getInitialAssignment() != null) { 812 requestEl.addElement("initial"); 813 } 814 if (iSaveCurrent && getAssignment().getValue(request) != null) { 815 requestEl.addElement("current"); 816 } 817 if (iSaveBest && request.getBestAssignment() != null) { 818 requestEl.addElement("best"); 819 } 820 } 821 822 /** 823 * Save course request 824 * @param requestEl request element to be populated 825 * @param request course request to be saved 826 */ 827 protected void saveCourseRequest(Element requestEl, CourseRequest request) { 828 requestEl.addAttribute("id", getId("request", request.getId())); 829 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 830 if (request.isAlternative()) 831 requestEl.addAttribute("alternative", "true"); 832 if (request.getWeight() != 1.0) 833 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 834 requestEl.addAttribute("waitlist", request.isWaitlist() ? "true" : "false"); 835 if (request.getRequestPriority() != RequestPriority.Normal) 836 requestEl.addAttribute("importance", request.getRequestPriority().name()); 837 if (request.getRequestPriority() == RequestPriority.Critical) 838 requestEl.addAttribute("critical", "true"); 839 if (request.getTimeStamp() != null) 840 requestEl.addAttribute("timeStamp", request.getTimeStamp().toString()); 841 boolean first = true; 842 for (Course course : request.getCourses()) { 843 if (first) 844 requestEl.addAttribute("course", getId("course", course.getId())); 845 else 846 requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId())); 847 first = false; 848 } 849 for (Choice choice : request.getWaitlistedChoices()) { 850 Element choiceEl = requestEl.addElement("waitlisted"); 851 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 852 choiceEl.setText(choice.getId()); 853 } 854 for (Choice choice : request.getSelectedChoices()) { 855 Element choiceEl = requestEl.addElement("selected"); 856 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 857 choiceEl.setText(choice.getId()); 858 } 859 for (Choice choice : request.getRequiredChoices()) { 860 Element choiceEl = requestEl.addElement("required"); 861 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 862 choiceEl.setText(choice.getId()); 863 } 864 if (iSaveInitial && request.getInitialAssignment() != null) { 865 saveEnrollment(requestEl.addElement("initial"), request.getInitialAssignment()); 866 } 867 if (iSaveCurrent && getAssignment().getValue(request) != null) { 868 saveEnrollment(requestEl.addElement("current"), getAssignment().getValue(request)); 869 } 870 if (iSaveBest && request.getBestAssignment() != null) { 871 saveEnrollment(requestEl.addElement("best"), request.getBestAssignment()); 872 } 873 if (request.isFixed()) 874 saveEnrollment(requestEl.addElement("fixed"), request.getFixedValue()); 875 for (RequestGroup g: request.getRequestGroups()) { 876 Element groupEl = requestEl.addElement("group").addAttribute("id", getId("group", g.getId())).addAttribute("course", getId("course", g.getCourse().getId())); 877 if (iShowNames) 878 groupEl.addAttribute("name", g.getName()); 879 } 880 } 881 882 /** 883 * Save enrollment 884 * @param assignmentEl assignment element to be populated 885 * @param enrollment enrollment to be saved 886 */ 887 protected void saveEnrollment(Element assignmentEl, Enrollment enrollment) { 888 if (enrollment.getReservation() != null) 889 assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId())); 890 if (enrollment.getCourse() != null) 891 assignmentEl.addAttribute("course", getId("course", enrollment.getCourse().getId())); 892 for (Section section : enrollment.getSections()) { 893 Element sectionEl = assignmentEl.addElement("section").addAttribute("id", 894 getId("section", section.getId())); 895 if (iShowNames) 896 sectionEl.setText(section.getName() + " " + 897 (section.getTime() == null ? " Arr Hrs" : " " + section.getTime().getLongName(true)) + 898 (section.getNrRooms() == 0 ? "" : " " + section.getPlacement().getRoomName(",")) + 899 (section.hasInstructors() ? " " + section.getInstructorNames(",") : "")); 900 } 901 } 902 903 /** 904 * Save linked sections 905 * @param root document root 906 */ 907 protected void saveLinkedSections(Element root) { 908 Element constrainstEl = root.addElement("constraints"); 909 for (LinkedSections linkedSections: getModel().getLinkedSections()) { 910 Element linkEl = constrainstEl.addElement("linked-sections"); 911 linkEl.addAttribute("mustBeUsed", linkedSections.isMustBeUsed() ? "true" : "false"); 912 for (Offering offering: linkedSections.getOfferings()) 913 for (Subpart subpart: linkedSections.getSubparts(offering)) 914 for (Section section: linkedSections.getSections(subpart)) 915 linkEl.addElement("section") 916 .addAttribute("offering", getId("offering", offering.getId())) 917 .addAttribute("id", getId("section", section.getId())); 918 } 919 } 920 921 /** 922 * Save travel times 923 * @param root document root 924 */ 925 protected void saveTravelTimes(Element root) { 926 if (getModel().getDistanceMetric() != null) { 927 Map<Long, Map<Long, Integer>> travelTimes = getModel().getDistanceMetric().getTravelTimes(); 928 if (travelTimes != null) { 929 Element travelTimesEl = root.addElement("travel-times"); 930 for (Map.Entry<Long, Map<Long, Integer>> e1: travelTimes.entrySet()) 931 for (Map.Entry<Long, Integer> e2: e1.getValue().entrySet()) 932 travelTimesEl.addElement("travel-time") 933 .addAttribute("id1", getId("room", e1.getKey().toString())) 934 .addAttribute("id2", getId("room", e2.getKey().toString())) 935 .addAttribute("minutes", e2.getValue().toString()); 936 } 937 } 938 } 939}