001package org.cpsolver.instructor.model; 002 003import java.text.DecimalFormat; 004import java.util.ArrayList; 005import java.util.BitSet; 006import java.util.Collection; 007import java.util.Date; 008import java.util.HashMap; 009import java.util.HashSet; 010import java.util.Iterator; 011import java.util.List; 012import java.util.Map; 013import java.util.Set; 014import java.util.TreeSet; 015 016import org.apache.logging.log4j.Logger; 017import org.cpsolver.coursett.Constants; 018import org.cpsolver.coursett.model.TimeLocation; 019import org.cpsolver.ifs.assignment.Assignment; 020import org.cpsolver.ifs.criteria.Criterion; 021import org.cpsolver.ifs.model.Constraint; 022import org.cpsolver.ifs.model.Model; 023import org.cpsolver.ifs.util.DataProperties; 024import org.cpsolver.ifs.util.ToolBox; 025import org.cpsolver.instructor.constraints.InstructorConstraint; 026import org.cpsolver.instructor.constraints.SameInstructorConstraint; 027import org.cpsolver.instructor.constraints.SameLinkConstraint; 028import org.cpsolver.instructor.criteria.UnusedInstructorLoad; 029import org.cpsolver.instructor.criteria.AttributePreferences; 030import org.cpsolver.instructor.criteria.BackToBack; 031import org.cpsolver.instructor.criteria.CoursePreferences; 032import org.cpsolver.instructor.criteria.InstructorPreferences; 033import org.cpsolver.instructor.criteria.SameInstructor; 034import org.cpsolver.instructor.criteria.DifferentLecture; 035import org.cpsolver.instructor.criteria.OriginalInstructor; 036import org.cpsolver.instructor.criteria.SameCommon; 037import org.cpsolver.instructor.criteria.SameCourse; 038import org.cpsolver.instructor.criteria.SameDays; 039import org.cpsolver.instructor.criteria.SameLink; 040import org.cpsolver.instructor.criteria.SameRoom; 041import org.cpsolver.instructor.criteria.TeachingPreferences; 042import org.cpsolver.instructor.criteria.TimeOverlaps; 043import org.cpsolver.instructor.criteria.TimePreferences; 044import org.dom4j.Document; 045import org.dom4j.DocumentHelper; 046import org.dom4j.Element; 047 048/** 049 * Instructor Scheduling Model. Variables are {@link org.cpsolver.instructor.model.TeachingRequest}, values are {@link org.cpsolver.instructor.model.TeachingAssignment}. 050 * Each teaching request has a course (see {@link org.cpsolver.instructor.model.Course}) and one or more sections (see {link {@link org.cpsolver.instructor.model.Section}}). 051 * Each assignment assigns one instructor (see {@link org.cpsolver.instructor.model.Instructor}) to a single teaching request. 052 * 053 * @author Tomáš Müller 054 * @version IFS 1.3 (Instructor Sectioning)<br> 055 * Copyright (C) 2016 Tomáš Müller<br> 056 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 057 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 058 * <br> 059 * This library is free software; you can redistribute it and/or modify 060 * it under the terms of the GNU Lesser General Public License as 061 * published by the Free Software Foundation; either version 3 of the 062 * License, or (at your option) any later version. <br> 063 * <br> 064 * This library is distributed in the hope that it will be useful, but 065 * WITHOUT ANY WARRANTY; without even the implied warranty of 066 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 067 * Lesser General Public License for more details. <br> 068 * <br> 069 * You should have received a copy of the GNU Lesser General Public 070 * License along with this library; if not see 071 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 072 */ 073public class InstructorSchedulingModel extends Model<TeachingRequest.Variable, TeachingAssignment> { 074 private static Logger sLog = org.apache.logging.log4j.LogManager.getLogger(InstructorSchedulingModel.class); 075 private DataProperties iProperties; 076 private Set<Attribute.Type> iTypes = new HashSet<Attribute.Type>(); 077 private List<Instructor> iInstructors = new ArrayList<Instructor>(); 078 private List<TeachingRequest> iRequests = new ArrayList<TeachingRequest>(); 079 080 /** 081 * Constructor 082 * @param properties data properties 083 */ 084 public InstructorSchedulingModel(DataProperties properties) { 085 super(); 086 iProperties = properties; 087 addCriterion(new AttributePreferences()); 088 addCriterion(new InstructorPreferences()); 089 addCriterion(new TeachingPreferences()); 090 addCriterion(new TimePreferences()); 091 addCriterion(new CoursePreferences()); 092 addCriterion(new BackToBack()); 093 addCriterion(new SameInstructor()); 094 addCriterion(new TimeOverlaps()); 095 addCriterion(new DifferentLecture()); 096 addCriterion(new SameLink()); 097 addCriterion(new OriginalInstructor()); 098 addCriterion(new UnusedInstructorLoad()); 099 addCriterion(new SameCourse()); 100 addCriterion(new SameCommon()); 101 addCriterion(new SameDays()); 102 addCriterion(new SameRoom()); 103 addGlobalConstraint(new InstructorConstraint()); 104 } 105 106 /** 107 * Return solver configuration 108 * @return data properties given in the constructor 109 */ 110 public DataProperties getProperties() { 111 return iProperties; 112 } 113 114 /** 115 * Add instructor 116 * @param instructor an instructor 117 */ 118 public void addInstructor(Instructor instructor) { 119 instructor.setModel(this); 120 iInstructors.add(instructor); 121 for (Attribute attribute: instructor.getAttributes()) 122 addAttributeType(attribute.getType()); 123 } 124 125 /** 126 * All instructors 127 * @return all instructors in the model 128 */ 129 public List<Instructor> getInstructors() { 130 return iInstructors; 131 } 132 133 /** 134 * Add teaching request and the related variables 135 * @param request teaching request 136 */ 137 public void addRequest(TeachingRequest request) { 138 iRequests.add(request); 139 for (TeachingRequest.Variable variable: request.getVariables()) 140 addVariable(variable); 141 } 142 143 /** 144 * All teaching requests 145 * @return all teaching requests in the model 146 */ 147 public List<TeachingRequest> getRequests() { 148 return iRequests; 149 } 150 151 152 /** 153 * Return registered attribute types 154 * @return attribute types in the problem 155 */ 156 public Set<Attribute.Type> getAttributeTypes() { return iTypes; } 157 158 /** 159 * Register an attribute type 160 * @param type attribute type 161 */ 162 public void addAttributeType(Attribute.Type type) { iTypes.add(type); } 163 164 @Override 165 public Map<String, String> getInfo(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 166 Map<String, String> info = super.getInfo(assignment); 167 168 double totalLoad = 0.0; 169 double assignedLoad = 0.0; 170 for (TeachingRequest.Variable clazz : variables()) { 171 totalLoad += clazz.getRequest().getLoad(); 172 if (assignment.getValue(clazz) != null) 173 assignedLoad += clazz.getRequest().getLoad(); 174 } 175 info.put("Assigned Load", getPerc(assignedLoad, totalLoad, 0) + "% (" + sDoubleFormat.format(assignedLoad) + " / " + sDoubleFormat.format(totalLoad) + ")"); 176 177 return info; 178 } 179 180 @Override 181 public double getTotalValue(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 182 double ret = 0; 183 for (Criterion<TeachingRequest.Variable, TeachingAssignment> criterion : getCriteria()) 184 ret += criterion.getWeightedValue(assignment); 185 return ret; 186 } 187 188 @Override 189 public double getTotalValue(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, Collection<TeachingRequest.Variable> variables) { 190 double ret = 0; 191 for (Criterion<TeachingRequest.Variable, TeachingAssignment> criterion : getCriteria()) 192 ret += criterion.getWeightedValue(assignment, variables); 193 return ret; 194 } 195 196 /** 197 * Store the problem (together with its solution) in an XML format 198 * @param assignment current assignment 199 * @return XML document with the problem 200 */ 201 public Document save(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 202 DecimalFormat sDF7 = new DecimalFormat("0000000"); 203 boolean saveInitial = getProperties().getPropertyBoolean("Xml.SaveInitial", false); 204 boolean saveBest = getProperties().getPropertyBoolean("Xml.SaveBest", false); 205 boolean saveSolution = getProperties().getPropertyBoolean("Xml.SaveSolution", true); 206 Document document = DocumentHelper.createDocument(); 207 if (assignment != null && assignment.nrAssignedVariables() > 0) { 208 StringBuffer comments = new StringBuffer("Solution Info:\n"); 209 Map<String, String> solutionInfo = (getProperties().getPropertyBoolean("Xml.ExtendedInfo", true) ? getExtendedInfo(assignment) : getInfo(assignment)); 210 for (String key : new TreeSet<String>(solutionInfo.keySet())) { 211 String value = solutionInfo.get(key); 212 comments.append(" " + key + ": " + value + "\n"); 213 } 214 document.addComment(comments.toString()); 215 } 216 Element root = document.addElement("instructor-schedule"); 217 root.addAttribute("version", "1.0"); 218 root.addAttribute("created", String.valueOf(new Date())); 219 Element typesEl = root.addElement("attributes"); 220 for (Attribute.Type type: getAttributeTypes()) { 221 Element typeEl = typesEl.addElement("type"); 222 if (type.getTypeId() != null) 223 typeEl.addAttribute("id", String.valueOf(type.getTypeId())); 224 typeEl.addAttribute("name", type.getTypeName()); 225 typeEl.addAttribute("conjunctive", type.isConjunctive() ? "true" : "false"); 226 typeEl.addAttribute("required", type.isRequired() ? "true": "false"); 227 Set<Attribute> attributes = new HashSet<Attribute>(); 228 for (TeachingRequest request: getRequests()) { 229 for (Preference<Attribute> pref: request.getAttributePreferences()) { 230 Attribute attribute = pref.getTarget(); 231 if (type.equals(attribute.getType()) && attributes.add(attribute)) { 232 Element attributeEl = typeEl.addElement("attribute"); 233 if (attribute.getAttributeId() != null) 234 attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId())); 235 attributeEl.addAttribute("name", attribute.getAttributeName()); 236 if (attribute.getParentAttribute() != null && attribute.getParentAttribute().getAttributeId() != null) 237 attributeEl.addAttribute("parent", String.valueOf(attribute.getParentAttribute().getAttributeId())); 238 } 239 } 240 for (Instructor instructor: getInstructors()) { 241 for (Attribute attribute: instructor.getAttributes()) { 242 if (type.equals(attribute.getType()) && attributes.add(attribute)) { 243 Element attributeEl = typeEl.addElement("attribute"); 244 if (attribute.getAttributeId() != null) 245 attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId())); 246 attributeEl.addAttribute("name", attribute.getAttributeName()); 247 if (attribute.getParentAttribute() != null && attribute.getParentAttribute().getAttributeId() != null) 248 attributeEl.addAttribute("parent", String.valueOf(attribute.getParentAttribute().getAttributeId())); 249 } 250 } 251 } 252 } 253 } 254 Element requestsEl = root.addElement("teaching-requests"); 255 for (TeachingRequest request: getRequests()) { 256 Element requestEl = requestsEl.addElement("request"); 257 requestEl.addAttribute("id", String.valueOf(request.getRequestId())); 258 if (request.getNrInstructors() != 1) 259 requestEl.addAttribute("nrInstructors", String.valueOf(request.getNrInstructors())); 260 Course course = request.getCourse(); 261 Element courseEl = requestEl.addElement("course"); 262 if (course.getCourseId() != null) 263 courseEl.addAttribute("id", String.valueOf(course.getCourseId())); 264 if (course.getCourseName() != null) 265 courseEl.addAttribute("name", String.valueOf(course.getCourseName())); 266 for (Section section: request.getSections()) { 267 Element sectionEl = requestEl.addElement("section"); 268 sectionEl.addAttribute("id", String.valueOf(section.getSectionId())); 269 if (section.getExternalId() != null && !section.getExternalId().isEmpty()) sectionEl.addAttribute("externalId", section.getExternalId()); 270 if (section.getSectionType() != null && !section.getSectionType().isEmpty()) sectionEl.addAttribute("type", section.getSectionType()); 271 if (section.getSectionName() != null && !section.getSectionName().isEmpty()) sectionEl.addAttribute("name", section.getSectionName()); 272 if (section.hasTime()) { 273 TimeLocation tl = section.getTime(); 274 Element timeEl = sectionEl.addElement("time"); 275 timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode())))); 276 timeEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 277 timeEl.addAttribute("length", String.valueOf(tl.getLength())); 278 if (tl.getBreakTime() != 0) 279 timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 280 if (tl.getTimePatternId() != null) 281 timeEl.addAttribute("pattern", tl.getTimePatternId().toString()); 282 if (tl.getDatePatternId() != null) 283 timeEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 284 if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty()) 285 timeEl.addAttribute("datePatternName", tl.getDatePatternName()); 286 if (tl.getWeekCode() != null) 287 timeEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 288 timeEl.setText(tl.getLongName(false)); 289 } 290 if (section.hasRoom()) sectionEl.addAttribute("room", section.getRoom()); 291 if (section.isAllowOverlap()) sectionEl.addAttribute("canOverlap", "true"); 292 if (section.isCommon()) sectionEl.addAttribute("common", "true"); 293 } 294 requestEl.addAttribute("load", sDoubleFormat.format(request.getLoad())); 295 requestEl.addAttribute("sameCourse", Constants.preferenceLevel2preference(request.getSameCoursePreference())); 296 requestEl.addAttribute("sameCommon", Constants.preferenceLevel2preference(request.getSameCommonPreference())); 297 for (Preference<Attribute> pref: request.getAttributePreferences()) { 298 Element attributeEl = requestEl.addElement("attribute"); 299 if (pref.getTarget().getAttributeId() != null) 300 attributeEl.addAttribute("id", String.valueOf(pref.getTarget().getAttributeId())); 301 attributeEl.addAttribute("name", pref.getTarget().getAttributeName()); 302 attributeEl.addAttribute("type", pref.getTarget().getType().getTypeName()); 303 attributeEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference()))); 304 if (pref.getTarget().getParentAttribute() != null && pref.getTarget().getParentAttribute().getAttributeId() != null) 305 attributeEl.addAttribute("parent", String.valueOf(pref.getTarget().getParentAttribute().getAttributeId())); 306 } 307 for (Preference<Instructor> pref: request.getInstructorPreferences()) { 308 Element instructorEl = requestEl.addElement("instructor"); 309 instructorEl.addAttribute("id", String.valueOf(pref.getTarget().getInstructorId())); 310 if (pref.getTarget().hasExternalId()) 311 instructorEl.addAttribute("externalId", pref.getTarget().getExternalId()); 312 if (pref.getTarget().hasName()) 313 instructorEl.addAttribute("name", pref.getTarget().getName()); 314 instructorEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference()))); 315 } 316 if (saveBest) 317 for (TeachingRequest.Variable variable: request.getVariables()) { 318 if (variable.getBestAssignment() != null) { 319 Instructor instructor = variable.getBestAssignment().getInstructor(); 320 Element instructorEl = requestEl.addElement("best-instructor"); 321 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 322 if (request.getNrInstructors() != 1) 323 instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex())); 324 if (instructor.hasExternalId()) 325 instructorEl.addAttribute("externalId", instructor.getExternalId()); 326 if (instructor.hasName()) 327 instructorEl.addAttribute("name", instructor.getName()); 328 } 329 } 330 if (saveInitial) 331 for (TeachingRequest.Variable variable: request.getVariables()) { 332 if (variable.getInitialAssignment() != null) { 333 Instructor instructor = variable.getInitialAssignment().getInstructor(); 334 Element instructorEl = requestEl.addElement("initial-instructor"); 335 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 336 if (request.getNrInstructors() != 1) 337 instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex())); 338 if (instructor.hasExternalId()) 339 instructorEl.addAttribute("externalId", instructor.getExternalId()); 340 if (instructor.hasName()) 341 instructorEl.addAttribute("name", instructor.getName()); 342 } 343 } 344 if (saveSolution) 345 for (TeachingRequest.Variable variable: request.getVariables()) { 346 TeachingAssignment ta = assignment.getValue(variable); 347 if (ta != null) { 348 Instructor instructor = ta.getInstructor(); 349 Element instructorEl = requestEl.addElement("assigned-instructor"); 350 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 351 if (request.getNrInstructors() != 1) 352 instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex())); 353 if (instructor.hasExternalId()) 354 instructorEl.addAttribute("externalId", instructor.getExternalId()); 355 if (instructor.hasName()) 356 instructorEl.addAttribute("name", instructor.getName()); 357 } 358 } 359 } 360 Element instructorsEl = root.addElement("instructors"); 361 for (Instructor instructor: getInstructors()) { 362 Element instructorEl = instructorsEl.addElement("instructor"); 363 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 364 if (instructor.hasExternalId()) 365 instructorEl.addAttribute("externalId", instructor.getExternalId()); 366 if (instructor.hasName()) 367 instructorEl.addAttribute("name", instructor.getName()); 368 if (instructor.getPreference() != 0) 369 instructorEl.addAttribute("preference", String.valueOf(instructor.getPreference())); 370 if (instructor.getBackToBackPreference() != 0) 371 instructorEl.addAttribute("btb", String.valueOf(instructor.getBackToBackPreference())); 372 if (instructor.getSameDaysPreference() != 0) 373 instructorEl.addAttribute("same-days", String.valueOf(instructor.getSameDaysPreference())); 374 if (instructor.getSameRoomPreference() != 0) 375 instructorEl.addAttribute("same-room", String.valueOf(instructor.getSameRoomPreference())); 376 for (Attribute attribute: instructor.getAttributes()) { 377 Element attributeEl = instructorEl.addElement("attribute"); 378 if (attribute.getAttributeId() != null) 379 attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId())); 380 attributeEl.addAttribute("name", attribute.getAttributeName()); 381 attributeEl.addAttribute("type", attribute.getType().getTypeName()); 382 if (attribute.getParentAttribute() != null && attribute.getParentAttribute().getAttributeId() != null) 383 attributeEl.addAttribute("parent", String.valueOf(attribute.getParentAttribute().getAttributeId())); 384 } 385 instructorEl.addAttribute("maxLoad", sDoubleFormat.format(instructor.getMaxLoad())); 386 for (Preference<TimeLocation> tp: instructor.getTimePreferences()) { 387 388 Element timeEl = instructorEl.addElement("time"); 389 TimeLocation tl = tp.getTarget(); 390 timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode())))); 391 timeEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 392 timeEl.addAttribute("length", String.valueOf(tl.getLength())); 393 if (tl.getBreakTime() != 0) 394 timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 395 if (tl.getTimePatternId() != null) 396 timeEl.addAttribute("pattern", tl.getTimePatternId().toString()); 397 if (tl.getDatePatternId() != null) 398 timeEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 399 if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty()) 400 timeEl.addAttribute("datePatternName", tl.getDatePatternName()); 401 if (tl.getWeekCode() != null) 402 timeEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 403 timeEl.addAttribute("preference", tp.isProhibited() ? "P" : tp.isRequired() ? "R" : String.valueOf(tp.getPreference())); 404 if (tp.getTarget() instanceof EnrolledClass) { 405 Element classEl = timeEl.addElement("section"); 406 Element courseEl = null; 407 EnrolledClass ec = (EnrolledClass)tp.getTarget(); 408 if (ec.getCourseId() != null || ec.getCourse() != null) { 409 courseEl = timeEl.addElement("course"); 410 if (ec.getCourseId() != null) courseEl.addAttribute("id", String.valueOf(ec.getCourseId())); 411 if (ec.getCourse() != null) courseEl.addAttribute("name", ec.getCourse()); 412 } 413 if (ec.getClassId() != null) classEl.addAttribute("id", String.valueOf(ec.getClassId())); 414 if (ec.getType() != null) classEl.addAttribute("type", ec.getType()); 415 if (ec.getSection() != null) classEl.addAttribute("name", ec.getSection()); 416 if (ec.getExternalId() != null) classEl.addAttribute("externalId", ec.getExternalId()); 417 if (ec.getRoom() != null) classEl.addAttribute("room", ec.getRoom()); 418 classEl.addAttribute("role", ec.isInstructor() ? "instructor" : "student"); 419 } else { 420 timeEl.setText(tl.getLongName(false)); 421 } 422 } 423 for (Preference<Course> cp: instructor.getCoursePreferences()) { 424 Element courseEl = instructorEl.addElement("course"); 425 Course course = cp.getTarget(); 426 if (course.getCourseId() != null) 427 courseEl.addAttribute("id", String.valueOf(course.getCourseId())); 428 if (course.getCourseName() != null) 429 courseEl.addAttribute("name", String.valueOf(course.getCourseName())); 430 courseEl.addAttribute("preference", cp.isProhibited() ? "P" : cp.isRequired() ? "R" : String.valueOf(cp.getPreference())); 431 } 432 } 433 Element constraintsEl = root.addElement("constraints"); 434 for (Constraint<TeachingRequest.Variable, TeachingAssignment> c: constraints()) { 435 if (c instanceof SameInstructorConstraint) { 436 SameInstructorConstraint si = (SameInstructorConstraint) c; 437 Element sameInstEl = constraintsEl.addElement("same-instructor"); 438 if (si.getConstraintId() != null) 439 sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId())); 440 if (si.getName() != null) 441 sameInstEl.addAttribute("name", si.getName()); 442 sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference())); 443 for (TeachingRequest.Variable request: c.variables()) { 444 Element assignmentEl = sameInstEl.addElement("request"); 445 assignmentEl.addAttribute("id", String.valueOf(request.getRequest().getRequestId())); 446 if (request.getRequest().getNrInstructors() != 1) 447 assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex())); 448 } 449 } else if (c instanceof SameLinkConstraint) { 450 SameLinkConstraint si = (SameLinkConstraint) c; 451 Element sameInstEl = constraintsEl.addElement("same-link"); 452 if (si.getConstraintId() != null) 453 sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId())); 454 if (si.getName() != null) 455 sameInstEl.addAttribute("name", si.getName()); 456 sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference())); 457 for (TeachingRequest.Variable request: c.variables()) { 458 Element assignmentEl = sameInstEl.addElement("request"); 459 assignmentEl.addAttribute("id", String.valueOf(request.getRequest().getRequestId())); 460 if (request.getRequest().getNrInstructors() != 1) 461 assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex())); 462 } 463 } 464 } 465 return document; 466 } 467 468 /** 469 * Load the problem (and its solution) from an XML format 470 * @param document XML document 471 * @param assignment current assignment 472 * @return true, if the problem was successfully loaded in 473 */ 474 public boolean load(Document document, Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 475 boolean loadInitial = getProperties().getPropertyBoolean("Xml.LoadInitial", true); 476 boolean loadBest = getProperties().getPropertyBoolean("Xml.LoadBest", true); 477 boolean loadSolution = getProperties().getPropertyBoolean("Xml.LoadSolution", true); 478 String defaultBtb = getProperties().getProperty("Defaults.BackToBack", "0"); 479 String defaultSameDays = getProperties().getProperty("Defaults.SameDays", "0"); 480 String defaultSameRoom = getProperties().getProperty("Defaults.SameRoom", "0"); 481 String defaultConjunctive = getProperties().getProperty("Defaults.Conjunctive", "false"); 482 String defaultRequired = getProperties().getProperty("Defaults.Required", "false"); 483 String defaultSameCourse = getProperties().getProperty("Defaults.SameCourse", "R"); 484 String defaultSameCommon = getProperties().getProperty("Defaults.SameCommon", "R"); 485 Element root = document.getRootElement(); 486 if (!"instructor-schedule".equals(root.getName())) 487 return false; 488 Map<String, Attribute.Type> types = new HashMap<String, Attribute.Type>(); 489 Map<Long, Attribute> attributes = new HashMap<Long, Attribute>(); 490 Map<Long, Long> parents = new HashMap<Long, Long>(); 491 if (root.element("attributes") != null) { 492 for (Iterator<?> i = root.element("attributes").elementIterator("type"); i.hasNext();) { 493 Element typeEl = (Element) i.next(); 494 Attribute.Type type = new Attribute.Type( 495 Long.parseLong(typeEl.attributeValue("id")), 496 typeEl.attributeValue("name"), 497 "true".equalsIgnoreCase(typeEl.attributeValue("conjunctive", defaultConjunctive)), 498 "true".equalsIgnoreCase(typeEl.attributeValue("required", defaultRequired))); 499 addAttributeType(type); 500 if (type.getTypeName() != null) 501 types.put(type.getTypeName(), type); 502 for (Iterator<?> j = typeEl.elementIterator("attribute"); j.hasNext();) { 503 Element attributeEl = (Element) j.next(); 504 Attribute attribute = new Attribute( 505 Long.parseLong(attributeEl.attributeValue("id")), 506 attributeEl.attributeValue("name"), 507 type); 508 attributes.put(attribute.getAttributeId(), attribute); 509 if (attributeEl.attributeValue("parent") != null) 510 parents.put(attribute.getAttributeId(), Long.parseLong(attributeEl.attributeValue("parent"))); 511 } 512 } 513 } 514 Map<Long, Course> courses = new HashMap<Long, Course>(); 515 if (root.element("courses") != null) { 516 for (Iterator<?> i = root.element("courses").elementIterator("course"); i.hasNext();) { 517 Element courseEl = (Element) i.next(); 518 Course course = new Course( 519 Long.parseLong(courseEl.attributeValue("id")), 520 courseEl.attributeValue("name")); 521 courses.put(course.getCourseId(), course); 522 } 523 } 524 Map<Long, Instructor> instructors = new HashMap<Long, Instructor>(); 525 for (Iterator<?> i = root.element("instructors").elementIterator("instructor"); i.hasNext();) { 526 Element instructorEl = (Element) i.next(); 527 Instructor instructor = new Instructor( 528 Long.parseLong(instructorEl.attributeValue("id")), 529 instructorEl.attributeValue("externalId"), 530 instructorEl.attributeValue("name"), 531 string2preference(instructorEl.attributeValue("preference")), 532 Float.parseFloat(instructorEl.attributeValue("maxLoad", "0"))); 533 instructor.setBackToBackPreference(Integer.valueOf(instructorEl.attributeValue("btb", defaultBtb))); 534 instructor.setSameDaysPreference(Integer.valueOf(instructorEl.attributeValue("same-days", defaultSameDays))); 535 instructor.setSameRoomPreference(Integer.valueOf(instructorEl.attributeValue("same-room", defaultSameRoom))); 536 for (Iterator<?> j = instructorEl.elementIterator("attribute"); j.hasNext();) { 537 Element f = (Element) j.next(); 538 Long attributeId = Long.valueOf(f.attributeValue("id")); 539 Attribute attribute = attributes.get(attributeId); 540 if (attribute == null) { 541 Attribute.Type type = types.get(f.attributeValue("type")); 542 if (type == null) { 543 type = new Attribute.Type(types.size(), f.attributeValue("type"), 544 "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)), 545 "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired))); 546 types.put(type.getTypeName(), type); 547 } 548 attribute = new Attribute(attributeId, f.attributeValue("name"), type); 549 attributes.put(attributeId, attribute); 550 if (f.attributeValue("parent") != null) 551 parents.put(attribute.getAttributeId(), Long.parseLong(f.attributeValue("parent"))); 552 } 553 instructor.addAttribute(attribute); 554 } 555 for (Iterator<?> j = instructorEl.elementIterator("time"); j.hasNext();) { 556 Element f = (Element) j.next(); 557 Element classEl = f.element("section"); 558 Element courseEl = f.element("course"); 559 TimeLocation time = null; 560 if (classEl != null) { 561 time = new EnrolledClass( 562 courseEl == null || courseEl.attributeValue("id") == null ? null : Long.valueOf(courseEl.attributeValue("id")), 563 classEl.attributeValue("id") == null ? null : Long.valueOf(classEl.attributeValue("id")), 564 courseEl == null ? null : courseEl.attributeValue("name"), 565 classEl.attributeValue("type"), 566 classEl.attributeValue("name"), 567 classEl.attributeValue("externalId"), 568 Integer.parseInt(f.attributeValue("days"), 2), 569 Integer.parseInt(f.attributeValue("start")), 570 Integer.parseInt(f.attributeValue("length")), 571 f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")), 572 f.attributeValue("datePatternName", ""), 573 createBitSet(f.attributeValue("dates")), 574 Integer.parseInt(f.attributeValue("breakTime", "0")), 575 classEl.attributeValue("room"), 576 "instructor".equalsIgnoreCase(classEl.attributeValue("role", "instructor"))); 577 } else { 578 time = new TimeLocation( 579 Integer.parseInt(f.attributeValue("days"), 2), 580 Integer.parseInt(f.attributeValue("start")), 581 Integer.parseInt(f.attributeValue("length")), 0, 0, 582 f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")), 583 f.attributeValue("datePatternName", ""), 584 createBitSet(f.attributeValue("dates")), 585 Integer.parseInt(f.attributeValue("breakTime", "0"))); 586 } 587 if (f.attributeValue("pattern") != null) 588 time.setTimePatternId(Long.valueOf(f.attributeValue("pattern"))); 589 instructor.addTimePreference(new Preference<TimeLocation>(time, string2preference(f.attributeValue("preference")))); 590 } 591 for (Iterator<?> j = instructorEl.elementIterator("course"); j.hasNext();) { 592 Element f = (Element) j.next(); 593 instructor.addCoursePreference(new Preference<Course>(new Course(Long.parseLong(f.attributeValue("id")), f.attributeValue("name")), string2preference(f.attributeValue("preference")))); 594 } 595 addInstructor(instructor); 596 instructors.put(instructor.getInstructorId(), instructor); 597 } 598 Map<Long, TeachingRequest> requests = new HashMap<Long, TeachingRequest>(); 599 Map<TeachingRequest, Map<Integer, Instructor>> current = new HashMap<TeachingRequest, Map<Integer, Instructor>>(); 600 Map<TeachingRequest, Map<Integer, Instructor>> best = new HashMap<TeachingRequest, Map<Integer, Instructor>>(); 601 Map<TeachingRequest, Map<Integer, Instructor>> initial = new HashMap<TeachingRequest, Map<Integer, Instructor>>(); 602 for (Iterator<?> i = root.element("teaching-requests").elementIterator("request"); i.hasNext();) { 603 Element requestEl = (Element) i.next(); 604 Element courseEl = requestEl.element("course"); 605 Course course = null; 606 if (courseEl != null) { 607 Long courseId = Long.valueOf(courseEl.attributeValue("id")); 608 course = courses.get(courseId); 609 if (course == null) { 610 course = new Course(courseId, courseEl.attributeValue("name")); 611 } 612 } else { 613 course = courses.get(Long.valueOf(requestEl.attributeValue("course"))); 614 } 615 List<Section> sections = new ArrayList<Section>(); 616 for (Iterator<?> j = requestEl.elementIterator("section"); j.hasNext();) { 617 Element f = (Element) j.next(); 618 TimeLocation time = null; 619 Element timeEl = f.element("time"); 620 if (timeEl != null) { 621 time = new TimeLocation( 622 Integer.parseInt(timeEl.attributeValue("days"), 2), 623 Integer.parseInt(timeEl.attributeValue("start")), 624 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 625 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 626 timeEl.attributeValue("datePatternName", ""), 627 createBitSet(timeEl.attributeValue("dates")), 628 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 629 if (timeEl.attributeValue("pattern") != null) 630 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 631 } 632 Section section = new Section( 633 Long.valueOf(f.attributeValue("id")), 634 f.attributeValue("externalId"), 635 f.attributeValue("type"), 636 f.attributeValue("name"), 637 time, 638 f.attributeValue("room"), 639 "true".equalsIgnoreCase(f.attributeValue("canOverlap", "false")), 640 "true".equalsIgnoreCase(f.attributeValue("common", "false"))); 641 sections.add(section); 642 } 643 TeachingRequest request = new TeachingRequest( 644 Long.parseLong(requestEl.attributeValue("id")), 645 Integer.parseInt(requestEl.attributeValue("nrInstructors", "1")), 646 course, 647 Float.valueOf(requestEl.attributeValue("load", "0")), 648 sections, 649 Constants.preference2preferenceLevel(requestEl.attributeValue("sameCourse", defaultSameCourse)), 650 Constants.preference2preferenceLevel(requestEl.attributeValue("sameCommon", defaultSameCommon))); 651 requests.put(request.getRequestId(), request); 652 for (Iterator<?> j = requestEl.elementIterator("attribute"); j.hasNext();) { 653 Element f = (Element) j.next(); 654 Long attributeId = Long.valueOf(f.attributeValue("id")); 655 Attribute attribute = attributes.get(attributeId); 656 if (attribute == null) { 657 Attribute.Type type = types.get(f.attributeValue("type")); 658 if (type == null) { 659 type = new Attribute.Type(types.size(), f.attributeValue("type"), 660 "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)), 661 "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired))); 662 types.put(type.getTypeName(), type); 663 } 664 attribute = new Attribute(attributeId, f.attributeValue("name"), type); 665 attributes.put(attributeId, attribute); 666 if (f.attributeValue("parent") != null) 667 parents.put(attribute.getAttributeId(), Long.parseLong(f.attributeValue("parent"))); 668 } 669 request.addAttributePreference(new Preference<Attribute>(attribute, string2preference(f.attributeValue("preference")))); 670 } 671 for (Iterator<?> j = requestEl.elementIterator("instructor"); j.hasNext();) { 672 Element f = (Element) j.next(); 673 Long instructorId = Long.valueOf(f.attributeValue("id")); 674 Instructor instructor = instructors.get(instructorId); 675 if (instructor != null) 676 request.addInstructorPreference(new Preference<Instructor>(instructor, string2preference(f.attributeValue("preference")))); 677 } 678 if (loadBest) { 679 for (Iterator<?> j = requestEl.elementIterator("best-instructor"); j.hasNext();) { 680 Element f = (Element) j.next(); 681 Map<Integer, Instructor> idx2inst = best.get(request); 682 if (idx2inst == null) { 683 idx2inst = new HashMap<Integer, Instructor>(); 684 best.put(request, idx2inst); 685 } 686 int index = 1 + Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size()))); 687 Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id"))); 688 if (instructor != null) 689 idx2inst.put(index, instructor); 690 } 691 } 692 if (loadInitial) { 693 for (Iterator<?> j = requestEl.elementIterator("initial-instructor"); j.hasNext();) { 694 Element f = (Element) j.next(); 695 Map<Integer, Instructor> idx2inst = initial.get(request); 696 if (idx2inst == null) { 697 idx2inst = new HashMap<Integer, Instructor>(); 698 initial.put(request, idx2inst); 699 } 700 int index = 1 + Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size()))); 701 Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id"))); 702 if (instructor != null) 703 idx2inst.put(index, instructor); 704 } 705 } 706 if (loadSolution) { 707 for (Iterator<?> j = requestEl.elementIterator("assigned-instructor"); j.hasNext();) { 708 Element f = (Element) j.next(); 709 Map<Integer, Instructor> idx2inst = current.get(request); 710 if (idx2inst == null) { 711 idx2inst = new HashMap<Integer, Instructor>(); 712 current.put(request, idx2inst); 713 } 714 int index = Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size()))); 715 Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id"))); 716 if (instructor != null) 717 idx2inst.put(index, instructor); 718 } 719 } 720 addRequest(request); 721 } 722 if (root.element("constraints") != null) { 723 for (Iterator<?> i = root.element("constraints").elementIterator(); i.hasNext();) { 724 Element constraintEl = (Element) i.next(); 725 Constraint<TeachingRequest.Variable, TeachingAssignment> constraint = null; 726 if ("same-link".equals(constraintEl.getName())) { 727 constraint = new SameLinkConstraint( 728 (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))), 729 constraintEl.attributeValue("name"), 730 constraintEl.attributeValue("preference")); 731 } else if ("same-instructor".equals(constraintEl.getName())) { 732 constraint = new SameInstructorConstraint( 733 (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))), 734 constraintEl.attributeValue("name"), 735 constraintEl.attributeValue("preference")); 736 } 737 if (constraint != null) { 738 for (Iterator<?> j = constraintEl.elementIterator("request"); j.hasNext();) { 739 Element f = (Element) j.next(); 740 TeachingRequest request = requests.get(Long.valueOf(f.attributeValue("id"))); 741 if (request != null) { 742 int index = Integer.valueOf(f.attributeValue("index", "0")); 743 if (index >= 0 && index < request.getNrInstructors()) 744 constraint.addVariable(request.getVariables()[index]); 745 } 746 } 747 addConstraint(constraint); 748 } 749 } 750 } 751 for (Map.Entry<Long, Long> e: parents.entrySet()) 752 attributes.get(e.getKey()).setParentAttribute(attributes.get(e.getValue())); 753 for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: best.entrySet()) 754 for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet()) 755 if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) { 756 TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()]; 757 variable.setBestAssignment(new TeachingAssignment(variable, e2.getValue()), 0l); 758 } 759 760 for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: initial.entrySet()) 761 for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet()) 762 if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) { 763 TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()]; 764 variable.setInitialAssignment(new TeachingAssignment(variable, e2.getValue())); 765 } 766 767 if (!current.isEmpty()) { 768 for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: current.entrySet()) 769 for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet()) 770 if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) { 771 TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()]; 772 TeachingAssignment ta = new TeachingAssignment(variable, e2.getValue()); 773 Set<TeachingAssignment> conf = conflictValues(assignment, ta); 774 if (conf.isEmpty()) { 775 assignment.assign(0, ta); 776 } else { 777 sLog.error("Unable to assign " + ta.getName() + " to " + variable.getName()); 778 sLog.error("Conflicts:" + ToolBox.dict2string(conflictConstraints(assignment, ta), 2)); 779 } 780 } 781 } 782 783 return true; 784 } 785 786 /** Convert bitset to a bit string */ 787 protected static String bitset2string(BitSet b) { 788 StringBuffer sb = new StringBuffer(); 789 for (int i = 0; i < b.length(); i++) 790 sb.append(b.get(i) ? "1" : "0"); 791 return sb.toString(); 792 } 793 794 /** Create BitSet from a bit string */ 795 protected static BitSet createBitSet(String bitString) { 796 if (bitString == null) return null; 797 BitSet ret = new BitSet(bitString.length()); 798 for (int i = 0; i < bitString.length(); i++) 799 if (bitString.charAt(i) == '1') 800 ret.set(i); 801 return ret; 802 } 803 804 /** Convert preference string to a preference value */ 805 protected static int string2preference(String pref) { 806 if (pref == null || pref.isEmpty()) return 0; 807 if (Constants.sPreferenceRequired.equals(pref)) 808 return Constants.sPreferenceLevelRequired; 809 if (Constants.sPreferenceProhibited.equals(pref)) 810 return Constants.sPreferenceLevelProhibited; 811 return Integer.valueOf(pref); 812 } 813}