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