@Override public SaveOrApproveEventRpcResponse execute(SaveEventRpcRequest request, EventContext context) { if (request.getEvent().hasContact() && (request.getEvent().getContact().getExternalId() == null || !request .getEvent() .getContact() .getExternalId() .equals(context.getUser().getExternalUserId()))) { switch (request.getEvent().getType()) { case Special: case Course: case Unavailabile: context.checkPermission(Right.EventLookupContact); } } if (request.getEvent().getId() == null) { // new even switch (request.getEvent().getType()) { case Special: context.checkPermission(Right.EventAddSpecial); break; case Course: context.checkPermission(Right.EventAddCourseRelated); break; case Unavailabile: context.checkPermission(Right.EventAddUnavailable); break; default: throw context.getException(); } } else { // existing event context.checkPermission(request.getEvent().getId(), "Event", Right.EventEdit); } // Check main contact email if (request.getEvent().hasContact() && request.getEvent().getContact().hasEmail()) { try { new InternetAddress(request.getEvent().getContact().getEmail(), true); } catch (AddressException e) { throw new GwtRpcException( MESSAGES.badEmailAddress(request.getEvent().getContact().getEmail(), e.getMessage())); } } // Check additional emails if (request.getEvent().hasEmail()) { String suffix = ApplicationProperty.EmailDefaultAddressSuffix.value(); for (String address : request.getEvent().getEmail().split("[\n,]")) { String email = address.trim(); if (email.isEmpty()) continue; if (suffix != null && email.indexOf('@') < 0) email += suffix; try { new InternetAddress(email, true); } catch (AddressException e) { throw new GwtRpcException(MESSAGES.badEmailAddress(address, e.getMessage())); } } } org.hibernate.Session hibSession = SessionDAO.getInstance().getSession(); Transaction tx = hibSession.beginTransaction(); try { Session session = SessionDAO.getInstance().get(request.getSessionId(), hibSession); Date now = new Date(); Event event = null; if (request.getEvent().getId() != null) { event = EventDAO.getInstance().get(request.getEvent().getId(), hibSession); } else { switch (request.getEvent().getType()) { case Special: event = new SpecialEvent(); break; case Course: event = new CourseEvent(); break; case Unavailabile: event = new UnavailableEvent(); break; default: throw new GwtRpcException( MESSAGES.failedSaveEventWrongType(request.getEvent().getType().getName(CONSTANTS))); } } SaveOrApproveEventRpcResponse response = new SaveOrApproveEventRpcResponse(); event.setEventName(request.getEvent().getName()); if (event.getEventName() == null || event.getEventName().isEmpty() && request.getEvent().getType() == EventType.Unavailabile) event.setEventName(MESSAGES.unavailableEventDefaultName()); event.setEmail(request.getEvent().getEmail()); if (context.hasPermission(Right.EventSetExpiration) || event.getExpirationDate() != null) event.setExpirationDate(request.getEvent().getExpirationDate()); event.setSponsoringOrganization( request.getEvent().hasSponsor() ? SponsoringOrganizationDAO.getInstance() .get(request.getEvent().getSponsor().getUniqueId()) : null); if (event instanceof UnavailableEvent) { } else if (event instanceof SpecialEvent) { event.setMinCapacity(request.getEvent().getMaxCapacity()); event.setMaxCapacity(request.getEvent().getMaxCapacity()); } if (event.getAdditionalContacts() == null) { event.setAdditionalContacts(new HashSet<EventContact>()); } if (context.hasPermission(Right.EventLookupContact)) { Set<EventContact> existingContacts = new HashSet<EventContact>(event.getAdditionalContacts()); event.getAdditionalContacts().clear(); if (request.getEvent().hasAdditionalContacts()) for (ContactInterface c : request.getEvent().getAdditionalContacts()) { if (c.getExternalId() == null) continue; EventContact contact = null; for (EventContact x : existingContacts) if (c.getExternalId().equals(x.getExternalUniqueId())) { contact = x; break; } if (contact == null) { contact = (EventContact) hibSession .createQuery("from EventContact where externalUniqueId = :externalId") .setString("externalId", c.getExternalId()) .setMaxResults(1) .uniqueResult(); } if (contact == null) { contact = new EventContact(); contact.setExternalUniqueId(c.getExternalId()); contact.setFirstName(c.getFirstName()); contact.setMiddleName(c.getMiddleName()); contact.setLastName(c.getLastName()); contact.setAcademicTitle(c.getAcademicTitle()); contact.setEmailAddress(c.getEmail()); contact.setPhone(c.getPhone()); hibSession.save(contact); } event.getAdditionalContacts().add(contact); } } EventContact main = event.getMainContact(); if (main == null || main.getExternalUniqueId() == null || !main.getExternalUniqueId().equals(request.getEvent().getContact().getExternalId())) { main = (EventContact) hibSession .createQuery("from EventContact where externalUniqueId = :externalId") .setString("externalId", request.getEvent().getContact().getExternalId()) .setMaxResults(1) .uniqueResult(); if (main == null) { main = new EventContact(); main.setExternalUniqueId(request.getEvent().getContact().getExternalId()); } } main.setFirstName(request.getEvent().getContact().getFirstName()); main.setMiddleName(request.getEvent().getContact().getMiddleName()); main.setLastName(request.getEvent().getContact().getLastName()); main.setAcademicTitle(request.getEvent().getContact().getAcademicTitle()); main.setEmailAddress(request.getEvent().getContact().getEmail()); main.setPhone(request.getEvent().getContact().getPhone()); hibSession.saveOrUpdate(main); event.setMainContact(main); if (event.getNotes() == null) event.setNotes(new HashSet<EventNote>()); if (event.getMeetings() == null) event.setMeetings(new HashSet<Meeting>()); Set<Meeting> remove = new HashSet<Meeting>(event.getMeetings()); TreeSet<Meeting> createdMeetings = new TreeSet<Meeting>(); Set<Meeting> cancelledMeetings = new TreeSet<Meeting>(); Set<Meeting> updatedMeetings = new TreeSet<Meeting>(); for (MeetingInterface m : request.getEvent().getMeetings()) { Meeting meeting = null; if (m.getApprovalStatus() == ApprovalStatus.Deleted) { if (!context.hasPermission(meeting, Right.EventMeetingDelete) && context.hasPermission(meeting, Right.EventMeetingCancel)) { // Cannot delete, but can cancel --> cancel the meeting instead m.setApprovalStatus(ApprovalStatus.Cancelled); } else { continue; } } if (m.getId() != null) for (Iterator<Meeting> i = remove.iterator(); i.hasNext(); ) { Meeting x = i.next(); if (m.getId().equals(x.getUniqueId())) { meeting = x; i.remove(); break; } } if (meeting != null) { if (m.getApprovalStatus().ordinal() != meeting.getApprovalStatus()) { switch (m.getApprovalStatus()) { case Cancelled: switch (meeting.getEvent().getEventType()) { case Event.sEventTypeFinalExam: case Event.sEventTypeMidtermExam: if (!context.hasPermission(meeting, Right.EventMeetingCancelExam)) throw new GwtRpcException( MESSAGES.failedApproveEventNoRightsToCancel(toString(meeting))); break; case Event.sEventTypeClass: if (!context.hasPermission(meeting, Right.EventMeetingCancelClass)) throw new GwtRpcException( MESSAGES.failedApproveEventNoRightsToCancel(toString(meeting))); break; default: if (!context.hasPermission(meeting, Right.EventMeetingCancel)) throw new GwtRpcException( MESSAGES.failedApproveEventNoRightsToCancel(toString(meeting))); break; } meeting.setStatus(Meeting.Status.CANCELLED); meeting.setApprovalDate(now); hibSession.update(meeting); cancelledMeetings.add(meeting); response.addCancelledMeeting(m); } } else { if (m.getStartOffset() != (meeting.getStartOffset() == null ? 0 : meeting.getStartOffset()) || m.getEndOffset() != (meeting.getStopOffset() == null ? 0 : meeting.getStopOffset())) { if (!context.hasPermission(meeting, Right.EventMeetingEdit)) throw new GwtRpcException( MESSAGES.failedSaveEventCanNotEditMeeting(toString(meeting))); meeting.setStartOffset(m.getStartOffset()); meeting.setStopOffset(m.getEndOffset()); hibSession.update(meeting); response.addUpdatedMeeting(m); updatedMeetings.add(meeting); } } } else { response.addCreatedMeeting(m); meeting = new Meeting(); meeting.setEvent(event); Location location = null; if (m.hasLocation()) { if (m.getLocation().getId() != null) location = LocationDAO.getInstance().get(m.getLocation().getId(), hibSession); else if (m.getLocation().getName() != null) location = Location.findByName( hibSession, request.getSessionId(), m.getLocation().getName()); } if (location == null) throw new GwtRpcException(MESSAGES.failedSaveEventNoLocation(toString(m))); meeting.setLocationPermanentId(location.getPermanentId()); meeting.setStatus(Meeting.Status.PENDING); meeting.setApprovalDate(null); if (!context.hasPermission(location, Right.EventLocation)) throw new GwtRpcException(MESSAGES.failedSaveEventWrongLocation(m.getLocationName())); if (request.getEvent().getType() == EventType.Unavailabile && !context.hasPermission(location, Right.EventLocationUnavailable)) throw new GwtRpcException( MESSAGES.failedSaveCannotMakeUnavailable(m.getLocationName())); if (m.getApprovalStatus() == ApprovalStatus.Approved && context.hasPermission(location, Right.EventLocationApprove)) { meeting.setStatus(Meeting.Status.APPROVED); meeting.setApprovalDate(now); } if (context.isPastOrOutside(m.getMeetingDate())) throw new GwtRpcException( MESSAGES.failedSaveEventPastOrOutside(getDateFormat().format(m.getMeetingDate()))); if (!context.hasPermission(location, Right.EventLocationOverbook)) { List<MeetingConflictInterface> conflicts = computeConflicts(hibSession, m, event.getUniqueId()); if (!conflicts.isEmpty()) throw new GwtRpcException( MESSAGES.failedSaveEventConflict(toString(m), toString(conflicts.get(0)))); } m.setApprovalDate(meeting.getApprovalDate()); m.setApprovalStatus(meeting.getApprovalStatus()); meeting.setStartPeriod(m.getStartSlot()); meeting.setStopPeriod(m.getEndSlot()); meeting.setStartOffset(m.getStartOffset()); meeting.setStopOffset(m.getEndOffset()); meeting.setClassCanOverride(true); meeting.setMeetingDate(m.getMeetingDate()); event.getMeetings().add(meeting); createdMeetings.add(meeting); } // automatic approval if (meeting.getApprovalDate() == null) { switch (request.getEvent().getType()) { case Unavailabile: case Class: case FinalExam: case MidtermExam: meeting.setStatus(Meeting.Status.APPROVED); meeting.setApprovalDate(now); break; } } } if (!remove.isEmpty()) { for (Iterator<Meeting> i = remove.iterator(); i.hasNext(); ) { Meeting m = i.next(); if (!context.hasPermission(m, Right.EventMeetingDelete)) { if (m.getStatus() == Status.CANCELLED || m.getStatus() == Status.REJECTED) { i.remove(); continue; } throw new GwtRpcException(MESSAGES.failedSaveEventCanNotDeleteMeeting(toString(m))); } MeetingInterface meeting = new MeetingInterface(); meeting.setId(m.getUniqueId()); meeting.setMeetingDate(m.getMeetingDate()); meeting.setDayOfWeek(Constants.getDayOfWeek(m.getMeetingDate())); meeting.setStartTime(m.getStartTime().getTime()); meeting.setStopTime(m.getStopTime().getTime()); meeting.setDayOfYear( CalendarUtils.date2dayOfYear(session.getSessionStartYear(), m.getMeetingDate())); meeting.setStartSlot(m.getStartPeriod()); meeting.setEndSlot(m.getStopPeriod()); meeting.setStartOffset(m.getStartOffset() == null ? 0 : m.getStartOffset()); meeting.setEndOffset(m.getStopOffset() == null ? 0 : m.getStopOffset()); meeting.setPast(context.isPastOrOutside(m.getStartTime())); meeting.setApprovalDate(m.getApprovalDate()); meeting.setApprovalStatus(m.getApprovalStatus()); if (m.getLocation() != null) { ResourceInterface location = new ResourceInterface(); location.setType(ResourceType.ROOM); location.setId(m.getLocation().getUniqueId()); location.setName(m.getLocation().getLabel()); location.setSize(m.getLocation().getCapacity()); location.setRoomType(m.getLocation().getRoomTypeLabel()); location.setBreakTime(m.getLocation().getEffectiveBreakTime()); location.setMessage(m.getLocation().getEventMessage()); location.setIgnoreRoomCheck(m.getLocation().isIgnoreRoomCheck()); meeting.setLocation(location); } response.addDeletedMeeting(meeting); } event.getMeetings().removeAll(remove); } EventInterface.DateFormatter df = new EventInterface.DateFormatter() { Formats.Format<Date> dfShort = Formats.getDateFormat(Formats.Pattern.DATE_EVENT_SHORT); Formats.Format<Date> dfLong = Formats.getDateFormat(Formats.Pattern.DATE_EVENT_LONG); @Override public String formatFirstDate(Date date) { return dfShort.format(date); } @Override public String formatLastDate(Date date) { return dfLong.format(date); } }; FileItem attachment = (FileItem) context.getAttribute(UploadServlet.SESSION_LAST_FILE); boolean attached = false; if (response.hasCreatedMeetings()) { EventNote note = new EventNote(); note.setEvent(event); note.setNoteType( event.getUniqueId() == null ? EventNote.sEventNoteTypeCreateEvent : EventNote.sEventNoteTypeAddMeetings); note.setTimeStamp(now); note.setUser(context.getUser().getTrueName()); note.setUserId(context.getUser().getTrueExternalUserId()); if (request.hasMessage()) note.setTextNote(request.getMessage()); note.setMeetings( EventInterface.toString(response.getCreatedMeetings(), CONSTANTS, "\n", df)); note.setAffectedMeetings(createdMeetings); event.getNotes().add(note); NoteInterface n = new NoteInterface(); n.setDate(now); n.setMeetings(note.getMeetings()); n.setUser(context.getUser().getTrueName()); n.setType(NoteInterface.NoteType.values()[note.getNoteType()]); n.setNote(note.getTextNote()); if (attachment != null) { note.setAttachedName(attachment.getName()); note.setAttachedFile(attachment.get()); note.setAttachedContentType(attachment.getContentType()); attached = true; n.setAttachment(attachment.getName()); } response.addNote(n); } if (response.hasUpdatedMeetings() || (!response.hasCreatedMeetings() && !response.hasDeletedMeetings() && !response.hasCancelledMeetings())) { EventNote note = new EventNote(); note.setEvent(event); note.setNoteType(EventNote.sEventNoteTypeEditEvent); note.setTimeStamp(now); note.setUser(context.getUser().getTrueName()); note.setUserId(context.getUser().getTrueExternalUserId()); note.setAffectedMeetings(updatedMeetings); if (request.hasMessage()) note.setTextNote(request.getMessage()); if (response.hasUpdatedMeetings()) note.setMeetings( EventInterface.toString(response.getUpdatedMeetings(), CONSTANTS, "\n", df)); event.getNotes().add(note); NoteInterface n = new NoteInterface(); n.setDate(now); n.setMeetings(note.getMeetings()); n.setUser(context.getUser().getTrueName()); n.setType(NoteInterface.NoteType.values()[note.getNoteType()]); n.setNote(note.getTextNote()); if (attachment != null && !attached) { note.setAttachedName(attachment.getName()); note.setAttachedFile(attachment.get()); note.setAttachedContentType(attachment.getContentType()); attached = true; n.setAttachment(attachment.getName()); } response.addNote(n); } if (response.hasDeletedMeetings()) { EventNote note = new EventNote(); note.setEvent(event); note.setNoteType(EventNote.sEventNoteTypeDeletion); note.setTimeStamp(now); note.setUser(context.getUser().getTrueName()); note.setUserId(context.getUser().getTrueExternalUserId()); if (request.hasMessage()) note.setTextNote(request.getMessage()); note.setMeetings( EventInterface.toString(response.getDeletedMeetings(), CONSTANTS, "\n", df)); event.getNotes().add(note); NoteInterface n = new NoteInterface(); n.setDate(now); n.setMeetings(note.getMeetings()); n.setUser(context.getUser().getTrueName()); n.setType(NoteInterface.NoteType.values()[note.getNoteType()]); n.setNote(note.getTextNote()); if (attachment != null && !attached) { note.setAttachedName(attachment.getName()); note.setAttachedFile(attachment.get()); note.setAttachedContentType(attachment.getContentType()); attached = true; n.setAttachment(attachment.getName()); } response.addNote(n); } if (response.hasCancelledMeetings()) { EventNote note = new EventNote(); note.setEvent(event); note.setNoteType(EventNote.sEventNoteTypeCancel); note.setTimeStamp(now); note.setUser(context.getUser().getTrueName()); note.setUserId(context.getUser().getTrueExternalUserId()); note.setAffectedMeetings(cancelledMeetings); if (request.hasMessage()) note.setTextNote(request.getMessage()); note.setMeetings( EventInterface.toString(response.getCancelledMeetings(), CONSTANTS, "\n", df)); event.getNotes().add(note); NoteInterface n = new NoteInterface(); n.setDate(now); n.setMeetings(note.getMeetings()); n.setUser(context.getUser().getTrueName()); n.setType(NoteInterface.NoteType.values()[note.getNoteType()]); n.setNote(note.getTextNote()); if (attachment != null && !attached) { note.setAttachedName(attachment.getName()); note.setAttachedFile(attachment.get()); note.setAttachedContentType(attachment.getContentType()); attached = true; n.setAttachment(attachment.getName()); } response.addNote(n); } if (request.getEvent().getType() == EventType.Course) { CourseEvent ce = (CourseEvent) event; ce.setReqAttendance(request.getEvent().hasRequiredAttendance()); if (ce.getRelatedCourses() == null) ce.setRelatedCourses(new HashSet<RelatedCourseInfo>()); else ce.getRelatedCourses().clear(); if (request.getEvent().hasRelatedObjects()) for (RelatedObjectInterface r : request.getEvent().getRelatedObjects()) { RelatedCourseInfo related = new RelatedCourseInfo(); related.setEvent(ce); if (r.getSelection() != null) { related.setOwnerId(r.getUniqueId()); related.setOwnerType(r.getType().ordinal()); related.setCourse( CourseOfferingDAO.getInstance().get(r.getSelection()[1], hibSession)); } else { switch (r.getType()) { case Course: related.setOwner(CourseOfferingDAO.getInstance().get(r.getUniqueId())); break; case Class: related.setOwner(Class_DAO.getInstance().get(r.getUniqueId())); break; case Config: related.setOwner(InstrOfferingConfigDAO.getInstance().get(r.getUniqueId())); break; case Offering: related.setOwner(InstructionalOfferingDAO.getInstance().get(r.getUniqueId())); break; } } ce.getRelatedCourses().add(related); } } if (event.getUniqueId() == null) { hibSession.save(event); response.setEvent( EventDetailBackend.getEventDetail( SessionDAO.getInstance().get(request.getSessionId(), hibSession), event, context)); } else if (event.getMeetings().isEmpty()) { response.setEvent( EventDetailBackend.getEventDetail( SessionDAO.getInstance().get(request.getSessionId(), hibSession), event, context)); response.getEvent().setId(null); hibSession.delete(event); } else { hibSession.update(event); response.setEvent( EventDetailBackend.getEventDetail( SessionDAO.getInstance().get(request.getSessionId(), hibSession), event, context)); } tx.commit(); new EventEmail(request, response).send(context); return response; } catch (Exception ex) { tx.rollback(); if (ex instanceof GwtRpcException) throw (GwtRpcException) ex; throw new GwtRpcException(ex.getMessage(), ex); } }
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()); } } }