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