001    package net.sf.cpsolver.exam.reports;
002    
003    import java.util.ArrayList;
004    import java.util.Collections;
005    import java.util.Comparator;
006    import java.util.List;
007    import java.util.TreeSet;
008    
009    import net.sf.cpsolver.exam.criteria.StudentBackToBackConflicts;
010    import net.sf.cpsolver.exam.criteria.StudentDistanceBackToBackConflicts;
011    import net.sf.cpsolver.exam.model.Exam;
012    import net.sf.cpsolver.exam.model.ExamModel;
013    import net.sf.cpsolver.exam.model.ExamOwner;
014    import net.sf.cpsolver.exam.model.ExamPeriod;
015    import net.sf.cpsolver.exam.model.ExamPlacement;
016    import net.sf.cpsolver.exam.model.ExamRoomPlacement;
017    import net.sf.cpsolver.exam.model.ExamStudent;
018    import net.sf.cpsolver.ifs.util.CSVFile;
019    import net.sf.cpsolver.ifs.util.CSVFile.CSVField;
020    
021    /**
022     * Export student direct, back-to-back, and more than two exams a day conflicts
023     * into a CSV file. <br>
024     * <br>
025     * Usage:<br>
026     * <code>
027     * &nbsp;&nbsp;&nbsp;&nbsp;new ExamStudentConflictsBySectionCourse(model).report().save(file);
028     * </code> <br>
029     * <br>
030     * 
031     * @version ExamTT 1.2 (Examination Timetabling)<br>
032     *          Copyright (C) 2008 - 2010 Tomas Muller<br>
033     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
034     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
035     * <br>
036     *          This library is free software; you can redistribute it and/or modify
037     *          it under the terms of the GNU Lesser General Public License as
038     *          published by the Free Software Foundation; either version 3 of the
039     *          License, or (at your option) any later version. <br>
040     * <br>
041     *          This library is distributed in the hope that it will be useful, but
042     *          WITHOUT ANY WARRANTY; without even the implied warranty of
043     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
044     *          Lesser General Public License for more details. <br>
045     * <br>
046     *          You should have received a copy of the GNU Lesser General Public
047     *          License along with this library; if not see
048     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
049     */
050    public class ExamStudentConflictsBySectionCourse {
051        private ExamModel iModel = null;
052    
053        /**
054         * Constructor
055         * 
056         * @param model
057         *            examination timetabling model
058         */
059        public ExamStudentConflictsBySectionCourse(ExamModel model) {
060            iModel = model;
061        }
062    
063        private List<ExamOwner> getOwners(Exam exam) {
064            if (!exam.getOwners().isEmpty())
065                return exam.getOwners();
066            ExamOwner cs = new ExamOwner(exam, exam.getId(), exam.getName());
067            cs.getStudents().addAll(exam.getStudents());
068            List<ExamOwner> ret = new ArrayList<ExamOwner>(1);
069            ret.add(cs);
070            return ret;
071        }
072    
073        private List<ExamOwner> getOwners(Exam exam, ExamStudent student) {
074            List<ExamOwner> ret = new ArrayList<ExamOwner>(exam.getOwners(student));
075            if (ret.isEmpty()) {
076                ExamOwner cs = new ExamOwner(exam, exam.getId(), exam.getName());
077                cs.getStudents().add(student);
078                ret.add(cs);
079            }
080            Collections.sort(ret);
081            return ret;
082        }
083    
084        /**
085         * generate report
086         */
087        public CSVFile report() {
088            CSVFile csv = new CSVFile();
089            csv.setHeader(new CSVField[] { new CSVField("Section/Course"), new CSVField("Period"), new CSVField("Day"),
090                    new CSVField("Time"), new CSVField("Room"), new CSVField("Student"), new CSVField("Type"),
091                    new CSVField("Section/Course"), new CSVField("Period"), new CSVField("Time"), new CSVField("Room"),
092                    new CSVField("Distance") });
093            boolean isDayBreakBackToBack = ((StudentBackToBackConflicts)iModel.getCriterion(StudentBackToBackConflicts.class)).isDayBreakBackToBack();
094            double backToBackDistance = ((StudentDistanceBackToBackConflicts)iModel.getCriterion(StudentDistanceBackToBackConflicts.class)).getBackToBackDistance();
095            TreeSet<ExamOwner> courseSections = new TreeSet<ExamOwner>();
096            for (Exam exam : iModel.variables()) {
097                courseSections.addAll(getOwners(exam));
098            }
099            for (ExamOwner cs : courseSections) {
100                Exam exam = cs.getExam();
101                ExamPlacement placement = exam.getAssignment();
102                if (placement == null)
103                    continue;
104                String roomsThisExam = "";
105                for (ExamRoomPlacement room : placement.getRoomPlacements()) {
106                    if (roomsThisExam.length() > 0)
107                        roomsThisExam += ", ";
108                    roomsThisExam += room.getName();
109                }
110                ExamPeriod period = placement.getPeriod();
111                boolean csPrinted = false;
112                List<ExamStudent> students = new ArrayList<ExamStudent>(cs.getStudents());
113                Collections.sort(students, new Comparator<ExamStudent>() {
114                    @Override
115                    public int compare(ExamStudent s1, ExamStudent s2) {
116                        int cmp = s1.getName().compareTo(s2.getName());
117                        if (cmp != 0)
118                            return cmp;
119                        return Double.compare(s1.getId(), s2.getId());
120                    }
121                });
122                for (ExamStudent student : students) {
123                    boolean stdPrinted = false;
124                    int nrExams = student.getExams(period).size();
125                    if (nrExams > 1) {
126                        boolean typePrinted = false;
127                        for (Exam otherExam : student.getExams(period)) {
128                            if (otherExam.equals(exam))
129                                continue;
130                            ExamPlacement otherPlacement = otherExam.getAssignment();
131                            ExamPeriod otherPeriod = otherPlacement.getPeriod();
132                            String roomsOtherExam = "";
133                            for (ExamRoomPlacement room : otherPlacement.getRoomPlacements()) {
134                                if (roomsOtherExam.length() > 0)
135                                    roomsOtherExam += ", ";
136                                roomsOtherExam += room.getName();
137                            }
138                            boolean otherPrinted = false;
139                            for (ExamOwner ocs : getOwners(otherExam, student)) {
140                                csv.addLine(new CSVField[] { new CSVField(csPrinted ? "" : cs.getName()),
141                                        new CSVField(csPrinted ? "" : String.valueOf(1 + period.getIndex())),
142                                        new CSVField(csPrinted ? "" : period.getDayStr()),
143                                        new CSVField(csPrinted ? "" : period.getTimeStr()),
144                                        new CSVField(csPrinted ? "" : roomsThisExam),
145                                        new CSVField(stdPrinted ? "" : student.getName()),
146                                        new CSVField(typePrinted ? "" : "direct"), new CSVField(ocs.getName()),
147                                        new CSVField(otherPrinted ? "" : String.valueOf(1 + otherPeriod.getIndex())),
148                                        new CSVField(otherPrinted ? "" : otherPeriod.getTimeStr()),
149                                        new CSVField(otherPrinted ? "" : roomsOtherExam) });
150                                csPrinted = true;
151                                stdPrinted = true;
152                                typePrinted = true;
153                                otherPrinted = true;
154                            }
155                        }
156                    }
157                    if (nrExams > 0) {
158                        boolean typePrinted = false;
159                        List<ExamPeriod> periods = new ArrayList<ExamPeriod>(2);
160                        if (period.prev() != null && !student.getExams(period.prev()).isEmpty()
161                                && (!isDayBreakBackToBack || period.prev().getDay() == period.getDay()))
162                            periods.add(period.prev());
163                        if (period.next() != null && !student.getExams(period.next()).isEmpty()
164                                && (!isDayBreakBackToBack || period.next().getDay() == period.getDay()))
165                            periods.add(period.next());
166                        for (ExamPeriod otherPeriod : periods) {
167                            for (Exam otherExam : student.getExams(otherPeriod)) {
168                                ExamPlacement otherPlacement = otherExam.getAssignment();
169                                String roomsOtherExam = "";
170                                for (ExamRoomPlacement room : otherPlacement.getRoomPlacements()) {
171                                    if (roomsOtherExam.length() > 0)
172                                        roomsOtherExam += ", ";
173                                    roomsOtherExam += room.getName();
174                                }
175                                String distStr = "";
176                                if (backToBackDistance >= 0) {
177                                    double dist = placement.getDistanceInMeters(otherPlacement);
178                                    if (dist > 0)
179                                        distStr = String.valueOf(dist);
180                                }
181                                boolean otherPrinted = false;
182                                for (ExamOwner ocs : getOwners(otherExam, student)) {
183                                    csv.addLine(new CSVField[] { new CSVField(csPrinted ? "" : cs.getName()),
184                                            new CSVField(csPrinted ? "" : String.valueOf(1 + period.getIndex())),
185                                            new CSVField(csPrinted ? "" : period.getDayStr()),
186                                            new CSVField(csPrinted ? "" : period.getTimeStr()),
187                                            new CSVField(csPrinted ? "" : roomsThisExam),
188                                            new CSVField(stdPrinted ? "" : student.getName()),
189                                            new CSVField(typePrinted ? "" : "back-to-back"), new CSVField(ocs.getName()),
190                                            new CSVField(otherPrinted ? "" : String.valueOf(1 + otherPeriod.getIndex())),
191                                            new CSVField(otherPrinted ? "" : otherPeriod.getTimeStr()),
192                                            new CSVField(otherPrinted ? "" : roomsOtherExam),
193                                            new CSVField(otherPrinted ? "" : distStr), });
194                                    csPrinted = true;
195                                    stdPrinted = true;
196                                    typePrinted = true;
197                                    otherPrinted = true;
198                                }
199                            }
200                        }
201                    }
202                    int nrExamsADay = student.getExamsADay(period.getDay()).size();
203                    if (nrExamsADay > 2) {
204                        boolean typePrinted = false;
205                        for (Exam otherExam : student.getExamsADay(period.getDay())) {
206                            if (otherExam.equals(exam))
207                                continue;
208                            ExamPlacement otherPlacement = otherExam.getAssignment();
209                            ExamPeriod otherPeriod = otherPlacement.getPeriod();
210                            String roomsOtherExam = "";
211                            for (ExamRoomPlacement room : otherPlacement.getRoomPlacements()) {
212                                if (roomsOtherExam.length() > 0)
213                                    roomsOtherExam += ", ";
214                                roomsOtherExam += room.getName();
215                            }
216                            boolean otherPrinted = false;
217                            for (ExamOwner ocs : getOwners(otherExam, student)) {
218                                csv.addLine(new CSVField[] { new CSVField(csPrinted ? "" : cs.getName()),
219                                        new CSVField(csPrinted ? "" : String.valueOf(1 + period.getIndex())),
220                                        new CSVField(csPrinted ? "" : period.getDayStr()),
221                                        new CSVField(csPrinted ? "" : period.getTimeStr()),
222                                        new CSVField(csPrinted ? "" : roomsThisExam),
223                                        new CSVField(stdPrinted ? "" : student.getName()),
224                                        new CSVField(typePrinted ? "" : "more-2-day"), new CSVField(ocs.getName()),
225                                        new CSVField(otherPrinted ? "" : String.valueOf(1 + otherPeriod.getIndex())),
226                                        new CSVField(otherPrinted ? "" : otherPeriod.getTimeStr()),
227                                        new CSVField(otherPrinted ? "" : roomsOtherExam) });
228                                csPrinted = true;
229                                stdPrinted = true;
230                                typePrinted = true;
231                                otherPrinted = true;
232                            }
233                        }
234                    }
235                }
236            }
237            return csv;
238        }
239    }