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>&nbsp;&nbsp;&nbsp;&nbsp;") + "<br> is not available."));
124                        else
125                            ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of " + enrollment.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;") +
126                                    "<br> conflicts with " + conflict.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;") + "</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>&nbsp;&nbsp;&nbsp;&nbsp;") + "<br> is not available."));
147                        else
148                            ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of " + selected.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;") +
149                                    "<br> conflicts with " + conflict.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;") + "</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}