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 Tomáš Müller<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.isSelected(section)) {
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 isSame(Enrollment e1, Enrollment e2) {
115        if (e1.getSections().size() != e2.getSections().size())
116            return false;
117        s1: for (Section s1 : e1.getSections()) {
118            for (Section s2 : e2.getSections())
119                if (s1.sameChoice(s2))
120                    continue s1;
121            return false;
122        }
123        return true;
124    }
125
126    public static boolean sameRooms(Section s, List<RoomLocation> rooms) {
127        if (s.getRooms() == null && rooms == null)
128            return true;
129        if (s.getRooms() == null || rooms == null)
130            return false;
131        return s.getRooms().size() == rooms.size() && s.getRooms().containsAll(rooms);
132    }
133
134    public static boolean sameTime(Section s, TimeLocation t) {
135        if (s.getTime() == null && t == null)
136            return true;
137        if (s.getTime() == null || t == null)
138            return false;
139        return s.getTime().getStartSlot() == t.getStartSlot() && s.getTime().getLength() == t.getLength()
140                && s.getTime().getDayCode() == t.getDayCode()
141                && ToolBox.equals(s.getTime().getDatePatternName(), t.getDatePatternName());
142    }
143
144    public static boolean sameName(Long courseId, Section s1, Section s2) {
145        return s1.getName(courseId).equals(s2.getName(courseId));
146    }
147    
148    public static boolean sameChoice(Section section, Choice choice) {
149        return choice.sameChoice(section);
150    }
151
152    /**
153     * Compare the old assignment with the current one
154     */
155    public static interface LastSectionProvider {
156        /**
157         * Check the choice (see {@link Choice})
158         * 
159         * @param current
160         *            current section
161         * @return true if the choice matches
162         */
163        public boolean sameLastChoice(Section current);
164
165        /**
166         * Check the time
167         * 
168         * @param current
169         *            current section
170         * @return true if the time matches
171         */
172        public boolean sameLastTime(Section current);
173
174        /**
175         * Check the room
176         * 
177         * @param current
178         *            current section
179         * @return true if the room matches
180         */
181        public boolean sameLastRoom(Section current);
182
183        /**
184         * Check section name (external id)
185         * 
186         * @param current
187         *            current section
188         * @param course
189         *            current course
190         * @return true if the section name matches
191         */
192        public boolean sameLastName(Section current, Course course);
193    }
194}