From f3a455b8f3047fe6b7b81d6639f97b3f39c473ed Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Wed, 1 Apr 2015 09:58:01 +0200 Subject: [PATCH] OO-1483: optimize the enrollment run view to load the infos in 1 - 2 queries --- .../olat/course/nodes/en/ENRunController.java | 182 ++++++----- .../olat/course/nodes/en/ENWebService.java | 4 +- .../course/nodes/en/EnrollmentManager.java | 113 ++++++- .../olat/course/nodes/en/EnrollmentRow.java | 115 +++++++ .../en/EnrollmentTableModelWithMaxSize.java} | 146 +++++---- .../org/olat/group/BusinessGroupService.java | 6 +- .../manager/BusinessGroupRelationDAO.java | 17 +- .../manager/BusinessGroupServiceImpl.java | 15 +- ...a => EnrollmentManagerConcurrentTest.java} | 11 +- .../nodes/en/EnrollmentManagerSerialTest.java | 303 ++++++++++++++++++ .../java/org/olat/test/AllTestsJunit4.java | 3 +- 11 files changed, 755 insertions(+), 160 deletions(-) create mode 100644 src/main/java/org/olat/course/nodes/en/EnrollmentRow.java rename src/main/java/org/olat/{group/ui/BusinessGroupTableModelWithMaxSize.java => course/nodes/en/EnrollmentTableModelWithMaxSize.java} (55%) rename src/test/java/org/olat/course/nodes/en/{EnrollmentManagerTest.java => EnrollmentManagerConcurrentTest.java} (98%) create mode 100644 src/test/java/org/olat/course/nodes/en/EnrollmentManagerSerialTest.java diff --git a/src/main/java/org/olat/course/nodes/en/ENRunController.java b/src/main/java/org/olat/course/nodes/en/ENRunController.java index 7f3d4f7acf2..831003147e2 100644 --- a/src/main/java/org/olat/course/nodes/en/ENRunController.java +++ b/src/main/java/org/olat/course/nodes/en/ENRunController.java @@ -29,7 +29,7 @@ import java.util.ArrayList; import java.util.List; import org.olat.NewControllerFactory; -import org.olat.core.CoreSpringFactory; +import org.olat.basesecurity.GroupRoles; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.EscapeMode; @@ -45,24 +45,27 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent; +import org.olat.core.util.resource.OresHelper; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.ENCourseNode; import org.olat.course.nodes.ObjectivesHelper; +import org.olat.course.nodes.en.EnrollmentTableModelWithMaxSize.Stats; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; import org.olat.group.area.BGAreaManager; -import org.olat.group.ui.BusinessGroupTableModelWithMaxSize; import org.olat.group.ui.edit.BusinessGroupModifiedEvent; import org.olat.modules.ModuleConfiguration; import org.olat.util.logging.activity.LoggingResourceable; +import org.springframework.beans.factory.annotation.Autowired; /** * Description:<BR> @@ -90,23 +93,25 @@ public class ENRunController extends BasicController implements GenericEventList private VelocityContainer enrollVC; private ENCourseNode enNode; - private BusinessGroupTableModelWithMaxSize groupListModel; + private EnrollmentTableModelWithMaxSize groupListModel; private TableController tableCtr; - // Managers - private final BGAreaManager areaManager; - private final EnrollmentManager enrollmentManager; - private final CourseGroupManager courseGroupManager; - private final BusinessGroupService businessGroupService; - private final CoursePropertyManager coursePropertyManager; + @Autowired + private BGAreaManager areaManager; + @Autowired + private EnrollmentManager enrollmentManager; + @Autowired + private BusinessGroupService businessGroupService; + + private CourseGroupManager courseGroupManager; + private CoursePropertyManager coursePropertyManager; - // workflow variables - private List<BusinessGroup> enrolledGroups; - private List<BusinessGroup> waitingListGroups; - private boolean cancelEnrollEnabled; private int maxEnrollCount; + //registered in event bus + private List<Long> registeredGroupKeys; + /** * @param moduleConfiguration * @param ureq @@ -123,38 +128,36 @@ public class ENRunController extends BasicController implements GenericEventList addLoggingResourceable(LoggingResourceable.wrap(enNode)); // init managers - areaManager = CoreSpringFactory.getImpl(BGAreaManager.class); - enrollmentManager = CoreSpringFactory.getImpl(EnrollmentManager.class); - businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class); courseGroupManager = userCourseEnv.getCourseEnvironment().getCourseGroupManager(); coursePropertyManager = userCourseEnv.getCourseEnvironment().getCoursePropertyManager(); // Get groupnames from configuration - enrollableGroupKeys = (List<Long>)moduleConfig.get(ENCourseNode.CONFIG_GROUP_IDS); + enrollableGroupKeys = moduleConfig.getList(ENCourseNode.CONFIG_GROUP_IDS, Long.class); if(enrollableGroupKeys == null) { String groupNamesConfig = (String)moduleConfig.get(ENCourseNode.CONFIG_GROUPNAME); enrollableGroupKeys = businessGroupService.toGroupKeys(groupNamesConfig, courseGroupManager.getCourseEntry()); } - enrollableAreaKeys = (List<Long>)moduleConfig.get(ENCourseNode.CONFIG_AREA_IDS); + enrollableAreaKeys = moduleConfig.getList(ENCourseNode.CONFIG_AREA_IDS, Long.class); if(enrollableAreaKeys == null) { String areaInitVal = (String) moduleConfig.get(ENCourseNode.CONFIG_AREANAME); enrollableAreaKeys = areaManager.toAreaKeys(areaInitVal, courseGroupManager.getCourseResource()); } - cancelEnrollEnabled = ((Boolean) moduleConfig.get(ENCourseNode.CONF_CANCEL_ENROLL_ENABLED)).booleanValue(); + maxEnrollCount = moduleConfiguration.getIntegerSafe(ENCourseNode.CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT, 1); + cancelEnrollEnabled = moduleConfig.getBooleanSafe(ENCourseNode.CONF_CANCEL_ENROLL_ENABLED); - Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity(); - enrolledGroups = enrollmentManager.getBusinessGroupsWhereEnrolled(identity, enrollableGroupKeys, enrollableAreaKeys, courseGroupManager.getCourseEntry()); - waitingListGroups = enrollmentManager.getBusinessGroupsWhereInWaitingList(identity, enrollableGroupKeys, enrollableAreaKeys); - registerGroupChangedEvents(enrollableGroupKeys, enrollableAreaKeys, ureq.getIdentity()); + registerGroupChangedEvents(enrollableGroupKeys, enrollableAreaKeys, getIdentity()); // Set correct view enrollVC = createVelocityContainer("enrollmultiple"); - List<BusinessGroup> groups = enrollmentManager.loadGroupsFromNames(enrollableGroupKeys, enrollableAreaKeys); + + List<EnrollmentRow> enrollmentRows = enrollmentManager.getEnrollments(getIdentity(), enrollableGroupKeys, enrollableAreaKeys, 256); + groupListModel = new EnrollmentTableModelWithMaxSize(enrollmentRows, getTranslator(), getIdentity(), cancelEnrollEnabled, maxEnrollCount); + Stats stats = groupListModel.getStats(); + tableCtr = createTableController(ureq, stats.isSomeGroupWaitingListEnabled()); + tableCtr.setTableDataModel(groupListModel); - tableCtr = createTableController(ureq, enrollmentManager.hasAnyWaitingList(groups)); - maxEnrollCount = moduleConfiguration.getIntegerSafe(ENCourseNode.CONFIG_ALLOW_MULTIPLE_ENROLL_COUNT, 1); - doEnrollView(ureq); + doEnrollView(stats); // push title and learning objectives, only visible on intro page enrollVC.contextPut("menuTitle", enNode.getShortTitle()); @@ -184,29 +187,33 @@ public class ENRunController extends BasicController implements GenericEventList @Override public void event(UserRequest ureq, Controller source, Event event) { String cmd = event.getCommand(); - if (source == tableCtr) { + if (source == tableCtr) { if (cmd.equals(Table.COMMANDLINK_ROWACTION_CLICKED)) { TableEvent te = (TableEvent) event; String actionid = te.getActionId(); int rowid = te.getRowId(); - BusinessGroup choosenGroup = groupListModel.getBusinessGroupAt(rowid); - addLoggingResourceable(LoggingResourceable.wrap(choosenGroup)); + EnrollmentRow row = groupListModel.getObject(rowid); + Long choosenGroupKey = row.getKey(); if (actionid.equals(CMD_ENROLL_IN_GROUP)) { - log.debug("CMD_ENROLL_IN_GROUP ureq.getComponentID()=" + ureq.getComponentID() + " ureq.getComponentTimestamp()=" + ureq.getComponentTimestamp()); + BusinessGroup choosenGroup = businessGroupService.loadBusinessGroup(choosenGroupKey); + addLoggingResourceable(LoggingResourceable.wrap(choosenGroup)); + + if(log.isDebug()) { + log.debug("CMD_ENROLL_IN_GROUP ureq.getComponentID()=" + ureq.getComponentID() + " ureq.getComponentTimestamp()=" + ureq.getComponentTimestamp()); + } + EnrollStatus enrollStatus = enrollmentManager.doEnroll(ureq.getIdentity(), ureq.getUserSession().getRoles(), choosenGroup, enNode, coursePropertyManager, getWindowControl(), getTranslator(), enrollableGroupKeys, enrollableAreaKeys, courseGroupManager); - if (enrollStatus.isEnrolled() ) { - enrolledGroups.add(choosenGroup); - } else if (enrollStatus.isInWaitingList() ) { - waitingListGroups.add(choosenGroup); + if (enrollStatus.isEnrolled() || enrollStatus.isInWaitingList() ) { + //OK } else { getWindowControl().setError(enrollStatus.getErrorMessage()); } // events are already fired BusinessGroupManager level :: BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, choosenGroup, ureq.getIdentity()); // but async // fire event to indicate runmaincontroller that the menuview is to update - doEnrollView(ureq); + doEnrollView(updateModel()); if (enrollStatus.isEnrolled() ) { fireEvent(ureq, new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, choosenGroup, getIdentity())); } else { @@ -214,24 +221,30 @@ public class ENRunController extends BasicController implements GenericEventList } } else if (actionid.equals(CMD_ENROLLED_CANCEL)) { - if (enrollmentManager.getBusinessGroupsWhereInWaitingList(getIdentity(), enrollableGroupKeys, enrollableAreaKeys).contains(choosenGroup)) { + BusinessGroup choosenGroup = businessGroupService.loadBusinessGroup(choosenGroupKey); + addLoggingResourceable(LoggingResourceable.wrap(choosenGroup)); + + List<String> roles = businessGroupService + .getIdentityRolesInBusinessGroup(getIdentity(), choosenGroup); + if (roles.contains(GroupRoles.waiting.name())) { enrollmentManager.doCancelEnrollmentInWaitingList(ureq.getIdentity(), choosenGroup, enNode, coursePropertyManager, getWindowControl(), getTranslator()); - waitingListGroups.remove(choosenGroup); - } else if(enrollmentManager.getBusinessGroupsWhereEnrolled(getIdentity(), enrollableGroupKeys, enrollableAreaKeys, courseGroupManager.getCourseEntry()).contains(choosenGroup)) { + } else if(roles.contains(GroupRoles.participant.name())) { enrollmentManager.doCancelEnrollment(ureq.getIdentity(), choosenGroup, enNode, coursePropertyManager, getWindowControl(), getTranslator()); - enrolledGroups.remove(choosenGroup); } + // fire event to indicate runmaincontroller that the menuview is to update fireEvent(ureq, new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, choosenGroup, getIdentity())); // events are already fired BusinessGroupManager level :: BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, ureq.getIdentity()); // but async - doEnrollView(ureq); + doEnrollView(updateModel()); } else if(CMD_VISIT_CARD.equals(actionid)) { + List<String> roles = businessGroupService.getIdentityRolesInBusinessGroup(getIdentity(), row); + String businessPath; - if(businessGroupService.isIdentityInBusinessGroup(getIdentity(), choosenGroup)) { - businessPath = "[BusinessGroup:" + choosenGroup.getKey() + "]"; + if(roles.contains(GroupRoles.coach.name()) || roles.contains(GroupRoles.participant.name())) { + businessPath = "[BusinessGroup:" + choosenGroupKey + "]"; } else { - businessPath = "[GroupCard:" + choosenGroup.getKey() + "]"; + businessPath = "[GroupCard:" + choosenGroupKey + "]"; } NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl()); } @@ -239,54 +252,61 @@ public class ENRunController extends BasicController implements GenericEventList } } + @Override public void event(Event event) { if (event instanceof OLATResourceableJustBeforeDeletedEvent) { dispose(); } } - private void doEnrollView(UserRequest ureq) { - enrollVC.contextPut("multiEnroll", (maxEnrollCount > 1 && enrolledGroups.size()+waitingListGroups.size()<maxEnrollCount)); - if(!enrolledGroups.isEmpty()||!waitingListGroups.isEmpty()){ - String[] hintNumbers = new String[2]; - hintNumbers[0]=String.valueOf(enrolledGroups.size()+waitingListGroups.size()); - hintNumbers[1]=String.valueOf(maxEnrollCount-enrolledGroups.size()-waitingListGroups.size()); + private void doEnrollView(Stats stats) { + //num. of groups where the user is participant or in the waiting list + int numOfParticipatingGroups = stats.getParticipantingGroupNames().size(); + int numOfWaitingGroups = stats.getWaitingGroupNames().size(); + + enrollVC.contextPut("multiEnroll", (maxEnrollCount > 1 && numOfParticipatingGroups + numOfWaitingGroups < maxEnrollCount)); + if(numOfParticipatingGroups > 0 || numOfWaitingGroups > 0){ + String[] hintNumbers = new String[]{ + String.valueOf(numOfParticipatingGroups + numOfWaitingGroups), + String.valueOf(maxEnrollCount - numOfParticipatingGroups - numOfWaitingGroups) + }; enrollVC.contextPut("multipleHint", translate("multiple.select.hint.outstanding", hintNumbers)); - }else{ + } else { enrollVC.contextPut("multipleHint", translate("multiple.select.hint", String.valueOf(maxEnrollCount))); } - if (!enrolledGroups.isEmpty()) { + if (numOfParticipatingGroups > 0) { enrollVC.contextPut("isEnrolledView", Boolean.TRUE); - ArrayList<String> groupnames = new ArrayList<String>(); - for(BusinessGroup group:enrolledGroups){ - groupnames.add(StringHelper.escapeHtml(group.getName())); + List<String> groupnames = new ArrayList<String>(numOfParticipatingGroups); + for(String groupName: stats.getParticipantingGroupNames()){ + groupnames.add(StringHelper.escapeHtml(groupName)); } enrollVC.contextPut("groupNames", groupnames); - }else{ + } else { enrollVC.contextPut("isEnrolledView", Boolean.FALSE); } - if (!waitingListGroups.isEmpty()){ + if (numOfWaitingGroups > 0){ enrollVC.contextPut("isInWaitingList", Boolean.TRUE); - ArrayList<String> waitingListNames = new ArrayList<String>(); - for(BusinessGroup waitingGroup:waitingListGroups){ - waitingListNames.add(StringHelper.escapeHtml(waitingGroup.getName())); + List<String> waitingListNames = new ArrayList<String>(numOfWaitingGroups); + for(String groupName:stats.getWaitingGroupNames()){ + waitingListNames.add(StringHelper.escapeHtml(groupName)); } enrollVC.contextPut("waitingListNames", waitingListNames); - }else{ + } else { enrollVC.contextPut("isInWaitingList", Boolean.FALSE); } - // 1. Fetch groups from database - List<BusinessGroup> groups = enrollmentManager.loadGroupsFromNames(enrollableGroupKeys, enrollableAreaKeys); - List<Integer> members = courseGroupManager.getNumberOfMembersFromGroups(groups); - // 2. Build group list - groupListModel = new BusinessGroupTableModelWithMaxSize(groups, members, getTranslator(), ureq.getIdentity(), cancelEnrollEnabled, maxEnrollCount); - tableCtr.setTableDataModel(groupListModel); - tableCtr.modelChanged(); // 3. Add group list to view enrollVC.put("grouplisttable", tableCtr.getInitialComponent()); } + + private Stats updateModel() { + List<EnrollmentRow> enrollmentRows = enrollmentManager.getEnrollments(getIdentity(), enrollableGroupKeys, enrollableAreaKeys, 256); + groupListModel = new EnrollmentTableModelWithMaxSize(enrollmentRows, getTranslator(), getIdentity(), cancelEnrollEnabled, maxEnrollCount); + Stats stats = groupListModel.getStats(); + tableCtr.setTableDataModel(groupListModel); + return stats; + } private TableController createTableController(UserRequest ureq, boolean hasAnyWaitingList) { TableGuiConfiguration tableConfig = new TableGuiConfiguration(); @@ -301,7 +321,7 @@ public class ENRunController extends BasicController implements GenericEventList descCd.setEscapeHtml(EscapeMode.antisamy); tableCtr.addColumnDescriptor(descCd); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("grouplist.table.partipiciant", 2, null, getLocale())); - tableCtr.addColumnDescriptor(hasAnyWaitingList,new DefaultColumnDescriptor("grouplist.table.waitingList", 3, null, getLocale())); + tableCtr.addColumnDescriptor(hasAnyWaitingList, new DefaultColumnDescriptor("grouplist.table.waitingList", 3, null, getLocale())); DefaultColumnDescriptor stateColdEsc = new DefaultColumnDescriptor("grouplist.table.state", 4, null, getLocale()); stateColdEsc.setEscapeHtml(EscapeMode.none); tableCtr.addColumnDescriptor(stateColdEsc); @@ -318,24 +338,28 @@ public class ENRunController extends BasicController implements GenericEventList * * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) */ + @Override protected void doDispose() { - deregisterGroupChangedEvents(enrollableGroupKeys, enrollableAreaKeys); + deregisterGroupChangedEvents(); } - + /* * Add as listener to BusinessGroups so we are being notified about changes. */ - private void registerGroupChangedEvents(List<Long> enrollableGroupKeys, List<Long> enrollableAreaKeys, Identity identity) { - List<BusinessGroup> groups = enrollmentManager.loadGroupsFromNames(enrollableGroupKeys, enrollableAreaKeys); - for (BusinessGroup group: groups) { - CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, identity, group); + private void registerGroupChangedEvents(List<Long> groupKeys, List<Long> areaKeys, Identity identity) { + registeredGroupKeys = enrollmentManager.getBusinessGroupKeys(groupKeys, areaKeys); + for (Long groupKey: registeredGroupKeys) { + OLATResourceable ores = OresHelper.createOLATResourceableInstance(BusinessGroup.class, groupKey); + CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, identity, ores); } } - private void deregisterGroupChangedEvents(List<Long> enrollableGroupKeys, List<Long> enrollableAreaKeys) { - List<BusinessGroup> groups = enrollmentManager.loadGroupsFromNames(enrollableGroupKeys, enrollableAreaKeys); - for (BusinessGroup group:groups) { - CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, group); + private void deregisterGroupChangedEvents() { + if(registeredGroupKeys != null) { + for (Long groupKey:registeredGroupKeys) { + OLATResourceable ores = OresHelper.createOLATResourceableInstance(BusinessGroup.class, groupKey); + CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, ores); + } } } } \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/en/ENWebService.java b/src/main/java/org/olat/course/nodes/en/ENWebService.java index edb037c859b..a5b45281a3f 100644 --- a/src/main/java/org/olat/course/nodes/en/ENWebService.java +++ b/src/main/java/org/olat/course/nodes/en/ENWebService.java @@ -241,8 +241,8 @@ public class ENWebService extends AbstractCourseNodeWebService { Long groupKey = new Long(groupId); keys.add(groupKey); } - List<BusinessGroupShort> groups = bgm.loadShortBusinessGroups(keys); - for(BusinessGroupShort bg:groups) { + List<BusinessGroupShort> groupsShort = bgm.loadShortBusinessGroups(keys); + for(BusinessGroupShort bg:groupsShort) { groupNames.add(bg.getName()); } } diff --git a/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java b/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java index a3a71b2d590..3cb929df4d5 100644 --- a/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java +++ b/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java @@ -31,11 +31,16 @@ import java.util.List; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.GroupRoles; +import org.olat.basesecurity.IdentityRef; +import org.olat.core.commons.persistence.DB; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.manager.BasicManager; +import org.olat.core.util.Formatter; +import org.olat.core.util.StringHelper; +import org.olat.core.util.filter.FilterFactory; import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailContext; import org.olat.core.util.mail.MailContextImpl; @@ -48,9 +53,11 @@ import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.ENCourseNode; import org.olat.course.properties.CoursePropertyManager; import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupImpl; import org.olat.group.BusinessGroupService; import org.olat.group.area.BGAreaManager; import org.olat.group.model.BGMembership; +import org.olat.group.model.BusinessGroupRefImpl; import org.olat.group.model.EnrollState; import org.olat.group.model.SearchBusinessGroupParams; import org.olat.group.ui.BGMailHelper; @@ -72,6 +79,8 @@ import org.springframework.stereotype.Service; @Service("enrollmentManager") public class EnrollmentManager extends BasicManager { + @Autowired + private DB dbInstance; @Autowired private MailManager mailManager; @Autowired @@ -126,9 +135,9 @@ public class EnrollmentManager extends BasicManager { // and move the users accordingly MailPackage doNotSendmailPackage = new MailPackage(false); businessGroupService.removeParticipants(identity, Collections.singletonList(identity), enrolledGroup, doNotSendmailPackage); - logInfo("doCancelEnrollment in group " + enrolledGroup, identity.getName()); + logInfo(" doCancelEnrollment in group " + enrolledGroup, identity.getName()); - logInfo("doCancelEnrollment in group " + enrolledGroup, identity.getName()); + logInfo(" doCancelEnrollment in group " + enrolledGroup, identity.getName()); // 2. Remove enrollmentdate property // only remove last time date, not firsttime Property lastTime = coursePropertyManager.findCourseNodeProperty(enNode, identity, null, ENCourseNode.PROPERTY_RECENT_ENROLLMENT_DATE); @@ -233,6 +242,106 @@ public class EnrollmentManager extends BasicManager { } return groups; } + protected List<Long> getBusinessGroupKeys(List<Long> groupKeys, List<Long> areaKeys) { + List<Long> allKeys = new ArrayList<>(); + if(groupKeys != null && !groupKeys.isEmpty()) { + allKeys.addAll(groupKeys); + } + if(areaKeys != null && !areaKeys.isEmpty()) { + List<Long> areaGroupKeys = areaManager.findBusinessGroupKeysOfAreaKeys(areaKeys); + allKeys.addAll(areaGroupKeys); + } + return allKeys; + } + + protected List<EnrollmentRow> getEnrollments(IdentityRef identity, List<Long> groupKeys, List<Long> areaKeys, + int descriptionMaxSize) { + List<Long> allGroupKeys = getBusinessGroupKeys(groupKeys, areaKeys); + if(allGroupKeys.isEmpty()) return Collections.emptyList(); + + // groupKey, name, description, maxParticipants, waitingListEnabled; + // numInWaitingList, numOfParticipants, participant, waiting; + + StringBuilder sb = new StringBuilder(); + sb.append("select grp.key, grp.name, grp.description, grp.maxParticipants, grp.waitingListEnabled, ") + //num of participant + .append(" (select count(participants.key) from bgroupmember participants ") + .append(" where participants.group=baseGroup and participants.role='").append(GroupRoles.participant.name()).append("'") + .append(" ) as numOfParticipants,") + //length of the waiting list + .append(" (select count(waiters.key) from bgroupmember waiters ") + .append(" where grp.waitingListEnabled=true and waiters.group=baseGroup and waiters.role='").append(GroupRoles.waiting.name()).append("'") + .append(" ) as numOfWaiters,") + //participant? + .append(" (select count(meParticipant.key) from bgroupmember meParticipant ") + .append(" where meParticipant.group=baseGroup and meParticipant.role='").append(GroupRoles.participant.name()).append("'") + .append(" and meParticipant.identity.key=:identityKey") + .append(" ) as numOfMeParticipant,") + //waiting? + .append(" (select count(meWaiting.key) from bgroupmember meWaiting ") + .append(" where grp.waitingListEnabled=true and meWaiting.group=baseGroup and meWaiting.role='").append(GroupRoles.waiting.name()).append("'") + .append(" and meWaiting.identity.key=:identityKey") + .append(" ) as numOfMeWaiting") + + .append(" from ").append(BusinessGroupImpl.class.getName()).append(" grp ") + .append(" inner join grp.baseGroup as baseGroup ") + .append(" where grp.key in (:groupKeys)"); + + List<Object[]> rows = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Object[].class) + .setParameter("groupKeys", allGroupKeys) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + + List<EnrollmentRow> enrollments = new ArrayList<>(rows.size()); + for(Object[] row:rows) { + Long key = ((Number)row[0]).longValue(); + String name = (String)row[1]; + String desc = (String)row[2]; + if(StringHelper.containsNonWhitespace(desc) && descriptionMaxSize > 0) { + desc = FilterFactory.getHtmlTagsFilter().filter(desc); + desc = Formatter.truncate(desc, 256); + } + + int maxParticipants = row[3] == null ? -1 : ((Number)row[3]).intValue(); + + Object enabled = row[4]; + boolean waitingListEnabled; + if(enabled == null) { + waitingListEnabled = false; + } else if(enabled instanceof Boolean) { + waitingListEnabled = ((Boolean)enabled).booleanValue(); + } else if(enabled instanceof Number) { + int val = ((Number)enabled).intValue(); + waitingListEnabled = val == 1; + } else { + waitingListEnabled = false; + } + + int numOfParticipants = row[5] == null ? 0 : ((Number)row[5]).intValue(); + int numOfWaiters = row[6] == null ? 0 : ((Number)row[6]).intValue(); + boolean participant = row[7] == null ? false : ((Number)row[7]).intValue() > 0; + boolean waiting = row[8] == null ? false : ((Number)row[8]).intValue() > 0; + + EnrollmentRow enrollment = new EnrollmentRow(key, name, desc, + maxParticipants, waitingListEnabled); + enrollment.setNumOfParticipants(numOfParticipants); + enrollment.setNumInWaitingList(numOfWaiters); + enrollment.setParticipant(participant); + enrollment.setWaiting(waiting); + + if(waitingListEnabled && waiting) { + int pos = businessGroupService.getPositionInWaitingListFor(identity, new BusinessGroupRefImpl(key)); + enrollment.setPositionInWaitingList(pos); + } else { + enrollment.setPositionInWaitingList(-1); + } + + enrollments.add(enrollment); + } + + return enrollments; + } /** * Check if in any business-group a waiting-list is configured. diff --git a/src/main/java/org/olat/course/nodes/en/EnrollmentRow.java b/src/main/java/org/olat/course/nodes/en/EnrollmentRow.java new file mode 100644 index 00000000000..7b1a9076ec6 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/en/EnrollmentRow.java @@ -0,0 +1,115 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.course.nodes.en; + +import org.olat.group.BusinessGroupRef; + + +/** + * + * Initial date: 31.03.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class EnrollmentRow implements BusinessGroupRef { + + private final Long groupKey; + private final String name; + private final String description; + private final int maxParticipants; + private final boolean waitingListEnabled; + + + private boolean waiting; + private boolean participant; + private int numInWaitingList; + private int numOfParticipants; + private int positionInWaitingList; + + public EnrollmentRow(Long groupKey, String name, String description, + int maxParticipants, boolean waitingListEnabled) { + this.groupKey = groupKey; + this.name = name; + this.description = description; + this.maxParticipants = maxParticipants; + this.waitingListEnabled = waitingListEnabled; + } + + @Override + public Long getKey() { + return groupKey; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getMaxParticipants() { + return maxParticipants; + } + + public boolean isWaitingListEnabled() { + return waitingListEnabled; + } + + public boolean isWaiting() { + return waiting; + } + + public void setWaiting(boolean waiting) { + this.waiting = waiting; + } + + public int getPositionInWaitingList() { + return positionInWaitingList; + } + + public boolean isParticipant() { + return participant; + } + + public void setParticipant(boolean participant) { + this.participant = participant; + } + + public int getNumInWaitingList() { + return numInWaitingList; + } + + public void setNumInWaitingList(int numInWaitingList) { + this.numInWaitingList = numInWaitingList; + } + + public int getNumOfParticipants() { + return numOfParticipants; + } + + public void setNumOfParticipants(int numOfParticipants) { + this.numOfParticipants = numOfParticipants; + } + + public void setPositionInWaitingList(int positionInWaitingList) { + this.positionInWaitingList = positionInWaitingList; + } +} diff --git a/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java b/src/main/java/org/olat/course/nodes/en/EnrollmentTableModelWithMaxSize.java similarity index 55% rename from src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java rename to src/main/java/org/olat/course/nodes/en/EnrollmentTableModelWithMaxSize.java index 446263118ca..d1b794f6559 100644 --- a/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java +++ b/src/main/java/org/olat/course/nodes/en/EnrollmentTableModelWithMaxSize.java @@ -23,23 +23,17 @@ * under the Apache 2.0 license as the original file. */ -package org.olat.group.ui; +package org.olat.course.nodes.en; import java.util.ArrayList; import java.util.List; -import org.olat.basesecurity.GroupRoles; -import org.olat.core.CoreSpringFactory; import org.olat.core.gui.components.table.DefaultTableDataModel; import org.olat.core.gui.components.table.TableDataModelWithMarkableRows; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; -import org.olat.core.util.Formatter; -import org.olat.core.util.filter.FilterFactory; -import org.olat.group.BusinessGroup; -import org.olat.group.BusinessGroupService; /** * Description:<BR> @@ -50,16 +44,15 @@ import org.olat.group.BusinessGroupService; * * @author gnaegi */ -public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<BusinessGroup> implements TableDataModelWithMarkableRows<BusinessGroup> { - private static final OLog log = Tracing.createLoggerFor(BusinessGroupTableModelWithMaxSize.class); +public class EnrollmentTableModelWithMaxSize extends DefaultTableDataModel<EnrollmentRow> implements TableDataModelWithMarkableRows<EnrollmentRow> { + private static final OLog log = Tracing.createLoggerFor(EnrollmentTableModelWithMaxSize.class); private static final int COLUMN_COUNT = 7; - private List<Integer> members; - private Translator trans; - private Identity identity; - private boolean cancelEnrollEnabled; - private BusinessGroupService businessGroupService; - private int maxEnrolCount; + + private final Translator trans; + private final Identity identity; + private final boolean cancelEnrollEnabled; + private final int maxEnrolCount; /** * @param groups List of business groups @@ -67,12 +60,11 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu * The index of the list corresponds with the index of the group list * @param trans */ - public BusinessGroupTableModelWithMaxSize(List<BusinessGroup> groups, List<Integer> members, Translator trans, Identity identity, boolean cancelEnrollEnabled, int maxEnrolCount) { + public EnrollmentTableModelWithMaxSize(List<EnrollmentRow> groups, Translator trans, Identity identity, + boolean cancelEnrollEnabled, int maxEnrolCount) { super(groups); - this.members = members; this.trans = trans; this.identity = identity; - businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class); this.cancelEnrollEnabled = cancelEnrollEnabled; this.maxEnrolCount = maxEnrolCount; } @@ -80,6 +72,7 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu /** * @see org.olat.core.gui.components.table.TableDataModel#getColumnCount() */ + @Override public int getColumnCount() { return COLUMN_COUNT; } @@ -87,18 +80,14 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu /** * @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int) */ + @Override public Object getValueAt(int row, int col) { - BusinessGroup businessGroup = objects.get(row); - Integer numbParts = members.get(row); - Integer max = businessGroup.getMaxParticipants(); + EnrollmentRow enrollmentRow = objects.get(row); + int numbParts = enrollmentRow.getNumOfParticipants(); + Integer max = enrollmentRow.getMaxParticipants(); switch (col) { - case 0: - return businessGroup.getName(); - case 1: - String description = businessGroup.getDescription(); - description = FilterFactory.getHtmlTagsFilter().filter(description); - description = Formatter.truncate(description, 256); - return description; + case 0: return enrollmentRow.getName(); + case 1: return enrollmentRow.getDescription(); case 2: // Belegt/Plätze if (max == null) { @@ -107,51 +96,50 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu } // return format 2/10 StringBuilder buf = new StringBuilder(); - buf.append(numbParts); - buf.append(trans.translate("grouplist.table.partipiciant.delimiter")); - buf.append(businessGroup.getMaxParticipants()); - if(numbParts>businessGroup.getMaxParticipants()) { - log.info("Group overflow detected for the group: " + businessGroup + ", participants: " + numbParts + " maxParticipamts: " + businessGroup.getMaxParticipants()); + buf.append(numbParts) + .append(trans.translate("grouplist.table.partipiciant.delimiter")) + .append(enrollmentRow.getMaxParticipants()); + if(numbParts > enrollmentRow.getMaxParticipants()) { + log.info("Group overflow detected for the group: " + enrollmentRow.getKey() + "[name=" + enrollmentRow.getName() + "], participants: " + numbParts + " maxParticipamts: " + enrollmentRow.getMaxParticipants()); } return buf.toString(); case 3: // Waiting-list - if (businessGroup.getWaitingListEnabled().booleanValue()) { + if (enrollmentRow.isWaitingListEnabled()) { // Waitinglist is enabled => show current size - int intValue = businessGroupService.countMembers(businessGroup, GroupRoles.waiting.name()); - return new Integer(intValue); + return new Integer(enrollmentRow.getNumInWaitingList()); } return trans.translate("grouplist.table.noWaitingList"); case 4: // Status - if (businessGroupService.hasRoles(identity, businessGroup, GroupRoles.participant.name())) { + if (enrollmentRow.isParticipant()) { return trans.translate("grouplist.table.state.onPartipiciantList"); - } else if (businessGroupService.hasRoles(identity, businessGroup, GroupRoles.waiting.name())) { - int pos = businessGroupService.getPositionInWaitingListFor(identity,businessGroup); + } else if (enrollmentRow.isWaiting()) { + int pos = enrollmentRow.getPositionInWaitingList(); String[] onWaitingListArgs = new String[] { Integer.toString(pos) }; return trans.translate("grouplist.table.state.onWaitingList",onWaitingListArgs); - } else if (max != null && !businessGroup.getWaitingListEnabled().booleanValue() && (numbParts.intValue() >= max.intValue()) ) { + } else if (max != null && !enrollmentRow.isWaitingListEnabled() && numbParts >= max.intValue()) { return trans.translate("grouplist.table.state.enroll.full"); - } else if (max != null && businessGroup.getWaitingListEnabled().booleanValue() && (numbParts.intValue() >= max.intValue()) ) { + } else if (max != null && enrollmentRow.isWaitingListEnabled() && numbParts >= max.intValue()) { return trans.translate("grouplist.table.state.WaitingList"); } return trans.translate("grouplist.table.state.notEnrolled"); case 5: // Action enroll - if (getEnrolCount(identity) >= maxEnrolCount || isEnrolledIn(businessGroup, identity)) { + if (getEnrolCount() >= maxEnrolCount || isEnrolledIn(enrollmentRow)) { // Already too much enrollments or already enrolled in the bg of the row => does not show action-link 'enroll' return Boolean.FALSE; } - if (max != null && !businessGroup.getWaitingListEnabled().booleanValue() && (numbParts.intValue() >= max.intValue()) ) { + if (max != null && !enrollmentRow.isWaitingListEnabled() && numbParts >= max.intValue()) { // group is full => => does not show action-link 'enroll' return Boolean.FALSE; } return Boolean.TRUE; case 6: // Action cancel enrollment - if (isEnrolledIn(businessGroup, identity)) { + if (isEnrolledIn(enrollmentRow)) { // check if user is on waiting-list - if (businessGroupService.hasRoles(identity, businessGroup, GroupRoles.waiting.name())) { + if (enrollmentRow.isWaiting()) { // user is on waitinglist => show allways action cancelEnrollment for waitinglist return Boolean.TRUE; } @@ -161,20 +149,19 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu } } return Boolean.FALSE; - default: - return "ERROR"; + default: return "ERROR"; } } @Override public Object createCopyWithEmptyList() { - return new BusinessGroupTableModelWithMaxSize(new ArrayList<BusinessGroup>(), members, trans, identity, cancelEnrollEnabled, maxEnrolCount); + return new EnrollmentTableModelWithMaxSize(new ArrayList<EnrollmentRow>(), trans, identity, cancelEnrollEnabled, maxEnrolCount); } /** * @param owned */ - public void setEntries(List<BusinessGroup> owned) { + public void setEntries(List<EnrollmentRow> owned) { this.objects = owned; } @@ -182,7 +169,7 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu * @param row * @return the business group at the given row */ - public BusinessGroup getBusinessGroupAt(int row) { + public EnrollmentRow getRowAt(int row) { return objects.get(row); } @@ -192,12 +179,8 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu * @param ident * @return true: Found identity in PartipiciantGroup or WaitingGroup. */ - private boolean isEnrolledIn(BusinessGroup businessGroup, Identity ident) { - if (businessGroupService.hasRoles(ident, businessGroup, GroupRoles.participant.name()) - || businessGroupService.hasRoles(ident, businessGroup, GroupRoles.waiting.name())) { - return true; - } - return false; + private boolean isEnrolledIn(EnrollmentRow enrollmentRow) { + return enrollmentRow.isWaiting() || enrollmentRow.isParticipant(); } /** @@ -205,11 +188,11 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu * @param ident * @return amount of business groups the identity is enrolled in */ - private int getEnrolCount(Identity ident) { + private int getEnrolCount() { int enrolCount=0; // loop over all business-groups - for (BusinessGroup businessGroup:objects) { - if (isEnrolledIn(businessGroup, ident) ) { + for (EnrollmentRow enrollmentRow:objects) { + if (isEnrolledIn(enrollmentRow) ) { enrolCount++; // optimize, enough is enough if (maxEnrolCount == enrolCount) { @@ -222,10 +205,47 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu @Override public String getRowCssClass(int rowId) { - BusinessGroup businessGroup = objects.get(rowId); - boolean isEnrolled = isEnrolledIn(businessGroup, identity); - return (isEnrolled ? "o_row_selected" : ""); + EnrollmentRow enrollmentRow = objects.get(rowId); + return isEnrolledIn(enrollmentRow) ? "o_row_selected" : ""; + } + + public Stats getStats() { + Stats stats = new Stats(); + for(int i=getRowCount(); i-->0; ) { + EnrollmentRow row = getObject(i); + if(row.isWaitingListEnabled() && row.isWaiting()) { + stats.getWaitingGroupNames().add(row.getName()); + } + if(row.isParticipant()) { + stats.getParticipantingGroupNames().add(row.getName()); + } + if(row.isWaitingListEnabled()) { + stats.setSomeGroupWaitingListEnabled(true); + } + } + return stats; } + + public static class Stats { + + private boolean someGroupWaitingListEnabled = false; + private final List<String> participantingGroupNames = new ArrayList<>(5); + private final List<String> waitingGroupNames = new ArrayList<>(5); + + public boolean isSomeGroupWaitingListEnabled() { + return someGroupWaitingListEnabled; + } + + public void setSomeGroupWaitingListEnabled(boolean someGroupWaitingListEnabled) { + this.someGroupWaitingListEnabled = someGroupWaitingListEnabled; + } - + public List<String> getParticipantingGroupNames() { + return participantingGroupNames; + } + + public List<String> getWaitingGroupNames() { + return waitingGroupNames; + } + } } \ No newline at end of file diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java index 67d43fd7d11..e7d8213f76a 100644 --- a/src/main/java/org/olat/group/BusinessGroupService.java +++ b/src/main/java/org/olat/group/BusinessGroupService.java @@ -346,7 +346,7 @@ public interface BusinessGroupService { * @param businessGroup * @return */ - public int getPositionInWaitingListFor(Identity identity, BusinessGroup businessGroup); + public int getPositionInWaitingListFor(IdentityRef identity, BusinessGroupRef businessGroup); //memberships /** @@ -531,7 +531,9 @@ public interface BusinessGroupService { * @param businessGroup * @return True if coach or participant */ - public boolean isIdentityInBusinessGroup(Identity identity, BusinessGroup businessGroup); + public boolean isIdentityInBusinessGroup(IdentityRef identity, BusinessGroupRef businessGroup); + + public List<String> getIdentityRolesInBusinessGroup(IdentityRef identity, BusinessGroupRef businessGroup); /** * Checks if an identity is in the list of business groups either as owner or as participant diff --git a/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java b/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java index d758ceacdd6..524193da609 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java @@ -305,9 +305,24 @@ public class BusinessGroupRelationDAO { return members; } + public List<Long> getMemberKeysOrderByDate(BusinessGroupRef group, String... roles) { + StringBuilder sb = new StringBuilder(); + sb.append("select membership.identity.key from ").append(BusinessGroupImpl.class.getName()).append(" as bgroup ") + .append(" inner join bgroup.baseGroup as baseGroup") + .append(" inner join baseGroup.members as membership") + .append(" where bgroup.key=:businessGroupKey and membership.role in (:roles) order by membership.creationDate"); + + List<String> roleList = GroupRoles.toList(roles); + List<Long> members = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Long.class) + .setParameter("businessGroupKey", group.getKey()) + .setParameter("roles", roleList) + .getResultList(); + return members; + } + public List<Identity> getMembersOrderByDate(BusinessGroupRef group, String... roles) { StringBuilder sb = new StringBuilder(); - sb.append("select membership.identity from ").append(BusinessGroupImpl.class.getName()).append(" as bgroup ") + sb.append("select membership.identity.key from ").append(BusinessGroupImpl.class.getName()).append(" as bgroup ") .append(" inner join bgroup.baseGroup as baseGroup") .append(" inner join baseGroup.members as membership") .append(" where bgroup.key=:businessGroupKey and membership.role in (:roles) order by membership.creationDate"); diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java index ecb3cd91596..2f84d88b7f2 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java @@ -1303,12 +1303,12 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD } @Override - public int getPositionInWaitingListFor(Identity identity, BusinessGroup businessGroup) { + public int getPositionInWaitingListFor(IdentityRef identity, BusinessGroupRef businessGroup) { // get position in waiting-list - List<Identity> identities = businessGroupRelationDAO.getMembersOrderByDate(businessGroup, GroupRoles.waiting.name()); + List<Long> identities = businessGroupRelationDAO.getMemberKeysOrderByDate(businessGroup, GroupRoles.waiting.name()); for (int i = 0; i<identities.size(); i++) { - Identity waitingListIdentity = identities.get(i); - if (waitingListIdentity.equals(identity) ) { + Long waitingListIdentity = identities.get(i); + if (waitingListIdentity.equals(identity.getKey()) ) { return i+1;// '+1' because list begins with 0 } } @@ -1694,7 +1694,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD } @Override - public boolean isIdentityInBusinessGroup(Identity identity, BusinessGroup businessGroup) { + public boolean isIdentityInBusinessGroup(IdentityRef identity, BusinessGroupRef businessGroup) { if(businessGroup == null || identity == null) return false; List<String> roles = businessGroupRelationDAO.getRoles(identity, businessGroup); if(roles == null || roles.isEmpty() || (roles.size() == 1 && GroupRoles.waiting.name().equals(roles.get(0)))) { @@ -1703,6 +1703,11 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD return roles.size() > 0; } + @Override + public List<String> getIdentityRolesInBusinessGroup(IdentityRef identity, BusinessGroupRef businessGroup) { + return businessGroupRelationDAO.getRoles(identity, businessGroup); + } + @Override public List<BusinessGroupMembership> getBusinessGroupsMembership(Collection<BusinessGroup> businessGroups) { return businessGroupDAO.getBusinessGroupsMembership(businessGroups); diff --git a/src/test/java/org/olat/course/nodes/en/EnrollmentManagerTest.java b/src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java similarity index 98% rename from src/test/java/org/olat/course/nodes/en/EnrollmentManagerTest.java rename to src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java index 255181589fa..f2d1c643e6e 100644 --- a/src/test/java/org/olat/course/nodes/en/EnrollmentManagerTest.java +++ b/src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java @@ -82,9 +82,9 @@ import org.springframework.beans.factory.annotation.Autowired; * @author patrick * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class EnrollmentManagerTest extends OlatTestCase implements WindowControl { +public class EnrollmentManagerConcurrentTest extends OlatTestCase implements WindowControl { // - private static OLog log = Tracing.createLoggerFor(EnrollmentManagerTest.class); + private static OLog log = Tracing.createLoggerFor(EnrollmentManagerConcurrentTest.class); /* * ::Test Setup:: */ @@ -128,7 +128,7 @@ public class EnrollmentManagerTest extends OlatTestCase implements WindowControl log.info("TEST bgWithWaitingList.getMaxParticipants()=" + bgWithWaitingList.getMaxParticipants() ); log.info("TEST bgWithWaitingList.getWaitingListEnabled()=" + bgWithWaitingList.getWaitingListEnabled() ); // create mock objects - testTranslator = Util.createPackageTranslator(EnrollmentManagerTest.class, new Locale("de")); + testTranslator = Util.createPackageTranslator(EnrollmentManagerConcurrentTest.class, new Locale("de")); // Identities wg1 = JunitTestHelper.createAndPersistIdentityAsUser("wg1"); wg1Roles = securityManager.getRoles(wg1); @@ -147,7 +147,8 @@ public class EnrollmentManagerTest extends OlatTestCase implements WindowControl * Enroll 3 identities (group with max-size=2 and waiting-list). * Cancel enrollment. Check size after each step. */ - @Test public void testEnroll() throws Exception { + @Test + public void testEnroll() throws Exception { log.info("testEnroll: start..."); ENCourseNode enNode = new ENCourseNode(); OLATResourceable ores = OresHelper.createOLATResourceableTypeWithoutCheck("TestCourse"); @@ -299,7 +300,7 @@ public class EnrollmentManagerTest extends OlatTestCase implements WindowControl CoursePropertyManager coursePropertyManager = userCourseEnv.getCourseEnvironment().getCoursePropertyManager(); CourseGroupManager courseGroupManager = userCourseEnv.getCourseEnvironment().getCourseGroupManager(); - enrollmentManager.doEnroll(identity, JunitTestHelper.getUserRoles(), group, enNode, coursePropertyManager, EnrollmentManagerTest.this /*WindowControl mock*/, testTranslator, + enrollmentManager.doEnroll(identity, JunitTestHelper.getUserRoles(), group, enNode, coursePropertyManager, EnrollmentManagerConcurrentTest.this /*WindowControl mock*/, testTranslator, new ArrayList<Long>()/*enrollableGroupNames*/, new ArrayList<Long>()/*enrollableAreaNames*/, courseGroupManager); DBFactory.getInstance().commitAndCloseSession(); } catch (Exception e) { diff --git a/src/test/java/org/olat/course/nodes/en/EnrollmentManagerSerialTest.java b/src/test/java/org/olat/course/nodes/en/EnrollmentManagerSerialTest.java new file mode 100644 index 00000000000..6ff9a65ffe4 --- /dev/null +++ b/src/test/java/org/olat/course/nodes/en/EnrollmentManagerSerialTest.java @@ -0,0 +1,303 @@ +package org.olat.course.nodes.en; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.basesecurity.BaseSecurity; +import org.olat.basesecurity.GroupRoles; +import org.olat.core.commons.persistence.DB; +import org.olat.core.id.Identity; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.olat.group.area.BGArea; +import org.olat.group.area.BGAreaManager; +import org.olat.group.manager.BusinessGroupRelationDAO; +import org.olat.repository.RepositoryEntry; +import org.olat.test.JunitTestHelper; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 31.03.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class EnrollmentManagerSerialTest extends OlatTestCase { + + @Autowired + private DB dbInstance; + @Autowired + private BGAreaManager areaManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private EnrollmentManager enrollmentManager; + @Autowired + private BusinessGroupService businessGroupService; + @Autowired + private BusinessGroupRelationDAO businessGroupRelationDao; + + @Test + public void getEnrollmentRows_withWaitingList() { + Identity coach = JunitTestHelper.createAndPersistIdentityAsRndUser("en-coach-1"); + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-2"); + Identity waiter1 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-wait-3"); + Identity out = JunitTestHelper.createAndPersistIdentityAsRndUser("en-out-4"); + RepositoryEntry resource = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup group = businessGroupService.createBusinessGroup(coach, "en-1", "en-1", 0, 10, true, false, resource); + businessGroupRelationDao.addRole(participant1, group, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(participant2, group, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(waiter1, group, GroupRoles.waiting.name()); + Assert.assertNotNull(group); + dbInstance.commitAndCloseSession(); + + List<Long> groupKeys = new ArrayList<>(); + groupKeys.add(group.getKey()); + + //check participant 1 + List<EnrollmentRow> enrollments = enrollmentManager.getEnrollments(participant1, groupKeys, null, 128); + Assert.assertNotNull(enrollments); + Assert.assertEquals(1, enrollments.size()); + EnrollmentRow enrollment = enrollments.get(0); + Assert.assertEquals(group.getKey(), enrollment.getKey()); + Assert.assertEquals(group.getName(), enrollment.getName()); + Assert.assertEquals(2, enrollment.getNumOfParticipants()); + Assert.assertEquals(1, enrollment.getNumInWaitingList()); + Assert.assertTrue(enrollment.isParticipant()); + Assert.assertFalse(enrollment.isWaiting()); + + //check waiter + List<EnrollmentRow> waitingEnrollments = enrollmentManager.getEnrollments(waiter1, groupKeys, null, 128); + Assert.assertNotNull(waitingEnrollments); + Assert.assertEquals(1, waitingEnrollments.size()); + EnrollmentRow waitingEnrollment = waitingEnrollments.get(0); + Assert.assertEquals(group.getKey(), waitingEnrollment.getKey()); + Assert.assertEquals(group.getName(), waitingEnrollment.getName()); + Assert.assertEquals(2, waitingEnrollment.getNumOfParticipants()); + Assert.assertEquals(1, waitingEnrollment.getNumInWaitingList()); + Assert.assertEquals(1, waitingEnrollment.getPositionInWaitingList()); + Assert.assertFalse(waitingEnrollment.isParticipant()); + Assert.assertTrue(waitingEnrollment.isWaiting()); + + //check out + List<EnrollmentRow> outEnrollments = enrollmentManager.getEnrollments(out, groupKeys, null, 128); + Assert.assertNotNull(outEnrollments); + Assert.assertEquals(1, outEnrollments.size()); + EnrollmentRow outEnrollment = outEnrollments.get(0); + Assert.assertEquals(group.getKey(), outEnrollment.getKey()); + Assert.assertEquals(group.getName(), outEnrollment.getName()); + Assert.assertEquals(2, outEnrollment.getNumOfParticipants()); + Assert.assertEquals(1, outEnrollment.getNumInWaitingList()); + Assert.assertFalse(outEnrollment.isParticipant()); + Assert.assertFalse(outEnrollment.isWaiting()); + } + + @Test + public void getEnrollmentRows_withoutWaitingList() { + Identity coach = JunitTestHelper.createAndPersistIdentityAsRndUser("en-coach-1"); + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-2"); + Identity waiter1 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-wait-3"); + Identity out = JunitTestHelper.createAndPersistIdentityAsRndUser("en-out-4"); + RepositoryEntry resource = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup group = businessGroupService.createBusinessGroup(coach, "en-1", "en-1", 0, 10, false, false, resource); + + businessGroupRelationDao.addRole(participant1, group, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(participant2, group, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(waiter1, group, GroupRoles.waiting.name()); + Assert.assertNotNull(group); + dbInstance.commitAndCloseSession(); + + List<Long> groupKeys = new ArrayList<>(); + groupKeys.add(group.getKey()); + + //check participant 1 + List<EnrollmentRow> enrollments = enrollmentManager.getEnrollments(participant1, groupKeys, null, 128); + Assert.assertNotNull(enrollments); + Assert.assertEquals(1, enrollments.size()); + EnrollmentRow enrollment = enrollments.get(0); + Assert.assertEquals(group.getKey(), enrollment.getKey()); + Assert.assertEquals(group.getName(), enrollment.getName()); + Assert.assertEquals(2, enrollment.getNumOfParticipants()); + Assert.assertEquals(0, enrollment.getNumInWaitingList()); + Assert.assertTrue(enrollment.isParticipant()); + Assert.assertFalse(enrollment.isWaiting()); + + //check waiter (which not exists in enroll because the flag waiting list is set to false) + List<EnrollmentRow> waitingEnrollments = enrollmentManager.getEnrollments(waiter1, groupKeys, null, 128); + Assert.assertNotNull(waitingEnrollments); + Assert.assertEquals(1, waitingEnrollments.size()); + EnrollmentRow waitingEnrollment = waitingEnrollments.get(0); + Assert.assertEquals(group.getKey(), waitingEnrollment.getKey()); + Assert.assertEquals(group.getName(), waitingEnrollment.getName()); + Assert.assertEquals(2, waitingEnrollment.getNumOfParticipants()); + Assert.assertEquals(0, waitingEnrollment.getNumInWaitingList()); + Assert.assertEquals(-1, waitingEnrollment.getPositionInWaitingList()); + Assert.assertFalse(waitingEnrollment.isParticipant()); + Assert.assertFalse(waitingEnrollment.isWaiting()); + + //check out + List<EnrollmentRow> outEnrollments = enrollmentManager.getEnrollments(out, groupKeys, null, 128); + Assert.assertNotNull(outEnrollments); + Assert.assertEquals(1, outEnrollments.size()); + EnrollmentRow outEnrollment = outEnrollments.get(0); + Assert.assertEquals(group.getKey(), outEnrollment.getKey()); + Assert.assertEquals(group.getName(), outEnrollment.getName()); + Assert.assertEquals(2, outEnrollment.getNumOfParticipants()); + Assert.assertEquals(0, outEnrollment.getNumInWaitingList()); + Assert.assertFalse(outEnrollment.isParticipant()); + Assert.assertFalse(outEnrollment.isWaiting()); + } + + @Test + public void getEnrollmentRows_withAreas() { + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("en-area-1"); + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-2"); + Identity participant3 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-3"); + Identity participant4 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-4"); + Identity participant5 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-part-5"); + Identity waiter1 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-wait-3"); + Identity waiter2 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-wait-4"); + Identity waiter3 = JunitTestHelper.createAndPersistIdentityAsRndUser("en-wait-5"); + + //create a resource, an area, a group + RepositoryEntry resource = JunitTestHelper.createAndPersistRepositoryEntry(); + String areaName = UUID.randomUUID().toString(); + BGArea area = areaManager.createAndPersistBGArea("en-area-" + areaName, "description:" + areaName, resource.getOlatResource()); + BusinessGroup group1 = businessGroupService.createBusinessGroup(null, "en-area-group", "area-group-desc", 0, 10, false, false, resource); + BusinessGroup group2 = businessGroupService.createBusinessGroup(null, "en-group-2", "area-group-desc", 0, 10, true, false, resource); + BusinessGroup group3 = businessGroupService.createBusinessGroup(null, "en-group-3", "area-group-desc", 0, 10, true, false, resource); + + businessGroupRelationDao.addRole(participant1, group1, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(participant2, group2, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(participant3, group2, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(participant4, group2, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(participant5, group2, GroupRoles.participant.name()); + businessGroupRelationDao.addRole(waiter1, group2, GroupRoles.waiting.name()); + businessGroupRelationDao.addRole(waiter2, group2, GroupRoles.waiting.name()); + businessGroupRelationDao.addRole(waiter3, group2, GroupRoles.waiting.name()); + + areaManager.addBGToBGArea(group1, area); + areaManager.addBGToBGArea(group2, area); + dbInstance.commitAndCloseSession(); + + List<Long> groupKeys = new ArrayList<>(); + groupKeys.add(group2.getKey()); + groupKeys.add(group3.getKey()); + List<Long> areaKeys = new ArrayList<>(); + areaKeys.add(area.getKey()); + + //check id enrollments + List<EnrollmentRow> idEnrollments = enrollmentManager.getEnrollments(id, groupKeys, areaKeys, 128); + Assert.assertNotNull(idEnrollments); + Assert.assertEquals(3, idEnrollments.size()); + + //check enrollment group 1 + EnrollmentRow enrollment1 = getEnrollmentRowFor(group1, idEnrollments); + Assert.assertEquals(group1.getKey(), enrollment1.getKey()); + Assert.assertEquals(group1.getName(), enrollment1.getName()); + Assert.assertEquals(1, enrollment1.getNumOfParticipants()); + Assert.assertEquals(0, enrollment1.getNumInWaitingList()); + Assert.assertFalse(enrollment1.isParticipant()); + Assert.assertFalse(enrollment1.isWaiting()); + + //check enrollment group 2 + EnrollmentRow enrollment2 = getEnrollmentRowFor(group2, idEnrollments); + Assert.assertEquals(group2.getKey(), enrollment2.getKey()); + Assert.assertEquals(group2.getName(), enrollment2.getName()); + Assert.assertEquals(4, enrollment2.getNumOfParticipants()); + Assert.assertEquals(3, enrollment2.getNumInWaitingList()); + Assert.assertFalse(enrollment2.isParticipant()); + Assert.assertFalse(enrollment2.isWaiting()); + + //check enrollment group 3 + EnrollmentRow enrollment3 = getEnrollmentRowFor(group3, idEnrollments); + Assert.assertEquals(group3.getKey(), enrollment3.getKey()); + Assert.assertEquals(group3.getName(), enrollment3.getName()); + Assert.assertEquals(0, enrollment3.getNumOfParticipants()); + Assert.assertEquals(0, enrollment3.getNumInWaitingList()); + Assert.assertFalse(enrollment3.isParticipant()); + Assert.assertFalse(enrollment3.isWaiting()); + + + //check enrollments of participant5 + List<EnrollmentRow> part5Enrollments = enrollmentManager.getEnrollments(participant5, groupKeys, areaKeys, 128); + Assert.assertNotNull(part5Enrollments); + Assert.assertEquals(3, part5Enrollments.size()); + + EnrollmentRow enrollment2_w5 = getEnrollmentRowFor(group2, part5Enrollments); + Assert.assertEquals(group2.getKey(), enrollment2_w5.getKey()); + Assert.assertEquals(group2.getName(), enrollment2_w5.getName()); + Assert.assertEquals(4, enrollment2_w5.getNumOfParticipants()); + Assert.assertEquals(3, enrollment2_w5.getNumInWaitingList()); + Assert.assertTrue(enrollment2_w5.isParticipant()); + Assert.assertFalse(enrollment2_w5.isWaiting()); + + + //check enrollments of waiter 3 + List<EnrollmentRow> wait3Enrollments = enrollmentManager.getEnrollments(waiter3, groupKeys, areaKeys, 128); + Assert.assertNotNull(wait3Enrollments); + Assert.assertEquals(3, wait3Enrollments.size()); + + EnrollmentRow enrollment2_p3 = getEnrollmentRowFor(group2, wait3Enrollments); + Assert.assertEquals(group2.getKey(), enrollment2_p3.getKey()); + Assert.assertEquals(group2.getName(), enrollment2_p3.getName()); + Assert.assertEquals(4, enrollment2_p3.getNumOfParticipants()); + Assert.assertEquals(3, enrollment2_p3.getNumInWaitingList()); + Assert.assertFalse(enrollment2_p3.isParticipant()); + Assert.assertTrue(enrollment2_p3.isWaiting()); + } + + private EnrollmentRow getEnrollmentRowFor(BusinessGroup group, List<EnrollmentRow> enrollments) { + if(enrollments == null || enrollments.isEmpty()) return null; + + EnrollmentRow row = null; + for(EnrollmentRow enrollment:enrollments) { + if(enrollment.getKey().equals(group.getKey())) { + row = enrollment; + } + } + + return row; + } + + /** + * Test the bevahior with no data. It's important because some of the value returned + * by the database can be null, and this is database dependant. + */ + @Test + public void getEnrollmentRows_null() { + Identity dummy = JunitTestHelper.createAndPersistIdentityAsRndUser("en-dummy-1"); + dbInstance.commitAndCloseSession(); + + //null + List<EnrollmentRow> nullEnrollments = enrollmentManager.getEnrollments(dummy, null, null, 128); + Assert.assertNotNull(nullEnrollments); + Assert.assertEquals(0, nullEnrollments.size()); + + //wrong keys + List<Long> groupKeys = new ArrayList<>(); + groupKeys.add(27l); + List<Long> areaKeys = new ArrayList<>(); + areaKeys.add(27l); + + List<EnrollmentRow> groupEnrollments = enrollmentManager.getEnrollments(dummy, groupKeys, null, 128); + Assert.assertNotNull(groupEnrollments); + Assert.assertEquals(0, groupEnrollments.size()); + + List<EnrollmentRow> areaEnrollments = enrollmentManager.getEnrollments(dummy, null, areaKeys, 128); + Assert.assertNotNull(areaEnrollments); + Assert.assertEquals(0, areaEnrollments.size()); + + List<EnrollmentRow> groupAndAreaEnrollments = enrollmentManager.getEnrollments(dummy, groupKeys, areaKeys, 128); + Assert.assertNotNull(groupAndAreaEnrollments); + Assert.assertEquals(0, groupAndAreaEnrollments.size()); + } + +} diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index a1a51517ef2..a204b45e151 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -122,7 +122,8 @@ import org.junit.runners.Suite; org.olat.instantMessaging.InstantMessagePreferencesDAOTest.class, org.olat.instantMessaging.RosterDAOTest.class, org.olat.instantMessaging.InstantMessageServiceTest.class, - org.olat.course.nodes.en.EnrollmentManagerTest.class, + org.olat.course.nodes.en.EnrollmentManagerSerialTest.class, + org.olat.course.nodes.en.EnrollmentManagerConcurrentTest.class, org.olat.course.nodes.gta.manager.GTAManagerTest.class, org.olat.course.assessment.AssessmentManagerTest.class, org.olat.course.assessment.manager.UserCourseInformationsManagerTest.class, -- GitLab