001package org.cpsolver.instructor.test; 002 003import java.io.BufferedReader; 004import java.io.File; 005import java.io.FileReader; 006import java.io.IOException; 007import java.io.PrintWriter; 008import java.text.DecimalFormat; 009import java.util.ArrayList; 010import java.util.Collections; 011import java.util.Comparator; 012import java.util.HashMap; 013import java.util.HashSet; 014import java.util.List; 015import java.util.Map; 016import java.util.Set; 017import java.util.TreeSet; 018 019import org.apache.log4j.Logger; 020import org.cpsolver.coursett.Constants; 021import org.cpsolver.coursett.model.TimeLocation; 022import org.cpsolver.ifs.assignment.Assignment; 023import org.cpsolver.ifs.model.Constraint; 024import org.cpsolver.ifs.util.DataProperties; 025import org.cpsolver.ifs.util.ToolBox; 026import org.cpsolver.instructor.Test; 027import org.cpsolver.instructor.constraints.SameInstructorConstraint; 028import org.cpsolver.instructor.constraints.SameLinkConstraint; 029import org.cpsolver.instructor.criteria.DifferentLecture; 030import org.cpsolver.instructor.model.Course; 031import org.cpsolver.instructor.model.Instructor; 032import org.cpsolver.instructor.model.Attribute; 033import org.cpsolver.instructor.model.Preference; 034import org.cpsolver.instructor.model.Section; 035import org.cpsolver.instructor.model.TeachingAssignment; 036import org.cpsolver.instructor.model.TeachingRequest; 037 038/** 039 * Math teaching assistant assignment problem. Different file format for the input data. 040 * 041 * @version IFS 1.3 (Instructor Sectioning)<br> 042 * Copyright (C) 2016 Tomas Muller<br> 043 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 044 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 045 * <br> 046 * This library is free software; you can redistribute it and/or modify 047 * it under the terms of the GNU Lesser General Public License as 048 * published by the Free Software Foundation; either version 3 of the 049 * License, or (at your option) any later version. <br> 050 * <br> 051 * This library is distributed in the hope that it will be useful, but 052 * WITHOUT ANY WARRANTY; without even the implied warranty of 053 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 054 * Lesser General Public License for more details. <br> 055 * <br> 056 * You should have received a copy of the GNU Lesser General Public 057 * License along with this library; if not see 058 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 059 */ 060public class MathTest extends Test { 061 private static Logger sLog = Logger.getLogger(MathTest.class); 062 063 public MathTest(DataProperties properties) { 064 super(properties); 065 removeCriterion(DifferentLecture.class); 066 } 067 068 public String getLevel(Instructor instructor) { 069 for (Attribute attribute: instructor.getAttributes()) 070 if (attribute.getType().getTypeName().equals("Level")) return attribute.getAttributeName(); 071 return null; 072 } 073 074 public String toString(Instructor instructor) { 075 StringBuffer sb = new StringBuffer(); 076 sb.append(instructor.getExternalId()); 077 sb.append(",\"" + instructor.getAvailable() + "\""); 078 Collections.sort(instructor.getCoursePreferences(), new Comparator<Preference<Course>>() { 079 @Override 080 public int compare(Preference<Course> p1, Preference<Course> p2) { 081 if (p1.getPreference() == p2.getPreference()) 082 return p1.getTarget().getCourseName().compareTo(p2.getTarget().getCourseName()); 083 return p1.getPreference() < p2.getPreference() ? -1 : 1; 084 } 085 }); 086 for (int i = 0; i < 3; i++) { 087 Preference<Course> p = (i < instructor.getCoursePreferences().size() ? instructor.getCoursePreferences().get(i) : null); 088 sb.append("," + (p == null ? "" : p.getTarget().getCourseName())); 089 } 090 sb.append("," + (instructor.getPreference() == 0 ? "Yes" : "No")); 091 sb.append("," + (instructor.isBackToBackPreferred() ? "1" : instructor.isBackToBackDiscouraged() ? "-1" : "0")); 092 sb.append("," + new DecimalFormat("0.0").format(instructor.getMaxLoad())); 093 String level = getLevel(instructor); 094 sb.append("," + (level == null ? "" : level)); 095 return sb.toString(); 096 } 097 098 public String getLink(TeachingRequest request) { 099 for (Constraint<TeachingRequest, TeachingAssignment> c: request.constraints()) { 100 if (c instanceof SameLinkConstraint) 101 return c.getName().substring(c.getName().indexOf('-') + 1); 102 } 103 return null; 104 } 105 106 public Long getAssignmentId(TeachingRequest request) { 107 for (Constraint<TeachingRequest, TeachingAssignment> c: request.constraints()) { 108 if (c instanceof SameInstructorConstraint && ((SameInstructorConstraint) c).getConstraintId() != null) 109 return ((SameInstructorConstraint) c).getConstraintId(); 110 } 111 return null; 112 } 113 114 public int countDiffLinks(Set<TeachingAssignment> assignments) { 115 Set<String> links = new HashSet<String>(); 116 for (TeachingAssignment assignment : assignments) { 117 String link = getLink(assignment.variable()); 118 if (link != null) 119 links.add(link); 120 } 121 return Math.max(0, links.size() - 1); 122 } 123 124 public String toString(TeachingRequest request) { 125 StringBuffer sb = new StringBuffer(); 126 Long assId = getAssignmentId(request); 127 sb.append(assId == null ? "" : assId); 128 sb.append("," + request.getCourse().getCourseName()); 129 Section section = request.getSections().get(0); 130 sb.append("," + section.getSectionName()); 131 sb.append("," + section.getTimeName(true)); 132 sb.append(",\"" + (section.hasRoom() ? section.getRoom() : "") + "\""); 133 String link = getLink(request); 134 sb.append("," + (link == null ? "" : link)); 135 Map<String, Integer> levels = new HashMap<String, Integer>(); 136 for (Preference<Attribute> p: request.getAttributePreferences()) 137 levels.put(p.getTarget().getAttributeName(), - p.getPreference()); 138 sb.append(",\"" + levels + "\""); 139 sb.append("," + new DecimalFormat("0.0").format(request.getLoad())); 140 return sb.toString(); 141 } 142 143 @Override 144 protected boolean load(File dir, Assignment<TeachingRequest, TeachingAssignment> assignment) { 145 if (!dir.isDirectory()) 146 return super.load(dir, assignment); 147 try { 148 String line = null; 149 BufferedReader r = new BufferedReader(new FileReader(new File(dir, "courses.csv"))); 150 Map<String, Course> courses = new HashMap<String, Course>(); 151 Map<Long, List<TeachingRequest>> id2classes = new HashMap<Long, List<TeachingRequest>>(); 152 Map<String, List<TeachingRequest>> link2classes = new HashMap<String, List<TeachingRequest>>(); 153 long assId = 0, reqId = 0; 154 while ((line = r.readLine()) != null) { 155 if (line.trim().isEmpty()) 156 continue; 157 String[] fields = line.split(","); 158 Long id = Long.valueOf(fields[0]); 159 String course = fields[1]; 160 String section = fields[2]; 161 int idx = 3; 162 int dayCode = 0; 163 idx: while (idx < fields.length && (idx == 3 || fields[idx].length() == 1)) { 164 for (int i = 0; i < fields[idx].length(); i++) { 165 switch (fields[idx].charAt(i)) { 166 case 'M': 167 dayCode += Constants.DAY_CODES[0]; 168 break; 169 case 'T': 170 dayCode += Constants.DAY_CODES[1]; 171 break; 172 case 'W': 173 dayCode += Constants.DAY_CODES[2]; 174 break; 175 case 'R': 176 dayCode += Constants.DAY_CODES[3]; 177 break; 178 case 'F': 179 dayCode += Constants.DAY_CODES[4]; 180 break; 181 default: 182 break idx; 183 } 184 } 185 idx++; 186 } 187 int startSlot = 0; 188 if (dayCode > 0) { 189 int time = Integer.parseInt(fields[idx++]); 190 startSlot = 12 * (time / 100) + (time % 100) / 5; 191 } 192 String room = null; 193 if (idx < fields.length) 194 room = fields[idx++]; 195 String link = null; 196 if (idx < fields.length) 197 link = fields[idx++]; 198 int length = 12; 199 if (idx < fields.length) { 200 int time = Integer.parseInt(fields[idx++]); 201 int endSlot = 12 * (time / 100) + (time % 100) / 5; 202 length = endSlot - startSlot; 203 if (length == 10) 204 length = 12; 205 else if (length == 15) 206 length = 18; 207 } 208 List<Section> sections = new ArrayList<Section>(); 209 TimeLocation time = new TimeLocation(dayCode, startSlot, length, 0, 0.0, 0, null, "", null, (length == 18 ? 15 : 10)); 210 sections.add(new Section(assId++, id.toString(), section, course + " " + section + " " + time.getName(true) + (room == null ? "" : " " + room), time, room, false, false)); 211 Course c = courses.get(course); 212 if (c == null) { 213 c = new Course(courses.size(), course, true, false); 214 courses.put(course, c); 215 } 216 TeachingRequest clazz = new TeachingRequest(reqId++, 0, c, 0f, sections); 217 addVariable(clazz); 218 List<TeachingRequest> classes = id2classes.get(id); 219 if (classes == null) { 220 classes = new ArrayList<TeachingRequest>(); 221 id2classes.put(id, classes); 222 } 223 classes.add(clazz); 224 if (link != null && !link.isEmpty()) { 225 List<TeachingRequest> linked = link2classes.get(course + "-" + link); 226 if (linked == null) { 227 linked = new ArrayList<TeachingRequest>(); 228 link2classes.put(course + "-" + link, linked); 229 } 230 linked.add(clazz); 231 } 232 } 233 234 for (Map.Entry<Long, List<TeachingRequest>> e : id2classes.entrySet()) { 235 Long id = e.getKey(); 236 List<TeachingRequest> classes = e.getValue(); 237 if (classes.size() > 1) { 238 SameInstructorConstraint sa = new SameInstructorConstraint(id, "A" + id.toString(), Constants.sPreferenceRequired); 239 for (TeachingRequest c : classes) 240 sa.addVariable(c); 241 addConstraint(sa); 242 } 243 } 244 for (Map.Entry<String, List<TeachingRequest>> e : link2classes.entrySet()) { 245 String link = e.getKey(); 246 List<TeachingRequest> classes = e.getValue(); 247 if (classes.size() > 1) { 248 SameLinkConstraint sa = new SameLinkConstraint(null, link, Constants.sPreferencePreferred); 249 for (TeachingRequest c : classes) 250 sa.addVariable(c); 251 addConstraint(sa); 252 } 253 } 254 255 Attribute.Type level = new Attribute.Type(0l, "Level", false, true); 256 addAttributeType(level); 257 Map<String, Attribute> code2attribute = new HashMap<String, Attribute>(); 258 259 r = new BufferedReader(new FileReader(new File(dir, "level_codes.csv"))); 260 String[] codes = r.readLine().split(","); 261 while ((line = r.readLine()) != null) { 262 if (line.trim().isEmpty()) 263 continue; 264 String[] fields = line.split(","); 265 String code = fields[0]; 266 if (code.startsWith("\"") && code.endsWith("\"")) 267 code = code.substring(1, code.length() - 1); 268 Attribute attribute = code2attribute.get(code); 269 if (attribute == null) { 270 attribute = new Attribute(code2attribute.size(), code, level); 271 code2attribute.put(code, attribute); 272 } 273 for (int i = 1; i < codes.length; i++) { 274 int pref = Integer.parseInt(fields[i]); 275 if (pref > 0) 276 for (TeachingRequest clazz : variables()) { 277 if (clazz.getName().contains(codes[i])) 278 clazz.addAttributePreference(new Preference<Attribute>(attribute, -pref)); 279 } 280 } 281 } 282 r = new BufferedReader(new FileReader(new File(dir, "hours_per_course.csv"))); 283 while ((line = r.readLine()) != null) { 284 if (line.trim().isEmpty()) 285 continue; 286 String[] fields = line.split(","); 287 for (TeachingRequest clazz : variables()) { 288 if (clazz.getName().contains(fields[0])) 289 clazz.setLoad(Float.parseFloat(fields[1])); 290 } 291 } 292 293 String defaultCode = getProperties().getProperty("TA.DefaultLevelCode", "XXX"); 294 Attribute defaultAttribute = code2attribute.get(defaultCode); 295 if (defaultAttribute == null) { 296 defaultAttribute = new Attribute(code2attribute.size(), defaultCode, level); 297 code2attribute.put(defaultCode, defaultAttribute); 298 } 299 for (TeachingRequest clazz : variables()) { 300 sLog.info("Added class " + toString(clazz)); 301 if (clazz.getAttributePreferences().isEmpty()) { 302 sLog.error("No level: " + toString(clazz)); 303 clazz.addAttributePreference(new Preference<Attribute>(defaultAttribute, -1)); 304 } 305 if (clazz.getLoad() == 0.0) { 306 sLog.error("No load: " + toString(clazz)); 307 clazz.setLoad(getProperties().getPropertyFloat("TA.DefaultLoad", 10f)); 308 } 309 } 310 311 r = new BufferedReader(new FileReader(new File(dir, "students.csv"))); 312 Set<String> studentIds = new HashSet<String>(); 313 double studentMaxLoad = 0.0; 314 while ((line = r.readLine()) != null) { 315 if (line.trim().isEmpty()) 316 continue; 317 String[] fields = line.split(","); 318 if ("puid".equals(fields[0])) 319 continue; 320 int idx = 0; 321 String id = fields[idx++]; 322 if (!studentIds.add(id)) { 323 sLog.error("Student " + id + " is two or more times in the file."); 324 } 325 boolean[] av = new boolean[50]; 326 for (int i = 0; i < 50; i++) 327 av[i] = "1".equals(fields[idx++]); 328 List<String> prefs = new ArrayList<String>(); 329 for (int i = 0; i < 3; i++) { 330 String p = fields[idx++].replace("Large lecture", "LEC").replace("Lecture", "LEC").replace("Recitation", "REC"); 331 if (p.startsWith("MA ")) 332 p = p.substring(3); 333 if ("I have no preference".equals(p)) 334 continue; 335 prefs.add(p); 336 } 337 boolean grad = "Yes".equals(fields[idx++]); 338 int b2b = Integer.parseInt(fields[idx++]); 339 float maxLoad = Float.parseFloat(fields[idx++]); 340 if (maxLoad == 0) 341 maxLoad = getProperties().getPropertyFloat("TA.DefaultMaxLoad", 20f); 342 String code = (idx < fields.length ? fields[idx++] : null); 343 Instructor instructor = new Instructor(Long.valueOf(id.replace("-","")), id, null, grad ? Constants.sPreferenceLevelNeutral : Constants.sPreferenceLevelDiscouraged, maxLoad); 344 for (int i = 0; i < prefs.size(); i++) { 345 String pref = prefs.get(i); 346 if (pref.indexOf(' ') > 0) pref = pref.substring(0, pref.indexOf(' ')); 347 Course c = courses.get(pref); 348 if (c == null) { 349 c = new Course(courses.size(), pref, true, false); 350 courses.put(pref, c); 351 } 352 instructor.addCoursePreference(new Preference<Course>(c, i == 0 ? -10 : i == 1 ? -8 : -5)); 353 } 354 if (code != null) { 355 Attribute attribute = code2attribute.get(code); 356 if (attribute == null) { 357 attribute = new Attribute(code2attribute.size(), code, level); 358 code2attribute.put(code, attribute); 359 } 360 instructor.addAttribute(attribute); 361 } 362 if (b2b == 1) 363 instructor.setBackToBackPreference(Constants.sPreferenceLevelPreferred); 364 else if (b2b == -1) 365 instructor.setBackToBackPreference(Constants.sPreferenceLevelDiscouraged); 366 for (int d = 0; d < 5; d++) { 367 int f = -1; 368 for (int t = 0; t < 10; t++) { 369 if (!av[10 * d + t]) { 370 if (f < 0) f = t; 371 } else { 372 if (f >= 0) { 373 instructor.addTimePreference(new Preference<TimeLocation>(new TimeLocation(Constants.DAY_CODES[d], 90 + 12 * f, (t - f) * 12, 0, 0.0, null, "", null, 0), Constants.sPreferenceLevelProhibited)); 374 f = -1; 375 } 376 } 377 } 378 if (f >= 0) { 379 instructor.addTimePreference(new Preference<TimeLocation>(new TimeLocation(Constants.DAY_CODES[d], 90 + 12 * f, (10 - f) * 12, 0, 0.0, null, "", null, 0), Constants.sPreferenceLevelProhibited)); 380 f = -1; 381 } 382 } 383 if (instructor.getMaxLoad() > 0) { 384 addInstructor(instructor); 385 sLog.info("Added student " + toString(instructor)); 386 int nrClasses = 0; 387 for (TeachingRequest req : variables()) { 388 if (instructor.canTeach(req) && !req.getAttributePreference(instructor).isProhibited()) { 389 sLog.info(" -- " + toString(req) + "," + (-req.getAttributePreference(instructor).getPreferenceInt()) + "," + (-instructor.getCoursePreference(req.getCourse()).getPreference())); 390 nrClasses++; 391 } 392 } 393 if (nrClasses == 0) { 394 sLog.info(" -- no courses available"); 395 } 396 studentMaxLoad += instructor.getMaxLoad(); 397 } else { 398 sLog.info("Ignoring student " + instructor); 399 if (instructor.getMaxLoad() == 0) 400 sLog.info(" -- zero max load"); 401 else 402 sLog.info(" -- no courses available"); 403 } 404 } 405 406 double totalLoad = 0.0; 407 for (TeachingRequest clazz : variables()) { 408 if (clazz.values(getEmptyAssignment()).isEmpty()) 409 sLog.error("No values: " + toString(clazz)); 410 totalLoad += clazz.getLoad(); 411 } 412 413 Map<String, Double> studentLevel2load = new HashMap<String, Double>(); 414 for (Instructor instructor: getInstructors()) { 415 Set<Attribute> levels = instructor.getAttributes(level); 416 String studentLevel = (levels.isEmpty() ? "null" : levels.iterator().next().getAttributeName()); 417 Double load = studentLevel2load.get(studentLevel); 418 studentLevel2load.put(studentLevel, instructor.getMaxLoad() + (load == null ? 0.0 : load)); 419 } 420 sLog.info("Student max loads: (total: " + sDoubleFormat.format(studentMaxLoad) + ")"); 421 for (String studentLevel : new TreeSet<String>(studentLevel2load.keySet())) { 422 Double load = studentLevel2load.get(studentLevel); 423 sLog.info(" " + studentLevel + ": " + sDoubleFormat.format(load)); 424 } 425 Map<String, Double> clazzLevel2load = new HashMap<String, Double>(); 426 for (TeachingRequest clazz : variables()) { 427 String classLevel = null; 428 TreeSet<String> levels = new TreeSet<String>(); 429 for (Preference<Attribute> ap: clazz.getAttributePreferences()) 430 levels.add(ap.getTarget().getAttributeName()); 431 for (String l : levels) { 432 classLevel = (classLevel == null ? "" : classLevel + ",") + l; 433 } 434 if (classLevel == null) 435 classLevel = "null"; 436 if (clazz.getId() < 0) 437 classLevel = clazz.getName(); 438 Double load = clazzLevel2load.get(level); 439 clazzLevel2load.put(classLevel, clazz.getLoad() + (load == null ? 0.0 : load)); 440 } 441 sLog.info("Class loads: (total: " + sDoubleFormat.format(totalLoad) + ")"); 442 for (String classLevel : new TreeSet<String>(clazzLevel2load.keySet())) { 443 Double load = clazzLevel2load.get(classLevel); 444 sLog.info(" " + level + ": " + sDoubleFormat.format(load)); 445 } 446 return true; 447 } catch (IOException e) { 448 sLog.error("Failed to load the problem: " + e.getMessage(), e); 449 return false; 450 } 451 } 452 453 @Override 454 protected void generateReports(File dir, Assignment<TeachingRequest, TeachingAssignment> assignment) throws IOException { 455 PrintWriter out = new PrintWriter(new File(dir, "solution-assignments.csv")); 456 out.println("Assignment Id,Course,Section,Time,Room,Link,Level,Load,Student,Availability,1st Preference,2nd Preference,3rd Preference,Graduate,Back-To-Back,Max Load,Level,Level,Preference"); 457 for (TeachingRequest request : variables()) { 458 Long assId = getAssignmentId(request); 459 out.print(assId == null ? "" : assId); 460 out.print("," + request.getCourse().getCourseName()); 461 Section section = request.getSections().get(0); 462 out.print("," + section.getSectionType()); 463 out.print("," + section.getTimeName(true)); 464 out.print(",\"" + (section.hasRoom() ? section.getRoom() : "") + "\""); 465 String link = getLink(request); 466 out.print("," + (link == null ? "" : link)); 467 Map<String, Integer> levels = new HashMap<String, Integer>(); 468 for (Preference<Attribute> p: request.getAttributePreferences()) 469 if (p.getTarget().getType().getTypeName().equals("Level")) 470 levels.put(p.getTarget().getAttributeName(), - p.getPreference()); 471 out.print(",\"" + levels + "\""); 472 out.print("," + new DecimalFormat("0.0").format(request.getLoad())); 473 TeachingAssignment value = assignment.getValue(request); 474 if (value != null) { 475 out.print("," + toString(value.getInstructor())); 476 out.print("," + (-value.getAttributePreference())); 477 out.print("," + (value.getCoursePreference() == -10 ? "1" : value.getCoursePreference() == -8 ? "2" : value.getCoursePreference() == -5 ? "3" : value.getCoursePreference())); 478 } 479 out.println(); 480 } 481 out.flush(); 482 out.close(); 483 484 out = new PrintWriter(new File(dir, "solution-students.csv")); 485 out.println("Student,Availability,1st Preference,2nd Preference,3rd Preference,Graduate,Back-To-Back,Max Load,Level,Assigned Load,Avg Level,Avg Preference,Back-To-Back,Diff Links,1st Assignment,2nd Assignment, 3rd Assignment"); 486 for (Instructor instructor: getInstructors()) { 487 out.print(instructor.getExternalId()); 488 out.print(",\"" + instructor.getAvailable() + "\""); 489 for (int i = 0; i < 3; i++) { 490 Preference<Course> p = (i < instructor.getCoursePreferences().size() ? instructor.getCoursePreferences().get(i) : null); 491 out.print("," + (p == null ? "" : p.getTarget().getCourseName())); 492 } 493 out.print("," + (instructor.getPreference() == 0 ? "Yes" : "No")); 494 out.print("," + (instructor.isBackToBackPreferred() ? "1" : instructor.isBackToBackDiscouraged() ? "-1" : "0")); 495 out.print("," + new DecimalFormat("0.0").format(instructor.getMaxLoad())); 496 String level = getLevel(instructor); 497 out.print("," + (level == null ? "" : level)); 498 Instructor.Context context = instructor.getContext(assignment); 499 out.print("," + new DecimalFormat("0.0").format(context.getLoad())); 500 double att = 0.0, pref = 0.0; 501 for (TeachingAssignment ta : context.getAssignments()) { 502 att += Math.abs(ta.getAttributePreference()); 503 pref += (ta.getCoursePreference() == -10 ? 1 : ta.getCoursePreference() == -8 ? 2 : ta.getCoursePreference() == -5 ? 3 : ta.getCoursePreference()); 504 } 505 int diffLinks = countDiffLinks(context.getAssignments()); 506 out.print("," + (context.getAssignments().isEmpty() ? "" : new DecimalFormat("0.0").format(att / context.getAssignments().size()))); 507 out.print("," + (context.getAssignments().isEmpty() || pref == 0.0 ? "" : new DecimalFormat("0.0").format(pref / context.getAssignments().size()))); 508 out.print("," + new DecimalFormat("0.0").format(100.0 * context.countBackToBackPercentage())); 509 out.print("," + (diffLinks <= 0 ? "" : diffLinks)); 510 for (TeachingAssignment ta : context.getAssignments()) { 511 String link = getLink(ta.variable()); 512 out.print("," + ta.variable().getCourse() + " " + ta.variable().getSections().get(0).getSectionType() + " " + ta.variable().getSections().get(0).getTime().getName(true) + (link == null ? "" : " " + link)); 513 } 514 out.println(); 515 } 516 out.flush(); 517 out.close(); 518 519 out = new PrintWriter(new File(dir, "input-courses.csv")); 520 Set<String> levels = new TreeSet<String>(); 521 for (TeachingRequest request : variables()) { 522 for (Preference<Attribute> p: request.getAttributePreferences()) 523 levels.add(p.getTarget().getAttributeName()); 524 } 525 out.print("Course,Type,Load"); 526 for (String level: levels) 527 out.print("," + level); 528 out.println(); 529 Set<String> courses = new HashSet<String>(); 530 for (TeachingRequest request : variables()) { 531 if (courses.add(request.getCourse() + "," + request.getSections().get(0).getSectionType())) { 532 out.print(request.getCourse().getCourseName() + "," + request.getSections().get(0).getSectionType() + "," + request.getLoad()); 533 } 534 for (String level: levels) { 535 int pref = 0; 536 for (Preference<Attribute> p: request.getAttributePreferences()) 537 if (p.getTarget().getAttributeName().equals(level)) pref = p.getPreference(); 538 out.print("," + (pref == 0 ? "" : -pref)); 539 } 540 out.println(); 541 } 542 out.flush(); 543 out.close(); 544 } 545 546 public static void main(String[] args) throws Exception { 547 DataProperties config = new DataProperties(); 548 config.load(MathTest.class.getClass().getResourceAsStream("/org/cpsolver/instructor/test/math.properties")); 549 config.putAll(System.getProperties()); 550 ToolBox.configureLogging(); 551 552 new MathTest(config).execute(); 553 } 554}