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