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