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