001package org.cpsolver.instructor.model;
002
003import java.util.Collection;
004
005import org.cpsolver.coursett.model.TimeLocation;
006import org.cpsolver.instructor.criteria.DifferentLecture;
007
008/**
009 * Section. A section (part of a teaching request that needs an instructor) has an id, a name, a time, a room.
010 * A section may be allowed to overlap in time (in which case the overlapping time is to be minimized) and/or
011 * marked as common. This is, for instance, to be able to ensure that all assignments of a course that are
012 * given to a single instructor share the same lecture.  
013 * 
014 * @version IFS 1.3 (Instructor Sectioning)<br>
015 *          Copyright (C) 2016 Tomas Muller<br>
016 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
017 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
018 * <br>
019 *          This library is free software; you can redistribute it and/or modify
020 *          it under the terms of the GNU Lesser General Public License as
021 *          published by the Free Software Foundation; either version 3 of the
022 *          License, or (at your option) any later version. <br>
023 * <br>
024 *          This library is distributed in the hope that it will be useful, but
025 *          WITHOUT ANY WARRANTY; without even the implied warranty of
026 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
027 *          Lesser General Public License for more details. <br>
028 * <br>
029 *          You should have received a copy of the GNU Lesser General Public
030 *          License along with this library; if not see
031 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
032 */
033public class Section {
034    private Long iId;
035    private String iExternalId;
036    private String iType;
037    private String iName;
038    private TimeLocation iTime;
039    private String iRoom;
040    private boolean iAllowOverlap;
041    private boolean iCommon;
042    
043    /**
044     * Constructor
045     * @param id section unique id
046     * @param externalId section external id
047     * @param type instructional type
048     * @param name section name
049     * @param time section time assignment
050     * @param room section room assignment
051     * @param allowOverlap can this section overlap with some other section
052     * @param common is this a common part of the course
053     */
054    public Section(long id, String externalId, String type, String name, TimeLocation time, String room, boolean allowOverlap, boolean common) {
055        iId = id;
056        iExternalId = externalId;
057        iType = type;
058        iName = name;
059        iTime = time;
060        iRoom = room;
061        iAllowOverlap = allowOverlap;
062        iCommon = common;
063    }
064    
065    /**
066     * Section unique id that was provided in the constructor
067     * @return section unique id
068     */
069    public Long getSectionId() { return iId; }
070    
071    /**
072     * Section external unique id that was provided in the constructor
073     * @return section external id
074     */
075    public String getExternalId() { return iExternalId; }
076    
077    /**
078     * Section instructional type (e.g., Lecture) that was provided in the constructor
079     * @return section instructional type
080     */
081    public String getSectionType() { return iType; }
082    
083    /**
084     * Has section type filled in?
085     * @return true, if there is a section instructional type filled in
086     */
087    public boolean hasSectionType() { return iType != null && !iType.isEmpty(); }
088    
089    /**
090     * Section name that was provided in the constructor
091     * @return section name
092     */
093    public String getSectionName() { return iName; }
094    
095    /**
096     * Section time that was provided in the constructor
097     * @return section time
098     */
099    public TimeLocation getTime() { return iTime; }
100    
101    /**
102     * Has section time filled in?
103     * @return true if section is assigned in time
104     */
105    public boolean hasTime() { return getTime() != null && getTime().getDayCode() != 0; }
106    
107    /**
108     * Section room (or rooms)
109     * @return section room
110     */
111    public String getRoom() { return iRoom; }
112    
113    /**
114     * Has section room filled in?
115     * @return true if section is assigned in space
116     */
117    public boolean hasRoom() { return iRoom != null && !iRoom.isEmpty(); }
118    
119    /**
120     * Are time overlaps with other sections and with prohibited time preferences allowed? If true, the number of overlapping time slots should be minimized instead.
121     * @return true if other sections can overlap with this section or if the student can teach this section even when he/she is unavailable
122     */
123    public boolean isAllowOverlap() { return iAllowOverlap; }
124    
125    /**
126     * Is common part of the course (e.g., a lecture)? It is possible to either require all assignments of a course that are given to the same instructor to have
127     * to share the common part (when {@link Course#isSameCommon()} is true) or to minimize the different sections that are not shared among all the assignments
128     * (using {@link DifferentLecture} criterion and {@link TeachingRequest#nrSameLectures(TeachingRequest)}). 
129     * @return true if this section forms a common part of the course
130     */
131    public boolean isCommon() { return iCommon; }
132    
133    /**
134     * Check if this section overlaps in time with some other section
135     * @param section the other section
136     * @return true, if neither of the two sections allow for overlap and they are assigned in overlapping times (see {@link TimeLocation#hasIntersection(TimeLocation)})
137     */
138    public boolean isOverlapping(Section section) {
139        if (isAllowOverlap() || section.isAllowOverlap()) return false;
140        if (getTime() == null || section.getTime() == null) return false;
141        return getTime().hasIntersection(section.getTime());
142    }
143    
144    /**
145     * Check if this section overlaps in time with at least one of the given sections
146     * @param sections the other sections
147     * @return true, if there is a section among the sections that overlaps in time with this section (see {@link Section#isOverlapping(Section)})
148     */
149    public boolean isOverlapping(Collection<Section> sections) {
150        if (isAllowOverlap()) return false;
151        if (getTime() == null) return false;
152        if (sections.contains(this)) return false;
153        for (Section section : sections) {
154            if (section.isAllowOverlap()) continue;
155            if (section.getTime() == null) continue;
156            if (getTime().hasIntersection(section.getTime())) return true;
157        }
158        return false;
159    }
160    
161    /**
162     * Check if this section is back to back with some other section
163     * @param section the other section
164     * @return true, if this section is back-to-back with the other section
165     */
166    public boolean isBackToBack(Section section) {
167        if (getTime() == null || section.getTime() == null) return false;
168        return getTime().shareWeeks(section.getTime()) && getTime().shareDays(section.getTime()) && 
169                (getTime().getStartSlot() + getTime().getLength() == section.getTime().getStartSlot() || section.getTime().getStartSlot() + section.getTime().getLength() == getTime().getStartSlot());
170    }
171    
172    /**
173     * Check if this section is placed in the same room as the other section
174     * @param section the other section
175     * @return true, if both sections have no room or if they have the same room
176     */
177    public boolean isSameRoom(Section section) {
178        return hasRoom() == section.hasRoom() && (!hasRoom() || getRoom().equals(section.getRoom()));
179    }
180
181    /**
182     * Check if this section has the same instructional type as the other section
183     * @param section the other section
184     * @return true, if both sections have no instructional type or if they have the same instructional type
185     */
186    public boolean isSameSectionType(Section section) {
187        return hasSectionType() == section.hasSectionType() && (!hasSectionType() || getSectionType().equals(section.getSectionType()));
188    }
189    
190    /**
191     * Check if this section is back-to-back with some other section in the list
192     * @param sections the other sections
193     * @param diffRoomWeight different room penalty (should be between 1 and 0)
194     * @param diffTypeWeight different instructional type penalty (should be between 1 and 0)
195     * @return 1.0 if there is a section in the list that is back-to-back and in the same room and with the same type, 0.0 if there is no back-to-back section in the list, etc.
196     * If there are multiple back-to-back sections, the best back-to-back value is returned.
197     */
198    public double countBackToBacks(Collection<Section> sections, double diffRoomWeight, double diffTypeWeight) {
199        if (sections.contains(this)) return 0.0;
200        double btb = 0;
201        for (Section section : sections)
202            if (isBackToBack(section)) {
203                double w = 1.0;
204                if (!isSameRoom(section)) w *= diffRoomWeight;
205                if (!isSameSectionType(section)) w *= diffTypeWeight;
206                if (w > btb) btb = w;
207            }
208        return btb;
209    }
210    
211    /**
212     * If this section can overlap in time with the other section, compute the number of overlapping time slots
213     * @param section the other section
214     * @return number of shared days times number of shared slots (a day)
215     */
216    public int share(Section section) {
217        if (getTime() != null && section.getTime() != null && (isAllowOverlap() || section.isAllowOverlap()) && getTime().hasIntersection(section.getTime()))
218            return getTime().nrSharedDays(section.getTime()) * getTime().nrSharedHours(section.getTime());
219        else
220            return 0;
221    }
222    
223    /**
224     * Compute the number of overlapping time slots between this section and the given time
225     * @param time the given time
226     * @return number of shared days times number of shared slots (a day)
227     */
228    public int share(TimeLocation time) {
229        if (getTime() != null && time != null && getTime().hasIntersection(time))
230            return getTime().nrSharedDays(time) * getTime().nrSharedHours(time);
231        else
232            return 0;
233    }
234    
235    /**
236     * If this section can overlap in time with any of the given section, compute the number of overlapping time slots
237     * @param sections the other sections
238     * @return number of shared days times number of shared slots (a day)
239     */
240    public int share(Collection<Section> sections) {
241        if (sections.contains(this)) return 0;
242        int ret = 0;
243        for (Section section : sections)
244            ret += share(section);
245        return ret;
246    }
247    
248    /**
249     * Format the time statement
250     * @param useAmPm use 12-hours or 24-hours format
251     * @return <Days of week> <Start time> - <End time>
252     */
253    public String getTimeName(boolean useAmPm) {
254        if (getTime() == null || getTime().getDayCode() == 0) return "-";
255        return getTime().getDayHeader() + " " + getTime().getStartTimeHeader(useAmPm) + " - " + getTime().getEndTimeHeader(useAmPm);
256    }
257    
258    @Override
259    public String toString() {
260        return (getExternalId() != null ? (getSectionType() == null ? "" : getSectionType() + " ") + getExternalId() :
261            getSectionName() != null ? getSectionName() : getTime() != null ? getTime().getName(true) : "S" + getSectionId());
262    }
263    
264    @Override
265    public int hashCode() {
266        return getSectionId().hashCode();
267    }
268    
269    @Override
270    public boolean equals(Object o) {
271        if (o == null || !(o instanceof Section)) return false;
272        Section s = (Section)o;
273        return getSectionId().equals(s.getSectionId());
274    }
275}