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    }