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