diff --git a/src/main/java/org/olat/core/logging/activity/ActionObject.java b/src/main/java/org/olat/core/logging/activity/ActionObject.java index 62c820b77fd4806510580b811e438bdae0d344b1..9dbe001de9d6a38bc749f5d826725e2ca1c4de89 100644 --- a/src/main/java/org/olat/core/logging/activity/ActionObject.java +++ b/src/main/java/org/olat/core/logging/activity/ActionObject.java @@ -101,6 +101,7 @@ public enum ActionObject { bulkassessment, lectures, lecturesRollcall, - assessmentdocument; + assessmentdocument, + completionType; } diff --git a/src/main/java/org/olat/core/logging/activity/LearningResourceLoggingAction.java b/src/main/java/org/olat/core/logging/activity/LearningResourceLoggingAction.java index 9725031a9d21dd481b454ddee8aed320b511f2e2..f62493c2086b354917af8c42cc7a8ed5979defcd 100644 --- a/src/main/java/org/olat/core/logging/activity/LearningResourceLoggingAction.java +++ b/src/main/java/org/olat/core/logging/activity/LearningResourceLoggingAction.java @@ -131,6 +131,12 @@ public class LearningResourceLoggingAction extends BaseLoggingAction { new LearningResourceLoggingAction(ActionType.admin, CrudAction.update, ActionVerb.remove, ActionObject.layout).setTypeList(LEARNING_RESOURCE_OPEN_CLOSE_LIST); public static final ILoggingAction REPOSITORY_ENTRY_PROPERTIES_COURSELAYOUT_CUSTOM_ADDED = new LearningResourceLoggingAction(ActionType.admin, CrudAction.update, ActionVerb.add, ActionObject.layout).setTypeList(LEARNING_RESOURCE_OPEN_CLOSE_LIST); + public static final ILoggingAction REPOSITORY_ENTRY_PROPERTIES_COMPLETION_TYPE_NUMBEr_OF_NODES = + new LearningResourceLoggingAction(ActionType.admin, CrudAction.update, ActionVerb.add, ActionObject.completionType).setTypeList(LEARNING_RESOURCE_OPEN_CLOSE_LIST); + public static final ILoggingAction REPOSITORY_ENTRY_PROPERTIES_COMPLETION_TYPE_DURATION = + new LearningResourceLoggingAction(ActionType.admin, CrudAction.update, ActionVerb.add, ActionObject.completionType).setTypeList(LEARNING_RESOURCE_OPEN_CLOSE_LIST); + public static final ILoggingAction REPOSITORY_ENTRY_PROPERTIES_COMPLETION_TYPE_NONE = + new LearningResourceLoggingAction(ActionType.admin, CrudAction.update, ActionVerb.remove, ActionObject.completionType).setTypeList(LEARNING_RESOURCE_OPEN_CLOSE_LIST); // lecture block public static final ILoggingAction LECTURE_BLOCK_CREATED = diff --git a/src/main/java/org/olat/course/assessment/CourseAssessmentService.java b/src/main/java/org/olat/course/assessment/CourseAssessmentService.java index 087fcf1b4e89c689d2764b7c1399119d415e0c82..00ed977957c8f7d3e956d2ff1cdc22a74d7f27ea 100644 --- a/src/main/java/org/olat/course/assessment/CourseAssessmentService.java +++ b/src/main/java/org/olat/course/assessment/CourseAssessmentService.java @@ -30,7 +30,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.id.Identity; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; -import org.olat.course.nodeaccess.NodeAccessType; +import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.AccountingEvaluators; import org.olat.course.run.scoring.AssessmentEvaluation; @@ -57,7 +57,7 @@ public interface CourseAssessmentService { public AssessmentConfig getAssessmentConfig(CourseNode courseNode); - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType); + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig); /** @@ -318,5 +318,4 @@ public interface CourseAssessmentService { UserCourseEnvironment coachCourseEnv, AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback); - } diff --git a/src/main/java/org/olat/course/assessment/handler/AssessmentHandler.java b/src/main/java/org/olat/course/assessment/handler/AssessmentHandler.java index dc501560d6cc5e2bb577ac86a5b203128d2a2deb..381e2e36266585cde6c16f7e3f3fb60592840599 100644 --- a/src/main/java/org/olat/course/assessment/handler/AssessmentHandler.java +++ b/src/main/java/org/olat/course/assessment/handler/AssessmentHandler.java @@ -25,7 +25,7 @@ import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; -import org.olat.course.nodeaccess.NodeAccessType; +import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.CourseNodeProvider; import org.olat.course.run.scoring.AccountingEvaluators; @@ -64,10 +64,10 @@ public interface AssessmentHandler extends CourseNodeProvider { * Provides some evaluators to calculate some values automatically. * * @param courseNode - * @param nodeAccessType + * @param courseConfig * @return */ - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType); + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig); /** * This method has to be implemented if the AssessmentConfig.isEvaluationCalculated() return true. diff --git a/src/main/java/org/olat/course/assessment/handler/NonAssessmentHandler.java b/src/main/java/org/olat/course/assessment/handler/NonAssessmentHandler.java index 4aad5e1557ef66fdba1b9d772b5dab798b9f5389..7cd4ec638fe3cdf82e2e023f681390283318a3b7 100644 --- a/src/main/java/org/olat/course/assessment/handler/NonAssessmentHandler.java +++ b/src/main/java/org/olat/course/assessment/handler/NonAssessmentHandler.java @@ -25,7 +25,7 @@ import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; -import org.olat.course.nodeaccess.NodeAccessType; +import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.AccountingEvaluators; import org.olat.course.run.scoring.AccountingEvaluatorsBuilder; @@ -66,7 +66,7 @@ public class NonAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { return AccountingEvaluatorsBuilder.defaultConventional(); } diff --git a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java index 1fe557fb241b77067e8b275e9c844b5a91c443da..e25251613f56d23c6fe295a28dc86002a654fbf5 100644 --- a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java +++ b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentServiceImpl.java @@ -40,7 +40,7 @@ import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.handler.NonAssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; -import org.olat.course.nodeaccess.NodeAccessType; +import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.run.navigation.NodeVisitedListener; import org.olat.course.run.scoring.AccountingEvaluators; @@ -103,8 +103,8 @@ public class CourseAssessmentServiceImpl implements CourseAssessmentService, Nod } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - return getAssessmentHandler(courseNode).getEvaluators(courseNode, nodeAccessType); + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + return getAssessmentHandler(courseNode).getEvaluators(courseNode, courseConfig); } @Override diff --git a/src/main/java/org/olat/course/config/CompletionType.java b/src/main/java/org/olat/course/config/CompletionType.java new file mode 100644 index 0000000000000000000000000000000000000000..fddcb92c973dac7d334afac2c3ce06d02e695d23 --- /dev/null +++ b/src/main/java/org/olat/course/config/CompletionType.java @@ -0,0 +1,34 @@ +/** + * <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.config; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public enum CompletionType { + + none, + numberOfNodes, + duration + +} diff --git a/src/main/java/org/olat/course/config/CourseConfig.java b/src/main/java/org/olat/course/config/CourseConfig.java index 407c4ad432371f5966c21a637697fd4f9c5c0889..8b1320c72ccc50e8c2f76c0f735f5be221514af2 100644 --- a/src/main/java/org/olat/course/config/CourseConfig.java +++ b/src/main/java/org/olat/course/config/CourseConfig.java @@ -74,7 +74,7 @@ public class CourseConfig implements Serializable, Cloneable { /** * current config file version */ - private static final transient int CURRENTVERSION = 17; + private static final transient int CURRENTVERSION = 18; public static final transient String KEY_LOGLEVEL_ADMIN = "LOGLEVELADMIN"; public static final transient String KEY_LOGLEVEL_USER = "LOGLEVELUSER"; @@ -113,6 +113,8 @@ public class CourseConfig implements Serializable, Cloneable { public static final transient String KEY_SHAREDFOLDER_SOFTKEY = "SHAREDFOLDER_SOFTKEY"; public static final transient String KEY_SHAREDFOLDER_READONLY = "SHAREDFOLDER_RO"; + + private static final transient String KEY_COMPLETION_TYPE = "COMPLETION_TYPE"; /** * current key set @@ -175,6 +177,7 @@ public class CourseConfig implements Serializable, Cloneable { configuration.put(DOCUMENTS_ENABLED, Boolean.FALSE); configuration.put(NODE_ACCESS_TYPE, NODE_ACCESS_TYPE_DEFAULT); + configuration.put(KEY_COMPLETION_TYPE, CompletionType.numberOfNodes.name()); this.version = CURRENTVERSION; } @@ -288,7 +291,14 @@ public class CourseConfig implements Serializable, Cloneable { this.version = 17; } - + + if (version == 17) { + if (!configuration.containsKey(KEY_COMPLETION_TYPE)) { + configuration.put(KEY_COMPLETION_TYPE, CompletionType.numberOfNodes.name()); + } + + this.version = 18; + } /* * after resolving the issues, the version number is merged to the @@ -326,6 +336,18 @@ public class CourseConfig implements Serializable, Cloneable { public NodeAccessType getNodeAccessType() { return NodeAccessType.of((String) configuration.get(NODE_ACCESS_TYPE)); } + + public CompletionType getCompletionType() { + String completionEvaluationStr = (String) configuration.get(KEY_COMPLETION_TYPE); + return CompletionType.valueOf(completionEvaluationStr); + } + + public void setCompletionType(CompletionType completionType) { + String completionTypeStr = completionType != null + ? completionType.name() + : CompletionType.none.name(); + configuration.put(KEY_COMPLETION_TYPE, completionTypeStr); + } public boolean isChatEnabled() { Boolean bool = (Boolean) configuration.get(KEY_CHAT_ENABLED); diff --git a/src/main/java/org/olat/course/config/CourseConfigEvent.java b/src/main/java/org/olat/course/config/CourseConfigEvent.java index 55e52b0cae0032405f65f80af1df554b4ebdd534..533a639055bfff640e22534d82e7f16773142cee 100644 --- a/src/main/java/org/olat/course/config/CourseConfigEvent.java +++ b/src/main/java/org/olat/course/config/CourseConfigEvent.java @@ -66,6 +66,7 @@ public class CourseConfigEvent extends MultiUserEvent { forum, chat, glossary, - layout + layout, + completionType } } diff --git a/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java b/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java index 648489ac1d43213c01d2cee12ce27c09ac7cc264..4b5b51c2462affbe0128018d50fe1ac5d03ee012 100644 --- a/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java +++ b/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java @@ -32,6 +32,8 @@ public interface LearningPathConfigs { public Integer getDuration(); + public void setDuration(Integer duration); + public AssessmentObligation getObligation(); public FullyAssessedResult isFullyAssessedOnNodeVisited(); diff --git a/src/main/java/org/olat/course/learningpath/LearningPathNodeHandler.java b/src/main/java/org/olat/course/learningpath/LearningPathNodeHandler.java index f17ec3a69f9ed2cfc300f2b858e6419292d107e5..c745921de93b01172bb93b3206d35e486ccda662 100644 --- a/src/main/java/org/olat/course/learningpath/LearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/learningpath/LearningPathNodeHandler.java @@ -24,6 +24,7 @@ import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.CourseNodeProvider; +import org.olat.repository.RepositoryEntry; /** * @@ -37,6 +38,7 @@ public interface LearningPathNodeHandler extends CourseNodeProvider { public LearningPathConfigs getConfigs(CourseNode courseNode); - public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode); + public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + CourseNode courseNode); } diff --git a/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentHandler.java b/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentHandler.java index 897991f979a17ea8ad7785fa94200a71b3d6faea..9f541dd8fac79ef83c8a214659b4c4abce31e912 100644 --- a/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentHandler.java +++ b/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentHandler.java @@ -29,9 +29,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.AccountingEvaluators; import org.olat.course.run.scoring.AccountingEvaluatorsBuilder; @@ -60,8 +60,8 @@ public abstract class LearningPathOnlyAssessmentHandler implements AssessmentHan } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/learningpath/LearningPathService.java b/src/main/java/org/olat/course/learningpath/LearningPathService.java index 12110a15d1e68aedbd1acb571830ff8f1dad359d..b6a7df2a331613ab52664fe323af601a1f7f49d2 100644 --- a/src/main/java/org/olat/course/learningpath/LearningPathService.java +++ b/src/main/java/org/olat/course/learningpath/LearningPathService.java @@ -31,7 +31,7 @@ import org.olat.course.nodes.CourseNode; public interface LearningPathService { public LearningPathConfigs getConfigs(CourseNode courseNode); - + public AccessEvaluator getAccessEvaluator(); } diff --git a/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java b/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java index 75cba9c2cc045468c90057c13f3e070287077be8..3690965f65e75861969c43a553f22dbae1c04de0 100644 --- a/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java +++ b/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java @@ -40,6 +40,7 @@ import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseEditorTreeModel; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentEntryStatus; +import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -82,7 +83,8 @@ public class LearningPathNodeAccessProvider implements NodeAccessProvider, NodeV @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode, UserCourseEnvironment userCourseEnvironment, CourseEditorTreeModel editorModel) { - Controller configCtrl = registry.getLearningPathNodeHandler(courseNode).createConfigEditController(ureq, wControl, courseNode); + RepositoryEntry courseEntry = userCourseEnvironment.getCourseEditorEnv().getCourseGroupManager().getCourseEntry(); + Controller configCtrl = registry.getLearningPathNodeHandler(courseNode).createConfigEditController(ureq, wControl, courseEntry, courseNode); return new TabbableLeaningPathNodeConfigController(ureq, wControl, configCtrl); } diff --git a/src/main/java/org/olat/course/learningpath/manager/UnsupportedLearningPathNodeHandler.java b/src/main/java/org/olat/course/learningpath/manager/UnsupportedLearningPathNodeHandler.java index 5446810c11ccb9100bc34f2300aead4e7db61a07..beb8764000b9ceca2e262c42c3910df545cdc6e1 100644 --- a/src/main/java/org/olat/course/learningpath/manager/UnsupportedLearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/learningpath/manager/UnsupportedLearningPathNodeHandler.java @@ -26,6 +26,7 @@ import org.olat.course.learningpath.LearningPathConfigs; import org.olat.course.learningpath.LearningPathNodeHandler; import org.olat.course.learningpath.model.UnsupportedLearningPathConfigs; import org.olat.course.nodes.CourseNode; +import org.olat.repository.RepositoryEntry; import org.springframework.stereotype.Service; /** @@ -57,7 +58,8 @@ public class UnsupportedLearningPathNodeHandler implements LearningPathNodeHandl } @Override - public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode) { + public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + CourseNode courseNode) { return null; } diff --git a/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java b/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java index b9168551484f59bbc63acc60892883465fa00cf4..57486ecb7d6382d01bafe81e4d56683b6aa94d4a 100644 --- a/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java +++ b/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java @@ -53,6 +53,11 @@ public class ModuleLearningPathConfigs implements LearningPathConfigs { return integerOrNull(duration); } + @Override + public void setDuration(Integer duration) { + moduleConfiguration.setStringValue(CONFIG_KEY_DURATION, duration.toString()); + } + private Integer integerOrNull(String value) { if (StringHelper.containsNonWhitespace(value)) { try { diff --git a/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java b/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java index c9200073e96012e83f030b6605406878e04d4c8c..dfa5e8b136214107b261d612104ec42cba8231fd 100644 --- a/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java +++ b/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java @@ -36,6 +36,11 @@ public class UnsupportedLearningPathConfigs implements LearningPathConfigs { return null; } + @Override + public void setDuration(Integer duration) { + // + } + @Override public AssessmentObligation getObligation() { return AssessmentObligation.optional; diff --git a/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java b/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java index 59f0746320e30f06ab6f8ba33b2036756b798f49..0d3461f8cc40fd07d4795450f0d31c641c5e597f 100644 --- a/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java +++ b/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java @@ -24,6 +24,7 @@ import static org.olat.core.gui.components.util.KeyValues.entry; import java.util.Arrays; import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; import org.olat.core.gui.components.form.flexible.elements.TextElement; @@ -34,8 +35,12 @@ import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.util.StringHelper; +import org.olat.course.CourseFactory; +import org.olat.course.config.CompletionType; +import org.olat.course.config.CourseConfig; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.model.AssessmentObligation; +import org.olat.repository.RepositoryEntry; /** * @@ -59,27 +64,30 @@ public class LearningPathNodeConfigController extends FormBasicController { private SingleSelection obligationEl; private SingleSelection triggerEl; - private final ModuleConfiguration configs; + private final CourseConfig courseConfig; + private final ModuleConfiguration moduleConfigs; private final LearningPathControllerConfig ctrlConfig; - public LearningPathNodeConfigController(UserRequest ureq, WindowControl wControl, - ModuleConfiguration configs, LearningPathControllerConfig ctrlConfig) { + public LearningPathNodeConfigController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + ModuleConfiguration moduleConfig, LearningPathControllerConfig ctrlConfig) { super(ureq, wControl); - this.configs = configs; + this.courseConfig = CourseFactory.loadCourse(courseEntry).getCourseConfig(); + this.moduleConfigs = moduleConfig; this.ctrlConfig = ctrlConfig; initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - String estimatedTime = configs.getStringValue(CONFIG_KEY_DURATION); + String estimatedTime = moduleConfigs.getStringValue(CONFIG_KEY_DURATION); durationEl = uifactory.addTextElement("config.duration", 128, estimatedTime , formLayout); KeyValues obligationKV = new KeyValues(); obligationKV.add(entry(AssessmentObligation.mandatory.name(), translate("config.obligation.mandatory"))); obligationKV.add(entry(AssessmentObligation.optional.name(), translate("config.obligation.optional"))); obligationEl = uifactory.addRadiosHorizontal("config.obligation", formLayout, obligationKV.keys(), obligationKV.values()); - String obligationKey = configs.getStringValue(CONFIG_KEY_OBLIGATION, CONFIG_DEFAULT_OBLIGATION); + obligationEl.addActionListener(FormEvent.ONCHANGE); + String obligationKey = moduleConfigs.getStringValue(CONFIG_KEY_OBLIGATION, CONFIG_DEFAULT_OBLIGATION); if (Arrays.asList(obligationEl.getKeys()).contains(obligationKey)) { obligationEl.select(obligationKey, true); } @@ -88,14 +96,16 @@ public class LearningPathNodeConfigController extends FormBasicController { triggerEl = uifactory.addRadiosVertical("config.trigger", formLayout, triggerKV.keys(), triggerKV.values()); triggerEl.addActionListener(FormEvent.ONCHANGE); - String triggerKey = configs.getStringValue(CONFIG_KEY_TRIGGER, CONFIG_DEFAULT_TRIGGER); + String triggerKey = moduleConfigs.getStringValue(CONFIG_KEY_TRIGGER, CONFIG_DEFAULT_TRIGGER); if (Arrays.asList(triggerEl.getKeys()).contains(triggerKey)) { triggerEl.select(triggerKey, true); } uifactory.addFormSubmitButton("save", formLayout); + + updateUI(); } - + private KeyValues getTriggerKV() { KeyValues triggerKV = new KeyValues(); triggerKV.add(entry(CONFIG_VALUE_TRIGGER_NONE, translate("config.trigger.none"))); @@ -119,54 +129,67 @@ public class LearningPathNodeConfigController extends FormBasicController { : translate(defaulI18nKey); } + private void updateUI() { + durationEl.setMandatory(isDurationMandatory()); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source == obligationEl) { + updateUI(); + } + super.formInnerEvent(ureq, source, event); + } + @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; - allOk = validateInteger(durationEl, 1, 10000, "error.positiv.int"); + allOk = validateInteger(durationEl, 1, 10000, isDurationMandatory(), "error.positiv.int"); return allOk & super.validateFormLogic(ureq); } - public static boolean validateInteger(TextElement el, int min, int max, String i18nKey) { + public static boolean validateInteger(TextElement el, int min, int max, boolean mandatory, String i18nKey) { boolean allOk = true; el.clearError(); if(el.isEnabled() && el.isVisible()) { String val = el.getValue(); if(StringHelper.containsNonWhitespace(val)) { - try { int value = Integer.parseInt(val); if(min > value) { - el.setErrorKey(i18nKey, null); allOk = false; } else if(max < value) { - el.setErrorKey(i18nKey, null); allOk = false; } } catch (NumberFormatException e) { - el.setErrorKey(i18nKey, null); allOk = false; } + } else if (mandatory) { + allOk = false; } } + if (!allOk) { + el.setErrorKey(i18nKey, null); + } return allOk; } @Override protected void formOK(UserRequest ureq) { String estimatedTime = durationEl.getValue(); - configs.setStringValue(CONFIG_KEY_DURATION, estimatedTime); + moduleConfigs.setStringValue(CONFIG_KEY_DURATION, estimatedTime); String obligation = obligationEl.isOneSelected() ? obligationEl.getSelectedKey() : CONFIG_DEFAULT_OBLIGATION; - configs.setStringValue(CONFIG_KEY_OBLIGATION, obligation); + moduleConfigs.setStringValue(CONFIG_KEY_OBLIGATION, obligation); String trigger = triggerEl.isOneSelected() ? triggerEl.getSelectedKey() : CONFIG_DEFAULT_TRIGGER; - configs.setStringValue(CONFIG_KEY_TRIGGER, trigger); + moduleConfigs.setStringValue(CONFIG_KEY_TRIGGER, trigger); fireEvent(ureq, Event.DONE_EVENT); } @@ -176,6 +199,12 @@ public class LearningPathNodeConfigController extends FormBasicController { // } + private boolean isDurationMandatory() { + return CompletionType.duration.equals(courseConfig.getCompletionType()) + && obligationEl.isOneSelected() + && AssessmentObligation.mandatory.name().equals(obligationEl.getSelectedKey()); + } + public interface LearningPathControllerConfig { public boolean isTriggerNodeVisited(); diff --git a/src/main/java/org/olat/course/nodeaccess/ui/DurationConfirmationController.java b/src/main/java/org/olat/course/nodeaccess/ui/DurationConfirmationController.java new file mode 100644 index 0000000000000000000000000000000000000000..96a0189b542998b253293d17f3c7103be2b6f53d --- /dev/null +++ b/src/main/java/org/olat/course/nodeaccess/ui/DurationConfirmationController.java @@ -0,0 +1,113 @@ +/** + * <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.nodeaccess.ui; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.TextElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.util.StringHelper; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class DurationConfirmationController extends FormBasicController { + + private TextElement durationEl; + + public DurationConfirmationController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl); + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + setFormInfo("settings.completion.type.confirmation.info"); + + durationEl = uifactory.addTextElement("settings.completion.type.confirmation.duration", 128, null, formLayout); + + FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + formLayout.add("buttons", buttonLayout); + uifactory.addFormSubmitButton("save", buttonLayout); + uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl()); + } + + public Integer getDuration() { + return Integer.valueOf(durationEl.getValue()); + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = true; + + allOk = validateInteger(durationEl, 1, 10000, "error.positiv.int"); + + return allOk & super.validateFormLogic(ureq); + } + + private boolean validateInteger(TextElement el, int min, int max, String i18nKey) { + boolean allOk = true; + el.clearError(); + if(el.isEnabled() && el.isVisible()) { + String val = el.getValue(); + if(StringHelper.containsNonWhitespace(val)) { + try { + int value = Integer.parseInt(val); + if(min > value) { + allOk = false; + } else if(max < value) { + allOk = false; + } + } catch (NumberFormatException e) { + allOk = false; + } + } else { + allOk = false; // mandatory + } + } + if (!allOk) { + el.setErrorKey(i18nKey, null); + } + return allOk; + } + + @Override + protected void formOK(UserRequest ureq) { + fireEvent(ureq, Event.DONE_EVENT); + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + @Override + protected void doDispose() { + // + } + +} diff --git a/src/main/java/org/olat/course/nodeaccess/ui/NodeAccessSettingsController.java b/src/main/java/org/olat/course/nodeaccess/ui/NodeAccessSettingsController.java index aa8386431234534fee341f4fe6125ffc593d5921..49863689ef345c28710b3dc7c76bd591896e9238 100644 --- a/src/main/java/org/olat/course/nodeaccess/ui/NodeAccessSettingsController.java +++ b/src/main/java/org/olat/course/nodeaccess/ui/NodeAccessSettingsController.java @@ -19,19 +19,48 @@ */ package org.olat.course.nodeaccess.ui; -import static org.olat.course.nodeaccess.NodeAccessType.of; +import static org.olat.core.gui.components.util.KeyValues.entry; +import static org.olat.modules.assessment.model.AssessmentObligation.mandatory; + +import java.util.Arrays; +import java.util.function.Function; +import java.util.function.Predicate; import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.tree.TreeNode; import org.olat.core.gui.components.util.KeyValues; import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.id.OLATResourceable; +import org.olat.core.logging.AssertException; +import org.olat.core.logging.activity.ILoggingAction; +import org.olat.core.logging.activity.LearningResourceLoggingAction; +import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; +import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.event.EventBus; +import org.olat.core.util.nodes.INode; +import org.olat.core.util.tree.TreeVisitor; import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.config.CompletionType; +import org.olat.course.config.CourseConfig; +import org.olat.course.config.CourseConfigEvent; +import org.olat.course.config.CourseConfigEvent.CourseConfigType; +import org.olat.course.learningpath.LearningPathConfigs; +import org.olat.course.learningpath.LearningPathService; +import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; import org.olat.course.nodeaccess.NodeAccessProviderIdentifier; import org.olat.course.nodeaccess.NodeAccessService; -import org.olat.course.nodeaccess.NodeAccessType; +import org.olat.course.nodes.CollectingVisitor; +import org.olat.course.nodes.CourseNode; +import org.olat.course.tree.CourseEditorTreeNode; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -44,15 +73,23 @@ import org.springframework.beans.factory.annotation.Autowired; public class NodeAccessSettingsController extends FormBasicController { private SingleSelection nodeAccessEl; + private SingleSelection completionEvaluationeEl; + + private CloseableModalController cmc; + private DurationConfirmationController durationConfirmationCtrl; - private final NodeAccessType nodeAccessType; - + private final RepositoryEntry courseEntry; + private final CourseConfig courseConfig; + @Autowired private NodeAccessService nodeAccessService; + @Autowired + private LearningPathService learningPathService; - public NodeAccessSettingsController(UserRequest ureq, WindowControl wControl, RepositoryEntry entry) { + public NodeAccessSettingsController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry) { super(ureq, wControl); - this.nodeAccessType = of(CourseFactory.loadCourse(entry)); + this.courseEntry = courseEntry; + this.courseConfig = CourseFactory.loadCourse(courseEntry).getCourseConfig(); initForm(ureq); } @@ -62,12 +99,157 @@ public class NodeAccessSettingsController extends FormBasicController { KeyValues nodeAccessKV = new KeyValues(); for (NodeAccessProviderIdentifier identifier : nodeAccessService.getNodeAccessProviderIdentifer()) { - nodeAccessKV.add(KeyValues.entry(identifier.getType(), identifier.getDisplayName(getLocale()))); + nodeAccessKV.add(entry(identifier.getType(), identifier.getDisplayName(getLocale()))); } nodeAccessEl = uifactory.addDropdownSingleselect("settings.type", "settings.type", formLayout, nodeAccessKV.keys(), nodeAccessKV.values()); nodeAccessEl.setEnabled(false); - nodeAccessEl.select(nodeAccessType.getType(), true); + nodeAccessEl.select(courseConfig.getNodeAccessType().getType(), true); + + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { + KeyValues completionKV = new KeyValues(); + completionKV.add(entry(CompletionType.numberOfNodes.name(), translate("settings.completion.type.number.of.nodes"))); + completionKV.add(entry(CompletionType.duration.name(), translate("settings.completion.type.duration"))); + completionEvaluationeEl = uifactory.addRadiosVertical("settings.completion.type", formLayout, + completionKV.keys(), completionKV.values()); + initCompletionTypeFromConfig(); + completionEvaluationeEl.addActionListener(FormEvent.ONCHANGE); + } + } + + private void initCompletionTypeFromConfig() { + String completionKey = courseConfig.getCompletionType().name(); + if (Arrays.asList(completionEvaluationeEl.getKeys()).contains(completionKey)) { + completionEvaluationeEl.select(completionKey, true); + } + flc.setDirty(true); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source == completionEvaluationeEl) { + doConfirmCompletionEvaluation(ureq); + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == durationConfirmationCtrl) { + if (Event.DONE_EVENT.equals(event)) { + doSetCompletionTypeDuration(durationConfirmationCtrl.getDuration()); + } else if (Event.CANCELLED_EVENT.equals(event)) { + initCompletionTypeFromConfig(); + CourseFactory.closeCourseEditSession(courseEntry.getOlatResource().getResourceableId(), false); + } + cmc.deactivate(); + cleanUp(); + } else if (source == cmc) { + cmc.deactivate(); + cleanUp(); + } + } + + private void cleanUp() { + removeAsListenerAndDispose(durationConfirmationCtrl); + removeAsListenerAndDispose(cmc); + durationConfirmationCtrl = null; + cmc = null; + } + + private void doConfirmCompletionEvaluation(UserRequest ureq) { + CompletionType completionType = completionEvaluationeEl.isOneSelected() + ? CompletionType.valueOf(completionEvaluationeEl.getSelectedKey()) + : CompletionType.numberOfNodes; + + boolean changedToDurationType = CompletionType.duration.equals(completionType) + && !CompletionType.duration.equals(courseConfig.getCompletionType()); + + if (changedToDurationType) { + doConfirmCompletionTypeDuration(ureq); + } else { + if(CourseFactory.isCourseEditSessionOpen(courseEntry.getOlatResource().getResourceableId())) { + showWarning("error.course.locked"); + initCompletionTypeFromConfig(); + return; + } + saveCompletionTypeAndCloseEditSession(completionType); + } + } + + private void doConfirmCompletionTypeDuration(UserRequest ureq) { + OLATResourceable courseOres = courseEntry.getOlatResource(); + if(CourseFactory.isCourseEditSessionOpen(courseOres.getResourceableId())) { + showWarning("error.course.locked"); + initCompletionTypeFromConfig(); + return; + } + + CourseFactory.openCourseEditSession(courseOres.getResourceableId()); + durationConfirmationCtrl = new DurationConfirmationController(ureq, getWindowControl()); + listenTo(durationConfirmationCtrl); + + cmc = new CloseableModalController(getWindowControl(), translate("close"), + durationConfirmationCtrl.getInitialComponent(), true, translate("settings.completion.type.confirmation.title")); + cmc.activate(); + listenTo(cmc); + } + + + private void doSetCompletionTypeDuration(Integer duration) { + ICourse course = CourseFactory.loadCourse(courseEntry); + + CollectingVisitor editorVisitor = CollectingVisitor.applying(new MissingDurationFunction()); + TreeNode editorRootNode = course.getEditorTreeModel().getRootNode(); + doInitDuration(editorVisitor, editorRootNode, duration); + + CollectingVisitor visitor = CollectingVisitor.testing(new MissingDurationPredicate()); + CourseNode runRootNode = course.getRunStructure().getRootNode(); + doInitDuration(visitor, runRootNode, duration); + + CourseFactory.saveCourse(courseEntry.getOlatResource().getResourceableId()); + + saveCompletionTypeAndCloseEditSession(CompletionType.duration); + + EventBus eventBus = CoordinatorManager.getInstance().getCoordinator().getEventBus(); + CourseConfigEvent courseConfigEvent = new CourseConfigEvent(CourseConfigType.completionType, course.getResourceableId()); + eventBus.fireEventToListenersOf(courseConfigEvent, course); + } + + private void doInitDuration(CollectingVisitor visitor, INode rootNode, Integer duration) { + TreeVisitor tv = new TreeVisitor(visitor, rootNode, true); + tv.visitAll(); + visitor.getCourseNodes().stream() + .map(courseNode -> learningPathService.getConfigs(courseNode)) + .forEach(config -> config.setDuration(duration)); + } + + private void saveCompletionTypeAndCloseEditSession(CompletionType completionType) { + boolean changed = !completionType.equals(courseConfig.getCompletionType()); + if (changed) { + courseConfig.setCompletionType(completionType); + logActivity(completionType); + } + CourseFactory.closeCourseEditSession(courseEntry.getOlatResource().getResourceableId(), false); + } + + private void logActivity(CompletionType completionType) { + ILoggingAction loggingAction = null; + switch (completionType) { + case numberOfNodes: + loggingAction = LearningResourceLoggingAction.REPOSITORY_ENTRY_PROPERTIES_COMPLETION_TYPE_NUMBEr_OF_NODES; + break; + case duration: + loggingAction = LearningResourceLoggingAction.REPOSITORY_ENTRY_PROPERTIES_COMPLETION_TYPE_DURATION; + break; + case none: + loggingAction = LearningResourceLoggingAction.REPOSITORY_ENTRY_PROPERTIES_COMPLETION_TYPE_NONE; + break; + } + if (loggingAction == null) { + throw new AssertException("No LoggingAction for CompletionType: " + completionType); + } + ThreadLocalUserActivityLogger.log(loggingAction, getClass()); } @Override @@ -77,7 +259,34 @@ public class NodeAccessSettingsController extends FormBasicController { @Override protected void doDispose() { - // + CourseFactory.closeCourseEditSession(courseEntry.getOlatResource().getResourceableId(), false); + } + + private final class MissingDurationPredicate implements Predicate<CourseNode> { + + @Override + public boolean test(CourseNode courseNode) { + LearningPathConfigs configs = learningPathService.getConfigs(courseNode); + return mandatory.equals(configs.getObligation()) && configs.getDuration() == null; + } + } + + private final class MissingDurationFunction implements Function<INode, CourseNode> { + + private Predicate<CourseNode> missingDuration = new MissingDurationPredicate(); + + @Override + public CourseNode apply(INode iNode) { + if (iNode instanceof CourseEditorTreeNode) { + CourseEditorTreeNode courseEditorTreeNode = (CourseEditorTreeNode) iNode; + CourseNode courseNode = courseEditorTreeNode.getCourseNode(); + if (missingDuration.test(courseNode)) { + return courseNode; + } + } + return null; + } + } } diff --git a/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_de.properties index 8cf0e1f9c399f16c0d16cd070f2f347e29b1f5d3..45c31f58f73a68bccfca46c74affe86c9de9b3cc 100644 --- a/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_de.properties @@ -1,2 +1,10 @@ +error.course.locked=Dieser Kurs wird bereits von jemand anderem bearbeitet. Diese Einstellun kann nicht g\u00E4ndert werden. +error.positiv.int=$org.olat.course.learningpath.ui\:error.positiv.int +settings.completion.type.confirmation.duration=$org.olat.course.learningpath.ui\:config.duration +settings.completion.type.confirmation.info=Wollen Sie den Lernfortschritt wirklich anhand der gesch\u00E4tztden Zeitdauer der einzelnen Kurselemente berechnen? In diesem Fall muss in jedem Kurselement die gesch\u00E4tzte Dauer definiert sein. Geben Sie hier einen Initialwert ein, welcher bei Kurselemnten ohne gesch\u00E4tzte Dauer eingetragen wird. +settings.completion.type.confirmation.title=Berechnungsmethode des Lernfortschrittes +settings.completion.type.duration=Anhand der gesch\u00E4tzten Dauer der Kurselemente +settings.completion.type.number.of.nodes=Anhand der Anzahl der Kurselemente +settings.completion.type=Lernfortschritt berechnen settings.title=Zugriff Kursbausteine settings.type=Typ \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_en.properties index 35d892206a1aa1b51e4e2c9ca5da5029925afa37..7e1b88bc99860ade13bd7b4b5dfa06afcb01dbf2 100644 --- a/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodeaccess/ui/_i18n/LocalStrings_en.properties @@ -1,2 +1,10 @@ +error.course.locked=This course is already being edited on by someone else. This setting cannot be changed. +error.positiv.int=$org.olat.course.learningpath.ui\:error.positiv.int +settings.completion.type.confirmation.duration=$org.olat.course.learningpath.ui\:config.duration +settings.completion.type.confirmation.info=Do you really want to calculate the learning progress based on the estimated duration of the individual course elements? In this case, the estimated duration must be defined in each course element. Enter an initial value here, which is entered for course elements without an estimated duration. +settings.completion.type.confirmation.title=Calculation of learning progress +settings.completion.type.duration=According to the estimated duration of the course elements +settings.completion.type.number.of.nodes=According to the number of course elements +settings.completion.type=Calculation of learning progress settings.title=Access course elements settings.type=Type \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/CollectingVisitor.java b/src/main/java/org/olat/course/nodes/CollectingVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..314b564640d60a0d4bea9e33b6238804019355aa --- /dev/null +++ b/src/main/java/org/olat/course/nodes/CollectingVisitor.java @@ -0,0 +1,84 @@ +/** + * <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; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.olat.core.util.nodes.INode; +import org.olat.core.util.tree.Visitor; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class CollectingVisitor implements Visitor { + + private final List<CourseNode> courseNodes = new ArrayList<>(); + private final Predicate<CourseNode> predicate; + private final Function<INode, CourseNode> function; + + public static final CollectingVisitor testing(Predicate<CourseNode> predicate) { + return new CollectingVisitor(predicate, null); + } + + public static final CollectingVisitor applying(Function<INode, CourseNode> function) { + return new CollectingVisitor(null, function); + } + + private CollectingVisitor(Predicate<CourseNode> predicate, Function<INode, CourseNode> function) { + this.predicate = predicate; + this.function = function; + } + + @Override + public void visit(INode node) { + if (predicate != null) { + test(node); + } else if (function != null) { + apply(node); + } + } + + private void test(INode node) { + if (node instanceof CourseNode) { + CourseNode courseNode = (CourseNode) node; + if (predicate.test(courseNode)) { + courseNodes.add(courseNode); + } + } + } + + private void apply(INode node) { + CourseNode courseNode = function.apply(node); + if (courseNode != null) { + courseNodes.add(courseNode); + } + } + + public List<CourseNode> getCourseNodes() { + return courseNodes; + } + +} diff --git a/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentHandler.java b/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentHandler.java index c7ed15f3128e46ad761c7e102f86a832fcaeec17..3d2469a19e74fb44097c96c12ae7a9d24ecfec9d 100644 --- a/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentHandler.java @@ -29,9 +29,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.BasicLTICourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -75,8 +75,8 @@ public class LTIAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/cl/CheckListAssessmentHandler.java b/src/main/java/org/olat/course/nodes/cl/CheckListAssessmentHandler.java index 0ebe028f3e96b3249f823d9b4abcae4a806cf1ae..ec36598d17b32d9af498201229a648fea22c2100 100644 --- a/src/main/java/org/olat/course/nodes/cl/CheckListAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/cl/CheckListAssessmentHandler.java @@ -31,9 +31,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CheckListCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.cl.ui.AssessedIdentityCheckListController; @@ -76,8 +76,8 @@ public class CheckListAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/gta/AbstractGTAAssessmentHandler.java b/src/main/java/org/olat/course/nodes/gta/AbstractGTAAssessmentHandler.java index 51fe9fdcfa18028f91e093a7867eb661ece25a30..87fddc94dda79a99a84a2f94978bdd8c0c79bef0 100644 --- a/src/main/java/org/olat/course/nodes/gta/AbstractGTAAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/gta/AbstractGTAAssessmentHandler.java @@ -30,9 +30,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.GTACourseNode; import org.olat.course.nodes.gta.ui.GTAAssessmentDetailsController; @@ -69,8 +69,8 @@ public abstract class AbstractGTAAssessmentHandler implements AssessmentHandler } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentHandler.java b/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentHandler.java index 50b18903dc353a259f0cac601d761106c10ebfd9..d057ec755897ad7c6ba4abb5efecbc0b4a0e0812 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentHandler.java @@ -34,9 +34,9 @@ import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.handler.NonAssessmentConfig; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -90,8 +90,8 @@ public class IQTESTAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/ms/MSAssessmentHandler.java b/src/main/java/org/olat/course/nodes/ms/MSAssessmentHandler.java index 7deb6098a86a9d607bdfdbecdd17673575182783..aed0963e2b664c54e42dc677c76607340bf9520b 100644 --- a/src/main/java/org/olat/course/nodes/ms/MSAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/ms/MSAssessmentHandler.java @@ -29,9 +29,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.MSCourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -74,8 +74,8 @@ public class MSAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/portfolio/PortfolioAssessmentHandler.java b/src/main/java/org/olat/course/nodes/portfolio/PortfolioAssessmentHandler.java index a80a868522fa79fc2fe6bab9ce43eba02c5b4955..601b2aa2886b2a631dc95bafb9f5f4b37adf2176 100644 --- a/src/main/java/org/olat/course/nodes/portfolio/PortfolioAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/portfolio/PortfolioAssessmentHandler.java @@ -30,9 +30,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.PortfolioCourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -81,8 +81,8 @@ public class PortfolioAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerAssessmentHandler.java b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerAssessmentHandler.java index 7d00113b59f5dfed5467a91dd46d853f74e470bb..a2c9a564d49de176ae65d56265b45c07814e7ec3 100644 --- a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerAssessmentHandler.java @@ -29,9 +29,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.ProjectBrokerCourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -73,8 +73,8 @@ public class ProjectBrokerAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentHandler.java b/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentHandler.java index 78024755da9f5728c0ea94beb9ff3c79ae7f045b..6309bd182c7bfdb4f2edf3458161bdf625d63dc8 100644 --- a/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentHandler.java @@ -29,9 +29,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.ScormCourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -74,8 +74,8 @@ public class ScormAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/sp/SPLearningPathNodeHandler.java b/src/main/java/org/olat/course/nodes/sp/SPLearningPathNodeHandler.java index 16044ae38fc972ebb72f027f7e69eb72dc868838..a02b5a220c642d4cabcbb47401c822e292d0e3ea 100644 --- a/src/main/java/org/olat/course/nodes/sp/SPLearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/nodes/sp/SPLearningPathNodeHandler.java @@ -29,6 +29,7 @@ import org.olat.course.learningpath.ui.LearningPathNodeConfigController; import org.olat.course.learningpath.ui.LearningPathNodeConfigController.LearningPathControllerConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.SPCourseNode; +import org.olat.repository.RepositoryEntry; import org.springframework.stereotype.Service; /** @@ -56,11 +57,12 @@ public class SPLearningPathNodeHandler implements LearningPathNodeHandler { } @Override - public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode) { + public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + CourseNode courseNode) { LearningPathControllerConfig ctrlConfig = LearningPathNodeConfigController.builder() .enableNodeVisited() .build(); - return new LearningPathNodeConfigController(ureq, wControl, courseNode.getModuleConfiguration(), ctrlConfig); + return new LearningPathNodeConfigController(ureq, wControl, courseEntry, courseNode.getModuleConfiguration(), ctrlConfig); } } diff --git a/src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java b/src/main/java/org/olat/course/nodes/st/assessment/AverageCompletionEvaluator.java similarity index 61% rename from src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java rename to src/main/java/org/olat/course/nodes/st/assessment/AverageCompletionEvaluator.java index 6e21359cf325e8297fbb6fefb07516fa8e5ed8cd..2a3ae201b7abe1e76060e2c15d8035714b10ab3f 100644 --- a/src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java +++ b/src/main/java/org/olat/course/nodes/st/assessment/AverageCompletionEvaluator.java @@ -19,22 +19,20 @@ */ package org.olat.course.nodes.st.assessment; -import java.util.ArrayList; -import java.util.List; +import java.util.function.Function; -import org.olat.core.util.nodes.INode; +import org.olat.core.CoreSpringFactory; +import org.olat.core.util.tree.TreeVisitor; import org.olat.course.assessment.CourseAssessmentService; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentConfig.Mode; -import org.olat.course.config.CourseConfig; +import org.olat.course.nodes.CollectingVisitor; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.CompletionEvaluator; import org.olat.course.run.scoring.ScoreAccounting; import org.olat.modules.assessment.model.AssessmentEntryStatus; import org.olat.modules.assessment.model.AssessmentObligation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; /** * @@ -42,53 +40,63 @@ import org.springframework.stereotype.Component; * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com * */ -@Component -public class NumberOfNodesCompletionEvaluator implements CompletionEvaluator { +public class AverageCompletionEvaluator implements CompletionEvaluator { - @Autowired - private CourseAssessmentService courseAssessmentService; + final static Function<AssessmentEvaluation, Integer> UNWEIGHTED = (ae) -> 1; + final static Function<AssessmentEvaluation, Integer> DURATION_WEIGHTED = + (ae) -> ae.getDuration() != null? ae.getDuration(): Integer.valueOf(1); + + private final CourseAssessmentService courseAssessmentService; + private final Function<AssessmentEvaluation, Integer> weightFunction; + + public AverageCompletionEvaluator(Function<AssessmentEvaluation, Integer> weightFunction) { + this(CoreSpringFactory.getImpl(CourseAssessmentService.class), weightFunction); + } + + public AverageCompletionEvaluator(CourseAssessmentService courseAssessmentService, + Function<AssessmentEvaluation, Integer> weightFunction) { + this.courseAssessmentService = courseAssessmentService; + this.weightFunction = weightFunction; + } @Override public Double getCompletion(AssessmentEvaluation currentEvaluation, CourseNode courseNode, - CourseConfig courseConfig, ScoreAccounting scoreAccounting) { + ScoreAccounting scoreAccounting) { - List<CourseNode> children = new ArrayList<>(); - collectChildren(children, courseNode); + CollectingVisitor visitor = CollectingVisitor.testing(cn -> true); + TreeVisitor tv = new TreeVisitor(visitor, courseNode, true); + tv.visitAll(); int count = 0; double completion = 0.0; - for (CourseNode child: children) { + for (CourseNode child: visitor.getCourseNodes()) { AssessmentEvaluation assessmentEvaluation = scoreAccounting.evalCourseNode(child); if (isMandatory(assessmentEvaluation)) { AssessmentConfig assessmentConfig = courseAssessmentService.getAssessmentConfig(child); + int nodeCount = 0; + double nodeCompletion = 0.0; if (Mode.setByNode.equals(assessmentConfig.getCompletionMode())) { - count++; - completion += assessmentEvaluation.getCompletion() != null + nodeCount = 1; + nodeCompletion = assessmentEvaluation.getCompletion() != null ? assessmentEvaluation.getCompletion().doubleValue() : 0.0; } else if (Mode.none.equals(assessmentConfig.getCompletionMode())) { - count++; + nodeCount = 1; completion += getCompletion(assessmentEvaluation); } + int weight = weightFunction.apply(assessmentEvaluation).intValue(); + count += weight * nodeCount; + completion += weight * nodeCompletion; } } return count > 0? completion / count: null; } - private void collectChildren(List<CourseNode> children, CourseNode courseNode) { - for (int i = 0; i < courseNode.getChildCount(); i++) { - INode child = courseNode.getChildAt(i); - if (child instanceof CourseNode) { - CourseNode childCourseNode = (CourseNode) child; - children.add(childCourseNode); - collectChildren(children, childCourseNode); - } - } - } - private boolean isMandatory(AssessmentEvaluation evaluation) { - return evaluation.getObligation() != null && AssessmentObligation.mandatory.equals(evaluation.getObligation()); + return evaluation != null + && evaluation.getObligation() != null + && AssessmentObligation.mandatory.equals(evaluation.getObligation()); } private double getCompletion(AssessmentEvaluation evaluation) { diff --git a/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentHandler.java b/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentHandler.java index 662be236cdae26f074c5e95465890ef17194b5a5..6c7ae39bb1b9842bab60db8ce9a4d945e47cda70 100644 --- a/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentHandler.java @@ -19,6 +19,9 @@ */ package org.olat.course.nodes.st.assessment; +import static org.olat.course.nodes.st.assessment.AverageCompletionEvaluator.DURATION_WEIGHTED; +import static org.olat.course.nodes.st.assessment.AverageCompletionEvaluator.UNWEIGHTED; + import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.components.stack.TooledStackedPanel; @@ -31,14 +34,16 @@ import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.handler.NonAssessmentConfig; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.condition.interpreter.ConditionInterpreter; +import org.olat.course.config.CompletionType; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.STCourseNode; import org.olat.course.nodes.st.STIdentityListCourseNodeController; import org.olat.course.run.scoring.AccountingEvaluators; import org.olat.course.run.scoring.AccountingEvaluatorsBuilder; import org.olat.course.run.scoring.AssessmentEvaluation; +import org.olat.course.run.scoring.CompletionEvaluator; import org.olat.course.run.scoring.FullyAssessedEvaluator; import org.olat.course.run.scoring.LastModificationsEvaluator; import org.olat.course.run.scoring.PassedEvaluator; @@ -51,7 +56,6 @@ import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.ui.AssessmentToolContainer; import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** @@ -77,9 +81,6 @@ public class STAssessmentHandler implements AssessmentHandler { .withLastModificationsEvaluator(LAST_MODIFICATION_EVALUATOR) .build(); - @Autowired - private NumberOfNodesCompletionEvaluator numberOfNodesCompletionEvaluator; - @Override public String acceptCourseNodeType() { return STCourseNode.TYPE; @@ -102,17 +103,20 @@ public class STAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { - return AccountingEvaluatorsBuilder.builder() + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { + AccountingEvaluatorsBuilder builder = AccountingEvaluatorsBuilder.builder() .withDurationEvaluator(CUMULATION_DURATION_EVALUATOR) .withScoreEvaluator(CONDITION_SCORE_EVALUATOR) .withPassedEvaluator(CONDITION_PASSED_EVALUATOR) - .withCompletionEvaluator(numberOfNodesCompletionEvaluator) .withStatusEvaluator(STATUS_LEARNING_PATH_STATUS_EVALUATOR) .withFullyAssessedEvaluator(FULLY_ASSESSED_EVALUATOR) - .withLastModificationsEvaluator(LAST_MODIFICATION_EVALUATOR) - .build(); + .withLastModificationsEvaluator(LAST_MODIFICATION_EVALUATOR); + CompletionEvaluator completionEvaluator = CompletionType.duration.equals(courseConfig.getCompletionType()) + ? new AverageCompletionEvaluator(DURATION_WEIGHTED) + : new AverageCompletionEvaluator(UNWEIGHTED); + builder.withCompletionEvaluator(completionEvaluator); + return builder.build(); } return CONVENTIONAL_EVALUATORS; } diff --git a/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java b/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java index dcc2124f2464149a685784b46ed957c2e8900311..cc7ba0ca30b4e2d3014251d603faec95724928dc 100644 --- a/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java +++ b/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java @@ -36,6 +36,11 @@ public class STLearningPathConfigs implements LearningPathConfigs { return null; } + @Override + public void setDuration(Integer duration) { + // Duration is calculated. You can not set it in the course node. + } + @Override public AssessmentObligation getObligation() { return null; diff --git a/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathNodeHandler.java b/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathNodeHandler.java index 4727fff03016adafa628bef1130f1df2816a1296..106e1ccae8fdc50571ec7e3dfbfefedd9c409e0f 100644 --- a/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathNodeHandler.java @@ -30,6 +30,7 @@ import org.olat.course.learningpath.LearningPathNodeHandler; import org.olat.course.learningpath.ui.TabbableLeaningPathNodeConfigController; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.STCourseNode; +import org.olat.repository.RepositoryEntry; import org.springframework.stereotype.Service; /** @@ -59,7 +60,8 @@ public class STLearningPathNodeHandler implements LearningPathNodeHandler { } @Override - public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode) { + public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + CourseNode courseNode) { Translator translator = Util.createPackageTranslator(TabbableLeaningPathNodeConfigController.class, ureq.getLocale()); return MessageUIFactory.createInfoMessage(ureq, wControl, null, translator.translate("no.configurations")); } diff --git a/src/main/java/org/olat/course/nodes/survey/SurveyLearningPathNodeHandler.java b/src/main/java/org/olat/course/nodes/survey/SurveyLearningPathNodeHandler.java index 0af4b1c53e78a2da59f273f98e00d95049edf387..3b134b4724ac2ff31f516ec63367300faa571685 100644 --- a/src/main/java/org/olat/course/nodes/survey/SurveyLearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/nodes/survey/SurveyLearningPathNodeHandler.java @@ -32,6 +32,7 @@ import org.olat.course.learningpath.ui.LearningPathNodeConfigController.Learning import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.SurveyCourseNode; import org.olat.course.nodes.survey.ui.SurveyRunController; +import org.olat.repository.RepositoryEntry; import org.springframework.stereotype.Service; /** @@ -59,13 +60,14 @@ public class SurveyLearningPathNodeHandler implements LearningPathNodeHandler { } @Override - public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode) { + public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + CourseNode courseNode) { Translator translator = Util.createPackageTranslator(SurveyRunController.class, ureq.getLocale()); LearningPathControllerConfig ctrlConfig = LearningPathNodeConfigController.builder() .enableNodeVisited() .enableStatusDone(translator.translate("done.trigger.status.done")) .build(); - return new LearningPathNodeConfigController(ureq, wControl, courseNode.getModuleConfiguration(), ctrlConfig); + return new LearningPathNodeConfigController(ureq, wControl, courseEntry, courseNode.getModuleConfiguration(), ctrlConfig); } } diff --git a/src/main/java/org/olat/course/nodes/ta/TAAssessmentHandler.java b/src/main/java/org/olat/course/nodes/ta/TAAssessmentHandler.java index b9287befba9ce1e963b9fed9bcca40daae105157..cb1749c58b178d0d9fd021b0f3308a90a9412edb 100644 --- a/src/main/java/org/olat/course/nodes/ta/TAAssessmentHandler.java +++ b/src/main/java/org/olat/course/nodes/ta/TAAssessmentHandler.java @@ -29,9 +29,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentHandler; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.config.CourseConfig; import org.olat.course.learningpath.evaluation.LearningPathEvaluatorBuilder; import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; -import org.olat.course.nodeaccess.NodeAccessType; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.TACourseNode; import org.olat.course.run.scoring.AccountingEvaluators; @@ -74,8 +74,8 @@ public class TAAssessmentHandler implements AssessmentHandler { } @Override - public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { - if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { + public AccountingEvaluators getEvaluators(CourseNode courseNode, CourseConfig courseConfig) { + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { return LearningPathEvaluatorBuilder.buildDefault(); } return AccountingEvaluatorsBuilder.defaultConventional(); diff --git a/src/main/java/org/olat/course/nodes/video/VideoLearningPathNodeHandler.java b/src/main/java/org/olat/course/nodes/video/VideoLearningPathNodeHandler.java index b40681646ebeedce59dd786a78f6d4f9b5c45f02..621c920384acaca3dc9868261629a0bb54bc35a6 100644 --- a/src/main/java/org/olat/course/nodes/video/VideoLearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/nodes/video/VideoLearningPathNodeHandler.java @@ -29,6 +29,7 @@ import org.olat.course.learningpath.ui.LearningPathNodeConfigController; import org.olat.course.learningpath.ui.LearningPathNodeConfigController.LearningPathControllerConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.VideoCourseNode; +import org.olat.repository.RepositoryEntry; import org.springframework.stereotype.Service; /** @@ -56,11 +57,12 @@ public class VideoLearningPathNodeHandler implements LearningPathNodeHandler { } @Override - public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, CourseNode courseNode) { + public Controller createConfigEditController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + CourseNode courseNode) { LearningPathControllerConfig ctrlConfig = LearningPathNodeConfigController.builder() .enableNodeVisited() .build(); - return new LearningPathNodeConfigController(ureq, wControl, courseNode.getModuleConfiguration(), ctrlConfig); + return new LearningPathNodeConfigController(ureq, wControl, courseEntry, courseNode.getModuleConfiguration(), ctrlConfig); } } diff --git a/src/main/java/org/olat/course/run/CourseRuntimeController.java b/src/main/java/org/olat/course/run/CourseRuntimeController.java index dba0e1fc6dc7eb38a29919852a6f47b3b6b7984f..95e79515e8b4578108188c7c5a7cd2f095c3283e 100644 --- a/src/main/java/org/olat/course/run/CourseRuntimeController.java +++ b/src/main/java/org/olat/course/run/CourseRuntimeController.java @@ -1989,6 +1989,10 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im //don't restart for that break; } + case completionType: { + doDisposeAfterEvent(); + break; + } } } diff --git a/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsFactory.java b/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsFactory.java index 25b521f67a06020d1d6496bb129c0fe3af183d53..3520fb464902bb6ad503d0ae511994d1f6895f9a 100644 --- a/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsFactory.java +++ b/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsFactory.java @@ -22,7 +22,6 @@ package org.olat.course.run.scoring; import java.util.List; import org.olat.course.condition.interpreter.ConditionInterpreter; -import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.modules.assessment.model.AssessmentEntryStatus; import org.olat.modules.assessment.model.AssessmentObligation; @@ -136,7 +135,7 @@ class AccountingEvaluatorsFactory { @Override public Double getCompletion(AssessmentEvaluation currentEvaluation, CourseNode courseNode, - CourseConfig courseConfig, ScoreAccounting scoureAccounting) { + ScoreAccounting scoureAccounting) { return currentEvaluation.getCompletion(); } diff --git a/src/main/java/org/olat/course/run/scoring/AssessmentAccounting.java b/src/main/java/org/olat/course/run/scoring/AssessmentAccounting.java index 18092b15fc7a4b825e764bb1a1510aba0c69f433..78bbeb407204a94526829022c02f1179a22df036 100644 --- a/src/main/java/org/olat/course/run/scoring/AssessmentAccounting.java +++ b/src/main/java/org/olat/course/run/scoring/AssessmentAccounting.java @@ -34,7 +34,7 @@ import org.olat.core.logging.Tracing; import org.olat.core.util.nodes.INode; import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.CourseAssessmentService; -import org.olat.course.nodeaccess.NodeAccessType; +import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.LastModificationsEvaluator.LastModifications; import org.olat.course.run.userview.UserCourseEnvironment; @@ -54,7 +54,7 @@ public class AssessmentAccounting implements ScoreAccounting { private static final Logger log = Tracing.createLoggerFor(AssessmentAccounting.class); private final UserCourseEnvironment userCourseEnvironment; - private final NodeAccessType nodeAccessType; + private final CourseConfig courseConfig; private Map<String, AssessmentEntry> identToEntry = new HashMap<>(); private final Map<CourseNode, AssessmentEvaluation> courseNodeToEval = new HashMap<>(); private AssessmentEvaluation previosEvaluation; @@ -64,7 +64,7 @@ public class AssessmentAccounting implements ScoreAccounting { public AssessmentAccounting(UserCourseEnvironment userCourseEnvironment) { this.userCourseEnvironment = userCourseEnvironment; - this.nodeAccessType = NodeAccessType.of(userCourseEnvironment); + this.courseConfig = userCourseEnvironment.getCourseEnvironment().getCourseConfig(); CoreSpringFactory.autowireObject(this); } @@ -145,7 +145,7 @@ public class AssessmentAccounting implements ScoreAccounting { AssessmentEvaluation currentEvaluation = courseNodeToEval.get(courseNode); AccountingResult result = new AccountingResult(currentEvaluation); - AccountingEvaluators evaluators = courseAssessmentService.getEvaluators(courseNode, nodeAccessType); + AccountingEvaluators evaluators = courseAssessmentService.getEvaluators(courseNode, courseConfig); ObligationEvaluator obligationEvaluator = evaluators.getObligationEvaluator(); AssessmentObligation obligation = obligationEvaluator.getObligation(courseNode); @@ -194,9 +194,7 @@ public class AssessmentAccounting implements ScoreAccounting { result.setLastCoachModified(lastModifications.getLastCoachModified()); CompletionEvaluator completionEvaluator = evaluators.getCompletionEvaluator(); - Double completion = completionEvaluator.getCompletion(result, courseNode, - userCourseEnvironment.getCourseEnvironment().getCourseConfig(), - this); + Double completion = completionEvaluator.getCompletion(result, courseNode, this); result.setCompletion(completion); status = statusEvaluator.getStatus(result, children); diff --git a/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java b/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java index 1a642a80a37aa9be62590cda2b37853b3cd28846..d6a454cae1486e593b45ed2b39e1a53e6358d522 100644 --- a/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java +++ b/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java @@ -19,7 +19,6 @@ */ package org.olat.course.run.scoring; -import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; /** @@ -31,6 +30,6 @@ import org.olat.course.nodes.CourseNode; public interface CompletionEvaluator { public Double getCompletion(AssessmentEvaluation currentEvaluation, CourseNode courseNode, - CourseConfig courseConfig, ScoreAccounting scoreAccounting); + ScoreAccounting scoreAccounting); } diff --git a/src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java b/src/test/java/org/olat/course/nodes/st/assessment/AverageCompletionEvaluatorTest.java similarity index 71% rename from src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java rename to src/test/java/org/olat/course/nodes/st/assessment/AverageCompletionEvaluatorTest.java index 61a3aeda27f6dc9e165bd35ffc8102f66be668f0..0efc9cfb5e42b1d66bfac4862c338f218203c8d6 100644 --- a/src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java +++ b/src/test/java/org/olat/course/nodes/st/assessment/AverageCompletionEvaluatorTest.java @@ -22,12 +22,13 @@ package org.olat.course.nodes.st.assessment; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.mockito.Mockito.when; +import static org.olat.course.nodes.st.assessment.AverageCompletionEvaluator.DURATION_WEIGHTED; +import static org.olat.course.nodes.st.assessment.AverageCompletionEvaluator.UNWEIGHTED; import static org.olat.modules.assessment.model.AssessmentObligation.mandatory; import static org.olat.modules.assessment.model.AssessmentObligation.optional; import org.junit.Before; import org.junit.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.olat.course.assessment.CourseAssessmentService; @@ -48,7 +49,7 @@ import org.olat.modules.assessment.model.AssessmentObligation; * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com * */ -public class NumberOfNodesCompletionEvaluatorTest { +public class AverageCompletionEvaluatorTest { @Mock private AssessmentConfig configSetByNode; @@ -60,9 +61,7 @@ public class NumberOfNodesCompletionEvaluatorTest { @Mock private CourseAssessmentService courseAssessmentService; - @InjectMocks - private NumberOfNodesCompletionEvaluator sut; - + private AverageCompletionEvaluator sut; @Before public void setUp() { @@ -71,6 +70,8 @@ public class NumberOfNodesCompletionEvaluatorTest { when(configEvaluated.getCompletionMode()).thenReturn(Mode.evaluated); when(configSetByNode.getCompletionMode()).thenReturn(Mode.setByNode); when(configNone.getCompletionMode()).thenReturn(Mode.none); + + sut = new AverageCompletionEvaluator(courseAssessmentService, UNWEIGHTED); } @Test @@ -78,7 +79,7 @@ public class NumberOfNodesCompletionEvaluatorTest { MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); CourseNode parent = new STCourseNode(); - Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + Double completion = sut.getCompletion(null, parent, scoreAccounting); assertThat(completion).isNull(); } @@ -92,28 +93,60 @@ public class NumberOfNodesCompletionEvaluatorTest { // Child: uncalculated CourseNode childUncalculated = new Card2BrainCourseNode(); parent.addChild(childUncalculated); - AssessmentEvaluation childUncalculatedEvaluation = createAssessmentEvaluation(mandatory, Double.valueOf(0.5), null, null); + AssessmentEvaluation childUncalculatedEvaluation = createAssessmentEvaluation(mandatory, null, Double.valueOf(0.5), null, null); scoreAccounting.put(childUncalculated, childUncalculatedEvaluation); when(courseAssessmentService.getAssessmentConfig(childUncalculated)).thenReturn(configSetByNode); // Child: Calculated CourseNode childCalculated = new STCourseNode(); parent.addChild(childCalculated); - AssessmentEvaluation childCalculatedEvaluation = createAssessmentEvaluation(mandatory, Double.valueOf(0.1), null, null); + AssessmentEvaluation childCalculatedEvaluation = createAssessmentEvaluation(mandatory, null, Double.valueOf(0.1), null, null); scoreAccounting.put(childCalculated, childCalculatedEvaluation); when(courseAssessmentService.getAssessmentConfig(childCalculated)).thenReturn(configEvaluated); // Child level 2: calculated CourseNode child2Uncalculated = new SPCourseNode(); - parent.addChild(child2Uncalculated); - AssessmentEvaluation child2UncalculatedEvaluation = createAssessmentEvaluation(mandatory, Double.valueOf(1.0), null, null); + childCalculated.addChild(child2Uncalculated); + AssessmentEvaluation child2UncalculatedEvaluation = createAssessmentEvaluation(mandatory, null, Double.valueOf(1.0), null, null); scoreAccounting.put(child2Uncalculated, child2UncalculatedEvaluation); when(courseAssessmentService.getAssessmentConfig(child2Uncalculated)).thenReturn(configSetByNode); - Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + Double completion = sut.getCompletion(null, parent, scoreAccounting); assertThat(completion).isEqualTo(0.75); } + @Test + public void shouldGetAverageWeightedByDuration() { + MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); + + // Parent + CourseNode parent = new STCourseNode(); + // Child 1 + CourseNode child1 = new Card2BrainCourseNode(); + parent.addChild(child1); + AssessmentEvaluation child1Evaluation = createAssessmentEvaluation(mandatory, null, Double.valueOf(0.0), null, null); + scoreAccounting.put(child1, child1Evaluation); + when(courseAssessmentService.getAssessmentConfig(child1)).thenReturn(configSetByNode); + // Child 2 + CourseNode child2 = new Card2BrainCourseNode(); + parent.addChild(child2); + AssessmentEvaluation child2Evaluation = createAssessmentEvaluation(mandatory, 2, Double.valueOf(0.5), null, null); + scoreAccounting.put(child2, child2Evaluation); + when(courseAssessmentService.getAssessmentConfig(child2)).thenReturn(configSetByNode); + // Child 3 + CourseNode child3 = new Card2BrainCourseNode(); + parent.addChild(child3); + AssessmentEvaluation child3Evaluation = createAssessmentEvaluation(mandatory, 3, Double.valueOf(1.0), null, null); + scoreAccounting.put(child3, child3Evaluation); + when(courseAssessmentService.getAssessmentConfig(child3)).thenReturn(configSetByNode); + + AverageCompletionEvaluator weightedSut = new AverageCompletionEvaluator(courseAssessmentService, DURATION_WEIGHTED); + Double completion = weightedSut.getCompletion(null, parent, scoreAccounting); + + double expected = (1 * 0.0 + 2 * 0.5 + 3 * 1.0) / 6; + assertThat(completion).isEqualTo(expected, offset(0.001)); + } + @Test public void shouldOnlyRespectMandatoryEvaluations() { MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); @@ -123,23 +156,23 @@ public class NumberOfNodesCompletionEvaluatorTest { // Child: mandatory CourseNode childMandatory = new Card2BrainCourseNode(); parent.addChild(childMandatory); - AssessmentEvaluation childMandatoryEvaluation = createAssessmentEvaluation(mandatory, Double.valueOf(0.5), null, null); + AssessmentEvaluation childMandatoryEvaluation = createAssessmentEvaluation(mandatory, null, Double.valueOf(0.5), null, null); scoreAccounting.put(childMandatory, childMandatoryEvaluation); when(courseAssessmentService.getAssessmentConfig(childMandatory)).thenReturn(configSetByNode); // Child: optional CourseNode childOptional = new Card2BrainCourseNode(); parent.addChild(childOptional); - AssessmentEvaluation childOptionalEvaluation = createAssessmentEvaluation(optional, Double.valueOf(0.6), null, null); + AssessmentEvaluation childOptionalEvaluation = createAssessmentEvaluation(optional, null, Double.valueOf(0.6), null, null); scoreAccounting.put(childOptional, childOptionalEvaluation); when(courseAssessmentService.getAssessmentConfig(childOptional)).thenReturn(configSetByNode); // Child: no obligation CourseNode childNoObligation = new Card2BrainCourseNode(); parent.addChild(childNoObligation); - AssessmentEvaluation childNoObligationEvaluation = createAssessmentEvaluation(null, Double.valueOf(0.7), null, null); + AssessmentEvaluation childNoObligationEvaluation = createAssessmentEvaluation(null, null, Double.valueOf(0.7), null, null); scoreAccounting.put(childNoObligation, childNoObligationEvaluation); when(courseAssessmentService.getAssessmentConfig(childNoObligation)).thenReturn(configSetByNode); - Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + Double completion = sut.getCompletion(null, parent, scoreAccounting); assertThat(completion).isEqualTo(0.5); } @@ -153,57 +186,56 @@ public class NumberOfNodesCompletionEvaluatorTest { // Child: fully assessed CourseNode childFullyAssessed = new Card2BrainCourseNode(); parent.addChild(childFullyAssessed); - AssessmentEvaluation childFullyAssessedEvaluation = createAssessmentEvaluation(mandatory, null, null, Boolean.TRUE); + AssessmentEvaluation childFullyAssessedEvaluation = createAssessmentEvaluation(mandatory, null, null, null, Boolean.TRUE); scoreAccounting.put(childFullyAssessed, childFullyAssessedEvaluation); when(courseAssessmentService.getAssessmentConfig(childFullyAssessed)).thenReturn(configNone); // Child: no status CourseNode childNoStatus = new Card2BrainCourseNode(); parent.addChild(childNoStatus); - AssessmentEvaluation childNoStatusEvaluation = createAssessmentEvaluation(mandatory, null, null, null); + AssessmentEvaluation childNoStatusEvaluation = createAssessmentEvaluation(mandatory, null, null, null, null); scoreAccounting.put(childNoStatus, childNoStatusEvaluation); when(courseAssessmentService.getAssessmentConfig(childNoStatus)).thenReturn(configNone); // Child: notReady CourseNode childNotReady = new Card2BrainCourseNode(); parent.addChild(childNotReady); - AssessmentEvaluation childNotReadyEvaluation = createAssessmentEvaluation(mandatory, null, AssessmentEntryStatus.notReady, null); + AssessmentEvaluation childNotReadyEvaluation = createAssessmentEvaluation(mandatory, null, null, AssessmentEntryStatus.notReady, null); scoreAccounting.put(childNotReady, childNotReadyEvaluation); when(courseAssessmentService.getAssessmentConfig(childNotReady)).thenReturn(configNone); // Child: notStarted CourseNode childNotStarted = new Card2BrainCourseNode(); parent.addChild(childNotStarted); - AssessmentEvaluation childNotStartedEvaluation = createAssessmentEvaluation(mandatory, null, AssessmentEntryStatus.notStarted, null); + AssessmentEvaluation childNotStartedEvaluation = createAssessmentEvaluation(mandatory, null, null, AssessmentEntryStatus.notStarted, null); scoreAccounting.put(childNotStarted, childNotStartedEvaluation); when(courseAssessmentService.getAssessmentConfig(childNotStarted)).thenReturn(configNone); // Child: inProgress CourseNode childInProgress = new Card2BrainCourseNode(); parent.addChild(childInProgress); - AssessmentEvaluation childInProgressEvaluation = createAssessmentEvaluation(mandatory, null, AssessmentEntryStatus.inProgress, null); + AssessmentEvaluation childInProgressEvaluation = createAssessmentEvaluation(mandatory, null, null, AssessmentEntryStatus.inProgress, null); scoreAccounting.put(childInProgress, childInProgressEvaluation); when(courseAssessmentService.getAssessmentConfig(childInProgress)).thenReturn(configNone); // Child: inReview CourseNode childInReview = new Card2BrainCourseNode(); parent.addChild(childInReview); - AssessmentEvaluation childInReviewEvaluation = createAssessmentEvaluation(mandatory, null, AssessmentEntryStatus.inReview, null); + AssessmentEvaluation childInReviewEvaluation = createAssessmentEvaluation(mandatory, null, null, AssessmentEntryStatus.inReview, null); scoreAccounting.put(childInReview, childInReviewEvaluation); when(courseAssessmentService.getAssessmentConfig(childInReview)).thenReturn(configNone); // Child: done CourseNode childDone = new Card2BrainCourseNode(); parent.addChild(childDone); - AssessmentEvaluation childDoneEvaluation = createAssessmentEvaluation(mandatory, null, AssessmentEntryStatus.done, null); + AssessmentEvaluation childDoneEvaluation = createAssessmentEvaluation(mandatory, null, null, AssessmentEntryStatus.done, null); scoreAccounting.put(childDone, childDoneEvaluation); when(courseAssessmentService.getAssessmentConfig(childDone)).thenReturn(configNone); - Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + Double completion = sut.getCompletion(null, parent, scoreAccounting); double expected = (1.0 + 0.0 + 0.0 + 0.0 + 0.5 + 0.75 + 1.0) / 7; assertThat(completion).isEqualTo(expected, offset(0.001)); - } - private AssessmentEvaluation createAssessmentEvaluation(AssessmentObligation obligation, Double completion, - AssessmentEntryStatus status, Boolean fullyAssessed) { + private AssessmentEvaluation createAssessmentEvaluation(AssessmentObligation obligation, Integer duration, + Double completion, AssessmentEntryStatus status, Boolean fullyAssessed) { return new AssessmentEvaluation(null, null, null, completion, status, null, fullyAssessed, null, null, null, - null, null, 0, null, null, null, null, obligation, null); + null, null, 0, null, null, null, null, obligation, duration); } } diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 98f2751b226f3ead5533ed779e58f21edc66d9c4..e634f91b7b0d27662f9d0e45914d48a38a55529a 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -460,7 +460,7 @@ import org.junit.runners.Suite; org.olat.course.learningpath.evaluation.LinearAccessEvaluator.class, org.olat.course.learningpath.manager.LearningPathNodeAccessProviderTest.class, org.olat.course.nodes.st.assessment.CumulatingDurationEvaluatorTest.class, - org.olat.course.nodes.st.assessment.NumberOfNodesCompletionEvaluatorTest.class, + org.olat.course.nodes.st.assessment.AverageCompletionEvaluatorTest.class, org.olat.course.nodes.st.assessment.STFullyAssessedEvaluatorTest.class, org.olat.course.nodes.st.assessment.STLastModificationsEvaluatorTest.class, org.olat.course.nodes.st.assessment.STLinearStatusEvaluatorTest.class,