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