001package org.cpsolver.studentsct; 002 003import java.io.File; 004import java.io.FileOutputStream; 005import java.io.IOException; 006import java.math.RoundingMode; 007import java.text.DecimalFormat; 008import java.text.DecimalFormatSymbols; 009import java.util.BitSet; 010import java.util.Date; 011import java.util.Locale; 012import java.util.Map; 013import java.util.Set; 014import java.util.TreeSet; 015 016import org.cpsolver.coursett.IdConvertor; 017import org.cpsolver.coursett.model.RoomLocation; 018import org.cpsolver.coursett.model.TimeLocation; 019import org.cpsolver.ifs.solver.Solver; 020import org.cpsolver.ifs.util.Progress; 021import org.cpsolver.studentsct.constraint.LinkedSections; 022import org.cpsolver.studentsct.model.AcademicAreaCode; 023import org.cpsolver.studentsct.model.AreaClassificationMajor; 024import org.cpsolver.studentsct.model.Choice; 025import org.cpsolver.studentsct.model.Config; 026import org.cpsolver.studentsct.model.Course; 027import org.cpsolver.studentsct.model.CourseRequest; 028import org.cpsolver.studentsct.model.Enrollment; 029import org.cpsolver.studentsct.model.FreeTimeRequest; 030import org.cpsolver.studentsct.model.Instructor; 031import org.cpsolver.studentsct.model.Offering; 032import org.cpsolver.studentsct.model.Request; 033import org.cpsolver.studentsct.model.Request.RequestPriority; 034import org.cpsolver.studentsct.model.RequestGroup; 035import org.cpsolver.studentsct.model.Section; 036import org.cpsolver.studentsct.model.Student; 037import org.cpsolver.studentsct.model.Student.StudentPriority; 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 Tomas Muller<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 (iShowNames && section.getNameByCourse() != null) 415 for (Map.Entry<Long, String> entry: section.getNameByCourse().entrySet()) 416 sectionEl.addElement("cname").addAttribute("id", getId("course", entry.getKey())).setText(entry.getValue()); 417 if (section.getParent() != null) 418 sectionEl.addAttribute("parent", getId("section", section.getParent().getId())); 419 if (section.hasInstructors()) { 420 for (Instructor instructor: section.getInstructors()) { 421 Element instructorEl = sectionEl.addElement("instructor"); 422 instructorEl.addAttribute("id", getId("instructor", instructor.getId())); 423 if (iShowNames && instructor.getName() != null) 424 instructorEl.addAttribute("name", instructor.getName()); 425 if (iShowNames && instructor.getExternalId() != null) 426 instructorEl.addAttribute("externalId", instructor.getExternalId()); 427 if (iShowNames && instructor.getEmail() != null) 428 instructorEl.addAttribute("email", instructor.getExternalId()); 429 } 430 } 431 if (iShowNames) 432 sectionEl.addAttribute("name", section.getName()); 433 if (section.getPlacement() != null) { 434 TimeLocation tl = section.getPlacement().getTimeLocation(); 435 if (tl != null) { 436 Element timeLocationEl = sectionEl.addElement("time"); 437 timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer 438 .toBinaryString(tl.getDayCode())))); 439 timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 440 timeLocationEl.addAttribute("length", String.valueOf(tl.getLength())); 441 if (tl.getBreakTime() != 0) 442 timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 443 if (iShowNames && tl.getTimePatternId() != null) 444 timeLocationEl.addAttribute("pattern", getId("timePattern", tl.getTimePatternId())); 445 if (iShowNames && tl.getDatePatternId() != null) 446 timeLocationEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 447 if (iShowNames && tl.getDatePatternName() != null 448 && tl.getDatePatternName().length() > 0) 449 timeLocationEl.addAttribute("datePatternName", tl.getDatePatternName()); 450 timeLocationEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 451 if (iShowNames) 452 timeLocationEl.setText(tl.getLongName(true)); 453 } 454 for (RoomLocation rl : section.getRooms()) { 455 Element roomLocationEl = sectionEl.addElement("room"); 456 roomLocationEl.addAttribute("id", getId("room", rl.getId())); 457 if (iShowNames && rl.getBuildingId() != null) 458 roomLocationEl.addAttribute("building", getId("building", rl.getBuildingId())); 459 if (iShowNames && rl.getName() != null) 460 roomLocationEl.addAttribute("name", rl.getName()); 461 roomLocationEl.addAttribute("capacity", String.valueOf(rl.getRoomSize())); 462 if (rl.getPosX() != null && rl.getPosY() != null) 463 roomLocationEl.addAttribute("location", rl.getPosX() + "," + rl.getPosY()); 464 if (rl.getIgnoreTooFar()) 465 roomLocationEl.addAttribute("ignoreTooFar", "true"); 466 } 467 } 468 if (iSaveOnlineSectioningInfo) { 469 if (section.getSpaceHeld() != 0.0) 470 sectionEl.addAttribute("hold", sStudentWeightFormat.format(section.getSpaceHeld())); 471 if (section.getSpaceExpected() != 0.0) 472 sectionEl.addAttribute("expect", sStudentWeightFormat 473 .format(section.getSpaceExpected())); 474 } 475 if (section.getIgnoreConflictWithSectionIds() != null && !section.getIgnoreConflictWithSectionIds().isEmpty()) { 476 Element ignoreEl = sectionEl.addElement("no-conflicts"); 477 for (Long sectionId: section.getIgnoreConflictWithSectionIds()) 478 ignoreEl.addElement("section").addAttribute("id", getId("section", sectionId)); 479 } 480 } 481 482 /** 483 * Save reservations of the given offering 484 * @param offeringEl offering element to be populated with reservations 485 * @param offering offering which reservations are to be saved 486 */ 487 protected void saveReservations(Element offeringEl, Offering offering) { 488 if (!offering.getReservations().isEmpty()) { 489 for (Reservation r: offering.getReservations()) { 490 saveReservation(offeringEl.addElement("reservation"), r); 491 } 492 } 493 } 494 495 /** 496 * Save reservation 497 * @param reservationEl reservation element to be populated 498 * @param reservation reservation to be saved 499 */ 500 protected void saveReservation(Element reservationEl, Reservation reservation) { 501 reservationEl.addAttribute("id", getId("reservation", reservation.getId())); 502 reservationEl.addAttribute("expired", reservation.isExpired() ? "true" : "false"); 503 if (reservation instanceof LearningCommunityReservation) { 504 LearningCommunityReservation lc = (LearningCommunityReservation)reservation; 505 reservationEl.addAttribute("type", "lc"); 506 for (Long studentId: lc.getStudentIds()) 507 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 508 if (lc.getReservationLimit() >= 0.0) 509 reservationEl.addAttribute("limit", String.valueOf(lc.getReservationLimit())); 510 reservationEl.addAttribute("course", getId("course",lc.getCourse().getId())); 511 } else if (reservation instanceof GroupReservation) { 512 GroupReservation gr = (GroupReservation)reservation; 513 reservationEl.addAttribute("type", "group"); 514 for (Long studentId: gr.getStudentIds()) 515 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 516 if (gr.getReservationLimit() >= 0.0) 517 reservationEl.addAttribute("limit", String.valueOf(gr.getReservationLimit())); 518 } else if (reservation instanceof ReservationOverride) { 519 reservationEl.addAttribute("type", "override"); 520 ReservationOverride o = (ReservationOverride)reservation; 521 for (Long studentId: o.getStudentIds()) 522 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 523 } else if (reservation instanceof IndividualReservation) { 524 reservationEl.addAttribute("type", "individual"); 525 for (Long studentId: ((IndividualReservation)reservation).getStudentIds()) 526 reservationEl.addElement("student").addAttribute("id", getId("student", studentId)); 527 } else if (reservation instanceof CurriculumReservation) { 528 reservationEl.addAttribute("type", (reservation instanceof CurriculumOverride ? "curriculum-override" : "curriculum")); 529 CurriculumReservation cr = (CurriculumReservation)reservation; 530 if (cr.getReservationLimit() >= 0.0) 531 reservationEl.addAttribute("limit", String.valueOf(cr.getReservationLimit())); 532 reservationEl.addAttribute("area", cr.getAcademicArea()); 533 for (String clasf: cr.getClassifications()) 534 reservationEl.addElement("classification").addAttribute("code", clasf); 535 for (String major: cr.getMajors()) 536 reservationEl.addElement("major").addAttribute("code", major); 537 } else if (reservation instanceof CourseReservation) { 538 reservationEl.addAttribute("type", "course"); 539 CourseReservation cr = (CourseReservation)reservation; 540 reservationEl.addAttribute("course", getId("course",cr.getCourse().getId())); 541 } else if (reservation instanceof DummyReservation) { 542 reservationEl.addAttribute("type", "dummy"); 543 } 544 reservationEl.addAttribute("priority", String.valueOf(reservation.getPriority())); 545 reservationEl.addAttribute("mustBeUsed", reservation.mustBeUsed() ? "true" : "false"); 546 reservationEl.addAttribute("allowOverlap", reservation.isAllowOverlap() ? "true" : "false"); 547 reservationEl.addAttribute("canAssignOverLimit", reservation.canAssignOverLimit() ? "true" : "false"); 548 reservationEl.addAttribute("allowDisabled", reservation.isAllowDisabled() ? "true" : "false"); 549 if (reservation.neverIncluded()) reservationEl.addAttribute("neverIncluded", "true"); 550 if (reservation.canBreakLinkedSections()) reservationEl.addAttribute("breakLinkedSections", "true"); 551 for (Config config: reservation.getConfigs()) 552 reservationEl.addElement("config").addAttribute("id", getId("config", config.getId())); 553 for (Map.Entry<Subpart, Set<Section>> entry: reservation.getSections().entrySet()) { 554 for (Section section: entry.getValue()) { 555 reservationEl.addElement("section").addAttribute("id", getId("section", section.getId())); 556 } 557 } 558 } 559 560 /** 561 * Save restrictions of the given offering 562 * @param offeringEl offering element to be populated with restrictions 563 * @param offering offering which restrictions are to be saved 564 */ 565 protected void saveRestrictions(Element offeringEl, Offering offering) { 566 if (!offering.getRestrictions().isEmpty()) { 567 for (Restriction r: offering.getRestrictions()) { 568 saveRestriction(offeringEl.addElement("restriction"), r); 569 } 570 } 571 } 572 573 /** 574 * Save restriction 575 * @param restrictionEl restriction element to be populated 576 * @param restriction restriction to be saved 577 */ 578 protected void saveRestriction(Element restrictionEl, Restriction restriction) { 579 restrictionEl.addAttribute("id", getId("restriction", restriction.getId())); 580 if (restriction instanceof IndividualRestriction) { 581 restrictionEl.addAttribute("type", "individual"); 582 for (Long studentId: ((IndividualRestriction)restriction).getStudentIds()) 583 restrictionEl.addElement("student").addAttribute("id", getId("student", studentId)); 584 } else if (restriction instanceof CurriculumRestriction) { 585 restrictionEl.addAttribute("type", "curriculum"); 586 CurriculumRestriction cr = (CurriculumRestriction)restriction; 587 restrictionEl.addAttribute("area", cr.getAcademicArea()); 588 for (String clasf: cr.getClassifications()) 589 restrictionEl.addElement("classification").addAttribute("code", clasf); 590 for (String major: cr.getMajors()) 591 restrictionEl.addElement("major").addAttribute("code", major); 592 } else if (restriction instanceof CourseRestriction) { 593 restrictionEl.addAttribute("type", "course"); 594 CourseRestriction cr = (CourseRestriction)restriction; 595 restrictionEl.addAttribute("course", getId("course",cr.getCourse().getId())); 596 } 597 for (Config config: restriction.getConfigs()) 598 restrictionEl.addElement("config").addAttribute("id", getId("config", config.getId())); 599 for (Map.Entry<Subpart, Set<Section>> entry: restriction.getSections().entrySet()) { 600 for (Section section: entry.getValue()) { 601 restrictionEl.addElement("section").addAttribute("id", getId("section", section.getId())); 602 } 603 } 604 } 605 606 /** 607 * Save students 608 * @param root document root 609 */ 610 protected void saveStudents(Element root) { 611 Element studentsEl = root.addElement("students"); 612 for (Student student : getModel().getStudents()) { 613 Element studentEl = studentsEl.addElement("student"); 614 saveStudent(studentEl, student); 615 for (Request request : student.getRequests()) { 616 saveRequest(studentEl, request); 617 } 618 } 619 } 620 621 /** 622 * Save student 623 * @param studentEl student element to be populated 624 * @param student student to be saved 625 */ 626 protected void saveStudent(Element studentEl, Student student) { 627 studentEl.addAttribute("id", getId("student", student.getId())); 628 if (iShowNames) { 629 if (student.getExternalId() != null && !student.getExternalId().isEmpty()) 630 studentEl.addAttribute("externalId", student.getExternalId()); 631 if (student.getName() != null && !student.getName().isEmpty()) 632 studentEl.addAttribute("name", student.getName()); 633 if (student.getStatus() != null && !student.getStatus().isEmpty()) 634 studentEl.addAttribute("status", student.getStatus()); 635 } 636 if (student.isDummy()) 637 studentEl.addAttribute("dummy", "true"); 638 if (student.getPriority().ordinal() < StudentPriority.Normal.ordinal()) 639 studentEl.addAttribute("priority", student.getPriority().name()); 640 if (student.isNeedShortDistances()) 641 studentEl.addAttribute("shortDistances", "true"); 642 if (student.isAllowDisabled()) 643 studentEl.addAttribute("allowDisabled", "true"); 644 if (student.hasMinCredit()) 645 studentEl.addAttribute("minCredit", String.valueOf(student.getMinCredit())); 646 if (student.hasMaxCredit()) 647 studentEl.addAttribute("maxCredit", String.valueOf(student.getMaxCredit())); 648 if (iSaveStudentInfo) { 649 for (AcademicAreaCode aac : student.getAcademicAreaClasiffications()) { 650 Element aacEl = studentEl.addElement("classification"); 651 if (aac.getArea() != null) 652 aacEl.addAttribute("area", aac.getArea()); 653 if (aac.getCode() != null) 654 aacEl.addAttribute("code", aac.getCode()); 655 if (aac.getLabel() != null) 656 aacEl.addAttribute("label", aac.getLabel()); 657 } 658 for (AcademicAreaCode aac : student.getMajors()) { 659 Element aacEl = studentEl.addElement("major"); 660 if (aac.getArea() != null) 661 aacEl.addAttribute("area", aac.getArea()); 662 if (aac.getCode() != null) 663 aacEl.addAttribute("code", aac.getCode()); 664 if (aac.getLabel() != null) 665 aacEl.addAttribute("label", aac.getLabel()); 666 } 667 for (AcademicAreaCode aac : student.getMinors()) { 668 Element aacEl = studentEl.addElement("minor"); 669 if (aac.getArea() != null) 670 aacEl.addAttribute("area", aac.getArea()); 671 if (aac.getCode() != null) 672 aacEl.addAttribute("code", aac.getCode()); 673 if (aac.getLabel() != null) 674 aacEl.addAttribute("label", aac.getLabel()); 675 } 676 for (AreaClassificationMajor acm : student.getAreaClassificationMajors()) { 677 Element acmEl = studentEl.addElement("acm"); 678 if (acm.getArea() != null) 679 acmEl.addAttribute("area", acm.getArea()); 680 if (acm.getArea() != null) 681 acmEl.addAttribute("classification", acm.getClassification()); 682 if (acm.getArea() != null) 683 acmEl.addAttribute("major", acm.getMajor()); 684 } 685 } 686 if (iShowNames && iSaveStudentInfo) { 687 for (Instructor adv: student.getAdvisors()) { 688 Element advEl = studentEl.addElement("advisor"); 689 if (adv.getExternalId() != null) 690 advEl.addAttribute("externalId", adv.getExternalId()); 691 if (adv.getName() != null) 692 advEl.addAttribute("name", adv.getName()); 693 if (adv.getEmail() != null) 694 advEl.addAttribute("email", adv.getEmail()); 695 } 696 } 697 for (Unavailability unavailability: student.getUnavailabilities()) { 698 Element unavEl = studentEl.addElement("unavailability"); 699 unavEl.addAttribute("offering", getId("offering", unavailability.getSection().getSubpart().getConfig().getOffering().getId())); 700 unavEl.addAttribute("section", getId("section", unavailability.getSection().getId())); 701 if (unavailability.isAllowOverlap()) unavEl.addAttribute("allowOverlap", "true"); 702 } 703 } 704 705 /** 706 * Save request 707 * @param studentEl student element to be populated 708 * @param request request to be saved 709 */ 710 protected void saveRequest(Element studentEl, Request request) { 711 if (request instanceof FreeTimeRequest) { 712 saveFreeTimeRequest(studentEl.addElement("freeTime"), (FreeTimeRequest) request); 713 } else if (request instanceof CourseRequest) { 714 saveCourseRequest(studentEl.addElement("course"), (CourseRequest) request); 715 } 716 } 717 718 /** 719 * Save free time request 720 * @param requestEl request element to be populated 721 * @param request free time request to be saved 722 */ 723 protected void saveFreeTimeRequest(Element requestEl, FreeTimeRequest request) { 724 requestEl.addAttribute("id", getId("request", request.getId())); 725 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 726 if (request.isAlternative()) 727 requestEl.addAttribute("alternative", "true"); 728 if (request.getWeight() != 1.0) 729 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 730 TimeLocation tl = request.getTime(); 731 if (tl != null) { 732 requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl 733 .getDayCode())))); 734 requestEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 735 requestEl.addAttribute("length", String.valueOf(tl.getLength())); 736 if (iShowNames && tl.getDatePatternId() != null) 737 requestEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 738 requestEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 739 if (iShowNames) 740 requestEl.setText(tl.getLongName(true)); 741 } 742 if (iSaveInitial && request.getInitialAssignment() != null) { 743 requestEl.addElement("initial"); 744 } 745 if (iSaveCurrent && getAssignment().getValue(request) != null) { 746 requestEl.addElement("current"); 747 } 748 if (iSaveBest && request.getBestAssignment() != null) { 749 requestEl.addElement("best"); 750 } 751 } 752 753 /** 754 * Save course request 755 * @param requestEl request element to be populated 756 * @param request course request to be saved 757 */ 758 protected void saveCourseRequest(Element requestEl, CourseRequest request) { 759 requestEl.addAttribute("id", getId("request", request.getId())); 760 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 761 if (request.isAlternative()) 762 requestEl.addAttribute("alternative", "true"); 763 if (request.getWeight() != 1.0) 764 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 765 requestEl.addAttribute("waitlist", request.isWaitlist() ? "true" : "false"); 766 if (request.getRequestPriority() != RequestPriority.Normal) 767 requestEl.addAttribute("importance", request.getRequestPriority().name()); 768 if (request.getRequestPriority() == RequestPriority.Critical) 769 requestEl.addAttribute("critical", "true"); 770 if (request.getTimeStamp() != null) 771 requestEl.addAttribute("timeStamp", request.getTimeStamp().toString()); 772 boolean first = true; 773 for (Course course : request.getCourses()) { 774 if (first) 775 requestEl.addAttribute("course", getId("course", course.getId())); 776 else 777 requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId())); 778 first = false; 779 } 780 for (Choice choice : request.getWaitlistedChoices()) { 781 Element choiceEl = requestEl.addElement("waitlisted"); 782 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 783 choiceEl.setText(choice.getId()); 784 } 785 for (Choice choice : request.getSelectedChoices()) { 786 Element choiceEl = requestEl.addElement("selected"); 787 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 788 choiceEl.setText(choice.getId()); 789 } 790 for (Choice choice : request.getRequiredChoices()) { 791 Element choiceEl = requestEl.addElement("required"); 792 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 793 choiceEl.setText(choice.getId()); 794 } 795 if (iSaveInitial && request.getInitialAssignment() != null) { 796 saveEnrollment(requestEl.addElement("initial"), request.getInitialAssignment()); 797 } 798 if (iSaveCurrent && getAssignment().getValue(request) != null) { 799 saveEnrollment(requestEl.addElement("current"), getAssignment().getValue(request)); 800 } 801 if (iSaveBest && request.getBestAssignment() != null) { 802 saveEnrollment(requestEl.addElement("best"), request.getBestAssignment()); 803 } 804 if (request.isFixed()) 805 saveEnrollment(requestEl.addElement("fixed"), request.getFixedValue()); 806 for (RequestGroup g: request.getRequestGroups()) { 807 Element groupEl = requestEl.addElement("group").addAttribute("id", getId("group", g.getId())).addAttribute("course", getId("course", g.getCourse().getId())); 808 if (iShowNames) 809 groupEl.addAttribute("name", g.getName()); 810 } 811 } 812 813 /** 814 * Save enrollment 815 * @param assignmentEl assignment element to be populated 816 * @param enrollment enrollment to be saved 817 */ 818 protected void saveEnrollment(Element assignmentEl, Enrollment enrollment) { 819 if (enrollment.getReservation() != null) 820 assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId())); 821 if (enrollment.getCourse() != null) 822 assignmentEl.addAttribute("course", getId("course", enrollment.getCourse().getId())); 823 for (Section section : enrollment.getSections()) { 824 Element sectionEl = assignmentEl.addElement("section").addAttribute("id", 825 getId("section", section.getId())); 826 if (iShowNames) 827 sectionEl.setText(section.getName() + " " + 828 (section.getTime() == null ? " Arr Hrs" : " " + section.getTime().getLongName(true)) + 829 (section.getNrRooms() == 0 ? "" : " " + section.getPlacement().getRoomName(",")) + 830 (section.hasInstructors() ? " " + section.getInstructorNames(",") : "")); 831 } 832 } 833 834 /** 835 * Save linked sections 836 * @param root document root 837 */ 838 protected void saveLinkedSections(Element root) { 839 Element constrainstEl = root.addElement("constraints"); 840 for (LinkedSections linkedSections: getModel().getLinkedSections()) { 841 Element linkEl = constrainstEl.addElement("linked-sections"); 842 linkEl.addAttribute("mustBeUsed", linkedSections.isMustBeUsed() ? "true" : "false"); 843 for (Offering offering: linkedSections.getOfferings()) 844 for (Subpart subpart: linkedSections.getSubparts(offering)) 845 for (Section section: linkedSections.getSections(subpart)) 846 linkEl.addElement("section") 847 .addAttribute("offering", getId("offering", offering.getId())) 848 .addAttribute("id", getId("section", section.getId())); 849 } 850 } 851 852 /** 853 * Save travel times 854 * @param root document root 855 */ 856 protected void saveTravelTimes(Element root) { 857 if (getModel().getDistanceMetric() != null) { 858 Map<Long, Map<Long, Integer>> travelTimes = getModel().getDistanceMetric().getTravelTimes(); 859 if (travelTimes != null) { 860 Element travelTimesEl = root.addElement("travel-times"); 861 for (Map.Entry<Long, Map<Long, Integer>> e1: travelTimes.entrySet()) 862 for (Map.Entry<Long, Integer> e2: e1.getValue().entrySet()) 863 travelTimesEl.addElement("travel-time") 864 .addAttribute("id1", getId("room", e1.getKey().toString())) 865 .addAttribute("id2", getId("room", e2.getKey().toString())) 866 .addAttribute("minutes", e2.getValue().toString()); 867 } 868 } 869 } 870}