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