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