    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}