use of org.cpsolver.studentsct.model.Subpart in project cpsolver by UniTime.
the class EqualWeightCriterion method canImprove.
@Override
public boolean canImprove(Assignment<Request, Enrollment> assignment, int maxIdx, Enrollment[] current, Enrollment[] best) {
// 0. best number of assigned course requests (including alternativity &
// priority)
int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0;
int currentAssignedRequests = 0, bestAssignedRequests = 0;
int currentAssignedPriority = 0, bestAssignedPriority = 0;
int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0;
int currentNotFollowedReservations = 0, bestNotFollowedReservations = 0;
int alt = 0;
for (int idx = 0; idx < current.length; idx++) {
if (idx < maxIdx) {
if (current[idx] != null && current[idx].getAssignments() != null) {
currentAssignedRequests++;
if (current[idx].isCourseRequest())
currentAssignedCourseReq++;
currentAssignedPriority += current[idx].getTruePriority() * current[idx].getTruePriority();
currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0);
if (current[idx].getTruePriority() < current[idx].getPriority())
currentNotFollowedReservations++;
} else if (!isFreeTime(idx) && !getRequest(idx).isAlternative()) {
alt++;
}
} else {
if (!getRequest(idx).isAlternative()) {
currentAssignedRequests++;
if (!isFreeTime(idx))
currentAssignedCourseReq++;
} else if (alt > 0) {
currentAssignedRequests++;
currentAssignedCourseReq++;
alt--;
currentAssignedAlternativity++;
}
}
if (best[idx] != null && best[idx].getAssignments() != null) {
bestAssignedRequests++;
if (best[idx].isCourseRequest())
bestAssignedCourseReq++;
bestAssignedPriority += best[idx].getTruePriority() * best[idx].getTruePriority();
bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0);
if (best[idx].getTruePriority() < best[idx].getPriority())
bestNotFollowedReservations++;
}
}
if (currentAssignedCourseReq > bestAssignedCourseReq)
return true;
if (bestAssignedCourseReq > currentAssignedCourseReq)
return false;
if (currentAssignedPriority < bestAssignedPriority)
return true;
if (bestAssignedPriority < currentAssignedPriority)
return false;
if (currentAssignedAlternativity < bestAssignedAlternativity)
return true;
if (bestAssignedAlternativity < currentAssignedAlternativity)
return false;
// 0.1. allowed, but not available sections
int notAvailable = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest && best[idx].getReservation() != null && best[idx].getReservation().canAssignOverLimit()) {
for (Section section : best[idx].getSections()) {
if (section.getLimit() == 0)
notAvailable++;
}
}
if (idx < maxIdx && current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest && current[idx].getReservation() != null && current[idx].getReservation().canAssignOverLimit()) {
for (Section section : current[idx].getSections()) {
if (section.getLimit() == 0)
notAvailable--;
}
}
}
if (notAvailable > 0) {
return true;
}
// 0.5. avoid course time overlaps & unavailabilities
if (getModel().getStudentQuality() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getRequest() instanceof CourseRequest)
bestTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.CourseTimeOverlap, best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getRequest() instanceof CourseRequest)
currentTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.CourseTimeOverlap, current[x], current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.Unavailability, best[idx]);
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.Unavailability, current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return true;
if (bestTimeOverlaps < currentTimeOverlaps)
return false;
} else if (getModel().getTimeOverlaps() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getRequest() instanceof CourseRequest)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getRequest() instanceof CourseRequest)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]);
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return true;
if (bestTimeOverlaps < currentTimeOverlaps)
return false;
}
// 1. maximize number of penalties
double bestPenalties = 0, currentPenalties = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) bestPenalties += getModel().getOverExpected(assignment, best, idx, section, best[idx].getRequest());
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) currentPenalties += getModel().getOverExpected(assignment, current, idx, section, current[idx].getRequest());
}
}
if (currentPenalties < bestPenalties)
return true;
if (bestPenalties < currentPenalties)
return false;
// 2. best number of assigned requests (including free time requests)
if (currentAssignedRequests > bestAssignedRequests)
return true;
if (bestAssignedRequests > currentAssignedRequests)
return false;
// 3. maximize selection
int bestSelected = 0, currentSelected = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].isCourseRequest()) {
Set<Section> preferred = getPreferredSections(best[idx].getRequest());
if (preferred != null && !preferred.isEmpty()) {
for (Section section : best[idx].getSections()) if (preferred.contains(section)) {
if (idx < maxIdx)
bestSelected++;
} else if (idx >= maxIdx)
bestSelected--;
}
}
if (current[idx] != null && idx < maxIdx && current[idx].isCourseRequest()) {
Set<Section> preferred = getPreferredSections(current[idx].getRequest());
if (preferred != null && !preferred.isEmpty()) {
for (Section section : current[idx].getSections()) if (preferred.contains(section))
currentSelected++;
}
}
}
if (currentSelected > bestSelected)
return true;
if (bestSelected > currentSelected)
return false;
// 3.5 maximize preferences
double bestSelectedConfigs = 0, currentSelectedConfigs = 0;
double bestSelectedSections = 0, currentSelectedSections = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestSelectedSections += best[idx].percentSelectedSameSection();
bestSelectedConfigs += best[idx].percentSelectedSameConfig();
if (idx >= maxIdx) {
bestSelectedSections -= 1.0;
bestSelectedConfigs -= 1.0;
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentSelectedSections += current[idx].percentSelectedSameSection();
currentSelectedConfigs += current[idx].percentSelectedSameConfig();
}
}
if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections)
return true;
if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections)
return false;
// 3.9 minimize enrollments where the reservation is not followed
if (currentNotFollowedReservations < bestNotFollowedReservations)
return true;
if (bestNotFollowedReservations < currentNotFollowedReservations)
return false;
// 3.95 avoid past sections
int bestPast = 0, currentPast = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (Section section : best[idx].getSections()) {
if (section.isPast())
bestPast++;
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null) {
for (Section section : current[idx].getSections()) {
if (section.isPast())
currentPast++;
}
}
}
if (currentPast < bestPast)
return true;
if (bestPast < currentPast)
return false;
// 4-5. solution quality
if (getModel().getStudentQuality() != null) {
double bestQuality = 0, currentQuality = 0;
for (StudentQuality.Type type : StudentQuality.Type.values()) {
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
bestQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, best[idx]);
for (int x = 0; x < idx; x++) {
if (best[x] != null)
bestQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx) {
currentQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, current[idx]);
for (int x = 0; x < idx; x++) {
if (current[x] != null)
currentQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, current[x], current[idx]);
}
}
}
}
if (currentQuality < bestQuality)
return true;
if (bestQuality < currentQuality)
return false;
} else {
// 4. avoid time overlaps
if (getModel().getTimeOverlaps() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (int x = 0; x < idx; x++) {
if (best[x] != null)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]);
else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), best[idx]);
}
}
if (current[idx] != null && idx < maxIdx) {
for (int x = 0; x < idx; x++) {
if (current[x] != null)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]);
else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]);
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return true;
if (bestTimeOverlaps < currentTimeOverlaps)
return false;
}
// 5. avoid distance conflicts
if (getModel().getDistanceConflict() != null) {
int bestDistanceConf = 0, currentDistanceConf = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[idx]);
for (int x = 0; x < idx; x++) {
if (best[x] != null)
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx) {
currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[idx]);
for (int x = 0; x < idx; x++) {
if (current[x] != null)
currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], current[idx]);
}
}
}
if (currentDistanceConf < bestDistanceConf)
return true;
if (bestDistanceConf < currentDistanceConf)
return false;
}
}
// 6. avoid no-time and online sections (no-time first, online second)
int bestNoTime = 0, currentNoTime = 0;
int bestOnline = 0, currentOnline = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) {
if (!section.hasTime())
bestNoTime++;
if (section.isOnline())
bestOnline++;
}
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) {
if (!section.hasTime())
currentNoTime++;
if (section.isOnline())
currentOnline++;
}
}
}
if (currentNoTime < bestNoTime)
return true;
if (bestNoTime < currentNoTime)
return false;
if (currentOnline < bestOnline)
return true;
if (bestOnline < currentOnline)
return false;
// 7. balance sections
double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0;
int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) {
Subpart subpart = section.getSubpart();
// skip unlimited and single section subparts
if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0)
continue;
// average size
double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size();
// section is below average
if (section.getLimit() < averageSize)
bestUnavailableSize += (averageSize - section.getLimit()) / averageSize;
bestAltSectionsWithLimit++;
}
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) {
Subpart subpart = section.getSubpart();
// skip unlimited and single section subparts
if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0)
continue;
// average size
double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size();
// section is below average
if (section.getLimit() < averageSize)
currentUnavailableSize += (averageSize - section.getLimit()) / averageSize;
currentAltSectionsWithLimit++;
}
}
}
double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit : 0.0);
double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize / currentAltSectionsWithLimit : 0.0);
if (currentUnavailableSizeFraction < bestUnavailableSizeFraction)
return true;
if (bestUnavailableSizeFraction < currentUnavailableSizeFraction)
return false;
// 8. average penalty sections
double bestPenalty = 0.0, currentPenalty = 0.0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) bestPenalty += section.getPenalty() / best[idx].getSections().size();
if (idx >= maxIdx && best[idx].isCourseRequest())
bestPenalty -= ((CourseRequest) best[idx].getRequest()).getMinPenalty();
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) currentPenalty += section.getPenalty() / current[idx].getSections().size();
}
}
if (currentPenalty < bestPenalty)
return true;
if (bestPenalty < currentPenalty)
return false;
return true;
}
use of org.cpsolver.studentsct.model.Subpart in project cpsolver by UniTime.
the class TableauReport method create.
@Override
public CSVFile create(Assignment<Request, Enrollment> assignment, DataProperties properties) {
CSVFile csv = new CSVFile();
boolean simple = properties.getPropertyBoolean("simple", false);
if (simple) {
csv.setHeader(new CSVFile.CSVField[] { new CSVFile.CSVField("__Student"), new CSVFile.CSVField("Student"), new CSVFile.CSVField("Course"), new CSVFile.CSVField("Course Limit"), new CSVFile.CSVField("Primary"), new CSVFile.CSVField("Priority"), new CSVFile.CSVField("Alternativity"), new CSVFile.CSVField("Enrolled"), new CSVFile.CSVField("Request Type") });
} else {
csv.setHeader(new CSVFile.CSVField[] { new CSVFile.CSVField("__Student"), new CSVFile.CSVField("Student"), new CSVFile.CSVField("Course"), new CSVFile.CSVField("Course Limit"), new CSVFile.CSVField("Controlling Course"), new CSVFile.CSVField("Primary"), new CSVFile.CSVField("Priority"), new CSVFile.CSVField("Alternativity"), new CSVFile.CSVField("Enrolled"), new CSVFile.CSVField("Credits"), new CSVFile.CSVField("Sections"), new CSVFile.CSVField("Preferred Sections"), new CSVFile.CSVField("Required Sections"), new CSVFile.CSVField("Instructional Method"), new CSVFile.CSVField("Preferred Instructional Methods"), new CSVFile.CSVField("Required Instructional Methods"), new CSVFile.CSVField("Request Type") });
}
for (Student student : getModel().getStudents()) {
if (student.isDummy())
continue;
int regPriority = 1, altPriority = 1;
for (Request r : student.getRequests()) {
if (r instanceof CourseRequest) {
CourseRequest cr = (CourseRequest) r;
Enrollment e = cr.getAssignment(assignment);
int primary = (cr.isAlternative() ? 0 : 1);
int priority = 0;
if (cr.isAlternative())
priority = altPriority++;
else
priority = regPriority++;
int alternativity = 0;
for (Course course : cr.getCourses()) {
int enrolled = (e != null && e.getCourse().equals(course) ? 1 : 0);
String sect = null;
if (e != null && e.getCourse().equals(course)) {
sect = "";
Set<String> added = new HashSet<String>();
for (Section s : e.getSections()) {
String x = s.getName(e.getCourse().getId());
if (x.indexOf('-') > 0)
x = x.substring(0, x.indexOf('-'));
if (added.add(x))
sect += (sect.isEmpty() ? "" : ",") + x;
}
}
String imR = "", sctR = "";
Set<String> addedR = new HashSet<String>();
for (Choice ch : cr.getRequiredChoices()) {
if (course.getOffering().equals(ch.getOffering())) {
if (ch.getConfigId() != null) {
for (Config cfg : ch.getOffering().getConfigs()) {
if (ch.getConfigId().equals(cfg.getId())) {
String im = cfg.getInstructionalMethodReference();
if (im != null && addedR.add(im))
imR += (imR.isEmpty() ? "" : ",") + im;
}
}
}
if (ch.getSectionId() != null) {
String x = ch.getOffering().getSection(ch.getSectionId()).getName(course.getId());
if (x.indexOf('-') > 0)
x = x.substring(0, x.indexOf('-'));
if (addedR.add(x))
sctR += (sctR.isEmpty() ? "" : ",") + x;
}
}
}
for (Reservation rs : cr.getReservations(course)) {
if (rs.mustBeUsed()) {
for (Map.Entry<Subpart, Set<Section>> ent : rs.getSections().entrySet()) {
for (Section s : ent.getValue()) {
String x = s.getName(course.getId());
if (x.indexOf('-') > 0)
x = x.substring(0, x.indexOf('-'));
if (addedR.add(x))
sctR += (sctR.isEmpty() ? "" : ",") + x;
}
}
if (rs.getSections().isEmpty()) {
for (Config cfg : rs.getConfigs()) {
String im = cfg.getInstructionalMethodReference();
if (im != null && addedR.add(im))
imR += (imR.isEmpty() ? "" : ",") + im;
}
}
}
}
String imP = "", sctP = "";
for (Choice ch : cr.getSelectedChoices()) {
Set<String> added = new HashSet<String>();
if (course.getOffering().equals(ch.getOffering())) {
if (ch.getConfigId() != null) {
for (Config cfg : ch.getOffering().getConfigs()) {
if (ch.getConfigId().equals(cfg.getId())) {
String im = cfg.getInstructionalMethodReference();
if (im != null && added.add(im))
imP += (imP.isEmpty() ? "" : ",") + im;
}
}
}
if (ch.getSectionId() != null) {
String x = ch.getOffering().getSection(ch.getSectionId()).getName(course.getId());
if (x.indexOf('-') > 0)
x = x.substring(0, x.indexOf('-'));
if (added.add(x))
sctP += (sctP.isEmpty() ? "" : ",") + x;
}
}
}
if (simple)
csv.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(student.getId()), new CSVFile.CSVField(student.getExternalId()), new CSVFile.CSVField(course.getName()), new CSVFile.CSVField(course.getLimit() < 0 ? null : course.getLimit()), new CSVFile.CSVField(primary == 1 ? "Yes" : "No"), new CSVFile.CSVField(priority), new CSVFile.CSVField(alternativity), new CSVFile.CSVField(enrolled == 1 ? "Yes" : "No"), new CSVFile.CSVField(cr.getRequestPriority() == null ? "" : cr.getRequestPriority().name()) });
else
csv.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(student.getId()), new CSVFile.CSVField(student.getExternalId()), new CSVFile.CSVField(course.getName()), new CSVFile.CSVField(course.getLimit() < 0 ? null : course.getLimit()), new CSVFile.CSVField(course.getOffering().getCourses().size() <= 1 ? null : course.getOffering().getName()), new CSVFile.CSVField(primary == 1 ? "Yes" : "No"), new CSVFile.CSVField(priority), new CSVFile.CSVField(alternativity), new CSVFile.CSVField(enrolled == 1 ? "Yes" : "No"), new CSVFile.CSVField(enrolled == 1 ? e.getCredit() : course.getCreditValue() == null ? 0f : course.getCreditValue()), new CSVFile.CSVField(sect), new CSVFile.CSVField(sctP), new CSVFile.CSVField(sctR), new CSVFile.CSVField(e != null ? e.getConfig().getInstructionalMethodReference() : null), new CSVFile.CSVField(imP), new CSVFile.CSVField(imR), new CSVFile.CSVField(cr.getRequestPriority() == null ? "" : cr.getRequestPriority().name()) });
alternativity++;
}
}
}
}
return csv;
}
use of org.cpsolver.studentsct.model.Subpart 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.Subpart in project cpsolver by UniTime.
the class PriorityStudentWeights method getWeight.
@Override
public double getWeight(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
double weight = getCachedWeight(enrollment.getRequest());
switch(enrollment.getPriority()) {
case 1:
weight *= iFirstAlternativeFactor;
break;
case 2:
weight *= iSecondAlternativeFactor;
break;
}
if (enrollment.isCourseRequest() && iBalancingFactor != 0.0) {
double configUsed = enrollment.getConfig().getEnrollmentTotalWeight(assignment, enrollment.getRequest()) + enrollment.getRequest().getWeight();
double disbalanced = 0;
double total = 0;
for (Section section : enrollment.getSections()) {
Subpart subpart = section.getSubpart();
if (subpart.getSections().size() <= 1)
continue;
double used = section.getEnrollmentTotalWeight(assignment, enrollment.getRequest()) + enrollment.getRequest().getWeight();
// sections have limits -> desired size is section limit x (total enrollment / total limit)
// unlimited sections -> desired size is total enrollment / number of sections
double desired = (subpart.getLimit() > 0 ? section.getLimit() * (configUsed / subpart.getLimit()) : configUsed / subpart.getSections().size());
if (used > desired)
disbalanced += Math.min(enrollment.getRequest().getWeight(), used - desired) / enrollment.getRequest().getWeight();
else
disbalanced -= Math.min(enrollment.getRequest().getWeight(), desired - used) / enrollment.getRequest().getWeight();
total++;
}
if (disbalanced > 0)
weight *= (1.0 - disbalanced / total * iBalancingFactor);
}
if (iMPP) {
double difference = getDifference(enrollment);
if (difference > 0.0)
weight *= (1.0 - difference * iPerturbationFactor);
}
if (iSelectionFactor != 0.0) {
double selection = getSelection(enrollment);
if (selection > 0.0)
weight *= (1.0 - selection * iSelectionFactor);
}
if (enrollment.isCourseRequest() && iGroupFactor != 0.0) {
double sameGroup = 0.0;
int groupCount = 0;
for (RequestGroup g : ((CourseRequest) enrollment.getRequest()).getRequestGroups()) {
if (g.getCourse().equals(enrollment.getCourse())) {
sameGroup += g.getEnrollmentSpread(assignment, enrollment, iGroupBestRatio, iGroupFillRatio);
groupCount++;
}
}
if (groupCount > 0) {
double difference = 1.0 - sameGroup / groupCount;
weight *= (1.0 - difference * iGroupFactor);
}
}
return round(weight);
}
use of org.cpsolver.studentsct.model.Subpart 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