From 1a61f3da9a3fdc8705c8d0d5da72e9eb2b3540cb Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Tue, 23 Aug 2016 14:45:55 +0200 Subject: [PATCH] OO-1593: rework small statistics panel of assessment tool, anonymize selft test archive, rs, wording --- .../assessment/AssessmentToolManager.java | 3 +- .../manager/AssessmentToolManagerImpl.java | 69 +++++++++++------ ...ssmentCourseStatisticsSmallController.java | 32 +++++--- .../ui/tool/_content/course_stats_small.html | 14 +++- .../ui/tool/_i18n/LocalStrings_de.properties | 1 + .../ui/tool/_i18n/LocalStrings_en.properties | 1 + .../nodes/iq/IQConfigurationController.java | 14 +++- .../iq/QTI21AssessmentRunController.java | 2 +- .../export/Archive_2_UserSelectionStep.java | 19 +++++ .../manager/archive/QTI21ArchiveFormat.java | 76 +++++++++++++------ .../qti21/model/InMemoryOutcomeListener.java | 41 ++++++++++ .../handlers/QTI21AssessmentTestHandler.java | 2 +- .../ui/AssessmentTestDisplayController.java | 8 +- .../ui/QTI21AssessmentDetailsController.java | 3 + .../ui/QTI21RetrieveTestsToolController.java | 3 + .../QTI21StatisticsSecurityCallback.java | 19 +++++ .../model/AssessmentMembersStatistics.java | 61 +++++++++++++++ .../AssessmentStatisticsSmallController.java | 24 ++++-- .../ui/_content/test_stats_small.html | 12 ++- .../ui/_i18n/LocalStrings_de.properties | 1 + .../ui/_i18n/LocalStrings_en.properties | 1 + 21 files changed, 321 insertions(+), 85 deletions(-) create mode 100644 src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java create mode 100644 src/main/java/org/olat/modules/assessment/model/AssessmentMembersStatistics.java diff --git a/src/main/java/org/olat/course/assessment/AssessmentToolManager.java b/src/main/java/org/olat/course/assessment/AssessmentToolManager.java index 3cd610db434..321223ad2bd 100644 --- a/src/main/java/org/olat/course/assessment/AssessmentToolManager.java +++ b/src/main/java/org/olat/course/assessment/AssessmentToolManager.java @@ -28,6 +28,7 @@ import org.olat.course.assessment.model.AssessmentStatistics; import org.olat.course.assessment.model.SearchAssessedIdentityParams; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.model.AssessmentEntryStatus; +import org.olat.modules.assessment.model.AssessmentMembersStatistics; /** * The manager taylored for the assessment tool. @@ -59,7 +60,7 @@ public interface AssessmentToolManager { * @param params * @return */ - public int getNumberOfParticipants(Identity coach, SearchAssessedIdentityParams params); + public AssessmentMembersStatistics getNumberOfParticipants(Identity coach, SearchAssessedIdentityParams params); /** * The number of user who launched the course / resource diff --git a/src/main/java/org/olat/course/assessment/manager/AssessmentToolManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/AssessmentToolManagerImpl.java index 57cd72a5e75..0c99e15b13a 100644 --- a/src/main/java/org/olat/course/assessment/manager/AssessmentToolManagerImpl.java +++ b/src/main/java/org/olat/course/assessment/manager/AssessmentToolManagerImpl.java @@ -20,7 +20,9 @@ package org.olat.course.assessment.manager; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.persistence.TypedQuery; @@ -37,6 +39,7 @@ import org.olat.course.assessment.model.AssessmentStatistics; import org.olat.course.assessment.model.SearchAssessedIdentityParams; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.model.AssessmentEntryStatus; +import org.olat.modules.assessment.model.AssessmentMembersStatistics; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -69,56 +72,74 @@ public class AssessmentToolManagerImpl implements AssessmentToolManager { @Override - public int getNumberOfParticipants(Identity coach, SearchAssessedIdentityParams params) { + public AssessmentMembersStatistics getNumberOfParticipants(Identity coach, SearchAssessedIdentityParams params) { RepositoryEntry courseEntry = params.getEntry(); + int loggedIn = 0; + int numOfOtherUsers = 0; int numOfParticipants = 0; + int participantLoggedIn = 0; + + StringBuilder sc = new StringBuilder(); + sc.append("select infos.identity.key from usercourseinfos as infos ") + .append(" inner join infos.resource as infosResource on (infosResource.key=:resourceKey)"); + List<Long> allKeys = dbInstance.getCurrentEntityManager() + .createQuery(sc.toString(), Long.class) + .setParameter("resourceKey", courseEntry.getOlatResource().getKey()) + .getResultList(); + if(params.isAdmin()) { StringBuilder sb = new StringBuilder(); - sb.append("select count(participant.identity.key) from repoentrytogroup as rel") + sb.append("select participant.identity.key from repoentrytogroup as rel") .append(" inner join rel.group as bGroup") .append(" inner join bGroup.members as participant on (participant.role='").append(GroupRoles.participant.name()).append("')") .append(" where rel.entry.key=:repoEntryKey)"); - List<Number> count = dbInstance.getCurrentEntityManager() - .createQuery(sb.toString(), Number.class) + List<Long> keys = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) .setParameter("repoEntryKey", courseEntry.getKey()) .getResultList(); - numOfParticipants = count == null || count.isEmpty() || count.get(0) == null ? 0 : count.get(0).intValue(); + Set<Long> participantKeys = new HashSet<>(keys); + numOfParticipants = participantKeys.size(); + Set<Long> participantLoggedInKeys = new HashSet<>(allKeys); + participantLoggedInKeys.retainAll(participantKeys); + participantLoggedIn = participantLoggedInKeys.size(); + //count the users which login but are not members of the course if(params.isNonMembers()) { - StringBuilder sc = new StringBuilder(); - sc.append("select count(infos.key) from usercourseinfos as infos ") - .append(" inner join infos.resource as infosResource on (infosResource.key=:resourceKey)") - .append(" where not exists (select membership.identity from repoentrytogroup as rel, bgroupmember as membership") - .append(" where rel.entry.key=:repoEntryKey and rel.group.key=membership.group.key and membership.identity.key=infos.identity.key") - .append(" )"); - - List<Number> countAlt = dbInstance.getCurrentEntityManager() - .createQuery(sc.toString(), Number.class) - .setParameter("repoEntryKey", courseEntry.getKey()) - .setParameter("resourceKey", courseEntry.getOlatResource().getKey()) - .getResultList(); - numOfParticipants += countAlt == null || countAlt.isEmpty() || countAlt.get(0) == null ? 0 : countAlt.get(0).intValue(); + Set<Long> allLoggedInKeys = new HashSet<>(allKeys); + allLoggedInKeys.removeAll(participantLoggedInKeys); + loggedIn = allLoggedInKeys.size(); + allLoggedInKeys.removeAll(participantKeys); + numOfOtherUsers = allLoggedInKeys.size(); + } else { + loggedIn = participantLoggedIn; } - } else if(params.isBusinessGroupCoach() || params.isRepositoryEntryCoach()) { StringBuilder sb = new StringBuilder(); - sb.append("select count(participant.identity.key) from repoentrytogroup as rel") + sb.append("select participant.identity.key from repoentrytogroup as rel") .append(" inner join rel.group as bGroup") .append(" inner join bGroup.members as coach on (coach.identity.key=:identityKey and coach.role='").append(GroupRoles.coach.name()).append("')") .append(" inner join bGroup.members as participant on (participant.role='").append(GroupRoles.participant.name()).append("')") .append(" where rel.entry.key=:repoEntryKey)"); - List<Number> count = dbInstance.getCurrentEntityManager() - .createQuery(sb.toString(), Number.class) + List<Long> keys = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) .setParameter("identityKey", coach.getKey()) .setParameter("repoEntryKey", courseEntry.getKey()) .getResultList(); - numOfParticipants = count == null || count.isEmpty() || count.get(0) == null ? 0 : count.get(0).intValue(); + + Set<Long> participantKeys = new HashSet<>(keys); + numOfParticipants = participantKeys.size(); + + Set<Long> participantLoggedInKeys = new HashSet<>(allKeys); + participantLoggedInKeys.retainAll(participantKeys); + participantLoggedIn = participantLoggedInKeys.size(); + + loggedIn = participantLoggedIn; } - return numOfParticipants; + return new AssessmentMembersStatistics(numOfParticipants, participantLoggedIn, numOfOtherUsers, loggedIn); } @Override diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java index ea7e34e0f47..c53ccf8d03f 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java @@ -31,6 +31,7 @@ import org.olat.course.assessment.AssessmentHelper; import org.olat.course.assessment.AssessmentToolManager; import org.olat.course.assessment.model.AssessmentStatistics; import org.olat.course.assessment.model.SearchAssessedIdentityParams; +import org.olat.modules.assessment.model.AssessmentMembersStatistics; import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -50,8 +51,8 @@ public class AssessmentCourseStatisticsSmallController extends BasicController { private int numOfPassed; private int numOfFailed; - private int numOfParticipants; private int numOfAssessedIdentities; + private AssessmentMembersStatistics memberStatistics; @Autowired private AssessmentToolManager assessmentToolManager; @@ -79,8 +80,8 @@ public class AssessmentCourseStatisticsSmallController extends BasicController { return numOfAssessedIdentities; } - public int getNumOfParticipants() { - return numOfParticipants; + public AssessmentMembersStatistics getMemberStatistics() { + return memberStatistics; } public void updateStatistics() { @@ -91,24 +92,33 @@ public class AssessmentCourseStatisticsSmallController extends BasicController { numOfAssessedIdentities = assessmentToolManager.getNumberOfAssessedIdentities(getIdentity(), params); mainVC.contextPut("numOfAssessedIdentities", numOfAssessedIdentities); - numOfParticipants = assessmentToolManager.getNumberOfParticipants(getIdentity(), params); - mainVC.contextPut("numOfParticipants", numOfParticipants); + memberStatistics = assessmentToolManager.getNumberOfParticipants(getIdentity(), params); + mainVC.contextPut("numOfParticipants", memberStatistics.getNumOfParticipants()); + if(assessmentCallback.canAssessNonMembers()) { + mainVC.contextPut("numOfOtherUsers", memberStatistics.getNumOfOtherUsers()); + } AssessmentStatistics stats = assessmentToolManager.getStatistics(getIdentity(), params); mainVC.contextPut("scoreAverage", AssessmentHelper.getRoundedScore(stats.getAverageScore())); numOfPassed = stats.getCountPassed(); mainVC.contextPut("numOfPassed", numOfPassed); - int percentPassed = numOfParticipants == 0 ? 0 : - Math.round(100.0f * (stats.getCountPassed() / numOfParticipants)); + + int total = memberStatistics.getTotal(); + int percentPassed = total == 0 ? 0 : + Math.round(100.0f * (stats.getCountPassed() / total)); mainVC.contextPut("percentPassed", percentPassed); numOfFailed = stats.getCountFailed(); mainVC.contextPut("numOfFailed", numOfFailed); - int percentFailed = numOfParticipants == 0 ? 0 : - Math.round(100.0f * (stats.getCountFailed() / numOfParticipants)); + int percentFailed = total == 0 ? 0 : + Math.round(100.0f * (stats.getCountFailed() / total)); mainVC.contextPut("percentFailed", percentFailed); - int numOfLaunches = assessmentToolManager.getNumberOfInitialLaunches(getIdentity(), params); - mainVC.contextPut("numOfInitialLaunch", numOfLaunches); + int numOfParticipantLaunches = memberStatistics.getNumOfParticipantsLoggedIn(); + mainVC.contextPut("numOfParticipantLaunches", numOfParticipantLaunches); + if(assessmentCallback.canAssessNonMembers()) { + int numOfOtherUserLaunches = memberStatistics.getLoggedIn(); + mainVC.contextPut("numOfOtherUserLaunches", numOfOtherUserLaunches); + } } @Override diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/course_stats_small.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/course_stats_small.html index 2f044dd1b81..a1e23d2d87e 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_content/course_stats_small.html +++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/course_stats_small.html @@ -7,17 +7,23 @@ <th>$r.translate("table.header.numOfParticipants")</th> <td>$numOfParticipants</td> </tr> + #if($r.isNotNull($numOfOtherUsers)) + <tr> + <th>$r.translate("table.header.numOfOtherUsers")</th> + <td>$numOfOtherUsers</td> + </tr> + #end <tr> <th>$r.translate("table.header.numOfInitialLaunch")</th> <td> - #if($numOfInitialLaunch && $numOfInitialLaunch == 0) - <i class="o_icon o_green_led"> </i> - #elseif($numOfInitialLaunch && $numOfInitialLaunch == $numOfParticipants) + #if($r.isNotNull($numOfParticipantLaunches) && $numOfParticipantLaunches == 0) <i class="o_icon o_red_led"> </i> + #elseif($r.isNotNull($numOfParticipantLaunches) && $numOfParticipantLaunches == $numOfParticipants) + <i class="o_icon o_green_led"> </i> #else <i class="o_icon o_yellow_led"> </i> #end - $numOfInitialLaunch</td> + $numOfParticipantLaunches #if($r.isNotNull($numOfOtherUserLaunches))/ $numOfOtherUserLaunches#end</td> </tr> <tr> <th>$r.translate("table.header.scoreAverage")</th> diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties index 9152de27ffd..c4373f11906 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties @@ -45,6 +45,7 @@ table.header.numOfAssessedIdentities=$org.olat.modules.assessment.ui\:table.head table.header.numOfInitialLaunch=$org.olat.modules.assessment.ui\:table.header.numOfInitialLaunch table.header.numOfParticipants=$org.olat.modules.assessment.ui\:table.header.numOfParticipants table.header.numOfPassed=$org.olat.modules.assessment.ui\:table.header.numOfPassed +table.header.numOfOtherUsers=# andere Benutzer table.header.passed=Bestanden table.header.scoreAverage=$org.olat.modules.assessment.ui\:table.header.scoreAverage tooltip.of={0} von {1} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties index 14425cadf6c..1569c6c11b1 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties @@ -45,6 +45,7 @@ table.header.numOfAssessedIdentities=$org.olat.modules.assessment.ui\:table.head table.header.numOfInitialLaunch=$org.olat.modules.assessment.ui\:table.header.numOfInitialLaunch table.header.numOfParticipants=$org.olat.modules.assessment.ui\:table.header.numOfParticipants table.header.numOfPassed=$org.olat.modules.assessment.ui\:table.header.numOfPassed +table.header.numOfOtherUsers=# other users table.header.passed=Passed table.header.scoreAverage=$org.olat.modules.assessment.ui\:table.header.scoreAverage tooltip.of={0} of {1} diff --git a/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java b/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java index 01cb2f0f580..6242f5f49ce 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java @@ -376,11 +376,19 @@ public class IQConfigurationController extends BasicController { RepositoryEntry re = getIQReference(moduleConfiguration, false); if(re != null && !OnyxModule.isOnyxTest(re.getOlatResource())) { + Controller previewController; if(ImsQTI21Resource.TYPE_NAME.equals(re.getOlatResource().getResourceableTypeName())) { - //TODO + //TODO qti + /* need to clean up the assessment test session + QTI21DeliveryOptions deliveryOptions = qti21service.getDeliveryOptions(re); + RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + previewController = new AssessmentTestDisplayController(ureq, getWindowControl(), new InMemoryOutcomeListener(), + re, courseEntry, courseNode.getIdent(), + deliveryOptions, true, true, true); + */ } else { long courseResId = course.getResourceableId().longValue(); - Controller previewController = iqManager.createIQDisplayController(moduleConfiguration, new IQPreviewSecurityCallback(), ureq, getWindowControl(), courseResId, courseNode.getIdent(), null); + previewController = iqManager.createIQDisplayController(moduleConfiguration, new IQPreviewSecurityCallback(), ureq, getWindowControl(), courseResId, courseNode.getIdent(), null); previewLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), previewController); stackPanel.pushController(translate("preview"), previewLayoutCtr); } @@ -407,7 +415,7 @@ public class IQConfigurationController extends BasicController { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(searchController); - String[] types = new String[]{ TestFileResource.TYPE_NAME }; + String[] types = new String[]{ TestFileResource.TYPE_NAME, ImsQTI21Resource.TYPE_NAME }; searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseTest")); listenTo(searchController); diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java index 9f58c130167..056b7ef967f 100644 --- a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java @@ -298,7 +298,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen WindowControl bwControl = addToHistory(ureq, ores, null); RepositoryEntry courseRe = userCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); - displayCtrl = new AssessmentTestDisplayController(ureq, bwControl, this, testEntry, courseRe, courseNode.getIdent(), deliveryOptions, true, false); + displayCtrl = new AssessmentTestDisplayController(ureq, bwControl, this, testEntry, courseRe, courseNode.getIdent(), deliveryOptions, true, false, false); listenTo(displayCtrl); if(displayCtrl.isTerminated()) { //do nothing diff --git a/src/main/java/org/olat/ims/qti/export/Archive_2_UserSelectionStep.java b/src/main/java/org/olat/ims/qti/export/Archive_2_UserSelectionStep.java index 65483709267..77e2ef49d85 100644 --- a/src/main/java/org/olat/ims/qti/export/Archive_2_UserSelectionStep.java +++ b/src/main/java/org/olat/ims/qti/export/Archive_2_UserSelectionStep.java @@ -1,3 +1,22 @@ +/** + * <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.ims.qti.export; import org.olat.core.gui.UserRequest; diff --git a/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java b/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java index 772beff1a8a..0d72f4521f8 100644 --- a/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java +++ b/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java @@ -51,10 +51,12 @@ import org.olat.core.util.openxml.OpenXMLWorksheet; import org.olat.core.util.openxml.OpenXMLWorksheet.Row; import org.olat.core.util.openxml.workbookstyle.CellStyle; import org.olat.course.CourseFactory; +import org.olat.course.ICourse; import org.olat.course.nodes.CourseNode; import org.olat.fileresource.FileResourceManager; import org.olat.ims.qti.export.QTIArchiver; import org.olat.ims.qti.export.QTIExportFormatter; +import org.olat.ims.qti.export.helper.IdentityAnonymizerCallback; import org.olat.ims.qti21.AssessmentItemSession; import org.olat.ims.qti21.AssessmentResponse; import org.olat.ims.qti21.AssessmentTestSession; @@ -129,6 +131,7 @@ public class QTI21ArchiveFormat { private ResolvedAssessmentTest resolvedAssessmentTest; private List<UserPropertyHandler> userPropertyHandlers; + private IdentityAnonymizerCallback anonymizerCallback; private boolean participants; private boolean allUsers; @@ -193,12 +196,17 @@ public class QTI21ArchiveFormat { File unzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource()); resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false); - CourseNode courseNode = CourseFactory.loadCourse(courseEntry).getRunStructure().getNode(subIdent); + ICourse course = CourseFactory.loadCourse(courseEntry); + CourseNode courseNode = course.getRunStructure().getNode(subIdent); String label = courseNode.getType() + "_" + StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + ".xlsx"; + if("iqself".equals(courseNode.getType())) { + anonymizerCallback = course.getCourseEnvironment().getCoursePropertyManager(); + } + export(courseEntry, subIdent, testEntry, label, exportStream); } @@ -213,7 +221,7 @@ public class QTI21ArchiveFormat { export(null, null, testEntry, archiveName, exportStream); } - private void export(RepositoryEntry courseEntry, String subIdent, RepositoryEntry testEntry, String filename, ZipOutputStream exportStream) { + private void export(RepositoryEntry courseEntry, String subIdent, RepositoryEntry testEntry, String filename, ZipOutputStream exportStream) { //content final List<AssessmentResponse> responses = responseDao.getResponse(courseEntry, subIdent, testEntry, participants, allUsers, anonymUsers); try { @@ -240,12 +248,17 @@ public class QTI21ArchiveFormat { File unzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource()); resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false); - CourseNode courseNode = CourseFactory.loadCourse(courseEntry).getRunStructure().getNode(subIdent); + ICourse course = CourseFactory.loadCourse(courseEntry); + CourseNode courseNode = course.getRunStructure().getNode(subIdent); String label = courseNode.getType() + "_" + StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + ".xlsx"; + if("iqself".equals(courseNode.getType())) { + anonymizerCallback = course.getCourseEnvironment().getCoursePropertyManager(); + } + //content final List<AssessmentResponse> responses = responseDao.getResponse(courseEntry, subIdent, testEntry, participants, allUsers, anonymUsers); @@ -271,12 +284,16 @@ public class QTI21ArchiveFormat { //first header Row header1Row = exportSheet.newRow(); int col = 1; - for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { - if (userPropertyHandler != null) { - col++; + if(anonymizerCallback != null) { + col += 5;// anonymized name -> test duration + } else { + for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { + if (userPropertyHandler != null) { + col++; + } } + col += 5;// homepage -> test duration } - col += 5;// homepage -> test duration List<ItemInfos> infos = getItemInfos(); for(int i=0; i<infos.size(); i++) { @@ -298,20 +315,24 @@ public class QTI21ArchiveFormat { Row header2Row = exportSheet.newRow(); String sequentialNumber = translator.translate("column.header.seqnum"); header2Row.addCell(col++, sequentialNumber, headerStyle); - - for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { - if (userPropertyHandler == null) { - continue; + + if(anonymizerCallback != null) { + col++; + } else { + for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { + if (userPropertyHandler == null) { + continue; + } + String header = translator.translate(userPropertyHandler.i18nFormElementLabelKey()); + header2Row.addCell(col++, header, headerStyle); } - String header = translator.translate(userPropertyHandler.i18nFormElementLabelKey()); - header2Row.addCell(col++, header, headerStyle); + + // add other user and session information + header2Row.addCell(col++, translator.translate("column.header.homepage"), headerStyle); } - // add other user and session information - header2Row.addCell(col++, translator.translate("column.header.homepage"), headerStyle); header2Row.addCell(col++, translator.translate("column.header.assesspoints"), headerStyle); header2Row.addCell(col++, translator.translate("column.header.passed"), headerStyle); - //header2Row.addCell(col++, translator.translate("column.header.ipaddress"), headerStyle); header2Row.addCell(col++, translator.translate("column.header.date"), headerStyle); header2Row.addCell(col++, translator.translate("column.header.duration"), headerStyle); @@ -370,6 +391,9 @@ public class QTI21ArchiveFormat { } } } + } else if(anonymizerCallback != null) { + String anonymizedName = anonymizerCallback.getAnonymizedUserName(entry.getIdentity()); + dataRow.addCell(col++, anonymizedName, null); } else { User assessedUser = entry.getIdentity().getUser(); for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { @@ -380,16 +404,19 @@ public class QTI21ArchiveFormat { } } - //homepage, assesspoints, passed, ipaddress, date, duration - String homepage; - if(entry.getIdentity() == null) { - homepage = ""; - } else { - ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(entry.getIdentity()); - homepage = BusinessControlFactory.getInstance().getAsURIString(Collections.singletonList(ce), false); + //homepage + if(anonymizerCallback == null) { + String homepage; + if(entry.getIdentity() == null) { + homepage = ""; + } else { + ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(entry.getIdentity()); + homepage = BusinessControlFactory.getInstance().getAsURIString(Collections.singletonList(ce), false); + } + dataRow.addCell(col++, homepage, null); } - dataRow.addCell(col++, homepage, null); + //assesspoints, passed, ipaddress, date, duration if(testSession.getScore() != null) { dataRow.addCell(col++, testSession.getScore(), null); } else { @@ -400,7 +427,6 @@ public class QTI21ArchiveFormat { } else { col++; } - //dataRow.addCell(col++, "0.0.0.1", null); dataRow.addCell(col++, testSession.getCreationDate(), workbook.getStyles().getDateStyle()); dataRow.addCell(col++, toDurationInMilliseconds(testSession.getDuration()), null); diff --git a/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java b/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java new file mode 100644 index 00000000000..a5b7cab32cb --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java @@ -0,0 +1,41 @@ +/** + * <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.ims.qti21.model; + +import org.olat.ims.qti21.OutcomesListener; + +/** + * + * Initial date: 23.08.2016<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class InMemoryOutcomeListener implements OutcomesListener { + + @Override + public void updateOutcomes(Float score, Boolean pass) { + // + } + + @Override + public void submit(Float score, Boolean pass, Long assessmentId) { + // + } +} diff --git a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java index 2a6e7697f17..f24b2d95141 100644 --- a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java +++ b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java @@ -359,7 +359,7 @@ public class QTI21AssessmentTestHandler extends FileHandler { boolean authorMode = reSecurity.isEntryAdmin(); CoreSpringFactory.getImpl(UserCourseInformationsManager.class) .updateUserCourseInformations(entry.getOlatResource(), uureq.getIdentity()); - return new AssessmentTestDisplayController(uureq, wwControl, null, entry, entry, null, options, false, authorMode); + return new AssessmentTestDisplayController(uureq, wwControl, null, entry, entry, null, options, false, authorMode, false); } }); } diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java index c0411bfe803..ca35367be3e 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -197,7 +197,7 @@ public class AssessmentTestDisplayController extends BasicController implements */ public AssessmentTestDisplayController(UserRequest ureq, WindowControl wControl, OutcomesListener listener, RepositoryEntry testEntry, RepositoryEntry entry, String subIdent, QTI21DeliveryOptions deliveryOptions, - boolean showCloseResults, boolean authorMode) { + boolean showCloseResults, boolean authorMode, boolean anonym) { super(ureq, wControl); this.entry = entry; @@ -208,12 +208,12 @@ public class AssessmentTestDisplayController extends BasicController implements this.showCloseResults = showCloseResults; UserSession usess = ureq.getUserSession(); - if(usess.getRoles().isGuestOnly()) { - anonym = true; + if(usess.getRoles().isGuestOnly() || anonym) { + this.anonym = anonym; assessedIdentity = null; anonymousIdentifier = getAnonymousIdentifier(usess); } else { - anonym = false; + this.anonym = anonym; assessedIdentity = getIdentity(); anonymousIdentifier = null; } diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java index 85202d5b450..96b1b920093 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -265,6 +265,9 @@ public class QTI21AssessmentDetailsController extends FormBasicController { } private void doPullSession(AssessmentTestSession session) { + if(session.getFinishTime() == null) { + session.setFinishTime(new Date()); + } session.setTerminationTime(new Date()); session = qtiService.updateAssessmentTestSession(session); dbInstance.commit();//make sure that the changes committed before sending the event diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java index 340b6839159..c6d6bae4b04 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java @@ -161,6 +161,9 @@ public class QTI21RetrieveTestsToolController extends BasicController implements } private void doRetrieveTest(AssessmentTestSession session) { + if(session.getFinishTime() == null) { + session.setFinishTime(new Date()); + } session.setTerminationTime(new Date()); session = qtiService.updateAssessmentTestSession(session); dbInstance.commit();//make sure that the changes committed before sending the event diff --git a/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsSecurityCallback.java b/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsSecurityCallback.java index 31a05ab29a5..332ecadcd4b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsSecurityCallback.java +++ b/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsSecurityCallback.java @@ -1,3 +1,22 @@ +/** + * <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.ims.qti21.ui.statistics; /** diff --git a/src/main/java/org/olat/modules/assessment/model/AssessmentMembersStatistics.java b/src/main/java/org/olat/modules/assessment/model/AssessmentMembersStatistics.java new file mode 100644 index 00000000000..035413ea65a --- /dev/null +++ b/src/main/java/org/olat/modules/assessment/model/AssessmentMembersStatistics.java @@ -0,0 +1,61 @@ +/** + * <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.modules.assessment.model; + +/** + * + * Initial date: 23.08.2016<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AssessmentMembersStatistics { + + private final int numOfParticipants; + private final int numOfOtherUsers; + private final int loggedIn; + private final int numOfParticipantsLoggedIn; + + public AssessmentMembersStatistics(int numOfParticipants, int numOfParticipantsLoggedIn, int numOfOtherUsers, int loggedIn) { + this.numOfParticipants = numOfParticipants; + this.numOfParticipantsLoggedIn = numOfParticipantsLoggedIn; + this.numOfOtherUsers = numOfOtherUsers; + this.loggedIn = loggedIn; + } + + public int getNumOfParticipants() { + return numOfParticipants; + } + + public int getNumOfOtherUsers() { + return numOfOtherUsers; + } + + public int getLoggedIn() { + return loggedIn; + } + + public int getNumOfParticipantsLoggedIn() { + return numOfParticipantsLoggedIn; + } + + public int getTotal() { + return numOfOtherUsers + numOfParticipants; + } +} diff --git a/src/main/java/org/olat/modules/assessment/ui/AssessmentStatisticsSmallController.java b/src/main/java/org/olat/modules/assessment/ui/AssessmentStatisticsSmallController.java index ca344da089e..e4f47cd73e9 100644 --- a/src/main/java/org/olat/modules/assessment/ui/AssessmentStatisticsSmallController.java +++ b/src/main/java/org/olat/modules/assessment/ui/AssessmentStatisticsSmallController.java @@ -29,7 +29,7 @@ import org.olat.course.assessment.AssessmentHelper; import org.olat.course.assessment.AssessmentToolManager; import org.olat.course.assessment.model.AssessmentStatistics; import org.olat.course.assessment.model.SearchAssessedIdentityParams; -import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.modules.assessment.model.AssessmentMembersStatistics; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -48,8 +48,8 @@ public class AssessmentStatisticsSmallController extends BasicController { private int numOfPassed; private int numOfFailed; - private int numOfParticipants; private int numOfAssessedIdentities; + private AssessmentMembersStatistics membersStatistics; @Autowired private AssessmentToolManager assessmentToolManager; @@ -82,22 +82,30 @@ public class AssessmentStatisticsSmallController extends BasicController { numOfAssessedIdentities = assessmentToolManager.getNumberOfAssessedIdentities(getIdentity(), params); mainVC.contextPut("numOfAssessedIdentities", numOfAssessedIdentities); - numOfParticipants = assessmentToolManager.getNumberOfParticipants(getIdentity(), params); - mainVC.contextPut("numOfParticipants", numOfParticipants); + membersStatistics = assessmentToolManager.getNumberOfParticipants(getIdentity(), params); + mainVC.contextPut("numOfParticipants", membersStatistics.getNumOfParticipants()); + if(assessmentCallback.canAssessNonMembers()) { + mainVC.contextPut("numOfOtherUsers", membersStatistics.getNumOfOtherUsers()); + } AssessmentStatistics stats = assessmentToolManager.getStatistics(getIdentity(), params); mainVC.contextPut("scoreAverage", AssessmentHelper.getRoundedScore(stats.getAverageScore())); numOfPassed = stats.getCountPassed(); mainVC.contextPut("numOfPassed", numOfPassed); - int percentPassed = numOfParticipants <= 0 ? 0 : Math.round(100.0f * (stats.getCountPassed() / numOfParticipants)); + + int total = membersStatistics.getTotal(); + int percentPassed = total <= 0 ? 0 : Math.round(100.0f * (stats.getCountPassed() / total)); mainVC.contextPut("percentPassed", percentPassed); numOfFailed = stats.getCountFailed(); mainVC.contextPut("numOfFailed", numOfFailed); - int percentFailed = numOfParticipants <= 0 ? 0 : Math.round(100.0f * (stats.getCountFailed() / numOfParticipants)); + int percentFailed = total <= 0 ? 0 : Math.round(100.0f * (stats.getCountFailed() / total)); mainVC.contextPut("percentFailed", percentFailed); - int numOfLaunches = assessmentToolManager.getNumberOfInitialLaunches(getIdentity(), params); - mainVC.contextPut("numOfInitialLaunch", numOfLaunches); + int numOfParticipantLaunches = membersStatistics.getNumOfParticipantsLoggedIn(); + mainVC.contextPut("numOfParticipantLaunches", numOfParticipantLaunches); + if(assessmentCallback.canAssessNonMembers()) { + mainVC.contextPut("numOfOtherUserLaunches", membersStatistics.getLoggedIn()); + } } @Override diff --git a/src/main/java/org/olat/modules/assessment/ui/_content/test_stats_small.html b/src/main/java/org/olat/modules/assessment/ui/_content/test_stats_small.html index 15ec159336d..3b7711d6bc2 100644 --- a/src/main/java/org/olat/modules/assessment/ui/_content/test_stats_small.html +++ b/src/main/java/org/olat/modules/assessment/ui/_content/test_stats_small.html @@ -7,17 +7,23 @@ <th>$r.translate("table.header.numOfParticipants")</th> <td>$numOfParticipants</td> </tr> + #if($r.isNotNull($numOfOtherUsers)) + <tr> + <th>$r.translate("table.header.numOfOtherUsers")</th> + <td>$numOfOtherUsers</td> + </tr> + #end <tr> <th>$r.translate("table.header.numOfInitialLaunch")</th> <td> - #if($numOfInitialLaunch && $numOfInitialLaunch == 0) + #if($r.isNotNull($numOfParticipantLaunches) && $numOfParticipantLaunches == 0) <i class="o_icon o_red_led"> </i> - #elseif($numOfInitialLaunch && $numOfInitialLaunch == $numOfParticipants) + #elseif($r.isNotNull($numOfParticipantLaunches) && $numOfParticipantLaunches == $numOfParticipantLaunches) <i class="o_icon o_green_led"> </i> #else <i class="o_icon o_yellow_led"> </i> #end - $numOfInitialLaunch</td> + $numOfParticipantLaunches #if($r.isNotNull($numOfOtherUserLaunches))/ $numOfOtherUserLaunches#end</td> </tr> <tr> <th>$r.translate("table.header.scoreAverage")</th> diff --git a/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_de.properties index 08f8e423294..5385ec008f8 100644 --- a/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_de.properties @@ -31,6 +31,7 @@ table.header.numOfParticipants=\# Teilnehmer table.header.numOfAssessedIdentities=\# Teilnehmer table.header.numOfInitialLaunch=Eingeloggt table.header.numOfPassed=Bestanden +table.header.numOfOtherUsers=# andere Benutzer table.header.scoreAverage=Durchschnitt users=Benutzer waiting.review=Anstehende Bewertungen diff --git a/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_en.properties index ec9bd1a5248..829c8acc440 100644 --- a/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/assessment/ui/_i18n/LocalStrings_en.properties @@ -31,6 +31,7 @@ table.header.numOfParticipants=\# participants table.header.numOfAssessedIdentities=\# participants table.header.numOfInitialLaunch=Logged in table.header.numOfPassed=Passed +table.header.numOfOtherUsers=# other users table.header.scoreAverage=Average users=Users waiting.review=Pending reviews -- GitLab