Пример #1
0
  public static GwtRpcResponseList<ClassAssignmentInterface.Enrollment> convert(
      Collection<StudentClassEnrollment> enrollments,
      Map<Long, List<Meeting>> conflicts,
      boolean canShowExtId,
      boolean canRegister,
      boolean canUseAssistant) {
    GwtRpcResponseList<ClassAssignmentInterface.Enrollment> converted =
        new GwtRpcResponseList<ClassAssignmentInterface.Enrollment>();
    Map<String, String> approvedBy2name = new Hashtable<String, String>();
    Hashtable<Long, ClassAssignmentInterface.Enrollment> student2enrollment =
        new Hashtable<Long, ClassAssignmentInterface.Enrollment>();
    for (StudentClassEnrollment enrollment : enrollments) {
      ClassAssignmentInterface.Enrollment enrl =
          student2enrollment.get(enrollment.getStudent().getUniqueId());
      if (enrl == null) {
        ClassAssignmentInterface.Student st = new ClassAssignmentInterface.Student();
        st.setId(enrollment.getStudent().getUniqueId());
        st.setExternalId(enrollment.getStudent().getExternalUniqueId());
        st.setCanShowExternalId(canShowExtId);
        st.setCanRegister(canRegister);
        st.setCanUseAssistant(canUseAssistant);
        st.setName(
            enrollment
                .getStudent()
                .getName(ApplicationProperty.OnlineSchedulingStudentNameFormat.value()));
        for (AcademicAreaClassification ac :
            enrollment.getStudent().getAcademicAreaClassifications()) {
          st.addArea(ac.getAcademicArea().getAcademicAreaAbbreviation());
          st.addClassification(ac.getAcademicClassification().getCode());
        }
        for (PosMajor m : enrollment.getStudent().getPosMajors()) {
          st.addMajor(m.getCode());
        }
        for (StudentGroup g : enrollment.getStudent().getGroups()) {
          st.addGroup(g.getGroupAbbreviation());
        }
        for (StudentAccomodation a : enrollment.getStudent().getAccomodations()) {
          st.addAccommodation(a.getAbbreviation());
        }
        enrl = new ClassAssignmentInterface.Enrollment();
        enrl.setStudent(st);
        enrl.setEnrolledDate(enrollment.getTimestamp());
        CourseAssignment c = new CourseAssignment();
        c.setCourseId(enrollment.getCourseOffering().getUniqueId());
        c.setSubject(enrollment.getCourseOffering().getSubjectAreaAbbv());
        c.setCourseNbr(enrollment.getCourseOffering().getCourseNbr());
        enrl.setCourse(c);
        student2enrollment.put(enrollment.getStudent().getUniqueId(), enrl);
        if (enrollment.getCourseRequest() != null) {
          enrl.setPriority(1 + enrollment.getCourseRequest().getCourseDemand().getPriority());
          if (enrollment.getCourseRequest().getCourseDemand().getCourseRequests().size() > 1) {
            CourseRequest first = null;
            for (CourseRequest r :
                enrollment.getCourseRequest().getCourseDemand().getCourseRequests()) {
              if (first == null || r.getOrder().compareTo(first.getOrder()) < 0) first = r;
            }
            if (!first.equals(enrollment.getCourseRequest()))
              enrl.setAlternative(first.getCourseOffering().getCourseName());
          }
          if (enrollment.getCourseRequest().getCourseDemand().isAlternative()) {
            CourseDemand first = enrollment.getCourseRequest().getCourseDemand();
            demands:
            for (CourseDemand cd : enrollment.getStudent().getCourseDemands()) {
              if (!cd.isAlternative()
                  && cd.getPriority().compareTo(first.getPriority()) < 0
                  && !cd.getCourseRequests().isEmpty()) {
                for (CourseRequest cr : cd.getCourseRequests())
                  if (cr.getClassEnrollments().isEmpty()) continue demands;
                first = cd;
              }
            }
            CourseRequest alt = null;
            for (CourseRequest r : first.getCourseRequests()) {
              if (alt == null || r.getOrder().compareTo(alt.getOrder()) < 0) alt = r;
            }
            enrl.setAlternative(alt.getCourseOffering().getCourseName());
          }
          enrl.setRequestedDate(enrollment.getCourseRequest().getCourseDemand().getTimestamp());
          enrl.setApprovedDate(enrollment.getApprovedDate());
          if (enrollment.getApprovedBy() != null) {
            String name = approvedBy2name.get(enrollment.getApprovedBy());
            if (name == null) {
              TimetableManager mgr =
                  (TimetableManager)
                      EventDAO.getInstance()
                          .getSession()
                          .createQuery("from TimetableManager where externalUniqueId = :externalId")
                          .setString("externalId", enrollment.getApprovedBy())
                          .setMaxResults(1)
                          .uniqueResult();
              if (mgr != null) {
                name = mgr.getName();
              } else {
                DepartmentalInstructor instr =
                    (DepartmentalInstructor)
                        EventDAO.getInstance()
                            .getSession()
                            .createQuery(
                                "from DepartmentalInstructor where externalUniqueId = :externalId and department.session.uniqueId = :sessionId")
                            .setString("externalId", enrollment.getApprovedBy())
                            .setLong(
                                "sessionId", enrollment.getStudent().getSession().getUniqueId())
                            .setMaxResults(1)
                            .uniqueResult();
                if (instr != null) name = instr.nameLastNameFirst();
              }
              if (name != null) approvedBy2name.put(enrollment.getApprovedBy(), name);
            }
            enrl.setApprovedBy(name == null ? enrollment.getApprovedBy() : name);
          }
        } else {
          enrl.setPriority(-1);
        }

        List<Meeting> conf =
            (conflicts == null ? null : conflicts.get(enrollment.getStudent().getUniqueId()));
        if (conf != null) {
          Map<Event, TreeSet<Meeting>> events = new HashMap<Event, TreeSet<Meeting>>();
          for (Meeting m : conf) {
            TreeSet<Meeting> ms = events.get(m.getEvent());
            if (ms == null) {
              ms = new TreeSet<Meeting>();
              events.put(m.getEvent(), ms);
            }
            ms.add(m);
          }
          for (Event confEvent : new TreeSet<Event>(events.keySet())) {
            Conflict conflict = new Conflict();
            conflict.setName(confEvent.getEventName());
            conflict.setType(confEvent.getEventTypeAbbv());
            String lastDate = null, lastTime = null, lastRoom = null;
            for (MultiMeeting mm : Event.getMultiMeetings(events.get(confEvent))) {
              String date =
                  getDateFormat().format(mm.getMeetings().first().getMeetingDate())
                      + (mm.getMeetings().size() == 1
                          ? ""
                          : " - "
                              + getDateFormat().format(mm.getMeetings().last().getMeetingDate()));
              if (lastDate == null) {
                conflict.setDate(date);
              } else if (lastDate.equals(date)) {
                conflict.setDate(conflict.getDate() + "<br>");
              } else {
                conflict.setDate(conflict.getDate() + "<br>" + date);
              }
              lastDate = date;

              String time =
                  mm.getDays(CONSTANTS.days(), CONSTANTS.shortDays())
                      + " "
                      + (mm.getMeetings().first().isAllDay()
                          ? "All Day"
                          : mm.getMeetings().first().startTime()
                              + " - "
                              + mm.getMeetings().first().stopTime());
              if (lastTime == null) {
                conflict.setTime(time);
              } else if (lastTime.equals(time)) {
                conflict.setTime(conflict.getTime() + "<br>");
              } else {
                conflict.setTime(conflict.getTime() + "<br>" + time);
              }
              lastTime = time;

              String room =
                  (mm.getMeetings().first().getLocation() == null
                      ? ""
                      : mm.getMeetings().first().getLocation().getLabel());
              if (lastRoom == null) {
                conflict.setRoom(room);
              } else if (lastRoom.equals(room)) {
                conflict.setRoom(conflict.getRoom() + "<br>");
              } else {
                conflict.setRoom(conflict.getRoom() + "<br>" + room);
              }
              lastRoom = room;
            }
            enrl.addConflict(conflict);
          }
        }

        converted.add(enrl);
      }
      ClassAssignmentInterface.ClassAssignment c = enrl.getCourse().addClassAssignment();
      c.setClassId(enrollment.getClazz().getUniqueId());
      c.setSection(enrollment.getClazz().getClassSuffix(enrollment.getCourseOffering()));
      if (c.getSection() == null) c.setSection(enrollment.getClazz().getSectionNumberString());
      c.setClassNumber(enrollment.getClazz().getSectionNumberString());
      c.setSubpart(enrollment.getClazz().getSchedulingSubpart().getItypeDesc());
    }
    return converted;
  }
Пример #2
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());
      }
    }
  }