001package org.cpsolver.studentsct; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Iterator; 006import java.util.List; 007import java.util.Set; 008 009import org.cpsolver.ifs.assignment.Assignment; 010import org.cpsolver.ifs.assignment.DefaultSingleAssignment; 011import org.cpsolver.ifs.model.Model; 012import org.cpsolver.ifs.model.Neighbour; 013import org.cpsolver.ifs.solution.Solution; 014import org.cpsolver.ifs.util.DataProperties; 015import org.cpsolver.studentsct.constraint.SectionLimit; 016import org.cpsolver.studentsct.constraint.StudentConflict; 017import org.cpsolver.studentsct.heuristics.selection.BranchBoundSelection; 018import org.cpsolver.studentsct.model.Course; 019import org.cpsolver.studentsct.model.CourseRequest; 020import org.cpsolver.studentsct.model.Enrollment; 021import org.cpsolver.studentsct.model.Request; 022import org.cpsolver.studentsct.model.Student; 023 024 025/** 026 * Online student sectioning test (using {@link BranchBoundSelection} 027 * selection). This class is used by the online student sectioning mock-up page. <br> 028 * <br> 029 * Usage: 030 * <pre><code> 031 * StudentSctBBTest test = new StudentSctBBTest(student); //student already has all his/her requests defined<br> 032 * Solution sectioningSolution = test.getSolution(); //solution contains only one student (the given one) with his/her schedule<br> 033 * Vector sectioningMessages = test.getMessages(); //sectioning messages (to be printed in the GUI). 034 * </code></pre> 035 * 036 * @version StudentSct 1.3 (Student Sectioning)<br> 037 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 038 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 039 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 040 * <br> 041 * This library is free software; you can redistribute it and/or modify 042 * it under the terms of the GNU Lesser General Public License as 043 * published by the Free Software Foundation; either version 3 of the 044 * License, or (at your option) any later version. <br> 045 * <br> 046 * This library is distributed in the hope that it will be useful, but 047 * WITHOUT ANY WARRANTY; without even the implied warranty of 048 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 049 * Lesser General Public License for more details. <br> 050 * <br> 051 * You should have received a copy of the GNU Lesser General Public 052 * License along with this library; if not see 053 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 054 */ 055public class StudentSctBBTest extends Model<Request, Enrollment> { 056 private Student iStudent = null; 057 private Solution<Request, Enrollment> iSolution = null; 058 private long iTime; 059 private boolean iTimeoutReached = false; 060 061 /** 062 * Constructor 063 * 064 * @param student 065 * a student to be sectioned 066 */ 067 public StudentSctBBTest(Student student) { 068 iStudent = student; 069 for (Request request : iStudent.getRequests()) 070 addVariable(request); 071 addGlobalConstraint(new SectionLimit(new DataProperties())); 072 addConstraint(new StudentConflict(student)); 073 } 074 075 /** Return the given student 076 * @return selected student 077 **/ 078 public Student getStudent() { 079 return iStudent; 080 } 081 082 /** 083 * Compute and return the sectioning solution. It contains only the given 084 * student with his/her schedule 085 * @return current solution 086 */ 087 public Solution<Request, Enrollment> getSolution() { 088 if (iSolution == null) { 089 iSolution = new Solution<Request, Enrollment>(this, new DefaultSingleAssignment<Request, Enrollment>()); 090 BranchBoundSelection.Selection selection = new BranchBoundSelection(new DataProperties()).getSelection(iSolution.getAssignment(), getStudent()); 091 Neighbour<Request, Enrollment> neighbour = selection.select(); 092 if (neighbour != null) 093 neighbour.assign(iSolution.getAssignment(), 0); 094 iTime = selection.getTime(); 095 iTimeoutReached = selection.isTimeoutReached(); 096 } 097 return iSolution; 098 } 099 100 /** 101 * Return a list of messages ({@link Message} objects) from the sectioning 102 * of the given student 103 * @return enrollment messages 104 */ 105 public List<Message> getMessages() { 106 Assignment<Request, Enrollment> assignment = getSolution().getAssignment(); 107 List<Message> ret = new ArrayList<Message>(); 108 ret.add(new Message(Message.sMsgLevelInfo, null, "<li>Solution found in " + iTime + " ms.")); 109 if (iTimeoutReached) 110 ret.add(new Message(Message.sMsgLevelInfo, null, 111 "<li>Time out reached, solution optimality can not be guaranteed.")); 112 for (Request request : getStudent().getRequests()) { 113 if (!request.isAlternative() && assignment.getValue(request) == null) { 114 ret.add(new Message(Message.sMsgLevelWarn, request, 115 "<li>Unable to enroll to " + request + ", " + (request instanceof CourseRequest ? ((CourseRequest) request).getCourses().size() == 1 ? "course is" : "courses are" : "time is") + " not available.")); 116 Collection<Enrollment> values = (request instanceof CourseRequest ? (Collection<Enrollment>) ((CourseRequest) request).getAvaiableEnrollmentsSkipSameTime(assignment) : request.computeEnrollments(assignment)); 117 for (Iterator<Enrollment> f = values.iterator(); f.hasNext();) { 118 Enrollment enrollment = f.next(); 119 Set<Enrollment> conf = conflictValues(assignment, enrollment); 120 if (conf != null && !conf.isEmpty()) { 121 Enrollment conflict = conf.iterator().next(); 122 if (conflict.equals(enrollment)) 123 ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of " + enrollment.getName().replaceAll("\n", "<br> ") + "<br> is not available.")); 124 else 125 ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of " + enrollment.getName().replaceAll("\n", "<br> ") + 126 "<br> conflicts with " + conflict.getName().replaceAll("\n", "<br> ") + "</ul>")); 127 } 128 } 129 } 130 if (request instanceof CourseRequest && assignment.getValue(request) != null) { 131 CourseRequest courseRequest = (CourseRequest) request; 132 Enrollment enrollment = assignment.getValue(request); 133 List<Enrollment> selectedEnrollments = courseRequest.getSelectedEnrollments(assignment, false); 134 if (selectedEnrollments != null && !selectedEnrollments.isEmpty() 135 && !selectedEnrollments.contains(enrollment)) { 136 Course course = (courseRequest.getSelectedChoices().iterator().next()).getOffering().getCourse( 137 getStudent()); 138 Enrollment selected = selectedEnrollments.get(0); 139 Set<Enrollment> conf = conflictValues(assignment, selected); 140 if (conf != null && !conf.isEmpty()) { 141 ret.add(new Message(Message.sMsgLevelWarn, request, 142 "<li>Unable to enroll selected enrollment for " + course.getName() + ", seleted " + (courseRequest.getSelectedChoices().size() == 1 ? "class is" : "classes are") + 143 " conflicting with other choices.")); 144 Enrollment conflict = conf.iterator().next(); 145 if (conflict.equals(selected)) 146 ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of " + selected.getName().replaceAll("\n", "<br> ") + "<br> is not available.")); 147 else 148 ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of " + selected.getName().replaceAll("\n", "<br> ") + 149 "<br> conflicts with " + conflict.getName().replaceAll("\n", "<br> ") + "</ul>")); 150 } else { 151 ret.add(new Message(Message.sMsgLevelWarn, request, "<li>Unable to enroll selected enrollment for " + course.getName() + ".")); 152 } 153 } 154 } 155 } 156 return ret; 157 } 158 159 /** Sectioning message */ 160 public static class Message { 161 /** Message levels */ 162 public static String[] sMsgLevels = { "INFO", "WARN", "ERROR" }; 163 /** Info message level */ 164 public static int sMsgLevelInfo = 0; 165 /** Warning message level */ 166 public static int sMsgLevelWarn = 1; 167 /** Error message level */ 168 public static int sMsgLevelError = 2; 169 170 private int iLevel; 171 private Request iRequest; 172 private String iMessage; 173 174 /** 175 * Constructor 176 * 177 * @param level 178 * message level (one of 179 * {@link StudentSctBBTest.Message#sMsgLevelInfo}, 180 * {@link StudentSctBBTest.Message#sMsgLevelWarn}, and 181 * {@link StudentSctBBTest.Message#sMsgLevelError}) 182 * @param request 183 * related course / free time request 184 * @param message 185 * a message 186 */ 187 public Message(int level, Request request, String message) { 188 iLevel = level; 189 iRequest = request; 190 iMessage = message; 191 } 192 193 /** 194 * Message level (one of {@link StudentSctBBTest.Message#sMsgLevelInfo}, 195 * {@link StudentSctBBTest.Message#sMsgLevelWarn}, and 196 * {@link StudentSctBBTest.Message#sMsgLevelError}) 197 * @return message level 198 */ 199 public int getLevel() { 200 return iLevel; 201 } 202 203 /** Message level as string 204 * @return message level 205 **/ 206 public String getLevelString() { 207 return sMsgLevels[iLevel]; 208 } 209 210 /** Related course / free time request 211 * @return associated student request 212 **/ 213 public Request getRequest() { 214 return iRequest; 215 } 216 217 /** Message 218 * @return message text 219 **/ 220 public String getMessage() { 221 return iMessage; 222 } 223 224 /** String representation (message level: message) */ 225 @Override 226 public String toString() { 227 return getLevelString() + ":" + getMessage(); 228 } 229 } 230}