From 00fdfd84ddeb3e6f9ceb85f7da8a42105002f46e Mon Sep 17 00:00:00 2001 From: uhensler <urs.hensler@frentix.com> Date: Mon, 23 Sep 2019 12:05:17 +0200 Subject: [PATCH] OO-4207: Calculate progress of structure course nodes without weight --- .../assessment/handler/AssessmentConfig.java | 10 +- .../handler/ModuleAssessmentConfig.java | 4 +- .../handler/NonAssessmentConfig.java | 4 +- .../manager/AssessmentHandlerRegistry.java | 72 ++++++ .../LearningPathOnlyAssessmentConfig.java | 4 +- .../nodes/basiclti/LTIAssessmentConfig.java | 4 +- .../IQIdentityListCourseNodeController.java | 5 +- .../nodes/iq/IQTESTAssessmentConfig.java | 6 +- .../nodes/scorm/ScormAssessmentConfig.java | 4 +- .../NumberOfNodesCompletionEvaluator.java | 110 +++++++++ .../st/assessment/STAssessmentConfig.java | 4 +- .../st/assessment/STAssessmentHandler.java | 25 ++- .../nodes/survey/SurveyAssessmentConfig.java | 4 +- .../run/scoring/AccountingEvaluators.java | 2 + .../scoring/AccountingEvaluatorsBuilder.java | 20 +- .../scoring/AccountingEvaluatorsFactory.java | 18 +- .../course/run/scoring/AccountingResult.java | 12 + .../run/scoring/AssessmentAccounting.java | 10 +- .../run/scoring/AssessmentEvaluation.java | 3 +- .../run/scoring/CompletionEvaluator.java | 36 +++ .../course/run/scoring/ScoreAccounting.java | 4 +- .../assessment/MappedScoreAccounting.java | 63 ++++++ .../NumberOfNodesCompletionEvaluatorTest.java | 209 ++++++++++++++++++ .../java/org/olat/test/AllTestsJunit4.java | 1 + 24 files changed, 594 insertions(+), 40 deletions(-) create mode 100644 src/main/java/org/olat/course/assessment/manager/AssessmentHandlerRegistry.java create mode 100644 src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java create mode 100644 src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java create mode 100644 src/test/java/org/olat/course/assessment/MappedScoreAccounting.java create mode 100644 src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java diff --git a/src/main/java/org/olat/course/assessment/handler/AssessmentConfig.java b/src/main/java/org/olat/course/assessment/handler/AssessmentConfig.java index 8394933494b..2f27a419447 100644 --- a/src/main/java/org/olat/course/assessment/handler/AssessmentConfig.java +++ b/src/main/java/org/olat/course/assessment/handler/AssessmentConfig.java @@ -27,6 +27,12 @@ package org.olat.course.assessment.handler; */ public interface AssessmentConfig { + public enum Mode { + none, + setByNode, + evaluated + } + /** * Real assessments are in efficiency statements and are shown in the assessment tool. * @@ -75,9 +81,9 @@ public interface AssessmentConfig { public Float getCutValue(); /** - * @return True if this course node can produces a completion variable for the learner + * @return if this course node can produces a completion variable for the learner */ - public boolean hasCompletion(); + public Mode getCompletionMode(); /** * @return True if this course node produces an attempts variable for the learner diff --git a/src/main/java/org/olat/course/assessment/handler/ModuleAssessmentConfig.java b/src/main/java/org/olat/course/assessment/handler/ModuleAssessmentConfig.java index ce59e6d54ca..6ad1a4bd68d 100644 --- a/src/main/java/org/olat/course/assessment/handler/ModuleAssessmentConfig.java +++ b/src/main/java/org/olat/course/assessment/handler/ModuleAssessmentConfig.java @@ -87,8 +87,8 @@ public abstract class ModuleAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return false; + public Mode getCompletionMode() { + return Mode.none; } @Override diff --git a/src/main/java/org/olat/course/assessment/handler/NonAssessmentConfig.java b/src/main/java/org/olat/course/assessment/handler/NonAssessmentConfig.java index 4ab29e3fc64..906b9d79230 100644 --- a/src/main/java/org/olat/course/assessment/handler/NonAssessmentConfig.java +++ b/src/main/java/org/olat/course/assessment/handler/NonAssessmentConfig.java @@ -78,8 +78,8 @@ public class NonAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return false; + public Mode getCompletionMode() { + return Mode.none; } @Override diff --git a/src/main/java/org/olat/course/assessment/manager/AssessmentHandlerRegistry.java b/src/main/java/org/olat/course/assessment/manager/AssessmentHandlerRegistry.java new file mode 100644 index 00000000000..60f3de0ec33 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/manager/AssessmentHandlerRegistry.java @@ -0,0 +1,72 @@ +/** + * <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.assessment.manager; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + +import org.olat.course.assessment.handler.AssessmentHandler; +import org.olat.course.assessment.handler.NonAssessmentHandler; +import org.olat.course.nodes.CourseNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +@Service +class AssessmentHandlerRegistry { + + private static final String NON_ASSESSMENT_TYPE = NonAssessmentHandler.NODE_TYPE; + + @Autowired + private List<AssessmentHandler> loadedAssessmentHandlers; + private Map<String, AssessmentHandler> assessmentHandlers = new HashMap<>(); + private AssessmentHandler nonAssessmentHandler; + + @PostConstruct + void initProviders() { + for (AssessmentHandler handler: loadedAssessmentHandlers) { + if (NON_ASSESSMENT_TYPE.equals(handler.acceptCourseNodeType())) { + nonAssessmentHandler = handler; + } else { + assessmentHandlers.put(handler.acceptCourseNodeType(), handler); + } + } + } + + AssessmentHandler getAssessmentHandler(CourseNode courseNode) { + AssessmentHandler handler = null; + if (courseNode != null) { + handler = assessmentHandlers.get(courseNode.getType()); + } + if (handler == null) { + handler = nonAssessmentHandler; + } + return handler; + } + +} diff --git a/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentConfig.java b/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentConfig.java index 1dbd27a16a2..09ebb4a3faf 100644 --- a/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentConfig.java +++ b/src/main/java/org/olat/course/learningpath/LearningPathOnlyAssessmentConfig.java @@ -70,8 +70,8 @@ public class LearningPathOnlyAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return false; + public Mode getCompletionMode() { + return Mode.none; } @Override diff --git a/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentConfig.java b/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentConfig.java index 61a0f43b09c..95b2d8ff808 100644 --- a/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentConfig.java +++ b/src/main/java/org/olat/course/nodes/basiclti/LTIAssessmentConfig.java @@ -94,8 +94,8 @@ public class LTIAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return false; + public Mode getCompletionMode() { + return Mode.none; } @Override diff --git a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java index 588ccecd7bf..23ff75409d8 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java @@ -53,6 +53,7 @@ import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.assessment.AssessmentHelper; 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.assessment.ui.tool.IdentityListCourseNodeController; import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols; import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; @@ -145,7 +146,7 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo super.initStatusColumns(columnsModel); IQTESTCourseNode testCourseNode = (IQTESTCourseNode)courseNode; AssessmentConfig assessmentConfig = courseAssessmentService.getAssessmentConfig(courseNode); - if(testCourseNode != null && assessmentConfig.hasCompletion()) { + if(testCourseNode != null && Mode.setByNode.equals(assessmentConfig.getCompletionMode())) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.currentCompletion)); } @@ -374,7 +375,7 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo (IQTESTCourseNode)courseNode, assessedIdentity, coachCourseEnv); } return new IdentityListCourseNodeToolsController(ureq, getWindowControl(), - (IQTESTCourseNode)courseNode, assessedIdentity, coachCourseEnv); + courseNode, assessedIdentity, coachCourseEnv); } private List<Identity> getIdentities() { diff --git a/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java b/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java index 82a742134a4..733a31d2071 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java +++ b/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java @@ -146,8 +146,10 @@ public class IQTESTAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return IQEditController.CONFIG_VALUE_QTI21.equals(courseNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE_QTI)); + public Mode getCompletionMode() { + return IQEditController.CONFIG_VALUE_QTI21.equals(courseNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE_QTI)) + ? Mode.setByNode + : Mode.none; } @Override diff --git a/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentConfig.java b/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentConfig.java index 8e1df913643..a55c9f4a354 100644 --- a/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentConfig.java +++ b/src/main/java/org/olat/course/nodes/scorm/ScormAssessmentConfig.java @@ -86,8 +86,8 @@ public class ScormAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return false; + public Mode getCompletionMode() { + return Mode.none; } @Override diff --git a/src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java b/src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java new file mode 100644 index 00000000000..6e21359cf32 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluator.java @@ -0,0 +1,110 @@ +/** + * <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.st.assessment; + +import java.util.ArrayList; +import java.util.List; + +import org.olat.core.util.nodes.INode; +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.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; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +@Component +public class NumberOfNodesCompletionEvaluator implements CompletionEvaluator { + + @Autowired + private CourseAssessmentService courseAssessmentService; + + @Override + public Double getCompletion(AssessmentEvaluation currentEvaluation, CourseNode courseNode, + CourseConfig courseConfig, ScoreAccounting scoreAccounting) { + + List<CourseNode> children = new ArrayList<>(); + collectChildren(children, courseNode); + + int count = 0; + double completion = 0.0; + for (CourseNode child: children) { + AssessmentEvaluation assessmentEvaluation = scoreAccounting.evalCourseNode(child); + if (isMandatory(assessmentEvaluation)) { + AssessmentConfig assessmentConfig = courseAssessmentService.getAssessmentConfig(child); + if (Mode.setByNode.equals(assessmentConfig.getCompletionMode())) { + count++; + completion += assessmentEvaluation.getCompletion() != null + ? assessmentEvaluation.getCompletion().doubleValue() + : 0.0; + } else if (Mode.none.equals(assessmentConfig.getCompletionMode())) { + count++; + completion += getCompletion(assessmentEvaluation); + } + } + } + + 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()); + } + + private double getCompletion(AssessmentEvaluation evaluation) { + if (evaluation.getFullyAssessed() != null && evaluation.getFullyAssessed().booleanValue()) return 1.0; + + AssessmentEntryStatus assessmentStatus = evaluation.getAssessmentStatus(); + if (assessmentStatus == null) return 0.0; + + switch (assessmentStatus) { + case notReady: return 0.0; + case notStarted: return 0.0; + case inProgress: return 0.5; + case inReview: return 0.75; + case done: return 1.0; + default: return 0.0; + } + } + +} diff --git a/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentConfig.java b/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentConfig.java index 54c6840a200..02e63938eca 100644 --- a/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentConfig.java +++ b/src/main/java/org/olat/course/nodes/st/assessment/STAssessmentConfig.java @@ -85,8 +85,8 @@ public class STAssessmentConfig implements AssessmentConfig { } @Override - public boolean hasCompletion() { - return false; + public Mode getCompletionMode() { + return Mode.evaluated; } @Override 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 cbe18864dcc..662be236cda 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 @@ -39,7 +39,6 @@ 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.DurationEvaluator; import org.olat.course.run.scoring.FullyAssessedEvaluator; import org.olat.course.run.scoring.LastModificationsEvaluator; import org.olat.course.run.scoring.PassedEvaluator; @@ -52,6 +51,7 @@ 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; /** @@ -63,9 +63,9 @@ import org.springframework.stereotype.Service; @Service public class STAssessmentHandler implements AssessmentHandler { + private static final CumulatingDurationEvaluator CUMULATION_DURATION_EVALUATOR = new CumulatingDurationEvaluator(); private static final ScoreEvaluator CONDITION_SCORE_EVALUATOR = new ConditionScoreEvaluator(); private static final PassedEvaluator CONDITION_PASSED_EVALUATOR = new ConditionPassedEvaluator(); - private static final DurationEvaluator CUMMULATING_DURATION_EVALUATOR = new CumulatingDurationEvaluator(); private static final StatusEvaluator SCORE_STATUS_EVALUATOR = new ScoreStatusEvaluator(); private static final StatusEvaluator STATUS_LEARNING_PATH_STATUS_EVALUATOR = new STLinearStatusEvaluator(); private static final FullyAssessedEvaluator FULLY_ASSESSED_EVALUATOR = new STFullyAssessedEvaluator(); @@ -76,14 +76,9 @@ public class STAssessmentHandler implements AssessmentHandler { .withStatusEvaluator(SCORE_STATUS_EVALUATOR) .withLastModificationsEvaluator(LAST_MODIFICATION_EVALUATOR) .build(); - private static final AccountingEvaluators LEARNING_PATH_EVALUATORS = AccountingEvaluatorsBuilder.builder() - .withDurationEvaluator(CUMMULATING_DURATION_EVALUATOR) - .withScoreEvaluator(CONDITION_SCORE_EVALUATOR) - .withPassedEvaluator(CONDITION_PASSED_EVALUATOR) - .withStatusEvaluator(STATUS_LEARNING_PATH_STATUS_EVALUATOR) - .withFullyAssessedEvaluator(FULLY_ASSESSED_EVALUATOR) - .withLastModificationsEvaluator(LAST_MODIFICATION_EVALUATOR) - .build(); + + @Autowired + private NumberOfNodesCompletionEvaluator numberOfNodesCompletionEvaluator; @Override public String acceptCourseNodeType() { @@ -109,7 +104,15 @@ public class STAssessmentHandler implements AssessmentHandler { @Override public AccountingEvaluators getEvaluators(CourseNode courseNode, NodeAccessType nodeAccessType) { if (LearningPathNodeAccessProvider.TYPE.equals(nodeAccessType.getType())) { - return LEARNING_PATH_EVALUATORS; + return 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(); } return CONVENTIONAL_EVALUATORS; } diff --git a/src/main/java/org/olat/course/nodes/survey/SurveyAssessmentConfig.java b/src/main/java/org/olat/course/nodes/survey/SurveyAssessmentConfig.java index 137aade6610..79f40b503fb 100644 --- a/src/main/java/org/olat/course/nodes/survey/SurveyAssessmentConfig.java +++ b/src/main/java/org/olat/course/nodes/survey/SurveyAssessmentConfig.java @@ -30,8 +30,8 @@ import org.olat.course.learningpath.LearningPathOnlyAssessmentConfig; public class SurveyAssessmentConfig extends LearningPathOnlyAssessmentConfig { @Override - public boolean hasCompletion() { - return true; + public Mode getCompletionMode() { + return Mode.setByNode; } } diff --git a/src/main/java/org/olat/course/run/scoring/AccountingEvaluators.java b/src/main/java/org/olat/course/run/scoring/AccountingEvaluators.java index 03e0ff00c01..ff2de92bdb4 100644 --- a/src/main/java/org/olat/course/run/scoring/AccountingEvaluators.java +++ b/src/main/java/org/olat/course/run/scoring/AccountingEvaluators.java @@ -37,6 +37,8 @@ public interface AccountingEvaluators { public LastModificationsEvaluator getLastModificationsEvaluator(); + public CompletionEvaluator getCompletionEvaluator(); + public StatusEvaluator getStatusEvaluator(); public FullyAssessedEvaluator getFullyAssessedEvaluator(); diff --git a/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsBuilder.java b/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsBuilder.java index 0b63a5ac42a..93076e0edc2 100644 --- a/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsBuilder.java +++ b/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsBuilder.java @@ -32,6 +32,7 @@ public class AccountingEvaluatorsBuilder { .withDurationEvaluator(AccountingEvaluatorsFactory.createNullDurationEvaluator()) .withScoreEvaluator(AccountingEvaluatorsFactory.createUnchangingScoreEvaluator()) .withPassedEvaluator(AccountingEvaluatorsFactory.createUnchangingPassedEvaluator()) + .withCompletionEvaluator(AccountingEvaluatorsFactory.createUnchangingCompletionEvaluator()) .withStatusEvaluator(AccountingEvaluatorsFactory.createUnchangingStatusEvaluator()) .withFullyAssessedEvaluator(AccountingEvaluatorsFactory.createUnchangingFullyAssessedEvaluator()) .withLastModificationsEvaluator(AccountingEvaluatorsFactory.createUnchangingLastModificationsEvaluator()) @@ -41,6 +42,7 @@ public class AccountingEvaluatorsBuilder { private DurationEvaluator durationEvaluator; private ScoreEvaluator scoreEvaluator; private PassedEvaluator passedEvaluator; + private CompletionEvaluator completionEvaluator; private StatusEvaluator statusEvaluator; private FullyAssessedEvaluator fullyAssessedEvaluator; private LastModificationsEvaluator lastModificationsEvaluator; @@ -69,6 +71,11 @@ public class AccountingEvaluatorsBuilder { return this; } + public AccountingEvaluatorsBuilder withCompletionEvaluator(CompletionEvaluator completionEvaluator) { + this.completionEvaluator = completionEvaluator; + return this; + } + public AccountingEvaluatorsBuilder withStatusEvaluator(StatusEvaluator statusEvaluator) { this.statusEvaluator = statusEvaluator; return this; @@ -98,6 +105,9 @@ public class AccountingEvaluatorsBuilder { impl.passedEvaluator = this.passedEvaluator != null ? this.passedEvaluator : AccountingEvaluatorsFactory.createUnchangingPassedEvaluator(); + impl.completionEvaluator = this.completionEvaluator != null + ? this.completionEvaluator + : AccountingEvaluatorsFactory.createUnchangingCompletionEvaluator(); impl.statusEvaluator = this.statusEvaluator != null ? this.statusEvaluator : AccountingEvaluatorsFactory.createUnchangingStatusEvaluator(); @@ -124,6 +134,7 @@ public class AccountingEvaluatorsBuilder { private DurationEvaluator durationEvaluator; private ScoreEvaluator scoreEvaluator; private PassedEvaluator passedEvaluator; + private CompletionEvaluator completionEvaluator; private StatusEvaluator statusEvaluator; private FullyAssessedEvaluator fullyAssessedEvaluator; private LastModificationsEvaluator lastModificationsEvaluator; @@ -147,7 +158,12 @@ public class AccountingEvaluatorsBuilder { public PassedEvaluator getPassedEvaluator() { return passedEvaluator; } - + + @Override + public CompletionEvaluator getCompletionEvaluator() { + return completionEvaluator; + } + @Override public StatusEvaluator getStatusEvaluator() { return statusEvaluator; @@ -162,7 +178,7 @@ public class AccountingEvaluatorsBuilder { public LastModificationsEvaluator getLastModificationsEvaluator() { return lastModificationsEvaluator; } - + } } 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 6d2173313df..25b521f67a0 100644 --- a/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsFactory.java +++ b/src/main/java/org/olat/course/run/scoring/AccountingEvaluatorsFactory.java @@ -22,6 +22,7 @@ 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; @@ -39,6 +40,7 @@ class AccountingEvaluatorsFactory { private static final DurationEvaluator NULL_DURATION_EVALUATOR = new NullDurationEvaluator(); private static final ScoreEvaluator UNCHANGING_SCORE_EVALUATOR = new UnchangingScoreEvaluator(); private static final PassedEvaluator UNCHANGING_PASSED_EVALUATOR = new UnchangingPassedEvaluator(); + private static final CompletionEvaluator UNCHANGING_COMPLETION_EVALUATOR = new UnchangingCompletionEvaluator(); private static final StatusEvaluator UNCHANGING_STATUS_EVALUATOR = new UnchangingStatusEvaluator(); private static final FullyAssessedEvaluator UNCHANGING_FULLY_ASSESSED_EVALUATOR = new UnchangingFullyAssessedEvaluator(); private static final LastModificationsEvaluator UNCHANGING_LAST_MODIFICATIONS_EVALUATOR = new UnchangingLastModificationEvaluator(); @@ -58,6 +60,10 @@ class AccountingEvaluatorsFactory { return UNCHANGING_PASSED_EVALUATOR; } + public static CompletionEvaluator createUnchangingCompletionEvaluator() { + return UNCHANGING_COMPLETION_EVALUATOR; + } + static StatusEvaluator createUnchangingStatusEvaluator() { return UNCHANGING_STATUS_EVALUATOR; } @@ -126,6 +132,16 @@ class AccountingEvaluatorsFactory { } } + private static class UnchangingCompletionEvaluator implements CompletionEvaluator { + + @Override + public Double getCompletion(AssessmentEvaluation currentEvaluation, CourseNode courseNode, + CourseConfig courseConfig, ScoreAccounting scoureAccounting) { + return currentEvaluation.getCompletion(); + } + + } + private static class UnchangingStatusEvaluator implements StatusEvaluator { @Override @@ -159,5 +175,5 @@ class AccountingEvaluatorsFactory { } } - + } diff --git a/src/main/java/org/olat/course/run/scoring/AccountingResult.java b/src/main/java/org/olat/course/run/scoring/AccountingResult.java index df8dacdf488..fba8d9b6f20 100644 --- a/src/main/java/org/olat/course/run/scoring/AccountingResult.java +++ b/src/main/java/org/olat/course/run/scoring/AccountingResult.java @@ -38,6 +38,7 @@ public class AccountingResult extends AssessmentEvaluation { private AssessmentObligation evaluatedObligation; private Float evaluatedScore; private Boolean evaluatedPassed; + private Double evaluatedCompletion; private AssessmentEntryStatus evaluatedStatus; private Boolean evaluatedFullyAssessed; private Date evaluatedLastUserModified; @@ -50,6 +51,7 @@ public class AccountingResult extends AssessmentEvaluation { this.evaluatedObligation = origin.getObligation(); this.evaluatedScore = origin.getScore(); this.evaluatedPassed = origin.getPassed(); + this.evaluatedCompletion = origin.getCompletion(); this.evaluatedStatus = origin.getAssessmentStatus(); this.evaluatedFullyAssessed = origin.getFullyAssessed(); this.evaluatedLastUserModified = origin.getLastUserModified(); @@ -92,6 +94,15 @@ public class AccountingResult extends AssessmentEvaluation { this.evaluatedPassed = passed; } + @Override + public Double getCompletion() { + return evaluatedCompletion; + } + + public void setCompletion(Double completion) { + this.evaluatedCompletion = completion; + } + @Override public AssessmentEntryStatus getAssessmentStatus() { return evaluatedStatus; @@ -136,6 +147,7 @@ public class AccountingResult extends AssessmentEvaluation { || !Objects.equals(origin.getFullyAssessed(), evaluatedFullyAssessed) || !Objects.equals(origin.getLastUserModified(), evaluatedLastUserModified) || !Objects.equals(origin.getLastCoachModified(), evaluatedLastCoachModified) + || !Objects.equals(origin.getCompletion(), evaluatedCompletion) || !Objects.equals(origin.getAssessmentStatus(), evaluatedStatus); } 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 4337bc690fd..18092b15fc7 100644 --- a/src/main/java/org/olat/course/run/scoring/AssessmentAccounting.java +++ b/src/main/java/org/olat/course/run/scoring/AssessmentAccounting.java @@ -157,9 +157,6 @@ public class AssessmentAccounting implements ScoreAccounting { result.setDuration(duration); } - //TODO uh score 1:1 übernehmen (con / lp) - //TODO uh passed 1:1 übernehmen (con / lp) - //TODO uh status. con = passed. lp = childen fully assessed ScoreEvaluator scoreEvaluator = evaluators.getScoreEvaluator(); Float score = scoreEvaluator.getScore(result, courseNode, userCourseEnvironment.getConditionInterpreter()); result.setScore(score); @@ -196,6 +193,12 @@ public class AssessmentAccounting implements ScoreAccounting { result.setLastUserModified(lastModifications.getLastUserModified()); result.setLastCoachModified(lastModifications.getLastCoachModified()); + CompletionEvaluator completionEvaluator = evaluators.getCompletionEvaluator(); + Double completion = completionEvaluator.getCompletion(result, courseNode, + userCourseEnvironment.getCourseEnvironment().getCourseConfig(), + this); + result.setCompletion(completion); + status = statusEvaluator.getStatus(result, children); result.setStatus(status); @@ -220,6 +223,7 @@ public class AssessmentAccounting implements ScoreAccounting { entry.setDuration(result.getDuration()); entry.setLastUserModified(result.getLastUserModified()); entry.setLastCoachModified(result.getLastCoachModified()); + entry.setDuration(result.getDuration()); entry.setAssessmentStatus(result.getAssessmentStatus()); entry.setFullyAssessed(result.getFullyAssessed()); diff --git a/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java b/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java index 315e05f9106..9fed8b4b348 100644 --- a/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java +++ b/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java @@ -22,6 +22,7 @@ package org.olat.course.run.scoring; import java.util.Date; import org.olat.course.assessment.handler.AssessmentConfig; +import org.olat.course.assessment.handler.AssessmentConfig.Mode; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.model.AssessmentEntryStatus; import org.olat.modules.assessment.model.AssessmentObligation; @@ -171,7 +172,7 @@ public class AssessmentEvaluation extends ScoreEvaluation { Double completion = null; Double currentRunCompletion = null; AssessmentRunStatus runStatus = null; - if(assessmentConfig.hasCompletion()) { + if(assessmentConfig.getCompletionMode() != null && !Mode.none.equals(assessmentConfig.getCompletionMode())) { completion = entry.getCompletion(); currentRunCompletion = entry.getCurrentRunCompletion(); runStatus = entry.getCurrentRunStatus(); diff --git a/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java b/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java new file mode 100644 index 00000000000..1a642a80a37 --- /dev/null +++ b/src/main/java/org/olat/course/run/scoring/CompletionEvaluator.java @@ -0,0 +1,36 @@ +/** + * <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.run.scoring; + +import org.olat.course.config.CourseConfig; +import org.olat.course.nodes.CourseNode; + +/** + * + * Initial date: 22 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public interface CompletionEvaluator { + + public Double getCompletion(AssessmentEvaluation currentEvaluation, CourseNode courseNode, + CourseConfig courseConfig, ScoreAccounting scoreAccounting); + +} diff --git a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java index 5a0f2f9c031..f6bf1c2cc34 100644 --- a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java +++ b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java @@ -45,9 +45,9 @@ public interface ScoreAccounting { /** * Evaluates the course node or simply returns the evaluation from the cache. - * @param cn + * @param cncourseNode * @return ScoreEvaluation */ - AssessmentEvaluation evalCourseNode(CourseNode cn); + AssessmentEvaluation evalCourseNode(CourseNode courseNode); } \ No newline at end of file diff --git a/src/test/java/org/olat/course/assessment/MappedScoreAccounting.java b/src/test/java/org/olat/course/assessment/MappedScoreAccounting.java new file mode 100644 index 00000000000..5e28800d6c3 --- /dev/null +++ b/src/test/java/org/olat/course/assessment/MappedScoreAccounting.java @@ -0,0 +1,63 @@ +/** + * <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.assessment; + +import java.util.HashMap; +import java.util.Map; + +import org.olat.course.nodes.CourseNode; +import org.olat.course.run.scoring.AssessmentEvaluation; +import org.olat.course.run.scoring.ScoreAccounting; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class MappedScoreAccounting implements ScoreAccounting { + + private Map<CourseNode, AssessmentEvaluation> nodeToEvaluation = new HashMap<>(); + + public void put(CourseNode courseNode, AssessmentEvaluation evaluation) { + nodeToEvaluation.put(courseNode, evaluation); + } + + @Override + public void evaluateAll() { + // + } + + @Override + public boolean evaluateAll(boolean update) { + return false; + } + + @Override + public AssessmentEvaluation getScoreEvaluation(CourseNode courseNode) { + return nodeToEvaluation.get(courseNode); + } + + @Override + public AssessmentEvaluation evalCourseNode(CourseNode courseNode) { + return nodeToEvaluation.get(courseNode); + } + +} diff --git a/src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java b/src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java new file mode 100644 index 00000000000..61a3aeda27f --- /dev/null +++ b/src/test/java/org/olat/course/nodes/st/assessment/NumberOfNodesCompletionEvaluatorTest.java @@ -0,0 +1,209 @@ +/** + * <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.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.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; +import org.olat.course.assessment.MappedScoreAccounting; +import org.olat.course.assessment.handler.AssessmentConfig; +import org.olat.course.assessment.handler.AssessmentConfig.Mode; +import org.olat.course.nodes.Card2BrainCourseNode; +import org.olat.course.nodes.CourseNode; +import org.olat.course.nodes.SPCourseNode; +import org.olat.course.nodes.STCourseNode; +import org.olat.course.run.scoring.AssessmentEvaluation; +import org.olat.modules.assessment.model.AssessmentEntryStatus; +import org.olat.modules.assessment.model.AssessmentObligation; + +/** + * + * Initial date: 23 Sep 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class NumberOfNodesCompletionEvaluatorTest { + + @Mock + private AssessmentConfig configSetByNode; + @Mock + private AssessmentConfig configEvaluated; + @Mock + private AssessmentConfig configNone; + + @Mock + private CourseAssessmentService courseAssessmentService; + + @InjectMocks + private NumberOfNodesCompletionEvaluator sut; + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(configEvaluated.getCompletionMode()).thenReturn(Mode.evaluated); + when(configSetByNode.getCompletionMode()).thenReturn(Mode.setByNode); + when(configNone.getCompletionMode()).thenReturn(Mode.none); + } + + @Test + public void shouldReturnNullIfItHasNoChildren() { + MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); + CourseNode parent = new STCourseNode(); + + Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + + assertThat(completion).isNull(); + } + + @Test + public void shouldGetAverageCompletionOfChildren() { + MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); + + // Parent + CourseNode parent = new STCourseNode(); + // Child: uncalculated + CourseNode childUncalculated = new Card2BrainCourseNode(); + parent.addChild(childUncalculated); + AssessmentEvaluation childUncalculatedEvaluation = createAssessmentEvaluation(mandatory, 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); + 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); + scoreAccounting.put(child2Uncalculated, child2UncalculatedEvaluation); + when(courseAssessmentService.getAssessmentConfig(child2Uncalculated)).thenReturn(configSetByNode); + + Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + + assertThat(completion).isEqualTo(0.75); + } + + @Test + public void shouldOnlyRespectMandatoryEvaluations() { + MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); + + // Parent: calculated + CourseNode parent = new STCourseNode(); + // Child: mandatory + CourseNode childMandatory = new Card2BrainCourseNode(); + parent.addChild(childMandatory); + AssessmentEvaluation childMandatoryEvaluation = createAssessmentEvaluation(mandatory, 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); + 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); + scoreAccounting.put(childNoObligation, childNoObligationEvaluation); + when(courseAssessmentService.getAssessmentConfig(childNoObligation)).thenReturn(configSetByNode); + + Double completion = sut.getCompletion(null, parent, null, scoreAccounting); + + assertThat(completion).isEqualTo(0.5); + } + + @Test + public void shouldAssumeCompletionIfTheCourseNodeDoesNotSetIt() { + MappedScoreAccounting scoreAccounting = new MappedScoreAccounting(); + + // Parent: calculated + CourseNode parent = new STCourseNode(); + // Child: fully assessed + CourseNode childFullyAssessed = new Card2BrainCourseNode(); + parent.addChild(childFullyAssessed); + AssessmentEvaluation childFullyAssessedEvaluation = createAssessmentEvaluation(mandatory, 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); + 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); + 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); + 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); + 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); + 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); + scoreAccounting.put(childDone, childDoneEvaluation); + when(courseAssessmentService.getAssessmentConfig(childDone)).thenReturn(configNone); + + Double completion = sut.getCompletion(null, parent, null, 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) { + return new AssessmentEvaluation(null, null, null, completion, status, null, fullyAssessed, null, null, null, + null, null, 0, null, null, null, null, obligation, null); + } + +} diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 74b8f0d9949..98f2751b226 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -460,6 +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.STFullyAssessedEvaluatorTest.class, org.olat.course.nodes.st.assessment.STLastModificationsEvaluatorTest.class, org.olat.course.nodes.st.assessment.STLinearStatusEvaluatorTest.class, -- GitLab