/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.course.nodes; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.zip.ZipOutputStream; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.messages.MessageUIFactory; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.OLATResourceable; import org.olat.core.id.Roles; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.util.FileUtils; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.ZipUtil; import org.olat.core.util.resource.OresHelper; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.PublishEvents; import org.olat.course.editor.StatusDescription; import org.olat.course.export.CourseEnvironmentMapper; import org.olat.course.nodes.cl.CheckboxManager; import org.olat.course.nodes.cl.model.Checkbox; import org.olat.course.nodes.cl.model.CheckboxList; import org.olat.course.nodes.cl.ui.AssessedIdentityCheckListController; import org.olat.course.nodes.cl.ui.CheckListEditController; import org.olat.course.nodes.cl.ui.CheckListRunController; import org.olat.course.nodes.cl.ui.CheckListRunForCoachController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.repository.RepositoryEntry; /** * * Initial date: 06.02.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class CheckListCourseNode extends AbstractAccessableCourseNode implements PersistentAssessableCourseNode { private static final String translatorStr = Util.getPackageName(CheckListEditController.class); private static final long serialVersionUID = -7460670977531082040L; private static final String TYPE = "checklist"; private static final String ICON_CSS_CLASS = "o_cl_icon"; public static final String CONFIG_KEY_PASSED_SUM_CHECKBOX = "passedSumCheckbox"; public static final String CONFIG_KEY_PASSED_SUM_CUTVALUE = "passedSumCutValue"; public static final String CONFIG_KEY_PASSED_MANUAL_CORRECTION = "passedManualCorrection"; public static final String CONFIG_KEY_DUE_DATE = "dueDate"; public static final String CONFIG_KEY_CLOSE_AFTER_DUE_DATE = "closeAfterDueDate"; public static final String CONFIG_KEY_CHECKBOX = "checkbox"; public static final String FOLDER_NAME = "checklistfiles"; /** * Constructor for a course building block of the type structure */ public CheckListCourseNode() { super(TYPE); updateModuleConfigDefaults(true); } /** * @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); // only the precondition "access" can be configured till now CheckListEditController childTabCntrllr = new CheckListEditController(this, ureq, wControl, 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, final UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { updateModuleConfigDefaults(false); Controller ctrl; OLATResourceable ores = OresHelper.createOLATResourceableInstance("CourseModule", userCourseEnv.getCourseEnvironment().getCourseResourceableId()); Roles roles = ureq.getUserSession().getRoles(); if (roles.isGuestOnly()) { Translator trans = Util.createPackageTranslator(CheckListCourseNode.class, ureq.getLocale()); String title = trans.translate("guestnoaccess.title"); String message = trans.translate("guestnoaccess.message"); ctrl = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); } else if(userCourseEnv.isCoach() || userCourseEnv.isAdmin()) { ctrl = new CheckListRunForCoachController(ureq, wControl, userCourseEnv, ores, this); } else { ctrl = new CheckListRunController(ureq, wControl, userCourseEnv, ores, this); } Controller cont = TitledWrapperHelper.getWrapper(ureq, wControl, ctrl, this, ICON_CSS_CLASS); return new NodeRunConstructionResult(cont); } /** * @see org.olat.course.nodes.GenericCourseNode#createPreviewController(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 Controller createPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne) { return createNodeRunConstructionResult(ureq, wControl, userCourseEnv, ne, null).getRunController(); } /** * the structure node does not have a score itself, but calculates the * score/passed info by evaluating the configured expression in the the * (condition)interpreter. * * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public AssessmentEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) { if(hasPassedConfigured() || hasScoreConfigured()) { return getUserScoreEvaluation(getUserAssessmentEntry(userCourseEnv)); } return AssessmentEvaluation.EMPTY_EVAL; } @Override public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry) { return AssessmentEvaluation.toAssessmentEvalutation(entry, this); } @Override public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity(); return am.getAssessmentEntry(this, mySelf); } /** * @see org.olat.course.nodes.CourseNode#isConfigValid() */ public StatusDescription isConfigValid() { if (oneClickStatusCache != null) { return oneClickStatusCache[0]; } StatusDescription sd = StatusDescription.NOERROR; return sd; } /** * @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, translatorStr, getConditionExpressions()); oneClickStatusCache = StatusDescriptionHelper.sort(sds); return oneClickStatusCache; } @Override public List<StatusDescription> publishUpdatesExplanations(CourseEditorEnv cev) { List<StatusDescription> statusDescs = new ArrayList<>(); StatusDescription statusDesc1 = new StatusDescription(Level.INFO, "checklist.update.assessment", null, null, translatorStr); statusDesc1.setDescriptionForUnit(getIdent()); statusDescs.add(statusDesc1); StatusDescription statusDesc2 = new StatusDescription(Level.INFO, "checklist.update.efficiencystatements", null, null, translatorStr); statusDesc2.setDescriptionForUnit(getIdent()); statusDescs.add(statusDesc2); return statusDescs; } /** * @see org.olat.course.nodes.CourseNode#getReferencedRepositoryEntry() */ @Override public RepositoryEntry getReferencedRepositoryEntry() { return null; } /** * @see org.olat.course.nodes.CourseNode#needsReferenceToARepositoryEntry() */ @Override public boolean needsReferenceToARepositoryEntry() { return false; } @Override public boolean isAssessedBusinessGroups() { return false; } /** * @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); } ModuleConfiguration config = getModuleConfiguration(); Float cut = (Float) config.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); return cut; } /** * @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(MSCourseNode.CONFIG_KEY_SCORE_MAX); return max; } /** * @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); } ModuleConfiguration config = getModuleConfiguration(); Float min = (Float) config.get(MSCourseNode.CONFIG_KEY_SCORE_MIN); return min; } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserCoachComment(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public String getUserCoachComment(UserCourseEnvironment userCourseEnvironment) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); String coachCommentValue = am.getNodeCoachComment(this, userCourseEnvironment.getIdentityEnvironment().getIdentity()); return coachCommentValue; } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserLog(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public String getUserLog(UserCourseEnvironment userCourseEnvironment) { UserNodeAuditManager am = userCourseEnvironment.getCourseEnvironment().getAuditManager(); String logValue = am.getUserNodeLog(this, userCourseEnvironment.getIdentityEnvironment().getIdentity()); return logValue; } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserUserComment(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public String getUserUserComment(UserCourseEnvironment userCourseEnvironment) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); String userCommentValue = am.getNodeComment(this, userCourseEnvironment.getIdentityEnvironment().getIdentity()); return userCommentValue; } /** * @see org.olat.course.nodes.AssessableCourseNode#hasCommentConfigured() */ @Override public boolean hasCommentConfigured() { // never has comments return true; } /** * @see org.olat.course.nodes.AssessableCourseNode#hasPassedConfigured() */ @Override public boolean hasPassedConfigured() { ModuleConfiguration config = getModuleConfiguration(); Boolean passed = (Boolean) config.get(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD); if (passed == null) return false; return passed.booleanValue(); } /** * @see org.olat.course.nodes.AssessableCourseNode#hasScoreConfigured() */ @Override public boolean hasScoreConfigured() { ModuleConfiguration config = getModuleConfiguration(); Boolean score = (Boolean)config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD); if (score == null) return false; return score.booleanValue(); } /** * @see org.olat.course.nodes.AssessableCourseNode#hasStatusConfigured() */ @Override public boolean hasStatusConfigured() { return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#isEditableConfigured() */ @Override public boolean isEditableConfigured() { 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) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity(); if (coachComment != null) { am.saveNodeCoachComment(this, mySelf, coachComment); } } /** * @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) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity(); am.saveScoreEvaluation(this, coachingIdentity, mySelf, new ScoreEvaluation(scoreEvaluation), userCourseEnvironment, incrementAttempts); } /** * @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) { AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager(); Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity(); if (userComment != null) { am.saveNodeComment(this, coachingIdentity, mySelf, userComment); } } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserAttempts(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public Integer getUserAttempts(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(CheckListCourseNode.class, "No attempts available in ST 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) { throw new OLATRuntimeException(CheckListCourseNode.class, "Attempts variable can't be updated in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#incrementUserAttempts(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public void incrementUserAttempts(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(CheckListCourseNode.class, "Attempts variable can't be updated in ST nodes", null); } /** * @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) { Identity assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); Long resId = assessedUserCourseEnv.getCourseEnvironment().getCourseResourceableId(); OLATResourceable courseOres = OresHelper.createOLATResourceableInstance("CourseModule", resId); return new AssessedIdentityCheckListController(ureq, wControl, assessedIdentity, courseOres, coachCourseEnv, assessedUserCourseEnv, this, false, false); } /** * @see org.olat.course.nodes.AssessableCourseNode#getDetailsListView(org.olat.course.run.userview.UserCourseEnvironment) */ public String getDetailsListView(UserCourseEnvironment userCourseEnvironment) { return "checklist"; } /** * @see org.olat.course.nodes.AssessableCourseNode#getDetailsListViewHeaderKey() */ public String getDetailsListViewHeaderKey() { return null; } /** * @see org.olat.course.nodes.AssessableCourseNode#hasDetails() */ @Override public boolean hasDetails() { return true; } @Override public boolean archiveNodeData(Locale locale, ICourse course, ArchiveOptions options, ZipOutputStream exportStream, String charset) { String dirName = "cl_" + StringHelper.transformDisplayNameToFileSystemName(getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())); ModuleConfiguration config = getModuleConfiguration(); CheckboxList list = (CheckboxList)config.get(CONFIG_KEY_CHECKBOX); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); if(list != null && list.getList() != null) { Set<String> usedNames = new HashSet<String>(); for(Checkbox checkbox:list.getList()) { VFSContainer dir = checkboxManager.getFileContainer(course.getCourseEnvironment(), this); if(dir != null) { VFSItem item = dir.resolve(checkbox.getFilename()); if(item instanceof VFSLeaf) { String path = dirName + "/" + Formatter.makeStringFilesystemSave(checkbox.getTitle()); if(usedNames.contains(checkbox.getTitle())) { path += "_" + checkbox.getCheckboxId(); } else { usedNames.add(checkbox.getTitle()); } ZipUtil.addToZip(item, path, exportStream); } } } } return super.archiveNodeData(locale, course, options, exportStream, charset); } @Override public void exportNode(File fExportDirectory, ICourse course) { //export the files CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); ModuleConfiguration config = getModuleConfiguration(); CheckboxList list = (CheckboxList)config.get(CONFIG_KEY_CHECKBOX); if(list != null && list.getList() != null) { for(Checkbox checkbox:list.getList()) { File dir = checkboxManager.getFileDirectory(course.getCourseEnvironment(), this); if(dir.exists()) { File fFileExportDir = new File(fExportDirectory, "checklistfiles/" + getIdent() + "/" + checkbox.getCheckboxId()); fFileExportDir.mkdirs(); FileUtils.copyDirContentsToDir(dir, fFileExportDir, false, "export files of checkbox"); } } } } @Override public void importNode(File importDirectory, ICourse course, Identity owner, Locale locale, boolean withReferences) { CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); ModuleConfiguration config = getModuleConfiguration(); CheckboxList list = (CheckboxList)config.get(CONFIG_KEY_CHECKBOX); if(list != null && list.getList() != null) { for(Checkbox checkbox:list.getList()) { File fFileImportDir = new File(importDirectory, "checklistfiles/" + getIdent() + "/" + checkbox.getCheckboxId()); String newCheckboxId = UUID.randomUUID().toString(); checkbox.setCheckboxId(newCheckboxId); if(fFileImportDir.exists()) { File dir = checkboxManager.getFileDirectory(course.getCourseEnvironment(), this); dir.mkdirs(); FileUtils.copyDirContentsToDir(fFileImportDir, dir, false, "import file of checkbox"); } } } } @Override public void cleanupOnDelete(ICourse course) { super.cleanupOnDelete(course); CoreSpringFactory.getImpl(CheckboxManager.class).deleteCheckbox(course, getIdent()); } /** * The user course environment must be the coach or the user which preceed to * the changes. * @param userCourseEnv * @param assessedIdentity */ public void updateScoreEvaluation(Identity identity, UserCourseEnvironment assessedUserCourseEnv, Identity assessedIdentity) { ModuleConfiguration config = getModuleConfiguration(); Boolean sum = (Boolean)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_SUM_CHECKBOX); Float cutValue = (Float)config.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); Float maxScore = (Float)config.get(MSCourseNode.CONFIG_KEY_SCORE_MAX); Boolean manualCorrection = (Boolean)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_MANUAL_CORRECTION); if(cutValue != null) { doUpdateAssessment(cutValue, maxScore, identity, assessedUserCourseEnv, assessedIdentity); } else if(sum != null && sum.booleanValue()) { doUpdateAssessmentBySum(identity, assessedUserCourseEnv, assessedIdentity); } else if(manualCorrection != null && manualCorrection.booleanValue()) { doUpdateManualAssessment(maxScore, identity, assessedUserCourseEnv, assessedIdentity); } } private void doUpdateAssessment(Float cutValue, Float maxScore, Identity identity, UserCourseEnvironment assessedUserCourseEnv, Identity assessedIdentity) { OLATResourceable courseOres = OresHelper .createOLATResourceableInstance("CourseModule", assessedUserCourseEnv.getCourseEnvironment().getCourseResourceableId()); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); float score = checkboxManager.calculateScore(assessedIdentity, courseOres, getIdent()); if(maxScore != null && maxScore.floatValue() < score) { score = maxScore.floatValue(); } Boolean passed = null; if(cutValue != null) { boolean aboveCutValue = score >= cutValue.floatValue(); passed = new Boolean(aboveCutValue); } ScoreEvaluation sceval = new ScoreEvaluation(new Float(score), passed); AssessmentManager am = assessedUserCourseEnv.getCourseEnvironment().getAssessmentManager(); am.saveScoreEvaluation(this, identity, assessedIdentity, sceval, assessedUserCourseEnv, false); } private void doUpdateAssessmentBySum(Identity identity, UserCourseEnvironment assessedUserCourseEnv, Identity assessedIdentity) { OLATResourceable courseOres = OresHelper .createOLATResourceableInstance("CourseModule", assessedUserCourseEnv.getCourseEnvironment().getCourseResourceableId()); ModuleConfiguration config = getModuleConfiguration(); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); int checkedBox = checkboxManager.countChecked(assessedIdentity, courseOres, getIdent()); CheckboxList checkboxList = (CheckboxList)config.get(CONFIG_KEY_CHECKBOX); Integer cut = (Integer)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_SUM_CUTVALUE); int minNumOfCheckbox = cut == null ? checkboxList.getNumOfCheckbox() : cut.intValue(); boolean passed = checkedBox >= minNumOfCheckbox; Float score = null; if(passed) { Boolean scoreGrantedBool = (Boolean)config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD); if(scoreGrantedBool != null && scoreGrantedBool.booleanValue()) { score = checkboxManager.calculateScore(assessedIdentity, courseOres, getIdent()); Float maxScore = (Float)config.get(MSCourseNode.CONFIG_KEY_SCORE_MAX); if(maxScore != null && maxScore.floatValue() < score) { score = maxScore.floatValue(); } } } ScoreEvaluation sceval = new ScoreEvaluation(score, new Boolean(passed)); AssessmentManager am = assessedUserCourseEnv.getCourseEnvironment().getAssessmentManager(); am.saveScoreEvaluation(this, identity, assessedIdentity, sceval, assessedUserCourseEnv, false); } private void doUpdateManualAssessment(Float maxScore, Identity identity, UserCourseEnvironment assessedUserCourseEnv, Identity assessedIdentity) { OLATResourceable courseOres = OresHelper .createOLATResourceableInstance("CourseModule", assessedUserCourseEnv.getCourseEnvironment().getCourseResourceableId()); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); float score = checkboxManager.calculateScore(assessedIdentity, courseOres, getIdent()); if(maxScore != null && maxScore.floatValue() < score) { score = maxScore.floatValue(); } AssessmentManager am = assessedUserCourseEnv.getCourseEnvironment().getAssessmentManager(); ScoreEvaluation currentEval = getUserScoreEvaluation(am.getAssessmentEntry(this, assessedIdentity)); ScoreEvaluation sceval = new ScoreEvaluation(new Float(score), currentEval.getPassed()); am.saveScoreEvaluation(this, identity, assessedIdentity, sceval, assessedUserCourseEnv, false); } @Override public CourseNode createInstanceForCopy(boolean isNewTitle, ICourse course, Identity author) { CheckListCourseNode cNode = (CheckListCourseNode)super.createInstanceForCopy(isNewTitle, course, author); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); CheckboxList list = (CheckboxList)cNode.getModuleConfiguration().get(CONFIG_KEY_CHECKBOX); if (list!=null) { for(Checkbox checkbox:list.getList()) { checkbox.setCheckboxId(UUID.randomUUID().toString()); } } // the ident of the course node is the same File sourceDir = checkboxManager.getFileDirectory(course.getCourseEnvironment(), this); if(sourceDir.exists()) { File targetDir = checkboxManager.getFileDirectory(course.getCourseEnvironment(), cNode); if(!targetDir.exists()) { targetDir.mkdirs(); FileUtils.copyDirContentsToDir(sourceDir, targetDir, false, "copy files of checkbox"); } } return cNode; } @Override public void postCopy(CourseEnvironmentMapper envMapper, Processing processType, ICourse course, ICourse sourceCourse) { ModuleConfiguration config = getModuleConfiguration(); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); CheckboxList list = (CheckboxList)config.get(CONFIG_KEY_CHECKBOX); if (list !=null && list.getList().size() > 0) { for(Checkbox checkbox:list.getList()) { String sourceId = checkbox.getCheckboxId(); String targetId = envMapper.getTargetUniqueKey(getIdent(), sourceId); if(targetId == null) { targetId = UUID.randomUUID().toString(); envMapper.addUniqueKeyPair(getIdent(), sourceId, targetId); } checkbox.setCheckboxId(targetId); } } // the ident of the course node is the same File sourceDir = checkboxManager.getFileDirectory(sourceCourse.getCourseEnvironment(), this); if(sourceDir.exists()) { File targetDir = checkboxManager.getFileDirectory(course.getCourseEnvironment(), this); if(!targetDir.exists()) { targetDir.mkdirs(); FileUtils.copyDirContentsToDir(sourceDir, targetDir, false, "copy files of checkbox"); } } checkboxManager.syncCheckbox(list, course, getIdent()); super.postCopy(envMapper, processType, course, sourceCourse); } @Override public void updateOnPublish(Locale locale, ICourse course, Identity publisher, PublishEvents publishEvents) { ModuleConfiguration config = getModuleConfiguration(); //sync the checkbox with the database CheckboxList list = (CheckboxList)config.get(CONFIG_KEY_CHECKBOX); CheckboxManager checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class); checkboxManager.syncCheckbox(list, course, getIdent()); CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager(); List<Identity> assessedUsers = pm.getAllIdentitiesWithCourseAssessmentData(null); int count = 0; for(Identity assessedIdentity: assessedUsers) { updateScorePassedOnPublish(assessedIdentity, publisher, checkboxManager, course); if(++count % 10 == 0) { DBFactory.getInstance().commitAndCloseSession(); } } super.updateOnPublish(locale, course, publisher, publishEvents); } private void updateScorePassedOnPublish(Identity assessedIdentity, Identity coachIdentity, CheckboxManager checkboxManager, ICourse course) { AssessmentManager am = course.getCourseEnvironment().getAssessmentManager(); Float currentScore = am.getNodeScore(this, assessedIdentity); Boolean currentPassed = am.getNodePassed(this, assessedIdentity); Float updatedScore = null; Boolean updatedPassed = null; ModuleConfiguration config = getModuleConfiguration(); Boolean scoreGrantedBool = (Boolean)config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD); if(scoreGrantedBool != null && scoreGrantedBool.booleanValue()) { updatedScore = checkboxManager.calculateScore(assessedIdentity, course, getIdent()); } else { updatedScore = null; } Boolean passedBool = (Boolean)config.get(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD); if(passedBool != null && passedBool.booleanValue()) { Float cutValue = (Float)config.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); Boolean sumCheckbox = (Boolean)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_SUM_CHECKBOX); if(sumCheckbox != null && sumCheckbox.booleanValue()) { Integer minValue = (Integer)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_SUM_CUTVALUE); int checkedBox = checkboxManager.countChecked(assessedIdentity, course, getIdent()); if(minValue != null && minValue.intValue() <= checkedBox) { updatedPassed = Boolean.TRUE; } else { updatedPassed = Boolean.FALSE; } } else if (cutValue != null) { if(updatedScore == null) { updatedScore = checkboxManager.calculateScore(assessedIdentity, course, getIdent()); } if(updatedScore != null && cutValue.floatValue() <= updatedScore.floatValue()) { updatedPassed = Boolean.TRUE; } else { updatedPassed = Boolean.FALSE; } } } else { updatedPassed = null; } boolean needUpdate = false; Boolean manualCorrection = (Boolean)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_MANUAL_CORRECTION); if(manualCorrection == null || !manualCorrection.booleanValue()) { //update passed if((currentPassed == null && updatedPassed != null && updatedScore != null && updatedScore.floatValue() > 0f) || (currentPassed != null && updatedPassed == null) || (currentPassed != null && !currentPassed.equals(updatedPassed))) { needUpdate = true; } } if((currentScore == null && updatedScore != null && updatedScore.floatValue() > 0f) || (currentScore != null && updatedScore == null) || (currentScore != null && !currentScore.equals(updatedScore))) { needUpdate = true; } if(needUpdate) { ScoreEvaluation scoreEval = new ScoreEvaluation(updatedScore, updatedPassed); IdentityEnvironment identityEnv = new IdentityEnvironment(assessedIdentity, null); UserCourseEnvironment uce = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment()); am.saveScoreEvaluation(this, coachIdentity, assessedIdentity, scoreEval, uce, false); } } /** * Update the module configuration to have all mandatory configuration flags * set to usefull default values * * @param isNewNode true: an initial configuration is set; false: upgrading * from previous node configuration version, set default to maintain * previous behaviour */ @Override public void updateModuleConfigDefaults(boolean isNewNode) { ModuleConfiguration config = getModuleConfiguration(); if (isNewNode) { config.setConfigurationVersion(1); } } }