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