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 * 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}