use of org.cpsolver.studentsct.model.Offering in project cpsolver by UniTime.
the class RequestGroupTable method create.
@Override
public CSVFile create(Assignment<Request, Enrollment> assignment, DataProperties properties) {
boolean useAmPm = properties.getPropertyBoolean("useAmPm", true);
CSVFile csv = new CSVFile();
csv.setHeader(new CSVFile.CSVField[] { new CSVFile.CSVField("Group"), new CSVFile.CSVField("Course"), new CSVFile.CSVField("Total\nSpread"), new CSVFile.CSVField("Group\nEnrollment"), new CSVFile.CSVField("Class"), new CSVFile.CSVField("Meeting Time"), new CSVFile.CSVField("Class\nSpread"), new CSVFile.CSVField("Class\nEnrollment"), new CSVFile.CSVField("Class\nLimit") });
TreeSet<RequestGroup> groups = new TreeSet<RequestGroup>(new Comparator<RequestGroup>() {
@Override
public int compare(RequestGroup g1, RequestGroup g2) {
int cmp = g1.getName().compareTo(g2.getName());
if (cmp != 0)
return cmp;
cmp = g1.getCourse().getName().compareTo(g2.getCourse().getName());
if (cmp != 0)
return cmp;
if (g1.getId() < g2.getId())
return -1;
if (g1.getId() > g2.getId())
return 1;
return (g1.getCourse().getId() < g2.getCourse().getId() ? -1 : g1.getCourse().getId() > g2.getCourse().getId() ? 1 : 0);
}
});
for (Offering offering : iModel.getOfferings()) for (Course course : offering.getCourses()) groups.addAll(course.getRequestGroups());
for (RequestGroup group : groups) {
double groupEnrollment = group.getEnrollmentWeight(assignment, null);
double groupSpread = group.getAverageSpread(assignment);
for (Config config : group.getCourse().getOffering().getConfigs()) for (Subpart subpart : config.getSubparts()) for (Section section : subpart.getSections()) {
double s = group.getSectionWeight(assignment, section, null);
if (s > 0.00001) {
csv.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(group.getName()), new CSVFile.CSVField(group.getCourse().getName()), new CSVFile.CSVField(sDF.format(100.0 * groupSpread)), new CSVFile.CSVField(Math.round(groupEnrollment)), new CSVFile.CSVField(section.getSubpart().getName() + " " + section.getName(group.getCourse().getId())), new CSVFile.CSVField(section.getTime() == null ? "" : section.getTime().getDayHeader() + " " + section.getTime().getStartTimeHeader(useAmPm) + " - " + section.getTime().getEndTimeHeader(useAmPm)), new CSVFile.CSVField(sDF.format(100.0 * group.getSectionSpread(assignment, section))), new CSVFile.CSVField(Math.round(group.getSectionWeight(assignment, section, null))), new CSVFile.CSVField(section.getLimit()) });
}
}
}
return csv;
}
use of org.cpsolver.studentsct.model.Offering in project cpsolver by UniTime.
the class OriginalStudentWeights method main.
/**
* Test case -- run to see the weights for a few courses
* @param args program arguments
*/
public static void main(String[] args) {
OriginalStudentWeights pw = new OriginalStudentWeights(new DataProperties());
DecimalFormat df = new DecimalFormat("0.000");
Student s = new Student(0l);
new CourseRequest(1l, 0, false, s, ToolBox.toList(new Course(1, "A", "1", new Offering(0, "A")), new Course(1, "A", "2", new Offering(0, "A")), new Course(1, "A", "3", new Offering(0, "A"))), false, null);
new CourseRequest(2l, 1, false, s, ToolBox.toList(new Course(1, "B", "1", new Offering(0, "B")), new Course(1, "B", "2", new Offering(0, "B")), new Course(1, "B", "3", new Offering(0, "B"))), false, null);
new CourseRequest(3l, 2, false, s, ToolBox.toList(new Course(1, "C", "1", new Offering(0, "C")), new Course(1, "C", "2", new Offering(0, "C")), new Course(1, "C", "3", new Offering(0, "C"))), false, null);
new CourseRequest(4l, 3, false, s, ToolBox.toList(new Course(1, "D", "1", new Offering(0, "D")), new Course(1, "D", "2", new Offering(0, "D")), new Course(1, "D", "3", new Offering(0, "D"))), false, null);
new CourseRequest(5l, 4, false, s, ToolBox.toList(new Course(1, "E", "1", new Offering(0, "E")), new Course(1, "E", "2", new Offering(0, "E")), new Course(1, "E", "3", new Offering(0, "E"))), false, null);
new CourseRequest(6l, 5, true, s, ToolBox.toList(new Course(1, "F", "1", new Offering(0, "F")), new Course(1, "F", "2", new Offering(0, "F")), new Course(1, "F", "3", new Offering(0, "F"))), false, null);
new CourseRequest(7l, 6, true, s, ToolBox.toList(new Course(1, "G", "1", new Offering(0, "G")), new Course(1, "G", "2", new Offering(0, "G")), new Course(1, "G", "3", new Offering(0, "G"))), false, null);
Assignment<Request, Enrollment> assignment = new DefaultSingleAssignment<Request, Enrollment>();
Placement p = new Placement(null, new TimeLocation(1, 90, 12, 0, 0, null, null, new BitSet(), 10), new ArrayList<RoomLocation>());
for (Request r : s.getRequests()) {
CourseRequest cr = (CourseRequest) r;
double[] w = new double[] { 0.0, 0.0, 0.0 };
for (int i = 0; i < cr.getCourses().size(); i++) {
Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
Set<SctAssignment> sections = new HashSet<SctAssignment>();
sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
w[i] = pw.getWeight(assignment, e, null, null);
}
System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
}
System.out.println("With one distance conflict:");
for (Request r : s.getRequests()) {
CourseRequest cr = (CourseRequest) r;
double[] w = new double[] { 0.0, 0.0, 0.0 };
for (int i = 0; i < cr.getCourses().size(); i++) {
Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
Set<SctAssignment> sections = new HashSet<SctAssignment>();
sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
Set<DistanceConflict.Conflict> dc = new HashSet<DistanceConflict.Conflict>();
dc.add(new DistanceConflict.Conflict(s, e, (Section) sections.iterator().next(), e, (Section) sections.iterator().next()));
w[i] = pw.getWeight(assignment, e, dc, null);
}
System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
}
System.out.println("With two distance conflicts:");
for (Request r : s.getRequests()) {
CourseRequest cr = (CourseRequest) r;
double[] w = new double[] { 0.0, 0.0, 0.0 };
for (int i = 0; i < cr.getCourses().size(); i++) {
Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
Set<SctAssignment> sections = new HashSet<SctAssignment>();
sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
Set<DistanceConflict.Conflict> dc = new HashSet<DistanceConflict.Conflict>();
dc.add(new DistanceConflict.Conflict(s, e, (Section) sections.iterator().next(), e, (Section) sections.iterator().next()));
dc.add(new DistanceConflict.Conflict(s, e, (Section) sections.iterator().next(), e, new Section(1, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null)));
w[i] = pw.getWeight(assignment, e, dc, null);
}
System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
}
System.out.println("With 25% time overlapping conflicts:");
for (Request r : s.getRequests()) {
CourseRequest cr = (CourseRequest) r;
double[] w = new double[] { 0.0, 0.0, 0.0 };
for (int i = 0; i < cr.getCourses().size(); i++) {
Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
Set<SctAssignment> sections = new HashSet<SctAssignment>();
sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
Set<TimeOverlapsCounter.Conflict> toc = new HashSet<TimeOverlapsCounter.Conflict>();
toc.add(new TimeOverlapsCounter.Conflict(s, 3, e, sections.iterator().next(), e, sections.iterator().next()));
w[i] = pw.getWeight(assignment, e, null, toc);
}
System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
}
}
use of org.cpsolver.studentsct.model.Offering in project cpsolver by UniTime.
the class UnbalancedSectionsTable method createTable.
/**
* Create report
*
* @param assignment current assignment
* @param includeLastLikeStudents
* true, if last-like students should be included (i.e.,
* {@link Student#isDummy()} is true)
* @param includeRealStudents
* true, if real students should be included (i.e.,
* {@link Student#isDummy()} is false)
* @param useAmPm use 12-hour format
* @return report as comma separated text file
*/
public CSVFile createTable(Assignment<Request, Enrollment> assignment, boolean includeLastLikeStudents, boolean includeRealStudents, boolean useAmPm) {
CSVFile csv = new CSVFile();
csv.setHeader(new CSVFile.CSVField[] { new CSVFile.CSVField("Course"), new CSVFile.CSVField("Class"), new CSVFile.CSVField("Meeting Time"), new CSVFile.CSVField("Enrollment"), new CSVFile.CSVField("Target"), new CSVFile.CSVField("Limit"), new CSVFile.CSVField("Disbalance [%]") });
TreeSet<Offering> offerings = new TreeSet<Offering>(new Comparator<Offering>() {
@Override
public int compare(Offering o1, Offering o2) {
int cmp = o1.getName().compareToIgnoreCase(o2.getName());
if (cmp != 0)
return cmp;
return o1.getId() < o2.getId() ? -1 : o2.getId() == o2.getId() ? 0 : 1;
}
});
offerings.addAll(getModel().getOfferings());
Offering last = null;
for (Offering offering : offerings) {
for (Config config : offering.getConfigs()) {
double configEnrl = 0;
for (Enrollment e : config.getEnrollments(assignment)) {
if (e.getStudent().isDummy() && !includeLastLikeStudents)
continue;
if (!e.getStudent().isDummy() && !includeRealStudents)
continue;
configEnrl += e.getRequest().getWeight();
}
for (Subpart subpart : config.getSubparts()) {
if (subpart.getSections().size() <= 1)
continue;
if (subpart.getLimit() > 0) {
// sections have limits -> desired size is section limit x (total enrollment / total limit)
double ratio = configEnrl / subpart.getLimit();
for (Section section : subpart.getSections()) {
double enrl = 0.0;
for (Enrollment e : section.getEnrollments(assignment)) {
if (e.getStudent().isDummy() && !includeLastLikeStudents)
continue;
if (!e.getStudent().isDummy() && !includeRealStudents)
continue;
enrl += e.getRequest().getWeight();
}
double desired = ratio * section.getLimit();
if (Math.abs(desired - enrl) >= Math.max(1.0, 0.1 * section.getLimit())) {
if (last != null && !offering.equals(last))
csv.addLine();
csv.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(offering.equals(last) ? "" : offering.getName()), new CSVFile.CSVField(section.getSubpart().getName() + " " + section.getName()), new CSVFile.CSVField(section.getTime() == null ? "" : section.getTime().getDayHeader() + " " + section.getTime().getStartTimeHeader(useAmPm) + " - " + section.getTime().getEndTimeHeader(useAmPm)), new CSVFile.CSVField(sDF1.format(enrl)), new CSVFile.CSVField(sDF2.format(desired)), new CSVFile.CSVField(sDF1.format(section.getLimit())), new CSVFile.CSVField(sDF2.format(Math.min(1.0, Math.max(-1.0, (enrl - desired) / section.getLimit())))) });
last = offering;
}
}
} else {
// unlimited sections -> desired size is total enrollment / number of sections
for (Section section : subpart.getSections()) {
double enrl = 0.0;
for (Enrollment e : section.getEnrollments(assignment)) {
if (e.getStudent().isDummy() && !includeLastLikeStudents)
continue;
if (!e.getStudent().isDummy() && !includeRealStudents)
continue;
enrl += e.getRequest().getWeight();
}
double desired = configEnrl / subpart.getSections().size();
if (Math.abs(desired - enrl) >= Math.max(1.0, 0.1 * desired)) {
if (last != null && !offering.equals(last))
csv.addLine();
csv.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(offering.equals(last) ? "" : offering.getName()), new CSVFile.CSVField(section.getSubpart().getName() + " " + section.getName()), new CSVFile.CSVField(section.getTime() == null ? "" : section.getTime().getDayHeader() + " " + section.getTime().getStartTimeHeader(useAmPm) + " - " + section.getTime().getEndTimeHeader(useAmPm)), new CSVFile.CSVField(sDF1.format(enrl)), new CSVFile.CSVField(sDF2.format(desired)), new CSVFile.CSVField(""), new CSVFile.CSVField(sDF2.format(Math.min(1.0, Math.max(-1.0, (enrl - desired) / desired)))) });
last = offering;
}
}
}
}
}
}
return csv;
}
use of org.cpsolver.studentsct.model.Offering in project cpsolver by UniTime.
the class Test method loadCrsReqFiles.
/**
* Load course request from the given files (in the format being used by the
* old MSF system)
*
* @param model
* student sectioning model (with offerings loaded)
* @param files
* semi-colon separated list of files to be loaded
*/
public static void loadCrsReqFiles(StudentSectioningModel model, String files) {
try {
boolean lastLike = model.getProperties().getPropertyBoolean("Test.CrsReqIsLastLike", true);
boolean shuffleIds = model.getProperties().getPropertyBoolean("Test.CrsReqShuffleStudentIds", true);
boolean tryWithoutSuffix = model.getProperties().getPropertyBoolean("Test.CrsReqTryWithoutSuffix", false);
HashMap<Long, Student> students = new HashMap<Long, Student>();
long reqId = 0;
for (StringTokenizer stk = new StringTokenizer(files, ";"); stk.hasMoreTokens(); ) {
String file = stk.nextToken();
sLog.debug("Loading " + file + " ...");
BufferedReader in = new BufferedReader(new FileReader(file));
String line;
int lineIndex = 0;
while ((line = in.readLine()) != null) {
lineIndex++;
if (line.length() <= 150)
continue;
char code = line.charAt(13);
if (code == 'H' || code == 'T')
// skip header and tail
continue;
long studentId = Long.parseLong(line.substring(14, 23));
Student student = students.get(Long.valueOf(studentId));
if (student == null) {
student = new Student(studentId);
if (lastLike)
student.setDummy(true);
students.put(Long.valueOf(studentId), student);
sLog.debug(" -- loading student " + studentId + " ...");
} else
sLog.debug(" -- updating student " + studentId + " ...");
line = line.substring(150);
while (line.length() >= 20) {
String subjectArea = line.substring(0, 4).trim();
String courseNbr = line.substring(4, 8).trim();
if (subjectArea.length() == 0 || courseNbr.length() == 0) {
line = line.substring(20);
continue;
}
/*
* // UNUSED String instrSel = line.substring(8,10);
* //ZZ - Remove previous instructor selection char
* reqPDiv = line.charAt(10); //P - Personal preference;
* C - Conflict resolution; //0 - (Zero) used by program
* only, for change requests to reschedule division //
* (used to reschedule canceled division) String reqDiv
* = line.substring(11,13); //00 - Reschedule division
* String reqSect = line.substring(13,15); //Contains
* designator for designator-required courses String
* credit = line.substring(15,19); char nameRaise =
* line.charAt(19); //N - Name raise
*/
// A - Add; D - Drop; C -
char action = line.charAt(19);
// Change
sLog.debug(" -- requesting " + subjectArea + " " + courseNbr + " (action:" + action + ") ...");
Course course = null;
offerings: for (Offering offering : model.getOfferings()) {
for (Course c : offering.getCourses()) {
if (c.getSubjectArea().equals(subjectArea) && c.getCourseNumber().equals(courseNbr)) {
course = c;
break offerings;
}
}
}
if (course == null && tryWithoutSuffix && courseNbr.charAt(courseNbr.length() - 1) >= 'A' && courseNbr.charAt(courseNbr.length() - 1) <= 'Z') {
String courseNbrNoSfx = courseNbr.substring(0, courseNbr.length() - 1);
offerings: for (Offering offering : model.getOfferings()) {
for (Course c : offering.getCourses()) {
if (c.getSubjectArea().equals(subjectArea) && c.getCourseNumber().equals(courseNbrNoSfx)) {
course = c;
break offerings;
}
}
}
}
if (course == null) {
if (courseNbr.charAt(courseNbr.length() - 1) >= 'A' && courseNbr.charAt(courseNbr.length() - 1) <= 'Z') {
} else {
sLog.warn(" -- course " + subjectArea + " " + courseNbr + " not found (file " + file + ", line " + lineIndex + ")");
}
} else {
CourseRequest courseRequest = null;
for (Request request : student.getRequests()) {
if (request instanceof CourseRequest && ((CourseRequest) request).getCourses().contains(course)) {
courseRequest = (CourseRequest) request;
break;
}
}
if (action == 'A') {
if (courseRequest == null) {
List<Course> courses = new ArrayList<Course>(1);
courses.add(course);
courseRequest = new CourseRequest(reqId++, student.getRequests().size(), false, student, courses, false, null);
} else {
sLog.warn(" -- request for course " + course + " is already present");
}
} else if (action == 'D') {
if (courseRequest == null) {
sLog.warn(" -- request for course " + course + " is not present -- cannot be dropped");
} else {
student.getRequests().remove(courseRequest);
}
} else if (action == 'C') {
if (courseRequest == null) {
sLog.warn(" -- request for course " + course + " is not present -- cannot be changed");
} else {
// ?
}
} else {
sLog.warn(" -- unknown action " + action);
}
}
line = line.substring(20);
}
}
in.close();
}
HashMap<Course, List<Request>> requests = new HashMap<Course, List<Request>>();
Set<Long> studentIds = new HashSet<Long>();
for (Student student : students.values()) {
if (!student.getRequests().isEmpty())
model.addStudent(student);
if (shuffleIds) {
long newId = -1;
while (true) {
newId = 1 + (long) (999999999L * Math.random());
if (studentIds.add(Long.valueOf(newId)))
break;
}
student.setId(newId);
}
if (student.isDummy()) {
for (Request request : student.getRequests()) {
if (request instanceof CourseRequest) {
Course course = ((CourseRequest) request).getCourses().get(0);
List<Request> requestsThisCourse = requests.get(course);
if (requestsThisCourse == null) {
requestsThisCourse = new ArrayList<Request>();
requests.put(course, requestsThisCourse);
}
requestsThisCourse.add(request);
}
}
}
}
Collections.sort(model.getStudents(), new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getId(), o2.getId());
}
});
for (Map.Entry<Course, List<Request>> entry : requests.entrySet()) {
Course course = entry.getKey();
List<Request> requestsThisCourse = entry.getValue();
double weight = getLastLikeStudentWeight(course, 0, requestsThisCourse.size());
for (Request request : requestsThisCourse) {
request.setWeight(weight);
}
}
if (model.getProperties().getProperty("Test.EtrChk") != null) {
for (StringTokenizer stk = new StringTokenizer(model.getProperties().getProperty("Test.EtrChk"), ";"); stk.hasMoreTokens(); ) {
String file = stk.nextToken();
sLog.debug("Loading " + file + " ...");
BufferedReader in = new BufferedReader(new FileReader(file));
try {
String line;
while ((line = in.readLine()) != null) {
if (line.length() < 55)
continue;
char code = line.charAt(12);
if (code == 'H' || code == 'T')
// skip header and tail
continue;
if (code == 'D' || code == 'K')
// skip delete nad cancel
continue;
long studentId = Long.parseLong(line.substring(2, 11));
Student student = students.get(Long.valueOf(studentId));
if (student == null) {
sLog.info(" -- student " + studentId + " not found");
continue;
}
sLog.info(" -- reading student " + studentId);
String area = line.substring(15, 18).trim();
if (area.length() == 0)
continue;
String clasf = line.substring(18, 20).trim();
String major = line.substring(21, 24).trim();
String minor = line.substring(24, 27).trim();
student.getAreaClassificationMajors().clear();
student.getAreaClassificationMinors().clear();
if (major.length() > 0)
student.getAreaClassificationMajors().add(new AreaClassificationMajor(area, clasf, major));
if (minor.length() > 0)
student.getAreaClassificationMajors().add(new AreaClassificationMajor(area, clasf, minor));
}
} finally {
in.close();
}
}
}
int without = 0;
for (Student student : students.values()) {
if (student.getAreaClassificationMajors().isEmpty())
without++;
}
fixPriorities(model);
sLog.info("Students without academic area: " + without);
} catch (Exception e) {
sLog.error(e.getMessage(), e);
}
}
use of org.cpsolver.studentsct.model.Offering in project cpsolver by UniTime.
the class StudentSectioningModel method getExtendedInfo.
/**
* Model extended info. Some more information (that is more expensive to
* compute) is added to an ordinary {@link Model#getInfo(Assignment)}.
*/
@Override
public Map<String, String> getExtendedInfo(Assignment<Request, Enrollment> assignment) {
Map<String, String> info = getInfo(assignment);
/*
double dc = 0;
if (getDistanceConflict() != null && getDistanceConflict().getTotalNrConflicts(assignment) != 0) {
Set<DistanceConflict.Conflict> conf = getDistanceConflict().getAllConflicts(assignment);
int sdc = 0;
for (DistanceConflict.Conflict c: conf) {
dc += avg(c.getR1().getWeight(), c.getR2().getWeight()) * iStudentWeights.getDistanceConflictWeight(assignment, c);
if (c.getStudent().isNeedShortDistances()) sdc ++;
}
if (!conf.isEmpty())
info.put("Student distance conflicts", conf.size() + (sdc > 0 ? " (" + getDistanceConflict().getDistanceMetric().getShortDistanceAccommodationReference() + ": " + sdc + ", weighted: " : " (weighted: ") + sDecimalFormat.format(dc) + ")");
}
*/
if (getStudentQuality() == null && getTimeOverlaps() != null && getTimeOverlaps().getTotalNrConflicts(assignment) != 0) {
Set<TimeOverlapsCounter.Conflict> conf = getTimeOverlaps().getContext(assignment).computeAllConflicts(assignment);
int share = 0, crShare = 0;
for (TimeOverlapsCounter.Conflict c : conf) {
share += c.getShare();
if (c.getR1() instanceof CourseRequest && c.getR2() instanceof CourseRequest)
crShare += c.getShare();
}
if (share > 0)
info.put("Time overlapping conflicts", sDoubleFormat.format(5.0 * share / iStudents.size()) + " mins per student\n(" + sDoubleFormat.format(5.0 * crShare / iStudents.size()) + " between courses; " + sDoubleFormat.format(getTimeOverlaps().getTotalNrConflicts(assignment) / 12.0) + " hours total)");
}
/*
info.put("Overall solution value", sDecimalFormat.format(total - dc - toc) + (dc == 0.0 && toc == 0.0 ? "" :
" (" + (dc != 0.0 ? "distance: " + sDecimalFormat.format(dc): "") + (dc != 0.0 && toc != 0.0 ? ", " : "") +
(toc != 0.0 ? "overlap: " + sDecimalFormat.format(toc) : "") + ")")
);
*/
double disbWeight = 0;
int disbSections = 0;
int disb10Sections = 0;
int disb10Limit = getProperties().getPropertyInt("Info.ListDisbalancedSections", 0);
Set<String> disb10SectionList = (disb10Limit == 0 ? null : new TreeSet<String>());
for (Offering offering : getOfferings()) {
for (Config config : offering.getConfigs()) {
double enrl = config.getEnrollmentTotalWeight(assignment, null);
for (Subpart subpart : config.getSubparts()) {
if (subpart.getSections().size() <= 1)
continue;
if (subpart.getLimit() > 0) {
// sections have limits -> desired size is section limit x (total enrollment / total limit)
double ratio = enrl / subpart.getLimit();
for (Section section : subpart.getSections()) {
double desired = ratio * section.getLimit();
disbWeight += Math.abs(section.getEnrollmentTotalWeight(assignment, null) - desired);
disbSections++;
if (Math.abs(desired - section.getEnrollmentTotalWeight(assignment, null)) >= Math.max(1.0, 0.1 * section.getLimit())) {
disb10Sections++;
if (disb10SectionList != null)
disb10SectionList.add(section.getSubpart().getConfig().getOffering().getName() + " " + section.getSubpart().getName() + " " + section.getName());
}
}
} else {
// unlimited sections -> desired size is total enrollment / number of sections
for (Section section : subpart.getSections()) {
double desired = enrl / subpart.getSections().size();
disbWeight += Math.abs(section.getEnrollmentTotalWeight(assignment, null) - desired);
disbSections++;
if (Math.abs(desired - section.getEnrollmentTotalWeight(assignment, null)) >= Math.max(1.0, 0.1 * desired)) {
disb10Sections++;
if (disb10SectionList != null)
disb10SectionList.add(section.getSubpart().getConfig().getOffering().getName() + " " + section.getSubpart().getName() + " " + section.getName());
}
}
}
}
}
}
if (disbSections != 0) {
double assignedCRWeight = getContext(assignment).getAssignedCourseRequestWeight();
info.put("Average disbalance", sDecimalFormat.format(assignedCRWeight == 0 ? 0.0 : 100.0 * disbWeight / assignedCRWeight) + "% (" + sDecimalFormat.format(disbWeight / disbSections) + ")");
String list = "";
if (disb10SectionList != null) {
int i = 0;
for (String section : disb10SectionList) {
if (i == disb10Limit) {
list += "\n...";
break;
}
list += "\n" + section;
i++;
}
}
info.put("Sections disbalanced by 10% or more", sDecimalFormat.format(disbSections == 0 ? 0.0 : 100.0 * disb10Sections / disbSections) + "% (" + disb10Sections + ")" + (list.isEmpty() ? "" : "\n" + list));
}
int assCR = 0, priCR = 0;
for (Request r : variables()) {
if (r instanceof CourseRequest && !r.getStudent().isDummy()) {
CourseRequest cr = (CourseRequest) r;
Enrollment e = assignment.getValue(cr);
if (e != null) {
assCR++;
if (!cr.isAlternative() && cr.getCourses().get(0).equals(e.getCourse()))
priCR++;
}
}
}
if (assCR > 0)
info.put("Assigned priority course requests", sDoubleFormat.format(100.0 * priCR / assCR) + "% (" + priCR + "/" + assCR + ")");
int[] missing = new int[] { 0, 0, 0, 0, 0 };
int incomplete = 0;
for (Student student : getStudents()) {
if (student.isDummy())
continue;
int nrRequests = 0;
int nrAssignedRequests = 0;
for (Request r : student.getRequests()) {
// ignore free times
if (!(r instanceof CourseRequest))
continue;
if (!r.isAlternative())
nrRequests++;
if (r.isAssigned(assignment))
nrAssignedRequests++;
}
if (nrAssignedRequests < nrRequests) {
missing[Math.min(nrRequests - nrAssignedRequests, missing.length) - 1]++;
incomplete++;
}
}
for (int i = 0; i < missing.length; i++) if (missing[i] > 0)
info.put("Students missing " + (i == 0 ? "1 course" : i + 1 == missing.length ? (i + 1) + " or more courses" : (i + 1) + " courses"), sDecimalFormat.format(100.0 * missing[i] / incomplete) + "% (" + missing[i] + ")");
// + " [precise: " + sDoubleFormat.format(getTotalValue(assignment, true)) + "]");
info.put("Overall solution value", sDoubleFormat.format(getTotalValue(assignment)));
int nrStudentsBelowMinCredit = 0, nrStudents = 0;
for (Student student : getStudents()) {
if (student.isDummy())
continue;
if (student.hasMinCredit()) {
nrStudents++;
float credit = student.getAssignedCredit(assignment);
if (credit < student.getMinCredit() && !student.isComplete(assignment))
nrStudentsBelowMinCredit++;
}
}
if (nrStudentsBelowMinCredit > 0)
info.put("Students below min credit", sDoubleFormat.format(100.0 * nrStudentsBelowMinCredit / nrStudents) + "% (" + nrStudentsBelowMinCredit + "/" + nrStudents + ")");
int[] notAssignedPriority = new int[] { 0, 0, 0, 0, 0, 0, 0 };
int[] assignedChoice = new int[] { 0, 0, 0, 0, 0 };
int notAssignedTotal = 0, assignedChoiceTotal = 0;
int avgPriority = 0, avgChoice = 0;
for (Student student : getStudents()) {
if (student.isDummy())
continue;
for (Request r : student.getRequests()) {
// ignore free times
if (!(r instanceof CourseRequest))
continue;
Enrollment e = r.getAssignment(assignment);
if (e == null) {
if (!r.isAlternative()) {
notAssignedPriority[Math.min(r.getPriority(), notAssignedPriority.length - 1)]++;
notAssignedTotal++;
avgPriority += r.getPriority();
}
} else {
assignedChoice[Math.min(e.getTruePriority(), assignedChoice.length - 1)]++;
assignedChoiceTotal++;
avgChoice += e.getTruePriority();
}
}
}
for (int i = 0; i < notAssignedPriority.length; i++) if (notAssignedPriority[i] > 0)
info.put("Priority: Not-assigned priority " + (i + 1 == notAssignedPriority.length ? (i + 1) + "+" : (i + 1)) + " course requests", sDecimalFormat.format(100.0 * notAssignedPriority[i] / notAssignedTotal) + "% (" + notAssignedPriority[i] + ")");
if (notAssignedTotal > 0)
info.put("Priority: Average not-assigned priority", sDecimalFormat.format(1.0 + ((double) avgPriority) / notAssignedTotal));
for (int i = 0; i < assignedChoice.length; i++) if (assignedChoice[i] > 0)
info.put("Choice: assigned " + (i == 0 ? "1st" : i == 1 ? "2nd" : i == 2 ? "3rd" : i + 1 == assignedChoice.length ? (i + 1) + "th+" : (i + 1) + "th") + " course choice", sDecimalFormat.format(100.0 * assignedChoice[i] / assignedChoiceTotal) + "% (" + assignedChoice[i] + ")");
if (assignedChoiceTotal > 0)
info.put("Choice: Average assigned choice", sDecimalFormat.format(1.0 + ((double) avgChoice) / assignedChoiceTotal));
int nbrSections = 0, nbrFullSections = 0, nbrSections98 = 0, nbrSections95 = 0, nbrSections90 = 0, nbrSectionsDis = 0;
int enrlSections = 0, enrlFullSections = 0, enrlSections98 = 0, enrlSections95 = 0, enrlSections90 = 0, enrlSectionsDis = 0;
int nbrOfferings = 0, nbrFullOfferings = 0, nbrOfferings98 = 0, nbrOfferings95 = 0, nbrOfferings90 = 0;
int enrlOfferings = 0, enrlOfferingsFull = 0, enrlOfferings98 = 0, enrlOfferings95 = 0, enrlOfferings90 = 0;
for (Offering offering : getOfferings()) {
int offeringLimit = 0, offeringEnrollment = 0;
for (Config config : offering.getConfigs()) {
int configLimit = config.getLimit();
for (Subpart subpart : config.getSubparts()) {
int subpartLimit = 0;
for (Section section : subpart.getSections()) {
if (section.isCancelled())
continue;
int enrl = section.getEnrollments(assignment).size();
if (section.getLimit() < 0 || subpartLimit < 0)
subpartLimit = -1;
else
subpartLimit += (section.isEnabled() ? section.getLimit() : enrl);
nbrSections++;
enrlSections += enrl;
if (section.getLimit() >= 0 && section.getLimit() <= enrl) {
nbrFullSections++;
enrlFullSections += enrl;
}
if (!section.isEnabled() && (enrl > 0 || section.getLimit() >= 0)) {
nbrSectionsDis++;
enrlSectionsDis += enrl;
}
if (section.getLimit() >= 0 && (section.getLimit() - enrl) <= Math.round(0.02 * section.getLimit())) {
nbrSections98++;
enrlSections98 += enrl;
}
if (section.getLimit() >= 0 && (section.getLimit() - enrl) <= Math.round(0.05 * section.getLimit())) {
nbrSections95++;
enrlSections95 += enrl;
}
if (section.getLimit() >= 0 && (section.getLimit() - enrl) <= Math.round(0.10 * section.getLimit())) {
nbrSections90++;
enrlSections90 += enrl;
}
}
if (configLimit < 0 || subpartLimit < 0)
configLimit = -1;
else
configLimit = Math.min(configLimit, subpartLimit);
}
if (offeringLimit < 0 || configLimit < 0)
offeringLimit = -1;
else
offeringLimit += configLimit;
offeringEnrollment += config.getEnrollments(assignment).size();
}
nbrOfferings++;
enrlOfferings += offeringEnrollment;
if (offeringLimit >= 0 && offeringEnrollment >= offeringLimit) {
nbrFullOfferings++;
enrlOfferingsFull += offeringEnrollment;
}
if (offeringLimit >= 0 && (offeringLimit - offeringEnrollment) <= Math.round(0.02 * offeringLimit)) {
nbrOfferings98++;
enrlOfferings98 += offeringEnrollment;
}
if (offeringLimit >= 0 && (offeringLimit - offeringEnrollment) <= Math.round(0.05 * offeringLimit)) {
nbrOfferings95++;
enrlOfferings95 += offeringEnrollment;
}
if (offeringLimit >= 0 && (offeringLimit - offeringEnrollment) <= Math.round(0.10 * offeringLimit)) {
nbrOfferings90++;
enrlOfferings90 += offeringEnrollment;
}
}
if (enrlOfferings90 > 0 && enrlOfferings > 0)
info.put("Full Offerings", (nbrFullOfferings > 0 ? nbrFullOfferings + " with no space (" + sDecimalFormat.format(100.0 * nbrFullOfferings / nbrOfferings) + "% of all offerings, " + sDecimalFormat.format(100.0 * enrlOfferingsFull / enrlOfferings) + "% assignments)\n" : "") + (nbrOfferings98 > nbrFullOfferings ? nbrOfferings98 + " with ≤ 2% available (" + sDecimalFormat.format(100.0 * nbrOfferings98 / nbrOfferings) + "% of all offerings, " + sDecimalFormat.format(100.0 * enrlOfferings98 / enrlOfferings) + "% assignments)\n" : "") + (nbrOfferings95 > nbrOfferings98 ? nbrOfferings95 + " with ≤ 5% available (" + sDecimalFormat.format(100.0 * nbrOfferings95 / nbrOfferings) + "% of all offerings, " + sDecimalFormat.format(100.0 * enrlOfferings95 / enrlOfferings) + "% assignments)\n" : "") + (nbrOfferings90 > nbrOfferings95 ? nbrOfferings90 + " with ≤ 10% available (" + sDecimalFormat.format(100.0 * nbrOfferings90 / nbrOfferings) + "% of all offerings, " + sDecimalFormat.format(100.0 * enrlOfferings90 / enrlOfferings) + "% assignments)" : ""));
if ((enrlSections90 > 0 || nbrSectionsDis > 0) && enrlSections > 0)
info.put("Full Sections", (nbrFullSections > 0 ? nbrFullSections + " with no space (" + sDecimalFormat.format(100.0 * nbrFullSections / nbrSections) + "% of all sections, " + sDecimalFormat.format(100.0 * enrlFullSections / enrlSections) + "% assignments)\n" : "") + (nbrSectionsDis > 0 ? nbrSectionsDis + " disabled (" + sDecimalFormat.format(100.0 * nbrSectionsDis / nbrSections) + "% of all sections, " + sDecimalFormat.format(100.0 * enrlSectionsDis / enrlSections) + "% assignments)\n" : "") + (enrlSections98 > nbrFullSections ? nbrSections98 + " with ≤ 2% available (" + sDecimalFormat.format(100.0 * nbrSections98 / nbrSections) + "% of all sections, " + sDecimalFormat.format(100.0 * enrlSections98 / enrlSections) + "% assignments)\n" : "") + (nbrSections95 > enrlSections98 ? nbrSections95 + " with ≤ 5% available (" + sDecimalFormat.format(100.0 * nbrSections95 / nbrSections) + "% of all sections, " + sDecimalFormat.format(100.0 * enrlSections95 / enrlSections) + "% assignments)\n" : "") + (nbrSections90 > nbrSections95 ? nbrSections90 + " with ≤ 10% available (" + sDecimalFormat.format(100.0 * nbrSections90 / nbrSections) + "% of all sections, " + sDecimalFormat.format(100.0 * enrlSections90 / enrlSections) + "% assignments)" : ""));
if (getStudentQuality() != null) {
int shareCR = getStudentQuality().getContext(assignment).countTotalPenalty(StudentQuality.Type.CourseTimeOverlap, assignment);
int shareFT = getStudentQuality().getContext(assignment).countTotalPenalty(StudentQuality.Type.FreeTimeOverlap, assignment);
int shareUN = getStudentQuality().getContext(assignment).countTotalPenalty(StudentQuality.Type.Unavailability, assignment);
if (shareCR > 0) {
Set<Student> students = new HashSet<Student>();
for (StudentQuality.Conflict c : getStudentQuality().getContext(assignment).computeAllConflicts(StudentQuality.Type.CourseTimeOverlap, assignment)) {
students.add(c.getStudent());
}
info.put("Time overlaps: courses", students.size() + " students (avg " + sDoubleFormat.format(5.0 * shareCR / students.size()) + " mins)");
}
if (shareFT > 0) {
Set<Student> students = new HashSet<Student>();
for (StudentQuality.Conflict c : getStudentQuality().getContext(assignment).computeAllConflicts(StudentQuality.Type.FreeTimeOverlap, assignment)) {
students.add(c.getStudent());
}
info.put("Time overlaps: free times", students.size() + " students (avg " + sDoubleFormat.format(5.0 * shareFT / students.size()) + " mins)");
}
if (shareUN > 0) {
Set<Student> students = new HashSet<Student>();
for (StudentQuality.Conflict c : getStudentQuality().getContext(assignment).computeAllConflicts(StudentQuality.Type.Unavailability, assignment)) {
students.add(c.getStudent());
}
info.put("Time overlaps: teaching assignments", students.size() + " students (avg " + sDoubleFormat.format(5.0 * shareUN / students.size()) + " mins)");
}
} else if (getTimeOverlaps() != null && getTimeOverlaps().getTotalNrConflicts(assignment) != 0) {
Set<TimeOverlapsCounter.Conflict> conf = getTimeOverlaps().getContext(assignment).computeAllConflicts(assignment);
int shareCR = 0, shareFT = 0, shareUN = 0;
Set<Student> studentsCR = new HashSet<Student>();
Set<Student> studentsFT = new HashSet<Student>();
Set<Student> studentsUN = new HashSet<Student>();
for (TimeOverlapsCounter.Conflict c : conf) {
if (c.getR1() instanceof CourseRequest && c.getR2() instanceof CourseRequest) {
shareCR += c.getShare();
studentsCR.add(c.getStudent());
} else if (c.getS2() instanceof Unavailability) {
shareUN += c.getShare();
studentsUN.add(c.getStudent());
} else {
shareFT += c.getShare();
studentsFT.add(c.getStudent());
}
}
if (shareCR > 0)
info.put("Time overlaps: courses", studentsCR.size() + " students (avg " + sDoubleFormat.format(5.0 * shareCR / studentsCR.size()) + " mins)");
if (shareFT > 0)
info.put("Time overlaps: free times", studentsFT.size() + " students (avg " + sDoubleFormat.format(5.0 * shareFT / studentsFT.size()) + " mins)");
if (shareUN > 0)
info.put("Time overlaps: teaching assignments", studentsUN.size() + " students (avg " + sDoubleFormat.format(5.0 * shareUN / studentsUN.size()) + " mins)");
}
return info;
}
Aggregations