001package org.cpsolver.coursett.constraint; 002 003import java.util.Set; 004 005import org.cpsolver.coursett.criteria.additional.StudentOnlineConflict; 006import org.cpsolver.coursett.model.Lecture; 007import org.cpsolver.coursett.model.Placement; 008import org.cpsolver.coursett.model.RoomLocation; 009import org.cpsolver.coursett.model.TimetableModel; 010import org.cpsolver.ifs.assignment.Assignment; 011import org.cpsolver.ifs.model.GlobalConstraint; 012import org.cpsolver.ifs.model.Model; 013import org.cpsolver.ifs.util.DataProperties; 014 015/** 016 * An experimental global constraints that prohibits cases where a student has an online and in-person 017 * class on the same day. Online classes are identified by a regular expression matching the room name 018 * and set in the General.OnlineRoom parameter (defaults to (?i)ONLINE|). Classes without a 019 * room are considered online when the General.OnlineRoom parameter matches a blank string. 020 * If a class has multiple rooms, all rooms must be online for the class to be considered online. 021 * See {@link StudentOnlineConflict} criterion for a soft variant. 022 * <br> 023 * 024 * @author Tomáš Müller 025 * @version CourseTT 1.3 (University Course Timetabling)<br> 026 * Copyright (C) 2013 - 2023 Tomáš Müller<br> 027 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 028 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 029 * <br> 030 * This library is free software; you can redistribute it and/or modify 031 * it under the terms of the GNU Lesser General Public License as 032 * published by the Free Software Foundation; either version 3 of the 033 * License, or (at your option) any later version. <br> 034 * <br> 035 * This library is distributed in the hope that it will be useful, but 036 * WITHOUT ANY WARRANTY; without even the implied warranty of 037 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 038 * Lesser General Public License for more details. <br> 039 * <br> 040 * You should have received a copy of the GNU Lesser General Public 041 * License along with this library; if not see 042 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 043 */ 044public class NoStudentOnlineConflicts extends GlobalConstraint<Lecture, Placement>{ 045 private String iOnlineRoom = null; 046 047 @Override 048 public void setModel(Model<Lecture, Placement> model) { 049 super.setModel(model); 050 if (model != null && model instanceof TimetableModel) { 051 DataProperties config = ((TimetableModel)model).getProperties(); 052 iOnlineRoom = config.getProperty("General.OnlineRoom", "(?i)ONLINE|"); 053 } 054 } 055 056 @Override 057 public void computeConflicts(Assignment<Lecture, Placement> assignment, Placement placement, Set<Placement> conflicts) { 058 Lecture lecture = placement.variable(); 059 for (JenrlConstraint jenrl: lecture.jenrlConstraints()) { 060 if (jenrl.getJenrl() > 0l) { 061 Placement other = assignment.getValue(jenrl.another(lecture)); 062 if (isConsistent(placement, other)) 063 conflicts.add(other); 064 } 065 } 066 } 067 068 @Override 069 public boolean inConflict(Assignment<Lecture, Placement> assignment, Placement placement) { 070 Lecture lecture = placement.variable(); 071 for (JenrlConstraint jenrl: lecture.jenrlConstraints()) { 072 if (jenrl.getJenrl() > 0l && isConsistent(placement, assignment.getValue(jenrl.another(lecture)))) 073 return true; 074 } 075 return false; 076 } 077 078 @Override 079 public boolean isConsistent(Placement p1, Placement p2) { 080 if (p1 == null || p2 == null) { 081 // at least one class is not assigned > not a problem 082 return false; 083 } else if (p1.getTimeLocation().shareDays(p2.getTimeLocation()) && p1.getTimeLocation().shareWeeks(p2.getTimeLocation())) { 084 return isOnline(p1) != isOnline(p2); 085 } else { 086 // different days > not a problem 087 return false; 088 } 089 } 090 091 protected boolean isOnline(Placement p) { 092 if (iOnlineRoom == null) return false; 093 // no room -- General.OnlineRoom must allow for a blank string 094 if (p.getNrRooms() == 0) 095 return "".matches(iOnlineRoom); 096 // one room -- room name must match StudentConflict.OnlineRoom 097 if (p.getNrRooms() == 1) 098 return (p.getRoomLocation().getName() != null && p.getRoomLocation().getName().matches(iOnlineRoom)); 099 // multiple rooms -- all rooms must match StudentConflict.OnlineRoom 100 for (RoomLocation r: p.getRoomLocations()) 101 if (r.getName() == null || !r.getName().matches(iOnlineRoom)) return false; 102 return true; 103 } 104 105 @Override 106 public String getName() { 107 return "No Student Online Conflicts"; 108 } 109 110 @Override 111 public String toString() { 112 return "No Student Online Conflicts"; 113 } 114}