001 package net.sf.cpsolver.studentsct; 002 003 import java.io.File; 004 import java.io.FileOutputStream; 005 import java.io.IOException; 006 import java.text.DecimalFormat; 007 import java.util.BitSet; 008 import java.util.Date; 009 import java.util.Dictionary; 010 import java.util.Enumeration; 011 import java.util.Iterator; 012 013 import org.dom4j.Document; 014 import org.dom4j.DocumentHelper; 015 import org.dom4j.Element; 016 import org.dom4j.io.OutputFormat; 017 import org.dom4j.io.XMLWriter; 018 019 import net.sf.cpsolver.coursett.IdConvertor; 020 import net.sf.cpsolver.coursett.model.RoomLocation; 021 import net.sf.cpsolver.coursett.model.TimeLocation; 022 import net.sf.cpsolver.ifs.solver.Solver; 023 import net.sf.cpsolver.ifs.util.Progress; 024 import net.sf.cpsolver.ifs.util.ToolBox; 025 import net.sf.cpsolver.studentsct.model.AcademicAreaCode; 026 import net.sf.cpsolver.studentsct.model.Choice; 027 import net.sf.cpsolver.studentsct.model.Config; 028 import net.sf.cpsolver.studentsct.model.Course; 029 import net.sf.cpsolver.studentsct.model.CourseRequest; 030 import net.sf.cpsolver.studentsct.model.Enrollment; 031 import net.sf.cpsolver.studentsct.model.FreeTimeRequest; 032 import net.sf.cpsolver.studentsct.model.Offering; 033 import net.sf.cpsolver.studentsct.model.Request; 034 import net.sf.cpsolver.studentsct.model.Section; 035 import net.sf.cpsolver.studentsct.model.Student; 036 import net.sf.cpsolver.studentsct.model.Subpart; 037 038 /** 039 * Save student sectioning solution into an XML file. 040 * 041 * <br><br> 042 * Parameters: 043 * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr> 044 * <tr><td>General.Output</td><td>{@link String}</td><td>Folder with the output solution in XML format (solution.xml)</td></tr> 045 * <tr><td>Xml.ConvertIds</td><td>{@link Boolean}</td><td>If true, ids are converted (to be able to make input data public)</td></tr> 046 * <tr><td>Xml.ShowNames</td><td>{@link Boolean}</td><td>If false, names are not exported (to be able to make input data public)</td></tr> 047 * <tr><td>Xml.SaveBest</td><td>{@link Boolean}</td><td>If true, best solution is saved.</td></tr> 048 * <tr><td>Xml.SaveInitial</td><td>{@link Boolean}</td><td>If true, initial solution is saved.</td></tr> 049 * <tr><td>Xml.SaveCurrent</td><td>{@link Boolean}</td><td>If true, current solution is saved.</td></tr> 050 * <tr><td>Xml.SaveOnlineSectioningInfo</td><td>{@link Boolean}</td><td>If true, save online sectioning info (i.e., expected and held space of each section)</td></tr> 051 * <tr><td>Xml.SaveStudentInfo</td><td>{@link Boolean}</td><td>If true, save student information (i.e., academic area classification, major, minor)</td></tr> 052 * </table> 053 * <br><br> 054 * Usage:<br> 055 * <code> 056 * new StudentSectioningXMLSaver(solver).save(new File("solution.xml"));<br> 057 * </code> 058 * 059 * @version 060 * StudentSct 1.1 (Student Sectioning)<br> 061 * Copyright (C) 2007 Tomáš Müller<br> 062 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 063 * Lazenska 391, 76314 Zlin, Czech Republic<br> 064 * <br> 065 * This library is free software; you can redistribute it and/or 066 * modify it under the terms of the GNU Lesser General Public 067 * License as published by the Free Software Foundation; either 068 * version 2.1 of the License, or (at your option) any later version. 069 * <br><br> 070 * This library is distributed in the hope that it will be useful, 071 * but WITHOUT ANY WARRANTY; without even the implied warranty of 072 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 073 * Lesser General Public License for more details. 074 * <br><br> 075 * You should have received a copy of the GNU Lesser General Public 076 * License along with this library; if not, write to the Free Software 077 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 078 */ 079 080 public class StudentSectioningXMLSaver extends StudentSectioningSaver { 081 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(StudentSectioningXMLSaver.class); 082 private static DecimalFormat[] sDF = {new DecimalFormat(""),new DecimalFormat("0"),new DecimalFormat("00"),new DecimalFormat("000"),new DecimalFormat("0000"),new DecimalFormat("00000"),new DecimalFormat("000000"),new DecimalFormat("0000000")}; 083 private static DecimalFormat sStudentWeightFormat = new DecimalFormat("0.0000"); 084 private Solver iSolver = null; 085 private File iOutputFolder = null; 086 087 private boolean iSaveBest = false; 088 private boolean iSaveInitial = false; 089 private boolean iSaveCurrent = false; 090 private boolean iSaveOnlineSectioningInfo = false; 091 private boolean iSaveStudentInfo = true; 092 093 private boolean iConvertIds = false; 094 private boolean iShowNames = false; 095 096 /** 097 * Constructor 098 * @param solver student sectioning solver 099 */ 100 public StudentSectioningXMLSaver(Solver solver) { 101 super(solver); 102 iOutputFolder = new File(getModel().getProperties().getProperty("General.Output","."+File.separator+"output")); 103 iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", true); 104 iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", true); 105 iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", false); 106 iSaveOnlineSectioningInfo = getModel().getProperties().getPropertyBoolean("Xml.SaveOnlineSectioningInfo", true); 107 iSaveStudentInfo = getModel().getProperties().getPropertyBoolean("Xml.SaveStudentInfo", true); 108 iShowNames = getModel().getProperties().getPropertyBoolean("Xml.ShowNames",true); 109 iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds",false); 110 } 111 112 /** Convert bitset to a bit string */ 113 private static String bitset2string(BitSet b) { 114 StringBuffer sb = new StringBuffer(); 115 for (int i=0;i<b.length();i++) 116 sb.append(b.get(i)?"1":"0"); 117 return sb.toString(); 118 } 119 120 /** Generate id for given object with the given id */ 121 private String getId(String type, String id) { 122 if (!iConvertIds) return id.toString(); 123 return IdConvertor.getInstance().convert(type, id); 124 } 125 126 /** Generate id for given object with the given id */ 127 private String getId(String type, Number id) { 128 return getId(type, id.toString()); 129 } 130 131 /** Generate id for given object with the given id */ 132 private String getId(String type, long id) { 133 return getId(type, String.valueOf(id)); 134 } 135 136 /** Save an XML file */ 137 public void save() throws Exception { 138 save(null); 139 } 140 141 /** Save an XML file 142 * @param outFile output file 143 */ 144 public void save(File outFile) throws Exception { 145 if (outFile==null) 146 outFile = new File(iOutputFolder,"solution.xml"); 147 outFile.getParentFile().mkdirs(); 148 sLogger.debug("Writting XML data to:"+outFile); 149 150 Document document = DocumentHelper.createDocument(); 151 document.addComment("Student Sectioning"); 152 153 if ((iSaveCurrent || iSaveBest)) { // && !getModel().assignedVariables().isEmpty() 154 StringBuffer comments = new StringBuffer("Solution Info:\n"); 155 Dictionary solutionInfo=(getSolution()==null?getModel().getExtendedInfo():getSolution().getExtendedInfo()); 156 for (Enumeration e=ToolBox.sortEnumeration(solutionInfo.keys());e.hasMoreElements();) { 157 String key = (String)e.nextElement(); 158 Object value = solutionInfo.get(key); 159 comments.append(" "+key+": "+value+"\n"); 160 } 161 document.addComment(comments.toString()); 162 } 163 164 Element root = document.addElement("sectioning"); 165 root.addAttribute("version","1.0"); 166 root.addAttribute("initiative", getModel().getProperties().getProperty("Data.Initiative")); 167 root.addAttribute("term", getModel().getProperties().getProperty("Data.Term")); 168 root.addAttribute("year", getModel().getProperties().getProperty("Data.Year")); 169 root.addAttribute("created", String.valueOf(new Date())); 170 171 Element offeringsEl = root.addElement("offerings"); 172 for (Enumeration e=getModel().getOfferings().elements();e.hasMoreElements();) { 173 Offering offering = (Offering)e.nextElement(); 174 Element offeringEl = offeringsEl.addElement("offering"); 175 offeringEl.addAttribute("id", getId("offering", offering.getId())); 176 if (iShowNames) 177 offeringEl.addAttribute("name", offering.getName()); 178 for (Enumeration f=offering.getCourses().elements();f.hasMoreElements();) { 179 Course course = (Course)f.nextElement(); 180 Element courseEl = offeringEl.addElement("course"); 181 courseEl.addAttribute("id", getId("course", course.getId())); 182 if (iShowNames) 183 courseEl.addAttribute("subjectArea", course.getSubjectArea()); 184 if (iShowNames) 185 courseEl.addAttribute("courseNbr", course.getCourseNumber()); 186 if (iShowNames && course.getLimit()!=0) 187 courseEl.addAttribute("limit", String.valueOf(course.getLimit())); 188 if (iShowNames && course.getProjected()!=0) 189 courseEl.addAttribute("projected", String.valueOf(course.getProjected())); 190 } 191 for (Enumeration f=offering.getConfigs().elements();f.hasMoreElements();) { 192 Config config = (Config)f.nextElement(); 193 Element configEl = offeringEl.addElement("config"); 194 configEl.addAttribute("id", getId("config", config.getId())); 195 if (iShowNames) 196 configEl.addAttribute("name", config.getName()); 197 for (Enumeration g=config.getSubparts().elements();g.hasMoreElements();) { 198 Subpart subpart = (Subpart)g.nextElement(); 199 Element subpartEl = configEl.addElement("subpart"); 200 subpartEl.addAttribute("id", getId("subpart", subpart.getId())); 201 subpartEl.addAttribute("itype", subpart.getInstructionalType()); 202 if (subpart.getParent()!=null) 203 subpartEl.addAttribute("parent", getId("subpart", subpart.getParent().getId())); 204 if (iShowNames) 205 subpartEl.addAttribute("name", subpart.getName()); 206 for (Enumeration h=subpart.getSections().elements();h.hasMoreElements();) { 207 Section section = (Section)h.nextElement(); 208 Element sectionEl = subpartEl.addElement("section"); 209 sectionEl.addAttribute("id", getId("section", section.getId())); 210 sectionEl.addAttribute("limit", String.valueOf(section.getLimit())); 211 if (section.getParent()!=null) 212 sectionEl.addAttribute("parent", getId("section", section.getParent().getId())); 213 if (iShowNames && section.getChoice().getInstructorIds()!=null) 214 sectionEl.addAttribute("instructorIds", section.getChoice().getInstructorIds()); 215 if (iShowNames && section.getChoice().getInstructorNames()!=null) 216 sectionEl.addAttribute("instructorNames", section.getChoice().getInstructorNames()); 217 if (iShowNames) sectionEl.addAttribute("name", section.getName()); 218 if (section.getPlacement()!=null) { 219 TimeLocation tl = section.getPlacement().getTimeLocation(); 220 if (tl!=null) { 221 Element timeLocationEl = (Element)sectionEl.addElement("time"); 222 timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl.getDayCode())))); 223 timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 224 timeLocationEl.addAttribute("length", String.valueOf(tl.getLength())); 225 if (tl.getBreakTime()!=0) 226 timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 227 if (iShowNames && tl.getTimePatternId()!=null) 228 timeLocationEl.addAttribute("pattern", getId("timePattern", tl.getTimePatternId())); 229 if (iShowNames && tl.getDatePatternId()!=null) 230 timeLocationEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 231 if (iShowNames && tl.getDatePatternName()!=null && tl.getDatePatternName().length()>0) 232 timeLocationEl.addAttribute("datePatternName", tl.getDatePatternName()); 233 timeLocationEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 234 if (iShowNames) 235 timeLocationEl.setText(tl.getLongName()); 236 } 237 for (Enumeration i=section.getRooms().elements();i.hasMoreElements();) { 238 RoomLocation rl = (RoomLocation)i.nextElement(); 239 Element roomLocationEl = (Element)sectionEl.addElement("room"); 240 roomLocationEl.addAttribute("id", getId("room", rl.getId())); 241 if (iShowNames && rl.getBuildingId()!=null) 242 roomLocationEl.addAttribute("building", getId("building", rl.getBuildingId())); 243 if (iShowNames && rl.getName()!=null) 244 roomLocationEl.addAttribute("name",rl.getName()); 245 roomLocationEl.addAttribute("capacity", String.valueOf(rl.getRoomSize())); 246 if (rl.getPosX()>0 || rl.getPosY()>0) 247 roomLocationEl.addAttribute("location", rl.getPosX()+","+rl.getPosY()); 248 if (rl.getIgnoreTooFar()) 249 roomLocationEl.addAttribute("ignoreTooFar", "true"); 250 } 251 } 252 if (iSaveOnlineSectioningInfo) { 253 if (section.getSpaceHeld()!=0.0) 254 sectionEl.addAttribute("hold", sStudentWeightFormat.format(section.getSpaceHeld())); 255 if (section.getSpaceExpected()!=0.0) 256 sectionEl.addAttribute("expect", sStudentWeightFormat.format(section.getSpaceExpected())); 257 } 258 } 259 } 260 } 261 } 262 263 Element studentsEl = root.addElement("students"); 264 for (Enumeration e=getModel().getStudents().elements();e.hasMoreElements();) { 265 Student student = (Student)e.nextElement(); 266 Element studentEl = studentsEl.addElement("student"); 267 studentEl.addAttribute("id", getId("student", student.getId())); 268 if (student.isDummy()) 269 studentEl.addAttribute("dummy", "true"); 270 if (iSaveStudentInfo) { 271 for (Enumeration f=student.getAcademicAreaClasiffications().elements();f.hasMoreElements();) { 272 AcademicAreaCode aac = (AcademicAreaCode)f.nextElement(); 273 Element aacEl = studentEl.addElement("classification"); 274 if (aac.getArea()!=null) 275 aacEl.addAttribute("area", aac.getArea()); 276 if (aac.getCode()!=null) 277 aacEl.addAttribute("code", aac.getCode()); 278 } 279 for (Enumeration f=student.getMajors().elements();f.hasMoreElements();) { 280 AcademicAreaCode aac = (AcademicAreaCode)f.nextElement(); 281 Element aacEl = studentEl.addElement("major"); 282 if (aac.getArea()!=null) 283 aacEl.addAttribute("area", aac.getArea()); 284 if (aac.getCode()!=null) 285 aacEl.addAttribute("code", aac.getCode()); 286 } 287 for (Enumeration f=student.getMinors().elements();f.hasMoreElements();) { 288 AcademicAreaCode aac = (AcademicAreaCode)f.nextElement(); 289 Element aacEl = studentEl.addElement("minor"); 290 if (aac.getArea()!=null) 291 aacEl.addAttribute("area", aac.getArea()); 292 if (aac.getCode()!=null) 293 aacEl.addAttribute("code", aac.getCode()); 294 } 295 } 296 for (Enumeration f=student.getRequests().elements();f.hasMoreElements();) { 297 Request request = (Request)f.nextElement(); 298 if (request instanceof FreeTimeRequest) { 299 Element requestEl = studentEl.addElement("freeTime"); 300 FreeTimeRequest ft = (FreeTimeRequest)request; 301 requestEl.addAttribute("id", getId("request", request.getId())); 302 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 303 if (request.isAlternative()) 304 requestEl.addAttribute("alternative", "true"); 305 if (request.getWeight()!=1.0) 306 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 307 TimeLocation tl = ft.getTime(); 308 if (tl!=null) { 309 requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl.getDayCode())))); 310 requestEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 311 requestEl.addAttribute("length", String.valueOf(tl.getLength())); 312 if (iShowNames && tl.getDatePatternId()!=null) 313 requestEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 314 requestEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 315 if (iShowNames) 316 requestEl.setText(tl.getLongName()); 317 } 318 if (iSaveInitial && request.getInitialAssignment()!=null) { 319 Element assignmentEl = requestEl.addElement("initial"); 320 } 321 if (iSaveCurrent && request.getAssignment()!=null) { 322 Element assignmentEl = requestEl.addElement("current"); 323 } 324 if (iSaveBest && request.getBestAssignment()!=null) { 325 Element assignmentEl = requestEl.addElement("best"); 326 } 327 } else if (request instanceof CourseRequest) { 328 CourseRequest cr = (CourseRequest)request; 329 Element requestEl = studentEl.addElement("course"); 330 requestEl.addAttribute("id", getId("request", request.getId())); 331 requestEl.addAttribute("priority", String.valueOf(request.getPriority())); 332 if (request.isAlternative()) 333 requestEl.addAttribute("alternative", "true"); 334 if (request.getWeight()!=1.0) 335 requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight())); 336 if (cr.isWaitlist()) 337 requestEl.addAttribute("waitlist", "true"); 338 boolean first = true; 339 for (Enumeration g=cr.getCourses().elements();g.hasMoreElements();) { 340 Course course = (Course)g.nextElement(); 341 if (first) 342 requestEl.addAttribute("course", getId("course", course.getId())); 343 else 344 requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId())); 345 first = false; 346 } 347 for (Iterator i=cr.getWaitlistedChoices().iterator();i.hasNext();) { 348 Choice choice = (Choice)i.next(); 349 Element choiceEl = requestEl.addElement("waitlisted"); 350 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 351 choiceEl.setText(choice.getId()); 352 } 353 for (Iterator i=cr.getSelectedChoices().iterator();i.hasNext();) { 354 Choice choice = (Choice)i.next(); 355 Element choiceEl = requestEl.addElement("selected"); 356 choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId())); 357 choiceEl.setText(choice.getId()); 358 } 359 if (iSaveInitial && request.getInitialAssignment()!=null) { 360 Element assignmentEl = requestEl.addElement("initial"); 361 Enrollment enrollment = (Enrollment)request.getInitialAssignment(); 362 for (Iterator i=enrollment.getAssignments().iterator();i.hasNext();) { 363 Section section = (Section)i.next(); 364 Element sectionEl = assignmentEl.addElement("section").addAttribute("id", getId("section", section.getId())); 365 if (iShowNames) 366 sectionEl.setText(section.getName()+" "+ 367 (section.getTime()==null?" Arr Hrs":" "+section.getTime().getLongName())+ 368 (section.getNrRooms()==0?"":" "+section.getPlacement().getRoomName(","))+ 369 (section.getChoice().getInstructorNames()==null?"":" "+section.getChoice().getInstructorNames())); 370 } 371 } 372 if (iSaveCurrent && request.getAssignment()!=null) { 373 Element assignmentEl = requestEl.addElement("current"); 374 Enrollment enrollment = (Enrollment)request.getAssignment(); 375 for (Iterator i=enrollment.getAssignments().iterator();i.hasNext();) { 376 Section section = (Section)i.next(); 377 Element sectionEl = assignmentEl.addElement("section").addAttribute("id", getId("section", section.getId())); 378 if (iShowNames) 379 sectionEl.setText(section.getName()+" "+ 380 (section.getTime()==null?" Arr Hrs":" "+section.getTime().getLongName())+ 381 (section.getNrRooms()==0?"":" "+section.getPlacement().getRoomName(","))+ 382 (section.getChoice().getInstructorNames()==null?"":" "+section.getChoice().getInstructorNames())); 383 } 384 } 385 if (iSaveBest && request.getBestAssignment()!=null) { 386 Element assignmentEl = requestEl.addElement("best"); 387 Enrollment enrollment = (Enrollment)request.getBestAssignment(); 388 for (Iterator i=enrollment.getAssignments().iterator();i.hasNext();) { 389 Section section = (Section)i.next(); 390 Element sectionEl = assignmentEl.addElement("section").addAttribute("id", getId("section", section.getId())); 391 if (iShowNames) 392 sectionEl.setText(section.getName()+" "+ 393 (section.getTime()==null?" Arr Hrs":" "+section.getTime().getLongName())+ 394 (section.getNrRooms()==0?"":" "+section.getPlacement().getRoomName(","))+ 395 (section.getChoice().getInstructorNames()==null?"":" "+section.getChoice().getInstructorNames())); 396 } 397 } 398 } 399 } 400 } 401 402 if (iShowNames) { 403 Progress.getInstance(getModel()).save(root); 404 } 405 406 FileOutputStream fos = null; 407 try { 408 fos = new FileOutputStream(outFile); 409 (new XMLWriter(fos,OutputFormat.createPrettyPrint())).write(document); 410 fos.flush();fos.close();fos=null; 411 } finally { 412 try { 413 if (fos!=null) fos.close(); 414 } catch (IOException e) {} 415 } 416 417 if (iConvertIds) IdConvertor.getInstance().save(); 418 } 419 420 421 }