From 49405fdd758a0f957093fa779a097b07ab91b7f9 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Fri, 4 Dec 2015 17:59:49 +0100 Subject: [PATCH] OO-1593: assessment entry for structure course element --- .../course/assessment/AssessmentHelper.java | 19 +- .../course/assessment/AssessmentManager.java | 4 + .../IdentityAssessmentOverviewController.java | 2 +- .../manager/CourseAssessmentManagerImpl.java | 47 +- .../manager/EfficiencyStatementManager.java | 142 +++--- .../ui/tool/AssessedIdentityController.java | 36 ++ .../assessment/ui/tool/AssessmentForm.java | 413 ++++++++++++++++++ .../AssessmentIdentitiesCourseController.java | 399 ----------------- .../AssessmentIdentitiesCourseTableModel.java | 115 ----- ...essmentIdentitiesCourseTreeController.java | 12 +- .../AssessmentIdentityCourseController.java | 23 +- ...ssessmentIdentityCourseNodeController.java | 16 +- .../ui/tool/AssessmentToolController.java | 5 +- .../ui/tool/CourseToolContainer.java | 48 ++ ... => IdentityListCourseNodeController.java} | 63 ++- ... => IdentityListCourseNodeTableModel.java} | 15 +- .../nodes/CalculatedAssessableCourseNode.java | 4 + .../org/olat/course/nodes/STCourseNode.java | 6 + .../run/preview/PreviewAssessmentManager.java | 10 + .../course/run/scoring/ScoreAccounting.java | 114 ++++- .../modules/assessment/AssessmentService.java | 13 + .../manager/AssessmentEntryDAO.java | 18 + .../manager/AssessmentServiceImpl.java | 6 + .../org/olat/upgrade/OLATUpgrade_11_0_0.java | 134 ++++-- 24 files changed, 1004 insertions(+), 660 deletions(-) create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java delete mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java delete mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java rename src/main/java/org/olat/course/assessment/ui/tool/{AssessmentIdentitiesCourseNodeController.java => IdentityListCourseNodeController.java} (86%) rename src/main/java/org/olat/course/assessment/ui/tool/{AssessmentIdentitiesCourseNodeTableModel.java => IdentityListCourseNodeTableModel.java} (88%) diff --git a/src/main/java/org/olat/course/assessment/AssessmentHelper.java b/src/main/java/org/olat/course/assessment/AssessmentHelper.java index 6b2c4b37b8d..ddf4c264e5b 100644 --- a/src/main/java/org/olat/course/assessment/AssessmentHelper.java +++ b/src/main/java/org/olat/course/assessment/AssessmentHelper.java @@ -401,8 +401,25 @@ public class AssessmentHelper { return data; } + /** + * Calculated + * + * + * @param evaluatedScoreAccounting + * @param userCourseEnv + * @param discardEmptyNodes + * @param discardComments + * @return + */ + public static List<AssessmentNodeData> getAssessmentNodeDataList(ScoreAccounting evaluatedScoreAccounting, UserCourseEnvironment userCourseEnv, boolean discardEmptyNodes, boolean discardComments) { + List<AssessmentNodeData> data = new ArrayList<AssessmentNodeData>(50); + getAssessmentNodeDataList(0, userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode(), + evaluatedScoreAccounting, userCourseEnv, discardEmptyNodes, discardComments, data); + return data; + } + - private static int getAssessmentNodeDataList(int recursionLevel, CourseNode courseNode, ScoreAccounting scoreAccounting, + public static int getAssessmentNodeDataList(int recursionLevel, CourseNode courseNode, ScoreAccounting scoreAccounting, UserCourseEnvironment userCourseEnv, boolean discardEmptyNodes, boolean discardComments, List<AssessmentNodeData> data) { // 1) Get list of children data using recursion of this method AssessmentNodeData assessmentNodeData = new AssessmentNodeData(recursionLevel, courseNode); diff --git a/src/main/java/org/olat/course/assessment/AssessmentManager.java b/src/main/java/org/olat/course/assessment/AssessmentManager.java index 02cbfc59231..1961a3a3a6f 100644 --- a/src/main/java/org/olat/course/assessment/AssessmentManager.java +++ b/src/main/java/org/olat/course/assessment/AssessmentManager.java @@ -195,6 +195,10 @@ public interface AssessmentManager { public AssessmentEntry getAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, String referenceSoftKey); + + public AssessmentEntry createAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, ScoreEvaluation scoreEvaluation); + + public AssessmentEntry updateAssessmentEntry(AssessmentEntry assessmentEntry); public List<AssessmentEntry> getAssessmentEntries(CourseNode courseNode); diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java index d87f9cba9b0..44485f7778c 100644 --- a/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java +++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java @@ -237,7 +237,7 @@ public class IdentityAssessmentOverviewController extends BasicController { } } - private void doIdentityAssessmentOverview(UserRequest ureq) { + public void doIdentityAssessmentOverview(UserRequest ureq) { List<AssessmentNodeData> nodesTableList; if (loadNodesFromCourse) { // get list of course node and user data and populate table data model diff --git a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java index 2ffa0830b59..23c07572df9 100644 --- a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java +++ b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java @@ -20,10 +20,12 @@ package org.olat.course.assessment.manager; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Date; import java.util.List; import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.logging.activity.StringResourceableType; @@ -35,14 +37,18 @@ import org.olat.core.util.resource.OresHelper; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentChangedEvent; +import org.olat.course.assessment.AssessmentHelper; import org.olat.course.assessment.AssessmentLoggingAction; import org.olat.course.assessment.AssessmentManager; +import org.olat.course.assessment.model.AssessmentNodeData; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.certificate.CertificateTemplate; import org.olat.course.certificate.CertificatesManager; import org.olat.course.certificate.model.CertificateInfos; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CourseNode; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.run.scoring.ScoreAccounting; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; @@ -98,6 +104,27 @@ public class CourseAssessmentManagerImpl implements AssessmentManager { return assessmentService.loadAssessmentEntries(assessedGoup, courseEntry, courseNode.getIdent()); } + @Override + public AssessmentEntry createAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, ScoreEvaluation scoreEvaluation) { + RepositoryEntry referenceEntry = null; + if(courseNode.needsReferenceToARepositoryEntry()) { + referenceEntry = courseNode.getReferencedRepositoryEntry(); + } + Float score = null; + Boolean passed = null; + if(scoreEvaluation != null) { + score = scoreEvaluation.getScore(); + passed = scoreEvaluation.getPassed(); + } + return assessmentService + .createAssessmentEntry(assessedIdentity, courseEntry, courseNode.getIdent(), referenceEntry, score, passed); + } + + @Override + public AssessmentEntry updateAssessmentEntry(AssessmentEntry assessmentEntry) { + return assessmentService.updateAssessmentEntry(assessmentEntry); + } + @Override public void saveNodeAttempts(CourseNode courseNode, Identity identity, Identity assessedIdentity, Integer attempts) { ICourse course = CourseFactory.loadCourse(courseEntry.getOlatResource()); @@ -209,7 +236,8 @@ public class CourseAssessmentManagerImpl implements AssessmentManager { public void saveScoreEvaluation(AssessableCourseNode courseNode, Identity identity, Identity assessedIdentity, ScoreEvaluation scoreEvaluation, UserCourseEnvironment userCourseEnv, boolean incrementUserAttempts) { - ICourse course = CourseFactory.loadCourse(courseEntry.getOlatResource()); + final ICourse course = CourseFactory.loadCourse(courseEntry); + final CourseEnvironment courseEnv = userCourseEnv.getCourseEnvironment(); Float score = scoreEvaluation.getScore(); Boolean passed = scoreEvaluation.getPassed(); @@ -233,9 +261,14 @@ public class CourseAssessmentManagerImpl implements AssessmentManager { assessmentEntry.setAttempts(attempts); } assessmentEntry = assessmentService.updateAssessmentEntry(assessmentEntry); + DBFactory.getInstance().commit();//commit before sending events + //reevalute the tree + ScoreAccounting scoreAccounting = userCourseEnv.getScoreAccounting(); + scoreAccounting.evaluateAll(true); + DBFactory.getInstance().commit();//commit before sending events // node log - UserNodeAuditManager am = course.getCourseEnvironment().getAuditManager(); + UserNodeAuditManager am = courseEnv.getAuditManager(); am.appendToUserNodeLog(courseNode, identity, assessedIdentity, "score set to: " + String.valueOf(scoreEvaluation.getScore())); if(scoreEvaluation.getPassed()!=null) { am.appendToUserNodeLog(courseNode, identity, assessedIdentity, "passed set to: " + scoreEvaluation.getPassed().toString()); @@ -277,9 +310,13 @@ public class CourseAssessmentManagerImpl implements AssessmentManager { LoggingResourceable.wrapNonOlatResource(StringResourceableType.qtiAttempts, "", String.valueOf(attempts))); } - userCourseEnv.getScoreAccounting().evaluateAll();//.scoreInfoChanged(courseNode, scoreEvaluation); - // Update users efficiency statement - efficiencyStatementManager.updateUserEfficiencyStatement(userCourseEnv); + // write only when enabled for this course + if (courseEnv.getCourseConfig().isEfficencyStatementEnabled()) { + List<AssessmentNodeData> data = new ArrayList<AssessmentNodeData>(50); + AssessmentHelper.getAssessmentNodeDataList(0, courseEnv.getRunStructure().getRootNode(), + scoreAccounting, userCourseEnv, true, true, data); + efficiencyStatementManager.updateUserEfficiencyStatement(assessedIdentity, courseEnv, data, courseEntry); + } if(passed != null && passed.booleanValue() && course.getCourseConfig().isAutomaticCertificationEnabled()) { if(certificatesManager.isCertificationAllowed(assessedIdentity, courseEntry)) { diff --git a/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java b/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java index 625c7c48a0f..91dadd4c40b 100644 --- a/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java +++ b/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java @@ -37,7 +37,8 @@ import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; -import org.olat.core.manager.BasicManager; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.coordinate.SyncerExecutor; import org.olat.core.util.resource.OresHelper; @@ -56,6 +57,7 @@ import org.olat.course.assessment.model.UserEfficiencyStatementImpl; import org.olat.course.assessment.model.UserEfficiencyStatementLight; import org.olat.course.assessment.model.UserEfficiencyStatementStandalone; import org.olat.course.config.CourseConfig; +import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; @@ -78,7 +80,9 @@ import com.thoughtworks.xstream.XStream; * @author gnaegi */ @Service -public class EfficiencyStatementManager extends BasicManager implements UserDataDeletable { +public class EfficiencyStatementManager implements UserDataDeletable { + + private static final OLog log = Tracing.createLoggerFor(EfficiencyStatementManager.class); public static final String KEY_ASSESSMENT_NODES = "assessmentNodes"; public static final String KEY_COURSE_TITLE = "courseTitle"; @@ -97,10 +101,8 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * @param userCourseEnv */ public void updateUserEfficiencyStatement(UserCourseEnvironment userCourseEnv) { - Long courseResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); - OLATResourceable courseOres = OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId); RepositoryEntry re = userCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); - updateUserEfficiencyStatement(userCourseEnv, re, courseOres); + updateUserEfficiencyStatement(userCourseEnv, re); } public UserEfficiencyStatement createUserEfficiencyStatement(Date creationDate, Float score, Boolean passed, Identity identity, OLATResource resource) { @@ -158,63 +160,69 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * @param repoEntryKey * @param courseOres */ - private void updateUserEfficiencyStatement(final UserCourseEnvironment userCourseEnv, final RepositoryEntry repoEntry, OLATResourceable courseOres) { + private void updateUserEfficiencyStatement(final UserCourseEnvironment userCourseEnv, final RepositoryEntry repoEntry) { // o_clusterOK: by ld CourseConfig cc = userCourseEnv.getCourseEnvironment().getCourseConfig(); // write only when enabled for this course if (cc.isEfficencyStatementEnabled()) { Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity(); List<AssessmentNodeData> assessmentNodeList = AssessmentHelper.getAssessmentNodeDataList(userCourseEnv, true, true); - List<Map<String,Object>> assessmentNodes = AssessmentHelper.assessmentNodeDataListToMap(assessmentNodeList); - - EfficiencyStatement efficiencyStatement = new EfficiencyStatement(); - efficiencyStatement.setAssessmentNodes(assessmentNodes); - efficiencyStatement.setCourseTitle(userCourseEnv.getCourseEnvironment().getCourseTitle()); - efficiencyStatement.setCourseRepoEntryKey(repoEntry.getKey()); - String userInfos = userManager.getUserDisplayName(identity); - efficiencyStatement.setDisplayableUserInfo(userInfos); - efficiencyStatement.setLastUpdated(System.currentTimeMillis()); - - UserEfficiencyStatementImpl efficiencyProperty = getUserEfficiencyStatementFull(repoEntry, identity); - if (assessmentNodes != null) { - if (efficiencyProperty == null) { - // create new - efficiencyProperty = new UserEfficiencyStatementImpl(); - efficiencyProperty.setIdentity(identity); + updateUserEfficiencyStatement(identity, userCourseEnv.getCourseEnvironment(), assessmentNodeList, repoEntry); + } + } + + public void updateUserEfficiencyStatement(Identity assessedIdentity, final CourseEnvironment courseEnv, List<AssessmentNodeData> assessmentNodeList, final RepositoryEntry repoEntry) { + List<Map<String,Object>> assessmentNodes = AssessmentHelper.assessmentNodeDataListToMap(assessmentNodeList); + + EfficiencyStatement efficiencyStatement = new EfficiencyStatement(); + efficiencyStatement.setAssessmentNodes(assessmentNodes); + efficiencyStatement.setCourseTitle(courseEnv.getCourseTitle()); + efficiencyStatement.setCourseRepoEntryKey(repoEntry.getKey()); + String userInfos = userManager.getUserDisplayName(assessedIdentity); + efficiencyStatement.setDisplayableUserInfo(userInfos); + efficiencyStatement.setLastUpdated(System.currentTimeMillis()); + + boolean debug = log.isDebug(); + UserEfficiencyStatementImpl efficiencyProperty = getUserEfficiencyStatementFull(repoEntry, assessedIdentity); + if (assessmentNodes != null) { + if (efficiencyProperty == null) { + // create new + efficiencyProperty = new UserEfficiencyStatementImpl(); + efficiencyProperty.setIdentity(assessedIdentity); + efficiencyProperty.setCourseRepoKey(repoEntry.getKey()); + if(repoEntry != null) { + efficiencyProperty.setResource(repoEntry.getOlatResource()); efficiencyProperty.setCourseRepoKey(repoEntry.getKey()); - if(repoEntry != null) { - efficiencyProperty.setResource(repoEntry.getOlatResource()); - efficiencyProperty.setCourseRepoKey(repoEntry.getKey()); - } - - fillEfficiencyStatement(efficiencyStatement, efficiencyProperty); - dbInstance.getCurrentEntityManager().persist(efficiencyProperty); - if (isLogDebugEnabled()) { - logDebug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntry.getKey()); - } - } else { - // update existing - if (isLogDebugEnabled()) { - logDebug("updating efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntry.getKey()); - } - fillEfficiencyStatement(efficiencyStatement, efficiencyProperty); - dbInstance.getCurrentEntityManager().merge(efficiencyProperty); } + + fillEfficiencyStatement(efficiencyStatement, efficiencyProperty); + dbInstance.getCurrentEntityManager().persist(efficiencyProperty); + if (debug) { + log.debug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + assessedIdentity.getName() + " repoEntry::" + repoEntry.getKey()); + } } else { - if (efficiencyProperty != null) { - // remove existing since now empty efficiency statements - if (isLogDebugEnabled()) { - logDebug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntry.getKey() + " since empty"); - } - dbInstance.getCurrentEntityManager().remove(efficiencyProperty); + // update existing + if (debug) { + log.debug("updating efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + assessedIdentity.getName() + " repoEntry::" + repoEntry.getKey()); + } + fillEfficiencyStatement(efficiencyStatement, efficiencyProperty); + dbInstance.getCurrentEntityManager().merge(efficiencyProperty); + } + } else { + if (efficiencyProperty != null) { + // remove existing since now empty efficiency statements + if (debug) { + log.debug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + assessedIdentity.getName() + " repoEntry::" + repoEntry.getKey() + " since empty"); } - // else nothing to create and nothing to delete - } - - // send modified event to everybody - AssessmentChangedEvent ace = new AssessmentChangedEvent(AssessmentChangedEvent.TYPE_EFFICIENCY_STATEMENT_CHANGED, identity); - CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(ace, courseOres); - } + dbInstance.getCurrentEntityManager().remove(efficiencyProperty); + } + // else nothing to create and nothing to delete + } + + // send modified event to everybody + AssessmentChangedEvent ace = new AssessmentChangedEvent(AssessmentChangedEvent.TYPE_EFFICIENCY_STATEMENT_CHANGED, assessedIdentity); + OLATResourceable courseOres = OresHelper.createOLATResourceableInstance(CourseModule.class, courseEnv.getCourseResourceableId()); + CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(ace, courseOres); } public void fillEfficiencyStatement(EfficiencyStatement efficiencyStatement, UserEfficiencyStatementImpl efficiencyProperty) { @@ -270,7 +278,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData Boolean passed = (Boolean)nodeData.get(AssessmentHelper.KEY_PASSED); Integer attempts = (Integer)nodeData.get(AssessmentHelper.KEY_ATTEMPTS); String attemptsStr = attempts==null ? null : String.valueOf(attempts.intValue()); - logInfo("title: " + title + " score: " + score + " passed: " + passed + " attempts: " + attemptsStr); + log.info("title: " + title + " score: " + score + " passed: " + passed + " attempts: " + attemptsStr); } } } @@ -324,7 +332,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } return statement.get(0); } catch (Exception e) { - logError("Cannot retrieve efficiency statement: " + courseRepoEntry.getKey() + " from " + identity, e); + log.error("Cannot retrieve efficiency statement: " + courseRepoEntry.getKey() + " from " + identity, e); return null; } } @@ -346,7 +354,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } return statement.get(0); } catch (Exception e) { - logError("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e); + log.error("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e); return null; } } @@ -383,7 +391,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } return statement.get(0); } catch (Exception e) { - logError("Cannot retrieve efficiency statement: " + courseRepo.getKey() + " from " + identity, e); + log.error("Cannot retrieve efficiency statement: " + courseRepo.getKey() + " from " + identity, e); return null; } } @@ -404,7 +412,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } return statement.get(0); } catch (Exception e) { - logError("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e); + log.error("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e); return null; } } @@ -424,7 +432,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } return (EfficiencyStatement)xstream.fromXML(statement.get(0).getStatementXml()); } catch (Exception e) { - logError("Cannot retrieve efficiency statement: " + key, e); + log.error("Cannot retrieve efficiency statement: " + key, e); return null; } } @@ -549,7 +557,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } } catch (Exception e) { - logError("findEfficiencyStatements: " + identity, e); + log.error("findEfficiencyStatements: " + identity, e); } return efficiencyStatements; } @@ -566,7 +574,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData .setParameter("identityKey", identity.getKey()) .getResultList(); } catch (Exception e) { - logError("findEfficiencyStatements: " + identity, e); + log.error("findEfficiencyStatements: " + identity, e); return Collections.emptyList(); } } @@ -601,7 +609,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData .setParameter("repoKey", courseRepoEntryKey) .getResultList(); } catch (Exception e) { - logError("findIdentitiesWithEfficiencyStatements: " + courseRepoEntryKey, e); + log.error("findIdentitiesWithEfficiencyStatements: " + courseRepoEntryKey, e); return Collections.emptyList(); } } @@ -625,7 +633,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData dbInstance.deleteObject(statement); } } catch (Exception e) { - logError("deleteEfficiencyStatementsFromCourse: " + courseRepoEntryKey, e); + log.error("deleteEfficiencyStatementsFromCourse: " + courseRepoEntryKey, e); } } @@ -661,7 +669,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData public void updateEfficiencyStatements(final RepositoryEntry courseEntry, List<Identity> identities) { if (identities.size() > 0) { final ICourse course = CourseFactory.loadCourse(courseEntry); - logAudit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU"); + log.audit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU"); // preload cache to speed up things long start = System.currentTimeMillis(); @@ -674,7 +682,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData public void execute() { // create temporary user course env UserCourseEnvironment uce = AssessmentHelper.createAndInitUserCourseEnvironment(identity, course); - updateUserEfficiencyStatement(uce, courseEntry, course); + updateUserEfficiencyStatement(uce, courseEntry); } }); if (Thread.interrupted()) { @@ -682,9 +690,9 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } } - if (isLogDebugEnabled()) { + if (log.isDebug()) { long end = System.currentTimeMillis(); - logDebug("Updated efficiency statements for course::" + course.getResourceableId() + log.debug("Updated efficiency statements for course::" + course.getResourceableId() + "ms; Updating statements: " + (end-start) + "ms; Users: " + identities.size()); } } @@ -704,7 +712,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData for (Iterator<EfficiencyStatement> iter = efficiencyStatements.iterator(); iter.hasNext();) { deleteEfficiencyStatement(identity, iter.next()); } - logDebug("All efficiency statements deleted for identity=" + identity); + log.debug("All efficiency statements deleted for identity=" + identity); } } diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java new file mode 100644 index 00000000000..406c037be9d --- /dev/null +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java @@ -0,0 +1,36 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.course.assessment.ui.tool; + +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.generic.dtabs.Activateable2; +import org.olat.core.id.Identity; + +/** + * + * Initial date: 04.12.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public interface AssessedIdentityController extends Controller, Activateable2 { + + public Identity getAssessedIdentity(); + +} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java new file mode 100644 index 00000000000..835881965ad --- /dev/null +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java @@ -0,0 +1,413 @@ +/** +* OLAT - Online Learning and Training<br> +* http://www.olat.org +* <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 +* <p> +* http://www.apache.org/licenses/LICENSE-2.0 +* <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> +* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> +* University of Zurich, Switzerland. +* <hr> +* <a href="http://www.openolat.org"> +* OpenOLAT - Online Learning and Training</a><br> +* This file has been modified by the OpenOLAT community. Changes are licensed +* under the Apache 2.0 license as the original file. +*/ + +package org.olat.course.assessment.ui.tool; + +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.IntegerElement; +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.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.util.StringHelper; +import org.olat.core.util.Util; +import org.olat.course.assessment.AssessmentHelper; +import org.olat.course.assessment.AssessmentMainController; +import org.olat.course.nodes.AssessableCourseNode; +import org.olat.course.run.scoring.ScoreEvaluation; +import org.olat.course.run.userview.UserCourseEnvironment; + + +/** + * Initial Date: Jun 24, 2004 + * + * @author gnaegi + */ +public class AssessmentForm extends FormBasicController { + + private TextElement score; + private IntegerElement attempts; + private StaticTextElement cutVal; + private SingleSelection passed; + private TextElement userComment, coachComment; + private FormLink saveAndCloseLink; + + private final boolean saveAndClose; + + private final boolean hasScore, hasPassed, hasComment, hasAttempts; + private Float min, max, cut; + + private final UserCourseEnvironment assessedUserCourseEnv; + private final AssessableCourseNode assessableCourseNode; + + private Integer attemptsValue; + private Float scoreValue; + private String userCommentValue; + private String coachCommentValue; + + /** + * Constructor for an assessment detail form. The form will be configured according + * to the assessable course node parameters + * @param name The form name + * @param assessableCourseNode The course node + * @param assessedIdentityWrapper The wrapped identity + * @param trans The package translator + */ + public AssessmentForm(UserRequest ureq, WindowControl wControl, AssessableCourseNode assessableCourseNode, + UserCourseEnvironment assessedUserCourseEnv, boolean saveAndClose) { + super(ureq, wControl); + setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator())); + + this.saveAndClose = saveAndClose; + + hasAttempts = assessableCourseNode.hasAttemptsConfigured(); + hasScore = assessableCourseNode.hasScoreConfigured(); + hasPassed = assessableCourseNode.hasPassedConfigured(); + hasComment = assessableCourseNode.hasCommentConfigured(); + + this.assessedUserCourseEnv = assessedUserCourseEnv; + this.assessableCourseNode = assessableCourseNode; + + initForm(ureq); + } + + public boolean isAttemptsDirty() { + return hasAttempts && attemptsValue.intValue() != attempts.getIntValue(); + } + + public int getAttempts() { + return attempts.getIntValue(); + } + + + public Float getCut() { + return cut; + } + + public StaticTextElement getCutVal() { + return cutVal; + } + + public boolean isHasAttempts() { + return hasAttempts; + } + + public boolean isHasComment() { + return hasComment; + } + + public boolean isHasPassed() { + return hasPassed; + } + + public boolean isHasScore() { + return hasScore; + } + + public SingleSelection getPassed() { + return passed; + } + + public boolean isScoreDirty() { + if (!hasScore) return false; + if (scoreValue == null) return !score.getValue().equals(""); + return parseFloat(score) != scoreValue.floatValue(); + } + + public Float getScore() { + return parseFloat(score); + } + + public boolean isUserCommentDirty () { + return hasComment && !userComment.getValue().equals(userCommentValue); + } + public TextElement getUserComment() { + return userComment; + } + + public boolean isCoachCommentDirty () { + return !coachComment.getValue().equals(coachCommentValue); + } + + public TextElement getCoachComment() { + return coachComment; + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(saveAndCloseLink == source) { + if(validateFormLogic(ureq)) { + doUpdateAssessmentData(); + fireEvent(ureq, Event.DONE_EVENT); + } + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void formOK(UserRequest ureq) { + doUpdateAssessmentData(); + if(saveAndClose) { + fireEvent(ureq, Event.CHANGED_EVENT); + } else { + fireEvent(ureq, Event.DONE_EVENT); + } + } + + @Override + protected void formCancelled (UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + if (hasScore) { + try { + if(parseFloat(score) == null) { + score.setErrorKey("form.error.wrongFloat", null); + return false; + } + } catch (NumberFormatException e) { + score.setErrorKey("form.error.wrongFloat", null); + return false; + } + + Float fscore = parseFloat(score); + if ((min != null && fscore < min.floatValue()) + || fscore < AssessmentHelper.MIN_SCORE_SUPPORTED) { + score.setErrorKey("form.error.scoreOutOfRange", null); + return false; + } + if ((max != null && fscore > max.floatValue()) + || fscore > AssessmentHelper.MAX_SCORE_SUPPORTED) { + score.setErrorKey("form.error.scoreOutOfRange", null); + return false; + } + } + return true; + } + + private Float parseFloat(TextElement textEl) throws NumberFormatException { + String scoreStr = textEl.getValue(); + if(!StringHelper.containsNonWhitespace(scoreStr)) { + return null; + } + int index = scoreStr.indexOf(','); + if(index >= 0) { + scoreStr = scoreStr.replace(',', '.'); + return Float.parseFloat(scoreStr); + } + return Float.parseFloat(scoreStr); + } + + protected void doUpdateAssessmentData() { + ScoreEvaluation scoreEval = null; + Float newScore = null; + Boolean newPassed = null; + + if (isHasAttempts() && isAttemptsDirty()) { + assessableCourseNode.updateUserAttempts(new Integer(getAttempts()), assessedUserCourseEnv, getIdentity()); + } + + if (isHasScore() && isScoreDirty()) { + newScore = getScore(); + // Update properties in db later... see + // courseNode.updateUserSocreAndPassed... + } + + if (isHasPassed()) { + if (getCut() != null && getScore() != null) { + newPassed = getScore() >= getCut().floatValue() + ? Boolean.TRUE : Boolean.FALSE; + } else { + //"passed" info was changed or not + String selectedKeyString = getPassed().getSelectedKey(); + if("true".equalsIgnoreCase(selectedKeyString) || "false".equalsIgnoreCase(selectedKeyString)) { + newPassed = Boolean.valueOf(selectedKeyString); + } + else { + // "undefined" was choosen + newPassed = null; + } + } + } + // Update score,passed properties in db + scoreEval = new ScoreEvaluation(newScore, newPassed); + assessableCourseNode.updateUserScoreEvaluation(scoreEval, assessedUserCourseEnv, getIdentity(), false); + + if (isHasComment() && isUserCommentDirty()) { + String newComment = getUserComment().getValue(); + // Update properties in db + assessableCourseNode.updateUserUserComment(newComment, assessedUserCourseEnv, getIdentity()); + } + + if (isCoachCommentDirty()) { + String newCoachComment = getCoachComment().getValue(); + // Update properties in db + assessableCourseNode.updateUserCoachComment(newCoachComment, assessedUserCourseEnv); + } + } + + public void reloadData() { + ScoreEvaluation scoreEval = assessedUserCourseEnv.getScoreAccounting().evalCourseNode(assessableCourseNode); + if (scoreEval == null) scoreEval = new ScoreEvaluation(null, null); + + if (hasAttempts) { + attemptsValue = assessableCourseNode.getUserAttempts(assessedUserCourseEnv); + attempts.setIntValue(attemptsValue == null ? 0 : attemptsValue.intValue()); + } + + if (hasScore) { + scoreValue = scoreEval.getScore(); + if (scoreValue != null) { + score.setValue(AssessmentHelper.getRoundedScore(scoreValue)); + } + } + + if (hasPassed) { + Boolean passedValue = scoreEval.getPassed(); + passed.select(passedValue == null ? "undefined" :passedValue.toString(), true); + passed.setEnabled(cut == null); + } + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + setFormTitle("form.title", null); + formLayout.setElementCssClass("o_sel_assessment_form"); + + ScoreEvaluation scoreEval = assessedUserCourseEnv.getScoreAccounting().evalCourseNode(assessableCourseNode); + if (scoreEval == null) { + scoreEval = ScoreEvaluation.EMPTY_EVALUATION; + } + + if (hasAttempts) { + attemptsValue = assessableCourseNode.getUserAttempts(assessedUserCourseEnv); + attempts = uifactory.addIntegerElement("attempts", "form.attempts", (attemptsValue == null ? 0 : attemptsValue.intValue()), formLayout); + attempts.setDisplaySize(3); + attempts.setMinValueCheck(0, null); + } + + if (hasScore) { + min = assessableCourseNode.getMinScoreConfiguration(); + max = assessableCourseNode.getMaxScoreConfiguration(); + if (hasPassed) { + cut = assessableCourseNode.getCutValueConfiguration(); + } + + String minStr = AssessmentHelper.getRoundedScore(min); + String maxStr = AssessmentHelper.getRoundedScore(max); + uifactory.addStaticTextElement("minval", "form.min", ((min == null) ? translate("form.valueUndefined") : minStr), formLayout); + uifactory.addStaticTextElement("maxval", "form.max", ((max == null) ? translate("form.valueUndefined") : maxStr), formLayout); + + // Use init variables from wrapper, already loaded from db + scoreValue = scoreEval.getScore(); + score = uifactory.addTextElement("score","form.score" , 10, "", formLayout); + score.setDisplaySize(4); + score.setElementCssClass("o_sel_assessment_form_score"); + score.setExampleKey("form.score.rounded", null); + if (scoreValue != null) { + score.setValue(AssessmentHelper.getRoundedScore(scoreValue)); + } + // assessment overview with max score + score.setRegexMatchCheck("(\\d+)||(\\d+\\.\\d{1,3})||(\\d+\\,\\d{1,3})", "form.error.wrongFloat"); + } + + if (hasPassed) { + if (cut != null) { + // Display cut value if defined + cutVal = uifactory.addStaticTextElement( + "cutval","form.cut" , + ((cut == null) ? translate("form.valueUndefined") : AssessmentHelper.getRoundedScore(cut)), + formLayout + ); + } + + String[] trueFalseKeys = new String[] { "undefined", "true", "false" }; + String[] passedNotPassedValues = new String[] { + translate("form.passed.undefined"), + translate("form.passed.true"), + translate("form.passed.false") + }; + + //passed = new StaticSingleSelectionElement("form.passed", trueFalseKeys, passedNotPassedValues); + passed = uifactory.addRadiosVertical("passed", "form.passed", formLayout, trueFalseKeys, passedNotPassedValues); + passed.setElementCssClass("o_sel_assessment_form_passed"); + + Boolean passedValue = scoreEval.getPassed(); + passed.select(passedValue == null ? "undefined" :passedValue.toString(), true); + // When cut value is defined, no manual passed possible + passed.setEnabled(cut == null); + } + + if (hasComment) { + // Use init variables from db, not available from wrapper + userCommentValue = assessableCourseNode.getUserUserComment(assessedUserCourseEnv); + if (userCommentValue == null) { + userCommentValue = ""; + } + userComment = uifactory.addTextAreaElement("usercomment", "form.usercomment", 2500, 5, 40, true, userCommentValue, formLayout); + } + + coachCommentValue = assessableCourseNode.getUserCoachComment(assessedUserCourseEnv); + if (coachCommentValue == null) { + coachCommentValue = ""; + } + coachComment = uifactory.addTextAreaElement("coachcomment", "form.coachcomment", 2500, 5, 40, true, coachCommentValue, formLayout); + + //why does the TextElement not use its default error key??? + //userComment could be null for course elements of type Assessment (MSCourseNode) + if(userComment!=null) { + userComment.setNotLongerThanCheck(2500, "input.toolong"); + } + if(coachComment!=null) { + coachComment.setNotLongerThanCheck(2500, "input.toolong"); + } + + FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator()); + formLayout.add(buttonGroupLayout); + + uifactory.addFormSubmitButton("save", buttonGroupLayout); + if(saveAndClose) { + saveAndCloseLink = uifactory.addFormLink("save.close", buttonGroupLayout, Link.BUTTON); + saveAndCloseLink.setElementCssClass("o_sel_assessment_form_save_and_close"); + } + uifactory.addFormCancelButton("cancel", buttonGroupLayout, ureq, getWindowControl()); + } + + @Override + protected void doDispose() { + // + } +} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java deleted file mode 100644 index 713dba09fd7..00000000000 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java +++ /dev/null @@ -1,399 +0,0 @@ -/** - * <a href="http://www.openolat.org"> - * OpenOLAT - Online Learning and Training</a><br> - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); <br> - * you may not use this file except in compliance with the License.<br> - * You may obtain a copy of the License at the - * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> - * <p> - * Unless required by applicable law or agreed to in writing,<br> - * software distributed under the License is distributed on an "AS IS" BASIS, <br> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> - * See the License for the specific language governing permissions and <br> - * limitations under the License. - * <p> - * Initial code contributed and copyrighted by<br> - * frentix GmbH, http://www.frentix.com - * <p> - */ -package org.olat.course.assessment.ui.tool; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.olat.basesecurity.BaseSecurity; -import org.olat.basesecurity.BaseSecurityModule; -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -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.FlexiTableElement; -import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter; -import org.olat.core.gui.components.form.flexible.elements.FormLink; -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.elements.table.DefaultFlexiColumnModel; -import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; -import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; -import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableSearchEvent; -import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.components.stack.TooledStackedPanel; -import org.olat.core.gui.components.stack.TooledStackedPanel.Align; -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.wizard.Step; -import org.olat.core.gui.control.generic.wizard.StepRunnerCallback; -import org.olat.core.gui.control.generic.wizard.StepsMainRunController; -import org.olat.core.gui.control.generic.wizard.StepsRunContext; -import org.olat.core.id.Identity; -import org.olat.core.util.StringHelper; -import org.olat.core.util.Util; -import org.olat.course.CourseFactory; -import org.olat.course.ICourse; -import org.olat.course.assessment.AssessedIdentityWrapper; -import org.olat.course.assessment.AssessmentMainController; -import org.olat.course.assessment.AssessmentToolManager; -import org.olat.course.assessment.bulk.PassedCellRenderer; -import org.olat.course.assessment.model.SearchAssessedIdentityParams; -import org.olat.course.assessment.ui.tool.AssessmentIdentitiesCourseTableModel.IdentityCourseCols; -import org.olat.course.certificate.CertificateLight; -import org.olat.course.certificate.CertificateTemplate; -import org.olat.course.certificate.CertificatesManager; -import org.olat.course.certificate.model.CertificateInfos; -import org.olat.course.certificate.ui.Certificates_1_SelectionStep; -import org.olat.course.certificate.ui.DownloadCertificateCellRenderer; -import org.olat.course.config.CourseConfig; -import org.olat.group.BusinessGroup; -import org.olat.modules.assessment.model.AssessmentEntryStatus; -import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; -import org.olat.modules.coach.CoachingService; -import org.olat.modules.coach.model.EfficiencyStatementEntry; -import org.olat.repository.RepositoryEntry; -import org.olat.user.UserManager; -import org.olat.user.propertyhandlers.UserPropertyHandler; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * - * Initial date: 06.10.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class AssessmentIdentitiesCourseController extends FormBasicController { - - private final RepositoryEntry courseEntry; - private final boolean isAdministrativeUser; - private List<UserPropertyHandler> userPropertyHandlers; - private final AssessmentToolSecurityCallback assessmentCallback; - - private Link nextLink, previousLink; - private FormLink generateCertificateButton; - private FlexiTableElement tableEl; - private TooledStackedPanel stackPanel; - private AssessmentIdentitiesCourseTableModel usersTableModel; - - private StepsMainRunController wizardCtrl; - private AssessmentIdentityCourseController currentIdentityCtrl; - - @Autowired - private UserManager userManager; - @Autowired - private BaseSecurity securityManager; - @Autowired - private CoachingService coachingService; - @Autowired - private BaseSecurityModule securityModule; - @Autowired - private CertificatesManager certificatesManager; - @Autowired - private AssessmentToolManager assessmentToolManager; - - public AssessmentIdentitiesCourseController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, - RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) { - super(ureq, wControl, "identity_course"); - setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator())); - setTranslator(userManager.getPropertyHandlerTranslator(getTranslator())); - - this.stackPanel = stackPanel; - this.courseEntry = courseEntry; - this.assessmentCallback = assessmentCallback; - - isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles()); - userPropertyHandlers = userManager.getUserPropertyHandlersFor(AssessmentToolConstants.usageIdentifyer, isAdministrativeUser); - - initForm(ureq); - loadModel(null, null, null); - } - - @Override - protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - //add the table - FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); - if(isAdministrativeUser) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.username, "select")); - } - - int colIndex = AssessmentToolConstants.USER_PROPS_OFFSET; - for (int i = 0; i < userPropertyHandlers.size(); i++) { - UserPropertyHandler userPropertyHandler = userPropertyHandlers.get(i); - boolean visible = UserManager.getInstance().isMandatoryUserProperty(AssessmentToolConstants.usageIdentifyer , userPropertyHandler); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex, "select", - true, "userProp-" + colIndex)); - colIndex++; - } - - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.passed, new PassedCellRenderer())); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.score)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.lastScoreUpdate)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.certificate, new DownloadCertificateCellRenderer())); - - usersTableModel = new AssessmentIdentitiesCourseTableModel(columnsModel); - tableEl = uifactory.addTableElement(getWindowControl(), "table", usersTableModel, 20, false, getTranslator(), formLayout); - tableEl.setExportEnabled(true); - tableEl.setSearchEnabled(new AssessedIdentityListProvider(getIdentity(), courseEntry, null, null, assessmentCallback), ureq.getUserSession()); - - List<FlexiTableFilter> filters = new ArrayList<>(); - filters.add(new FlexiTableFilter(translate("filter.passed"), "passed")); - filters.add(new FlexiTableFilter(translate("filter.failed"), "failed")); - filters.add(new FlexiTableFilter(translate("filter.inProgress"), "inProgress")); - filters.add(new FlexiTableFilter(translate("filter.inReview"), "inReview")); - filters.add(new FlexiTableFilter(translate("filter.done"), "done")); - tableEl.setFilters("", filters); - - ICourse course = CourseFactory.loadCourse(courseEntry); - if(assessmentCallback.canAssessBusinessGoupMembers()) { - List<BusinessGroup> coachedGroups = null; - if(assessmentCallback.isAdmin()) { - coachedGroups = course.getCourseEnvironment().getCourseGroupManager().getAllBusinessGroups(); - } else { - coachedGroups = assessmentCallback.getCoachedGroups(); - } - - if(coachedGroups.size() > 0) { - List<FlexiTableFilter> groupFilters = new ArrayList<>(); - for(BusinessGroup coachedGroup:coachedGroups) { - groupFilters.add(new FlexiTableFilter(coachedGroup.getName(), coachedGroup.getKey().toString(), "o_icon o_icon_group")); - } - - tableEl.setExtendedFilterButton(translate("filter.groups"), groupFilters); - } - } - - CourseConfig courseConfig = course.getCourseConfig(); - if(courseConfig.isManualCertificationEnabled()) { - generateCertificateButton = uifactory.addFormLink("generate.certificate", formLayout, Link.BUTTON); - } - } - - public List<EfficiencyStatementEntry> loadModel(String searchStr, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) { - - SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(courseEntry, null, null, assessmentCallback); - - List<AssessmentEntryStatus> assessmentStatus = null; - if(filters != null && filters.size() > 0) { - assessmentStatus = new ArrayList<>(filters.size()); - for(FlexiTableFilter filter:filters) { - if("passed".equals(filter.getFilter())) { - params.setPassed(true); - } else if("failed".equals(filter.getFilter())) { - params.setFailed(true); - } else if(AssessmentEntryStatus.isValueOf(filter.getFilter())){ - assessmentStatus.add(AssessmentEntryStatus.valueOf(filter.getFilter())); - } - } - } - params.setAssessmentStatus(assessmentStatus); - - List<Long> businessGroupKeys = null; - if(extendedFilters != null && extendedFilters.size() > 0) { - businessGroupKeys = new ArrayList<>(extendedFilters.size()); - for(FlexiTableFilter extendedFilter:extendedFilters) { - if(StringHelper.isLong(extendedFilter.getFilter())) { - businessGroupKeys.add(Long.parseLong(extendedFilter.getFilter())); - } - } - } - params.setBusinessGroupKeys(businessGroupKeys); - params.setSearchString(searchStr); - - List<Identity> assessedIdentities = assessmentToolManager.getAssessedIdentities(getIdentity(), params); - List<EfficiencyStatementEntry> entries = coachingService.getCourse(getIdentity(), courseEntry); - Map<Long,EfficiencyStatementEntry> identityKeyToStatementMap = entries.stream() - .collect(Collectors.toMap(EfficiencyStatementEntry::getStudentKey, Function.identity())); - - List<AssessedIdentityCourseRow> rows = new ArrayList<>(assessedIdentities.size()); - for(Identity assessedIdentity: assessedIdentities) { - EfficiencyStatementEntry statement = identityKeyToStatementMap.get(assessedIdentity.getKey()); - rows.add(new AssessedIdentityCourseRow(assessedIdentity, statement, userPropertyHandlers, getLocale())); - } - - List<CertificateLight> certificates = certificatesManager.getLastCertificates(courseEntry.getOlatResource()); - ConcurrentMap<Long, CertificateLight> certificateMap = new ConcurrentHashMap<>(); - for(CertificateLight certificate:certificates) { - certificateMap.put(certificate.getIdentityKey(), certificate); - } - - usersTableModel.setCertificateMap(certificateMap); - usersTableModel.setObjects(rows); - tableEl.reloadData(); - return entries; - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void formOK(UserRequest ureq) { - // - } - - @Override - public void event(UserRequest ureq, Component source, Event event) { - if(previousLink == source) { - doPrevious(ureq); - } else if(nextLink == source) { - doNext(ureq); - } - super.event(ureq, source, event); - } - - - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(wizardCtrl == source) { - if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { - getWindowControl().pop(); - removeAsListenerAndDispose(wizardCtrl); - wizardCtrl = null; - } - } - super.event(ureq, source, event); - } - - @Override - protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { - if(tableEl == source) { - if(event instanceof SelectionEvent) { - SelectionEvent se = (SelectionEvent)event; - String cmd = se.getCommand(); - AssessedIdentityCourseRow row = usersTableModel.getObject(se.getIndex()); - if("select".equals(cmd)) { - doSelect(ureq, row); - } - } else if(event instanceof FlexiTableSearchEvent) { - FlexiTableSearchEvent ftse = (FlexiTableSearchEvent)event; - loadModel(ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters()); - } - } else if(generateCertificateButton == source) { - doGenerateCertificates(ureq); - } - - super.formInnerEvent(ureq, source, event); - } - - private void doGenerateCertificates(UserRequest ureq) { - ICourse course = CourseFactory.loadCourse(courseEntry); - - List<AssessedIdentityWrapper> datas = new ArrayList<>(); - Certificates_1_SelectionStep start = new Certificates_1_SelectionStep(ureq, courseEntry, datas, course.hasAssessableNodes()); - StepRunnerCallback finish = new StepRunnerCallback() { - @Override - public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) { - @SuppressWarnings("unchecked") - List<CertificateInfos> assessedIdentitiesInfos = (List<CertificateInfos>)runContext.get("infos"); - if(assessedIdentitiesInfos != null && assessedIdentitiesInfos.size() > 0) { - doGenerateCertificates(assessedIdentitiesInfos); - return StepsMainRunController.DONE_MODIFIED; - } - return StepsMainRunController.DONE_UNCHANGED; - } - }; - - wizardCtrl = new StepsMainRunController(ureq, getWindowControl(), start, finish, null, - translate("certificates.wizard.title"), "o_sel_certificates_wizard"); - listenTo(wizardCtrl); - getWindowControl().pushAsModalDialog(wizardCtrl.getInitialComponent()); - } - - private void doGenerateCertificates(List<CertificateInfos> assessedIdentitiesInfos) { - ICourse course = CourseFactory.loadCourse(courseEntry); - Long templateKey = course.getCourseConfig().getCertificateTemplate(); - CertificateTemplate template = null; - if(templateKey != null) { - template = certificatesManager.getTemplateById(templateKey); - } - - certificatesManager.generateCertificates(assessedIdentitiesInfos, courseEntry, template, true); - } - - private void doNext(UserRequest ureq) { - stackPanel.popController(currentIdentityCtrl); - - Identity currentIdentity = currentIdentityCtrl.getAssessedIdentity(); - int index = getIndexOf(currentIdentity); - if(index >= 0) { - int nextIndex = index + 1;//next - if(nextIndex >= 0 && nextIndex < usersTableModel.getRowCount()) { - doSelect(ureq, usersTableModel.getObject(nextIndex)); - } else if(usersTableModel.getRowCount() > 0) { - doSelect(ureq, usersTableModel.getObject(0)); - } - } - } - - private void doPrevious(UserRequest ureq) { - stackPanel.popController(currentIdentityCtrl); - - Identity currentIdentity = currentIdentityCtrl.getAssessedIdentity(); - int index = getIndexOf(currentIdentity); - if(index >= 0) { - int previousIndex = index - 1;//next - if(previousIndex >= 0 && previousIndex < usersTableModel.getRowCount()) { - doSelect(ureq, usersTableModel.getObject(previousIndex)); - } else if(usersTableModel.getRowCount() > 0) { - doSelect(ureq, usersTableModel.getObject(usersTableModel.getRowCount() - 1)); - } - } - } - - private int getIndexOf(Identity identity) { - int index = -1; - for(int i=usersTableModel.getRowCount(); i-->0; ) { - Long rowIdentityKey = usersTableModel.getObject(i).getIdentityKey(); - if(rowIdentityKey.equals(identity.getKey())) { - return i; - } - } - return index; - } - - private void doSelect(UserRequest ureq, AssessedIdentityCourseRow row) { - removeAsListenerAndDispose(currentIdentityCtrl); - - Identity assessedIdentity = securityManager.loadIdentityByKey(row.getIdentityKey()); - String fullName = userManager.getUserDisplayName(assessedIdentity); - - currentIdentityCtrl = new AssessmentIdentityCourseController(ureq, getWindowControl(), stackPanel, courseEntry, assessedIdentity); - listenTo(currentIdentityCtrl); - stackPanel.pushController(fullName, currentIdentityCtrl); - - previousLink = LinkFactory.createToolLink("previouselement","", this, "o_icon_previous_toolbar"); - previousLink.setTitle(translate("command.previous")); - stackPanel.addTool(previousLink, Align.rightEdge, false, "o_tool_previous"); - nextLink = LinkFactory.createToolLink("nextelement","", this, "o_icon_next_toolbar"); - nextLink.setTitle(translate("command.next")); - stackPanel.addTool(nextLink, Align.rightEdge, false, "o_tool_next"); - } -} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java deleted file mode 100644 index aab936bd5ee..00000000000 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * <a href="http://www.openolat.org"> - * OpenOLAT - Online Learning and Training</a><br> - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); <br> - * you may not use this file except in compliance with the License.<br> - * You may obtain a copy of the License at the - * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> - * <p> - * Unless required by applicable law or agreed to in writing,<br> - * software distributed under the License is distributed on an "AS IS" BASIS, <br> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> - * See the License for the specific language governing permissions and <br> - * limitations under the License. - * <p> - * Initial code contributed and copyrighted by<br> - * frentix GmbH, http://www.frentix.com - * <p> - */ -package org.olat.course.assessment.ui.tool; - -import java.util.List; -import java.util.concurrent.ConcurrentMap; - -import org.olat.core.commons.persistence.SortKey; -import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel; -import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef; -import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; -import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel; -import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate; -import org.olat.course.certificate.CertificateLight; - -/** - * - * Initial date: 07.10.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class AssessmentIdentitiesCourseTableModel extends DefaultFlexiTableDataModel<AssessedIdentityCourseRow> - implements SortableFlexiTableDataModel<AssessedIdentityCourseRow> { - - private ConcurrentMap<Long, CertificateLight> certificateMap; - - public AssessmentIdentitiesCourseTableModel(FlexiTableColumnModel columnModel) { - super(columnModel); - } - - public void setCertificateMap(ConcurrentMap<Long, CertificateLight> certificateMap) { - this.certificateMap = certificateMap; - } - - @Override - public void sort(SortKey orderBy) { - SortableFlexiTableModelDelegate<AssessedIdentityCourseRow> sorter - = new SortableFlexiTableModelDelegate<>(orderBy, this, null); - List<AssessedIdentityCourseRow> views = sorter.sort(); - super.setObjects(views); - } - - @Override - public Object getValueAt(int row, int col) { - AssessedIdentityCourseRow entry = getObject(row); - return getValueAt(entry, col); - } - - @Override - public Object getValueAt(AssessedIdentityCourseRow row, int col) { - if(col >= 0 && col < IdentityCourseCols.values().length) { - - switch(IdentityCourseCols.values()[col]) { - case username: return row.getIdentityName(); - case certificate: return certificateMap.get(row.getIdentityKey()); - case score: return row.getScore(); - case passed: return row.getPassed(); - case lastScoreUpdate: return row.getLastModified(); - } - } - int propPos = col - AssessmentToolConstants.USER_PROPS_OFFSET; - return row.getIdentityProp(propPos); - } - - @Override - public DefaultFlexiTableDataModel<AssessedIdentityCourseRow> createCopyWithEmptyList() { - return new AssessmentIdentitiesCourseTableModel(getTableColumnModel()); - } - - public enum IdentityCourseCols implements FlexiSortableColumnDef { - username("table.header.name"), - passed("table.header.passed"), - certificate("table.header.certificate"), - score("table.header.score"), - lastScoreUpdate("table.header.lastScoreDate"); - - private final String i18nKey; - - private IdentityCourseCols(String i18nKey) { - this.i18nKey = i18nKey; - } - - @Override - public String i18nHeaderKey() { - return i18nKey; - } - - @Override - public boolean sortable() { - return true; - } - - @Override - public String sortKey() { - return name(); - } - } -} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java index 0d56bd00e26..f60d5a0ca92 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java @@ -60,16 +60,18 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im private final MenuTree menuTree; private final Panel mainPanel; private final TooledStackedPanel stackPanel; + private final CourseToolContainer toolContainer; private Controller currentCtrl; private final RepositoryEntry courseEntry; private AssessmentToolSecurityCallback assessmentCallback; public AssessmentIdentitiesCourseTreeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, - RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) { + RepositoryEntry courseEntry, CourseToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { super(ureq, wControl); this.courseEntry = courseEntry; this.stackPanel = stackPanel; + this.toolContainer = toolContainer; this.assessmentCallback = assessmentCallback; ICourse course = CourseFactory.loadCourse(courseEntry); @@ -119,18 +121,14 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im OLATResourceable ores = OresHelper.createOLATResourceableInstance("Node", new Long(courseNode.getIdent())); WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl()); - - ICourse course = CourseFactory.loadCourse(courseEntry); - if(course.getRunStructure().getRootNode().equals(courseNode)) { - currentCtrl = new AssessmentIdentitiesCourseController(ureq, bwControl, stackPanel, courseEntry, assessmentCallback); - } else if(courseNode instanceof AssessableCourseNode && ((AssessableCourseNode)courseNode).isAssessedBusinessGroups()) { + if(courseNode instanceof AssessableCourseNode && ((AssessableCourseNode)courseNode).isAssessedBusinessGroups()) { if(courseNode instanceof GTACourseNode) { CourseEnvironment courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); currentCtrl = ((GTACourseNode)courseNode).getCoachedGroupListController(ureq, getWindowControl(), stackPanel, courseEnv, assessmentCallback.isAdmin(), assessmentCallback.getCoachedGroups()); } } else { - currentCtrl = new AssessmentIdentitiesCourseNodeController(ureq, bwControl, stackPanel, courseEntry, courseNode, assessmentCallback); + currentCtrl = new IdentityListCourseNodeController(ureq, bwControl, stackPanel, courseEntry, courseNode, toolContainer, assessmentCallback); } listenTo(currentCtrl); mainPanel.setContent(currentCtrl.getInitialComponent()); diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java index 8acd7c50e9d..02b86c3357f 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java @@ -19,6 +19,8 @@ */ package org.olat.course.assessment.ui.tool; +import java.util.List; + import org.olat.basesecurity.BaseSecurity; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -35,6 +37,8 @@ import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowC import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.Roles; +import org.olat.core.id.context.ContextEntry; +import org.olat.core.id.context.StateEntry; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.assessment.IdentityAssessmentOverviewController; @@ -53,7 +57,7 @@ import org.springframework.beans.factory.annotation.Autowired; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class AssessmentIdentityCourseController extends BasicController { +public class AssessmentIdentityCourseController extends BasicController implements AssessedIdentityController { private final Identity assessedIdentity; private final RepositoryEntry courseEntry; @@ -105,7 +109,8 @@ public class AssessmentIdentityCourseController extends BasicController { putInitialPanel(identityAssessmentVC); } - + + @Override public Identity getAssessedIdentity() { return assessedIdentity; } @@ -115,6 +120,11 @@ public class AssessmentIdentityCourseController extends BasicController { // } + @Override + public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { + // + } + @Override protected void event(UserRequest ureq, Controller source, Event event) { if(treeOverviewCtrl == source) { @@ -134,6 +144,15 @@ public class AssessmentIdentityCourseController extends BasicController { cleanUp(); } else if(courseNodeChooserCalloutCtrl == source) { cleanUp(); + } else if(currentNodeCtrl == source) { + if(event == Event.DONE_EVENT) { + treeOverviewCtrl.doIdentityAssessmentOverview(ureq); + stackPanel.popController(currentNodeCtrl); + } else if(event == Event.CHANGED_EVENT) { + treeOverviewCtrl.doIdentityAssessmentOverview(ureq); + } else if(event == Event.CANCELLED_EVENT) { + stackPanel.popController(currentNodeCtrl); + } } super.event(ureq, source, event); } diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java index 2d98fcd76bd..b5015ad9ebd 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java @@ -19,6 +19,8 @@ */ package org.olat.course.assessment.ui.tool; +import java.util.List; + import org.olat.basesecurity.BaseSecurity; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -31,10 +33,11 @@ import org.olat.core.gui.control.controller.BasicController; import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.Roles; +import org.olat.core.id.context.ContextEntry; +import org.olat.core.id.context.StateEntry; import org.olat.core.util.Formatter; import org.olat.course.CourseFactory; import org.olat.course.ICourse; -import org.olat.course.assessment.AssessmentForm; import org.olat.course.assessment.OpenSubDetailsEvent; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CourseNode; @@ -53,7 +56,7 @@ import org.springframework.beans.factory.annotation.Autowired; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class AssessmentIdentityCourseNodeController extends BasicController { +public class AssessmentIdentityCourseNodeController extends BasicController implements AssessedIdentityController { private final TooledStackedPanel stackPanel; private final VelocityContainer identityAssessmentVC; @@ -96,6 +99,7 @@ public class AssessmentIdentityCourseNodeController extends BasicController { Roles roles = securityManager.getRoles(assessedIdentity); IdentityEnvironment identityEnv = new IdentityEnvironment(assessedIdentity, roles); UserCourseEnvironment assessedUserCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment()); + assessedUserCourseEnv.getScoreAccounting().evaluateAll(); // Add the assessment details form if(courseNode instanceof AssessableCourseNode) { @@ -116,6 +120,7 @@ public class AssessmentIdentityCourseNodeController extends BasicController { putInitialPanel(identityAssessmentVC); } + @Override public Identity getAssessedIdentity() { return assessedIdentity; } @@ -128,6 +133,11 @@ public class AssessmentIdentityCourseNodeController extends BasicController { protected void doDispose() { // } + + @Override + public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { + // + } @Override protected void event(UserRequest ureq, Controller source, Event event) { @@ -145,6 +155,8 @@ public class AssessmentIdentityCourseNodeController extends BasicController { listenTo(subDetailsController); stackPanel.pushController(translate("sub.details"), subDetailsController); } + } else if(assessmentForm == source) { + fireEvent(ureq, event); } super.event(ureq, source, event); } diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java index 0ccf2a1a422..89bfbd04a6d 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java @@ -58,6 +58,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen private Link usersLink, efficiencyStatementsLink, bulkAssessmentLink; private final TooledStackedPanel stackPanel; + private final CourseToolContainer toolContainer; private AssessmentCourseOverviewController overviewCtrl; private AssessmentIdentitiesCourseTreeController currentCtl; @@ -71,7 +72,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen this.courseEntry = courseEntry; this.stackPanel = stackPanel; this.assessmentCallback = assessmentCallback; - + toolContainer = new CourseToolContainer(); overviewCtrl = new AssessmentCourseOverviewController(ureq, getWindowControl(), courseEntry, assessmentCallback); listenTo(overviewCtrl); @@ -152,7 +153,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl()); addToHistory(ureq, bwControl); AssessmentIdentitiesCourseTreeController treeCtrl = new AssessmentIdentitiesCourseTreeController(ureq, bwControl, stackPanel, - courseEntry, assessmentCallback); + courseEntry, toolContainer, assessmentCallback); listenTo(treeCtrl); stackPanel.pushController(translate("users"), treeCtrl); currentCtl = treeCtrl; diff --git a/src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java b/src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java new file mode 100644 index 00000000000..4d84baf9681 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java @@ -0,0 +1,48 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.course.assessment.ui.tool; + +import java.util.concurrent.ConcurrentMap; + +import org.olat.course.certificate.CertificateLight; + +/** + * + * + * + * Initial date: 04.12.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CourseToolContainer { + + private ConcurrentMap<Long, CertificateLight> certificateMap; + + public ConcurrentMap<Long, CertificateLight> getCertificateMap() { + return certificateMap; + } + + public void setCertificateMap(ConcurrentMap<Long, CertificateLight> certificateMap) { + this.certificateMap = certificateMap; + } + + + +} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java similarity index 86% rename from src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java rename to src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java index d7dcbd5d5e5..7fe2a93af3e 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityModule; @@ -58,7 +60,10 @@ import org.olat.course.assessment.AssessmentMainController; import org.olat.course.assessment.AssessmentToolManager; import org.olat.course.assessment.bulk.PassedCellRenderer; import org.olat.course.assessment.model.SearchAssessedIdentityParams; -import org.olat.course.assessment.ui.tool.AssessmentIdentitiesCourseNodeTableModel.IdentityCourseElementCols; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols; +import org.olat.course.certificate.CertificateLight; +import org.olat.course.certificate.CertificatesManager; +import org.olat.course.certificate.ui.DownloadCertificateCellRenderer; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.AssessmentToolOptions; import org.olat.course.nodes.CourseNode; @@ -79,7 +84,7 @@ import org.springframework.beans.factory.annotation.Autowired; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class AssessmentIdentitiesCourseNodeController extends FormBasicController { +public class IdentityListCourseNodeController extends FormBasicController { private final BusinessGroup group; private final CourseNode courseNode; @@ -92,9 +97,10 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle private Link nextLink, previousLink; private FlexiTableElement tableEl; private final TooledStackedPanel stackPanel; - private AssessmentIdentitiesCourseNodeTableModel usersTableModel; + private final CourseToolContainer toolContainer; + private IdentityListCourseNodeTableModel usersTableModel; - private AssessmentIdentityCourseNodeController currentIdentityCtrl; + private AssessedIdentityController currentIdentityCtrl; @Autowired private UserManager userManager; @@ -105,10 +111,13 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle @Autowired private RepositoryService repositoryService; @Autowired + private CertificatesManager certificatesManager; + @Autowired private AssessmentToolManager assessmentToolManager; - public AssessmentIdentitiesCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, - RepositoryEntry courseEntry, CourseNode courseNode, AssessmentToolSecurityCallback assessmentCallback) { + public IdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, CourseNode courseNode, CourseToolContainer toolContainer, + AssessmentToolSecurityCallback assessmentCallback) { super(ureq, wControl, "identity_courseelement"); setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator())); setTranslator(userManager.getPropertyHandlerTranslator(getTranslator())); @@ -117,6 +126,7 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle this.courseNode = courseNode; this.stackPanel = stackPanel; this.courseEntry = courseEntry; + this.toolContainer = toolContainer; this.assessmentCallback = assessmentCallback; if(courseNode.needsReferenceToARepositoryEntry()) { @@ -173,8 +183,9 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.assessmentStatus, new AssessmentStatusCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.initialLaunchDate, "select")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.lastScoreUpdate, "select")); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.certificate, new DownloadCertificateCellRenderer())); - usersTableModel = new AssessmentIdentitiesCourseNodeTableModel(columnsModel, assessableNode); + usersTableModel = new IdentityListCourseNodeTableModel(columnsModel, assessableNode); tableEl = uifactory.addTableElement(getWindowControl(), "table", usersTableModel, 20, false, getTranslator(), formLayout); tableEl.setExportEnabled(true); tableEl.setSearchEnabled(new AssessedIdentityListProvider(getIdentity(), courseEntry, referenceEntry, courseNode.getIdent(), assessmentCallback), ureq.getUserSession()); @@ -249,6 +260,16 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle rows.add(new AssessedIdentityCourseElementRow(assessedIdentity, entry, userPropertyHandlers, getLocale())); } } + + if(toolContainer.getCertificateMap() == null) { + List<CertificateLight> certificates = certificatesManager.getLastCertificates(courseEntry.getOlatResource()); + ConcurrentMap<Long, CertificateLight> certificateMap = new ConcurrentHashMap<>(); + for(CertificateLight certificate:certificates) { + certificateMap.put(certificate.getIdentityKey(), certificate); + } + toolContainer.setCertificateMap(certificateMap); + } + usersTableModel.setCertificateMap(toolContainer.getCertificateMap()); usersTableModel.setObjects(rows); tableEl.reloadData(); @@ -283,6 +304,10 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle flc.contextPut("toolCmpNames", toolCmpNames); } + private void updateModel(UserRequest ureq, Identity assessedIdentity) { + updateModel(ureq, null, null, null); + } + private boolean accept(AssessmentEntry entry, SearchAssessedIdentityParams params) { boolean ok = true; @@ -339,6 +364,21 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle super.event(ureq, source, event); } + @Override + public void event(UserRequest ureq, Controller source, Event event) { + if(currentIdentityCtrl == source) { + if(event == Event.CHANGED_EVENT) { + updateModel(ureq, currentIdentityCtrl.getAssessedIdentity()); + } else if(event == Event.DONE_EVENT) { + updateModel(ureq, currentIdentityCtrl.getAssessedIdentity()); + stackPanel.popController(currentIdentityCtrl); + } else if(event == Event.CANCELLED_EVENT) { + stackPanel.popController(currentIdentityCtrl); + } + } + super.event(ureq, source, event); + } + @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(tableEl == source) { @@ -404,8 +444,13 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle Identity assessedIdentity = securityManager.loadIdentityByKey(row.getIdentityKey()); String fullName = userManager.getUserDisplayName(assessedIdentity); - currentIdentityCtrl = new AssessmentIdentityCourseNodeController(ureq, getWindowControl(), stackPanel, - courseEntry, courseNode, assessedIdentity); + if(courseNode.getParent() == null) { + currentIdentityCtrl = new AssessmentIdentityCourseController(ureq, getWindowControl(), stackPanel, + courseEntry, assessedIdentity); + } else { + currentIdentityCtrl = new AssessmentIdentityCourseNodeController(ureq, getWindowControl(), stackPanel, + courseEntry, courseNode, assessedIdentity); + } listenTo(currentIdentityCtrl); stackPanel.pushController(fullName, currentIdentityCtrl); diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeTableModel.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java similarity index 88% rename from src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeTableModel.java rename to src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java index 9c9d85ff4bc..646b7f0b193 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeTableModel.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java @@ -21,6 +21,7 @@ package org.olat.course.assessment.ui.tool; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentMap; import org.olat.core.commons.persistence.SortKey; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel; @@ -30,6 +31,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate; import org.olat.core.util.StringHelper; +import org.olat.course.certificate.CertificateLight; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.STCourseNode; import org.olat.modules.assessment.model.AssessmentEntryStatus; @@ -40,16 +42,21 @@ import org.olat.modules.assessment.model.AssessmentEntryStatus; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableDataModel<AssessedIdentityCourseElementRow> +public class IdentityListCourseNodeTableModel extends DefaultFlexiTableDataModel<AssessedIdentityCourseElementRow> implements SortableFlexiTableDataModel<AssessedIdentityCourseElementRow>, FilterableFlexiTableModel { private final AssessableCourseNode courseNode; private List<AssessedIdentityCourseElementRow> backups; + private ConcurrentMap<Long, CertificateLight> certificateMap; - public AssessmentIdentitiesCourseNodeTableModel(FlexiTableColumnModel columnModel, AssessableCourseNode courseNode) { + public IdentityListCourseNodeTableModel(FlexiTableColumnModel columnModel, AssessableCourseNode courseNode) { super(columnModel); this.courseNode = courseNode; } + + public void setCertificateMap(ConcurrentMap<Long, CertificateLight> certificateMap) { + this.certificateMap = certificateMap; + } @Override public void filter(String key) { @@ -122,6 +129,7 @@ public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableD case status: return ""; case passed: return row.getPassed(); case assessmentStatus: return row.getAssessmentStatus(); + case certificate: return certificateMap.get(row.getIdentityKey()); case initialLaunchDate: return row.getCreationDate(); case lastScoreUpdate: return row.getLastModified(); } @@ -132,7 +140,7 @@ public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableD @Override public DefaultFlexiTableDataModel<AssessedIdentityCourseElementRow> createCopyWithEmptyList() { - return new AssessmentIdentitiesCourseNodeTableModel(getTableColumnModel(), courseNode); + return new IdentityListCourseNodeTableModel(getTableColumnModel(), courseNode); } public enum IdentityCourseElementCols implements FlexiSortableColumnDef { @@ -144,6 +152,7 @@ public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableD status("table.header.status"), passed("table.header.passed"), assessmentStatus("table.header.assessmentStatus"), + certificate("table.header.certificate"), initialLaunchDate("table.header.initialLaunchDate"), lastScoreUpdate("table.header.lastScoreDate"); diff --git a/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java b/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java index f61c916a8bd..cbc94574684 100644 --- a/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java +++ b/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java @@ -19,7 +19,9 @@ */ package org.olat.course.nodes; +import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreCalculator; +import org.olat.modules.assessment.AssessmentEntry; /** * @@ -30,5 +32,7 @@ import org.olat.course.run.scoring.ScoreCalculator; public interface CalculatedAssessableCourseNode extends AssessableCourseNode { public ScoreCalculator getScoreCalculator(); + + public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry); } diff --git a/src/main/java/org/olat/course/nodes/STCourseNode.java b/src/main/java/org/olat/course/nodes/STCourseNode.java index 7d022cf8bf1..9f2900d65a0 100644 --- a/src/main/java/org/olat/course/nodes/STCourseNode.java +++ b/src/main/java/org/olat/course/nodes/STCourseNode.java @@ -77,6 +77,7 @@ import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseInternalLinkTreeModel; import org.olat.modules.ModuleConfiguration; +import org.olat.modules.assessment.AssessmentEntry; import org.olat.repository.RepositoryEntry; import org.olat.util.logging.activity.LoggingResourceable; @@ -292,6 +293,11 @@ public class STCourseNode extends AbstractAccessableCourseNode implements Calcul return new AssessmentEvaluation(score, passed); } + @Override + public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry) { + return AssessmentEvaluation.toAssessmentEvalutation(entry, this); + } + /** * @see org.olat.course.nodes.CourseNode#isConfigValid() */ diff --git a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java index f9d77b19397..236c96e6212 100644 --- a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java +++ b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java @@ -82,6 +82,16 @@ final class PreviewAssessmentManager extends BasicManager implements AssessmentM return Collections.emptyList(); } + @Override + public AssessmentEntry createAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, ScoreEvaluation scoreEvaluation) { + return null; + } + + @Override + public AssessmentEntry updateAssessmentEntry(AssessmentEntry assessmentEntry) { + return assessmentEntry; + } + /** * @see org.olat.course.assessment.AssessmentManager#saveNodeAttempts(org.olat.course.nodes.CourseNode, org.olat.core.id.Identity, org.olat.core.id.Identity, java.lang.Integer) */ diff --git a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java index 7f7c2e7360d..049c4020224 100644 --- a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java +++ b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java @@ -25,6 +25,7 @@ package org.olat.course.run.scoring; +import java.math.BigDecimal; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,11 +36,14 @@ import org.olat.core.logging.Tracing; import org.olat.core.util.nodes.INode; import org.olat.core.util.tree.TreeVisitor; import org.olat.core.util.tree.Visitor; +import org.olat.course.condition.interpreter.ConditionInterpreter; import org.olat.course.nodes.AssessableCourseNode; +import org.olat.course.nodes.CalculatedAssessableCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.PersistentAssessableCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.modules.assessment.AssessmentEntry; +import org.olat.modules.assessment.model.AssessmentEntryStatus; /** * Description:<BR/> @@ -69,11 +73,15 @@ public class ScoreAccounting { * Retrieve all the score evaluations for all course nodes */ public void evaluateAll() { + evaluateAll(false); + } + + public boolean evaluateAll(boolean update) { Identity identity = userCourseEnvironment.getIdentityEnvironment().getIdentity(); List<AssessmentEntry> entries = userCourseEnvironment.getCourseEnvironment() .getAssessmentManager().getAssessmentEntries(identity); - AssessableTreeVisitor visitor = new AssessableTreeVisitor(userCourseEnvironment, entries); + AssessableTreeVisitor visitor = new AssessableTreeVisitor(entries, update); // collect all assessable nodes and eval 'em CourseNode root = userCourseEnvironment.getCourseEnvironment().getRunStructure().getRootNode(); // breadth first traversal gives an easier order of evaluation for debugging @@ -81,21 +89,32 @@ public class ScoreAccounting { // the score accoutings local cache hash map will never be used. this can slow down things like // crazy (course with 10 tests, 300 users and some crazy score and passed calculations will have // 10 time performance differences) + + cachedScoreEvals.clear(); + for(AssessmentEntry entry:entries) { + String nodeIdent = entry.getSubIdent(); + CourseNode courseNode = userCourseEnvironment.getCourseEnvironment().getRunStructure().getNode(nodeIdent); + if(courseNode instanceof AssessableCourseNode) { + AssessableCourseNode acn = (AssessableCourseNode)courseNode; + AssessmentEvaluation se = AssessmentEvaluation.toAssessmentEvalutation(entry, acn); + cachedScoreEvals.put(acn, se); + } + } + TreeVisitor tv = new TreeVisitor(visitor, root, true); // true=depth first tv.visitAll(); - cachedScoreEvals.clear(); - cachedScoreEvals.putAll(visitor.nodeToScoreEvals); + return visitor.hasChanges(); } private class AssessableTreeVisitor implements Visitor { + private final boolean update; + private boolean changes = false; private int recursionLevel = 0; - private final UserCourseEnvironment userCourseEnv; private final Map<String,AssessmentEntry> identToEntries = new HashMap<>(); - private final Map<AssessableCourseNode, AssessmentEvaluation> nodeToScoreEvals = new HashMap<>(); - public AssessableTreeVisitor(UserCourseEnvironment userCourseEnv, List<AssessmentEntry> entries) { - this.userCourseEnv = userCourseEnv; + public AssessableTreeVisitor(List<AssessmentEntry> entries, boolean update) { + this.update = update; for(AssessmentEntry entry:entries) { String ident = entry.getSubIdent(); if(identToEntries.containsKey(ident)) { @@ -108,6 +127,10 @@ public class ScoreAccounting { } } } + + public boolean hasChanges() { + return changes; + } @Override public void visit(INode node) { @@ -122,20 +145,91 @@ public class ScoreAccounting { recursionLevel++; AssessmentEvaluation se = null; if (recursionLevel <= 15) { - se = nodeToScoreEvals.get(cn); + se = cachedScoreEvals.get(cn); if (se == null) { // result of this node has not been calculated yet, do it AssessmentEntry entry = identToEntries.get(cn.getIdent()); if(cn instanceof PersistentAssessableCourseNode) { se = ((PersistentAssessableCourseNode)cn).getUserScoreEvaluation(entry); + } else if(cn instanceof CalculatedAssessableCourseNode) { + if(update) { + se = calculateScoreEvaluation(entry, (CalculatedAssessableCourseNode)cn); + } else { + se = ((CalculatedAssessableCourseNode)cn).getUserScoreEvaluation(entry); + } } else { - se = cn.getUserScoreEvaluation(userCourseEnv); + se = cn.getUserScoreEvaluation(userCourseEnvironment); } - nodeToScoreEvals.put(cn, se); + cachedScoreEvals.put(cn, se); + } else if(update && cn instanceof CalculatedAssessableCourseNode) { + AssessmentEntry entry = identToEntries.get(cn.getIdent()); + se = calculateScoreEvaluation(entry, (CalculatedAssessableCourseNode)cn); + cachedScoreEvals.put(cn, se); } } recursionLevel--; return se; } + + private AssessmentEvaluation calculateScoreEvaluation(AssessmentEntry entry, CalculatedAssessableCourseNode cNode) { + AssessmentEvaluation se; + if(cNode.hasScoreConfigured() || cNode.hasPassedConfigured()) { + ScoreCalculator scoreCalculator = cNode.getScoreCalculator(); + String scoreExpressionStr = scoreCalculator.getScoreExpression(); + String passedExpressionStr = scoreCalculator.getPassedExpression(); + + Float score = null; + Boolean passed = null; + AssessmentEntryStatus assessmentStatus = AssessmentEntryStatus.inProgress; + ConditionInterpreter ci = userCourseEnvironment.getConditionInterpreter(); + if (scoreExpressionStr != null) { + score = new Float(ci.evaluateCalculation(scoreExpressionStr)); + } + if (passedExpressionStr != null) { + boolean hasPassed = ci.evaluateCondition(passedExpressionStr); + if(hasPassed) { + passed = Boolean.TRUE; + assessmentStatus = AssessmentEntryStatus.done; + } + //some rules to set -> failed + } + se = new AssessmentEvaluation(score, passed, null, assessmentStatus, null, null, null, null); + + if(entry == null) { + Identity assessedIdentity = userCourseEnvironment.getIdentityEnvironment().getIdentity(); + userCourseEnvironment.getCourseEnvironment().getAssessmentManager() + .createAssessmentEntry(cNode, assessedIdentity, se); + changes = true; + } else if(!same(se, entry)) { + entry.setScore(new BigDecimal(score)); + entry.setPassed(passed); + entry = userCourseEnvironment.getCourseEnvironment().getAssessmentManager().updateAssessmentEntry(entry); + identToEntries.put(cNode.getIdent(), entry); + changes = true; + } + } else { + se = AssessmentEvaluation.EMPTY_EVAL; + } + return se; + } + + private boolean same(AssessmentEvaluation se, AssessmentEntry entry) { + boolean same = true; + + if((se.getPassed() == null && entry.getPassed() != null) + || (se.getPassed() != null && entry.getPassed() == null) + || (se.getPassed() != null && !se.getPassed().equals(entry.getPassed()))) { + same &= false; + } + + if((se.getScore() == null && entry.getScore() != null) + || (se.getScore() != null && entry.getScore() == null) + || (se.getScore() != null && entry.getScore() != null + && Math.abs(se.getScore().floatValue() - entry.getScore().floatValue()) > 0.00001)) { + same &= false; + } + + return same; + } } /** diff --git a/src/main/java/org/olat/modules/assessment/AssessmentService.java b/src/main/java/org/olat/modules/assessment/AssessmentService.java index e89be12007e..50d15294989 100644 --- a/src/main/java/org/olat/modules/assessment/AssessmentService.java +++ b/src/main/java/org/olat/modules/assessment/AssessmentService.java @@ -34,6 +34,19 @@ import org.olat.repository.RepositoryEntry; */ public interface AssessmentService { + /** + * + * @param assessedIdentity + * @param entry + * @param subIdent + * @param referenceEntry + * @param score + * @param passed + * @return + */ + public AssessmentEntry createAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent, + RepositoryEntry referenceEntry, Float score, Boolean passed); + /** * * @param assessedIdentity diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java index c9d4ef85527..321db32cb7f 100644 --- a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java +++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java @@ -19,6 +19,7 @@ */ package org.olat.modules.assessment.manager; +import java.math.BigDecimal; import java.util.Date; import java.util.List; @@ -61,6 +62,23 @@ public class AssessmentEntryDAO { return data; } + public AssessmentEntry createCourseNodeAssessment(Identity assessedIdentity, + RepositoryEntry entry, String subIdent, RepositoryEntry referenceEntry, + Float score, Boolean passed) { + + AssessmentEntryImpl data = new AssessmentEntryImpl(); + data.setCreationDate(new Date()); + data.setLastModified(data.getCreationDate()); + data.setIdentity(assessedIdentity); + data.setRepositoryEntry(entry); + data.setSubIdent(subIdent); + data.setReferenceEntry(referenceEntry); + data.setScore(new BigDecimal(score)); + data.setPassed(passed); + dbInstance.getCurrentEntityManager().persist(data); + return data; + } + public AssessmentEntry loadAssessmentEntryById(Long id) { List<AssessmentEntry> nodeAssessment = dbInstance.getCurrentEntityManager() .createNamedQuery("loadAssessmentEntryById", AssessmentEntry.class) diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java index f27fb16770b..fa1b08af855 100644 --- a/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java +++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java @@ -45,6 +45,12 @@ public class AssessmentServiceImpl implements AssessmentService { private DB dbInstance; @Autowired private AssessmentEntryDAO assessmentEntryDao; + + @Override + public AssessmentEntry createAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent, + RepositoryEntry referenceEntry, Float score, Boolean passed) { + return assessmentEntryDao.createCourseNodeAssessment(assessedIdentity, entry, subIdent, referenceEntry, score, passed); + } @Override public AssessmentEntry getOrCreateAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent, diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java index 8d8b0a05ea9..a4488196ba7 100644 --- a/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java +++ b/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java @@ -49,6 +49,7 @@ import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl; 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.IdentityEnvironment; import org.olat.core.id.OLATResourceable; import org.olat.core.id.Roles; import org.olat.core.logging.activity.LoggingObject; @@ -63,6 +64,7 @@ import org.olat.core.util.tree.Visitor; import org.olat.course.CourseFactory; import org.olat.course.CourseModule; import org.olat.course.ICourse; +import org.olat.course.assessment.model.UserEfficiencyStatementLight; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.BasicLTICourseNode; import org.olat.course.nodes.CourseNode; @@ -81,6 +83,7 @@ import org.olat.course.nodes.ta.StatusForm; import org.olat.course.nodes.ta.StatusManager; import org.olat.course.nodes.ta.TaskController; import org.olat.course.run.scoring.ScoreCalculator; +import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.course.tree.CourseEditorTreeNode; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; @@ -105,6 +108,7 @@ import org.olat.portfolio.model.structel.PortfolioStructureMap; import org.olat.portfolio.model.structel.StructureStatusEnum; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryRef; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryService; import org.olat.repository.model.SearchRepositoryEntryParameters; @@ -191,6 +195,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { do { courses = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, 50, true); for(RepositoryEntry course:courses) { + convertUserEfficiencyStatemen(course); allOk &= processCourseAssessmentData(course); } counter += courses.size(); @@ -203,6 +208,44 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { return allOk; } + private void convertUserEfficiencyStatemen(RepositoryEntry courseEntry) { + final ICourse course = CourseFactory.loadCourse(courseEntry); + CourseNode rootNode = course.getRunStructure().getRootNode(); + Set<Long> identityKeys = new HashSet<>(loadIdentityKeyOfAssessmentEntries(courseEntry, rootNode.getIdent())); + + int count = 0; + List<UserEfficiencyStatementLight> statements = getUserEfficiencyStatements(courseEntry); + for(UserEfficiencyStatementLight statement:statements) { + Identity identity = statement.getIdentity(); + if(!identityKeys.contains(identity.getKey())) { + AssessmentEntry entry = createAssessmentEntry(identity, null, course, courseEntry, rootNode.getIdent()); + if(statement.getScore() != null) { + entry.setScore(new BigDecimal(statement.getScore().floatValue())); + } + if(statement.getPassed() != null) { + entry.setPassed(statement.getPassed()); + } + dbInstance.getCurrentEntityManager().persist(entry); + if(count++ % 25 == 0) { + dbInstance.commitAndCloseSession(); + } + } + } + dbInstance.commitAndCloseSession(); + } + + public List<UserEfficiencyStatementLight> getUserEfficiencyStatements(RepositoryEntryRef courseRepoEntry) { + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ") + .append(" inner join fetch statement.identity as ident") + .append(" where statement.courseRepoKey=:repoKey"); + + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), UserEfficiencyStatementLight.class) + .setParameter("repoKey", courseRepoEntry.getKey()) + .getResultList(); + } + // select count(*) from o_property where name in ('SCORE','PASSED','ATTEMPTS','COMMENT','COACH_COMMENT','ASSESSMENT_ID','FULLY_ASSESSED'); private boolean processCourseAssessmentData(RepositoryEntry courseEntry) { final Long courseResourceId = courseEntry.getOlatResource().getResourceableId(); @@ -211,46 +254,47 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { //load all assessable identities List<Identity> assessableIdentities = getAllAssessableIdentities(course, courseEntry); - //load already migrated data - List<AssessmentEntryImpl> currentNodeAssessmentList = loadAssessmentEntries(courseEntry); Map<AssessmentDataKey,AssessmentEntryImpl> curentNodeAssessmentMap = new HashMap<>(); - for(AssessmentEntryImpl currentNodeAssessment:currentNodeAssessmentList) { - AssessmentDataKey key = new AssessmentDataKey(currentNodeAssessment.getIdentity(), courseResourceId, currentNodeAssessment.getSubIdent()); - curentNodeAssessmentMap.put(key, currentNodeAssessment); + {//load already migrated data + List<AssessmentEntryImpl> currentNodeAssessmentList = loadAssessmentEntries(courseEntry); + for(AssessmentEntryImpl currentNodeAssessment:currentNodeAssessmentList) { + AssessmentDataKey key = new AssessmentDataKey(currentNodeAssessment.getIdentity().getKey(), courseResourceId, currentNodeAssessment.getSubIdent()); + curentNodeAssessmentMap.put(key, currentNodeAssessment); + } } Map<AssessmentDataKey,AssessmentEntryImpl> nodeAssessmentMap = new HashMap<>(); - - //processed properties - List<Property> courseProperties = loadAssessmentProperties(courseEntry); - for(Property property:courseProperties) { - String propertyCategory = property.getCategory(); - if(StringHelper.containsNonWhitespace(propertyCategory)) { - int nodeIdentIndex = propertyCategory.indexOf("::"); - if(nodeIdentIndex > 0) { - String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2); - AssessmentDataKey key = new AssessmentDataKey(property.getIdentity(), property.getResourceTypeId(), nodeIdent); - if(curentNodeAssessmentMap.containsKey(key)) { - continue; - } - - AssessmentEntryImpl nodeAssessment; - if(nodeAssessmentMap.containsKey(key)) { - nodeAssessment = nodeAssessmentMap.get(key); - if(nodeAssessment.getCreationDate().after(property.getCreationDate())) { - nodeAssessment.setCreationDate(property.getCreationDate()); + {//processed properties + List<Property> courseProperties = loadAssessmentProperties(courseEntry); + for(Property property:courseProperties) { + String propertyCategory = property.getCategory(); + if(StringHelper.containsNonWhitespace(propertyCategory)) { + int nodeIdentIndex = propertyCategory.indexOf("::"); + if(nodeIdentIndex > 0) { + String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2); + AssessmentDataKey key = new AssessmentDataKey(property.getIdentity(), property.getResourceTypeId(), nodeIdent); + if(curentNodeAssessmentMap.containsKey(key)) { + continue; } - if(nodeAssessment.getLastModified().before(property.getLastModified())) { - nodeAssessment.setLastModified(property.getLastModified()); + AssessmentEntryImpl nodeAssessment; + if(nodeAssessmentMap.containsKey(key)) { + nodeAssessment = nodeAssessmentMap.get(key); + if(nodeAssessment.getCreationDate().after(property.getCreationDate())) { + nodeAssessment.setCreationDate(property.getCreationDate()); + } + + if(nodeAssessment.getLastModified().before(property.getLastModified())) { + nodeAssessment.setLastModified(property.getLastModified()); + } + } else { + nodeAssessment = createAssessmentEntry(property.getIdentity(), property, course, courseEntry, nodeIdent); } - } else { - nodeAssessment = createAssessmentEntry(property.getIdentity(), property, course, courseEntry, nodeIdent); + copyAssessmentProperty(property, nodeAssessment, course); + nodeAssessmentMap.put(key, nodeAssessment); } - copyAssessmentProperty(property, nodeAssessment, course); - nodeAssessmentMap.put(key, nodeAssessment); - } - } + } + } } //check the transient qti ser @@ -282,8 +326,13 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { if(allOk) { List<STCourseNode> nodes = hasAssessableSTCourseNode(course); if(nodes.size() > 0) { - System.out.println("Has assessables ST nodes"); - //processStructureNodes(assessableIdentities, course); + log.info("Has assessables ST nodes"); + for(Identity identity:assessableIdentities) { + IdentityEnvironment identityEnv = new IdentityEnvironment(identity, null); + UserCourseEnvironmentImpl userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment()); + userCourseEnv.getScoreAccounting().evaluateAll(true); + dbInstance.commit(); + } } } @@ -402,7 +451,9 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { */ private boolean verifyCourseAssessmentData(List<Identity> assessableIdentities, RepositoryEntry courseEntry) { //load the cache and fill it with the same amount of datas as in assessment tool - ICourse course = CourseFactory.loadCourse(courseEntry); + final ICourse course = CourseFactory.loadCourse(courseEntry); + final Long courseResourceableId = course.getResourceableId(); + StaticCacheWrapper cache = new StaticCacheWrapper(); NewCachePersistingAssessmentManager assessmentManager = new NewCachePersistingAssessmentManager(course, cache); assessmentManager.preloadCache(assessableIdentities); @@ -440,7 +491,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { String nodeIdent = key.substring(0, index); String dataType = key.substring(index + 1); - AssessmentDataKey assessmentDataKey = new AssessmentDataKey(identityKey, course.getResourceableId(), nodeIdent); + AssessmentDataKey assessmentDataKey = new AssessmentDataKey(identityKey, courseResourceableId, nodeIdent); AssessmentEntryImpl nodeAssessment = nodeAssessmentMap.get(assessmentDataKey); allOk &= compareProperty(dataType, data.getValue(), nodeAssessment, courseEntry, nodeIdent, identityKey); } @@ -511,7 +562,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { } boolean allOk = true; - if(node != null) { + if(node instanceof AssessableCourseNode && !(node instanceof STCourseNode)) { Identity assessedIdentity = entry.getIdentity(); Integer attempts = assessmentManager.getNodeAttempts(node, assessedIdentity); @@ -912,6 +963,15 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade { .getResultList(); } + private List<Long> loadIdentityKeyOfAssessmentEntries(RepositoryEntry courseEntry, String subIdent) { + String sb = "select data.identity.key from assessmententry data where data.repositoryEntry.key=:courseEntryKey and data.subIdent=:subIdent"; + return dbInstance.getCurrentEntityManager() + .createQuery(sb, Long.class) + .setParameter("courseEntryKey", courseEntry.getKey()) + .setParameter("subIdent", subIdent) + .getResultList(); + } + private List<Property> loadAssessmentProperties(RepositoryEntry course) { StringBuilder sb = new StringBuilder(); sb.append("from org.olat.properties.Property as p") -- GitLab