diff --git a/src/main/java/org/olat/core/gui/components/util/KeyValues.java b/src/main/java/org/olat/core/gui/components/util/KeyValues.java index c09e99629dce55baf243f2945dfb84f15ce55e2b..211ed12df47c743e14219b5d5cdc374013101cac 100644 --- a/src/main/java/org/olat/core/gui/components/util/KeyValues.java +++ b/src/main/java/org/olat/core/gui/components/util/KeyValues.java @@ -22,7 +22,6 @@ package org.olat.core.gui.components.util; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Objects; /** * @@ -103,7 +102,7 @@ public class KeyValues { } public boolean containsKey(String key) { - return keyValues.stream().map(KeyValue::getKey).anyMatch(k -> Objects.equals(k, key)); + return keyValues.stream().map(KeyValue::getKey).anyMatch(k -> k.equals(key)); } public int size() { diff --git a/src/main/java/org/olat/course/nodes/MSCourseNode.java b/src/main/java/org/olat/course/nodes/MSCourseNode.java index 71ad44b0948f01ae9861f6fed2f1d369ea791c13..b98aeaa932d0617406a1ae8c3834e68126adb8c2 100644 --- a/src/main/java/org/olat/course/nodes/MSCourseNode.java +++ b/src/main/java/org/olat/course/nodes/MSCourseNode.java @@ -41,20 +41,25 @@ import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.gui.translator.PackageTranslator; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; +import org.olat.core.id.Organisation; import org.olat.core.id.Roles; import org.olat.core.logging.OLATRuntimeException; +import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; +import org.olat.course.condition.ConditionEditController; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; import org.olat.course.nodes.ms.MSCourseNodeEditController; import org.olat.course.nodes.ms.MSCourseNodeRunController; -import org.olat.course.nodes.ms.MSEditFormController; +import org.olat.course.nodes.ms.MSEvaluationFormExecutionController; import org.olat.course.nodes.ms.MSIdentityListCourseNodeController; +import org.olat.course.nodes.ms.MSService; +import org.olat.course.nodes.ms.MinMax; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.properties.PersistingCoursePropertyManager; import org.olat.course.run.navigation.NodeRunConstructionResult; @@ -69,8 +74,14 @@ import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; import org.olat.modules.assessment.ui.AssessmentToolContainer; import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.modules.forms.EvaluationFormSession; +import org.olat.modules.forms.handler.EvaluationFormResource; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryImportExport; +import org.olat.repository.RepositoryManager; +import org.olat.repository.handlers.RepositoryHandler; +import org.olat.repository.handlers.RepositoryHandlerFactory; import org.olat.resource.OLATResource; /** @@ -80,16 +91,22 @@ import org.olat.resource.OLATResource; * @author BPS (<a href="http://www.bps-system.de/">BPS Bildungsportal Sachsen GmbH</a>) */ public class MSCourseNode extends AbstractAccessableCourseNode implements PersistentAssessableCourseNode { + private static final long serialVersionUID = -7741172700015384397L; + + @SuppressWarnings("deprecation") private static final String PACKAGE_MS = Util.getPackageName(MSCourseNodeRunController.class); + public static final int CURRENT_VERSION = 2; private static final String TYPE = "ms"; /** configuration: score can be set */ public static final String CONFIG_KEY_HAS_SCORE_FIELD = "hasScoreField"; /** configuration: score min value */ public static final String CONFIG_KEY_SCORE_MIN = "scoreMin"; + public static final Float CONFIG_DEFAULT_SCORE_MIN = Float.valueOf(0); /** configuration: score max value */ public static final String CONFIG_KEY_SCORE_MAX = "scoreMax"; + public static final Float CONFIG_DEFAULT_SCORE_MAX = Float.valueOf(0); /** configuration: passed can be set */ public static final String CONFIG_KEY_HAS_PASSED_FIELD = "hasPassedField"; /** configuration: passed set to when score higher than cut value */ @@ -104,13 +121,23 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis public static final String CONFIG_KEY_INFOTEXT_COACH = "nfoTextCoach"; /** configuration: infotext for coach */ public static final String CONFIG_KEY_OPTIONAL = "cnOptional"; + + public static final String CONFIG_KEY_SCORE = "score"; + public static final String CONFIG_VALUE_SCORE_NONE = "score.none"; + public static final String CONFIG_VALUE_SCORE_MANUAL = "score.manual"; + public static final String CONFIG_VALUE_SCORE_EVAL_FORM_SUM = "score.evaluation.form.sum"; + public static final String CONFIG_VALUE_SCORE_EVAL_FORM_AVG = "score.evaluation.form.avg"; + public static final String CONFIG_KEY_EVAL_FORM_ENABLED = "evaluation.form.enabled"; + public static final String CONFIG_KEY_EVAL_FORM_SOFTKEY = "evaluation.form.softkey"; + public static final String CONFIG_KEY_EVAL_FORM_SCALE = "evaluation.form.scale"; + public static final String CONFIG_DEFAULT_EVAL_FORM_SCALE = "1.0"; /** * Constructor for a course building block of type manual score */ public MSCourseNode() { super(TYPE); - MSCourseNode.initDefaultConfig(getModuleConfiguration()); + updateModuleConfigDefaults(true); } /** @@ -121,32 +148,47 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis */ public static void initDefaultConfig(ModuleConfiguration moduleConfiguration) { moduleConfiguration.set(CONFIG_KEY_HAS_SCORE_FIELD, Boolean.FALSE); - moduleConfiguration.set(CONFIG_KEY_SCORE_MIN, Float.valueOf(0)); - moduleConfiguration.set(CONFIG_KEY_SCORE_MAX, Float.valueOf(0)); + moduleConfiguration.set(CONFIG_KEY_SCORE_MIN, CONFIG_DEFAULT_SCORE_MIN); + moduleConfiguration.set(CONFIG_KEY_SCORE_MAX, CONFIG_DEFAULT_SCORE_MAX); moduleConfiguration.set(CONFIG_KEY_HAS_PASSED_FIELD, Boolean.TRUE); // no preset for passed cut value -> manual setting of passed moduleConfiguration.set(CONFIG_KEY_HAS_COMMENT_FIELD, Boolean.TRUE); moduleConfiguration.set(CONFIG_KEY_INFOTEXT_USER, ""); moduleConfiguration.set(CONFIG_KEY_INFOTEXT_COACH, ""); } + + @Override + public void updateModuleConfigDefaults(boolean isNewNode) { + ModuleConfiguration config = getModuleConfiguration(); + if (isNewNode) { + initDefaultConfig(config); + config.setStringValue(CONFIG_KEY_SCORE, CONFIG_VALUE_SCORE_NONE); + } + if (config.getConfigurationVersion() < 2) { + // migrate legacy configs + boolean hasScoreFiled = config.getBooleanSafe(CONFIG_KEY_HAS_SCORE_FIELD, false); + if (hasScoreFiled) { + config.setStringValue(CONFIG_KEY_SCORE, CONFIG_VALUE_SCORE_MANUAL); + } else { + config.setStringValue(CONFIG_KEY_SCORE, CONFIG_VALUE_SCORE_NONE); + } + // init configs from v1 + config.setBooleanEntry(CONFIG_KEY_HAS_INDIVIDUAL_ASSESSMENT_DOCS, false); + // new configs + config.setBooleanEntry(CONFIG_KEY_EVAL_FORM_ENABLED, false); + config.setStringValue(CONFIG_KEY_EVAL_FORM_SCALE, CONFIG_DEFAULT_EVAL_FORM_SCALE); + } + config.setConfigurationVersion(CURRENT_VERSION); + } - /** - * @see org.olat.course.nodes.CourseNode#createEditController(org.olat.core.gui.UserRequest, - * org.olat.core.gui.control.WindowControl, org.olat.course.ICourse) - */ @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { + updateModuleConfigDefaults(false); MSCourseNodeEditController childTabCntrllr = new MSCourseNodeEditController(ureq, wControl, this, course, euce); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, childTabCntrllr); } - /** - * @see org.olat.course.nodes.CourseNode#createNodeRunConstructionResult(org.olat.core.gui.UserRequest, - * org.olat.core.gui.control.WindowControl, - * org.olat.course.run.userview.UserCourseEnvironment, - * org.olat.course.run.userview.NodeEvaluation) - */ @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { @@ -166,64 +208,70 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return new NodeRunConstructionResult(wrappedCtrl); } - /** - * @see org.olat.course.nodes.CourseNode#getReferencedRepositoryEntry() - */ @Override public RepositoryEntry getReferencedRepositoryEntry() { - return null; + return getEvaluationForm(getModuleConfiguration()); } - /** - * @see org.olat.course.nodes.CourseNode#needsReferenceToARepositoryEntry() - */ @Override public boolean needsReferenceToARepositoryEntry() { - return false; + return getReferencedRepositoryEntry() != null? true: false; } - /** - * @see org.olat.course.nodes.CourseNode#isConfigValid() - */ + @SuppressWarnings("deprecation") + @Override + public StatusDescription[] isConfigValid(CourseEditorEnv cev) { + String translatorStr = Util.getPackageName(ConditionEditController.class); + List<StatusDescription> statusDescs = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); + return StatusDescriptionHelper.sort(statusDescs); + } + @Override public StatusDescription isConfigValid() { - /* - * first check the one click cache - */ - if (oneClickStatusCache != null) { return oneClickStatusCache[0]; } - - boolean isValid = MSEditFormController.isConfigValid(getModuleConfiguration()); - StatusDescription sd = StatusDescription.NOERROR; - if (!isValid) { - // FIXME: refine statusdescriptions by moving the statusdescription - // generation to the MSEditForm - String shortKey = "error.missingconfig.short"; - String longKey = "error.missingconfig.long"; - String[] params = new String[] { this.getShortTitle() }; - sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, PACKAGE_MS); - sd.setDescriptionForUnit(getIdent()); - // set which pane is affected by error - sd.setActivateableViewIdentifier(MSCourseNodeEditController.PANE_TAB_CONFIGURATION); - } - return sd; + return StatusDescription.NOERROR; } - /** - * @see org.olat.course.nodes.CourseNode#isConfigValid(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override - public StatusDescription[] isConfigValid(CourseEditorEnv cev) { - oneClickStatusCache = null; - // only here we know which translator to take for translating condition - // error messages - List<StatusDescription> sds = isConfigValidWithTranslator(cev, PACKAGE_MS, getConditionExpressions()); - oneClickStatusCache = StatusDescriptionHelper.sort(sds); - return oneClickStatusCache; + public void exportNode(File exportDirectory, ICourse course) { + RepositoryEntry re = getEvaluationForm(getModuleConfiguration()); + if (re == null) return; + + File fExportDirectory = new File(exportDirectory, getIdent()); + fExportDirectory.mkdirs(); + RepositoryEntryImportExport reie = new RepositoryEntryImportExport(re, fExportDirectory); + reie.exportDoExport(); + } + + @Override + public void importNode(File importDirectory, ICourse course, Identity owner, Organisation organisation, Locale locale, boolean withReferences) { + RepositoryEntryImportExport rie = new RepositoryEntryImportExport(importDirectory, getIdent()); + if(withReferences && rie.anyExportedPropertiesAvailable()) { + RepositoryHandler handler = RepositoryHandlerFactory.getInstance().getRepositoryHandler(EvaluationFormResource.TYPE_NAME); + RepositoryEntry re = handler.importResource(owner, rie.getInitialAuthor(), rie.getDisplayName(), + rie.getDescription(), false, organisation, locale, rie.importGetExportedFile(), null); + setEvaluationFormReference(re, getModuleConfiguration()); + } else { + removeEvaluationFormReference(getModuleConfiguration()); + } + } + + public static RepositoryEntry getEvaluationForm(ModuleConfiguration config) { + if (config == null) return null; + + String repoSoftkey = config.getStringValue(CONFIG_KEY_EVAL_FORM_SOFTKEY); + if (!StringHelper.containsNonWhitespace(repoSoftkey)) return null; + + return RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repoSoftkey, false); + } + + public static void setEvaluationFormReference(RepositoryEntry re, ModuleConfiguration moduleConfig) { + moduleConfig.set(CONFIG_KEY_EVAL_FORM_SOFTKEY, re.getSoftkey()); + } + + public static void removeEvaluationFormReference(ModuleConfiguration moduleConfig) { + moduleConfig.remove(CONFIG_KEY_EVAL_FORM_SOFTKEY); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public AssessmentEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) { if(hasPassedConfigured() || hasScoreConfigured() || hasCommentConfigured()) { @@ -244,10 +292,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return am.getAssessmentEntry(this, mySelf); } - /** - * @see org.olat.course.nodes.CourseNode#informOnDelete(org.olat.core.gui.UserRequest, - * org.olat.course.ICourse) - */ @Override public String informOnDelete(Locale locale, ICourse course) { CoursePropertyManager cpm = PersistingCoursePropertyManager.getInstance(course); @@ -257,10 +301,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return trans.translate("warn.nodedelete"); } - /** - * @see org.olat.course.nodes.CourseNode#cleanupOnDelete( - * org.olat.course.ICourse) - */ @Override public void cleanupOnDelete(ICourse course) { super.cleanupOnDelete(course); @@ -271,11 +311,13 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis OLATResource resource = course.getCourseEnvironment().getCourseGroupManager().getCourseResource(); CoreSpringFactory.getImpl(TaskExecutorManager.class).delete(resource, getIdent()); + + // Delete the surveys + MSService msService = CoreSpringFactory.getImpl(MSService.class); + RepositoryEntry ores = RepositoryManager.getInstance().lookupRepositoryEntry(course, true); + msService.deleteSessions(ores, getIdent()); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#hasCommentConfigured() - */ @Override public boolean hasCommentConfigured() { ModuleConfiguration config = getModuleConfiguration(); @@ -289,9 +331,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return getModuleConfiguration().getBooleanSafe(CONFIG_KEY_HAS_INDIVIDUAL_ASSESSMENT_DOCS, false); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#hasPassedConfigured() - */ @Override public boolean hasPassedConfigured() { ModuleConfiguration config = getModuleConfiguration(); @@ -300,20 +339,14 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return passed.booleanValue(); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#hasScoreConfigured() - */ @Override public boolean hasScoreConfigured() { + updateModuleConfigDefaults(false); ModuleConfiguration config = getModuleConfiguration(); - Boolean score = (Boolean) config.get(CONFIG_KEY_HAS_SCORE_FIELD); - if (score == null) return false; - return score.booleanValue(); + String scoreKey = config.getStringValue(CONFIG_KEY_SCORE); + return !CONFIG_VALUE_SCORE_NONE.equals(scoreKey); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#hasStatusConfigured() - */ @Override public boolean hasStatusConfigured() { return false; @@ -324,31 +357,37 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return false; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getMaxScoreConfiguration() - */ @Override public Float getMaxScoreConfiguration() { - if (!hasScoreConfigured()) { throw new OLATRuntimeException(MSCourseNode.class, "getMaxScore not defined when hasScore set to false", null); } - ModuleConfiguration config = getModuleConfiguration(); - Float max = (Float) config.get(CONFIG_KEY_SCORE_MAX); - return max; + if (!hasScoreConfigured()) { throw new OLATRuntimeException(MSCourseNode.class, "getMaxScore not defined", null); } + return getMinMax().getMax(); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getMinScoreConfiguration() - */ @Override public Float getMinScoreConfiguration() { - if (!hasScoreConfigured()) { throw new OLATRuntimeException(MSCourseNode.class, "getMinScore not defined when hasScore set to false", null); } + if (!hasScoreConfigured()) { throw new OLATRuntimeException(MSCourseNode.class, "getMinScore not defined", null); } + return getMinMax().getMin(); + } + + private MinMax getMinMax() { ModuleConfiguration config = getModuleConfiguration(); - Float min = (Float) config.get(CONFIG_KEY_SCORE_MIN); - return min; + String scoreConfig = config.getStringValue(CONFIG_KEY_SCORE); + String scaleConfig = config.getStringValue(CONFIG_KEY_EVAL_FORM_SCALE); + + if (CONFIG_VALUE_SCORE_MANUAL.equals(scoreConfig)) { + Float min = (Float) config.get(CONFIG_KEY_SCORE_MIN); + Float max = (Float) config.get(CONFIG_KEY_SCORE_MAX); + return MinMax.of(min, max); + } else if (CONFIG_VALUE_SCORE_EVAL_FORM_SUM.equals(scoreConfig)) { + MSService msService = CoreSpringFactory.getImpl(MSService.class); + return msService.calculateMinMaxSum(getEvaluationForm(config), Float.parseFloat(scaleConfig)); + } else if (CONFIG_VALUE_SCORE_EVAL_FORM_AVG.equals(scoreConfig)) { + MSService msService = CoreSpringFactory.getImpl(MSService.class); + return msService.calculateMinMaxAvg(getEvaluationForm(config), Float.parseFloat(scaleConfig)); + } + return MinMax.of(0.0f, 0.0f); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getCutValueConfiguration() - */ @Override public Float getCutValueConfiguration() { if (!hasPassedConfigured()) { throw new OLATRuntimeException(MSCourseNode.class, "getCutValue not defined when hasPassed set to false", null); } @@ -357,9 +396,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return cut; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getUserCoachComment(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public String getUserCoachComment(UserCourseEnvironment userCourseEnvironment) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); @@ -367,9 +403,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return coachCommentValue; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getUserUserComment(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public String getUserUserComment(UserCourseEnvironment userCourseEnvironment) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); @@ -382,9 +415,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return am.getIndividualAssessmentDocuments(this, userCourseEnvironment.getIdentityEnvironment().getIdentity()); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getUserLog(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public String getUserLog(UserCourseEnvironment userCourseEnvironment) { UserNodeAuditManager am = userCourseEnvironment.getCourseEnvironment().getAuditManager(); @@ -392,19 +422,12 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return logValue; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#isEditableConfigured() - */ @Override public boolean isEditableConfigured() { // manual scoring fields can be edited manually return true; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#updateUserCoachComment(java.lang.String, - * org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public void updateUserCoachComment(String coachComment, UserCourseEnvironment userCourseEnvironment) { if (coachComment != null) { @@ -414,11 +437,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis } } - /** - * @see org.olat.course.nodes.AssessableCourseNode#updateUserScoreEvaluation(org.olat.course.run.scoring.ScoreEvaluation, - * org.olat.course.run.userview.UserCourseEnvironment, - * org.olat.core.id.Identity) - */ @Override public void updateUserScoreEvaluation(ScoreEvaluation scoreEvaluation, UserCourseEnvironment userCourseEnvironment, Identity coachingIdentity, boolean incrementAttempts, Role by) { @@ -427,11 +445,6 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis am.saveScoreEvaluation(this, coachingIdentity, mySelf, new ScoreEvaluation(scoreEvaluation), userCourseEnvironment, incrementAttempts, by); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#updateUserUserComment(java.lang.String, - * org.olat.course.run.userview.UserCourseEnvironment, - * org.olat.core.id.Identity) - */ @Override public void updateUserUserComment(String userComment, UserCourseEnvironment userCourseEnvironment, Identity coachingIdentity) { if (userComment != null) { @@ -459,36 +472,22 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis } } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getUserAttempts(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public Integer getUserAttempts(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(MSCourseNode.class, "No attempts available in MS nodes", null); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#hasAttemptsConfigured() - */ @Override public boolean hasAttemptsConfigured() { return false; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#updateUserAttempts(java.lang.Integer, - * org.olat.course.run.userview.UserCourseEnvironment, - * org.olat.core.id.Identity) - */ @Override public void updateUserAttempts(Integer userAttempts, UserCourseEnvironment userCourseEnvironment, Identity coachingIdentity, Role by) { throw new OLATRuntimeException(MSCourseNode.class, "Attempts variable can't be updated in MS nodes", null); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#incrementUserAttempts(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public void incrementUserAttempts(UserCourseEnvironment userCourseEnvironment, Role by) { throw new OLATRuntimeException(MSCourseNode.class, "Attempts variable can't be updated in MS nodes", null); @@ -517,15 +516,10 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis am.updateLastModifications(this, assessedIdentity, userCourseEnvironment, by); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getDetailsEditController(org.olat.core.gui.UserRequest, - * org.olat.core.gui.control.WindowControl, - * org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public Controller getDetailsEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, - UserCourseEnvironment coachCourseenv, UserCourseEnvironment assessedUserCourseEnv) { - throw new OLATRuntimeException(MSCourseNode.class, "Details controler not available in MS nodes", null); + UserCourseEnvironment coachCourseEnv, UserCourseEnvironment assessedUserCourseEnv) { + return new MSEvaluationFormExecutionController(ureq, wControl, assessedUserCourseEnv, this); } @Override @@ -541,20 +535,61 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis return null; } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getDetailsListViewHeaderKey() - */ @Override public String getDetailsListViewHeaderKey() { throw new OLATRuntimeException(MSCourseNode.class, "Details not available in MS nodes", null); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#hasDetails() - */ @Override public boolean hasDetails() { - return false; + return getModuleConfiguration().getBooleanSafe(CONFIG_KEY_EVAL_FORM_ENABLED); } - + + public void updateScoreEvaluation(Identity identity, UserCourseEnvironment assessedUserCourseEnv, + Identity assessedIdentity, Role by, EvaluationFormSession session) { + AssessmentManager am = assessedUserCourseEnv.getCourseEnvironment().getAssessmentManager(); + MSService msService = CoreSpringFactory.getImpl(MSService.class); + ModuleConfiguration config = getModuleConfiguration(); + + // Get score + String scoreConfig = config.getStringValue(CONFIG_KEY_SCORE); + String scaleConfig = config.getStringValue(CONFIG_KEY_EVAL_FORM_SCALE); + float scale = Float.parseFloat(scaleConfig); + Float score = null; + if (CONFIG_VALUE_SCORE_EVAL_FORM_AVG.equals(scoreConfig)) { + score = msService.calculateScoreByAvg(session); + score = msService.scaleScore(score, scale); + } else if (CONFIG_VALUE_SCORE_EVAL_FORM_SUM.equals(scoreConfig)) { + score = msService.calculateScoreBySum(session); + score = msService.scaleScore(score, scale); + } else if (CONFIG_VALUE_SCORE_MANUAL.equals(scoreConfig)) { + ScoreEvaluation currentEval = getUserScoreEvaluation(am.getAssessmentEntry(this, assessedIdentity)); + score = currentEval.getScore(); + } + + // Score has to be in configured range. + MinMax minMax = getMinMax(); + if(score != null && minMax.getMax().floatValue() < score.floatValue()) { + score = minMax.getMax(); + } + if(score != null && minMax.getMin().floatValue() > score.floatValue()) { + score = minMax.getMin(); + } + + // Get passed + Float cutConfig = (Float) config.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); + Boolean passed = null; + if (cutConfig != null && score != null) { + boolean aboveCutValue = score.floatValue() >= cutConfig.floatValue(); + passed = Boolean.valueOf(aboveCutValue); + } else { + ScoreEvaluation currentEval = getUserScoreEvaluation(am.getAssessmentEntry(this, assessedIdentity)); + passed = currentEval.getPassed(); + } + + // save + ScoreEvaluation scoreEvaluation = new ScoreEvaluation(score, passed); + am.saveScoreEvaluation(this, identity, assessedIdentity, scoreEvaluation, assessedUserCourseEnv, false, by); + } + } \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/SurveyCourseNode.java b/src/main/java/org/olat/course/nodes/SurveyCourseNode.java index b532fe686f40bb4f8fe07c6fd5a38c8be400296c..a638274235ab0b7abe7deb01b1445e2b334bbdd8 100644 --- a/src/main/java/org/olat/course/nodes/SurveyCourseNode.java +++ b/src/main/java/org/olat/course/nodes/SurveyCourseNode.java @@ -19,12 +19,15 @@ */ package org.olat.course.nodes; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + import java.io.File; import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.zip.ZipOutputStream; +import org.apache.logging.log4j.Logger; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; @@ -33,7 +36,6 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.id.Identity; import org.olat.core.id.Organisation; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; @@ -56,6 +58,7 @@ import org.olat.course.statistic.StatisticType; import org.olat.modules.ModuleConfiguration; import org.olat.modules.forms.EvaluationFormManager; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.olat.modules.forms.SessionFilter; import org.olat.modules.forms.SessionFilterFactory; import org.olat.modules.forms.handler.EvaluationFormResource; @@ -165,7 +168,7 @@ public class SurveyCourseNode extends AbstractAccessableCourseNode { RepositoryEntry ores = userCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); SurveyRunSecurityCallback secCallback = new SurveyRunSecurityCallback(getModuleConfiguration(), userCourseEnv); Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity(); - return new SurveyStatisticResourceResult(ores, getIdent(), identity, secCallback); + return new SurveyStatisticResourceResult(of(ores, getIdent()), identity, secCallback); } return null; } @@ -241,9 +244,10 @@ public class SurveyCourseNode extends AbstractAccessableCourseNode { RepositoryEntry ores = RepositoryManager.getInstance().lookupRepositoryEntry(course, true); EvaluationFormManager evaluationFormManager = CoreSpringFactory.getImpl(EvaluationFormManager.class); RepositoryEntry formEntry = getEvaluationForm(getModuleConfiguration()); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(ores, getIdent()); + EvaluationFormSurveyIdentifier surveyIdent = of(ores, getIdent()); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(surveyIdent); if (survey == null) { - survey = evaluationFormManager.createSurvey(ores, getIdent(), formEntry); + survey = evaluationFormManager.createSurvey(surveyIdent, formEntry); } else { boolean isFormUpdateable = evaluationFormManager.isFormUpdateable(survey); if (isFormUpdateable) { @@ -258,7 +262,7 @@ public class SurveyCourseNode extends AbstractAccessableCourseNode { EvaluationFormManager evaluationFormManager = CoreSpringFactory.getImpl(EvaluationFormManager.class); RepositoryEntry ores = RepositoryManager.getInstance().lookupRepositoryEntry(course, true); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(ores, getIdent()); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(of(ores, getIdent())); SessionFilter filter = SessionFilterFactory.createSelectDone(survey); Form form = evaluationFormManager.loadForm(survey.getFormEntry()); @@ -282,7 +286,7 @@ public class SurveyCourseNode extends AbstractAccessableCourseNode { EvaluationFormManager evaluationFormManager = CoreSpringFactory.getImpl(EvaluationFormManager.class); RepositoryEntry ores = RepositoryManager.getInstance().lookupRepositoryEntry(course, true); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(ores, getIdent()); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(of(ores, getIdent())); evaluationFormManager.deleteSurvey(survey); } diff --git a/src/main/java/org/olat/course/nodes/TACourseNode.java b/src/main/java/org/olat/course/nodes/TACourseNode.java index 80b9fb9f4a3befcdbf415ad2965ece318c76131b..0755cc00da598c291fb8a7a36e0e26a24aa51cfc 100644 --- a/src/main/java/org/olat/course/nodes/TACourseNode.java +++ b/src/main/java/org/olat/course/nodes/TACourseNode.java @@ -38,6 +38,7 @@ import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import org.apache.logging.log4j.Logger; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.commons.services.taskexecutor.TaskExecutorManager; @@ -54,7 +55,6 @@ import org.olat.core.id.Identity; import org.olat.core.id.Organisation; import org.olat.core.id.Roles; import org.olat.core.logging.OLATRuntimeException; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.ExportUtil; import org.olat.core.util.FileUtils; diff --git a/src/main/java/org/olat/course/nodes/ms/MSConfigController.java b/src/main/java/org/olat/course/nodes/ms/MSConfigController.java new file mode 100644 index 0000000000000000000000000000000000000000..823bd40b87641ecad1a0f34c581d06b47510ec23 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/MSConfigController.java @@ -0,0 +1,574 @@ +/** + * <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.ms; + +import static org.olat.core.gui.components.util.KeyValues.entry; +import static org.olat.core.gui.translator.TranslatorHelper.translateAll; +import static org.olat.modules.forms.handler.EvaluationFormResource.FORM_XML_FILE; + +import java.io.File; +import java.util.Map; + +import org.olat.NewControllerFactory; +import org.olat.core.commons.fullWebApp.LayoutMain3ColsPreviewController; +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.FormLink; +import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; +import org.olat.core.gui.components.form.flexible.elements.RichTextElement; +import org.olat.core.gui.components.form.flexible.elements.SingleSelection; +import org.olat.core.gui.components.form.flexible.elements.StaticTextElement; +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.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.link.Link; +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.util.StringHelper; +import org.olat.course.ICourse; +import org.olat.course.nodes.MSCourseNode; +import org.olat.fileresource.FileResourceManager; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.ceditor.DataStorage; +import org.olat.modules.forms.EvaluationFormManager; +import org.olat.modules.forms.handler.EvaluationFormResource; +import org.olat.modules.forms.ui.EvaluationFormExecutionController; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.repository.controllers.ReferencableEntriesSearchController; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 10 Jun 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class MSConfigController extends FormBasicController { + + private static final String[] EMPTY_ARRAY = new String[]{}; + private static final String[] ENABLED_KEYS = new String[]{"on"}; + + private MultipleSelectionElement evaluationFormEnabledEl; + private StaticTextElement evaluationFormNotChoosen; + private FormLink evaluationFormLink; + private FormLink chooseLink; + private FormLink replaceLink; + private FormLink editLink; + private SingleSelection scoreEl; + private TextElement minEl; + private TextElement maxEl; + private TextElement scaleEl; + private MultipleSelectionElement passedEl; + private SingleSelection passedTypeEl; + private String[] trueFalseKeys; + private String[] passedTypeValues; + private TextElement cutEl; + private MultipleSelectionElement commentFlagEl; + private MultipleSelectionElement individualAssessmentDocsFlagEl; + private RichTextElement infotextUserEl; + private RichTextElement infotextCoachEl; + + private CloseableModalController cmc; + private ReferencableEntriesSearchController searchCtrl; + private LayoutMain3ColsPreviewController previewCtr; + + private final ModuleConfiguration config; + private final OLATResourceable ores; + private final String nodeIdent; + private RepositoryEntry formEntry; + private MinMax formMinMax; + + @Autowired + private MSService msService; + @Autowired + private EvaluationFormManager evaluationFormManager; + + public MSConfigController(UserRequest ureq, WindowControl wControl, ICourse course, + MSCourseNode courseNode) { + super(ureq, wControl, FormBasicController.LAYOUT_DEFAULT); + this.config = courseNode.getModuleConfiguration(); + this.ores = RepositoryManager.getInstance().lookupRepositoryEntry(course, true); + this.nodeIdent = courseNode.getIdent(); + this.formEntry = MSCourseNode.getEvaluationForm(config); + doCalculateMinMax(); + + trueFalseKeys = new String[] { Boolean.TRUE.toString(), Boolean.FALSE.toString() }; + passedTypeValues = new String[] { translate("form.passedtype.cutval"), translate("form.passedtype.manual") }; + + initForm(ureq); + } + + public void setDisplayOnly(boolean displayOnly) { + Map<String, FormItem> formItems = flc.getFormComponents(); + for (String formItemName : formItems.keySet()) { + formItems.get(formItemName).setEnabled(!displayOnly); + } + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + // Evaluation Form + evaluationFormEnabledEl = uifactory.addCheckboxesHorizontal("form.evaluation.enabled", formLayout, + ENABLED_KEYS, translateAll(getTranslator(), ENABLED_KEYS)); + evaluationFormEnabledEl.addActionListener(FormEvent.ONCHANGE); + Boolean evalFormEnabled = config.getBooleanEntry(MSCourseNode.CONFIG_KEY_EVAL_FORM_ENABLED); + evaluationFormEnabledEl.select(ENABLED_KEYS[0], evalFormEnabled); + + evaluationFormNotChoosen = uifactory.addStaticTextElement("form.evaluation.not.choosen", "form.evaluation", + translate("form.evaluation.not.choosen"), formLayout); + evaluationFormLink = uifactory.addFormLink("form.evaluation", "", translate("form.evaluation"), formLayout, + Link.NONTRANSLATED); + evaluationFormLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview"); + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + buttonsCont.setRootForm(mainForm); + formLayout.add(buttonsCont); + chooseLink = uifactory.addFormLink("form.evaluation.choose", buttonsCont, "btn btn-default o_xsmall"); + replaceLink = uifactory.addFormLink("form.evaluation.replace", buttonsCont, "btn btn-default o_xsmall"); + editLink = uifactory.addFormLink("form.evaluation.edit", buttonsCont, "btn btn-default o_xsmall"); + + uifactory.addSpacerElement("spacer0", formLayout, false); + + // Points + scoreEl = uifactory.addDropdownSingleselect("form.score", formLayout, EMPTY_ARRAY, EMPTY_ARRAY); + scoreEl.addActionListener(FormEvent.ONCHANGE); + + // Scale + String scale = config.getStringValue(MSCourseNode.CONFIG_KEY_EVAL_FORM_SCALE); + scaleEl = uifactory.addTextElement("form.scale", "form.scale", 8, scale, formLayout); + scaleEl.addActionListener(FormEvent.ONCHANGE); + + // Minimum + Float min = (Float) config.get(MSCourseNode.CONFIG_KEY_SCORE_MIN); + min = min != null? min: MSCourseNode.CONFIG_DEFAULT_SCORE_MIN; + minEl = uifactory.addTextElement("form.min", "form.min", 8, min.toString(), formLayout); + + // Maximim + Float max = (Float) config.get(MSCourseNode.CONFIG_KEY_SCORE_MAX); + max = max != null? max: MSCourseNode.CONFIG_DEFAULT_SCORE_MAX; + maxEl = uifactory.addTextElement("form.max", "form.max", 8, max.toString(), formLayout); + + uifactory.addSpacerElement("spacer1", formLayout, false); + + // display passed / failed + passedEl = uifactory.addCheckboxesHorizontal("form.passed", formLayout, ENABLED_KEYS, + translateAll(getTranslator(), ENABLED_KEYS)); + passedEl.addActionListener(FormEvent.ONCLICK); + Boolean passedField = config.getBooleanEntry(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD); + passedEl.select(ENABLED_KEYS[0], passedField); + + // passed/failed manually or automatically + passedTypeEl = uifactory.addRadiosVertical("form.passed.type", formLayout, trueFalseKeys, passedTypeValues); + passedTypeEl.addActionListener(FormEvent.ONCLICK); + passedTypeEl.setElementCssClass("o_sel_course_ms_display_type"); + + Float cut = (Float) config.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); + if (cut != null) { + passedTypeEl.select(trueFalseKeys[0], true); + } else { + passedTypeEl.select(trueFalseKeys[1], true); + cut = new Float(0.0); + } + + // Passing grade cut value + cutEl = uifactory.addTextElement("form.cut", "form.cut", 8, cut.toString(), formLayout); + + uifactory.addSpacerElement("spacer2", formLayout, false); + + // Comments + commentFlagEl = uifactory.addCheckboxesHorizontal("form.comment", formLayout, ENABLED_KEYS, + translateAll(getTranslator(), ENABLED_KEYS)); + Boolean commentField = config.getBooleanEntry(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD); + commentFlagEl.select(ENABLED_KEYS[0], commentField.booleanValue()); + + individualAssessmentDocsFlagEl = uifactory.addCheckboxesHorizontal("form.individual.assessment.docs", formLayout, ENABLED_KEYS, + translateAll(getTranslator(), ENABLED_KEYS)); + Boolean docsCf = config.getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_INDIVIDUAL_ASSESSMENT_DOCS, false); + individualAssessmentDocsFlagEl.select(ENABLED_KEYS[0], docsCf); + + uifactory.addSpacerElement("spacer3", formLayout, false); + + // Create the rich text fields. + String infoUser = (String) config.get(MSCourseNode.CONFIG_KEY_INFOTEXT_USER); + infoUser = infoUser != null? infoUser: ""; + infotextUserEl = uifactory.addRichTextElementForStringDataMinimalistic("infotextUser", "form.infotext.user", + infoUser, 10, -1, formLayout, getWindowControl()); + + String infoCoach = (String) config.get(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH); + infoCoach = infoCoach != null? infoCoach: ""; + infotextCoachEl = uifactory.addRichTextElementForStringDataMinimalistic("infotextCoach", "form.infotext.coach", + infoCoach, 10, -1, formLayout, getWindowControl()); + + uifactory.addFormSubmitButton("save", formLayout); + + updateUI(); + } + + private void updateUI() { + boolean formEnabled = evaluationFormEnabledEl.isAtLeastSelected(1); + boolean replacePossible = !msService.hasSessions(ores, nodeIdent); + + if (formEntry != null) { + String displayname = StringHelper.escapeHtml(formEntry.getDisplayname()); + evaluationFormLink.setI18nKey(displayname); + flc.setDirty(true); + } + boolean hasFormConfig = formEntry != null; + evaluationFormNotChoosen.setVisible(formEnabled && !hasFormConfig); + chooseLink.setVisible(formEnabled && !hasFormConfig); + evaluationFormLink.setVisible(formEnabled && hasFormConfig); + replaceLink.setVisible(formEnabled && hasFormConfig && replacePossible); + editLink.setVisible(formEnabled && hasFormConfig); + + // Score + String scoreKey = scoreEl.isOneSelected() + ? scoreEl.getSelectedKey() + : config.getStringValue(MSCourseNode.CONFIG_KEY_SCORE); + KeyValues scoreKV = new KeyValues(); + scoreKV.add(entry(MSCourseNode.CONFIG_VALUE_SCORE_NONE, translate("form.score.none"))); + scoreKV.add(entry(MSCourseNode.CONFIG_VALUE_SCORE_MANUAL, translate("form.score.manual"))); + if (evaluationFormEnabledEl.isAtLeastSelected(1)) { + scoreKV.add(entry(MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_SUM, translate("form.score.eval.sum"))); + scoreKV.add(entry(MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_AVG, translate("form.score.eval.avg"))); + } + scoreKey = scoreKV.containsKey(scoreKey) ? scoreKey : MSCourseNode.CONFIG_VALUE_SCORE_NONE; + scoreEl.setKeysAndValues(scoreKV.keys(), scoreKV.values(), null); + scoreEl.select(scoreKey, true); + + // min / max + minEl.setEnabled(true); + maxEl.setEnabled(true); + boolean minMaxVisible = !MSCourseNode.CONFIG_VALUE_SCORE_NONE.equals(scoreKey); + minEl.setVisible(minMaxVisible); + maxEl.setVisible(minMaxVisible); + if (MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_SUM.equals(scoreKey) + || MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_AVG.equals(scoreKey)) { + minEl.setValue(formMinMax.getMin().toString()); + minEl.setEnabled(false); + maxEl.setValue(formMinMax.getMax().toString()); + maxEl.setEnabled(false); + } + + // scaling factor + boolean scaleVisible = MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_SUM.equals(scoreKey) + || MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_AVG.equals(scoreKey); + scaleEl.setVisible(scaleVisible); + + // passed + boolean scoreEnabled = !MSCourseNode.CONFIG_VALUE_SCORE_NONE.equals(scoreKey); + boolean passedTypeVisible = scoreEnabled && passedEl.isAtLeastSelected(1); + passedTypeEl.setVisible(passedTypeVisible); + + // cut value + boolean cutVisible = passedTypeVisible && passedTypeEl.isOneSelected() && passedTypeEl.getSelected() == 0; + cutEl.setVisible(cutVisible); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source == evaluationFormEnabledEl) { + updateUI(); + } else if (source == chooseLink || source == replaceLink) { + doChooseEvaluationForm(ureq); + } else if (source == editLink) { + doEditEvaluationForm(ureq); + } else if (source == evaluationFormLink) { + doPreviewEvaluationForm(ureq); + } else if (source == scoreEl) { + doCalculateMinMax(); + updateUI(); + } else if (source == scaleEl) { + doCalculateMinMax(); + updateUI(); + } else if (source == passedEl) { + updateUI(); + } else if (source == passedTypeEl) { + updateUI(); + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (searchCtrl == source) { + if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { + doReplaceEvaluationForm(); + } + cmc.deactivate(); + cleanUp(); + } else if (source == previewCtr) { + cleanUp(); + } else if (cmc == source) { + cleanUp(); + } + super.event(ureq, source, event); + } + + private void cleanUp() { + removeAsListenerAndDispose(previewCtr); + removeAsListenerAndDispose(searchCtrl); + removeAsListenerAndDispose(cmc); + previewCtr = null; + searchCtrl = null; + cmc = null; + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = true; + + evaluationFormNotChoosen.clearError(); + if (evaluationFormNotChoosen.isVisible() && formEntry == null) { + evaluationFormNotChoosen.setErrorKey("form.legende.mandatory", null); + allOk = false; + } + + minEl.clearError(); + maxEl.clearError(); + boolean minIsFloat = isFloat(minEl.getValue()); + boolean maxIsFloat = isFloat(maxEl.getValue()); + if (minEl.isVisible() && minEl.isEnabled()) { + if (!minIsFloat) { + minEl.setErrorKey("form.error.wrongFloat", null); + allOk = false; + } + if (!maxIsFloat) { + maxEl.setErrorKey("form.error.wrongFloat", null); + allOk = false; + } + if (minIsFloat && maxIsFloat && isNotGreaterFloat(minEl.getValue(), maxEl.getValue())) { + maxEl.setErrorKey("form.error.minGreaterThanMax", null); + allOk = false; + } + } + + passedTypeEl.clearError(); + if (passedTypeEl.isVisible()) { + if (!passedTypeEl.isOneSelected()) { + passedTypeEl.setErrorKey("form.legende.mandatory", null); + allOk = false; + } + } + + cutEl.clearError(); + if (cutEl.isVisible()) { + boolean cutIsFloat = isFloat(cutEl.getValue()); + if (!cutIsFloat) { + cutEl.setErrorKey("form.error.wrongFloat", null); + allOk = false; + } + if (cutIsFloat && minIsFloat && maxIsFloat + && notInRange(minEl.getValue(), maxEl.getValue(), cutEl.getValue())) { + cutEl.setErrorKey("form.error.cutOutOfRange", null); + allOk = false; + } + } + + scaleEl.clearError(); + if (scaleEl.isVisible()) { + boolean scaleIsFloat = isFloat(scaleEl.getValue()); + if (!scaleIsFloat) { + scaleEl.setErrorKey("form.error.wrongFloat", null); + allOk = false; + } + } + + infotextCoachEl.clearError(); + if (infotextCoachEl.getValue().length() > 4000) { + infotextCoachEl.setErrorKey("input.toolong", new String[] {"4000"}); + allOk = false; + } + + infotextUserEl.clearError(); + if (infotextUserEl.getValue().length() > 4000) { + infotextUserEl.setErrorKey("input.toolong", new String[] {"4000"}); + allOk = false; + } + + return allOk & super.validateFormLogic(ureq); + } + + private boolean isFloat(String val) { + if (StringHelper.containsNonWhitespace(val)) { + try { + Float.parseFloat(val); + return true; + } catch (NumberFormatException e) { + // + } + } + return false; + } + + private boolean isNotGreaterFloat(String min, String max) { + return Float.parseFloat(min) >= Float.parseFloat(max); + } + + private boolean notInRange(String min, String max, String cut) { + return Float.parseFloat(cut) < Float.parseFloat(min) + || Float.parseFloat(cut) > Float.parseFloat(max); + } + + @Override + protected void formOK(UserRequest ureq) { + updateConfig(); + fireEvent(ureq, Event.DONE_EVENT); + } + + private void updateConfig() { + boolean evalFormEnabled = evaluationFormEnabledEl.isAtLeastSelected(1); + config.setBooleanEntry(MSCourseNode.CONFIG_KEY_EVAL_FORM_ENABLED, evalFormEnabled); + if (evalFormEnabled) { + MSCourseNode.setEvaluationFormReference(formEntry, config); + } else { + MSCourseNode.removeEvaluationFormReference(config); + } + + config.setStringValue(MSCourseNode.CONFIG_KEY_SCORE, scoreEl.getSelectedKey()); + + Float minScore = minEl.isVisible() + ? Float.parseFloat(minEl.getValue()) + : MSCourseNode.CONFIG_DEFAULT_SCORE_MIN; + config.set(MSCourseNode.CONFIG_KEY_SCORE_MIN, minScore); + + Float maxScore = maxEl.isVisible() + ? Float.parseFloat(maxEl.getValue()) + : MSCourseNode.CONFIG_DEFAULT_SCORE_MAX; + config.set(MSCourseNode.CONFIG_KEY_SCORE_MAX, maxScore); + + String scale = scaleEl.isVisible() + ? scaleEl.getValue() + : MSCourseNode.CONFIG_DEFAULT_EVAL_FORM_SCALE; + config.setStringValue(MSCourseNode.CONFIG_KEY_EVAL_FORM_SCALE, scale); + + boolean showPassed = passedEl.isAtLeastSelected(1); + config.set(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD, Boolean.valueOf(showPassed)); + + if (showPassed) { + // do cut value + Boolean cutAutomatically = Boolean.valueOf(passedTypeEl.getSelectedKey()); + if (cutAutomatically.booleanValue()) { + config.set(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE, Float.valueOf(cutEl.getValue())); + } else { + config.remove(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); + } + } else { + config.remove(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); + } + + Boolean commentFieldEnabled = Boolean.valueOf(commentFlagEl.isSelected(0)); + config.set(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD, commentFieldEnabled); + + Boolean individualAssessmentEnabled = Boolean.valueOf(individualAssessmentDocsFlagEl.isSelected(0)); + config.setBooleanEntry(MSCourseNode.CONFIG_KEY_HAS_INDIVIDUAL_ASSESSMENT_DOCS, individualAssessmentEnabled); + + String infoTextUser = infotextUserEl.getValue(); + if (StringHelper.containsNonWhitespace(infoTextUser)) { + config.set(MSCourseNode.CONFIG_KEY_INFOTEXT_USER, infoTextUser); + } else { + config.remove(MSCourseNode.CONFIG_KEY_INFOTEXT_USER); + } + + String infoTextCoach = infotextCoachEl.getValue(); + if (StringHelper.containsNonWhitespace(infoTextCoach)) { + config.set(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH, infoTextCoach); + } else { + config.remove(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH); + } + } + + private void doChooseEvaluationForm(UserRequest ureq) { + searchCtrl = new ReferencableEntriesSearchController(getWindowControl(), ureq, + EvaluationFormResource.TYPE_NAME, translate("form.evaluation.choose")); + this.listenTo(searchCtrl); + cmc = new CloseableModalController(getWindowControl(), translate("close"), + searchCtrl.getInitialComponent(), true, translate("form.evaluation.choose")); + cmc.activate(); + } + + private void doReplaceEvaluationForm() { + formEntry = searchCtrl.getSelectedEntry(); + doCalculateMinMax(); + updateUI(); + } + + private void doCalculateMinMax() { + String scoreKey = scoreEl != null && scoreEl.isOneSelected() + ? scoreEl.getSelectedKey() + : config.getStringValue(MSCourseNode.CONFIG_KEY_SCORE); + String scale = scaleEl != null + ? scaleEl.getValue() + : config.getStringValue(MSCourseNode.CONFIG_KEY_EVAL_FORM_SCALE); + float scalingFactor = floatOrZero(scale); + + switch (scoreKey) { + case MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_SUM: + formMinMax = msService.calculateMinMaxSum(formEntry, scalingFactor); + break; + case MSCourseNode.CONFIG_VALUE_SCORE_EVAL_FORM_AVG: + formMinMax = msService.calculateMinMaxAvg(formEntry, scalingFactor); + break; + default: + formMinMax = null; + } + } + + private float floatOrZero(String val) { + try { + return Float.parseFloat(val); + } catch (NumberFormatException e) { + // + } + return 0; + } + + private void doEditEvaluationForm(UserRequest ureq) { + String bPath = "[RepositoryEntry:" + formEntry.getKey() + "][Editor:0]"; + NewControllerFactory.getInstance().launch(bPath, ureq, getWindowControl()); + } + + private void doPreviewEvaluationForm(UserRequest ureq) { + File repositoryDir = new File( + FileResourceManager.getInstance().getFileResourceRoot(formEntry.getOlatResource()), + FileResourceManager.ZIPDIR); + File formFile = new File(repositoryDir, FORM_XML_FILE); + DataStorage storage = evaluationFormManager.loadStorage(formEntry); + Controller controller = new EvaluationFormExecutionController(ureq, getWindowControl(), formFile, storage); + + previewCtr = new LayoutMain3ColsPreviewController(ureq, getWindowControl(), null, + controller.getInitialComponent(), null); + previewCtr.addDisposableChildController(controller); + previewCtr.activate(); + listenTo(previewCtr); + } + + @Override + protected void doDispose() { + // + } + +} diff --git a/src/main/java/org/olat/course/nodes/ms/MSCourseNodeConfiguration.java b/src/main/java/org/olat/course/nodes/ms/MSCourseNodeConfiguration.java index e0ab3f79f3fa3d26369647a4e5b3c55789440af1..ca78aa8f17c2abca8012c412b2726d3f5a1dc338 100644 --- a/src/main/java/org/olat/course/nodes/ms/MSCourseNodeConfiguration.java +++ b/src/main/java/org/olat/course/nodes/ms/MSCourseNodeConfiguration.java @@ -34,12 +34,7 @@ import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.CourseNodeConfiguration; import org.olat.course.nodes.CourseNodeGroup; import org.olat.course.nodes.MSCourseNode; -/** - * - * Description:<br> - * TODO: guido Class Description for MSCourseNodeConfiguration - * - */ + public class MSCourseNodeConfiguration extends AbstractCourseNodeConfiguration { private MSCourseNodeConfiguration() { diff --git a/src/main/java/org/olat/course/nodes/ms/MSCourseNodeEditController.java b/src/main/java/org/olat/course/nodes/ms/MSCourseNodeEditController.java index 6c75940d658ebd1785e5a306e72e2e18084f6884..e5a0c5956e4c67f8ddb1e66d03543151077c8501 100644 --- a/src/main/java/org/olat/course/nodes/ms/MSCourseNodeEditController.java +++ b/src/main/java/org/olat/course/nodes/ms/MSCourseNodeEditController.java @@ -61,7 +61,7 @@ public class MSCourseNodeEditController extends ActivateableTabbableDefaultContr private MSCourseNode msNode; private VelocityContainer configurationVC; - private MSEditFormController modConfigController; + private MSConfigController configController; private HighScoreEditController highScoreNodeConfigController; private ConditionEditController accessibilityCondContr; @@ -95,9 +95,9 @@ public class MSCourseNodeEditController extends ActivateableTabbableDefaultContr AssessmentHelper.getAssessableNodes(editorModel, msNode)); this.listenTo(accessibilityCondContr); - modConfigController = new MSEditFormController(ureq, wControl, msNode.getModuleConfiguration()); - listenTo(modConfigController); - configurationVC.put("mseditform", modConfigController.getInitialComponent()); + configController = new MSConfigController(ureq, wControl, course, msNode); + listenTo(configController); + configurationVC.put("mseditform", configController.getInitialComponent()); highScoreNodeConfigController = new HighScoreEditController(ureq, wControl, msNode.getModuleConfiguration()); listenTo(highScoreNodeConfigController); @@ -107,26 +107,18 @@ public class MSCourseNodeEditController extends ActivateableTabbableDefaultContr hasLogEntries = auditManager.hasUserNodeLogs(msNode); configurationVC.contextPut("hasLogEntries", new Boolean(hasLogEntries)); if (hasLogEntries) { - modConfigController.setDisplayOnly(true); + configController.setDisplayOnly(true); } } - /** - * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, - * org.olat.core.gui.components.Component, org.olat.core.gui.control.Event) - */ @Override public void event(UserRequest ureq, Component source, Event event) { if (source == editScoringConfigButton) { - modConfigController.setDisplayOnly(false); + configController.setDisplayOnly(false); configurationVC.contextPut("isOverwriting", new Boolean(true)); } } - /** - * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, - * org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event) - */ @Override public void event(UserRequest ureq, Controller source, Event event) { if (source == accessibilityCondContr) { @@ -135,28 +127,11 @@ public class MSCourseNodeEditController extends ActivateableTabbableDefaultContr msNode.setPreConditionAccess(cond); fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); } - } else if (source == modConfigController) { - if (event == Event.CANCELLED_EVENT) { - // reset form - - if (modConfigController != null) { - removeAsListenerAndDispose(modConfigController); - } - modConfigController = new MSEditFormController(ureq, getWindowControl(), msNode.getModuleConfiguration()); - listenTo(modConfigController); - configurationVC.put("mseditform", modConfigController.getInitialComponent()); - if (hasLogEntries) { - modConfigController.setDisplayOnly(true); - } - configurationVC.contextPut("isOverwriting", new Boolean(false)); - return; - - } else if (event == Event.DONE_EVENT) { - modConfigController.updateModuleConfiguration(msNode.getModuleConfiguration()); + } else if (source == configController) { + if (event == Event.DONE_EVENT) { fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); } updateHighscoreTab(); - } else if (source == highScoreNodeConfigController){ if (event == Event.DONE_EVENT) { fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); @@ -165,13 +140,10 @@ public class MSCourseNodeEditController extends ActivateableTabbableDefaultContr } private void updateHighscoreTab() { - Boolean sf = msNode.getModuleConfiguration().getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD,false); - myTabbedPane.setEnabled(4, sf); + myTabbedPane.setEnabled(4, msNode.hasScoreConfigured()); } - /** - * @see org.olat.core.gui.control.generic.tabbable.TabbableDefaultController#addTabs(org.olat.core.gui.components.TabbedPane) - */ + @Override public void addTabs(TabbedPane tabbedPane) { myTabbedPane = tabbedPane; tabbedPane.addTab(translate(PANE_TAB_ACCESSIBILITY), accessibilityCondContr.getWrappedDefaultAccessConditionVC(translate("condition.accessibility.title"))); @@ -181,12 +153,9 @@ public class MSCourseNodeEditController extends ActivateableTabbableDefaultContr } - /** - * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) - */ @Override protected void doDispose() { - //child controllers registered with listenTo() get disposed in BasicController + // } @Override diff --git a/src/main/java/org/olat/course/nodes/ms/MSEvaluationFormExecutionController.java b/src/main/java/org/olat/course/nodes/ms/MSEvaluationFormExecutionController.java new file mode 100644 index 0000000000000000000000000000000000000000..dd7a9428f9f799e8306336c8e1622d7635a3a5a5 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/MSEvaluationFormExecutionController.java @@ -0,0 +1,161 @@ +/** + * <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.ms; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; +import org.olat.core.gui.components.velocity.VelocityContainer; +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.controller.BasicController; +import org.olat.core.id.Identity; +import org.olat.course.assessment.AssessmentManager; +import org.olat.course.assessment.ui.tool.AssessmentFormCallback; +import org.olat.course.nodes.MSCourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.assessment.AssessmentEntry; +import org.olat.modules.assessment.Role; +import org.olat.modules.assessment.model.AssessmentEntryStatus; +import org.olat.modules.forms.EvaluationFormSession; +import org.olat.modules.forms.EvaluationFormSessionStatus; +import org.olat.modules.forms.ui.EvaluationFormExecutionController; +import org.olat.repository.RepositoryEntry; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 11 Jun 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class MSEvaluationFormExecutionController extends BasicController implements AssessmentFormCallback { + + private final VelocityContainer mainVC; + private final Link reopenLink; + private EvaluationFormExecutionController executionCtrl; + + private final UserCourseEnvironment assessedUserCourseEnv; + private final ModuleConfiguration config; + private final MSCourseNode msCourseNode; + + private EvaluationFormSession session; + private boolean assessmentDone; + + @Autowired + private MSService msService; + + public MSEvaluationFormExecutionController(UserRequest ureq, WindowControl wControl, + UserCourseEnvironment assessedUserCourseEnv, MSCourseNode msCourseNode) { + super(ureq, wControl); + this.assessedUserCourseEnv = assessedUserCourseEnv; + this.msCourseNode = msCourseNode; + this.config = msCourseNode.getModuleConfiguration(); + + RepositoryEntry formEntry = MSCourseNode.getEvaluationForm(config); + RepositoryEntry ores = assessedUserCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + String nodeIdent = msCourseNode.getIdent(); + Identity assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); + session = msService.getOrCreateSession(formEntry, ores, nodeIdent, assessedIdentity); + + AssessmentManager am = assessedUserCourseEnv.getCourseEnvironment().getAssessmentManager(); + AssessmentEntry aEntry = am.getAssessmentEntry(msCourseNode, assessedIdentity); + assessmentDone = aEntry != null && aEntry.getAssessmentStatus() == AssessmentEntryStatus.done; + + mainVC = createVelocityContainer("evaluation_form_execution"); + reopenLink = LinkFactory.createButton("evaluation.execution.reopen", mainVC, this); + updateUI(ureq); + + putInitialPanel(mainVC); + } + + private void updateUI(UserRequest ureq) { + refreshExecutionController(ureq); + updateUIReopen(); + } + + private void refreshExecutionController(UserRequest ureq) { + if (executionCtrl != null) { + mainVC.remove(executionCtrl.getInitialComponent()); + removeAsListenerAndDispose(executionCtrl); + } + + executionCtrl = new EvaluationFormExecutionController(ureq, getWindowControl(), null, null, session, null, null, null, false, true); + listenTo(executionCtrl); + mainVC.put("execution", executionCtrl.getInitialComponent()); + } + + private void updateUIReopen() { + boolean reopenVisible = !assessmentDone && isSessionClosed(); + reopenLink.setVisible(reopenVisible); + mainVC.setDirty(true); + } + + private boolean isSessionClosed() { + return EvaluationFormSessionStatus.done.equals(session.getEvaluationFormSessionStatus()); + } + + @Override + public void assessmentDone(UserRequest ureq) { + assessmentDone = true; + session = msService.closeSession(session); + updateUI(ureq); + } + + @Override + public void assessmentReopen(UserRequest ureq) { + assessmentDone = false; + session = msService.reopenSession(session); + updateUI(ureq); + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + if (source == reopenLink) { + session = msService.reopenSession(session); + updateUI(ureq); + } + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == executionCtrl && Event.DONE_EVENT.equals(event)) { + doSetAssessmentScore(); + fireEvent(ureq, Event.CHANGED_EVENT); + } + super.event(ureq, source, event); + } + + private void doSetAssessmentScore() { + session = msService.getSession(session); + Identity assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); + msCourseNode.updateScoreEvaluation(getIdentity(), assessedUserCourseEnv, assessedIdentity, Role.coach, session); + updateUIReopen(); + } + + @Override + protected void doDispose() { + // + } + +} diff --git a/src/main/java/org/olat/course/nodes/ms/MSService.java b/src/main/java/org/olat/course/nodes/ms/MSService.java new file mode 100644 index 0000000000000000000000000000000000000000..0c431c64dfc929b99c1724b53bf9f4382f56e164 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/MSService.java @@ -0,0 +1,73 @@ +/** + * <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.ms; + +import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; +import org.olat.modules.forms.EvaluationFormSession; +import org.olat.modules.forms.EvaluationFormSessionRef; +import org.olat.repository.RepositoryEntry; + +/** + * + * Initial date: 11 Jun 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public interface MSService { + + EvaluationFormSession getOrCreateSession(RepositoryEntry formEntry, RepositoryEntry ores, String nodeIdent, + Identity assessedIdentity); + + EvaluationFormSession getSession(EvaluationFormSessionRef sessionRef); + + EvaluationFormSession closeSession(EvaluationFormSession session); + + EvaluationFormSession reopenSession(EvaluationFormSession session); + + boolean hasSessions(OLATResourceable ores, String nodeIdent); + + void deleteSessions(RepositoryEntry ores, String nodeIdent); + + /** + * Calculates the possible minimum and maximum sum of all rubrics in the + * evaluation form of the formEntry. + * + * @param formEntry + * @return + */ + MinMax calculateMinMaxSum(RepositoryEntry formEntry, float scalingFactor); + + /** + * Calculates the possible minimum and maximum average of all rubrics in the + * evaluation form of the formEntry. + * + * @param formEntry + * @return + */ + MinMax calculateMinMaxAvg(RepositoryEntry formEntry, float scalingFactor); + + Float calculateScoreBySum(EvaluationFormSession session); + + Float calculateScoreByAvg(EvaluationFormSession session); + + Float scaleScore(Float score, float scalingFactor); + +} diff --git a/src/main/java/org/olat/course/nodes/ms/MinMax.java b/src/main/java/org/olat/course/nodes/ms/MinMax.java new file mode 100644 index 0000000000000000000000000000000000000000..8ca2aff33484250b17c98077b41ebeb188bd6525 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/MinMax.java @@ -0,0 +1,60 @@ +/** + * <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.ms; + +/** + * + * Initial date: 12 Jun 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public interface MinMax { + + Float getMin(); + + Float getMax(); + + public static MinMax of(Float min, Float max) { + return new MinMaxImpl(min, max); + } + + static final class MinMaxImpl implements MinMax { + + private final Float min; + private final Float max; + + private MinMaxImpl(Float min, Float max) { + this.min = min; + this.max = max; + } + + @Override + public Float getMin() { + return min; + } + + @Override + public Float getMax() { + return max; + } + + } + +} diff --git a/src/main/java/org/olat/course/nodes/ms/_content/evaluation_form_execution.html b/src/main/java/org/olat/course/nodes/ms/_content/evaluation_form_execution.html new file mode 100644 index 0000000000000000000000000000000000000000..da1daa90e7c492e09acfdb1c76058b019b80e67a --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/_content/evaluation_form_execution.html @@ -0,0 +1,6 @@ +$r.render("execution") +#if($r.visible("evaluation.execution.reopen")) + <div class="o_button_group"> + $r.render("evaluation.execution.reopen") + </div> +#end \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_de.properties index 55665ad91e9f42ffca66ed6cdbe3d195f10775e7..0a453c1d6dc86e91316daf5b3ad4e62ed160a406 100644 --- a/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_de.properties @@ -5,6 +5,9 @@ comment.title=Kommentar condition.accessibility.title=Zugang error.missingconfig.long=\u00D6ffnen Sie den Tab "Bewertung", um "{0}" fertig zu konfigurieren. error.missingconfig.short=Die Bewertung f\u00FCr "{0}" ist nicht korrekt konfiguriert. +error.repo.entry.missing=Der Fragebogen, welchen Sie anzeigen m\u00F6chten, wurde in der Zwischenzeit gel\u00F6scht. +error.repo.entry.not.replaceable=Der Fragebogen kann nicht mehr ge\u00E4ndert werden. +evaluation.execution.reopen=Wieder \u00F6ffnen form.comment=Individueller Kommentar form.configuration=Konfiguration der manuellen Bewertung form.configuration.alreadydata=Die Konfiguration kann nicht mehr ge\u00E4ndert werden, da bereits Daten gespeichert wurden. @@ -13,6 +16,13 @@ form.error.cutButNoScore=Die <b>Art der Ausgabe</b> muss auf «Manuell durc form.error.cutOutOfRange=Punkteschwelle liegt nicht zwischen Punkteminimum/-maximum form.error.minGreaterThanMax=Punkteminimum ist gr\u00F6sser als Punktemaximum form.error.wrongFloat=Falsches Zahlenformat. Beispiele\: 15.0, 5.5, 10 +form.evaluation=Rubrik-Fragebogen +form.evaluation.choose=Ausw\u00E4hlen, erstellen oder importieren +form.evaluation.edit=Bearbeiten +form.evaluation.enabled=Rubrik-Bewertung +form.evaluation.not.choosen=Es ist kein Rubrik-Fragebogen ausw\u00E4hlt. +form.evaluation.replace=Ersetzen +form.evaluation.choose=Rubrik-Fragebogen ausw\u00E4hlen form.individual.assessment.docs=Individuelle Bewertungsdokumente form.infotext.coach=Hinweis f\u00FCr Betreuer form.infotext.user=Hinweis f\u00FCr alle Benutzer @@ -23,7 +33,12 @@ form.passed=Bestanden/Nicht bestanden ausgeben form.passed.type=Art der Ausgabe form.passedtype.cutval=Automatisch durch Punkteschwelle form.passedtype.manual=Manuell durch Betreuer +form.scale=Skalierungsfaktor form.score=Punkte vergeben +form.score.eval.avg=Durchschnitt aus Rubrik-Fragebogen \u00FCbernehmen +form.score.eval.sum=Summe aus Rubrik-Fragebogen \u00FCbernehmen +form.score.manual=Punkte manuell vergeben +form.score.none=Keine Punkte vergeben form.yes=Ja in.review=In Korrektur info.title=Information zur Bewertung diff --git a/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_en.properties index 72828bdb2fb23de912e3172346534b56ad4d5a3b..1af27e05da1da20aace03775413f32e2afb1e583 100644 --- a/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/ms/_i18n/LocalStrings_en.properties @@ -5,6 +5,9 @@ comment.title=Comments condition.accessibility.title=Access error.missingconfig.long=Please open the tab "Assessment" in order to complete the configuration of "{0}". error.missingconfig.short=The assessment for "{0}" is not correctly configured. +error.repo.entry.missing=This survey was deleted in the meantime.. +error.repo.entry.not.replaceable=The questionnaire can not be replaced anymore. +evaluation.execution.reopen=Reopen form.comment=Individual comment form.configuration=Configuration of manual assessment form.configuration.alreadydata=This configuration cannot be changed since there is already user data available. @@ -13,6 +16,13 @@ form.error.cutButNoScore=<b>Type of display</b> must be set to «Manually b form.error.cutOutOfRange=Cut value not between minimum and maximum score form.error.minGreaterThanMax=Minumum score greater than maximum score form.error.wrongFloat=Wrong numerical format. Examples\: 15.0, 5.5, 10 +form.evaluation=Rubric questionnaire +form.evaluation.choose=Choose, create or import +form.evaluation.edit=Edit +form.evaluation.enabled=Rubric assessment +form.evaluation.not.choosen=No rubric questionnaire chosen +form.evaluation.replace=Replace +form.evaluation.choose=Choose questionnaire form.individual.assessment.docs=Individual assessment documents form.infotext.coach=Notice for tutors form.infotext.user=Notice for all users @@ -23,7 +33,12 @@ form.passed=Display passed/failed form.passed.type=Type of display form.passedtype.cutval=Automatic (using cut value) form.passedtype.manual=Manually by tutor +form.scale=Scaling factor form.score=Score granted +form.score.eval.avg=Transfer average from rubric questionnaire +form.score.eval.sum=Transfer sum from rubric questionnaire +form.score.manual=Set points manually +form.score.none=No points form.yes=Yes in.review=In review info.title=Assessment information diff --git a/src/main/java/org/olat/course/nodes/ms/manager/MSServiceImpl.java b/src/main/java/org/olat/course/nodes/ms/manager/MSServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4b7d4ed7cc9f3d275335b6f634dc420ac380496f --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/manager/MSServiceImpl.java @@ -0,0 +1,222 @@ +/** + * <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.ms.manager; + +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + +import java.util.List; + +import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; +import org.olat.course.nodes.ms.MSService; +import org.olat.course.nodes.ms.MinMax; +import org.olat.modules.forms.EvaluationFormManager; +import org.olat.modules.forms.EvaluationFormParticipation; +import org.olat.modules.forms.EvaluationFormParticipationIdentifier; +import org.olat.modules.forms.EvaluationFormSession; +import org.olat.modules.forms.EvaluationFormSessionRef; +import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; +import org.olat.modules.forms.RubricStatistic; +import org.olat.modules.forms.SessionFilterFactory; +import org.olat.modules.forms.SliderStatistic; +import org.olat.modules.forms.model.xml.AbstractElement; +import org.olat.modules.forms.model.xml.Form; +import org.olat.modules.forms.model.xml.Rubric; +import org.olat.modules.forms.model.xml.Slider; +import org.olat.repository.RepositoryEntry; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 11 Jun 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +@Service +public class MSServiceImpl implements MSService { + + @Autowired + private EvaluationFormManager evaluationFormManager; + + @Override + public EvaluationFormSession getOrCreateSession(RepositoryEntry formEntry, RepositoryEntry ores, String nodeIdent, + Identity assessedIdentity) { + EvaluationFormSurveyIdentifier surveyIdent = of(ores, nodeIdent, assessedIdentity.getKey().toString()); + EvaluationFormSurvey survey = loadOrCreateSurvey(formEntry, surveyIdent); + EvaluationFormParticipation participation = loadOrCreateParticipation(survey); + return loadOrCreateSesssion(participation); + } + + private EvaluationFormSurvey loadOrCreateSurvey(RepositoryEntry formEntry, EvaluationFormSurveyIdentifier surveyIdent) { + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(surveyIdent); + if (survey == null) { + survey = evaluationFormManager.createSurvey(surveyIdent, formEntry); + } + return survey; + } + + private EvaluationFormParticipation loadOrCreateParticipation(EvaluationFormSurvey survey) { + // All coaches have to edit the same participation. So use the same identifier for all. + EvaluationFormParticipationIdentifier identifier = new EvaluationFormParticipationIdentifier("ms-course-node", "1"); + EvaluationFormParticipation loadedParticipation = evaluationFormManager.loadParticipationByIdentifier(survey, identifier); + if (loadedParticipation == null) { + loadedParticipation = evaluationFormManager.createParticipation(survey, identifier); + } + return loadedParticipation; + } + + private EvaluationFormSession loadOrCreateSesssion(EvaluationFormParticipation participation) { + EvaluationFormSession session = evaluationFormManager.loadSessionByParticipation(participation); + if (session == null) { + session = evaluationFormManager.createSession(participation); + } + return session; + } + + @Override + public EvaluationFormSession getSession(EvaluationFormSessionRef sessionRef) { + return evaluationFormManager.loadSessionByKey(sessionRef); + } + + @Override + public EvaluationFormSession closeSession(EvaluationFormSession session) { + return evaluationFormManager.finishSession(session); + } + + @Override + public EvaluationFormSession reopenSession(EvaluationFormSession session) { + return evaluationFormManager.reopenSession(session); + } + + @Override + public boolean hasSessions(OLATResourceable ores, String nodeIdent) { + return !evaluationFormManager.loadSurveys(of(ores, nodeIdent)).isEmpty(); + } + + @Override + public void deleteSessions(RepositoryEntry ores, String nodeIdent) { + List<EvaluationFormSurvey> surveys = evaluationFormManager.loadSurveys(of(ores, nodeIdent)); + for (EvaluationFormSurvey survey : surveys) { + evaluationFormManager.deleteSurvey(survey); + } + } + + @Override + public MinMax calculateMinMaxSum(RepositoryEntry formEntry, float scalingFactor) { + double sumMin = 0.0; + double sumMax = 0.0; + Form form = evaluationFormManager.loadForm(formEntry); + for (AbstractElement element : form.getElements()) { + if (Rubric.TYPE.equals(element.getType())) { + Rubric rubric = (Rubric) element; + int numberOfSliders = rubric.getSliders().size(); + int steps = rubric.getSteps(); + double min = rubric.getScaleType().getStepValue(steps, 1); + double max = rubric.getScaleType().getStepValue(steps, steps); + sumMin += numberOfSliders * min; + sumMax += numberOfSliders * max; + } + } + sumMin = scalingFactor * sumMin; + sumMax = scalingFactor * sumMax; + return MinMax.of(Float.valueOf((float)sumMin), Float.valueOf((float)sumMax)); + } + + @Override + public MinMax calculateMinMaxAvg(RepositoryEntry formEntry, float scalingFactor) { + double sumMin = 0.0; + double sumMax = 0.0; + int numberAvgs = 0; + Form form = evaluationFormManager.loadForm(formEntry); + for (AbstractElement element : form.getElements()) { + if (Rubric.TYPE.equals(element.getType())) { + Rubric rubric = (Rubric) element; + int numberOfSliders = rubric.getSliders().size(); + int steps = rubric.getSteps(); + double min = rubric.getScaleType().getStepValue(steps, 1); + double max = rubric.getScaleType().getStepValue(steps, steps); + sumMin += numberOfSliders * min; + sumMax += numberOfSliders * max; + numberAvgs += numberOfSliders; + } + } + if (numberAvgs > 0) { + double avgMin = sumMin / numberAvgs; + avgMin = scalingFactor * avgMin; + double avgMax = sumMax / numberAvgs; + avgMax = scalingFactor * avgMax; + return MinMax.of(Float.valueOf((float)avgMin), Float.valueOf((float)avgMax)); + } + return MinMax.of(0.0f, 0.0f); + } + + @Override + public Float calculateScoreBySum(EvaluationFormSession session) { + double sum = 0.0; + Form form = evaluationFormManager.loadForm(session.getSurvey().getFormEntry()); + for (AbstractElement element : form.getElements()) { + if (Rubric.TYPE.equals(element.getType())) { + Rubric rubric = (Rubric) element; + RubricStatistic rubricStatistic = evaluationFormManager.getRubricStatistic(rubric, SessionFilterFactory.create(session)); + Double rubricSum = rubricStatistic.getTotalStatistic().getSum(); + if (rubricSum != null) { + sum += (float)rubricSum.doubleValue(); + } + } + } + return Float.valueOf((float)sum); + } + + @Override + public Float calculateScoreByAvg(EvaluationFormSession session) { + double sumAvgs = 0.0; + int numberAvgs = 0; + Form form = evaluationFormManager.loadForm(session.getSurvey().getFormEntry()); + for (AbstractElement element : form.getElements()) { + if (Rubric.TYPE.equals(element.getType())) { + Rubric rubric = (Rubric) element; + RubricStatistic rubricStatistic = evaluationFormManager.getRubricStatistic(rubric, SessionFilterFactory.create(session)); + for (Slider slider : rubric.getSliders()) { + SliderStatistic sliderStatistic = rubricStatistic.getSliderStatistic(slider); + Double sliderAvg = sliderStatistic.getAvg(); + if (sliderAvg != null) { + numberAvgs++; + sumAvgs += sliderAvg.doubleValue(); + } + } + } + } + if (numberAvgs > 0) { + double avg = sumAvgs / numberAvgs; + return Float.valueOf((float)avg); + } + return null; + } + + @Override + public Float scaleScore(Float score, float scale) { + if (score == null) return null; + + return scale * score.floatValue(); + } + +} diff --git a/src/main/java/org/olat/course/nodes/survey/SurveyConfigController.java b/src/main/java/org/olat/course/nodes/survey/SurveyConfigController.java index 6a17be42cda12f2ab5a66944d6ffcc7697bac6fc..9dba272d94a6ed4705f8de48aaaee1fd4df53139 100644 --- a/src/main/java/org/olat/course/nodes/survey/SurveyConfigController.java +++ b/src/main/java/org/olat/course/nodes/survey/SurveyConfigController.java @@ -51,6 +51,7 @@ import org.olat.modules.ModuleConfiguration; import org.olat.modules.ceditor.DataStorage; import org.olat.modules.forms.EvaluationFormManager; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.olat.modules.forms.handler.EvaluationFormResource; import org.olat.modules.forms.ui.EvaluationFormExecutionController; import org.olat.repository.RepositoryEntry; @@ -102,6 +103,7 @@ public class SurveyConfigController extends FormBasicController { private final ModuleConfiguration moduleConfiguration; private final OLATResourceable ores; private final String subIdent; + private EvaluationFormSurveyIdentifier surveyIdent; private EvaluationFormSurvey survey; @Autowired @@ -113,7 +115,8 @@ public class SurveyConfigController extends FormBasicController { this.moduleConfiguration = surveyCourseNode.getModuleConfiguration(); this.ores = RepositoryManager.getInstance().lookupRepositoryEntry(course, true); this.subIdent = surveyCourseNode.getIdent(); - this.survey = evaluationFormManager.loadSurvey(ores, subIdent); + this.surveyIdent = EvaluationFormSurveyIdentifier.of(ores, subIdent); + this.survey = evaluationFormManager.loadSurvey(surveyIdent); initForm(ureq); } @@ -234,7 +237,7 @@ public class SurveyConfigController extends FormBasicController { RepositoryEntry formEntry = searchCtrl.getSelectedEntry(); if (formEntry != null) { if (survey == null) { - survey = evaluationFormManager.createSurvey(ores, subIdent, formEntry); + survey = evaluationFormManager.createSurvey(surveyIdent, formEntry); } else { boolean isFormUpdateable = evaluationFormManager.isFormUpdateable(survey); if (isFormUpdateable) { diff --git a/src/main/java/org/olat/course/nodes/survey/SurveyRunController.java b/src/main/java/org/olat/course/nodes/survey/SurveyRunController.java index f3bc7cdc60122fab9aa01f3a027a0ba068f034fa..77c598a0b4ec6664ed118c1aecafa6bfaae7cac7 100644 --- a/src/main/java/org/olat/course/nodes/survey/SurveyRunController.java +++ b/src/main/java/org/olat/course/nodes/survey/SurveyRunController.java @@ -19,6 +19,8 @@ */ package org.olat.course.nodes.survey; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + import java.util.UUID; import org.olat.core.gui.UserRequest; @@ -99,7 +101,7 @@ public class SurveyRunController extends BasicController { mainVC.contextPut("withCmds", Boolean.TRUE); } - survey = evaluationFormManager.loadSurvey(ores, subIdent); + survey = evaluationFormManager.loadSurvey(of(ores, subIdent)); doShowView(ureq); } diff --git a/src/main/java/org/olat/course/nodes/survey/SurveyStatisticResourceResult.java b/src/main/java/org/olat/course/nodes/survey/SurveyStatisticResourceResult.java index 92cc01fd4e996cfca0303680325b573f9f62ca26..06b9c5c42b855435179023200c1edea3e9eb32ba 100644 --- a/src/main/java/org/olat/course/nodes/survey/SurveyStatisticResourceResult.java +++ b/src/main/java/org/olat/course/nodes/survey/SurveyStatisticResourceResult.java @@ -29,12 +29,12 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.messages.SimpleMessageController; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; -import org.olat.core.id.OLATResourceable; import org.olat.core.util.Util; import org.olat.course.statistic.StatisticResourceResult; import org.olat.modules.forms.EvaluationFormManager; import org.olat.modules.forms.EvaluationFormParticipation; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.springframework.beans.factory.annotation.Autowired; /** @@ -45,18 +45,16 @@ import org.springframework.beans.factory.annotation.Autowired; */ public class SurveyStatisticResourceResult implements StatisticResourceResult { - private final OLATResourceable ores; - private final String subIdent; + private final EvaluationFormSurveyIdentifier surveyIdent; private final Identity identity; private final SurveyRunSecurityCallback secCallback; @Autowired private EvaluationFormManager evaluationFormManager; - public SurveyStatisticResourceResult(OLATResourceable ores, String subIdent, Identity identity, + public SurveyStatisticResourceResult(EvaluationFormSurveyIdentifier surveyIdent, Identity identity, SurveyRunSecurityCallback secCallback) { - this.ores = ores; - this.subIdent = subIdent; + this.surveyIdent = surveyIdent; this.identity = identity; this.secCallback = secCallback; CoreSpringFactory.autowireObject(this); @@ -70,7 +68,7 @@ public class SurveyStatisticResourceResult implements StatisticResourceResult { @Override public Controller getController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, TreeNode selectedNode) { - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(ores, subIdent); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(surveyIdent); EvaluationFormParticipation participation = evaluationFormManager.loadParticipationByExecutor(survey, identity); if (secCallback.canViewReporting(participation)) { return new SurveyReportingController(ureq, wControl, survey); diff --git a/src/main/java/org/olat/modules/forms/EvaluationFormDispatcher.java b/src/main/java/org/olat/modules/forms/EvaluationFormDispatcher.java index 27b04a78c4d8f7ae604c9dfdae2cb2a09b29c7e0..f061865bc45316ccff72e1a175b532d29294dd0b 100644 --- a/src/main/java/org/olat/modules/forms/EvaluationFormDispatcher.java +++ b/src/main/java/org/olat/modules/forms/EvaluationFormDispatcher.java @@ -27,6 +27,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.logging.log4j.Logger; import org.olat.NewControllerFactory; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.BaseFullWebappController; @@ -41,7 +42,6 @@ import org.olat.core.gui.control.WindowBackOffice; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.creator.ControllerCreator; import org.olat.core.helpers.Settings; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.UserSession; @@ -113,7 +113,7 @@ public class EvaluationFormDispatcher implements Dispatcher { .loadParticipationByIdentifier(identifier); EvaluationFormStandaloneProvider standaloneProvider = CoreSpringFactory .getImpl(EvaluationFormStandaloneProviderFactory.class) - .getProvider(participation.getSurvey().getOLATResourceable()); + .getProvider(participation.getSurvey().getIdentifier().getOLATResourceable()); if (participation.getExecutor().equals(usess.getIdentity()) && standaloneProvider.hasBusinessPath(participation)) { ChiefController chiefController = Windows.getWindows(usess).getChiefController(); WindowBackOffice windowBackOffice = chiefController.getWindow().getWindowBackOffice(); diff --git a/src/main/java/org/olat/modules/forms/EvaluationFormManager.java b/src/main/java/org/olat/modules/forms/EvaluationFormManager.java index edfaddc90264f931ef64fc99c3cc867250ab8cd4..0b2647bd191980837ba86e6f6996f6500a02d095 100644 --- a/src/main/java/org/olat/modules/forms/EvaluationFormManager.java +++ b/src/main/java/org/olat/modules/forms/EvaluationFormManager.java @@ -28,7 +28,6 @@ import java.util.List; import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.SortKey; import org.olat.core.id.Identity; -import org.olat.core.id.OLATResourceable; import org.olat.core.util.vfs.VFSLeaf; import org.olat.modules.ceditor.DataStorage; import org.olat.modules.forms.model.jpa.EvaluationFormResponses; @@ -50,11 +49,27 @@ public interface EvaluationFormManager { public DataStorage loadStorage(RepositoryEntry formEntry); - public EvaluationFormSurvey createSurvey(OLATResourceable ores, String subIdent, RepositoryEntry formEntry); + public EvaluationFormSurvey createSurvey(EvaluationFormSurveyIdentifier identifier, RepositoryEntry formEntry); - public EvaluationFormSurveyRef createSurvey(OLATResourceable ores, String subIdent, EvaluationFormSurvey previous); + public EvaluationFormSurveyRef createSurvey(EvaluationFormSurveyIdentifier identifier, EvaluationFormSurvey previous); - public EvaluationFormSurvey loadSurvey(OLATResourceable ores, String subIdent); + /** + * Load a unique survey with a specific identifier. If more then one surveys are + * found, only the "first" one is returned. + * + * @param identifier + * @return + */ + public EvaluationFormSurvey loadSurvey(EvaluationFormSurveyIdentifier identifier); + + /** + * Load all surveys of an identifier. The purpose of this method is to load all + * surveys by cut the subIdent(s). + * + * @param identifier + * @return + */ + public List<EvaluationFormSurvey> loadSurveys(EvaluationFormSurveyIdentifier identifier); /** * Checks whether a form of a survey can be updated or not. The form can not be diff --git a/src/main/java/org/olat/modules/forms/EvaluationFormSurvey.java b/src/main/java/org/olat/modules/forms/EvaluationFormSurvey.java index 7943e1044bb90e9ac89bb111bb83d93f0cdfc21c..d320992fe94029c9e3cc365b682a41b1a313e08b 100644 --- a/src/main/java/org/olat/modules/forms/EvaluationFormSurvey.java +++ b/src/main/java/org/olat/modules/forms/EvaluationFormSurvey.java @@ -21,7 +21,6 @@ package org.olat.modules.forms; import org.olat.core.id.CreateInfo; import org.olat.core.id.ModifiedInfo; -import org.olat.core.id.OLATResourceable; import org.olat.repository.RepositoryEntry; /** @@ -32,9 +31,7 @@ import org.olat.repository.RepositoryEntry; */ public interface EvaluationFormSurvey extends EvaluationFormSurveyRef, CreateInfo, ModifiedInfo { - public OLATResourceable getOLATResourceable(); - - public String getSubident(); + public EvaluationFormSurveyIdentifier getIdentifier(); /** * diff --git a/src/main/java/org/olat/modules/forms/EvaluationFormSurveyIdentifier.java b/src/main/java/org/olat/modules/forms/EvaluationFormSurveyIdentifier.java new file mode 100644 index 0000000000000000000000000000000000000000..8af61c4d2ae47f395f543e1d5f7e47c905d61e01 --- /dev/null +++ b/src/main/java/org/olat/modules/forms/EvaluationFormSurveyIdentifier.java @@ -0,0 +1,94 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.forms; + +import org.olat.core.id.OLATResourceable; + +/** + * + * Initial date: 12 Jun 2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public interface EvaluationFormSurveyIdentifier { + + /** + * OLATResourceable which is using the survey. + * + * @return + */ + public OLATResourceable getOLATResourceable(); + + /** + * The OLATResourceable may define a subIdent, e.g. the course node. + * + * @return + */ + public String getSubident(); + + /** + * The OLATResourceable may define even a second subIdent. + * + * @return + */ + public String getSubident2(); + + public static EvaluationFormSurveyIdentifier of(OLATResourceable ores) { + return of(ores, null); + } + + public static EvaluationFormSurveyIdentifier of(OLATResourceable ores, String resSubident) { + return of(ores, resSubident, null); + } + + public static EvaluationFormSurveyIdentifier of(OLATResourceable ores, String resSubident, String resSubident2) { + return new EvaluationFormSurveyIdentifierImpl(ores, resSubident, resSubident2) ; + } + + static class EvaluationFormSurveyIdentifierImpl implements EvaluationFormSurveyIdentifier { + + private final OLATResourceable ores; + private final String subident; + private final String subident2; + + private EvaluationFormSurveyIdentifierImpl(OLATResourceable ores, String resSubident, String resSubident2) { + this.ores = ores; + this.subident = resSubident; + this.subident2 = resSubident2; + } + + @Override + public OLATResourceable getOLATResourceable() { + return ores; + } + + @Override + public String getSubident() { + return subident; + } + + @Override + public String getSubident2() { + return subident2; + } + + } + +} diff --git a/src/main/java/org/olat/modules/forms/SliderStatistic.java b/src/main/java/org/olat/modules/forms/SliderStatistic.java index 8cf7856f88467604daa55db0fceb775a026f1407..81f443cbfeda66a3146b06a0df5cb841cc821588 100644 --- a/src/main/java/org/olat/modules/forms/SliderStatistic.java +++ b/src/main/java/org/olat/modules/forms/SliderStatistic.java @@ -32,6 +32,8 @@ public interface SliderStatistic { public Long getNumberOfNoResponses(); public Long getNumberOfResponses(); + + public Double getSum(); public Double getMedian(); diff --git a/src/main/java/org/olat/modules/forms/manager/EvaluationFormManagerImpl.java b/src/main/java/org/olat/modules/forms/manager/EvaluationFormManagerImpl.java index c2031a15bda5044ae75bb2e2fc62a3db76b56a46..8aa5a829cbe8b86947fe7fc25d87a5e383169eab 100644 --- a/src/main/java/org/olat/modules/forms/manager/EvaluationFormManagerImpl.java +++ b/src/main/java/org/olat/modules/forms/manager/EvaluationFormManagerImpl.java @@ -35,7 +35,6 @@ import java.util.List; import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.SortKey; import org.olat.core.id.Identity; -import org.olat.core.id.OLATResourceable; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.xml.XStreamHelper; import org.olat.fileresource.FileResourceManager; @@ -51,6 +50,7 @@ import org.olat.modules.forms.EvaluationFormSessionRef; import org.olat.modules.forms.EvaluationFormSessionStatus; import org.olat.modules.forms.EvaluationFormStatistic; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.olat.modules.forms.EvaluationFormSurveyRef; import org.olat.modules.forms.RubricRating; import org.olat.modules.forms.RubricStatistic; @@ -111,18 +111,27 @@ public class EvaluationFormManagerImpl implements EvaluationFormManager { } @Override - public EvaluationFormSurvey createSurvey(OLATResourceable ores, String subIdent, RepositoryEntry formEntry) { - return evaluationFormSurveyDao.createSurvey(ores, subIdent, formEntry, null); + public EvaluationFormSurvey createSurvey(EvaluationFormSurveyIdentifier identifier, RepositoryEntry formEntry) { + return evaluationFormSurveyDao.createSurvey(identifier.getOLATResourceable(), identifier.getSubident(), + identifier.getSubident2(), formEntry, null); } @Override - public EvaluationFormSurvey createSurvey(OLATResourceable ores, String subIdent, EvaluationFormSurvey previous) { - return evaluationFormSurveyDao.createSurvey(ores, subIdent, previous.getFormEntry(), previous); + public EvaluationFormSurvey createSurvey(EvaluationFormSurveyIdentifier identifier, EvaluationFormSurvey previous) { + return evaluationFormSurveyDao.createSurvey(identifier.getOLATResourceable(), identifier.getSubident(), + identifier.getSubident2(), previous.getFormEntry(), previous); } @Override - public EvaluationFormSurvey loadSurvey(OLATResourceable ores, String subIdent) { - return evaluationFormSurveyDao.loadByResourceable(ores, subIdent); + public EvaluationFormSurvey loadSurvey(EvaluationFormSurveyIdentifier identifier) { + return evaluationFormSurveyDao.loadByResourceable(identifier.getOLATResourceable(), identifier.getSubident(), + identifier.getSubident2()); + } + + @Override + public List<EvaluationFormSurvey> loadSurveys(EvaluationFormSurveyIdentifier identifier) { + return evaluationFormSurveyDao.loadSurveysByResourceable(identifier.getOLATResourceable(), identifier.getSubident(), + identifier.getSubident2()); } @Override diff --git a/src/main/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAO.java b/src/main/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAO.java index 225d210d67ea1c697f5b0d8705ca7e16b3b5c951..9f950f64df9f635ff26eb1df7d054eee980afc82 100644 --- a/src/main/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAO.java +++ b/src/main/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAO.java @@ -19,6 +19,7 @@ */ package org.olat.modules.forms.manager; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -47,14 +48,15 @@ class EvaluationFormSurveyDAO { @Autowired private DB dbInstance; - EvaluationFormSurvey createSurvey(OLATResourceable ores, String subIdent, RepositoryEntry formEntry, - EvaluationFormSurvey previous) { + EvaluationFormSurvey createSurvey(OLATResourceable ores, String subIdent, String subIdent2, + RepositoryEntry formEntry, EvaluationFormSurvey previous) { EvaluationFormSurveyImpl survey = new EvaluationFormSurveyImpl(); survey.setCreationDate(new Date()); survey.setLastModified(survey.getCreationDate()); survey.setResName(ores.getResourceableTypeName()); survey.setResId(ores.getResourceableId()); - survey.setSubident(subIdent); + survey.setResSubident(subIdent); + survey.setResSubident2(subIdent2); survey.setFormEntry(formEntry); dbInstance.getCurrentEntityManager().persist(survey); Long seriesKey = previous != null? previous.getSeriesKey(): survey.getKey(); @@ -78,10 +80,15 @@ class EvaluationFormSurveyDAO { surveyImpl.setLastModified(new Date()); return dbInstance.getCurrentEntityManager().merge(surveyImpl); } + + EvaluationFormSurvey loadByResourceable(OLATResourceable ores, String subIdent, String subIdent2) { + List<EvaluationFormSurvey> surveys = loadSurveysByResourceable(ores, subIdent, subIdent2); + return surveys.isEmpty() ? null : surveys.get(0); + } - EvaluationFormSurvey loadByResourceable(OLATResourceable ores, String subIdent) { + List<EvaluationFormSurvey> loadSurveysByResourceable(OLATResourceable ores, String subIdent, String subIdent2) { if (ores == null || ores.getResourceableTypeName() == null || ores.getResourceableId() == null) - return null; + return Collections.emptyList(); StringBuilder sb = new StringBuilder(); sb.append("select survey from evaluationformsurvey as survey"); @@ -90,6 +97,9 @@ class EvaluationFormSurveyDAO { if (StringHelper.containsNonWhitespace(subIdent)) { sb.append(" and survey.resSubident=:resSubident"); } + if (StringHelper.containsNonWhitespace(subIdent2)) { + sb.append(" and survey.resSubident2=:resSubident2"); + } TypedQuery<EvaluationFormSurvey> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), EvaluationFormSurvey.class) @@ -98,8 +108,10 @@ class EvaluationFormSurveyDAO { if (StringHelper.containsNonWhitespace(subIdent)) { query.setParameter("resSubident", subIdent); } - List<EvaluationFormSurvey> surveys = query.getResultList(); - return surveys.isEmpty() ? null : surveys.get(0); + if (StringHelper.containsNonWhitespace(subIdent2)) { + query.setParameter("resSubident2", subIdent2); + } + return query.getResultList(); } void delete(EvaluationFormSurveyRef survey) { diff --git a/src/main/java/org/olat/modules/forms/model/jpa/EvaluationFormSurveyImpl.java b/src/main/java/org/olat/modules/forms/model/jpa/EvaluationFormSurveyImpl.java index 194254fcfef6afc738adfea75278f966bbba51bc..7e37d922c1efdc4a0625881c9c2ee3fca5b5334e 100644 --- a/src/main/java/org/olat/modules/forms/model/jpa/EvaluationFormSurveyImpl.java +++ b/src/main/java/org/olat/modules/forms/model/jpa/EvaluationFormSurveyImpl.java @@ -19,6 +19,8 @@ */ package org.olat.modules.forms.model.jpa; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + import java.util.Date; import javax.persistence.Column; @@ -37,6 +39,7 @@ import javax.persistence.TemporalType; import org.olat.core.id.OLATResourceable; import org.olat.core.id.Persistable; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.olat.repository.RepositoryEntry; /** @@ -69,6 +72,8 @@ public class EvaluationFormSurveyImpl implements EvaluationFormSurvey, Persistab private Long resId; @Column(name="e_sub_ident", nullable=true, insertable=true, updatable=false) private String resSubident; + @Column(name="e_sub_ident2", nullable=true, insertable=true, updatable=false) + private String resSubident2; @Column(name="e_series_key", nullable=true, insertable=true, updatable=true) private Long seriesKey; @Column(name="e_series_index", nullable=true, insertable=true, updatable=true) @@ -124,9 +129,29 @@ public class EvaluationFormSurveyImpl implements EvaluationFormSurvey, Persistab public void setResId(Long resId) { this.resId = resId; } - + + public String getResSubident() { + return resSubident; + } + + public void setResSubident(String resSubident) { + this.resSubident = resSubident; + } + + public String getResSubident2() { + return resSubident2; + } + + public void setResSubident2(String resSubident2) { + this.resSubident2 = resSubident2; + } + @Override - public OLATResourceable getOLATResourceable() { + public EvaluationFormSurveyIdentifier getIdentifier() { + return of(getOLATResourceable() , resSubident, resSubident2); + } + + private OLATResourceable getOLATResourceable() { return new OLATResourceable() { @Override @@ -141,15 +166,6 @@ public class EvaluationFormSurveyImpl implements EvaluationFormSurvey, Persistab }; } - @Override - public String getSubident() { - return resSubident; - } - - public void setSubident(String subident) { - this.resSubident = subident; - } - @Override public RepositoryEntry getFormEntry() { return formEntry; diff --git a/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java b/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java index 612fe901e143c486d26434e6ea3d1e1723e741e3..aaacfdbf0390e820082aa42ce09f737c20da64de 100644 --- a/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java +++ b/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java @@ -93,12 +93,12 @@ public class RubricStatisticImpl implements RubricStatistic { List<Long> stepCounts = getStepCounts(slider); Long numOfResponses = getNumOfResponses(stepCounts); Double median = getMedian(stepCounts); - Double average = getAverage(stepCounts); + SumAverage sumAverage = getSumAverage(stepCounts); Double variance = getVariance(stepCounts); Double stdDev = getStdDev(stepCounts); - RubricRating rating = RubricRatingEvaluator.rate(rubric, average); - SliderStatisticImpl sliderStatistic = new SliderStatisticImpl(numOfNoRespones, numOfResponses, median, - average, variance, stdDev, stepCounts, rating); + RubricRating rating = RubricRatingEvaluator.rate(rubric, sumAverage.getAverage()); + SliderStatisticImpl sliderStatistic = new SliderStatisticImpl(numOfNoRespones, numOfResponses, + sumAverage.getSum(), median, sumAverage.getAverage(), variance, stdDev, stepCounts, rating); sliderToStatistic.put(slider, sliderStatistic); } } @@ -160,8 +160,8 @@ public class RubricStatisticImpl implements RubricStatistic { return median; } - private Double getAverage(List<Long> stepCounts) { - int sumValues = 0; + private SumAverage getSumAverage(List<Long> stepCounts) { + double sumValues = 0; int sumResponses = 0; for (int step = 1; step <= rubric.getSteps(); step++) { Long count = stepCounts.get(step - 1); @@ -171,11 +171,14 @@ public class RubricStatisticImpl implements RubricStatistic { sumResponses += count; } } - return sumResponses > 0? (double)sumValues / sumResponses: null; + return sumResponses > 0 + ? new SumAverage(sumValues, sumValues / sumResponses) + : new SumAverage(null, null); } private Double getVariance(List<Long> stepCounts) { - Double mean = getAverage(stepCounts); + SumAverage sumAverage = getSumAverage(stepCounts); + Double mean = sumAverage.getAverage(); if (mean == null) return null; List<Double> scaledValues = getScaledValues(stepCounts); @@ -214,12 +217,12 @@ public class RubricStatisticImpl implements RubricStatistic { List<Long> totalStepCounts = getTotalStepCounts(); Long numOfResponses = getNumOfResponses(totalStepCounts); Double median = getMedian(totalStepCounts); - Double average = getAverage(totalStepCounts); + SumAverage sumAverage = getSumAverage(totalStepCounts); Double variance = getVariance(totalStepCounts); Double stdDev = getStdDev(totalStepCounts); - RubricRating rating = RubricRatingEvaluator.rate(rubric, average); - totalStatistic = new SliderStatisticImpl(numberOfNoResponses, numOfResponses, median, average, variance, stdDev, - totalStepCounts, rating); + RubricRating rating = RubricRatingEvaluator.rate(rubric, sumAverage.getAverage()); + totalStatistic = new SliderStatisticImpl(numberOfNoResponses, numOfResponses, sumAverage.getSum(), median, + sumAverage.getAverage(), variance, stdDev, totalStepCounts, rating); } private List<Long> getTotalStepCounts() { @@ -235,5 +238,26 @@ public class RubricStatisticImpl implements RubricStatistic { } return totalStepCounts; } + + private static final class SumAverage { + + private final Double sum; + private final Double average; + + private SumAverage(Double sum, Double average) { + this.sum = sum; + this.average = average; + } + + private Double getSum() { + return sum; + } + + private Double getAverage() { + return average; + } + + + } } diff --git a/src/main/java/org/olat/modules/forms/model/jpa/SliderStatisticImpl.java b/src/main/java/org/olat/modules/forms/model/jpa/SliderStatisticImpl.java index e4f339316534bf1346104a23e0aab91db791e843..2dd0b4a1f7a1aa8f087d3f759db66c2a6a199a54 100644 --- a/src/main/java/org/olat/modules/forms/model/jpa/SliderStatisticImpl.java +++ b/src/main/java/org/olat/modules/forms/model/jpa/SliderStatisticImpl.java @@ -34,6 +34,7 @@ public class SliderStatisticImpl implements SliderStatistic { private final Long numberOfNoResponses; private final Long numberOfResponses; + private final Double sum; private final Double median; private final Double avg; private final Double variance; @@ -41,11 +42,12 @@ public class SliderStatisticImpl implements SliderStatistic { private final List<Long> stepCounts; private final RubricRating rating; - public SliderStatisticImpl(Long numberOfNoResponses, Long numberOfResponses, Double median, Double avg, Double variance, - Double stdDev, List<Long> stepCounts, RubricRating rating) { + public SliderStatisticImpl(Long numberOfNoResponses, Long numberOfResponses, Double sum, Double median, Double avg, + Double variance, Double stdDev, List<Long> stepCounts, RubricRating rating) { super(); this.numberOfNoResponses = numberOfNoResponses; this.numberOfResponses = numberOfResponses; + this.sum = sum; this.median = median; this.avg = avg; this.variance = variance; @@ -64,6 +66,11 @@ public class SliderStatisticImpl implements SliderStatistic { return numberOfResponses; } + @Override + public Double getSum() { + return sum; + } + @Override public Double getMedian() { return median; diff --git a/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java b/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java index 8338c9eddffa271d6bce27d39d09cca2158adaf6..6e35c328746b9380d7a656ba3594419e5455b63c 100644 --- a/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java +++ b/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.logging.log4j.Logger; import org.olat.core.commons.persistence.DB; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -41,7 +42,6 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.Identity; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.CodeHelper; import org.olat.core.util.xml.XStreamHelper; @@ -110,8 +110,6 @@ public class EvaluationFormExecutionController extends FormBasicController imple /** * Optimized to use already loaded responses and form. * - * @param formHeader - * */ public EvaluationFormExecutionController(UserRequest ureq, WindowControl wControl, EvaluationFormSession session, EvaluationFormResponses responses, Form form, DataStorage storage, Component header) { diff --git a/src/main/java/org/olat/modules/forms/ui/StandaloneExecutionController.java b/src/main/java/org/olat/modules/forms/ui/StandaloneExecutionController.java index 2327c0df2455737488a556fbbbd8879d9ebdeaaf..0611fb281c5a6e3821d9fbce24d77974543282dd 100644 --- a/src/main/java/org/olat/modules/forms/ui/StandaloneExecutionController.java +++ b/src/main/java/org/olat/modules/forms/ui/StandaloneExecutionController.java @@ -19,6 +19,7 @@ */ package org.olat.modules.forms.ui; +import org.apache.logging.log4j.Logger; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.velocity.VelocityContainer; @@ -28,7 +29,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.messages.MessageUIFactory; import org.olat.core.id.Identity; -import org.apache.logging.log4j.Logger; +import org.olat.core.id.OLATResourceable; import org.olat.core.logging.Tracing; import org.olat.modules.forms.EvaluationFormManager; import org.olat.modules.forms.EvaluationFormParticipation; @@ -76,7 +77,8 @@ public class StandaloneExecutionController extends BasicController implements Co doShowAlreadyDone(ureq); log.debug("Participation already done: " + identifier); } else { - EvaluationFormStandaloneProvider standaloneProvider = standaloneProviderFactory.getProvider(participation.getSurvey().getOLATResourceable()); + OLATResourceable surveyOres = participation.getSurvey().getIdentifier().getOLATResourceable(); + EvaluationFormStandaloneProvider standaloneProvider = standaloneProviderFactory.getProvider(surveyOres); if (standaloneProvider.isExecutable(participation)) { doShowExecution(ureq, participation); log.debug("Execute evaluation form with " + identifier); @@ -113,7 +115,8 @@ public class StandaloneExecutionController extends BasicController implements Co } private void doShowExecution(UserRequest ureq, EvaluationFormParticipation participation) { - EvaluationFormStandaloneProvider standaloneProvider = standaloneProviderFactory.getProvider(participation.getSurvey().getOLATResourceable()); + OLATResourceable surveyOres = participation.getSurvey().getIdentifier().getOLATResourceable(); + EvaluationFormStandaloneProvider standaloneProvider = standaloneProviderFactory.getProvider(surveyOres); headerCtrl = standaloneProvider.getExecutionHeader(ureq, getWindowControl(), participation); listenTo(headerCtrl); mainVC.put("header", headerCtrl.getInitialComponent()); diff --git a/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java b/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java index 243e5a0558d33e45845ee4e35fc40a88bb9b97c0..826483dd527c5bc7c01eb61df83d4e00bdec14e6 100644 --- a/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java +++ b/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.logging.log4j.Logger; import org.olat.basesecurity.Group; import org.olat.basesecurity.IdentityRef; import org.olat.basesecurity.manager.GroupDAO; @@ -41,7 +42,6 @@ import org.olat.core.commons.persistence.DB; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.FileUtils; @@ -69,6 +69,7 @@ import org.olat.modules.forms.EvaluationFormParticipation; import org.olat.modules.forms.EvaluationFormParticipationRef; import org.olat.modules.forms.EvaluationFormSession; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.olat.modules.forms.EvaluationFormSurveyRef; import org.olat.modules.portfolio.AssessmentSection; import org.olat.modules.portfolio.Assignment; @@ -1185,7 +1186,7 @@ public class PortfolioServiceImpl implements PortfolioService { } List<Identity> owners = getOwners(page, section); for (Identity owner: owners) { - EvaluationFormSurveyRef survey = evaluationFormManager.loadSurvey(getOLATResourceableForEvaluationForm(page.getBody()), null); + EvaluationFormSurveyRef survey = evaluationFormManager.loadSurvey(getSurveyIdent(page.getBody())); EvaluationFormParticipationRef participation = evaluationFormManager.loadParticipationByExecutor(survey, owner); EvaluationFormSession session = evaluationFormManager.loadSessionByParticipation(participation); evaluationFormManager.finishSession(session); @@ -1203,7 +1204,7 @@ public class PortfolioServiceImpl implements PortfolioService { } List<Identity> owners = getOwners(page, section); for (Identity owner: owners) { - EvaluationFormSurveyRef survey = evaluationFormManager.loadSurvey(getOLATResourceableForEvaluationForm(page.getBody()), null); + EvaluationFormSurveyRef survey = evaluationFormManager.loadSurvey(getSurveyIdent(page.getBody())); EvaluationFormParticipationRef participation = evaluationFormManager.loadParticipationByExecutor(survey, owner); EvaluationFormSession session = evaluationFormManager.loadSessionByParticipation(participation); evaluationFormManager.reopenSession(session); @@ -1481,10 +1482,10 @@ public class PortfolioServiceImpl implements PortfolioService { @Override public EvaluationFormSurvey loadOrCreateSurvey(PageBody body, RepositoryEntry formEntry) { - OLATResourceable ores = getOLATResourceableForEvaluationForm(body); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(ores, null); + EvaluationFormSurveyIdentifier surveyIdent = getSurveyIdent(body); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(surveyIdent); if (survey == null) { - survey = evaluationFormManager.createSurvey(ores, null, formEntry); + survey = evaluationFormManager.createSurvey(surveyIdent, formEntry); } return survey; } @@ -1510,11 +1511,15 @@ public class PortfolioServiceImpl implements PortfolioService { @Override public void deleteSurvey(PageBody body) { - OLATResourceable ores = getOLATResourceableForEvaluationForm(body); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(ores, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(body)); if (survey != null) { evaluationFormManager.deleteSurvey(survey); } } + private EvaluationFormSurveyIdentifier getSurveyIdent(PageBody body) { + OLATResourceable ores = getOLATResourceableForEvaluationForm(body); + return EvaluationFormSurveyIdentifier.of(ores); + } + } diff --git a/src/main/java/org/olat/modules/quality/manager/QualityMailing.java b/src/main/java/org/olat/modules/quality/manager/QualityMailing.java index de4e710d80fd8138cba7e5411bff903567e18361..c9100d0c2b064a59a57e99f42b1727d804ffe9cc 100644 --- a/src/main/java/org/olat/modules/quality/manager/QualityMailing.java +++ b/src/main/java/org/olat/modules/quality/manager/QualityMailing.java @@ -19,6 +19,8 @@ */ package org.olat.modules.quality.manager; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -36,6 +38,7 @@ import java.util.Date; import java.util.List; import java.util.Locale; +import org.apache.logging.log4j.Logger; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.services.pdf.PdfModule; import org.olat.core.commons.services.pdf.PdfService; @@ -46,7 +49,6 @@ import org.olat.core.gui.util.WindowControlMocker; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.id.User; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.CodeHelper; import org.olat.core.util.FileUtils; @@ -382,7 +384,7 @@ class QualityMailing { if (!pdfModule.isEnabled()) return null; if (tempDir == null || !tempDir.toFile().exists()) return null; - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(of(dataCollection)); Form form = evaluationFormManager.loadForm(survey.getFormEntry()); DataStorage storage = evaluationFormManager.loadStorage(survey.getFormEntry()); SessionFilter filter = SessionFilterFactory.createSelectDone(survey); diff --git a/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java b/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java index cd7c946a822fdaac0a6fe5ed22a50e6e1100f22b..eae6559854ecaefc95c5370ccec5e3a7c784accf 100644 --- a/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java +++ b/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.apache.logging.log4j.Logger; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.Group; @@ -46,11 +47,9 @@ import org.olat.basesecurity.manager.GroupDAO; import org.olat.core.commons.persistence.SortKey; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; -import org.olat.core.id.OLATResourceable; import org.olat.core.id.Organisation; import org.olat.core.id.OrganisationRef; import org.olat.core.id.Roles; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.modules.curriculum.Curriculum; import org.olat.modules.curriculum.CurriculumDataDeletable; @@ -61,6 +60,7 @@ import org.olat.modules.forms.EvaluationFormParticipation; import org.olat.modules.forms.EvaluationFormParticipationRef; import org.olat.modules.forms.EvaluationFormParticipationStatus; import org.olat.modules.forms.EvaluationFormSurvey; +import org.olat.modules.forms.EvaluationFormSurveyIdentifier; import org.olat.modules.forms.EvaluationFormSurveyRef; import org.olat.modules.forms.RubricRating; import org.olat.modules.forms.RubricStatistic; @@ -181,7 +181,7 @@ public class QualityServiceImpl public QualityDataCollection createDataCollection(Collection<Organisation> organisations, RepositoryEntry formEntry) { QualityDataCollection dataCollection = dataCollectionDao.createDataCollection(); - evaluationFormManager.createSurvey(dataCollection, null, formEntry); + evaluationFormManager.createSurvey(getSurveyIdent(dataCollection), formEntry); createDataCollectionReferences(organisations, formEntry, dataCollection); return dataCollection; } @@ -190,7 +190,7 @@ public class QualityServiceImpl public QualityDataCollection createDataCollection(Collection<Organisation> organisations, RepositoryEntry formEntry, QualityGenerator generator, Long generatorProviderKey) { QualityDataCollection dataCollection = createDataCollection(generator, generatorProviderKey); - evaluationFormManager.createSurvey(dataCollection, null, formEntry); + evaluationFormManager.createSurvey(getSurveyIdent(dataCollection), formEntry); createDataCollectionReferences(organisations, formEntry, dataCollection); return dataCollection; } @@ -199,8 +199,8 @@ public class QualityServiceImpl public QualityDataCollection createDataCollection(Collection<Organisation> organisations, QualityDataCollection previous, QualityGenerator generator, Long generatorProviderKey) { QualityDataCollection dataCollection = createDataCollection(generator, generatorProviderKey); - EvaluationFormSurvey previousSurvey = evaluationFormManager.loadSurvey(previous, null); - evaluationFormManager.createSurvey(dataCollection, null, previousSurvey); + EvaluationFormSurvey previousSurvey = evaluationFormManager.loadSurvey(getSurveyIdent(previous)); + evaluationFormManager.createSurvey(getSurveyIdent(dataCollection), previousSurvey); createDataCollectionReferences(organisations, previousSurvey.getFormEntry(), dataCollection); return dataCollection; } @@ -348,7 +348,7 @@ public class QualityServiceImpl private List<RubricStatistic> getRubricsStatistics(QualityDataCollection dataCollection) { RepositoryEntry formEntry = loadFormEntry(dataCollection); Form form = evaluationFormManager.loadForm(formEntry); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); SessionFilter filter = SessionFilterFactory.createSelectDone(survey); List<Rubric> rubrics = form.getElements().stream() @@ -381,7 +381,7 @@ public class QualityServiceImpl for (QualityContext context: contexts) { deleteContext(context); } - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); evaluationFormManager.deleteSurvey(survey); deleteReferences(dataCollection); resourceManager.deleteOLATResourceable(dataCollection); @@ -394,24 +394,24 @@ public class QualityServiceImpl @Override public RepositoryEntry loadFormEntry(QualityDataCollectionLight dataCollection) { - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); return survey.getFormEntry() != null? survey.getFormEntry(): null; } @Override public EvaluationFormSurvey loadSurvey(QualityDataCollectionLight dataCollection) { - return evaluationFormManager.loadSurvey(dataCollection, null); + return evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); } @Override public boolean isFormEntryUpdateable(QualityDataCollection dataCollection) { - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); return evaluationFormManager.isFormUpdateable(survey); } @Override public void updateFormEntry(QualityDataCollection dataCollection, RepositoryEntry formEntry) { - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); deleteReferences(dataCollection); evaluationFormManager.updateSurveyForm(survey, formEntry); @@ -456,7 +456,7 @@ public class QualityServiceImpl @Override public List<EvaluationFormParticipation> addParticipations(QualityDataCollectionLight dataCollection, Collection<Identity> executors) { List<EvaluationFormParticipation> participations = new ArrayList<>(); - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); for (Identity executor: executors) { EvaluationFormParticipation participation = evaluationFormManager.loadParticipationByExecutor(survey, executor); if (participation == null) { @@ -594,8 +594,8 @@ public class QualityServiceImpl private List<EvaluationFormParticipation> getParticipants(QualityReminder reminder) { QualityReminderType type = reminder.getType(); EvaluationFormParticipationStatus status = type.getParticipationStatus(); - OLATResourceable dataCollection = reminder.getDataCollection(); - EvaluationFormSurveyRef survey = evaluationFormManager.loadSurvey(dataCollection, null); + QualityDataCollection dataCollection = reminder.getDataCollection(); + EvaluationFormSurveyRef survey = evaluationFormManager.loadSurvey(getSurveyIdent(dataCollection)); return evaluationFormManager.loadParticipations(survey, status); } @@ -729,4 +729,8 @@ public class QualityServiceImpl return Collections.emptyList(); } + private EvaluationFormSurveyIdentifier getSurveyIdent(QualityDataCollectionLight dataCollection) { + return EvaluationFormSurveyIdentifier.of(dataCollection); + } + } diff --git a/src/main/java/org/olat/modules/quality/ui/DataCollectionReportController.java b/src/main/java/org/olat/modules/quality/ui/DataCollectionReportController.java index e8e2bd04da757584d63a2c07a21fb5417b4e842a..2f11187bece8d1986bc4426c2dbacdbfc6ff69fa 100644 --- a/src/main/java/org/olat/modules/quality/ui/DataCollectionReportController.java +++ b/src/main/java/org/olat/modules/quality/ui/DataCollectionReportController.java @@ -19,6 +19,8 @@ */ package org.olat.modules.quality.ui; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; @@ -65,7 +67,7 @@ public class DataCollectionReportController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null); + EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(of(dataCollection)); Form form = evaluationFormManager.loadForm(survey.getFormEntry()); DataStorage storage = evaluationFormManager.loadStorage(survey.getFormEntry()); SessionFilter filter = SessionFilterFactory.createSelectDone(survey); diff --git a/src/main/resources/database/mysql/alter_13_2_x_to_14_0_0.sql b/src/main/resources/database/mysql/alter_13_2_x_to_14_0_0.sql index 981cacfa985524fc93a091e9b44b30166ef8dee3..dcb47feec956929612b1a19a7723986332016109 100644 --- a/src/main/resources/database/mysql/alter_13_2_x_to_14_0_0.sql +++ b/src/main/resources/database/mysql/alter_13_2_x_to_14_0_0.sql @@ -170,3 +170,10 @@ alter table o_aconnect_user ENGINE = InnoDB; alter table o_aconnect_user add constraint aconn_ident_idx foreign key (fk_identity_id) references o_bs_identity (id); +-- Evaluation form +alter table o_eva_form_survey add e_sub_ident2 varchar(2048); + +drop index idx_eva_surv_ores_idx on o_eva_form_survey; +create index idx_eva_surv_ores_idx on o_eva_form_survey (e_resid, e_resname, e_sub_ident(255), e_sub_ident2(255)); + + diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 5b8049b64ff8a2b9c7c3acd3de368db800b8cd51..69d49a80bb2a7dad52240cfceabdb4a74faf2a99 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -2002,6 +2002,7 @@ create table o_eva_form_survey ( e_resname varchar(50) not null, e_resid bigint not null, e_sub_ident varchar(2048), + e_sub_ident2 varchar(2048), e_series_key bigint, e_series_index int, fk_form_entry bigint not null, @@ -3658,7 +3659,7 @@ alter table o_pf_page_user_infos add constraint page_pfpage_idx foreign key (fk_ -- evaluation form alter table o_eva_form_survey add constraint eva_surv_to_surv_idx foreign key (fk_series_previous) references o_eva_form_survey (id); -create unique index idx_eva_surv_ores_idx on o_eva_form_survey (e_resid, e_resname, e_sub_ident(255)); +create index idx_eva_surv_ores_idx on o_eva_form_survey (e_resid, e_resname, e_sub_ident(255), e_sub_ident2(255)); alter table o_eva_form_participation add constraint eva_part_to_surv_idx foreign key (fk_survey) references o_eva_form_survey (id); create unique index idx_eva_part_ident_idx on o_eva_form_participation (e_identifier_key, e_identifier_type, fk_survey); diff --git a/src/main/resources/database/oracle/alter_13_2_x_to_14_0_0.sql b/src/main/resources/database/oracle/alter_13_2_x_to_14_0_0.sql index 143a62b3c33cda64e62c98fec80a5c54bc9b4014..2a138c9784916ebc4ffa1ffcec8202d1f97dc2a2 100644 --- a/src/main/resources/database/oracle/alter_13_2_x_to_14_0_0.sql +++ b/src/main/resources/database/oracle/alter_13_2_x_to_14_0_0.sql @@ -167,3 +167,6 @@ create table o_aconnect_user ( alter table o_aconnect_user add constraint aconn_ident_idx foreign key (fk_identity_id) references o_bs_identity (id); create index idx_aconn_ident_idx on o_aconnect_user (fk_identity_id); + +-- Evaluation form +alter table o_eva_form_survey add e_sub_ident2 varchar(2048); diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index b5ecbd9f754922eb3600c7fd74bf6e12fc575f70..4bd1e82ef29726e1a7b973b9a67f3b5830d8e758 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -1928,6 +1928,7 @@ create table o_eva_form_survey ( e_resname varchar2(50) not null, e_resid number(20) not null, e_sub_ident varchar2(2048), + e_sub_ident2 varchar2(2048), e_series_key number(20), e_series_index number(20), fk_form_entry number(20) not null, diff --git a/src/main/resources/database/postgresql/alter_13_2_x_to_14_0_0.sql b/src/main/resources/database/postgresql/alter_13_2_x_to_14_0_0.sql index 9af68940ef9f69d573a38ec95b53f64af07e1f76..1353943c743031595e4a6b3c28e22e126300e3d2 100644 --- a/src/main/resources/database/postgresql/alter_13_2_x_to_14_0_0.sql +++ b/src/main/resources/database/postgresql/alter_13_2_x_to_14_0_0.sql @@ -166,3 +166,10 @@ create table o_aconnect_user ( alter table o_aconnect_user add constraint aconn_ident_idx foreign key (fk_identity_id) references o_bs_identity (id); create index idx_aconn_ident_idx on o_aconnect_user (fk_identity_id); + +-- Evaluation form +alter table o_eva_form_survey add e_sub_ident2 varchar(2048); + +drop index idx_eva_surv_ores_idx; +create index idx_eva_surv_ores_idx on o_eva_form_survey (e_resid, e_resname, e_sub_ident, e_sub_ident2); + diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 3aa82040360ee70a94eb272951b8227ad3d49d01..1219b3cf126cfe22c18b12543d8c332d9d2aa1e5 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -1895,6 +1895,7 @@ create table o_eva_form_survey ( e_resname varchar(50) not null, e_resid bigint not null, e_sub_ident varchar(2048), + e_sub_ident2 varchar(2048), e_series_key bigint, e_series_index int, fk_form_entry bigint not null, @@ -3690,7 +3691,7 @@ create index idx_wopi_ident_meta_idx on o_wopi_access(fk_identity, fk_metadata); -- evaluation form alter table o_eva_form_survey add constraint eva_surv_to_surv_idx foreign key (fk_series_previous) references o_eva_form_survey (id); -create unique index idx_eva_surv_ores_idx on o_eva_form_survey (e_resid, e_resname, e_sub_ident); +create index idx_eva_surv_ores_idx on o_eva_form_survey (e_resid, e_resname, e_sub_ident, e_sub_ident2); alter table o_eva_form_participation add constraint eva_part_to_surv_idx foreign key (fk_survey) references o_eva_form_survey (id); create unique index idx_eva_part_ident_idx on o_eva_form_participation (e_identifier_key, e_identifier_type, fk_survey); diff --git a/src/test/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAOTest.java b/src/test/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAOTest.java index ed354e01350840ad25a33c5d903d0fb9987b1dae..8b69c0a6cae370e98003a42f6f13810bff3247a8 100644 --- a/src/test/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAOTest.java +++ b/src/test/java/org/olat/modules/forms/manager/EvaluationFormSurveyDAOTest.java @@ -21,6 +21,7 @@ package org.olat.modules.forms.manager; import static org.assertj.core.api.Assertions.assertThat; +import java.util.List; import java.util.UUID; import org.assertj.core.api.SoftAssertions; @@ -60,17 +61,20 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldCreateSurvey() { OLATResourceable ores = JunitTestHelper.createRandomResource(); String subIdent = UUID.randomUUID().toString(); + String subIdent2 = UUID.randomUUID().toString(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); EvaluationFormSurvey previous = evaTestHelper.createSurvey(); dbInstance.commitAndCloseSession(); - EvaluationFormSurvey survey = sut.createSurvey(ores, subIdent, formEntry, previous); + EvaluationFormSurvey survey = sut.createSurvey(ores, subIdent, subIdent2, formEntry, previous); dbInstance.commitAndCloseSession(); SoftAssertions softly = new SoftAssertions(); softly.assertThat(survey).isNotNull(); softly.assertThat(survey.getCreationDate()).isNotNull(); softly.assertThat(survey.getLastModified()).isNotNull(); + softly.assertThat(survey.getIdentifier().getSubident()).isEqualTo(subIdent); + softly.assertThat(survey.getIdentifier().getSubident2()).isEqualTo(subIdent2); softly.assertThat(survey.getFormEntry()).isEqualTo(formEntry); softly.assertThat(survey.getSeriesKey()).isEqualTo(previous.getSeriesKey()); softly.assertThat(survey.getSeriesPrevious()).isEqualTo(previous); @@ -82,11 +86,26 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldLoadByResourceableWithSubident() { OLATResourceable ores = JunitTestHelper.createRandomResource(); String subIdent = UUID.randomUUID().toString(); + String subIdent2 = null; RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurveyRef survey = sut.createSurvey(ores, subIdent, formEntry, null); + EvaluationFormSurveyRef survey = sut.createSurvey(ores, subIdent, subIdent2, formEntry, null); dbInstance.commitAndCloseSession(); - EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, subIdent); + EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, subIdent, subIdent2); + + assertThat(loadedSurvey).isEqualTo(survey); + } + + @Test + public void shouldLoadByResourceableWithSubident2() { + OLATResourceable ores = JunitTestHelper.createRandomResource(); + String subIdent = UUID.randomUUID().toString(); + String subIdent2 = UUID.randomUUID().toString(); + RepositoryEntry formEntry = evaTestHelper.createFormEntry(); + EvaluationFormSurveyRef survey = sut.createSurvey(ores, subIdent, subIdent2, formEntry, null); + dbInstance.commitAndCloseSession(); + + EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, subIdent, subIdent2); assertThat(loadedSurvey).isEqualTo(survey); } @@ -95,25 +114,48 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldLoadByResourceableWithoutSubident() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurveyRef survey = sut.createSurvey(ores, null, formEntry, null); + EvaluationFormSurveyRef survey = sut.createSurvey(ores, null, null, formEntry, null); dbInstance.commitAndCloseSession(); - EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, null); + EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, null, null); assertThat(loadedSurvey).isEqualTo(survey); } + @Test + public void shouldLoadSurveysByResourceableWithoutSubident() { + RepositoryEntry formEntry = evaTestHelper.createFormEntry(); + OLATResourceable oresA = JunitTestHelper.createRandomResource(); + String subIdentA1 = UUID.randomUUID().toString(); + EvaluationFormSurvey surveyA = sut.createSurvey(oresA, subIdentA1, null, formEntry, null); + String subIdentB1 = UUID.randomUUID().toString(); + EvaluationFormSurvey surveyB = sut.createSurvey(oresA, subIdentB1, null, formEntry, null); + OLATResourceable oresOther = JunitTestHelper.createRandomResource(); + EvaluationFormSurvey surveyOther = sut.createSurvey(oresOther, subIdentA1, null, formEntry, null); + dbInstance.commitAndCloseSession(); + + List<EvaluationFormSurvey> surveys = sut.loadSurveysByResourceable(oresA, null, null); + + assertThat(surveys) + .containsExactlyInAnyOrder( + surveyA, + surveyB) + .doesNotContain( + surveyOther + ); + } + @Test public void shouldDeleteSurvey() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurveyRef survey = sut.createSurvey(ores, null, formEntry, null); + EvaluationFormSurveyRef survey = sut.createSurvey(ores, null, null, formEntry, null); dbInstance.commitAndCloseSession(); sut.delete(survey); dbInstance.commitAndCloseSession(); - EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, null); + EvaluationFormSurvey loadedSurvey = sut.loadByResourceable(ores, null, null); assertThat(loadedSurvey).isNull(); } @@ -121,9 +163,9 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldUpdateSeriesPrevious() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurvey previous = sut.createSurvey(ores, null, formEntry, null); - EvaluationFormSurvey survey = sut.createSurvey(ores, null, formEntry, previous); - EvaluationFormSurvey newPrevious = sut.createSurvey(ores, null, formEntry, null); + EvaluationFormSurvey previous = sut.createSurvey(ores, null, null, formEntry, null); + EvaluationFormSurvey survey = sut.createSurvey(ores, null, null, formEntry, previous); + EvaluationFormSurvey newPrevious = sut.createSurvey(ores, null, null, formEntry, null); dbInstance.commitAndCloseSession(); survey = sut.updateSeriesPrevious(survey, newPrevious); @@ -135,7 +177,7 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldCheckIfItHasNotSeriesNext() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurveyRef survey = sut.createSurvey(ores, null, formEntry, null); + EvaluationFormSurveyRef survey = sut.createSurvey(ores, null, null, formEntry, null); dbInstance.commitAndCloseSession(); boolean hasSeriesNext = sut.hasSeriesNext(survey); @@ -147,8 +189,8 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldCheckIfItHasSeriesNext() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurvey survey = sut.createSurvey(ores, null, formEntry, null); - sut.createSurvey(ores, null, formEntry, survey); + EvaluationFormSurvey survey = sut.createSurvey(ores, null, null, formEntry, null); + sut.createSurvey(ores, null, null, formEntry, survey); dbInstance.commitAndCloseSession(); boolean hasSeriesNext = sut.hasSeriesNext(survey); @@ -160,8 +202,8 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldLoadSeriesNext() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurvey survey = sut.createSurvey(ores, null, formEntry, null); - EvaluationFormSurvey next = sut.createSurvey(ores, null, formEntry, survey); + EvaluationFormSurvey survey = sut.createSurvey(ores, null, null, formEntry, null); + EvaluationFormSurvey next = sut.createSurvey(ores, null, null, formEntry, survey); dbInstance.commitAndCloseSession(); EvaluationFormSurvey loadedNext = sut.loadSeriesNext(survey); @@ -173,10 +215,10 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { public void shouldReindexSeries() { OLATResourceable ores = JunitTestHelper.createRandomResource(); RepositoryEntry formEntry = evaTestHelper.createFormEntry(); - EvaluationFormSurvey survey1 = sut.createSurvey(ores, "1", formEntry, null); - EvaluationFormSurvey survey2 = sut.createSurvey(ores, "2", formEntry, survey1); - EvaluationFormSurvey survey3 = sut.createSurvey(ores, "3", formEntry, survey2); - EvaluationFormSurvey survey4 = sut.createSurvey(ores, "4", formEntry, survey3); + EvaluationFormSurvey survey1 = sut.createSurvey(ores, "1", null, formEntry, null); + EvaluationFormSurvey survey2 = sut.createSurvey(ores, "2", null, formEntry, survey1); + EvaluationFormSurvey survey3 = sut.createSurvey(ores, "3", null, formEntry, survey2); + EvaluationFormSurvey survey4 = sut.createSurvey(ores, "4", null, formEntry, survey3); dbInstance.commitAndCloseSession(); EvaluationFormSurvey next = sut.loadSeriesNext(survey2); EvaluationFormSurvey seriesPrevious = survey2.getSeriesPrevious(); @@ -189,11 +231,11 @@ public class EvaluationFormSurveyDAOTest extends OlatTestCase { dbInstance.commitAndCloseSession(); SoftAssertions softly = new SoftAssertions(); - survey1 = sut.loadByResourceable(ores, "1"); + survey1 = sut.loadByResourceable(ores, "1", null); softly.assertThat(survey1.getSeriesIndex()).isEqualTo(1); - survey3 = sut.loadByResourceable(ores, "3"); + survey3 = sut.loadByResourceable(ores, "3", null); softly.assertThat(survey3.getSeriesIndex()).isEqualTo(2); - survey4 = sut.loadByResourceable(ores, "4"); + survey4 = sut.loadByResourceable(ores, "4", null); softly.assertThat(survey4.getSeriesIndex()).isEqualTo(3); } diff --git a/src/test/java/org/olat/modules/forms/manager/EvaluationFormTestsHelper.java b/src/test/java/org/olat/modules/forms/manager/EvaluationFormTestsHelper.java index 81ec8a6ec631e8a70781803ecea94d73e7a046be..4a4562bbbf31246f4521c42a4ad1c3c7b1378a85 100644 --- a/src/test/java/org/olat/modules/forms/manager/EvaluationFormTestsHelper.java +++ b/src/test/java/org/olat/modules/forms/manager/EvaluationFormTestsHelper.java @@ -19,6 +19,8 @@ */ package org.olat.modules.forms.manager; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; + import java.util.UUID; import org.olat.core.commons.persistence.DB; @@ -120,7 +122,7 @@ public class EvaluationFormTestsHelper { OLATResourceable ores = JunitTestHelper.createRandomResource(); String subIdent = UUID.randomUUID().toString(); RepositoryEntry formEntry = createFormEntry(); - return evaluationFormManager.createSurvey(ores, subIdent, formEntry); + return evaluationFormManager.createSurvey(of(ores, subIdent), formEntry); } EvaluationFormParticipation createParticipation() { diff --git a/src/test/java/org/olat/modules/quality/manager/QualityParticipationDAOTest.java b/src/test/java/org/olat/modules/quality/manager/QualityParticipationDAOTest.java index 8eaceecaf24ba2cd63c0934616899b704a0b05f8..b25dedbf48d8a427c6afc50b5c5f4fd70dae21a8 100644 --- a/src/test/java/org/olat/modules/quality/manager/QualityParticipationDAOTest.java +++ b/src/test/java/org/olat/modules/quality/manager/QualityParticipationDAOTest.java @@ -22,6 +22,7 @@ package org.olat.modules.quality.manager; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; import static org.olat.modules.quality.QualityDataCollectionStatus.FINISHED; import static org.olat.modules.quality.QualityDataCollectionStatus.PREPARATION; import static org.olat.modules.quality.QualityDataCollectionStatus.READY; @@ -330,7 +331,7 @@ public class QualityParticipationDAOTest extends OlatTestCase { @Test public void shouldFilterExecutorParticipationsByEvaluationFormParticipation() { QualityDataCollection dataCollection = qualityTestHelper.createDataCollection(); - EvaluationFormSurveyRef survey = evaManager.loadSurvey(dataCollection, null); + EvaluationFormSurveyRef survey = evaManager.loadSurvey(of(dataCollection)); Identity identity1 = JunitTestHelper.createAndPersistIdentityAsRndUser("quality-"); Identity identity2 = JunitTestHelper.createAndPersistIdentityAsRndUser("quality-"); Identity identity3 = JunitTestHelper.createAndPersistIdentityAsRndUser("quality-"); @@ -349,7 +350,7 @@ public class QualityParticipationDAOTest extends OlatTestCase { @Test public void shouldFilterExecutorParticipationsByParticipationStatus() { QualityDataCollection dataCollection = qualityTestHelper.createDataCollection(); - EvaluationFormSurveyRef survey = evaManager.loadSurvey(dataCollection, null); + EvaluationFormSurveyRef survey = evaManager.loadSurvey(of(dataCollection)); Identity identity1 = JunitTestHelper.createAndPersistIdentityAsRndUser("quality-"); Identity identity2 = JunitTestHelper.createAndPersistIdentityAsRndUser("quality-"); Identity identity3 = JunitTestHelper.createAndPersistIdentityAsRndUser("quality-"); diff --git a/src/test/java/org/olat/modules/quality/manager/QualityServiceImplTest.java b/src/test/java/org/olat/modules/quality/manager/QualityServiceImplTest.java index 59bcd9c15210ee2cb48f53efa8f6e3f758b3c93a..4ebc7c47e5fb39a8fe32e18599f609037fe2d149 100644 --- a/src/test/java/org/olat/modules/quality/manager/QualityServiceImplTest.java +++ b/src/test/java/org/olat/modules/quality/manager/QualityServiceImplTest.java @@ -74,7 +74,7 @@ public class QualityServiceImplTest { public void setUp() { MockitoAnnotations.initMocks(this); - when(evaluationFormManagerMock.loadSurvey(any(), any())).thenReturn(surveyMock); + when(evaluationFormManagerMock.loadSurvey(any())).thenReturn(surveyMock); } @Test diff --git a/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java b/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java index 8c81a9e7d24edc02c14092698fc7cea75626f171..583fa715ca0583ba96de02d4a2b6d2522716902a 100644 --- a/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java +++ b/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java @@ -20,6 +20,7 @@ package org.olat.modules.quality.manager; import static java.util.Collections.singletonList; +import static org.olat.modules.forms.EvaluationFormSurveyIdentifier.of; import java.util.Calendar; import java.util.Collection; @@ -203,13 +204,13 @@ public class QualityTestHelper { EvaluationFormSurvey createSurvey(QualityDataCollection dataCollection) { RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); - return evaluationFormManager.createSurvey(dataCollection, null, entry); + return evaluationFormManager.createSurvey(of(dataCollection), entry); } EvaluationFormSurvey createRandomSurvey() { OLATResource ores = JunitTestHelper.createRandomResource(); RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); - return evaluationFormManager.createSurvey(ores, null, entry); + return evaluationFormManager.createSurvey(of(ores), entry); } EvaluationFormParticipation createParticipation() {