001 package net.sf.cpsolver.studentsct; 002 003 import java.io.File; 004 import java.util.BitSet; 005 import java.util.Enumeration; 006 import java.util.HashSet; 007 import java.util.Hashtable; 008 import java.util.Iterator; 009 import java.util.Vector; 010 011 import org.dom4j.Document; 012 import org.dom4j.Element; 013 import org.dom4j.io.SAXReader; 014 015 import net.sf.cpsolver.coursett.model.Placement; 016 import net.sf.cpsolver.coursett.model.RoomLocation; 017 import net.sf.cpsolver.coursett.model.TimeLocation; 018 import net.sf.cpsolver.ifs.util.Progress; 019 import net.sf.cpsolver.studentsct.filter.StudentFilter; 020 import net.sf.cpsolver.studentsct.model.AcademicAreaCode; 021 import net.sf.cpsolver.studentsct.model.Choice; 022 import net.sf.cpsolver.studentsct.model.Config; 023 import net.sf.cpsolver.studentsct.model.Course; 024 import net.sf.cpsolver.studentsct.model.CourseRequest; 025 import net.sf.cpsolver.studentsct.model.Enrollment; 026 import net.sf.cpsolver.studentsct.model.FreeTimeRequest; 027 import net.sf.cpsolver.studentsct.model.Offering; 028 import net.sf.cpsolver.studentsct.model.Request; 029 import net.sf.cpsolver.studentsct.model.Section; 030 import net.sf.cpsolver.studentsct.model.Student; 031 import net.sf.cpsolver.studentsct.model.Subpart; 032 033 /** 034 * Load student sectioning model from an XML file. 035 * 036 * <br><br> 037 * Parameters: 038 * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr> 039 * <tr><td>General.Input</td><td>{@link String}</td><td>Path of an XML file to be loaded</td></tr> 040 * <tr><td>Xml.LoadBest</td><td>{@link Boolean}</td><td>If true, load best assignments</td></tr> 041 * <tr><td>Xml.LoadInitial</td><td>{@link Boolean}</td><td>If false, load initial assignments</td></tr> 042 * <tr><td>Xml.LoadCurrent</td><td>{@link Boolean}</td><td>If true, load current assignments</td></tr> 043 * <tr><td>Xml.LoadOfferings</td><td>{@link Boolean}</td><td>If true, load offerings (and their stucture, i.e., courses, configurations, subparts and sections)</td></tr> 044 * <tr><td>Xml.LoadStudents</td><td>{@link Boolean}</td><td>If true, load students (and their requests)</td></tr> 045 * <tr><td>Xml.StudentFilter</td><td>{@link StudentFilter}</td><td>If provided, students are filtered by the given student filter</td></tr> 046 * </table> 047 * 048 * <br><br> 049 * Usage:<br> 050 * <code> 051 * StudentSectioningModel model = new StudentSectioningModel(cfg);<br> 052 * new StudentSectioningXMLLoader(model).load();<br> 053 * </code> 054 * 055 * @version 056 * StudentSct 1.1 (Student Sectioning)<br> 057 * Copyright (C) 2007 Tomáš Müller<br> 058 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 059 * Lazenska 391, 76314 Zlin, Czech Republic<br> 060 * <br> 061 * This library is free software; you can redistribute it and/or 062 * modify it under the terms of the GNU Lesser General Public 063 * License as published by the Free Software Foundation; either 064 * version 2.1 of the License, or (at your option) any later version. 065 * <br><br> 066 * This library is distributed in the hope that it will be useful, 067 * but WITHOUT ANY WARRANTY; without even the implied warranty of 068 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 069 * Lesser General Public License for more details. 070 * <br><br> 071 * You should have received a copy of the GNU Lesser General Public 072 * License along with this library; if not, write to the Free Software 073 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 074 */ 075 076 public class StudentSectioningXMLLoader extends StudentSectioningLoader { 077 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(StudentSectioningXMLLoader.class); 078 079 private File iInputFile; 080 private boolean iLoadBest = false; 081 private boolean iLoadInitial = false; 082 private boolean iLoadCurrent = false; 083 private boolean iLoadOfferings = true; 084 private boolean iLoadStudents = true; 085 private StudentFilter iStudentFilter = null; 086 087 /** 088 * Constructor 089 * @param model student sectioning model 090 */ 091 public StudentSectioningXMLLoader(StudentSectioningModel model) { 092 super(model); 093 iInputFile = new File(getModel().getProperties().getProperty("General.Input","."+File.separator+"solution.xml")); 094 iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true); 095 iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true); 096 iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true); 097 iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true); 098 iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true); 099 if (getModel().getProperties().getProperty("Xml.StudentFilter")!=null) { 100 try { 101 iStudentFilter = (StudentFilter) 102 Class.forName(getModel().getProperties().getProperty("Xml.StudentFilter")). 103 getConstructor(new Class[]{}). 104 newInstance(new Object[]{}); 105 } catch (Exception e) { 106 sLogger.error("Unable to create student filter, reason: "+e.getMessage(),e); 107 } 108 } 109 } 110 111 /** Set input file (e.g., if it is not set by General.Input property) */ 112 public void setInputFile(File inputFile) { 113 iInputFile=inputFile; 114 } 115 116 /** Set student filter */ 117 public void setStudentFilter(StudentFilter filter) { 118 iStudentFilter = filter; 119 } 120 121 /** Set whether to load students */ 122 public void setLoadStudents(boolean loadStudents) { 123 iLoadStudents = loadStudents; 124 } 125 126 /** Set whether to load offerings */ 127 public void setLoadOfferings(boolean loadOfferings) { 128 iLoadOfferings = loadOfferings; 129 } 130 131 /** Create BitSet from a bit string */ 132 private static BitSet createBitSet(String bitString) { 133 BitSet ret = new BitSet(bitString.length()); 134 for (int i=0;i<bitString.length();i++) 135 if (bitString.charAt(i)=='1') ret.set(i); 136 return ret; 137 } 138 139 /** Load the file */ 140 public void load() throws Exception { 141 sLogger.debug("Reading XML data from "+iInputFile); 142 143 Document document = (new SAXReader()).read(iInputFile); 144 Element root = document.getRootElement(); 145 sLogger.debug("Root element: "+root.getName()); 146 if (!"sectioning".equals(root.getName())) { 147 sLogger.error("Given XML file is not student sectioning problem."); 148 return; 149 } 150 151 Progress.getInstance(getModel()).load(root, true); 152 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 153 154 if (root.attributeValue("term")!=null) 155 getModel().getProperties().setProperty("Data.Term", root.attributeValue("term")); 156 if (root.attributeValue("year")!=null) 157 getModel().getProperties().setProperty("Data.Year", root.attributeValue("year")); 158 if (root.attributeValue("initiative")!=null) 159 getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative")); 160 String version = root.attributeValue("version"); 161 162 163 Hashtable offeringTable = new Hashtable(); 164 Hashtable courseTable = new Hashtable(); 165 166 if (iLoadOfferings && root.element("offerings")!=null) { 167 Hashtable subpartTable = new Hashtable(); 168 Hashtable sectionTable = new Hashtable(); 169 for (Iterator i=root.element("offerings").elementIterator("offering");i.hasNext();) { 170 Element offeringEl = (Element)i.next(); 171 Offering offering = new Offering( 172 Long.parseLong(offeringEl.attributeValue("id")), 173 offeringEl.attributeValue("name","O"+offeringEl.attributeValue("id"))); 174 offeringTable.put(new Long(offering.getId()), offering); 175 getModel().addOffering(offering); 176 for (Iterator j=offeringEl.elementIterator("course");j.hasNext();) { 177 Element courseEl = (Element)j.next(); 178 Course course = new Course( 179 Long.parseLong(courseEl.attributeValue("id")), 180 courseEl.attributeValue("subjectArea",""), 181 courseEl.attributeValue("courseNbr","C"+courseEl.attributeValue("id")), 182 offering, 183 Integer.parseInt(courseEl.attributeValue("limit","0")), 184 Integer.parseInt(courseEl.attributeValue("projected","0")) 185 ); 186 courseTable.put(new Long(course.getId()), course); 187 } 188 for (Iterator j=offeringEl.elementIterator("config");j.hasNext();) { 189 Element configEl = (Element)j.next(); 190 Config config = new Config( 191 Long.parseLong(configEl.attributeValue("id")), 192 configEl.attributeValue("name","G"+configEl.attributeValue("id")), 193 offering); 194 for (Iterator k=configEl.elementIterator("subpart");k.hasNext();) { 195 Element subpartEl = (Element)k.next(); 196 Subpart parentSubpart = null; 197 if (subpartEl.attributeValue("parent")!=null) 198 parentSubpart = (Subpart)subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent"))); 199 Subpart subpart = new Subpart( 200 Long.parseLong(subpartEl.attributeValue("id")), 201 subpartEl.attributeValue("itype"), 202 subpartEl.attributeValue("name","P"+subpartEl.attributeValue("id")), 203 config, 204 parentSubpart); 205 subpartTable.put(new Long(subpart.getId()),subpart); 206 for (Iterator l=subpartEl.elementIterator("section");l.hasNext();) { 207 Element sectionEl = (Element)l.next(); 208 Section parentSection = null; 209 if (sectionEl.attributeValue("parent")!=null) 210 parentSection = (Section)sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent"))); 211 TimeLocation time = null; 212 Element timeEl = sectionEl.element("time"); 213 if (timeEl!=null) { 214 time = new TimeLocation( 215 Integer.parseInt(timeEl.attributeValue("days"),2), 216 Integer.parseInt(timeEl.attributeValue("start")), 217 Integer.parseInt(timeEl.attributeValue("length")), 218 0, 0, 219 timeEl.attributeValue("datePattern")==null?null:Long.valueOf(timeEl.attributeValue("datePattern")), 220 timeEl.attributeValue("datePatternName",""), 221 createBitSet(timeEl.attributeValue("dates")), 222 Integer.parseInt(timeEl.attributeValue("breakTime","0"))); 223 if (timeEl.attributeValue("pattern")!=null) 224 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 225 } 226 Vector rooms = new Vector(); 227 for (Iterator m=sectionEl.elementIterator("room");m.hasNext();) { 228 Element roomEl = (Element)m.next(); 229 int posX=-1, posY=-1; 230 if (roomEl.attributeValue("location")!=null) { 231 String loc = roomEl.attributeValue("location"); 232 posX = Integer.parseInt(loc.substring(0,loc.indexOf(','))); 233 posY = Integer.parseInt(loc.substring(loc.indexOf(',')+1)); 234 } 235 RoomLocation room = new RoomLocation( 236 Long.valueOf(roomEl.attributeValue("id")), 237 roomEl.attributeValue("name","R"+roomEl.attributeValue("id")), 238 roomEl.attributeValue("building")==null?null:Long.valueOf(roomEl.attributeValue("building")), 239 0, 240 Integer.parseInt(roomEl.attributeValue("capacity")), 241 posX, posY, 242 "true".equals(roomEl.attributeValue("ignoreTooFar")), 243 null); 244 rooms.add(room); 245 } 246 Placement placement = (time==null?null:new Placement(null, time, rooms)); 247 Section section = new Section( 248 Long.parseLong(sectionEl.attributeValue("id")), 249 Integer.parseInt(sectionEl.attributeValue("limit")), 250 sectionEl.attributeValue("name","S"+sectionEl.attributeValue("id")), 251 subpart, 252 placement, 253 sectionEl.attributeValue("instructorIds"), 254 sectionEl.attributeValue("instructorNames"), 255 parentSection); 256 sectionTable.put(new Long(section.getId()), section); 257 section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0"))); 258 section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0"))); 259 } 260 } 261 } 262 } 263 } else { 264 for (Enumeration e=getModel().getOfferings().elements();e.hasMoreElements();) { 265 Offering offering = (Offering)e.nextElement(); 266 offeringTable.put(new Long(offering.getId()), offering); 267 for (Enumeration f=offering.getCourses().elements();f.hasMoreElements();) { 268 Course course = (Course)f.nextElement(); 269 courseTable.put(new Long(course.getId()), course); 270 } 271 } 272 } 273 274 if (iLoadStudents && root.element("students")!=null) { 275 Vector bestEnrollments = new Vector(); 276 Vector currentEnrollments = new Vector(); 277 for (Iterator i=root.element("students").elementIterator("student");i.hasNext();) { 278 Element studentEl = (Element)i.next(); 279 Student student = new Student( 280 Long.parseLong(studentEl.attributeValue("id")), 281 "true".equals(studentEl.attributeValue("dummy"))); 282 for (Iterator j=studentEl.elementIterator();j.hasNext();) { 283 Element requestEl = (Element)j.next(); 284 if ("classification".equals(requestEl.getName())) { 285 student.getAcademicAreaClasiffications().add(new AcademicAreaCode(requestEl.attributeValue("area"),requestEl.attributeValue("code"))); 286 } else if ("major".equals(requestEl.getName())) { 287 student.getMajors().add(new AcademicAreaCode(requestEl.attributeValue("area"),requestEl.attributeValue("code"))); 288 } else if ("minor".equals(requestEl.getName())) { 289 student.getMinors().add(new AcademicAreaCode(requestEl.attributeValue("area"),requestEl.attributeValue("code"))); 290 } 291 } 292 if (iStudentFilter!=null && !iStudentFilter.accept(student)) continue; 293 for (Iterator j=studentEl.elementIterator();j.hasNext();) { 294 Element requestEl = (Element)j.next(); 295 if ("freeTime".equals(requestEl.getName())) { 296 TimeLocation time = new TimeLocation( 297 Integer.parseInt(requestEl.attributeValue("days"),2), 298 Integer.parseInt(requestEl.attributeValue("start")), 299 Integer.parseInt(requestEl.attributeValue("length")), 300 0, 0, 301 requestEl.attributeValue("datePattern")==null?null:Long.valueOf(requestEl.attributeValue("datePattern")), 302 "", 303 createBitSet(requestEl.attributeValue("dates")), 304 0); 305 FreeTimeRequest request = new FreeTimeRequest( 306 Long.parseLong(requestEl.attributeValue("id")), 307 Integer.parseInt(requestEl.attributeValue("priority")), 308 "true".equals(requestEl.attributeValue("alternative")), 309 student, 310 time); 311 if (requestEl.attributeValue("weight")!=null) 312 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 313 if (iLoadBest && requestEl.element("best")!=null) 314 bestEnrollments.add(request.createEnrollment()); 315 if (iLoadInitial && requestEl.element("initial")!=null) 316 request.setInitialAssignment(request.createEnrollment()); 317 if (iLoadCurrent && requestEl.element("current")!=null) 318 currentEnrollments.add(request.createEnrollment()); 319 } else if ("course".equals(requestEl.getName())) { 320 Vector courses = new Vector(); 321 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 322 for (Iterator k=requestEl.elementIterator("alternative");k.hasNext();) 323 courses.add(courseTable.get(Long.valueOf(((Element)k.next()).attributeValue("course")))); 324 CourseRequest courseRequest = new CourseRequest( 325 Long.parseLong(requestEl.attributeValue("id")), 326 Integer.parseInt(requestEl.attributeValue("priority")), 327 "true".equals(requestEl.attributeValue("alternative")), 328 student, 329 courses, 330 "true".equals(requestEl.attributeValue("waitlist"))); 331 if (requestEl.attributeValue("weight")!=null) 332 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 333 for (Iterator k=requestEl.elementIterator("waitlisted");k.hasNext();) { 334 Element choiceEl = (Element)k.next(); 335 courseRequest.getWaitlistedChoices().add(new Choice( 336 (Offering)offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), 337 choiceEl.getText())); 338 } 339 for (Iterator k=requestEl.elementIterator("selected");k.hasNext();) { 340 Element choiceEl = (Element)k.next(); 341 courseRequest.getSelectedChoices().add(new Choice( 342 (Offering)offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), 343 choiceEl.getText())); 344 } 345 Element initialEl = requestEl.element("initial"); 346 if (iLoadInitial && initialEl!=null) { 347 HashSet sections = new HashSet(); 348 for (Iterator k=initialEl.elementIterator("section");k.hasNext();) { 349 Element sectionEl = (Element)k.next(); 350 Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id"))); 351 sections.add(section); 352 } 353 if (!sections.isEmpty()) 354 courseRequest.setInitialAssignment(courseRequest.createEnrollment(sections)); 355 } 356 Element currentEl = requestEl.element("current"); 357 if (iLoadCurrent && currentEl!=null) { 358 HashSet sections = new HashSet(); 359 for (Iterator k=currentEl.elementIterator("section");k.hasNext();) { 360 Element sectionEl = (Element)k.next(); 361 Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id"))); 362 sections.add(section); 363 } 364 if (!sections.isEmpty()) 365 currentEnrollments.add(courseRequest.createEnrollment(sections)); 366 } 367 Element bestEl = requestEl.element("best"); 368 if (iLoadBest && bestEl!=null) { 369 HashSet sections = new HashSet(); 370 for (Iterator k=bestEl.elementIterator("section");k.hasNext();) { 371 Element sectionEl = (Element)k.next(); 372 Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id"))); 373 sections.add(section); 374 } 375 if (!sections.isEmpty()) 376 bestEnrollments.add(courseRequest.createEnrollment(sections)); 377 } 378 } 379 } 380 getModel().addStudent(student); 381 } 382 383 if (!bestEnrollments.isEmpty()) { 384 for (Enumeration e=bestEnrollments.elements();e.hasMoreElements();) { 385 Enrollment enrollment = (Enrollment)e.nextElement(); 386 Hashtable conflicts = getModel().conflictConstraints(enrollment); 387 if (conflicts.isEmpty()) 388 enrollment.variable().assign(0, enrollment); 389 else { 390 sLogger.warn("Enrollment "+enrollment+" conflicts with "+conflicts); 391 } 392 } 393 getModel().saveBest(); 394 } 395 396 if (!currentEnrollments.isEmpty()) { 397 for (Enumeration e=getModel().variables().elements();e.hasMoreElements();) { 398 Request request = (Request)e.nextElement(); 399 if (request.getAssignment()!=null) 400 request.unassign(0); 401 } 402 for (Enumeration e=currentEnrollments.elements();e.hasMoreElements();) { 403 Enrollment enrollment = (Enrollment)e.nextElement(); 404 enrollment.variable().assign(0, enrollment); 405 } 406 } 407 } 408 409 sLogger.debug("Model successfully loaded."); 410 } 411 412 413 }