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