001package org.cpsolver.studentsct.online.selection;
002
003import java.util.List;
004
005import org.cpsolver.coursett.model.RoomLocation;
006import org.cpsolver.coursett.model.TimeLocation;
007import org.cpsolver.ifs.assignment.Assignment;
008import org.cpsolver.ifs.util.DataProperties;
009import org.cpsolver.ifs.util.ToolBox;
010import org.cpsolver.studentsct.model.Choice;
011import org.cpsolver.studentsct.model.Course;
012import org.cpsolver.studentsct.model.CourseRequest;
013import org.cpsolver.studentsct.model.Enrollment;
014import org.cpsolver.studentsct.model.Request;
015import org.cpsolver.studentsct.model.Section;
016import org.cpsolver.studentsct.weights.StudentWeights;
017
018/**
019 * Re-scheduling variant of {@link StudentWeights} model. It is based on the
020 * {@link StudentSchedulingAssistantWeights}. This model is used when a single
021 * course of a student is rescheduled. Following criteria are included:
022 * <ul>
023 * <li>StudentWeights.SameChoiceFactor .. penalization of selecting a different
024 * choice (see {@link Choice})
025 * <li>StudentWeights.SameRoomsFactor .. penalization of selecting different
026 * room
027 * <li>StudentWeights.SameTimeFactor .. penalization of selecting different time
028 * <li>StudentWeights.SameNameFactor .. penalization of selecting different
029 * section (section name / external id does not match)
030 * </ul>
031 * 
032 * @version StudentSct 1.3 (Student Sectioning)<br>
033 *          Copyright (C) 2014 Tomas Muller<br>
034 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
035 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
036 * <br>
037 *          This library is free software; you can redistribute it and/or modify
038 *          it under the terms of the GNU Lesser General Public License as
039 *          published by the Free Software Foundation; either version 3 of the
040 *          License, or (at your option) any later version. <br>
041 * <br>
042 *          This library is distributed in the hope that it will be useful, but
043 *          WITHOUT ANY WARRANTY; without even the implied warranty of
044 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
045 *          Lesser General Public License for more details. <br>
046 * <br>
047 *          You should have received a copy of the GNU Lesser General Public
048 *          License along with this library; if not see <a
049 *          href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>.
050 * 
051 */
052public class ResectioningWeights extends StudentSchedulingAssistantWeights {
053    private double iSameChoiceFactor = 0.125;
054    private double iSameRoomsFactor = 0.007;
055    private double iSameTimeFactor = 0.070;
056    private double iSameNameFactor = 0.014;
057    private LastSectionProvider iLastSectionProvider = null;
058
059    public ResectioningWeights(DataProperties properties) {
060        super(properties);
061        iSameChoiceFactor = properties.getPropertyDouble("StudentWeights.SameChoiceFactor", iSameChoiceFactor);
062        iSameRoomsFactor = properties.getPropertyDouble("StudentWeights.SameRoomsFactor", iSameRoomsFactor);
063        iSameTimeFactor = properties.getPropertyDouble("StudentWeights.SameTimeFactor", iSameTimeFactor);
064        iSameNameFactor = properties.getPropertyDouble("StudentWeights.SameNameFactor", iSameNameFactor);
065    }
066
067    public void setLastSectionProvider(LastSectionProvider lastSectionProvider) {
068        iLastSectionProvider = lastSectionProvider;
069    }
070
071    @Override
072    public double getWeight(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
073        double weight = super.getWeight(assignment, enrollment);
074
075        if (enrollment.isCourseRequest() && enrollment.getAssignments() != null && iLastSectionProvider != null) {
076            int sameChoice = 0;
077            int sameTime = 0;
078            int sameRooms = 0;
079            int sameName = 0;
080            for (Section section : enrollment.getSections()) {
081                if (iLastSectionProvider.sameLastChoice(section))
082                    sameChoice++;
083                if (iLastSectionProvider.sameLastTime(section))
084                    sameTime++;
085                if (iLastSectionProvider.sameLastRoom(section))
086                    sameRooms++;
087                if (iLastSectionProvider.sameLastName(section, enrollment.getCourse()))
088                    sameName++;
089            }
090            CourseRequest cr = (CourseRequest) enrollment.getRequest();
091            if (sameChoice == 0 && !cr.getSelectedChoices().isEmpty()) {
092                for (Section section : enrollment.getSections()) {
093                    if (cr.getSelectedChoices().contains(section.getChoice())) {
094                        sameChoice++;
095                        continue;
096                    }
097                }
098            }
099            double size = enrollment.getAssignments().size();
100            double sameChoiceFraction = (size - sameChoice) / size;
101            double sameTimeFraction = (size - sameTime) / size;
102            double sameRoomsFraction = (size - sameRooms) / size;
103            double sameNameFraction = (size - sameName) / size;
104            double base = getBaseWeight(assignment, enrollment);
105            weight -= sameChoiceFraction * base * iSameChoiceFactor;
106            weight -= sameTimeFraction * base * iSameTimeFactor;
107            weight -= sameRoomsFraction * base * iSameRoomsFactor;
108            weight -= sameNameFraction * base * iSameNameFactor;
109        }
110
111        return weight;
112    }
113
114    public static boolean sameChoice(Section s, Choice ch) {
115        return sameChoice(s, ch == null ? null : ch.getId());
116    }
117
118    public static boolean sameChoice(Section s, String ch) {
119        if (s.getChoice() == null && ch == null)
120            return true;
121        if (s.getChoice() == null || ch == null)
122            return false;
123        return s.getChoice().getId().equals(ch);
124    }
125
126    public static boolean isSame(Enrollment e1, Enrollment e2) {
127        if (e1.getSections().size() != e2.getSections().size())
128            return false;
129        s1: for (Section s1 : e1.getSections()) {
130            for (Section s2 : e2.getSections())
131                if (sameChoice(s1, s2.getChoice()))
132                    continue s1;
133            return false;
134        }
135        return true;
136    }
137
138    public static boolean sameRooms(Section s, List<RoomLocation> rooms) {
139        if (s.getRooms() == null && rooms == null)
140            return true;
141        if (s.getRooms() == null || rooms == null)
142            return false;
143        return s.getRooms().size() == rooms.size() && s.getRooms().containsAll(rooms);
144    }
145
146    public static boolean sameTime(Section s, TimeLocation t) {
147        if (s.getTime() == null && t == null)
148            return true;
149        if (s.getTime() == null || t == null)
150            return false;
151        return s.getTime().getStartSlot() == t.getStartSlot() && s.getTime().getLength() == t.getLength()
152                && s.getTime().getDayCode() == t.getDayCode()
153                && ToolBox.equals(s.getTime().getDatePatternName(), t.getDatePatternName());
154    }
155
156    public static boolean sameName(Long courseId, Section s1, Section s2) {
157        return s1.getName(courseId).equals(s2.getName(courseId));
158    }
159
160    /**
161     * Compare the old assignment with the current one
162     */
163    public static interface LastSectionProvider {
164        /**
165         * Check the choice (see {@link Choice})
166         * 
167         * @param current
168         *            current section
169         * @return true if the choice matches
170         */
171        public boolean sameLastChoice(Section current);
172
173        /**
174         * Check the time
175         * 
176         * @param current
177         *            current section
178         * @return true if the time matches
179         */
180        public boolean sameLastTime(Section current);
181
182        /**
183         * Check the room
184         * 
185         * @param current
186         *            current section
187         * @return true if the room matches
188         */
189        public boolean sameLastRoom(Section current);
190
191        /**
192         * Check section name (external id)
193         * 
194         * @param current
195         *            current section
196         * @param course
197         *            current course
198         * @return true if the section name matches
199         */
200        public boolean sameLastName(Section current, Course course);
201    }
202}