001package net.sf.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 net.sf.cpsolver.exam.criteria.StudentBackToBackConflicts; 010import net.sf.cpsolver.exam.criteria.StudentDistanceBackToBackConflicts; 011import net.sf.cpsolver.exam.model.Exam; 012import net.sf.cpsolver.exam.model.ExamModel; 013import net.sf.cpsolver.exam.model.ExamOwner; 014import net.sf.cpsolver.exam.model.ExamPeriod; 015import net.sf.cpsolver.exam.model.ExamPlacement; 016import net.sf.cpsolver.exam.model.ExamRoomPlacement; 017import net.sf.cpsolver.exam.model.ExamStudent; 018import net.sf.cpsolver.ifs.util.CSVFile; 019import 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 * 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 Tomáš Müller<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 */ 050public 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}