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>&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    }