001package net.sf.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 net.sf.cpsolver.ifs.model.Model;
010import net.sf.cpsolver.ifs.model.Neighbour;
011import net.sf.cpsolver.ifs.solution.Solution;
012import net.sf.cpsolver.ifs.util.DataProperties;
013import net.sf.cpsolver.studentsct.constraint.SectionLimit;
014import net.sf.cpsolver.studentsct.constraint.StudentConflict;
015import net.sf.cpsolver.studentsct.heuristics.selection.BranchBoundSelection;
016import net.sf.cpsolver.studentsct.model.Course;
017import net.sf.cpsolver.studentsct.model.CourseRequest;
018import net.sf.cpsolver.studentsct.model.Enrollment;
019import net.sf.cpsolver.studentsct.model.Request;
020import 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 Tomáš Müller<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 */
054public 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>&nbsp;&nbsp;&nbsp;&nbsp;")
128                                    + "<br> is not available."));
129                        else
130                            ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of "
131                                    + enrollment.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;")
132                                    + "<br> conflicts with "
133                                    + conflict.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;") + "</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>&nbsp;&nbsp;&nbsp;&nbsp;")
156                                    + "<br> is not available."));
157                        else
158                            ret.add(new Message(Message.sMsgLevelInfo, request, "<ul>Assignment of "
159                                    + selected.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;")
160                                    + "<br> conflicts with "
161                                    + conflict.getName().replaceAll("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;") + "</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}