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, 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 073 /** 074 * Constructor 075 * @param properties data properties 076 */ 077 public InstructorSchedulingModel(DataProperties properties) { 078 super(); 079 iProperties = properties; 080 addCriterion(new AttributePreferences()); 081 addCriterion(new InstructorPreferences()); 082 addCriterion(new TeachingPreferences()); 083 addCriterion(new TimePreferences()); 084 addCriterion(new CoursePreferences()); 085 addCriterion(new BackToBack()); 086 addCriterion(new SameInstructor()); 087 addCriterion(new TimeOverlaps()); 088 addCriterion(new DifferentLecture()); 089 addCriterion(new SameLink()); 090 addCriterion(new OriginalInstructor()); 091 addGlobalConstraint(new InstructorConstraint()); 092 } 093 094 /** 095 * Return solver configuration 096 * @return data properties given in the constructor 097 */ 098 public DataProperties getProperties() { 099 return iProperties; 100 } 101 102 /** 103 * Add instructor 104 * @param instructor 105 */ 106 public void addInstructor(Instructor instructor) { 107 instructor.setModel(this); 108 iInstructors.add(instructor); 109 for (Attribute attribute: instructor.getAttributes()) 110 addAttributeType(attribute.getType()); 111 } 112 113 /** 114 * All instructors 115 * @return all instructors in the model 116 */ 117 public List<Instructor> getInstructors() { 118 return iInstructors; 119 } 120 121 /** 122 * Return registered attribute types 123 * @return attribute types in the problem 124 */ 125 public Set<Attribute.Type> getAttributeTypes() { return iTypes; } 126 127 /** 128 * Register an attribute type 129 * @param type attribute type 130 */ 131 public void addAttributeType(Attribute.Type type) { iTypes.add(type); } 132 133 @Override 134 public Map<String, String> getInfo(Assignment<TeachingRequest, TeachingAssignment> assignment) { 135 Map<String, String> info = super.getInfo(assignment); 136 137 double totalLoad = 0.0; 138 double assignedLoad = 0.0; 139 for (TeachingRequest clazz : variables()) { 140 totalLoad += clazz.getLoad(); 141 if (assignment.getValue(clazz) != null) 142 assignedLoad += clazz.getLoad(); 143 } 144 info.put("Assigned Load", getPerc(assignedLoad, totalLoad, 0) + "% (" + sDoubleFormat.format(assignedLoad) + " / " + sDoubleFormat.format(totalLoad) + ")"); 145 146 return info; 147 } 148 149 @Override 150 public double getTotalValue(Assignment<TeachingRequest, TeachingAssignment> assignment) { 151 double ret = 0; 152 for (Criterion<TeachingRequest, TeachingAssignment> criterion : getCriteria()) 153 ret += criterion.getWeightedValue(assignment); 154 return ret; 155 } 156 157 @Override 158 public double getTotalValue(Assignment<TeachingRequest, TeachingAssignment> assignment, Collection<TeachingRequest> variables) { 159 double ret = 0; 160 for (Criterion<TeachingRequest, TeachingAssignment> criterion : getCriteria()) 161 ret += criterion.getWeightedValue(assignment, variables); 162 return ret; 163 } 164 165 /** 166 * Store the problem (together with its solution) in an XML format 167 * @param assignment current assignment 168 * @return XML document with the problem 169 */ 170 public Document save(Assignment<TeachingRequest, TeachingAssignment> assignment) { 171 DecimalFormat sDF7 = new DecimalFormat("0000000"); 172 boolean saveInitial = getProperties().getPropertyBoolean("Xml.SaveInitial", false); 173 boolean saveBest = getProperties().getPropertyBoolean("Xml.SaveBest", false); 174 boolean saveSolution = getProperties().getPropertyBoolean("Xml.SaveSolution", true); 175 Document document = DocumentHelper.createDocument(); 176 if (assignment != null && assignment.nrAssignedVariables() > 0) { 177 StringBuffer comments = new StringBuffer("Solution Info:\n"); 178 Map<String, String> solutionInfo = (getProperties().getPropertyBoolean("Xml.ExtendedInfo", true) ? getExtendedInfo(assignment) : getInfo(assignment)); 179 for (String key : new TreeSet<String>(solutionInfo.keySet())) { 180 String value = solutionInfo.get(key); 181 comments.append(" " + key + ": " + value + "\n"); 182 } 183 document.addComment(comments.toString()); 184 } 185 Element root = document.addElement("instructor-schedule"); 186 root.addAttribute("version", "1.0"); 187 root.addAttribute("created", String.valueOf(new Date())); 188 Element typesEl = root.addElement("attributes"); 189 for (Attribute.Type type: getAttributeTypes()) { 190 Element typeEl = typesEl.addElement("type"); 191 if (type.getTypeId() != null) 192 typeEl.addAttribute("id", String.valueOf(type.getTypeId())); 193 typeEl.addAttribute("name", type.getTypeName()); 194 typeEl.addAttribute("conjunctive", type.isConjunctive() ? "true" : "false"); 195 typeEl.addAttribute("required", type.isRequired() ? "true": "false"); 196 Set<Attribute> attributes = new HashSet<Attribute>(); 197 for (TeachingRequest request: variables()) { 198 for (Preference<Attribute> pref: request.getAttributePreferences()) { 199 Attribute attribute = pref.getTarget(); 200 if (type.equals(attribute.getType()) && attributes.add(attribute)) { 201 Element attributeEl = typeEl.addElement("attribute"); 202 if (attribute.getAttributeId() != null) 203 attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId())); 204 attributeEl.addAttribute("name", attribute.getAttributeName()); 205 } 206 } 207 for (Instructor instructor: getInstructors()) { 208 for (Attribute attribute: instructor.getAttributes()) { 209 if (type.equals(attribute.getType()) && attributes.add(attribute)) { 210 Element attributeEl = typeEl.addElement("attribute"); 211 if (attribute.getAttributeId() != null) 212 attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId())); 213 attributeEl.addAttribute("name", attribute.getAttributeName()); 214 } 215 } 216 } 217 } 218 } 219 Set<Course> courses = new HashSet<Course>(); 220 Element coursesEl = root.addElement("courses"); 221 Element requestsEl = root.addElement("teaching-requests"); 222 for (TeachingRequest request: variables()) { 223 Element requestEl = requestsEl.addElement("request"); 224 requestEl.addAttribute("id", String.valueOf(request.getRequestId())); 225 if (request.getInstructorIndex() != 0) 226 requestEl.addAttribute("index", String.valueOf(request.getInstructorIndex())); 227 Course course = request.getCourse(); 228 if (courses.add(course)) { 229 Element courseEl = coursesEl.addElement("course"); 230 if (course.getCourseId() != null) 231 courseEl.addAttribute("id", String.valueOf(course.getCourseId())); 232 if (course.getCourseName() != null) 233 courseEl.addAttribute("name", String.valueOf(course.getCourseName())); 234 if (course.isExclusive()) 235 courseEl.addAttribute("exclusive", "true"); 236 if (course.isSameCommon()) 237 courseEl.addAttribute("common", "true"); 238 } 239 requestEl.addAttribute("course", String.valueOf(request.getCourse().getCourseId())); 240 for (Section section: request.getSections()) { 241 Element sectionEl = requestEl.addElement("section"); 242 sectionEl.addAttribute("id", String.valueOf(section.getSectionId())); 243 if (section.getExternalId() != null && !section.getExternalId().isEmpty()) sectionEl.addAttribute("externalId", section.getExternalId()); 244 if (section.getSectionType() != null && !section.getSectionType().isEmpty()) sectionEl.addAttribute("type", section.getSectionType()); 245 if (section.getSectionName() != null && !section.getSectionName().isEmpty()) sectionEl.addAttribute("name", section.getSectionName()); 246 if (section.hasTime()) { 247 TimeLocation tl = section.getTime(); 248 Element timeEl = sectionEl.addElement("time"); 249 timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode())))); 250 timeEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 251 timeEl.addAttribute("length", String.valueOf(tl.getLength())); 252 if (tl.getBreakTime() != 0) 253 timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 254 if (tl.getTimePatternId() != null) 255 timeEl.addAttribute("pattern", tl.getTimePatternId().toString()); 256 if (tl.getDatePatternId() != null) 257 timeEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 258 if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty()) 259 timeEl.addAttribute("datePatternName", tl.getDatePatternName()); 260 if (tl.getWeekCode() != null) 261 timeEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 262 timeEl.setText(tl.getLongName(false)); 263 } 264 if (section.hasRoom()) sectionEl.addAttribute("room", section.getRoom()); 265 if (section.isAllowOverlap()) sectionEl.addAttribute("canOverlap", "true"); 266 if (section.isCommon()) sectionEl.addAttribute("common", "true"); 267 } 268 requestEl.addAttribute("load", sDoubleFormat.format(request.getLoad())); 269 for (Preference<Attribute> pref: request.getAttributePreferences()) { 270 Element attributeEl = requestEl.addElement("attribute"); 271 if (pref.getTarget().getAttributeId() != null) 272 attributeEl.addAttribute("id", String.valueOf(pref.getTarget().getAttributeId())); 273 attributeEl.addAttribute("name", pref.getTarget().getAttributeName()); 274 attributeEl.addAttribute("type", pref.getTarget().getType().getTypeName()); 275 attributeEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference()))); 276 } 277 for (Preference<Instructor> pref: request.getInstructorPreferences()) { 278 Element instructorEl = requestEl.addElement("instructor"); 279 instructorEl.addAttribute("id", String.valueOf(pref.getTarget().getInstructorId())); 280 if (pref.getTarget().hasExternalId()) 281 instructorEl.addAttribute("externalId", pref.getTarget().getExternalId()); 282 if (pref.getTarget().hasName()) 283 instructorEl.addAttribute("name", pref.getTarget().getName()); 284 instructorEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference()))); 285 } 286 if (saveBest && request.getBestAssignment() != null) { 287 Instructor instructor = request.getBestAssignment().getInstructor(); 288 Element instructorEl = requestEl.addElement("best-instructor"); 289 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 290 if (instructor.hasExternalId()) 291 instructorEl.addAttribute("externalId", instructor.getExternalId()); 292 if (instructor.hasName()) 293 instructorEl.addAttribute("name", instructor.getName()); 294 } 295 if (saveInitial && request.getInitialAssignment() != null) { 296 Instructor instructor = request.getInitialAssignment().getInstructor(); 297 Element instructorEl = requestEl.addElement("initial-instructor"); 298 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 299 if (instructor.hasExternalId()) 300 instructorEl.addAttribute("externalId", instructor.getExternalId()); 301 if (instructor.hasName()) 302 instructorEl.addAttribute("name", instructor.getName()); 303 } 304 if (saveSolution) { 305 TeachingAssignment ta = assignment.getValue(request); 306 if (ta != null) { 307 Instructor instructor = ta.getInstructor(); 308 Element instructorEl = requestEl.addElement("assigned-instructor"); 309 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 310 if (instructor.hasExternalId()) 311 instructorEl.addAttribute("externalId", instructor.getExternalId()); 312 if (instructor.hasName()) 313 instructorEl.addAttribute("name", instructor.getName()); 314 } 315 } 316 } 317 Element instructorsEl = root.addElement("instructors"); 318 for (Instructor instructor: getInstructors()) { 319 Element instructorEl = instructorsEl.addElement("instructor"); 320 instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId())); 321 if (instructor.hasExternalId()) 322 instructorEl.addAttribute("externalId", instructor.getExternalId()); 323 if (instructor.hasName()) 324 instructorEl.addAttribute("name", instructor.getName()); 325 if (instructor.getPreference() != 0) 326 instructorEl.addAttribute("preference", String.valueOf(instructor.getPreference())); 327 if (instructor.getBackToBackPreference() != 0) 328 instructorEl.addAttribute("btb", String.valueOf(instructor.getBackToBackPreference())); 329 for (Attribute attribute: instructor.getAttributes()) { 330 Element attributeEl = instructorEl.addElement("attribute"); 331 if (attribute.getAttributeId() != null) 332 attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId())); 333 attributeEl.addAttribute("name", attribute.getAttributeName()); 334 attributeEl.addAttribute("type", attribute.getType().getTypeName()); 335 } 336 instructorEl.addAttribute("maxLoad", sDoubleFormat.format(instructor.getMaxLoad())); 337 for (Preference<TimeLocation> tp: instructor.getTimePreferences()) { 338 Element timeEl = instructorEl.addElement("time"); 339 TimeLocation tl = tp.getTarget(); 340 timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode())))); 341 timeEl.addAttribute("start", String.valueOf(tl.getStartSlot())); 342 timeEl.addAttribute("length", String.valueOf(tl.getLength())); 343 if (tl.getBreakTime() != 0) 344 timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime())); 345 if (tl.getTimePatternId() != null) 346 timeEl.addAttribute("pattern", tl.getTimePatternId().toString()); 347 if (tl.getDatePatternId() != null) 348 timeEl.addAttribute("datePattern", tl.getDatePatternId().toString()); 349 if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty()) 350 timeEl.addAttribute("datePatternName", tl.getDatePatternName()); 351 if (tl.getWeekCode() != null) 352 timeEl.addAttribute("dates", bitset2string(tl.getWeekCode())); 353 timeEl.addAttribute("preference", tp.isProhibited() ? "P" : tp.isRequired() ? "R" : String.valueOf(tp.getPreference())); 354 timeEl.setText(tl.getLongName(false)); 355 } 356 for (Preference<Course> cp: instructor.getCoursePreferences()) { 357 Element courseEl = instructorEl.addElement("course"); 358 Course course = cp.getTarget(); 359 if (course.getCourseId() != null) 360 courseEl.addAttribute("id", String.valueOf(course.getCourseId())); 361 if (course.getCourseName() != null) 362 courseEl.addAttribute("name", String.valueOf(course.getCourseName())); 363 courseEl.addAttribute("preference", cp.isProhibited() ? "P" : cp.isRequired() ? "R" : String.valueOf(cp.getPreference())); 364 } 365 } 366 Element constraintsEl = root.addElement("constraints"); 367 for (Constraint<TeachingRequest, TeachingAssignment> c: constraints()) { 368 if (c instanceof SameInstructorConstraint) { 369 SameInstructorConstraint si = (SameInstructorConstraint) c; 370 Element sameInstEl = constraintsEl.addElement("same-instructor"); 371 if (si.getConstraintId() != null) 372 sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId())); 373 if (si.getName() != null) 374 sameInstEl.addAttribute("name", si.getName()); 375 sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference())); 376 for (TeachingRequest request: c.variables()) { 377 Element assignmentEl = sameInstEl.addElement("request"); 378 assignmentEl.addAttribute("id", String.valueOf(request.getRequestId())); 379 if (request.getInstructorIndex() != 0) 380 assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex())); 381 } 382 } else if (c instanceof SameLinkConstraint) { 383 SameLinkConstraint si = (SameLinkConstraint) c; 384 Element sameInstEl = constraintsEl.addElement("same-link"); 385 if (si.getConstraintId() != null) 386 sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId())); 387 if (si.getName() != null) 388 sameInstEl.addAttribute("name", si.getName()); 389 sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference())); 390 for (TeachingRequest request: c.variables()) { 391 Element assignmentEl = sameInstEl.addElement("request"); 392 assignmentEl.addAttribute("id", String.valueOf(request.getRequestId())); 393 if (request.getInstructorIndex() != 0) 394 assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex())); 395 } 396 } 397 } 398 return document; 399 } 400 401 /** 402 * Load the problem (and its solution) from an XML format 403 * @param document XML document 404 * @param assignment current assignment 405 * @return true, if the problem was successfully loaded in 406 */ 407 public boolean load(Document document, Assignment<TeachingRequest, TeachingAssignment> assignment) { 408 boolean loadInitial = getProperties().getPropertyBoolean("Xml.LoadInitial", true); 409 boolean loadBest = getProperties().getPropertyBoolean("Xml.LoadBest", true); 410 boolean loadSolution = getProperties().getPropertyBoolean("Xml.LoadSolution", true); 411 String defaultBtb = getProperties().getProperty("Defaults.BackToBack", "0"); 412 String defaultConjunctive = getProperties().getProperty("Defaults.Conjunctive", "false"); 413 String defaultRequired = getProperties().getProperty("Defaults.Required", "false"); 414 String defaultExclusive = getProperties().getProperty("Defaults.Exclusive", "false"); 415 String defaultCommon = getProperties().getProperty("Defaults.SameCommon", "false"); 416 Element root = document.getRootElement(); 417 if (!"instructor-schedule".equals(root.getName())) 418 return false; 419 Map<String, Attribute.Type> types = new HashMap<String, Attribute.Type>(); 420 Map<Long, Attribute> attributes = new HashMap<Long, Attribute>(); 421 if (root.element("attributes") != null) { 422 for (Iterator<?> i = root.element("attributes").elementIterator("type"); i.hasNext();) { 423 Element typeEl = (Element) i.next(); 424 Attribute.Type type = new Attribute.Type( 425 Long.parseLong(typeEl.attributeValue("id")), 426 typeEl.attributeValue("name"), 427 "true".equalsIgnoreCase(typeEl.attributeValue("conjunctive", defaultConjunctive)), 428 "true".equalsIgnoreCase(typeEl.attributeValue("required", defaultRequired))); 429 addAttributeType(type); 430 if (type.getTypeName() != null) 431 types.put(type.getTypeName(), type); 432 for (Iterator<?> j = typeEl.elementIterator("attribute"); j.hasNext();) { 433 Element attributeEl = (Element) j.next(); 434 Attribute attribute = new Attribute( 435 Long.parseLong(attributeEl.attributeValue("id")), 436 attributeEl.attributeValue("name"), 437 type); 438 attributes.put(attribute.getAttributeId(), attribute); 439 } 440 } 441 } 442 Map<Long, Course> courses = new HashMap<Long, Course>(); 443 if (root.element("courses") != null) { 444 for (Iterator<?> i = root.element("courses").elementIterator("course"); i.hasNext();) { 445 Element courseEl = (Element) i.next(); 446 Course course = new Course( 447 Long.parseLong(courseEl.attributeValue("id")), 448 courseEl.attributeValue("name"), 449 "true".equalsIgnoreCase(courseEl.attributeValue("exclusive", defaultExclusive)), 450 "true".equalsIgnoreCase(courseEl.attributeValue("common", defaultCommon))); 451 courses.put(course.getCourseId(), course); 452 } 453 } 454 Map<Long, Instructor> instructors = new HashMap<Long, Instructor>(); 455 for (Iterator<?> i = root.element("instructors").elementIterator("instructor"); i.hasNext();) { 456 Element instructorEl = (Element) i.next(); 457 Instructor instructor = new Instructor( 458 Long.parseLong(instructorEl.attributeValue("id")), 459 instructorEl.attributeValue("externalId"), 460 instructorEl.attributeValue("name"), 461 string2preference(instructorEl.attributeValue("preference")), 462 Float.parseFloat(instructorEl.attributeValue("maxLoad", "0"))); 463 instructor.setBackToBackPreference(Integer.valueOf(instructorEl.attributeValue("btb", defaultBtb))); 464 for (Iterator<?> j = instructorEl.elementIterator("attribute"); j.hasNext();) { 465 Element f = (Element) j.next(); 466 Long attributeId = Long.valueOf(f.attributeValue("id")); 467 Attribute attribute = attributes.get(attributeId); 468 if (attribute == null) { 469 Attribute.Type type = types.get(f.attributeValue("type")); 470 if (type == null) { 471 type = new Attribute.Type(types.size(), f.attributeValue("type"), 472 "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)), 473 "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired))); 474 types.put(type.getTypeName(), type); 475 } 476 attribute = new Attribute(attributeId, f.attributeValue("name"), type); 477 attributes.put(attributeId, attribute); 478 } 479 instructor.addAttribute(attribute); 480 } 481 for (Iterator<?> j = instructorEl.elementIterator("time"); j.hasNext();) { 482 Element f = (Element) j.next(); 483 TimeLocation time = new TimeLocation( 484 Integer.parseInt(f.attributeValue("days"), 2), 485 Integer.parseInt(f.attributeValue("start")), 486 Integer.parseInt(f.attributeValue("length")), 0, 0, 487 f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")), 488 f.attributeValue("datePatternName", ""), 489 createBitSet(f.attributeValue("dates")), 490 Integer.parseInt(f.attributeValue("breakTime", "0"))); 491 if (f.attributeValue("pattern") != null) 492 time.setTimePatternId(Long.valueOf(f.attributeValue("pattern"))); 493 instructor.addTimePreference(new Preference<TimeLocation>(time, string2preference(f.attributeValue("preference")))); 494 } 495 for (Iterator<?> j = instructorEl.elementIterator("course"); j.hasNext();) { 496 Element f = (Element) j.next(); 497 Long courseId = Long.parseLong(f.attributeValue("id")); 498 Course course = courses.get(courseId); 499 if (course == null) { 500 course = new Course(courseId, 501 f.attributeValue("name"), 502 "true".equalsIgnoreCase(f.attributeValue("exclusive", defaultExclusive)), 503 "true".equalsIgnoreCase(f.attributeValue("common", defaultCommon))); 504 courses.put(course.getCourseId(), course); 505 } 506 instructor.addCoursePreference(new Preference<Course>(course, string2preference(f.attributeValue("preference")))); 507 } 508 addInstructor(instructor); 509 instructors.put(instructor.getInstructorId(), instructor); 510 } 511 Map<Long, Map<Integer, TeachingRequest>> requests = new HashMap<Long, Map<Integer, TeachingRequest>>(); 512 Map<TeachingRequest, Instructor> current = new HashMap<TeachingRequest, Instructor>(); 513 Map<TeachingRequest, Instructor> best = new HashMap<TeachingRequest, Instructor>(); 514 Map<TeachingRequest, Instructor> initial = new HashMap<TeachingRequest, Instructor>(); 515 for (Iterator<?> i = root.element("teaching-requests").elementIterator("request"); i.hasNext();) { 516 Element requestEl = (Element) i.next(); 517 Element courseEl = requestEl.element("course"); 518 Course course = null; 519 if (courseEl != null) { 520 Long courseId = Long.valueOf(courseEl.attributeValue("id")); 521 course = courses.get(courseId); 522 if (course == null) { 523 course = new Course(courseId, 524 courseEl.attributeValue("name"), 525 "true".equalsIgnoreCase(courseEl.attributeValue("exclusive", defaultExclusive)), 526 "true".equalsIgnoreCase(courseEl.attributeValue("common", defaultCommon))); 527 } 528 } else { 529 course = courses.get(Long.valueOf(requestEl.attributeValue("course"))); 530 } 531 List<Section> sections = new ArrayList<Section>(); 532 for (Iterator<?> j = requestEl.elementIterator("section"); j.hasNext();) { 533 Element f = (Element) j.next(); 534 TimeLocation time = null; 535 Element timeEl = f.element("time"); 536 if (timeEl != null) { 537 time = new TimeLocation( 538 Integer.parseInt(timeEl.attributeValue("days"), 2), 539 Integer.parseInt(timeEl.attributeValue("start")), 540 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 541 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 542 timeEl.attributeValue("datePatternName", ""), 543 createBitSet(timeEl.attributeValue("dates")), 544 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 545 if (timeEl.attributeValue("pattern") != null) 546 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 547 } 548 Section section = new Section( 549 Long.valueOf(f.attributeValue("id")), 550 f.attributeValue("externalId"), 551 f.attributeValue("type"), 552 f.attributeValue("name"), 553 time, 554 f.attributeValue("room"), 555 "true".equalsIgnoreCase(f.attributeValue("canOverlap", "false")), 556 "true".equalsIgnoreCase(f.attributeValue("common", "false"))); 557 sections.add(section); 558 } 559 TeachingRequest request = new TeachingRequest( 560 Long.parseLong(requestEl.attributeValue("id")), 561 Integer.parseInt(requestEl.attributeValue("index", "0")), 562 course, 563 Float.valueOf(requestEl.attributeValue("load", "0")), 564 sections); 565 Map<Integer, TeachingRequest> requestsSameId = requests.get(request.getRequestId()); 566 if (requestsSameId == null) { 567 requestsSameId = new HashMap<Integer, TeachingRequest>(); 568 requests.put(request.getRequestId(), requestsSameId); 569 } 570 requestsSameId.put(request.getInstructorIndex(), request); 571 for (Iterator<?> j = requestEl.elementIterator("attribute"); j.hasNext();) { 572 Element f = (Element) j.next(); 573 Long attributeId = Long.valueOf(f.attributeValue("id")); 574 Attribute attribute = attributes.get(attributeId); 575 if (attribute == null) { 576 Attribute.Type type = types.get(f.attributeValue("type")); 577 if (type == null) { 578 type = new Attribute.Type(types.size(), f.attributeValue("type"), 579 "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)), 580 "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired))); 581 types.put(type.getTypeName(), type); 582 } 583 attribute = new Attribute(attributeId, f.attributeValue("name"), type); 584 attributes.put(attributeId, attribute); 585 } 586 request.addAttributePreference(new Preference<Attribute>(attribute, string2preference(f.attributeValue("preference")))); 587 } 588 for (Iterator<?> j = requestEl.elementIterator("instructor"); j.hasNext();) { 589 Element f = (Element) j.next(); 590 Long instructorId = Long.valueOf(f.attributeValue("id")); 591 Instructor instructor = instructors.get(instructorId); 592 if (instructor != null) 593 request.addInstructorPreference(new Preference<Instructor>(instructor, string2preference(f.attributeValue("preference")))); 594 } 595 if (loadBest && requestEl.element("best-instructor") != null) 596 best.put(request, instructors.get(Long.valueOf(requestEl.element("best-instructor").attributeValue("id")))); 597 if (loadInitial && requestEl.element("initial-instructor") != null) 598 initial.put(request, instructors.get(Long.valueOf(requestEl.element("initial-instructor").attributeValue("id")))); 599 if (loadSolution && requestEl.element("assigned-instructor") != null) 600 current.put(request, instructors.get(Long.valueOf(requestEl.element("assigned-instructor").attributeValue("id")))); 601 addVariable(request); 602 } 603 if (root.element("constraints") != null) { 604 for (Iterator<?> i = root.element("constraints").elementIterator(); i.hasNext();) { 605 Element constraintEl = (Element) i.next(); 606 Constraint<TeachingRequest, TeachingAssignment> constraint = null; 607 if ("same-link".equals(constraintEl.getName())) { 608 constraint = new SameLinkConstraint( 609 (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))), 610 constraintEl.attributeValue("name"), 611 constraintEl.attributeValue("preference")); 612 } else if ("same-instructor".equals(constraintEl.getName())) { 613 constraint = new SameInstructorConstraint( 614 (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))), 615 constraintEl.attributeValue("name"), 616 constraintEl.attributeValue("preference")); 617 } 618 if (constraint != null) { 619 for (Iterator<?> j = constraintEl.elementIterator("request"); j.hasNext();) { 620 Element f = (Element) j.next(); 621 Map<Integer, TeachingRequest> requestsSameId = requests.get(Long.valueOf(f.attributeValue("id"))); 622 if (requestsSameId != null) { 623 TeachingRequest request = requestsSameId.get(Integer.valueOf(f.attributeValue("index", "0"))); 624 if (request != null) 625 constraint.addVariable(request); 626 } 627 } 628 addConstraint(constraint); 629 } 630 } 631 } 632 for (Map.Entry<TeachingRequest, Instructor> entry: best.entrySet()) 633 entry.getKey().setBestAssignment(new TeachingAssignment(entry.getKey(), entry.getValue()), 0l); 634 635 for (Map.Entry<TeachingRequest, Instructor> entry: initial.entrySet()) 636 entry.getKey().setInitialAssignment(new TeachingAssignment(entry.getKey(), entry.getValue())); 637 638 if (!current.isEmpty()) { 639 for (Map.Entry<TeachingRequest, Instructor> entry: current.entrySet()) { 640 TeachingRequest request = entry.getKey(); 641 TeachingAssignment ta = new TeachingAssignment(request, entry.getValue()); 642 Set<TeachingAssignment> conf = conflictValues(assignment, ta); 643 if (conf.isEmpty()) { 644 assignment.assign(0, ta); 645 } else { 646 sLog.error("Unable to assign " + ta.getName() + " to " + request.getName()); 647 sLog.error("Conflicts:" + ToolBox.dict2string(conflictConstraints(assignment, ta), 2)); 648 } 649 } 650 } 651 652 return true; 653 } 654 655 /** Convert bitset to a bit string */ 656 protected static String bitset2string(BitSet b) { 657 StringBuffer sb = new StringBuffer(); 658 for (int i = 0; i < b.length(); i++) 659 sb.append(b.get(i) ? "1" : "0"); 660 return sb.toString(); 661 } 662 663 /** Create BitSet from a bit string */ 664 protected static BitSet createBitSet(String bitString) { 665 if (bitString == null) return null; 666 BitSet ret = new BitSet(bitString.length()); 667 for (int i = 0; i < bitString.length(); i++) 668 if (bitString.charAt(i) == '1') 669 ret.set(i); 670 return ret; 671 } 672 673 /** Convert preference string to a preference value */ 674 protected static int string2preference(String pref) { 675 if (pref == null || pref.isEmpty()) return 0; 676 if (Constants.sPreferenceRequired.equals(pref)) 677 return Constants.sPreferenceLevelRequired; 678 if (Constants.sPreferenceProhibited.equals(pref)) 679 return Constants.sPreferenceLevelProhibited; 680 return Integer.valueOf(pref); 681 } 682}