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}