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