Example #1
0
  public XSubpart(SchedulingSubpart subpart, boolean courseCredit, OnlineSectioningHelper helper) {
    iUniqueId = subpart.getUniqueId();
    iInstructionalType =
        sF3Z.format(subpart.getItype().getItype())
            + subpart.getSchedulingSubpartSuffix(helper.getHibSession());
    iAllowOverlap = subpart.isStudentAllowOverlap();
    iName = subpart.getItype().getAbbv().trim();
    iConfigId = subpart.getInstrOfferingConfig().getUniqueId();
    iParentId =
        subpart.getParentSubpart() == null ? null : subpart.getParentSubpart().getUniqueId();
    if (subpart.getCredit() != null) {
      iCreditAbbv = subpart.getCredit().creditAbbv();
      iCreditText = subpart.getCredit().creditText();
    }
    if (courseCredit) {
      for (CourseOffering co :
          subpart.getInstrOfferingConfig().getInstructionalOffering().getCourseOfferings()) {
        if (co.getCredit() != null)
          iCreditByCourse.put(
              co.getUniqueId(),
              new String[] {co.getCredit().creditAbbv(), co.getCredit().creditText()});
      }
    }
    for (Class_ clazz : subpart.getClasses()) iSections.add(new XSection(clazz, helper));

    Collections.sort(iSections);
  }
 @Override
 public Set<WeightedStudentId> getDemands(CourseOffering course) {
   Hashtable<Long, Set<WeightedStudentId>> demands =
       iDemands.get(course.getSubjectArea().getUniqueId());
   if (demands == null) {
     demands = loadDemandsForSubjectArea(course.getSubjectArea());
     iDemands.put(course.getSubjectArea().getUniqueId(), demands);
   }
   return demands.get(course.getUniqueId());
 }
  public int compare(Object o1, Object o2) {
    // Check if objects are of class Instructional Offering
    if (!(o1 instanceof CourseOffering)) {
      throw new ClassCastException("o1 Class must be of type CourseOffering");
    }
    if (!(o2 instanceof CourseOffering)) {
      throw new ClassCastException("o2 Class must be of type CourseOffering");
    }

    CourseOffering co1 = (CourseOffering) o1;
    CourseOffering co2 = (CourseOffering) o2;

    // Same Course Offering
    if (co1.getUniqueId().equals(co2.getUniqueId())) {
      return 0;
    }

    // One of the offerings is a Controlling Course
    if (compareBy == COMPARE_BY_CTRL_CRS) {
      if (co1.isIsControl().booleanValue()) return -1;
      if (co2.isIsControl().booleanValue()) return 1;
    }

    // Compare by course name (also used if neither is controlling)
    if (co1.getSubjectAreaAbbv().equals(co2.getSubjectAreaAbbv())) {
      if (co1.getCourseNbr().equals(co2.getCourseNbr())) {
        if (co1.getTitle() == null && co2.getTitle() == null) {
          return (0);
        } else if (co1.getTitle() == null) {
          return (-1);
        } else if (co2.getTitle() == null) {
          return (1);
        }
        return (co1.getTitle().compareTo(co2.getTitle()));
      } else {
        return (co1.getCourseNbr().compareTo(co2.getCourseNbr()));
      }
    } else {
      return (co1.getSubjectAreaAbbv().compareTo(co2.getSubjectAreaAbbv()));
    }
  }
 @Override
 public Set<WeightedCourseOffering> getCourses(Long studentId) {
   if (iStudentRequests == null) {
     iStudentRequests = new Hashtable<Long, Set<WeightedCourseOffering>>();
     for (Object[] o :
         (List<Object[]>)
             iHibSession
                 .createQuery(
                     "select distinct d.student.uniqueId, c, d.priority, d.alternative, r.order "
                         + "from CourseRequest r inner join r.courseOffering c inner join r.courseDemand d where d.student.session.uniqueId = :sessionId")
                 .setLong("sessionId", iSessionId)
                 .setCacheable(true)
                 .list()) {
       Long sid = (Long) o[0];
       CourseOffering co = (CourseOffering) o[1];
       Integer priority = (Integer) o[2];
       Boolean alternative = (Boolean) o[3];
       Integer order = (Integer) o[4];
       Set<WeightedCourseOffering> courses = iStudentRequests.get(sid);
       if (courses == null) {
         courses = new HashSet<WeightedCourseOffering>();
         iStudentRequests.put(sid, courses);
       }
       courses.add(new WeightedCourseOffering(co));
       if (priority != null && Boolean.FALSE.equals(alternative)) {
         if (order != null) priority += order;
         Hashtable<Long, Double> priorities = iEnrollmentPriorities.get(studentId);
         if (priorities == null) {
           priorities = new Hashtable<Long, Double>();
           iEnrollmentPriorities.put(studentId, priorities);
         }
         priorities.put(co.getUniqueId(), Math.pow(iBasePriorityWeight, priority));
       }
     }
   }
   return iStudentRequests.get(studentId);
 }
Example #5
0
  public void reloadOffering(
      final OnlineSectioningServer server, OnlineSectioningHelper helper, Long offeringId) {
    // Load new students
    Map<Long, org.unitime.timetable.model.Student> newStudents =
        new HashMap<Long, org.unitime.timetable.model.Student>();
    /*
    for (org.unitime.timetable.model.Student student : (List<org.unitime.timetable.model.Student>)helper.getHibSession().createQuery(
                  "select s from Student s " +
                  "left join fetch s.courseDemands as cd " +
                  "left join fetch cd.courseRequests as cr " +
                  "left join fetch cr.courseOffering as co " +
                  "left join fetch cr.classWaitLists as cwl " +
                  "left join fetch s.classEnrollments as e " +
                  "left join fetch s.academicAreaClassifications as a " +
                  "left join fetch s.posMajors as mj " +
                  "left join fetch s.waitlists as w " +
                  "left join fetch s.groups as g " +
                  "where s.uniqueId in (select xe.student.uniqueId from StudentClassEnrollment xe where xe.courseOffering.instructionalOffering.uniqueId = :offeringId) " +
                  "or s.uniqueId in (select xr.courseDemand.student.uniqueId from CourseRequest xr where xr.courseOffering.instructionalOffering.uniqueId = :offeringId)"
                  ).setLong("offeringId", offeringId).list()) {
    	newStudents.put(student.getUniqueId(), student);
    }
    */
    for (org.unitime.timetable.model.Student student :
        (List<org.unitime.timetable.model.Student>)
            helper
                .getHibSession()
                .createQuery(
                    "select distinct s from Student s "
                        + "left join s.courseDemands as cd "
                        + "left join cd.courseRequests as cr "
                        + "left join fetch s.classEnrollments as e "
                        + "left join fetch s.academicAreaClassifications as a "
                        + "left join fetch s.posMajors as mj "
                        + "left join fetch s.waitlists as w "
                        + "left join fetch s.groups as g "
                        + "where cr.courseOffering.instructionalOffering.uniqueId = :offeringId")
                .setLong("offeringId", offeringId)
                .list()) {
      newStudents.put(student.getUniqueId(), student);
    }
    for (org.unitime.timetable.model.Student student :
        (List<org.unitime.timetable.model.Student>)
            helper
                .getHibSession()
                .createQuery(
                    "select distinct s from Student s "
                        + "left join fetch s.courseDemands as cd "
                        + "left join fetch cd.courseRequests as cr "
                        + "left join fetch cr.courseOffering as co "
                        + "left join fetch cr.classWaitLists as cwl "
                        + "left join fetch s.classEnrollments as e "
                        + "left join fetch s.academicAreaClassifications as a "
                        + "left join fetch s.posMajors as mj "
                        + "left join fetch s.waitlists as w "
                        + "left join fetch s.groups as g "
                        + "where e.courseOffering.instructionalOffering.uniqueId = :offeringId and e.courseRequest is null")
                .setLong("offeringId", offeringId)
                .list()) {
      newStudents.put(student.getUniqueId(), student);
    }

    // Persist expected spaces if needed
    if (server.needPersistExpectedSpaces(offeringId))
      PersistExpectedSpacesAction.persistExpectedSpaces(offeringId, false, server, helper);

    // Existing offering
    XOffering oldOffering = server.getOffering(offeringId);
    XEnrollments oldEnrollments = server.getEnrollments(offeringId);

    // New offering
    XOffering newOffering = null;
    InstructionalOffering io =
        InstructionalOfferingDAO.getInstance().get(offeringId, helper.getHibSession());
    List<XDistribution> distributions = new ArrayList<XDistribution>();
    if (io != null) {
      // Load linked sections and ignore student conflict constraints
      List<DistributionPref> distPrefs =
          helper
              .getHibSession()
              .createQuery(
                  "select distinct p from DistributionPref p inner join p.distributionObjects o, Department d, "
                      + "Class_ c inner join c.schedulingSubpart.instrOfferingConfig.instructionalOffering io "
                      + "where p.distributionType.reference in (:ref1, :ref2) and d.session.uniqueId = :sessionId "
                      + "and io.uniqueId = :offeringId and (o.prefGroup = c or o.prefGroup = c.schedulingSubpart) "
                      + "and p.owner = d and p.prefLevel.prefProlog = :pref")
              .setString("ref1", GroupConstraint.ConstraintType.LINKED_SECTIONS.reference())
              .setString("ref2", IgnoreStudentConflictsConstraint.REFERENCE)
              .setString("pref", PreferenceLevel.sRequired)
              .setLong("sessionId", server.getAcademicSession().getUniqueId())
              .setLong("offeringId", offeringId)
              .list();
      if (!distPrefs.isEmpty()) {
        for (DistributionPref pref : distPrefs) {
          int variant = 0;
          for (Collection<Class_> sections : ReloadAllData.getSections(pref)) {
            XDistributionType type = XDistributionType.IngoreConflicts;
            if (GroupConstraint.ConstraintType.LINKED_SECTIONS
                .reference()
                .equals(pref.getDistributionType().getReference()))
              type = XDistributionType.LinkedSections;
            XDistribution distribution =
                new XDistribution(type, pref.getUniqueId(), variant++, sections);
            distributions.add(distribution);
          }
        }
      }

      newOffering = ReloadAllData.loadOffering(io, distributions, server, helper);
      if (newOffering != null) server.update(newOffering);
      else if (oldOffering != null) server.remove(oldOffering);

      // Load sectioning info
      List<Object[]> infos =
          helper
              .getHibSession()
              .createQuery(
                  "select i.clazz.uniqueId, i.nbrExpectedStudents from SectioningInfo i where i.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.uniqueId = :offeringId")
              .setLong("offeringId", offeringId)
              .list();
      XExpectations expectations = new XExpectations(offeringId);
      for (Object[] info : infos) {
        Long sectionId = (Long) info[0];
        Double expected = (Double) info[1];
        expectations.setExpectedSpace(sectionId, expected);
      }
      server.update(expectations);
    } else if (oldOffering != null) {
      server.remove(oldOffering);
    }

    List<XStudent[]> students = new ArrayList<XStudent[]>();

    if (oldEnrollments != null) {
      Set<Long> checked = new HashSet<Long>();
      for (XRequest old : oldEnrollments.getRequests()) {
        if (!checked.add(old.getStudentId())) continue;
        XStudent oldStudent = server.getStudent(old.getStudentId());
        org.unitime.timetable.model.Student student = newStudents.get(oldStudent.getStudentId());
        if (student == null)
          student = StudentDAO.getInstance().get(oldStudent.getStudentId(), helper.getHibSession());
        XStudent newStudent =
            (student == null ? null : ReloadAllData.loadStudent(student, null, server, helper));
        if (newStudent != null) server.update(newStudent, true);
        else server.remove(oldStudent);
        students.add(new XStudent[] {oldStudent, newStudent});
        newStudents.remove(oldStudent.getStudentId());
      }
    }
    for (org.unitime.timetable.model.Student student : newStudents.values()) {
      XStudent oldStudent = server.getStudent(student.getUniqueId());
      XStudent newStudent = ReloadAllData.loadStudent(student, null, server, helper);
      if (newStudent != null) server.update(newStudent, true);
      else if (oldStudent != null) server.remove(oldStudent);
      students.add(new XStudent[] {oldStudent, newStudent});
    }

    if (!server.getAcademicSession().isSectioningEnabled()) return;

    if (!CustomStudentEnrollmentHolder.isAllowWaitListing()) return;

    if (newOffering == null && oldOffering == null) return;

    Set<SectioningRequest> queue = new TreeSet<SectioningRequest>();

    Set<XCourseId> courseIds = new HashSet<XCourseId>();
    if (newOffering != null) courseIds.addAll(newOffering.getCourses());
    if (oldOffering != null) courseIds.addAll(oldOffering.getCourses());

    for (XCourseId course : courseIds) {
      for (XStudent[] student : students) {
        if (student[0] == null && student[1] == null) continue;
        XEnrollment oldEnrollment = null;
        XCourseRequest oldRequest = null;
        if (student[0] != null) {
          for (XRequest r : student[0].getRequests())
            if (r instanceof XCourseRequest) {
              XCourseRequest cr = (XCourseRequest) r;
              for (XCourseId c : cr.getCourseIds()) {
                if (c.equals(course)) {
                  oldRequest = cr;
                  if (cr.getEnrollment() != null
                      && offeringId.equals(cr.getEnrollment().getOfferingId()))
                    oldEnrollment = cr.getEnrollment();
                  break;
                }
              }
            }
        }
        XCourseRequest newRequest = null;
        XEnrollment newEnrollment = null;
        if (student[1] != null) {
          for (XRequest r : student[1].getRequests())
            if (r instanceof XCourseRequest) {
              XCourseRequest cr = (XCourseRequest) r;
              for (XCourseId c : cr.getCourseIds())
                if (c.equals(course)) {
                  newRequest = cr;
                  if (cr.getEnrollment() != null
                      && offeringId.equals(cr.getEnrollment().getOfferingId()))
                    newEnrollment = cr.getEnrollment();
                  break;
                }
            }
        }
        if (oldRequest == null && newRequest == null) continue;

        OnlineSectioningLog.Action.Builder action =
            helper.addAction(this, server.getAcademicSession());
        action.setStudent(
            OnlineSectioningLog.Entity.newBuilder()
                .setUniqueId(
                    student[0] == null ? student[1].getStudentId() : student[0].getStudentId())
                .setExternalId(
                    student[0] == null ? student[1].getExternalId() : student[0].getExternalId()));
        action.addOther(
            OnlineSectioningLog.Entity.newBuilder()
                .setUniqueId(offeringId)
                .setName(newOffering == null ? oldOffering.getName() : newOffering.getName())
                .setType(OnlineSectioningLog.Entity.EntityType.OFFERING));
        action.addOther(
            OnlineSectioningLog.Entity.newBuilder()
                .setUniqueId(course.getCourseId())
                .setName(course.getCourseName())
                .setType(OnlineSectioningLog.Entity.EntityType.COURSE));

        if (oldEnrollment != null) {
          OnlineSectioningLog.Enrollment.Builder enrollment =
              OnlineSectioningLog.Enrollment.newBuilder();
          enrollment.setType(OnlineSectioningLog.Enrollment.EnrollmentType.PREVIOUS);
          for (Long sectionId : oldEnrollment.getSectionIds())
            enrollment.addSection(
                OnlineSectioningHelper.toProto(oldOffering.getSection(sectionId), oldEnrollment));
          action.addEnrollment(enrollment);
          if (newRequest == null) action.addRequest(OnlineSectioningHelper.toProto(oldRequest));
        }

        if (newRequest == null) {
          // nothing to re-assign
          if (oldEnrollment != null && CustomStudentEnrollmentHolder.hasProvider()) {
            // there was a drop
            try {
              CustomStudentEnrollmentHolder.getProvider()
                  .resection(
                      server,
                      helper,
                      new SectioningRequest(newOffering, newRequest, student[1], action)
                          .setOldOffering(oldOffering)
                          .setOldStudent(student[0])
                          .setOldRequest(oldRequest)
                          .setLastEnrollment(oldEnrollment),
                      null);
            } catch (Exception ex) {
              action.setResult(OnlineSectioningLog.Action.ResultType.FAILURE);
              action.addMessage(
                  OnlineSectioningLog.Message.newBuilder()
                      .setLevel(OnlineSectioningLog.Message.Level.FATAL)
                      .setText(ex.getMessage() == null ? "null" : ex.getMessage()));
              helper.error("Unable to resection student: " + ex.getMessage(), ex);
            }
          }
          action.setEndTime(System.currentTimeMillis());
          server.execute(
              server
                  .createAction(NotifyStudentAction.class)
                  .forStudent(
                      student[0] == null ? student[1].getStudentId() : student[0].getStudentId())
                  .oldEnrollment(oldOffering, course, oldEnrollment),
              helper.getUser());
          continue;
        } else {
          action.addRequest(OnlineSectioningHelper.toProto(newRequest));
        }

        if (oldEnrollment == null && newEnrollment == null) {
          if (student[1].canAssign(newRequest)
              && isWaitListed(student[1], newRequest, server, helper))
            queue.add(
                new SectioningRequest(newOffering, newRequest, student[1], action)
                    .setOldOffering(oldOffering)
                    .setOldRequest(oldRequest)
                    .setOldStudent(student[0]));
          continue;
        }

        if (newEnrollment != null) {
          // new enrollment is valid and / or has all the same times
          if (check(
              newOffering,
              course,
              distributions,
              student[1],
              newEnrollment,
              server)) { // || isSame(oldEnrollment, newEnrollment)) {
            OnlineSectioningLog.Enrollment.Builder enrollment =
                OnlineSectioningLog.Enrollment.newBuilder();
            enrollment.setType(OnlineSectioningLog.Enrollment.EnrollmentType.STORED);
            for (XSection assignment : newOffering.getSections(newEnrollment))
              enrollment.addSection(OnlineSectioningHelper.toProto(assignment, newEnrollment));
            action.addEnrollment(enrollment);

            // there may have been a change
            if (CustomStudentEnrollmentHolder.hasProvider()) {
              try {
                CustomStudentEnrollmentHolder.getProvider()
                    .resection(
                        server,
                        helper,
                        new SectioningRequest(newOffering, newRequest, student[1], action)
                            .setOldOffering(oldOffering)
                            .setOldStudent(student[0])
                            .setOldRequest(oldRequest)
                            .setLastEnrollment(oldEnrollment),
                        newEnrollment);
              } catch (Exception ex) {
                action.setResult(OnlineSectioningLog.Action.ResultType.FAILURE);
                action.addMessage(
                    OnlineSectioningLog.Message.newBuilder()
                        .setLevel(OnlineSectioningLog.Message.Level.FATAL)
                        .setText(ex.getMessage() == null ? "null" : ex.getMessage()));
                helper.error("Unable to resection student: " + ex.getMessage(), ex);
              }
            }
            action.setEndTime(System.currentTimeMillis());

            if (!isVerySame(
                newEnrollment.getCourseId(),
                newOffering.getSections(newEnrollment),
                oldOffering.getSections(oldEnrollment)))
              server.execute(
                  server
                      .createAction(NotifyStudentAction.class)
                      .forStudent(
                          student[0] == null
                              ? student[1].getStudentId()
                              : student[0].getStudentId())
                      .oldEnrollment(oldOffering, course, oldEnrollment),
                  helper.getUser());
            continue;
          }
        }
        newRequest = server.assign(newRequest, null);
        queue.add(
            new SectioningRequest(newOffering, newRequest, student[1], action)
                .setOldOffering(oldOffering)
                .setOldRequest(oldRequest)
                .setOldStudent(student[0])
                .setLastEnrollment(oldEnrollment)
                .setNewEnrollment(newEnrollment));
      }
    }

    if (!queue.isEmpty()) {
      DataProperties properties = new DataProperties();
      ResectioningWeights w = new ResectioningWeights(properties);
      DistanceConflict dc = new DistanceConflict(server.getDistanceMetric(), properties);
      TimeOverlapsCounter toc = new TimeOverlapsCounter(null, properties);
      Date ts = new Date();
      for (SectioningRequest r : queue) {
        helper.debug(
            "Resectioning "
                + r.getRequest()
                + " (was "
                + (r.getLastEnrollment() == null
                    ? "not assigned"
                    : r.getLastEnrollment().getSectionIds())
                + ")");
        long c0 = OnlineSectioningHelper.getCpuTime();
        XEnrollment e = r.resection(server, w, dc, toc);

        if (e != null) {
          e.setTimeStamp(ts);
          OnlineSectioningLog.Enrollment.Builder enrollment =
              OnlineSectioningLog.Enrollment.newBuilder();
          enrollment.setType(OnlineSectioningLog.Enrollment.EnrollmentType.STORED);
          for (Long sectionId : e.getSectionIds())
            enrollment.addSection(
                OnlineSectioningHelper.toProto(newOffering.getSection(sectionId), e));
          r.getAction().addEnrollment(enrollment);
        }

        if (CustomStudentEnrollmentHolder.hasProvider()) {
          try {
            e = CustomStudentEnrollmentHolder.getProvider().resection(server, helper, r, e);
          } catch (Exception ex) {
            r.getAction().setResult(OnlineSectioningLog.Action.ResultType.FAILURE);
            r.getAction()
                .addMessage(
                    OnlineSectioningLog.Message.newBuilder()
                        .setLevel(OnlineSectioningLog.Message.Level.FATAL)
                        .setText(ex.getMessage() == null ? "null" : ex.getMessage()));
            helper.error("Unable to resection student: " + ex.getMessage(), ex);
            if (r.getNewEnrollment() != null) server.assign(r.getRequest(), r.getNewEnrollment());
            continue;
          }
        }

        if (e == null && r.getLastEnrollment() == null) {
          // remained unassigned
          continue;
        }

        if (e != null) {
          r.setRequest(server.assign(r.getRequest(), e));
        }
        helper.debug("New: " + (e == null ? "not assigned" : e.getSectionIds()));

        org.unitime.timetable.model.Student student =
            StudentDAO.getInstance().get(r.getRequest().getStudentId(), helper.getHibSession());
        Map<Long, StudentClassEnrollment> enrollmentMap =
            new HashMap<Long, StudentClassEnrollment>();
        String approvedBy = null;
        Date approvedDate = null;
        for (Iterator<StudentClassEnrollment> i = student.getClassEnrollments().iterator();
            i.hasNext(); ) {
          StudentClassEnrollment enrl = i.next();
          if ((enrl.getCourseRequest() != null
                  && enrl.getCourseRequest()
                      .getCourseDemand()
                      .getUniqueId()
                      .equals(r.getRequest().getRequestId()))
              || (enrl.getCourseOffering() != null
                  && enrl.getCourseOffering()
                      .getUniqueId()
                      .equals(r.getCourseId().getCourseId()))) {
            helper.debug("Deleting " + enrl.getClazz().getClassLabel());
            enrollmentMap.put(enrl.getClazz().getUniqueId(), enrl);
            if (approvedBy == null && enrl.getApprovedBy() != null) {
              approvedBy = enrl.getApprovedBy();
              approvedDate = enrl.getApprovedDate();
            }
            enrl.getClazz().getStudentEnrollments().remove(enrl);
            helper.getHibSession().delete(enrl);
            i.remove();
          }
        }
        CourseDemand cd = null;
        demands:
        for (CourseDemand x : student.getCourseDemands())
          for (org.unitime.timetable.model.CourseRequest q : x.getCourseRequests())
            if (q.getCourseOffering().getInstructionalOffering().getUniqueId().equals(offeringId)) {
              cd = x;
              break demands;
            }

        if (r.getRequest().getEnrollment() != null) { // save enrollment
          org.unitime.timetable.model.CourseRequest cr = null;
          CourseOffering co = null;
          if (co == null)
            for (CourseOffering x : io.getCourseOfferings())
              if (x.getUniqueId().equals(r.getRequest().getEnrollment().getCourseId())) co = x;
          for (Long sectionId : r.getRequest().getEnrollment().getSectionIds()) {
            Class_ clazz = Class_DAO.getInstance().get(sectionId, helper.getHibSession());
            if (cd != null && cr == null) {
              for (org.unitime.timetable.model.CourseRequest x : cd.getCourseRequests())
                if (x.getCourseOffering()
                    .getInstructionalOffering()
                    .getUniqueId()
                    .equals(offeringId)) {
                  cr = x;
                  break;
                }
            }
            if (co == null)
              co =
                  clazz
                      .getSchedulingSubpart()
                      .getInstrOfferingConfig()
                      .getInstructionalOffering()
                      .getControllingCourseOffering();
            StudentClassEnrollment enrl = new StudentClassEnrollment();
            enrl.setClazz(clazz);
            clazz.getStudentEnrollments().add(enrl);
            enrl.setCourseOffering(co);
            enrl.setCourseRequest(cr);
            StudentClassEnrollment old = enrollmentMap.get(sectionId);
            enrl.setTimestamp(old != null ? old.getTimestamp() : ts);
            enrl.setChangedBy(
                old != null
                    ? old.getChangedBy()
                    : helper.getUser() == null
                        ? StudentClassEnrollment.SystemChange.SYSTEM.toString()
                        : helper.getUser().getExternalId());
            enrl.setStudent(student);
            enrl.setApprovedBy(approvedBy);
            enrl.setApprovedDate(approvedDate);
            student.getClassEnrollments().add(enrl);
            /*
            if (cr != null) {
            	if (cr.getClassEnrollments() == null) cr.setClassEnrollments(new HashSet<StudentClassEnrollment>());
            	cr.getClassEnrollments().add(enrl);
            }
            */
            helper.debug("Adding " + enrl.getClazz().getClassLabel());
          }
        } else if (!r.getRequest().isAlternative()) { // wait-list
          if (cd != null && !cd.isWaitlist()) {
            cd.setWaitlist(true);
            helper.getHibSession().saveOrUpdate(cd);
          }
          if (!r.getRequest().isWaitlist()) r.setRequest(server.waitlist(r.getRequest(), true));
        }

        helper.getHibSession().save(student);

        EnrollStudent.updateSpace(
            server,
            r.getRequest().getEnrollment() == null
                ? null
                : SectioningRequest.convert(
                    r.getStudent(),
                    r.getRequest(),
                    server,
                    newOffering,
                    r.getRequest().getEnrollment()),
            r.getLastEnrollment() == null
                ? null
                : SectioningRequest.convert(
                    r.getOldStudent(),
                    r.getOldRequest(),
                    server,
                    oldOffering,
                    r.getLastEnrollment()),
            newOffering,
            oldOffering);
        server.persistExpectedSpaces(offeringId);

        server.execute(
            server
                .createAction(NotifyStudentAction.class)
                .forStudent(r.getRequest().getStudentId())
                .oldEnrollment(oldOffering, r.getCourseId(), r.getLastEnrollment()),
            helper.getUser());

        r.getAction()
            .setResult(
                e == null
                    ? OnlineSectioningLog.Action.ResultType.NULL
                    : OnlineSectioningLog.Action.ResultType.SUCCESS);
        r.getAction().setCpuTime(OnlineSectioningHelper.getCpuTime() - c0);
        r.getAction().setEndTime(System.currentTimeMillis());
      }
    }
  }