From b1f31787084a0b9e6113a206c6fee18451cb65f2 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Mon, 12 Mar 2012 10:30:49 +0100 Subject: [PATCH] OO-170: refactoring of the property table, move efficiency statements and initial/recent launch dates to their own table --- .../assessment/AssessmentMainController.java | 34 +- .../org/olat/basesecurity/BaseSecurity.java | 2 +- .../basesecurity/BaseSecurityManager.java | 17 +- .../olat/basesecurity/IdentityImpl.hbm.xml | 18 + .../org/olat/basesecurity/IdentityShort.java | 96 ++++- .../bc/version/DeletedFileListController.java | 17 +- .../bc/version/RevisionListController.java | 17 +- .../_spring/databaseCorecontext.xml | 2 + src/main/java/org/olat/course/ICourse.java | 11 - .../archiver/ScoreAccountingHelper.java | 26 +- .../course/assessment/AssessmentHelper.java | 38 +- .../assessment/AssessmentMainController.java | 31 +- .../course/assessment/AssessmentManager.java | 5 - .../EfficiencyStatementController.java | 178 +++++++-- .../EfficiencyStatementManager.java | 350 ++++++++++++++---- .../EfficiencyStatementsListController.java | 20 +- .../EfficiencyStatementsListModel.java | 19 +- ...iciencyStatementsPortletRunController.java | 77 ++-- .../IdentityAssessmentEditController.java | 26 +- .../assessment/UserCourseInformations.java | 27 ++ .../assessment/UserEfficiencyStatement.java | 55 +++ .../_content/efficiencystatement.html | 88 +++-- .../assessment/_content/identityoverview.html | 42 ++- .../_i18n/LocalStrings_de.properties | 5 + .../assessment/_spring/assessmentContext.xml | 10 +- .../UserCourseInformationsManager.java | 24 ++ .../UserCourseInformationsManagerImpl.java | 185 +++++++++ .../model/UserCourseInfosImpl.hbm.xml | 43 +++ .../assessment/model/UserCourseInfosImpl.java | 103 ++++++ .../model/UserEfficiencyStatementImpl.hbm.xml | 83 +++++ .../model/UserEfficiencyStatementImpl.java | 172 +++++++++ .../model/UserEfficiencyStatementLight.java | 153 ++++++++ .../GetInitialCourseLaunchDateFunction.java | 20 +- .../GetRecentCourseLaunchDateFunction.java | 20 +- .../score/GetPassedWithCourseIdFunction.java | 6 +- .../score/GetScoreWithCourseIdFunction.java | 8 +- .../olat/course/run/RunMainController.java | 28 +- .../org/olat/upgrade/OLATUpgrade_8_1_0.java | 278 ++++++++++++++ .../_spring/databaseUpgradeContext.xml | 4 + .../olat/upgrade/_spring/upgradeContext.xml | 1 + .../database/mysql/alter_8_0_x_to_8_1_0.sql | 78 ++++ .../database/mysql/setupDatabase.sql | 79 ++++ .../postgresql/alter_8_0_x_to_8_1_0.sql | 74 ++++ .../database/postgresql/setupDatabase.sql | 76 ++++ 44 files changed, 2236 insertions(+), 410 deletions(-) create mode 100644 src/main/java/org/olat/course/assessment/UserCourseInformations.java create mode 100644 src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java create mode 100644 src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java create mode 100644 src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java create mode 100644 src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml create mode 100644 src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java create mode 100644 src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml create mode 100644 src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java create mode 100644 src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java create mode 100644 src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java create mode 100644 src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql create mode 100644 src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql diff --git a/src/main/java/de/bps/course/assessment/AssessmentMainController.java b/src/main/java/de/bps/course/assessment/AssessmentMainController.java index 81357d89fad..b22385e0bdd 100644 --- a/src/main/java/de/bps/course/assessment/AssessmentMainController.java +++ b/src/main/java/de/bps/course/assessment/AssessmentMainController.java @@ -27,7 +27,6 @@ package de.bps.course.assessment; import java.io.File; import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -40,6 +39,7 @@ import org.olat.admin.user.UserTableDataModel; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.SecurityGroup; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.persistence.PersistenceHelper; @@ -81,7 +81,6 @@ import org.olat.core.logging.OLATSecurityException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.ActionType; -import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OresHelper; @@ -98,6 +97,7 @@ import org.olat.course.assessment.GroupAndContextTableModel; import org.olat.course.assessment.IAssessmentCallback; import org.olat.course.assessment.IdentityAssessmentEditController; import org.olat.course.assessment.IndentedNodeRenderer; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.condition.Condition; import org.olat.course.condition.interpreter.ConditionExpression; import org.olat.course.condition.interpreter.OnlyGroupConditionInterpreter; @@ -107,19 +107,17 @@ import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.CourseNodeFactory; import org.olat.course.nodes.IQSURVCourseNode; import org.olat.course.nodes.STCourseNode; -import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.group.BusinessGroup; import org.olat.group.ui.context.BGContextTableModel; -import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.user.UserManager; import de.bps.onyx.plugin.OnyxModule; -import de.bps.webservices.clients.onyxreporter.OnyxReporterException; import de.bps.webservices.clients.onyxreporter.OnyxReporterConnector; +import de.bps.webservices.clients.onyxreporter.OnyxReporterException; /** * Initial Date: Jun 18, 2004 @@ -346,9 +344,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea // select user assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, localUserCourseEnvironmentCache, initialLaunchDates, course, null); - - UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment(); - identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true); + identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, focusOnIdentity, course, true); listenTo(identityAssessmentController); setContent(identityAssessmentController.getInitialComponent()); } @@ -582,9 +578,9 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea // init edit controller for this identity and this course node // or use identity assessment overview if no course node is defined if (this.currentCourseNode == null) { - UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment(); + Identity assessedIdentity = assessedIdentityWrapper.getIdentity(); removeAsListenerAndDispose(identityAssessmentController); - identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true); + identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, assessedIdentity, course, true); listenTo(identityAssessmentController); setContent(identityAssessmentController.getInitialComponent()); } else { @@ -753,7 +749,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea if(initialLaunchDates.containsKey(identityKeyFromEvent)) { initialLaunchDate = initialLaunchDates.get(identityKeyFromEvent); } else { - initialLaunchDate = AssessmentHelper.getInitialLaunchDate(wrappedIdFromModel.getUserCourseEnvironment()); + UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + initialLaunchDate = userCourseInformationsManager.getInitialLaunchDate(ores.getResourceableId(), wrappedIdFromModel.getIdentity()); } wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), initialLaunchDate, currentCourseNode); wrappers.add(wrappedIdFromModel); @@ -1349,21 +1346,10 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea long start = 0; boolean logDebug = log.isDebug(); if(logDebug) start = System.currentTimeMillis(); - course.getCourseEnvironment().getAssessmentManager().preloadCache(); - // 2) preload controller local user environment cache - start = System.currentTimeMillis(); List<Identity> identities = getAllIdentitisFromGroupmanagement(); - CourseNode node = course.getCourseEnvironment().getRunStructure().getRootNode(); - CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager(); - List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE); - Calendar cal = Calendar.getInstance(); - for(Property property:firstTime) { - if (StringHelper.containsNonWhitespace(property.getStringValue()) && property.getIdentity() != null) { - cal.setTimeInMillis(Long.parseLong(property.getStringValue())); - initialLaunchDates.put(property.getIdentity().getKey(), cal.getTime()); - } - } + UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + initialLaunchDates.putAll(mgr.getInitialLaunchDates(course.getResourceableId(), identities)); for (Identity identity : identities) { AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, initialLaunchDates, course, null); diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java index 5f46b5279df..3d124b646d2 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurity.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java @@ -157,7 +157,7 @@ public interface BaseSecurity { * @param identityNames * @return The identities */ - public List<Identity> findIdentitiesByName(Collection<String> identityName); + public List<IdentityShort> findShortIdentitiesByName(Collection<String> identityName); /** * find an identity by the key instead of the username. Prefer this method as diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index e0a54c752d2..e6149db50ec 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -891,12 +891,15 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { if (secGroups == null || secGroups.isEmpty()) { return Collections.emptyList(); } - + StringBuilder sb = new StringBuilder(); - sb.append("select new org.olat.basesecurity.IdentityShort(sgmsi.identity.key, sgmsi.identity.name) from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ") - .append(" where sgmsi.securityGroup in (:secGroups)"); - - DBQuery query = DBFactory.getInstance().createQuery(sb.toString()); + sb.append("select id from ").append(IdentityShort.class.getName()).append(" as id ") + .append(" where id.key in (") + .append(" select sgmsi.identity.key from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ") + .append(" where sgmsi.securityGroup in (:secGroups)") + .append(" )"); + + DBQuery query = DBFactory.getInstance().createQuery(sb .toString()); query.setParameterList("secGroups", secGroups); List<IdentityShort> idents = query.list(); return idents; @@ -1005,12 +1008,12 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { } @Override - public List<Identity> findIdentitiesByName(Collection<String> identityNames) { + public List<IdentityShort> findShortIdentitiesByName(Collection<String> identityNames) { if (identityNames == null || identityNames.isEmpty()) { return Collections.emptyList(); } StringBuilder sb = new StringBuilder(); - sb.append("select ident from ").append(IdentityImpl.class.getName()).append(" as ident where ident.name in (:names)"); + sb.append("select ident from ").append(IdentityShort.class.getName()).append(" as ident where ident.name in (:names)"); DBQuery query = DBFactory.getInstance().createQuery(sb.toString()); query.setParameterList("names", identityNames); diff --git a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml index 209777fdd41..a11438afee1 100644 --- a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml +++ b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml @@ -22,4 +22,22 @@ <many-to-one name="user" column="fk_user_id" class="org.olat.user.UserImpl" fetch="join" unique="true" cascade="none"/> </class> + + + <class name="org.olat.basesecurity.IdentityShort" table="o_bs_identity_short_v" mutable="false"> + <!-- the default columns --> + <composite-id> + <key-property name="key" column="id_id" type="long" /> + <key-property name="userKey" column="us_id" type="long" /> + </composite-id> + + <property name="lastLogin" column="id_lastlogin" type="timestamp" /> + <property name="name" column="id_name" type="string" /> + <property name="status" column="id_status" type="integer" /> + + <property name="firstName" column="first_name" type="string" /> + <property name="lastName" column="last_name" type="string" /> + <property name="email" column="email" type="string" /> + + </class> </hibernate-mapping> \ No newline at end of file diff --git a/src/main/java/org/olat/basesecurity/IdentityShort.java b/src/main/java/org/olat/basesecurity/IdentityShort.java index 20f340e7897..6897f3b155b 100644 --- a/src/main/java/org/olat/basesecurity/IdentityShort.java +++ b/src/main/java/org/olat/basesecurity/IdentityShort.java @@ -19,6 +19,10 @@ */ package org.olat.basesecurity; +import java.util.Date; + +import org.olat.core.commons.persistence.PersistentObject; + /** * * Description:<br> @@ -29,24 +33,98 @@ package org.olat.basesecurity; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ //fxdiff: FXOLAT-219 decrease the load for synching groups -public class IdentityShort { +public class IdentityShort extends PersistentObject { + + private static final long serialVersionUID = -9039644291427632379L; - private final Long key; - private final String name; + private Long userKey; - public IdentityShort(Long key, String name) { - this.key = key; - this.name = name; + private String name; + private Date lastLogin; + private int status; + private String firstName; + private String lastName; + private String email; + + public IdentityShort() { + // + } + + public Long getUserKey() { + return userKey; } - public Long getKey() { - return key; + public void setUserKey(Long userKey) { + this.userKey = userKey; } public String getName() { return name; } + + public void setName(String name) { + this.name = name; + } + + public Date getLastLogin() { + return lastLogin; + } + + public void setLastLogin(Date lastLogin) { + this.lastLogin = lastLogin; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @Override + public String toString() { + return "IdentityShort[name=" + name + "], " + super.toString(); + } + @Override + public int hashCode() { + return getKey() == null ? 3482601 : getKey().hashCode(); + } - + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof IdentityShort) { + IdentityShort id = (IdentityShort)obj; + return getKey() != null && getKey().equals(id.getKey()); + } + return false; + } } diff --git a/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java b/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java index e78dd90b724..ae64337a405 100644 --- a/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java +++ b/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java @@ -31,6 +31,7 @@ import java.util.Locale; import java.util.Map; import org.olat.basesecurity.BaseSecurityManager; +import org.olat.basesecurity.IdentityShort; import org.olat.core.commons.modules.bc.commands.FolderCommand; import org.olat.core.commons.modules.bc.commands.FolderCommandStatus; import org.olat.core.gui.UserRequest; @@ -51,8 +52,6 @@ import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.gui.media.MediaResource; -import org.olat.core.id.Identity; -import org.olat.core.id.UserConstants; import org.olat.core.util.StringHelper; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSManager; @@ -145,8 +144,8 @@ public class DeletedFileListController extends BasicController { } } - Map<String, Identity> mappedIdentities = new HashMap<String, Identity>(); - for(Identity identity :BaseSecurityManager.getInstance().findIdentitiesByName(names)) { + Map<String, IdentityShort> mappedIdentities = new HashMap<String, IdentityShort>(); + for(IdentityShort identity :BaseSecurityManager.getInstance().findShortIdentitiesByName(names)) { mappedIdentities.put(identity.getName(), identity); } @@ -276,9 +275,9 @@ public class DeletedFileListController extends BasicController { private final DateFormat format; private final List<Versions> versionList; private final Calendar cal = Calendar.getInstance(); - private final Map<String, Identity> mappedIdentities; + private final Map<String, IdentityShort> mappedIdentities; - public DeletedFileListDataModel(List<Versions> versionList, Map<String, Identity> mappedIdentities, Locale locale) { + public DeletedFileListDataModel(List<Versions> versionList, Map<String, IdentityShort> mappedIdentities, Locale locale) { this.versionList = versionList; this.mappedIdentities = mappedIdentities; format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale); @@ -320,15 +319,15 @@ public class DeletedFileListController extends BasicController { if(!StringHelper.containsNonWhitespace(name)) { return null; } - Identity id = mappedIdentities.get(name); + IdentityShort id = mappedIdentities.get(name); if(id == null) { return null; } StringBuilder sb = new StringBuilder(); - sb.append(id.getUser().getProperty(UserConstants.FIRSTNAME, null)) + sb.append(id.getFirstName()) .append(" ") - .append(id.getUser().getProperty(UserConstants.LASTNAME, null)); + .append(id.getLastName()); if(isAdmin) { sb.append(" (").append(name).append(")"); diff --git a/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java b/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java index 3b4307175d0..ff5953609e9 100644 --- a/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java +++ b/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java @@ -32,6 +32,7 @@ import java.util.Locale; import java.util.Map; import org.olat.basesecurity.BaseSecurityManager; +import org.olat.basesecurity.IdentityShort; import org.olat.core.commons.modules.bc.commands.FolderCommand; import org.olat.core.commons.modules.bc.commands.FolderCommandStatus; import org.olat.core.gui.UserRequest; @@ -52,8 +53,6 @@ import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.gui.media.MediaResource; -import org.olat.core.id.Identity; -import org.olat.core.id.UserConstants; import org.olat.core.util.StringHelper; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSMediaResource; @@ -175,8 +174,8 @@ public class RevisionListController extends BasicController { } } - Map<String, Identity> mappedIdentities = new HashMap<String, Identity>(); - for(Identity identity :BaseSecurityManager.getInstance().findIdentitiesByName(names)) { + Map<String, IdentityShort> mappedIdentities = new HashMap<String, IdentityShort>(); + for(IdentityShort identity :BaseSecurityManager.getInstance().findShortIdentitiesByName(names)) { mappedIdentities.put(identity.getName(), identity); } @@ -280,9 +279,9 @@ public class RevisionListController extends BasicController { private final DateFormat format; private final List<VFSRevision> versionList; private final Calendar cal = Calendar.getInstance(); - private final Map<String, Identity> mappedIdentities; + private final Map<String, IdentityShort> mappedIdentities; - public RevisionListDataModel(List<VFSRevision> versionList, Map<String, Identity> mappedIdentities, Locale locale) { + public RevisionListDataModel(List<VFSRevision> versionList, Map<String, IdentityShort> mappedIdentities, Locale locale) { this.versionList = versionList; this.mappedIdentities = mappedIdentities; format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale); @@ -324,15 +323,15 @@ public class RevisionListController extends BasicController { if(!StringHelper.containsNonWhitespace(name)) { return null; } - Identity id = mappedIdentities.get(name); + IdentityShort id = mappedIdentities.get(name); if(id == null) { return null; } StringBuilder sb = new StringBuilder(); - sb.append(id.getUser().getProperty(UserConstants.FIRSTNAME, null)) + sb.append(id.getFirstName()) .append(" ") - .append(id.getUser().getProperty(UserConstants.LASTNAME, null)); + .append(id.getLastName()); if(isAdmin) { sb.append(" (").append(name).append(")"); diff --git a/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml b/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml index bdd87aa107e..7653c49a47f 100644 --- a/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml +++ b/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml @@ -49,6 +49,8 @@ <value>org/olat/repository/MetaDataElement.hbm.xml</value> <value>org/olat/course/nodes/projectbroker/datamodel/Project.hbm.xml</value> <value>org/olat/course/nodes/projectbroker/datamodel/ProjectBroker.hbm.xml</value> + <value>org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml</value> + <value>org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml</value> <value>org/olat/modules/fo/ForumImpl.hbm.xml</value> <value>org/olat/modules/fo/MessageImpl.hbm.xml</value> <value>org/olat/modules/fo/ReadMessage.hbm.xml</value> diff --git a/src/main/java/org/olat/course/ICourse.java b/src/main/java/org/olat/course/ICourse.java index 75b8170307a..519219c6d5e 100644 --- a/src/main/java/org/olat/course/ICourse.java +++ b/src/main/java/org/olat/course/ICourse.java @@ -47,17 +47,6 @@ public interface ICourse extends OLATResourceable { * Name of folder within course root directory where nodes export their data. */ public static final String EXPORTED_DATA_FOLDERNAME = "export"; - - /** - * property name for the initial launch date will be set only the first - * time the users launch the course. - */ - public static final String PROPERTY_INITIAL_LAUNCH_DATE = "initialCourseLaunchDate"; - /** - * property name for the recent launch date will be changed every time the - * user start the course. - */ - public static final String PROPERTY_RECENT_LAUNCH_DATE = "recentCourseLaunchDate"; /** * @return The course run structure diff --git a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java index 78cbb7be22d..d2d37f2d382 100644 --- a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java +++ b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java @@ -26,9 +26,7 @@ package org.olat.course.archiver; import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -36,6 +34,7 @@ import java.util.Map; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.SecurityGroup; +import org.olat.core.CoreSpringFactory; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; @@ -48,17 +47,16 @@ import org.olat.course.CourseModule; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentHelper; import org.olat.course.assessment.AssessmentManager; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.STCourseNode; -import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.group.BusinessGroup; -import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.user.UserManager; @@ -129,24 +127,12 @@ public class ScoreAccountingHelper { // preload user properties cache CourseEnvironment courseEnvironment = course.getCourseEnvironment(); - AssessmentManager assessmentManager = courseEnvironment.getAssessmentManager(); - assessmentManager.preloadCache(); boolean firstIteration = true; int rowNumber = 1; - CourseNode node = courseEnvironment.getRunStructure().getRootNode(); - CoursePropertyManager pm = courseEnvironment.getCoursePropertyManager(); - List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE); - Map<Identity,Date> firstTimes = new HashMap<Identity,Date>((identities.size() * 2) + 1); - Calendar cal = Calendar.getInstance(); - for(Property property:firstTime) { - if (StringHelper.containsNonWhitespace(property.getStringValue())) { - cal.setTimeInMillis(Long.parseLong(property.getStringValue())); - firstTimes.put(property.getIdentity(), cal.getTime()); - } - } - + UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + Map<Long,Date> firstTimes = mgr.getInitialLaunchDates(courseEnvironment.getCourseResourceableId(), identities); Formatter formatter = Formatter.getInstance(locale); for (Identity identity:identities) { @@ -158,8 +144,8 @@ public class ScoreAccountingHelper { tableContent.append("\t"); String initialLaunchDate = ""; - if(firstTimes.containsKey(identity)) { - initialLaunchDate = formatter.formatDateAndTime(firstTimes.get(identity)); + if(firstTimes.containsKey(identity.getKey())) { + initialLaunchDate = formatter.formatDateAndTime(firstTimes.get(identity.getKey())); } tableContent.append(initialLaunchDate).append("\t"); diff --git a/src/main/java/org/olat/course/assessment/AssessmentHelper.java b/src/main/java/org/olat/course/assessment/AssessmentHelper.java index 499d3921e64..2a011bdc974 100644 --- a/src/main/java/org/olat/course/assessment/AssessmentHelper.java +++ b/src/main/java/org/olat/course/assessment/AssessmentHelper.java @@ -27,8 +27,8 @@ package org.olat.course.assessment; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.text.ParseException; import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -50,14 +50,12 @@ import org.olat.course.nodes.ProjectBrokerCourseNode; import org.olat.course.nodes.STCourseNode; import org.olat.course.nodes.ScormCourseNode; import org.olat.course.nodes.iq.IQEditController; -import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.course.tree.CourseEditorTreeModel; import org.olat.course.tree.CourseEditorTreeNode; import org.olat.modules.ModuleConfiguration; -import org.olat.properties.Property; /** * Description:<br> @@ -144,21 +142,6 @@ public class AssessmentHelper { AssessedIdentityWrapper aiw = new AssessedIdentityWrapper(uce, attempts, details, initialLaunchDate, lastModified); return aiw; } - - public static Date getInitialLaunchDate(UserCourseEnvironment uce) { - CourseNode node = uce.getCourseEnvironment().getRunStructure().getRootNode(); - CoursePropertyManager pm = uce.getCourseEnvironment().getCoursePropertyManager(); - Identity identity = uce.getIdentityEnvironment().getIdentity(); - Property firstTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE); - - Date initialLaunchDate = null; - if (firstTime != null && StringHelper.containsNonWhitespace(firstTime.getStringValue())) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(Long.parseLong(firstTime.getStringValue())); - initialLaunchDate = cal.getTime(); - } - return initialLaunchDate; - } /** * Create a user course environment for the given user and course. After @@ -168,7 +151,7 @@ public class AssessmentHelper { * @param course * @return Initialized user course environment */ - static UserCourseEnvironment createAndInitUserCourseEnvironment(Identity identity, ICourse course) { + public static UserCourseEnvironment createAndInitUserCourseEnvironment(Identity identity, ICourse course) { // create an identenv with no roles, no attributes, no locale IdentityEnvironment ienv = new IdentityEnvironment(); ienv.setIdentity(identity); @@ -269,6 +252,20 @@ public class AssessmentHelper { } //return Formatter.roundToString(score.floatValue(), 3); } + + public static Float getRoundedScore(String score) { + if (!StringHelper.containsNonWhitespace(score)) return null; + + //cluster_OK the formatter is not multi-thread and costly to create + synchronized(scoreFormat) { + try { + return new Float(scoreFormat.parse(score).floatValue()); + } catch (ParseException e) { + log.error("", e); + return null; + } + } + } public static final String KEY_TYPE = "type"; public static final String KEY_IDENTIFYER = "identifyer"; @@ -285,6 +282,9 @@ public class AssessmentHelper { //fxdiff VCRP-4: assessment overview with max score public static final String KEY_MIN = "minScore"; public static final String KEY_MAX = "maxScore"; + public static final String KEY_TOTAL_NODES = "totalNodes"; + public static final String KEY_ATTEMPTED_NODES = "attemptedNodes"; + public static final String KEY_PASSED_NODES = "attemptedNodes"; diff --git a/src/main/java/org/olat/course/assessment/AssessmentMainController.java b/src/main/java/org/olat/course/assessment/AssessmentMainController.java index 00244533921..984e8c3b533 100644 --- a/src/main/java/org/olat/course/assessment/AssessmentMainController.java +++ b/src/main/java/org/olat/course/assessment/AssessmentMainController.java @@ -26,7 +26,6 @@ package org.olat.course.assessment; import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -38,6 +37,7 @@ import org.olat.admin.user.UserTableDataModel; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.SecurityGroup; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.gui.ShortName; @@ -82,13 +82,13 @@ import org.olat.core.logging.OLATSecurityException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.ActionType; -import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OresHelper; import org.olat.core.util.tree.TreeHelper; import org.olat.course.CourseFactory; import org.olat.course.ICourse; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.condition.Condition; import org.olat.course.condition.interpreter.ConditionExpression; import org.olat.course.condition.interpreter.OnlyGroupConditionInterpreter; @@ -103,7 +103,6 @@ import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.group.BusinessGroup; import org.olat.group.ui.context.BGContextTableModel; -import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.user.UserManager; @@ -302,8 +301,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea // select user assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, localUserCourseEnvironmentCache, initialLaunchDates, course, null); - UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment(); - identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true); + identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, assessedIdentityWrapper.getIdentity(), course, true); listenTo(identityAssessmentController); setContent(identityAssessmentController.getInitialComponent()); } @@ -566,7 +564,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea if(initialLaunchDates.containsKey(identityKeyFromEvent)) { initialLaunchDate = initialLaunchDates.get(identityKeyFromEvent); } else { - initialLaunchDate = AssessmentHelper.getInitialLaunchDate(wrappedIdFromModel.getUserCourseEnvironment()); + UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + initialLaunchDate = userCourseInformationsManager.getInitialLaunchDate(ores.getResourceableId(), wrappedIdFromModel.getIdentity()); } wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), initialLaunchDate, currentCourseNode); wrappers.add(wrappedIdFromModel); @@ -644,9 +643,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea */ private void initIdentityEditController(UserRequest ureq, ICourse course) { if (currentCourseNode == null) { - UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment(); removeAsListenerAndDispose(identityAssessmentController); - identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true); + identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, assessedIdentityWrapper.getIdentity(), course, true); listenTo(identityAssessmentController); setContent(identityAssessmentController.getInitialComponent()); } else { @@ -1153,21 +1151,10 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea long start = 0; boolean logDebug = log.isDebug(); if(logDebug) start = System.currentTimeMillis(); - course.getCourseEnvironment().getAssessmentManager().preloadCache(); - // 2) preload controller local user environment cache - start = System.currentTimeMillis(); List<Identity> identities = getAllAssessableIdentities(); - - CourseNode node = course.getCourseEnvironment().getRunStructure().getRootNode(); - CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager(); - List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE); - Calendar cal = Calendar.getInstance(); - for(Property property:firstTime) { - if (StringHelper.containsNonWhitespace(property.getStringValue()) && property.getIdentity() != null) { - cal.setTimeInMillis(Long.parseLong(property.getStringValue())); - initialLaunchDates.put(property.getIdentity().getKey(), cal.getTime()); - } - } + + UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + initialLaunchDates.putAll(mgr.getInitialLaunchDates(course.getResourceableId(), identities)); for (Identity identity : identities) { AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, initialLaunchDates, course, null); diff --git a/src/main/java/org/olat/course/assessment/AssessmentManager.java b/src/main/java/org/olat/course/assessment/AssessmentManager.java index 4f422eb77cb..ee5d1dfef54 100644 --- a/src/main/java/org/olat/course/assessment/AssessmentManager.java +++ b/src/main/java/org/olat/course/assessment/AssessmentManager.java @@ -57,11 +57,6 @@ public interface AssessmentManager { public static final String COMMENT = "COMMENT"; public static final String COACH_COMMENT = "COACH_COMMENT"; public static final String ASSESSMENT_ID = "ASSESSMENT_ID"; - - /** - * Load all persisted assessment data of a course into a local cache if such a cache is available - */ - public void preloadCache(); /** * Load all persisted assessment data into a local cache if such a cache is available diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java index a6da07b86c4..6b3f25aae41 100644 --- a/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java +++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java @@ -25,9 +25,11 @@ package org.olat.course.assessment; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import org.olat.NewControllerFactory; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.gui.UserRequest; @@ -39,12 +41,22 @@ import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.MainLayoutBasicController; +import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.messages.MessageUIFactory; import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; import org.olat.core.id.Roles; +import org.olat.core.id.context.BusinessControl; +import org.olat.core.id.context.BusinessControlFactory; +import org.olat.core.id.context.ContextEntry; import org.olat.core.util.StringHelper; +import org.olat.core.util.mail.ContactList; +import org.olat.core.util.mail.ContactMessage; +import org.olat.core.util.resource.OresHelper; import org.olat.core.util.vfs.VFSContainer; import org.olat.course.assessment.portfolio.EfficiencyStatementArtefact; +import org.olat.group.BusinessGroup; +import org.olat.modules.co.ContactFormController; import org.olat.portfolio.EPArtefactHandler; import org.olat.portfolio.PortfolioModule; import org.olat.portfolio.model.artefacts.AbstractArtefact; @@ -65,14 +77,18 @@ public class EfficiencyStatementController extends MainLayoutBasicController { private VelocityContainer userDataVC; private static final String usageIdentifyer = EfficiencyStatementController.class.getCanonicalName(); - private EfficiencyStatement efficiencyStatement; + private final EfficiencyStatement efficiencyStatement; + private final Identity statementOwner; + private final Long businessGroupKey; //to collect the eff.Statement as artefact - private Link collectArtefactLink; + private Link collectArtefactLink, homeLink, courseLink, groupLink, contactLink; private PortfolioModule portfolioModule; // the collect-artefact-wizard - private Controller ePFCollCtrl; - + private Controller ePFCollCtrl; + //contact + private ContactFormController contactCtrl; + private CloseableModalController cmc; /** * Constructor * @param wControl @@ -84,32 +100,60 @@ public class EfficiencyStatementController extends MainLayoutBasicController { } public EfficiencyStatementController(WindowControl wControl, UserRequest ureq, EfficiencyStatement efficiencyStatement) { - this(wControl, ureq, ureq.getIdentity(), efficiencyStatement); + this(wControl, ureq, ureq.getIdentity(), null, efficiencyStatement, true); } - public EfficiencyStatementController(WindowControl wControl, UserRequest ureq, Identity statementOwner, EfficiencyStatement efficiencyStatement) { + public EfficiencyStatementController(WindowControl wControl, UserRequest ureq, Identity statementOwner, + BusinessGroup businessGroup, EfficiencyStatement efficiencyStatement, boolean mainLayout) { super(ureq, wControl); - - //either the efficiency statement or the error message, that no data is available goes to the content area - final Component content; - if (efficiencyStatement != null) { - this.efficiencyStatement = efficiencyStatement; - - //extract efficiency statement data - //fallback translation for user properties - setTranslator(UserManager.getInstance().getPropertyHandlerTranslator(getTranslator())); - userDataVC = createVelocityContainer("efficiencystatement"); + + this.businessGroupKey = businessGroup == null ? null : businessGroup.getKey(); + this.statementOwner = statementOwner; + this.efficiencyStatement = efficiencyStatement; + init(ureq, statementOwner, businessGroup, true); + } + + private void init(UserRequest ureq, Identity statementOwner, BusinessGroup group, boolean mainLayout) { + //extract efficiency statement data + //fallback translation for user properties + setTranslator(UserManager.getInstance().getPropertyHandlerTranslator(getTranslator())); + userDataVC = createVelocityContainer("efficiencystatement"); + if(efficiencyStatement != null) { userDataVC.contextPut("courseTitle", efficiencyStatement.getCourseTitle() + " (" + efficiencyStatement.getCourseRepoEntryKey().toString() + ")"); - userDataVC.contextPut("user", statementOwner.getUser()); - userDataVC.contextPut("username", statementOwner.getName()); - userDataVC.contextPut("date", StringHelper.formatLocaleDateTime(efficiencyStatement.getLastUpdated(), ureq.getLocale())); - Roles roles = ureq.getUserSession().getRoles(); - boolean isAdministrativeUser = (roles.isAuthor() || roles.isGroupManager() || roles.isUserManager() || roles.isOLATAdmin()); - List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor(usageIdentifyer, isAdministrativeUser); - userDataVC.contextPut("userPropertyHandlers", userPropertyHandlers); + courseLink = LinkFactory.createButton("course.link", userDataVC, this); + courseLink.setCustomEnabledLinkCSS("b_link_course"); + userDataVC.put("course.link", courseLink); + userDataVC.contextPut("date", StringHelper.formatLocaleDateTime(efficiencyStatement.getLastUpdated(), ureq.getLocale())); + } + + userDataVC.contextPut("user", statementOwner.getUser()); + userDataVC.contextPut("username", statementOwner.getName()); + + Roles roles = ureq.getUserSession().getRoles(); + boolean isAdministrativeUser = (roles.isAuthor() || roles.isGroupManager() || roles.isUserManager() || roles.isOLATAdmin()); + List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor(usageIdentifyer, isAdministrativeUser); + userDataVC.contextPut("userPropertyHandlers", userPropertyHandlers); + + if(!getIdentity().equals(statementOwner)) { + homeLink = LinkFactory.createButton("home.link", userDataVC, this); + homeLink.setCustomEnabledLinkCSS("b_link_to_home"); + userDataVC.put("home.link", homeLink); - Controller identityAssessmentCtr = new IdentityAssessmentOverviewController(ureq, wControl, efficiencyStatement.getAssessmentNodes()); + contactLink = LinkFactory.createButton("contact.link", userDataVC, this); + contactLink.setCustomEnabledLinkCSS("b_link_mail"); + userDataVC.put("contact.link", contactLink); + } + + if(group != null) { + userDataVC.contextPut("groupName", group.getName()); + groupLink = LinkFactory.createButton("group.link", userDataVC, this); + groupLink.setCustomEnabledLinkCSS("b_link_group"); + userDataVC.put("group.link", groupLink); + } + + if (efficiencyStatement != null) { + Controller identityAssessmentCtr = new IdentityAssessmentOverviewController(ureq, getWindowControl(), efficiencyStatement.getAssessmentNodes()); listenTo(identityAssessmentCtr);//dispose it when this one is disposed userDataVC.put("assessmentOverviewTable", identityAssessmentCtr.getInitialComponent()); @@ -122,20 +166,24 @@ public class EfficiencyStatementController extends MainLayoutBasicController { collectArtefactLink.setCustomEnabledLinkCSS("b_eportfolio_add_again"); } } - - content = userDataVC; } else { //message, that no data is available. This may happen in the case the "open efficiency" link is available, while in the meantime an author //disabled the efficiency statement. String text = translate("efficiencystatement.nodata"); - Controller messageCtr = MessageUIFactory.createErrorMessage(ureq, wControl, null, text); + Controller messageCtr = MessageUIFactory.createSimpleMessage(ureq, getWindowControl(), text); listenTo(messageCtr);//gets disposed as this controller gets disposed. - content = messageCtr.getInitialComponent(); + userDataVC.put("assessmentOverviewTable", messageCtr.getInitialComponent()); } + + //Content goes to a 3 cols layout without left and right column - LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), null, null, content, null); - listenTo(layoutCtr); - putInitialPanel(layoutCtr.getInitialComponent()); + if(mainLayout) { + LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), null, null, userDataVC, null); + listenTo(layoutCtr); + putInitialPanel(layoutCtr.getInitialComponent()); + } else { + putInitialPanel(userDataVC); + } } /** @@ -145,8 +193,74 @@ public class EfficiencyStatementController extends MainLayoutBasicController { public void event(UserRequest ureq, Component source, Event event) { if(source.equals(collectArtefactLink)){ popupArtefactCollector(ureq); + } else if (source == homeLink) { + openHome(ureq); + } else if (source == courseLink) { + openCourse(ureq); + } else if (source == groupLink) { + openGroup(ureq); + } else if (source == contactLink) { + contact(ureq); } - + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == cmc) { + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(contactCtrl); + cmc = null; + contactCtrl = null; + } else if (source == contactCtrl) { + cmc.deactivate(); + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(contactCtrl); + cmc = null; + contactCtrl = null; + } + } + + private void contact(UserRequest ureq) { + removeAsListenerAndDispose(cmc); + + ContactMessage cmsg = new ContactMessage(getIdentity()); + ContactList contactList = new ContactList("to"); + contactList.add(statementOwner); + cmsg.addEmailTo(contactList); + contactCtrl = new ContactFormController(ureq, getWindowControl(), true, true, false, false, cmsg); + listenTo(contactCtrl); + cmc = new CloseableModalController(getWindowControl(), translate("close"), contactCtrl.getInitialComponent()); + cmc.activate(); + listenTo(cmc); + } + + private void openGroup(UserRequest ureq) { + List<ContextEntry> ces = new ArrayList<ContextEntry>(1); + OLATResourceable ores = OresHelper.createOLATResourceableInstance("BusinessGroup", businessGroupKey); + ces.add(BusinessControlFactory.getInstance().createContextEntry(ores)); + + BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces); + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); + NewControllerFactory.getInstance().launch(ureq, bwControl); + } + + private void openCourse(UserRequest ureq) { + List<ContextEntry> ces = new ArrayList<ContextEntry>(1); + OLATResourceable ores = OresHelper.createOLATResourceableInstance("RepositoryEntry", efficiencyStatement.getCourseRepoEntryKey()); + ces.add(BusinessControlFactory.getInstance().createContextEntry(ores)); + + BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces); + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); + NewControllerFactory.getInstance().launch(ureq, bwControl); + } + + private void openHome(UserRequest ureq) { + List<ContextEntry> ces = new ArrayList<ContextEntry>(1); + ces.add(BusinessControlFactory.getInstance().createContextEntry(statementOwner)); + + BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces); + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); + NewControllerFactory.getInstance().launch(ureq, bwControl); } /** diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java index f5b3c614769..4e07607103e 100644 --- a/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java +++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java @@ -27,17 +27,20 @@ package org.olat.course.assessment; import java.io.File; import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import org.olat.admin.user.delete.service.UserDeletionManager; +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.commons.persistence.DBQuery; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.id.User; import org.olat.core.id.UserConstants; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; import org.olat.core.manager.BasicManager; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.coordinate.SyncerExecutor; @@ -46,15 +49,17 @@ import org.olat.core.util.xml.XStreamHelper; import org.olat.course.CourseFactory; import org.olat.course.CourseModule; import org.olat.course.ICourse; +import org.olat.course.assessment.model.UserEfficiencyStatementImpl; +import org.olat.course.assessment.model.UserEfficiencyStatementLight; import org.olat.course.config.CourseConfig; import org.olat.course.nodes.CourseNode; import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.properties.Property; -import org.olat.properties.PropertyManager; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.user.UserDataDeletable; +import com.thoughtworks.xstream.XStream; + /** * Description:<br> * Methods to update a users efficiency statement and to retrieve such statements @@ -65,12 +70,17 @@ import org.olat.user.UserDataDeletable; * @author gnaegi */ public class EfficiencyStatementManager extends BasicManager implements UserDataDeletable { - //TODO remove as already definded in basic manager - OLog log = Tracing.createLoggerFor(EfficiencyStatementManager.class); + public static final String KEY_ASSESSMENT_NODES = "assessmentNodes"; public static final String KEY_COURSE_TITLE = "courseTitle"; - private static final String PROPERTY_CATEGORY = "efficiencyStatement"; + public static final String PROPERTY_CATEGORY = "efficiencyStatement"; + private static EfficiencyStatementManager INSTANCE; + + private DB dbInstance; + private RepositoryManager repositoryManager; + private final XStream xstream = XStreamHelper.createXStreamInstance(); + /** * Constructor @@ -88,7 +98,22 @@ public class EfficiencyStatementManager extends BasicManager implements UserData return INSTANCE; } + /** + * [used by Spring] + * @param dbInstance + */ + public void setDbInstance(DB dbInstance) { + this.dbInstance = dbInstance; + } + /** + * [used by Spring] + * @param repositoryManager + */ + public void setRepositoryManager(RepositoryManager repositoryManager) { + this.repositoryManager = repositoryManager; + } + /** * Updates the users efficiency statement for this course. <p> * Called in AssessmentManager in a <code>doInSync</code> block, toghether with the saveScore. @@ -96,7 +121,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData */ protected void updateUserEfficiencyStatement(UserCourseEnvironment userCourseEnv) { Long courseResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); - RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry( + RepositoryEntry re = repositoryManager.lookupRepositoryEntry( OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId), false); ICourse course = CourseFactory.loadCourse(userCourseEnv.getCourseEnvironment().getCourseResourceableId()); updateUserEfficiencyStatement(userCourseEnv, re.getKey(), course, true); @@ -113,11 +138,8 @@ public class EfficiencyStatementManager extends BasicManager implements UserData CourseConfig cc = userCourseEnv.getCourseEnvironment().getCourseConfig(); // write only when enabled for this course if (cc.isEfficencyStatementEnabled()) { - final boolean logDebug = log.isDebug(); - final Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity(); - final PropertyManager pm = PropertyManager.getInstance(); - final String courseRepoEntryKey = getPropertyName(repoEntryKey); - + Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity(); + CourseNode rootNode = userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode(); List<Map<String,Object>> assessmentNodes = AssessmentHelper.addAssessableNodeAndDataToList(0, rootNode, userCourseEnv, true, true); @@ -128,33 +150,42 @@ public class EfficiencyStatementManager extends BasicManager implements UserData User user = identity.getUser(); efficiencyStatement.setDisplayableUserInfo(user.getProperty(UserConstants.FIRSTNAME, null) + " " + user.getProperty(UserConstants.LASTNAME, null) + " (" + identity.getName() + ")"); efficiencyStatement.setLastUpdated(System.currentTimeMillis()); - - // save efficiency statement as xtream persisted list - final String efficiencyStatementX = XStreamHelper.toXML(efficiencyStatement); - Property efficiencyProperty = null; + + UserEfficiencyStatementImpl efficiencyProperty = null; if (checkForExistingProperty) { - efficiencyProperty = pm.findUserProperty(identity, PROPERTY_CATEGORY, courseRepoEntryKey); + efficiencyProperty = getUserEfficiencyStatementFull(repoEntryKey, identity); } if (assessmentNodes != null) { if (efficiencyProperty == null) { // create new - efficiencyProperty = pm.createUserPropertyInstance(identity, PROPERTY_CATEGORY, courseRepoEntryKey, null, null, null, efficiencyStatementX); - pm.saveProperty(efficiencyProperty); - if (logDebug) log.debug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" - + identity.getName() + " repoEntry::" + courseRepoEntryKey); + efficiencyProperty = new UserEfficiencyStatementImpl(); + efficiencyProperty.setIdentity(identity); + efficiencyProperty.setCourseRepoKey(repoEntryKey); + RepositoryEntry re = repositoryManager.lookupRepositoryEntry(repoEntryKey, false); + if(re != null) { + efficiencyProperty.setResource(re.getOlatResource()); + } + + fillEfficiencyStatement(efficiencyStatement, efficiencyProperty); + dbInstance.saveObject(efficiencyProperty); + if (isLogDebugEnabled()) { + logDebug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntryKey); + } } else { // update existing - if (logDebug) log.debug("updatting efficiency statement property::" + efficiencyProperty.getKey() + " for id::" - + identity.getName() + " repoEntry::" + courseRepoEntryKey); - efficiencyProperty.setTextValue(efficiencyStatementX); - pm.updateProperty(efficiencyProperty); + if (isLogDebugEnabled()) { + logDebug("updatting efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntryKey); + } + fillEfficiencyStatement(efficiencyStatement, efficiencyProperty); + dbInstance.updateObject(efficiencyProperty); } } else { if (efficiencyProperty != null) { // remove existing since now empty empty efficiency statements - if (logDebug) log.debug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::" - + identity.getName() + " repoEntry::" + courseRepoEntryKey + " since empty"); - pm.deleteProperty(efficiencyProperty); + if (isLogDebugEnabled()) { + logDebug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntryKey + " since empty"); + } + dbInstance.deleteObject(efficiencyProperty); } // else nothing to create and nothing to delete } @@ -165,6 +196,44 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } } + public void fillEfficiencyStatement(EfficiencyStatement efficiencyStatement, UserEfficiencyStatementImpl efficiencyProperty) { + List<Map<String,Object>> nodeData = efficiencyStatement.getAssessmentNodes(); + if(!nodeData.isEmpty()) { + Map<String,Object> rootNode = nodeData.get(0); + Object passed = rootNode.get(AssessmentHelper.KEY_PASSED); + if(passed instanceof Boolean) { + efficiencyProperty.setPassed((Boolean)passed); + } + + Object fscore = rootNode.get(AssessmentHelper.KEY_SCORE_F); + if(fscore instanceof Float) { + efficiencyProperty.setScore((Float)fscore); + } + + Object shortTitle = rootNode.get(AssessmentHelper.KEY_TITLE_SHORT); + if(shortTitle instanceof String) { + efficiencyProperty.setShortTitle((String)shortTitle); + } + + Object longTitle = rootNode.get(AssessmentHelper.KEY_TITLE_LONG); + if(longTitle instanceof String) { + efficiencyProperty.setTitle((String)longTitle); + } + + int totalNodes = getTotalNodes(nodeData); + efficiencyProperty.setTotalNodes(totalNodes); + + int attemptedNodes = getAttemptedNodes(nodeData); + efficiencyProperty.setAttemptedNodes(attemptedNodes); + + int passedNodes = getPassedNodes(nodeData); + efficiencyProperty.setPassedNodes(passedNodes); + } + + efficiencyProperty.setLastModified(new Date()); + efficiencyProperty.setStatementXml(xstream.toXML(efficiencyStatement)); + } + /** * LD: Debug method. * @param efficiencyStatement @@ -194,16 +263,73 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * keys defined in the AssessmentHelper and the title of the course */ public EfficiencyStatement getUserEfficiencyStatement(Long courseRepoEntryKey, Identity identity){ - PropertyManager pm = PropertyManager.getInstance(); - Property efficiencyProperty; + UserEfficiencyStatementImpl s = getUserEfficiencyStatementFull(courseRepoEntryKey, identity); + if(s == null || s.getStatementXml() == null) { + return null; + } + return (EfficiencyStatement)xstream.fromXML(s.getStatementXml()); + } + + + public UserEfficiencyStatementImpl getUserEfficiencyStatementFull(Long courseRepoEntryKey, Identity identity) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ") + .append(" where statement.identity.key=:identityKey and statement.courseRepoKey=:repoKey"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("identityKey", identity.getKey()); + query.setLong("repoKey", courseRepoEntryKey); + List<UserEfficiencyStatementImpl> statement = query.list(); + if(statement.isEmpty()) { + return null; + } + return statement.get(0); + } catch (Exception e) { + logError("Cannot retrieve efficiency statement: " + courseRepoEntryKey + " from " + identity, e); + return null; + } + } + + public UserEfficiencyStatement getUserEfficiencyStatementLight(Long courseRepoEntryKey, Identity identity) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ") + .append(" where statement.identity.key=:identityKey and statement.courseRepoKey=:repoKey"); - efficiencyProperty = pm.findUserProperty(identity,PROPERTY_CATEGORY, getPropertyName(courseRepoEntryKey)); - if (efficiencyProperty == null) { + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("identityKey", identity.getKey()); + query.setLong("repoKey", courseRepoEntryKey); + List<UserEfficiencyStatement> statement = query.list(); + if(statement.isEmpty()) { + return null; + } + return statement.get(0); + } catch (Exception e) { + logError("Cannot retrieve efficiency statement: " + courseRepoEntryKey + " from " + identity, e); return null; - } else { - return (EfficiencyStatement) XStreamHelper.fromXML(efficiencyProperty.getTextValue()); } } + + public EfficiencyStatement getUserEfficiencyStatementByKey(Long key) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ") + .append(" where statement.key=:key"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("key", key); + List<UserEfficiencyStatementImpl> statement = query.list(); + if(statement.isEmpty()) { + return null; + } + return (EfficiencyStatement)xstream.fromXML(statement.get(0).getStatementXml()); + } catch (Exception e) { + logError("Cannot retrieve efficiency statement: " + key, e); + return null; + } + } + /** * Get the passed value of a course node of a specific efficiency statment @@ -224,6 +350,47 @@ public class EfficiencyStatementManager extends BasicManager implements UserData } return null; } + + public int getTotalNodes(List<Map<String,Object>> assessmentNodes) { + int count = 0; + for (Iterator<Map<String,Object>> iter = assessmentNodes.iterator(); iter.hasNext(); ) { + Map<String,Object> nodeData = iter.next(); + Boolean selectable = (Boolean)nodeData.get(AssessmentHelper.KEY_SELECTABLE); + if(selectable != null && selectable.booleanValue()) { + count++; + } + } + return count; + } + + public int getAttemptedNodes(List<Map<String,Object>> assessmentNodes) { + int count = 0; + for (Iterator<Map<String,Object>> iter = assessmentNodes.iterator(); iter.hasNext(); ) { + Map<String,Object> nodeData = iter.next(); + Boolean selectable = (Boolean)nodeData.get(AssessmentHelper.KEY_SELECTABLE); + if(selectable != null && selectable.booleanValue()) { + if(nodeData.containsKey(AssessmentHelper.KEY_SCORE)) { + count++; + } else if (nodeData.containsKey(AssessmentHelper.KEY_PASSED)) { + count++; + } + } + } + return count; + } + + public int getPassedNodes(List<Map<String,Object>> assessmentNodes) { + int count = 0; + for (Iterator<Map<String,Object>> iter = assessmentNodes.iterator(); iter.hasNext(); ) { + Map<String,Object> nodeData = iter.next(); + Boolean passed = (Boolean)nodeData.get(AssessmentHelper.KEY_PASSED); + Boolean selectable = (Boolean)nodeData.get(AssessmentHelper.KEY_SELECTABLE); + if(passed != null && passed.booleanValue() && selectable != null && selectable.booleanValue()) { + count++; + } + } + return count; + } /** * Get the score value of a course node of a specific efficiency statment @@ -253,26 +420,63 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * @return List of efficiency statements */ protected List<EfficiencyStatement> findEfficiencyStatements(Identity identity) { - PropertyManager pm = PropertyManager.getInstance(); - List<Property> esProperties = pm.listProperties(identity, null, null, PROPERTY_CATEGORY, null); List<EfficiencyStatement> efficiencyStatements = new ArrayList<EfficiencyStatement>(); - Iterator<Property> iter = esProperties.iterator(); - while (iter.hasNext()) { - Property efficiencyProperty = iter.next(); - EfficiencyStatement efficiencyStatement = (EfficiencyStatement) XStreamHelper.fromXML(efficiencyProperty.getTextValue()); - efficiencyStatements.add(efficiencyStatement); + try { + dbInstance = DBFactory.getInstance(); + + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ") + .append(" where statement.identity.key=:identityKey"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("identityKey", identity.getKey()); + List<UserEfficiencyStatementImpl> statements = query.list(); + for(UserEfficiencyStatementImpl statement:statements) { + EfficiencyStatement s = (EfficiencyStatement)xstream.fromXML(statement.getStatementXml()); + efficiencyStatements.add(s); + } + + } catch (Exception e) { + logError("findEfficiencyStatements: " + identity, e); } return efficiencyStatements; } + protected List<UserEfficiencyStatementLight> findEfficiencyStatementsLight(Identity identity) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ") + .append(" where statement.identity.key=:identityKey"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("identityKey", identity.getKey()); + List<UserEfficiencyStatementLight> statements = query.list(); + return statements; + } catch (Exception e) { + logError("findEfficiencyStatements: " + identity, e); + return Collections.emptyList(); + } + } + /** * Find all identities who have an efficiency statement for this course repository entry * @param courseRepoEntryKey * @return List of identities */ protected List<Identity> findIdentitiesWithEfficiencyStatements(Long courseRepoEntryKey) { - PropertyManager pm = PropertyManager.getInstance(); - return pm.findIdentitiesWithProperty(null, PROPERTY_CATEGORY, getPropertyName(courseRepoEntryKey), false); + try { + StringBuilder sb = new StringBuilder(); + sb.append("select distinct(statement.identity) from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ") + .append(" where statement.courseRepoKey=:repoKey"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("repoKey", courseRepoEntryKey); + List<Identity> identities = query.list(); + return identities; + } catch (Exception e) { + logError("findIdentitiesWithEfficiencyStatements: " + courseRepoEntryKey, e); + return Collections.emptyList(); + } } /** @@ -281,8 +485,20 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * @return int number of deleted efficiency statements */ public void deleteEfficiencyStatementsFromCourse(Long courseRepoEntryKey) { - PropertyManager pm = PropertyManager.getInstance(); - pm.deleteProperties(null, null, null, PROPERTY_CATEGORY, getPropertyName(courseRepoEntryKey)); + try { + StringBuilder sb = new StringBuilder(); + sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ") + .append(" where statement.courseRepoKey=:repoKey"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("repoKey", courseRepoEntryKey); + List<UserEfficiencyStatementImpl> statements = query.list(); + for(UserEfficiencyStatementImpl statement:statements) { + dbInstance.deleteObject(statement); + } + } catch (Exception e) { + logError("deleteEfficiencyStatementsFromCourse: " + courseRepoEntryKey, e); + } } /** @@ -291,20 +507,19 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * @param efficiencyStatement */ protected void deleteEfficiencyStatement(Identity identity, EfficiencyStatement efficiencyStatement) { - PropertyManager pm = PropertyManager.getInstance(); - String crourseRepoEntryKey = getPropertyName(efficiencyStatement.getCourseRepoEntryKey()); - pm.deleteProperties(identity, null, null, PROPERTY_CATEGORY, crourseRepoEntryKey); + UserEfficiencyStatement s = getUserEfficiencyStatementLight(efficiencyStatement.getCourseRepoEntryKey(), identity); + if(s != null) { + dbInstance.deleteObject(s); + } } - /** - * Internal helper: convert the course repository entry key to a value that is used - * in the property name field - * @param courseRepoEntryKey - * @return String converted course id + * Delete the given efficiency statement for this person + * @param identity + * @param efficiencyStatement */ - private String getPropertyName(Long courseRepoEntryKey) { - return courseRepoEntryKey.toString(); + protected void deleteEfficiencyStatement(UserEfficiencyStatementLight efficiencyStatement) { + dbInstance.deleteObject(efficiencyStatement); } /** @@ -319,16 +534,14 @@ public class EfficiencyStatementManager extends BasicManager implements UserData public void updateEfficiencyStatements(OLATResourceable ores, List<Identity> identities, final boolean checkForExistingProperty) { if (identities.size() > 0) { final ICourse course = CourseFactory.loadCourse(ores); - log.audit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU"); + logAudit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU"); Long courseResId = course.getCourseEnvironment().getCourseResourceableId(); - final RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry( + final RepositoryEntry re = repositoryManager.lookupRepositoryEntry( OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId), false); // preload cache to speed up things - AssessmentManager am = course.getCourseEnvironment().getAssessmentManager(); long start = System.currentTimeMillis(); - am.preloadCache(); - long between = System.currentTimeMillis(); + AssessmentManager am = course.getCourseEnvironment().getAssessmentManager(); Iterator<Identity> iter = identities.iterator(); while (iter.hasNext()) { @@ -345,15 +558,16 @@ public class EfficiencyStatementManager extends BasicManager implements UserData if (Thread.interrupted()) break; } //} - long end = System.currentTimeMillis(); - if (log.isDebug()) - log.debug("Updated efficiency statements for course::" + course.getResourceableId() - + ". Prepare cache: " + (between-start) + "ms; Updating statements: " + (end-between) + "ms; Users: " + identities.size()); + if (isLogDebugEnabled()) { + long end = System.currentTimeMillis(); + logDebug("Updated efficiency statements for course::" + course.getResourceableId() + + "ms; Updating statements: " + (end-start) + "ms; Users: " + identities.size()); + } } } public void archiveUserData(Identity identity, File archiveDir) { - List<EfficiencyStatement> efficiencyStatements = this.findEfficiencyStatements(identity); + List<EfficiencyStatement> efficiencyStatements = findEfficiencyStatements(identity); EfficiencyStatementArchiver.getInstance().archive(efficiencyStatements, identity, archiveDir); } @@ -362,11 +576,11 @@ public class EfficiencyStatementManager extends BasicManager implements UserData * @param identity Delete data for this identity. */ public void deleteUserData(Identity identity, String newDeletedUserName) { - List<EfficiencyStatement> efficiencyStatements = this.findEfficiencyStatements(identity); + List<EfficiencyStatement> efficiencyStatements = findEfficiencyStatements(identity); for (Iterator<EfficiencyStatement> iter = efficiencyStatements.iterator(); iter.hasNext();) { deleteEfficiencyStatement(identity, iter.next()); } - log.debug("All efficiency statements deleted for identity=" + identity); + logDebug("All efficiency statements deleted for identity=" + identity); } } diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java index 9a32d2dc82b..d0abd1191a2 100644 --- a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java +++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java @@ -59,6 +59,7 @@ import org.olat.core.gui.render.Renderer; import org.olat.core.gui.render.StringOutput; import org.olat.core.id.OLATResourceable; import org.olat.core.util.vfs.VFSContainer; +import org.olat.course.assessment.model.UserEfficiencyStatementLight; import org.olat.course.assessment.portfolio.EfficiencyStatementArtefact; import org.olat.portfolio.EPArtefactHandler; import org.olat.portfolio.PortfolioModule; @@ -83,7 +84,7 @@ public class EfficiencyStatementsListController extends BasicController { private TableController tableCtr; private EfficiencyStatementsListModel efficiencyStatementsListModel; private DialogBoxController confirmDeleteCtr; - private EfficiencyStatement efficiencyStatement; + private UserEfficiencyStatementLight efficiencyStatement; private Controller ePFCollCtrl; private PortfolioModule portfolioModule; @@ -115,7 +116,7 @@ public class EfficiencyStatementsListController extends BasicController { listenTo(tableCtr); EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance(); - List<EfficiencyStatement> efficiencyStatementsList = esm.findEfficiencyStatements(ureq.getIdentity()); + List<UserEfficiencyStatementLight> efficiencyStatementsList = esm.findEfficiencyStatementsLight(ureq.getIdentity()); efficiencyStatementsListModel = new EfficiencyStatementsListModel(efficiencyStatementsList); tableCtr.setTableDataModel(efficiencyStatementsListModel); @@ -143,7 +144,7 @@ public class EfficiencyStatementsListController extends BasicController { // will not be disposed on course run dispose, popus up as new browserwindow ControllerCreator ctrlCreator = new ControllerCreator() { public Controller createController(UserRequest lureq, WindowControl lwControl) { - return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoEntryKey()); + return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoKey()); } }; //wrap the content controller into a full header layout @@ -154,7 +155,7 @@ public class EfficiencyStatementsListController extends BasicController { // } else if (actionid.equals(CMD_LAUNCH_COURSE)) { RepositoryManager rm = RepositoryManager.getInstance(); - RepositoryEntry re = rm.lookupRepositoryEntry(efficiencyStatement.getCourseRepoEntryKey()); + RepositoryEntry re = rm.lookupRepositoryEntry(efficiencyStatement.getCourseRepoKey(), false); if (re == null) { showWarning("efficiencyStatements.course.noexists"); } else if (!rm.isAllowedToLaunch(ureq, re)) { @@ -167,7 +168,7 @@ public class EfficiencyStatementsListController extends BasicController { if (dt == null) { // does not yet exist -> create and add //fxdiff BAKS-7 Resume function - dt = dts.createDTab(ores, re, efficiencyStatement.getCourseTitle()); + dt = dts.createDTab(ores, re, efficiencyStatement.getShortTitle()); if (dt == null) return; Controller launchController = ControllerFactory.createLaunchController(ores, null, ureq, dt.getWindowControl(), true); dt.setController(launchController); @@ -178,7 +179,7 @@ public class EfficiencyStatementsListController extends BasicController { } } else if (actionid.equals(CMD_DELETE)) { // show confirmation dialog - confirmDeleteCtr = activateYesNoDialog(ureq, null, translate("efficiencyStatements.delete.confirm", efficiencyStatement.getCourseTitle()), confirmDeleteCtr); + confirmDeleteCtr = activateYesNoDialog(ureq, null, translate("efficiencyStatements.delete.confirm", efficiencyStatement.getShortTitle()), confirmDeleteCtr); return; } else if (actionid.equals(CMD_ARTEFACT)) { popupArtefactCollector(ureq); @@ -188,7 +189,7 @@ public class EfficiencyStatementsListController extends BasicController { if (DialogBoxUIFactory.isYesEvent(event)) { // delete efficiency statement manager EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance(); - esm.deleteEfficiencyStatement(ureq.getIdentity(), efficiencyStatement); + esm.deleteEfficiencyStatement(efficiencyStatement); efficiencyStatementsListModel.getObjects().remove(efficiencyStatement); efficiencyStatement = null; tableCtr.modelChanged(); @@ -204,8 +205,9 @@ public class EfficiencyStatementsListController extends BasicController { artefact.setAuthor(getIdentity());//only author can create artefact //no business path becouse we cannot launch an efficiency statement artefact.setCollectionDate(new Date()); - artefact.setTitle(translate("artefact.title", new String[]{efficiencyStatement.getCourseTitle()})); - artHandler.prefillArtefactAccordingToSource(artefact, efficiencyStatement); + artefact.setTitle(translate("artefact.title", new String[]{efficiencyStatement.getShortTitle()})); + EfficiencyStatement fullStatement = EfficiencyStatementManager.getInstance().getUserEfficiencyStatementByKey(efficiencyStatement.getKey()); + artHandler.prefillArtefactAccordingToSource(artefact, fullStatement); ePFCollCtrl = new ArtefactWizzardStepsController(ureq, getWindowControl(), artefact, (VFSContainer)null); listenTo(ePFCollCtrl); } diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java index d4df23428a8..326c2ec1d2a 100644 --- a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java +++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java @@ -26,10 +26,10 @@ package org.olat.course.assessment; import java.util.List; -import java.util.Map; import org.apache.commons.lang.StringEscapeUtils; import org.olat.core.gui.components.table.DefaultTableDataModel; +import org.olat.course.assessment.model.UserEfficiencyStatementLight; /** * Description:<br> @@ -44,7 +44,7 @@ public class EfficiencyStatementsListModel extends DefaultTableDataModel { /** * @param list of efficiencyStatements */ - public EfficiencyStatementsListModel(List efficiencyStatements) { + public EfficiencyStatementsListModel(List<UserEfficiencyStatementLight> efficiencyStatements) { super(efficiencyStatements); } @@ -59,16 +59,15 @@ public class EfficiencyStatementsListModel extends DefaultTableDataModel { * @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int) */ public Object getValueAt(int row, int col) { - EfficiencyStatement efficiencyStatement = getEfficiencyStatementAt(row); - List nodeData = efficiencyStatement.getAssessmentNodes(); - Map rootNode = (Map) nodeData.get(0); + UserEfficiencyStatementLight efficiencyStatement = getEfficiencyStatementAt(row); switch (col) { case 0: - return StringEscapeUtils.escapeHtml(efficiencyStatement.getCourseTitle()); + return StringEscapeUtils.escapeHtml(efficiencyStatement.getShortTitle()); case 1: - return rootNode.get(AssessmentHelper.KEY_SCORE); + Float score = efficiencyStatement.getScore(); + return AssessmentHelper.getRoundedScore(score); case 2: - return rootNode.get(AssessmentHelper.KEY_PASSED); + return efficiencyStatement.getPassed(); default: return "ERROR"; } @@ -78,7 +77,7 @@ public class EfficiencyStatementsListModel extends DefaultTableDataModel { * @param row * @return the efficiencyStatement at the given row */ - public EfficiencyStatement getEfficiencyStatementAt(int row) { - return (EfficiencyStatement) objects.get(row); + public UserEfficiencyStatementLight getEfficiencyStatementAt(int row) { + return (UserEfficiencyStatementLight) objects.get(row); } } diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java index c0e0d91cd3a..3e47875d595 100644 --- a/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java +++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java @@ -27,11 +27,7 @@ package org.olat.course.assessment; import java.util.ArrayList; import java.util.Comparator; -import java.util.Date; -import java.util.Iterator; import java.util.List; -import java.util.Locale; -import java.util.Map; import org.apache.commons.lang.StringEscapeUtils; import org.olat.NewControllerFactory; @@ -61,10 +57,9 @@ import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.context.BusinessControl; import org.olat.core.id.context.BusinessControlFactory; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; import org.olat.core.util.event.GenericEventListener; import org.olat.course.CourseModule; +import org.olat.course.assessment.model.UserEfficiencyStatementLight; /** @@ -79,13 +74,11 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun private static final String CMD_LAUNCH = "cmd.launch"; private TableController tableCtr; - //private EfficiencyStatementsListModel efficiencyStatementsListModel; private EfficiencyStatementsTableDataModel efficiencyStatementsListModel; private VelocityContainer efficiencyStatementsVC; private boolean needReloadModel; private Identity cOwner; private Link showAllLink; - private OLog log = Tracing.createLoggerFor(EfficiencyStatementsPortletRunController.class); /** * Constructor @@ -135,8 +128,8 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun * @return the PortletEntry list. */ private List<PortletEntry> getAllPortletEntries() { - List efficiencyStatementsList = EfficiencyStatementManager.getInstance().findEfficiencyStatements(identity); - List<PortletEntry> portletEntryList = this.convertEfficiencyStatementToPortletEntryList(efficiencyStatementsList); + List<UserEfficiencyStatementLight> efficiencyStatementsList = EfficiencyStatementManager.getInstance().findEfficiencyStatementsLight(identity); + List<PortletEntry> portletEntryList = convertEfficiencyStatementToPortletEntryList(efficiencyStatementsList); return portletEntryList; } @@ -145,11 +138,10 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun * @param items * @return */ - private List<PortletEntry> convertEfficiencyStatementToPortletEntryList(List<EfficiencyStatement> items) { + private List<PortletEntry> convertEfficiencyStatementToPortletEntryList(List<UserEfficiencyStatementLight> items) { List<PortletEntry> convertedList = new ArrayList<PortletEntry>(); - Iterator<EfficiencyStatement> listIterator = items.iterator(); - while(listIterator.hasNext()) { - convertedList.add(new EfficiencyStatementPortletEntry(listIterator.next())); + for(UserEfficiencyStatementLight item:items) { + convertedList.add(new EfficiencyStatementPortletEntry(item)); } return convertedList; } @@ -161,7 +153,7 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun protected void reloadModel(SortingCriteria sortingCriteria) { if (sortingCriteria.getSortingType() == SortingCriteria.AUTO_SORTING) { EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance(); - List efficiencyStatementsList = esm.findEfficiencyStatements(identity); + List<UserEfficiencyStatementLight> efficiencyStatementsList = esm.findEfficiencyStatementsLight(identity); efficiencyStatementsList = getSortedList(efficiencyStatementsList, sortingCriteria); List<PortletEntry> entries = convertEfficiencyStatementToPortletEntryList(efficiencyStatementsList); @@ -209,11 +201,11 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun String actionid = te.getActionId(); if (actionid.equals(CMD_LAUNCH)) { int rowid = te.getRowId(); - final EfficiencyStatement efficiencyStatement = efficiencyStatementsListModel.getEfficiencyStatementAt(rowid); + final UserEfficiencyStatementLight efficiencyStatement = efficiencyStatementsListModel.getEfficiencyStatementAt(rowid); // will not be disposed on course run dispose, popus up as new browserwindow ControllerCreator ctrlCreator = new ControllerCreator() { public Controller createController(UserRequest lureq, WindowControl lwControl) { - return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoEntryKey()); + return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoKey()); } }; //wrap the content controller into a full header layout @@ -261,7 +253,7 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun if(portletToolsController==null) { List<PortletEntry> portletEntryList = getAllPortletEntries(); - PortletDefaultTableDataModel tableDataModel = new EfficiencyStatementsManualSortingTableDataModel(portletEntryList, 2, ureq.getLocale()); + PortletDefaultTableDataModel tableDataModel = new EfficiencyStatementsManualSortingTableDataModel(portletEntryList, 2); List<PortletEntry> sortedItems = getPersistentManuallySortedItems(); portletToolsController = new PortletToolSortingControllerImpl(ureq, wControl, getTranslator(), sortingCriteria, tableDataModel, sortedItems); @@ -289,16 +281,14 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun * @param sortingCriteria * @return a Comparator for the input sortingCriteria */ - protected Comparator getComparator(final SortingCriteria sortingCriteria) { - return new Comparator(){ - public int compare(final Object o1, final Object o2) { - EfficiencyStatement statement1 = (EfficiencyStatement)o1; - EfficiencyStatement statement2 = (EfficiencyStatement)o2; + protected Comparator<UserEfficiencyStatementLight> getComparator(final SortingCriteria sortingCriteria) { + return new Comparator<UserEfficiencyStatementLight>(){ + public int compare(final UserEfficiencyStatementLight s1, final UserEfficiencyStatementLight s2) { int comparisonResult = 0; if(sortingCriteria.getSortingTerm()==SortingCriteria.ALPHABETICAL_SORTING) { - comparisonResult = collator.compare(statement1.getCourseTitle(), statement2.getCourseTitle()); + comparisonResult = collator.compare(s1.getShortTitle(), s2.getShortTitle()); } else if(sortingCriteria.getSortingTerm()==SortingCriteria.DATE_SORTING) { - comparisonResult = Long.valueOf(statement1.getLastUpdated()).compareTo(Long.valueOf(statement2.getLastUpdated())); + comparisonResult = s1.getLastModified().compareTo(s2.getLastModified()); } if(!sortingCriteria.isAscending()) { //if not isAscending return (-comparisonResult) @@ -323,23 +313,22 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun } public Object getValueAt(int row, int col) { - EfficiencyStatement efficiencyStatement = (EfficiencyStatement)this.getObject(row).getValue(); - List nodeData = efficiencyStatement.getAssessmentNodes(); - Map rootNode = (Map) nodeData.get(0); + UserEfficiencyStatementLight efficiencyStatement = getEfficiencyStatementAt(row); switch (col) { case 0: - return StringEscapeUtils.escapeHtml(efficiencyStatement.getCourseTitle()); + return StringEscapeUtils.escapeHtml(efficiencyStatement.getShortTitle()); case 1: - return rootNode.get(AssessmentHelper.KEY_SCORE); + Float score = efficiencyStatement.getScore(); + return AssessmentHelper.getRoundedScore(score); case 2: - return rootNode.get(AssessmentHelper.KEY_PASSED); + return efficiencyStatement.getPassed(); default: return "ERROR"; } } - public EfficiencyStatement getEfficiencyStatementAt(int row) { - return (EfficiencyStatement)this.getObject(row).getValue(); + public UserEfficiencyStatementLight getEfficiencyStatementAt(int row) { + return (UserEfficiencyStatementLight)getObject(row).getValue(); } } @@ -352,14 +341,12 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun * @author Lavinia Dumitrescu */ private class EfficiencyStatementsManualSortingTableDataModel extends PortletDefaultTableDataModel { - private Locale locale; /** * @param objects * @param locale */ - public EfficiencyStatementsManualSortingTableDataModel(List<PortletEntry> objects, int numCols, Locale locale) { + public EfficiencyStatementsManualSortingTableDataModel(List<PortletEntry> objects, int numCols) { super(objects, numCols); - this.locale = locale; } /** @@ -367,14 +354,12 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun */ public final Object getValueAt(int row, int col) { PortletEntry entry = getObject(row); - EfficiencyStatement statement = (EfficiencyStatement)entry.getValue(); + UserEfficiencyStatementLight statement = (UserEfficiencyStatementLight)entry.getValue(); switch (col) { case 0: - return statement.getCourseTitle(); + return statement.getShortTitle(); case 1: - Date lastUpdate = new Date(statement.getLastUpdated()); - //return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getTranslator().getLocale()).format(lastUpdate); - return lastUpdate; + return statement.getLastModified(); default: return "error"; } @@ -389,20 +374,20 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun * Initial Date: 07.12.2007 <br> * @author Lavinia Dumitrescu */ - private class EfficiencyStatementPortletEntry implements PortletEntry { - private EfficiencyStatement value; + private class EfficiencyStatementPortletEntry implements PortletEntry<UserEfficiencyStatementLight> { + private UserEfficiencyStatementLight value; private Long key; - public EfficiencyStatementPortletEntry(EfficiencyStatement efficiencyStatement) { + public EfficiencyStatementPortletEntry(UserEfficiencyStatementLight efficiencyStatement) { value = efficiencyStatement; - key = efficiencyStatement.getCourseRepoEntryKey(); + key = efficiencyStatement.getCourseRepoKey(); } public Long getKey() { return key; } - public EfficiencyStatement getValue() { + public UserEfficiencyStatementLight getValue() { return value; } } diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java index f7be1e5e1a9..069e7af85c1 100644 --- a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java +++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java @@ -27,6 +27,7 @@ package org.olat.course.assessment; import java.util.Date; +import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; @@ -46,6 +47,7 @@ import org.olat.core.util.resource.OresHelper; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.Structure; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.run.userview.UserCourseEnvironment; @@ -74,6 +76,7 @@ public class IdentityAssessmentEditController extends BasicController { private UserCourseEnvironment assessedUserCourseEnvironment; private Link backLink; private OLATResourceable ores; + private final boolean headers; /** * Constructor for the identity assessment overview controller @@ -84,13 +87,19 @@ public class IdentityAssessmentEditController extends BasicController { * @param mayEdit true: user may edit the assessment, false: readonly view (user view) */ public IdentityAssessmentEditController(WindowControl wControl, UserRequest ureq, - UserCourseEnvironment assessedUserCourseEnvironment, OLATResourceable ores, boolean mayEdit) { + Identity assessedIdentity, ICourse course, boolean mayEdit) { + this(wControl, ureq, assessedIdentity, course, mayEdit, false); + } + + public IdentityAssessmentEditController(WindowControl wControl, UserRequest ureq, + Identity assessedIdentity, ICourse course, boolean mayEdit, boolean headers) { super(ureq, wControl); this.mayEdit = mayEdit; this.main = new Panel("main"); - this.assessedUserCourseEnvironment = assessedUserCourseEnvironment; - this.ores = ores; + this.assessedUserCourseEnvironment = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course); + this.ores = course; + this.headers = headers; doIdentityAssessmentOverview(ureq, true); putInitialPanel(main); @@ -140,10 +149,12 @@ public class IdentityAssessmentEditController extends BasicController { private void doIdentityAssessmentOverview(UserRequest ureq, boolean initTable) { if (identityOverviewVC == null) { identityOverviewVC = createVelocityContainer("identityoverview"); - backLink = LinkFactory.createLinkBack(identityOverviewVC, this); - Identity assessedIdentity = assessedUserCourseEnvironment.getIdentityEnvironment().getIdentity(); - identityOverviewVC.contextPut("user", assessedIdentity.getUser()); + if(headers) { + backLink = LinkFactory.createLinkBack(identityOverviewVC, this); + Identity assessedIdentity = assessedUserCourseEnvironment.getIdentityEnvironment().getIdentity(); + identityOverviewVC.contextPut("user", assessedIdentity.getUser()); + } } if (initTable) { assessmentOverviewCtr = new IdentityAssessmentOverviewController(ureq, getWindowControl(), @@ -158,7 +169,8 @@ public class IdentityAssessmentEditController extends BasicController { private void doEditNodeAssessment(UserRequest ureq, AssessableCourseNode courseNode){ if (mayEdit) { ICourse course = CourseFactory.loadCourse(ores); - Date initialLaunchDate = AssessmentHelper.getInitialLaunchDate(assessedUserCourseEnvironment); + UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + Date initialLaunchDate = userCourseInformationsManager.getInitialLaunchDate(ores.getResourceableId(), assessedUserCourseEnvironment.getIdentityEnvironment().getIdentity()); AssessedIdentityWrapper assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedUserCourseEnvironment, initialLaunchDate, courseNode); assessmentEditCtr = new AssessmentEditController(ureq, getWindowControl(), course, courseNode, assessedIdentityWrapper); listenTo(assessmentEditCtr); diff --git a/src/main/java/org/olat/course/assessment/UserCourseInformations.java b/src/main/java/org/olat/course/assessment/UserCourseInformations.java new file mode 100644 index 00000000000..0112e6355ca --- /dev/null +++ b/src/main/java/org/olat/course/assessment/UserCourseInformations.java @@ -0,0 +1,27 @@ +package org.olat.course.assessment; + +import java.util.Date; + +import org.olat.core.id.Identity; + +/** + * Some statistical datas about a user visiting a course + * + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public interface UserCourseInformations { + + public Long getKey(); + + public Date getInitialLaunch(); + + public Date getRecentLaunch(); + + public int getVisit(); + + public long getTimeSpend(); + + public Identity getIdentity(); + +} diff --git a/src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java b/src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java new file mode 100644 index 00000000000..3bc60791e91 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java @@ -0,0 +1,55 @@ +/** +* 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; + +import java.util.Date; + +import org.olat.core.id.Identity; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public interface UserEfficiencyStatement { + + public Identity getIdentity(); + + public Date getLastModified(); + + public Long getCourseRepoKey(); + + public String getShortTitle(); + + public Float getScore(); + + public Boolean getPassed(); + + public Integer getTotalNodes(); + + public Integer getAttemptedNodes(); + + public Integer getPassedNodes(); +} diff --git a/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html b/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html index c3f88f0cdba..3a13a3b8008 100644 --- a/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html +++ b/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html @@ -15,33 +15,67 @@ Ext.onReady(function(){ #end <h3 class="b_with_small_icon_left o_efficiencystatement_icon">$r.translate("efficiencystatement.title")</h3> - <table class="b_table"> - <tr> - <td><label>$r.translate("course")</label></td> - <td>$courseTitle</td> - </tr> - <tr> - <td><label>$r.translate("username")</label></td> - <td>$username</td> - </tr> - - #foreach( $propertyHandler in $userPropertyHandlers ) - #if ($propertyHandler.getUserPropertyAsHTML($user, $locale) && $propertyHandler.getUserPropertyAsHTML($user, $locale) != "") - <tr> - <td> - <label>$r.translate($propertyHandler.i18nFormElementLabelKey())</label> - </td> - <td>$propertyHandler.getUserPropertyAsHTML($user, $locale)</td> - </tr> - #end - #end - - <tr> - <td><label>$r.translate("date")</label></td> - <td>$date</td> - </tr> - </table> - + <div class="b_clearfix"> + <div class="b_c50l"> + <table class="b_table"> + <tr> + <td><label>$r.translate("username")</label></td> + <td>$username</td> + <td> + #if($r.available("home.link")) + $r.render("home.link") + #end + + #if($r.available("contact.link")) + $r.render("contact.link") + #end + </td> + </tr> + #foreach( $propertyHandler in $userPropertyHandlers ) + #if ($propertyHandler.getUserPropertyAsHTML($user, $locale) && $propertyHandler.getUserPropertyAsHTML($user, $locale) != "") + <tr> + <td> + <label>$r.translate($propertyHandler.i18nFormElementLabelKey())</label> + </td> + <td> + #if($propertyHandler.name == "email") + $propertyHandler.getUserProperty($user, $locale) + #else + $propertyHandler.getUserPropertyAsHTML($user, $locale) + #end + </td> + </tr> + #end + #end + </table> + </div> + <div class="b_c50r"> + <table class="b_table"> + #if($groupName) + <tr> + <td><label>$r.translate("group")</label></td> + <td>$groupName</td> + #if($r.available("group.link")) + <td>$r.render("group.link")</td> + #end + </tr> + #end + #if($courseTitle) + <tr> + <td><label>$r.translate("course")</label></td> + <td>$courseTitle</td> + #if($r.available("course.link")) + <td>$r.render("course.link")</td> + #end + </tr> + <tr> + <td><label>$r.translate("date")</label></td> + <td>$date</td> + </tr> + #end + </table> + </div> + </div> <p> $r.render("assessmentOverviewTable") </p> diff --git a/src/main/java/org/olat/course/assessment/_content/identityoverview.html b/src/main/java/org/olat/course/assessment/_content/identityoverview.html index 22ec4f3946f..558c8da7b41 100644 --- a/src/main/java/org/olat/course/assessment/_content/identityoverview.html +++ b/src/main/java/org/olat/course/assessment/_content/identityoverview.html @@ -1,23 +1,27 @@ -<h5>$r.translate("identityoverview.title")</h5> -$r.render("backLink") -<br><br> -<span class="b_with_small_icon_left b_user_icon"> - $user.getProperty("firstName", $locale) $user.getProperty("lastName", $locale) -</span> -<br /> -<a href="mailto:$user.getProperty("email", $locale)" class="b_link_mailto">$user.getProperty("email", $locale)</a> -#if ($user.getProperty("institutionalEmail", $locale) && $user.getProperty("institutionalEmail", $locale) != "" && $user.getProperty("email", $locale) != $user.getProperty("institutionalEmail", $locale)) - <br /> - <a href="mailto:$user.getProperty("institutionalEmail", $locale)" class="b_link_mailto">$user.getProperty("institutionalEmail", $locale)</a> +#if($r.available("backLink")) + <h5>$r.translate("identityoverview.title")</h5> + $r.render("backLink") + <br><br> #end -#if ($user.getProperty("institutionalName", $locale)) - <br /> - <span class="b_with_small_icon_left b_institution_icon"> - $user.getProperty("institutionalName", $locale) + +#if($user) + <span class="b_with_small_icon_left b_user_icon"> + $user.getProperty("firstName", $locale) $user.getProperty("lastName", $locale) </span> + <br /> + <a href="mailto:$user.getProperty("email", $locale)" class="b_link_mailto">$user.getProperty("email", $locale)</a> + #if ($user.getProperty("institutionalEmail", $locale) && $user.getProperty("institutionalEmail", $locale) != "" && $user.getProperty("email", $locale) != $user.getProperty("institutionalEmail", $locale)) + <br /> + <a href="mailto:$user.getProperty("institutionalEmail", $locale)" class="b_link_mailto">$user.getProperty("institutionalEmail", $locale)</a> + #end + #if ($user.getProperty("institutionalName", $locale)) + <br /> + <span class="b_with_small_icon_left b_institution_icon"> + $user.getProperty("institutionalName", $locale) + </span> + #end + #if ($user.getProperty("institutionalUserIdentifier", $locale)) + ($user.getProperty("institutionalUserIdentifier", $locale)) + #end #end -#if ($user.getProperty("institutionalUserIdentifier", $locale)) - ($user.getProperty("institutionalUserIdentifier", $locale)) -#end - $r.render("assessmentOverviewTable") \ No newline at end of file diff --git a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties index 14c06179a80..84be500eed1 100644 --- a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties @@ -32,11 +32,14 @@ cmd.filterGroups=Von mir betreute Gruppen mit Zugriff auf gew\u00E4hlten Kursbau cmd.showAllCourseNodes=Alle bewertbaren Kursbausteine anzeigen cmd.showAllGroups=Alle von mir betreuten Gruppen anzeigen column=Spalte {0} +contact.link=Kontakt command.closeassessment=Schliessen command.hidelog=\u00C4nderungsverlauf ausblenden command.showlog=\u00C4nderungsverlauf anzeigen command.start.bulkwizard=Massenbewertung starten course=Kurs\: +group=Gruppe\: +group.link=Gruppe date=Datum\: detailview.title=Detailansicht der Bewertung efficiencyStatements.course.noaccess=Dieser Kurs wurde gesperrt, Sie haben keinen Zugang mehr @@ -95,6 +98,8 @@ institutionalname=Institution\: institutionaluseridentifier=Institutionsnummer (Matrikelnummer)\: lastname=Name\: log.title=\u00C4nderungsverlauf +home.link=Visitkarte +course.link=Kurs menu.bulkfocus=Massenbewertung menu.bulkfocus.alt=Massenbewertung von Bewertungs- und Aufgabekursbausteinen. menu.groupfocus=Nach Gruppen diff --git a/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml b/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml index 8f46e6f1fce..4ae1eaae9fc 100644 --- a/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml +++ b/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml @@ -1,9 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + <context:annotation-config /> + <context:component-scan base-package="org.olat.course.assessment.manager" /> <bean id="assessmentModule" class="org.olat.course.assessment.AssessmentModule" init-method="init" destroy-method="destroy" > <constructor-arg index="0" ref="courseModule" /> @@ -17,6 +23,8 @@ <bean id="org.olat.course.assessment.EfficiencyStatementManager" class="org.olat.course.assessment.EfficiencyStatementManager"> <constructor-arg index="0" ref="userDeletionManager" /> + <property name="dbInstance" ref="database"/> + <property name="repositoryManager" ref="repositoryManager"/> </bean> diff --git a/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java new file mode 100644 index 00000000000..ec5b0850629 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java @@ -0,0 +1,24 @@ +package org.olat.course.assessment.manager; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.olat.core.id.Identity; +import org.olat.course.assessment.UserCourseInformations; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public interface UserCourseInformationsManager { + + UserCourseInformations getUserCourseInformations(Long courseResourceId, Identity identity); + + public UserCourseInformations updateUserCourseInformations(Long courseResId, Identity identity); + + public Date getInitialLaunchDate(Long courseResourceId, Identity identity); + + public Map<Long,Date> getInitialLaunchDates(Long courseResourceId, List<Identity> identities); + +} diff --git a/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java new file mode 100644 index 00000000000..5c3f2359356 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java @@ -0,0 +1,185 @@ +/** +* 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.manager; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.olat.basesecurity.BaseSecurity; +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.DBQuery; +import org.olat.core.id.Identity; +import org.olat.core.manager.BasicManager; +import org.olat.course.assessment.UserCourseInformations; +import org.olat.course.assessment.model.UserCourseInfosImpl; +import org.olat.resource.OLATResource; +import org.olat.resource.OLATResourceManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Manager for infos as initial launch date... + * + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +@Service +public class UserCourseInformationsManagerImpl extends BasicManager implements UserCourseInformationsManager { + + @Autowired + private DB dbInstance; + @Autowired + private OLATResourceManager resourceManager; + + private UserCourseInfosImpl createUserCourseInformations(Identity identity, OLATResource courseResource) { + UserCourseInfosImpl infos = new UserCourseInfosImpl(); + infos.setIdentity(identity); + infos.setInitialLaunch(new Date()); + infos.setLastModified(new Date()); + infos.setRecentLaunch(new Date()); + infos.setVisit(1); + infos.setResource(courseResource); + dbInstance.saveObject(infos); + return infos; + } + + @Override + public UserCourseInfosImpl getUserCourseInformations(Long courseResourceId, Identity identity) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select infos from ").append(UserCourseInfosImpl.class.getName()).append(" as infos ") + .append(" inner join infos.resource as resource") + .append(" where infos.identity.key=:identityKey and resource.resId=:resId and resource.resName='CourseModule'"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + query.setLong("identityKey", identity.getKey()); + query.setLong("resId", courseResourceId); + @SuppressWarnings("unchecked") + List<UserCourseInfosImpl> infoList = query.list(); + if(infoList.isEmpty()) { + return null; + } + return infoList.get(0); + } catch (Exception e) { + logError("Cannot retrieve course informations for: " + identity + " from " + identity, e); + return null; + } + } + + /** + * Update (or create if not exists) the course informations for a user + * @param userCourseEnv + * @return + */ + @Override + public UserCourseInformations updateUserCourseInformations(Long courseResourceableId, Identity identity) { + try { + UserCourseInfosImpl infos = getUserCourseInformations(courseResourceableId, identity); + if(infos == null) { + OLATResource courseResource = resourceManager.findResourceable(courseResourceableId, "CourseModule"); + infos = createUserCourseInformations(identity, courseResource); + } else { + infos.setVisit(infos.getVisit() + 1); + infos.setRecentLaunch(new Date()); + infos.setLastModified(new Date()); + dbInstance.updateObject(infos); + } + return infos; + } catch (Exception e) { + logError("Cannot update course informations for: " + identity + " from " + identity, e); + return null; + } + } + + + + @Override + public Date getInitialLaunchDate(Long courseResourceId, Identity identity) { + return getInitialLaunchDate(courseResourceId, identity.getKey()); + } + + public Date getInitialLaunchDate(Long courseResourceId, Long identityKey) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select infos.initialLaunch from ").append(UserCourseInfosImpl.class.getName()).append(" as infos ") + .append(" inner join infos.resource as resource") + .append(" where infos.identity.key=:identityKey and resource.resId=:resId and resource.resName='CourseModule'"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + + query.setLong("identityKey", identityKey); + query.setLong("resId", courseResourceId); + @SuppressWarnings("unchecked") + List<Date> infoList = query.list(); + if(infoList.isEmpty()) { + return null; + } + return infoList.get(0); + } catch (Exception e) { + logError("Cannot retrieve course informations for: " + courseResourceId, e); + return null; + } + } + + /** + * Return a map of identity keys to initial launch date. + * @param courseEnv + * @param identities + * @return + */ + @Override + public Map<Long,Date> getInitialLaunchDates(Long courseResourceId, List<Identity> identities) { + try { + StringBuilder sb = new StringBuilder(); + sb.append("select infos.identity.key, infos.initialLaunch from ").append(UserCourseInfosImpl.class.getName()).append(" as infos ") + .append(" inner join infos.resource as resource") + .append(" where infos.identity.key in (:identityKeys) and resource.resId=:resId and resource.resName='CourseModule'"); + + DBQuery query = dbInstance.createQuery(sb.toString()); + List<Long> identityKeys = new ArrayList<Long>(); + for(Identity identity:identities) { + identityKeys.add(identity.getKey()); + } + query.setParameterList("identityKeys", identityKeys); + query.setLong("resourceKey", courseResourceId); + @SuppressWarnings("unchecked") + List<Object[]> infoList = query.list(); + Map<Long,Date> dateMap = new HashMap<Long,Date>(); + for(Object[] infos:infoList) { + Long identityKey = (Long)infos[0]; + Date initialLaunch = (Date)infos[1]; + dateMap.put(identityKey, initialLaunch); + } + return dateMap; + } catch (Exception e) { + logError("Cannot retrieve course informations for: " + courseResourceId, e); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml new file mode 100644 index 00000000000..c8374f7e5a0 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<!DOCTYPE hibernate-mapping PUBLIC + "-//Hibernate/Hibernate Mapping DTD//EN" + "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-lazy="false"> + + <class name="org.olat.course.assessment.model.UserCourseInfosImpl" table="o_as_user_course_infos"> + <cache usage="read-write" /> + + <id name="key" type="long" column="id" unsaved-value="null"> + <generator class="hilo"/> + </id> + + <version name="version" access="field" column="version" type="int"/> + <property name="creationDate" column="creationdate" type="timestamp" /> + <property name="lastModified" column="lastmodified" type="timestamp" /> + + + <property name="initialLaunch" column="initiallaunchdate" type="timestamp" /> + <property name="recentLaunch" column="recentlaunchdate" type="timestamp" /> + + <property name="visit" column="visit" type="int" /> + <property name="timeSpend" column="timespend" type="long" /> + + <many-to-one name="resource" + column="fk_resource_id" + foreign-key="none" + class="org.olat.resource.OLATResourceImpl" + outer-join="true" + unique="false" + not-found="ignore" + cascade="none"/> + + <many-to-one name="identity" + column="fk_identity" + foreign-key="cx_eff_statement_to_identity" + class="org.olat.basesecurity.IdentityImpl" + outer-join="true" + unique="false" + cascade="none"/> + + </class> +</hibernate-mapping> \ No newline at end of file diff --git a/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java new file mode 100644 index 00000000000..924eb7065bf --- /dev/null +++ b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java @@ -0,0 +1,103 @@ +package org.olat.course.assessment.model; + +import java.util.Date; + +import org.olat.core.commons.persistence.PersistentObject; +import org.olat.core.id.Identity; +import org.olat.core.id.ModifiedInfo; +import org.olat.course.assessment.UserCourseInformations; +import org.olat.resource.OLATResource; + +public class UserCourseInfosImpl extends PersistentObject implements UserCourseInformations, ModifiedInfo { + + private static final long serialVersionUID = -6933599547069673655L; + + private Date lastModified; + private Date initialLaunch; + private Date recentLaunch; + private int visit; + private long timeSpend; + + private Identity identity; + private OLATResource resource; + + @Override + public Date getLastModified() { + return lastModified; + } + + @Override + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } + + @Override + public Date getInitialLaunch() { + return initialLaunch; + } + + public void setInitialLaunch(Date initialLaunch) { + this.initialLaunch = initialLaunch; + } + + @Override + public Date getRecentLaunch() { + return recentLaunch; + } + + public void setRecentLaunch(Date recentLaunch) { + this.recentLaunch = recentLaunch; + } + + @Override + public int getVisit() { + return visit; + } + + public void setVisit(int visit) { + this.visit = visit; + } + + @Override + public long getTimeSpend() { + return timeSpend; + } + + public void setTimeSpend(long timeSpend) { + this.timeSpend = timeSpend; + } + + @Override + public Identity getIdentity() { + return identity; + } + + public void setIdentity(Identity identity) { + this.identity = identity; + } + + public OLATResource getResource() { + return resource; + } + + public void setResource(OLATResource resource) { + this.resource = resource; + } + + @Override + public int hashCode() { + return getKey() == null ? 9271 : getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof UserCourseInfosImpl) { + UserCourseInfosImpl prop = (UserCourseInfosImpl)obj; + return getKey() != null && getKey().equals(prop.getKey()); + } + return false; + } +} diff --git a/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml new file mode 100644 index 00000000000..d3f10f24324 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml @@ -0,0 +1,83 @@ +<?xml version="1.0"?> +<!DOCTYPE hibernate-mapping PUBLIC + "-//Hibernate/Hibernate Mapping DTD//EN" + "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-lazy="false"> + + <class name="org.olat.course.assessment.model.UserEfficiencyStatementImpl" table="o_as_eff_statement"> + <cache usage="read-write" /> + + <id name="key" type="long" column="id" unsaved-value="null"> + <generator class="hilo"/> + </id> + + <version name="version" access="field" column="version" type="int"/> + <property name="creationDate" column="creationdate" type="timestamp" /> + <property name="lastModified" column="lastmodified" type="timestamp" /> + + <property name="score" column="score" type="float" /> + <property name="passed" column="passed" type="boolean" /> + <property name="totalNodes" column="total_nodes" type="int" /> + <property name="attemptedNodes" column="attempted_nodes" type="int" /> + <property name="passedNodes" column="passed_nodes" type="int" /> + + <property name="title" column="course_title" length="255" type="string" /> + <property name="shortTitle" column="course_short_title" length="128" type="string" /> + <property name="courseRepoKey" column="course_repo_key" type="long" /> + <property name="statementXml" column="statement_xml" type="string" length="16777210" /> + + <many-to-one name="resource" + column="fk_resource_id" + foreign-key="none" + class="org.olat.resource.OLATResourceImpl" + outer-join="true" + unique="false" + not-found="ignore" + cascade="none"/> + + <many-to-one name="identity" + column="fk_identity" + foreign-key="cx_eff_statement_to_identity" + class="org.olat.basesecurity.IdentityImpl" + outer-join="true" + unique="false" + cascade="none"/> + </class> + + <class name="org.olat.course.assessment.model.UserEfficiencyStatementLight" table="o_as_eff_statement" mutable="false"> + <id name="key" type="long" column="id" unsaved-value="null"> + <generator class="hilo"/> + </id> + + <version name="version" access="field" column="version" type="int"/> + <property name="creationDate" column="creationdate" type="timestamp" /> + <property name="lastModified" column="lastmodified" type="timestamp" /> + + <property name="score" column="score" type="float" /> + <property name="passed" column="passed" type="boolean" /> + <property name="totalNodes" column="total_nodes" type="int" /> + <property name="attemptedNodes" column="attempted_nodes" type="int" /> + <property name="passedNodes" column="passed_nodes" type="int" /> + + <property name="shortTitle" column="course_short_title" length="128" type="string" /> + <property name="courseRepoKey" column="course_repo_key" type="long" /> + + <many-to-one name="resource" + column="fk_resource_id" + foreign-key="none" + class="org.olat.resource.OLATResourceImpl" + outer-join="true" + unique="false" + not-found="ignore" + cascade="none"/> + + <many-to-one name="identity" + column="fk_identity" + foreign-key="cx_eff_statement_to_identity" + class="org.olat.basesecurity.IdentityImpl" + outer-join="true" + unique="false" + cascade="none"/> + </class> + +</hibernate-mapping> \ No newline at end of file diff --git a/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java new file mode 100644 index 00000000000..ff13944b932 --- /dev/null +++ b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java @@ -0,0 +1,172 @@ +/** + * <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.model; + +import java.util.Date; + +import org.olat.core.commons.persistence.PersistentObject; +import org.olat.core.id.Identity; +import org.olat.core.id.ModifiedInfo; +import org.olat.course.assessment.UserEfficiencyStatement; +import org.olat.resource.OLATResource; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class UserEfficiencyStatementImpl extends PersistentObject implements UserEfficiencyStatement, ModifiedInfo { + + private static final long serialVersionUID = 2996458434418813284L; + + private Float score; + private Boolean passed; + private Integer totalNodes; + private Integer attemptedNodes; + private Integer passedNodes; + + private Identity identity; + private OLATResource resource; + + private String title; + private String shortTitle; + private Long courseRepoKey; + + private String statementXml; + + private Date lastModified; + + public Date getLastModified() { + return lastModified; + } + + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } + + public Float getScore() { + return score; + } + + public void setScore(Float score) { + this.score = score; + } + + public Boolean getPassed() { + return passed; + } + + public void setPassed(Boolean passed) { + this.passed = passed; + } + + public Integer getTotalNodes() { + return totalNodes; + } + + public void setTotalNodes(Integer totalNodes) { + this.totalNodes = totalNodes; + } + + public Integer getAttemptedNodes() { + return attemptedNodes; + } + + public void setAttemptedNodes(Integer attemptedNodes) { + this.attemptedNodes = attemptedNodes; + } + + public Integer getPassedNodes() { + return passedNodes; + } + + public void setPassedNodes(Integer passedNodes) { + this.passedNodes = passedNodes; + } + + public String getStatementXml() { + return statementXml; + } + + public void setStatementXml(String statementXml) { + this.statementXml = statementXml; + } + + public Identity getIdentity() { + return identity; + } + + public void setIdentity(Identity identity) { + this.identity = identity; + } + + public OLATResource getResource() { + return resource; + } + + public void setResource(OLATResource resource) { + this.resource = resource; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getShortTitle() { + return shortTitle; + } + + public void setShortTitle(String shortTitle) { + this.shortTitle = shortTitle; + } + + public Long getCourseRepoKey() { + return courseRepoKey; + } + + public void setCourseRepoKey(Long courseRepoKey) { + this.courseRepoKey = courseRepoKey; + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + public int hashCode() { + return getKey() == null ? -82654 : getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof UserEfficiencyStatementImpl) { + UserEfficiencyStatementImpl statement = (UserEfficiencyStatementImpl)obj; + return getKey() != null && getKey().equals(statement.getKey()); + } + return false; + } +} diff --git a/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java new file mode 100644 index 00000000000..f3b8f8d027b --- /dev/null +++ b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java @@ -0,0 +1,153 @@ +/** + * <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.model; + +import java.util.Date; + +import org.olat.core.commons.persistence.PersistentObject; +import org.olat.core.id.Identity; +import org.olat.core.id.ModifiedInfo; +import org.olat.course.assessment.UserEfficiencyStatement; +import org.olat.resource.OLATResource; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class UserEfficiencyStatementLight extends PersistentObject implements UserEfficiencyStatement, ModifiedInfo { + + private static final long serialVersionUID = 2996458434418813284L; + + private Float score; + private Boolean passed; + private Integer totalNodes; + private Integer attemptedNodes; + private Integer passedNodes; + + private Identity identity; + private OLATResource resource; + + private String shortTitle; + private Long courseRepoKey; + + private Date lastModified; + + public Date getLastModified() { + return lastModified; + } + + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } + + public Float getScore() { + return score; + } + + public void setScore(Float score) { + this.score = score; + } + + public Boolean getPassed() { + return passed; + } + + public void setPassed(Boolean passed) { + this.passed = passed; + } + + public Integer getTotalNodes() { + return totalNodes; + } + + public void setTotalNodes(Integer totalNodes) { + this.totalNodes = totalNodes; + } + + public Integer getAttemptedNodes() { + return attemptedNodes; + } + + public void setAttemptedNodes(Integer attemptedNodes) { + this.attemptedNodes = attemptedNodes; + } + + public Integer getPassedNodes() { + return passedNodes; + } + + public void setPassedNodes(Integer passedNodes) { + this.passedNodes = passedNodes; + } + + public Identity getIdentity() { + return identity; + } + + public void setIdentity(Identity identity) { + this.identity = identity; + } + + public OLATResource getResource() { + return resource; + } + + public void setResource(OLATResource resource) { + this.resource = resource; + } + + public String getShortTitle() { + return shortTitle; + } + + public void setShortTitle(String shortTitle) { + this.shortTitle = shortTitle; + } + + public Long getCourseRepoKey() { + return courseRepoKey; + } + + public void setCourseRepoKey(Long courseRepoKey) { + this.courseRepoKey = courseRepoKey; + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + public int hashCode() { + return getKey() == null ? -82654 : getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof UserEfficiencyStatementLight) { + UserEfficiencyStatementLight statement = (UserEfficiencyStatementLight)obj; + return getKey() != null && getKey().equals(statement.getKey()); + } + return false; + } +} diff --git a/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java b/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java index 7f8f01e66e9..b06c8f35972 100644 --- a/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java +++ b/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java @@ -20,13 +20,11 @@ package org.olat.course.condition.interpreter; -import org.olat.core.id.Identity; -import org.olat.course.ICourse; +import org.olat.core.CoreSpringFactory; +import org.olat.course.assessment.UserCourseInformations; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.editor.CourseEditorEnv; -import org.olat.course.nodes.CourseNode; -import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.properties.Property; /** * @@ -61,14 +59,10 @@ public class GetInitialCourseLaunchDateFunction extends AbstractFunction { } //the real function evaluation which is used during run time - CourseNode node = getUserCourseEnv().getCourseEnvironment().getRunStructure().getRootNode(); - CoursePropertyManager pm = getUserCourseEnv().getCourseEnvironment().getCoursePropertyManager(); - Identity identity = getUserCourseEnv().getIdentityEnvironment().getIdentity(); - - Property firstTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE); - if (firstTime != null) { - String firstTimeMillis = firstTime.getStringValue(); - return Double.valueOf(firstTimeMillis); + UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + UserCourseInformations infos = mgr.getUserCourseInformations(getUserCourseEnv().getCourseEnvironment().getCourseResourceableId(), getUserCourseEnv().getIdentityEnvironment().getIdentity()); + if (infos != null) { + return Double.valueOf(infos.getInitialLaunch().getTime()); } else { // what to do in case of no date available??? -> return date in the future return new Double(Double.POSITIVE_INFINITY); diff --git a/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java b/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java index 31e5fc51c62..5f98ae78c85 100644 --- a/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java +++ b/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java @@ -20,13 +20,11 @@ package org.olat.course.condition.interpreter; -import org.olat.core.id.Identity; -import org.olat.course.ICourse; +import org.olat.core.CoreSpringFactory; +import org.olat.course.assessment.UserCourseInformations; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.editor.CourseEditorEnv; -import org.olat.course.nodes.CourseNode; -import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.properties.Property; /** * @@ -59,14 +57,10 @@ public class GetRecentCourseLaunchDateFunction extends AbstractFunction { return defaultValue(); } - CourseNode node = getUserCourseEnv().getCourseEnvironment().getRunStructure().getRootNode(); - CoursePropertyManager pm = getUserCourseEnv().getCourseEnvironment().getCoursePropertyManager(); - Identity identity = getUserCourseEnv().getIdentityEnvironment().getIdentity(); - Property recentTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_RECENT_LAUNCH_DATE); - - if (recentTime != null) { - String firstTimeMillis = recentTime.getStringValue(); - return Double.valueOf(firstTimeMillis); + UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + UserCourseInformations infos = mgr.getUserCourseInformations(getUserCourseEnv().getCourseEnvironment().getCourseResourceableId(), getUserCourseEnv().getIdentityEnvironment().getIdentity()); + if (infos != null) { + return Double.valueOf(infos.getRecentLaunch().getTime()); } else { // what to do in case of no date available??? -> return date in the future return new Double(Double.POSITIVE_INFINITY); diff --git a/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java b/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java index c18bc90bb6c..c428f245482 100644 --- a/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java +++ b/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java @@ -25,8 +25,8 @@ package org.olat.course.condition.interpreter.score; -import org.olat.course.assessment.EfficiencyStatement; import org.olat.course.assessment.EfficiencyStatementManager; +import org.olat.course.assessment.UserEfficiencyStatement; import org.olat.course.condition.interpreter.AbstractFunction; import org.olat.course.condition.interpreter.ArgumentParseException; import org.olat.course.condition.interpreter.ConditionInterpreter; @@ -89,9 +89,9 @@ public class GetPassedWithCourseIdFunction extends AbstractFunction { * the real function evaluation which is used during run time */ EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance(); - EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity()); + UserEfficiencyStatement es = esm.getUserEfficiencyStatementLight(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity()); if (es == null) return defaultValue(); - Boolean passed = esm.getPassed(childId, es); + Boolean passed = es.getPassed(); if (passed == null) return defaultValue(); // finally check existing value return (passed.booleanValue() ? ConditionInterpreter.INT_TRUE : ConditionInterpreter.INT_FALSE); diff --git a/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java b/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java index 25a4d52af42..8f42f3af450 100644 --- a/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java +++ b/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java @@ -25,8 +25,8 @@ package org.olat.course.condition.interpreter.score; -import org.olat.course.assessment.EfficiencyStatement; import org.olat.course.assessment.EfficiencyStatementManager; +import org.olat.course.assessment.UserEfficiencyStatement; import org.olat.course.condition.interpreter.AbstractFunction; import org.olat.course.condition.interpreter.ArgumentParseException; import org.olat.course.editor.CourseEditorEnv; @@ -88,12 +88,12 @@ public class GetScoreWithCourseIdFunction extends AbstractFunction { */ EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance(); - EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity()); + UserEfficiencyStatement es = esm.getUserEfficiencyStatementLight(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity()); if (es == null) return defaultValue(); - Double score = esm.getScore(childId, es); + Float score = es.getScore(); if (score == null) return defaultValue(); // finally check existing value - return score; + return new Double(score.doubleValue()); } diff --git a/src/main/java/org/olat/course/run/RunMainController.java b/src/main/java/org/olat/course/run/RunMainController.java index e83aa118cab..e6fc0f8d458 100644 --- a/src/main/java/org/olat/course/run/RunMainController.java +++ b/src/main/java/org/olat/course/run/RunMainController.java @@ -35,6 +35,7 @@ import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.bookmark.AddAndEditBookmarkController; import org.olat.bookmark.BookmarkManager; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; import org.olat.core.commons.persistence.PersistenceHelper; @@ -93,10 +94,11 @@ import org.olat.course.archiver.IArchiverCallback; import org.olat.course.assessment.AssessmentChangedEvent; import org.olat.course.assessment.AssessmentUIFactory; import org.olat.course.assessment.CoachingGroupAccessAssessmentCallback; -import org.olat.course.assessment.EfficiencyStatement; import org.olat.course.assessment.EfficiencyStatementController; import org.olat.course.assessment.EfficiencyStatementManager; import org.olat.course.assessment.FullAccessAssessmentCallback; +import org.olat.course.assessment.UserEfficiencyStatement; +import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.config.CourseConfig; import org.olat.course.config.CourseConfigEvent; import org.olat.course.editor.PublishEvent; @@ -104,7 +106,6 @@ import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.groupsandrights.CourseRights; import org.olat.course.groupsandrights.ui.CourseGroupManagementMainController; import org.olat.course.nodes.CourseNode; -import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.calendar.CourseCalendarController; import org.olat.course.run.glossary.CourseGlossaryFactory; import org.olat.course.run.glossary.CourseGlossaryToolLinkController; @@ -122,7 +123,6 @@ import org.olat.instantMessaging.InstantMessagingModule; import org.olat.instantMessaging.groupchat.GroupChatManagerController; import org.olat.modules.cp.TreeNodeEvent; import org.olat.note.NoteController; -import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryStatus; import org.olat.repository.RepositoryManager; @@ -446,24 +446,8 @@ public class RunMainController extends MainLayoutBasicController implements Gene private void setLaunchDates(final Identity identity) { CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(createOLATResourceableForLocking(identity), new SyncerExecutor(){ public void execute() { - CourseNode rootNode = course.getRunStructure().getRootNode(); - //log launch date - String nowString = Long.toString(course.getCourseEnvironment().getCurrentTimeMillis()); - CoursePropertyManager cpm = course.getCourseEnvironment().getCoursePropertyManager(); - Property initialLaunchProperty = cpm.findCourseNodeProperty(rootNode, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE); - if(initialLaunchProperty == null) { - initialLaunchProperty = cpm.createCourseNodePropertyInstance(rootNode, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE, null, null, nowString, null); - cpm.saveProperty(initialLaunchProperty); - } - - Property recentLaunchProperty = cpm.findCourseNodeProperty(rootNode, identity, null, ICourse.PROPERTY_RECENT_LAUNCH_DATE); - if (recentLaunchProperty == null) { - recentLaunchProperty = cpm.createCourseNodePropertyInstance(rootNode, identity, null, ICourse.PROPERTY_RECENT_LAUNCH_DATE, null, null, nowString, null); - cpm.saveProperty(recentLaunchProperty); - } else { - recentLaunchProperty.setStringValue(nowString); - cpm.updateProperty(recentLaunchProperty); - } + UserCourseInformationsManager efficiencyStatementManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); + efficiencyStatementManager.updateUserCourseInformations(uce.getCourseEnvironment().getCourseResourceableId(), getIdentity()); } }); } @@ -1245,7 +1229,7 @@ public class RunMainController extends MainLayoutBasicController implements Gene myTool.addPopUpLink("efficiencystatement", translate("command.efficiencystatement"), "command.efficiencystatement", null, "750", "800", false); EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance(); - EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepositoryEntry.getKey(), identity); + UserEfficiencyStatement es = esm.getUserEfficiencyStatementLight(courseRepositoryEntry.getKey(), identity); if (es == null) { myTool.setEnabled("command.efficiencystatement", false); } diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java new file mode 100644 index 00000000000..12df950aaf4 --- /dev/null +++ b/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java @@ -0,0 +1,278 @@ +/** + * <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.upgrade; + +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.olat.basesecurity.BaseSecurity; +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.DBQuery; +import org.olat.core.id.Identity; +import org.olat.core.util.xml.XStreamHelper; +import org.olat.course.assessment.EfficiencyStatement; +import org.olat.course.assessment.EfficiencyStatementManager; +import org.olat.course.assessment.manager.UserCourseInformationsManager; +import org.olat.course.assessment.manager.UserCourseInformationsManagerImpl; +import org.olat.course.assessment.model.UserCourseInfosImpl; +import org.olat.course.assessment.model.UserEfficiencyStatementImpl; +import org.olat.properties.Property; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.resource.OLATResource; +import org.olat.resource.OLATResourceManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Description:<br> + * upgrade code for OLAT 7.1.0 -> OLAT 7.1.1 + * - fixing invalid structures being built by synchronisation, see OLAT-6316 and OLAT-6306 + * - merges all yet found data to last valid node + * + * <P> + * Initial Date: 24.03.2011 <br> + * + * @author Roman Haag, roman.haag@frentix.com, www.frentix.com + */ +public class OLATUpgrade_8_1_0 extends OLATUpgrade { + + private static final String TASK_EFFICIENCY_STATEMENT = "Upgrade efficiency statement"; + private static final String TASK_LAUNCH_DATES = "Upgrade launch dates"; + private static final int REPO_ENTRIES_BATCH_SIZE = 20; + private static final String VERSION = "OLAT_8.1.0"; + + private static final String PROPERTY_INITIAL_LAUNCH_DATE = "initialCourseLaunchDate"; + private static final String PROPERTY_RECENT_LAUNCH_DATE = "recentCourseLaunchDate"; + + @Autowired + private DB dbInstance; + @Autowired + private RepositoryManager repositoryManager; + @Autowired + private EfficiencyStatementManager efficiencyStatementManager; + @Autowired + private UserCourseInformationsManager userCourseInformationsManager; + @Autowired + private OLATResourceManager resourceManager; + @Autowired + private BaseSecurity securityManager; + + public OLATUpgrade_8_1_0() { + super(); + } + + @Override + public boolean doPreSystemInitUpgrade(UpgradeManager upgradeManager) { + return false; + } + + @Override + public boolean doPostSystemInitUpgrade(UpgradeManager upgradeManager) { + UpgradeHistoryData uhd = upgradeManager.getUpgradesHistory(VERSION); + if (uhd == null) { + // has never been called, initialize + uhd = new UpgradeHistoryData(); + } else { + if (uhd.isInstallationComplete()) { + return false; + } + } + + upgradeEfficiencyStatements(upgradeManager, uhd); + upgradeLaunchDates(upgradeManager, uhd); + + uhd.setInstallationComplete(true); + upgradeManager.setUpgradesHistory(uhd, VERSION); + log.audit("Finished OLATUpgrade_8_1_0 successfully!"); + return true; + } + + private void upgradeEfficiencyStatements(UpgradeManager upgradeManager, UpgradeHistoryData uhd) { + if (!uhd.getBooleanDataValue(TASK_EFFICIENCY_STATEMENT)) { + + int counter = 0; + List<Property> properties; + do { + properties = getEfficiencyStatement(counter); + for(Property property:properties) { + createStatement(property); + } + counter += properties.size(); + log.audit("Processed efficiency statement: " + properties.size()); + } while(properties.size() == REPO_ENTRIES_BATCH_SIZE); + + uhd.setBooleanDataValue(TASK_EFFICIENCY_STATEMENT, true); + upgradeManager.setUpgradesHistory(uhd, VERSION); + } + } + + private void createStatement(Property property) { + String repoKeyStr = property.getName(); + Long repoKey = new Long(repoKeyStr); + RepositoryEntry re = repositoryManager.lookupRepositoryEntry(repoKey, false); + UserEfficiencyStatementImpl impl = efficiencyStatementManager.getUserEfficiencyStatementFull(repoKey, property.getIdentity()); + if(impl != null) { + return; + } + + UserEfficiencyStatementImpl statement = new UserEfficiencyStatementImpl(); + statement.setIdentity(property.getIdentity()); + statement.setStatementXml(property.getTextValue()); + if(re != null) { + statement.setResource(re.getOlatResource()); + } + + EfficiencyStatement s = (EfficiencyStatement)XStreamHelper.createXStreamInstance().fromXML(property.getTextValue()); + efficiencyStatementManager.fillEfficiencyStatement(s, statement); + statement.setLastModified(property.getLastModified()); + + dbInstance.saveObject(statement); + dbInstance.commitAndCloseSession(); + } + + private List<Property> getEfficiencyStatement(int firstResult) { + StringBuilder query = new StringBuilder(); + query.append("select p from ").append(Property.class.getName()).append(" as p "); + query.append(" where p.category='efficiencyStatement' order by p.key"); + + DBQuery dbQuery = dbInstance.createQuery(query.toString()); + dbQuery.setFirstResult(firstResult); + dbQuery.setMaxResults(REPO_ENTRIES_BATCH_SIZE); + @SuppressWarnings("unchecked") + List<Property> props = dbQuery.list(); + return props; + } + + private void upgradeLaunchDates(UpgradeManager upgradeManager, UpgradeHistoryData uhd) { + if (!uhd.getBooleanDataValue(TASK_LAUNCH_DATES)) { + int count = 0; + Set<SimpleProp> props = getLaunchProperties(); + if(props == null) { + //problems + uhd.setBooleanDataValue(TASK_LAUNCH_DATES, false); + } else { + for(SimpleProp prop:props) { + Date d = ((UserCourseInformationsManagerImpl)userCourseInformationsManager).getInitialLaunchDate(prop.resourceId, prop.identityKey); + if(d == null) { + createUserCourseInformation(prop); + } + + if(count % 25 == 0) { + dbInstance.commitAndCloseSession(); + log.info("Convert lanch dates property: " + count); + } + } + uhd.setBooleanDataValue(TASK_LAUNCH_DATES, true); + } + upgradeManager.setUpgradesHistory(uhd, VERSION); + } + } + + private void createUserCourseInformation(SimpleProp prop) { + Identity identity = securityManager.loadIdentityByKey(prop.identityKey); + OLATResource resource = resourceManager.findResourceable(prop.resourceId, "CourseModule"); + + UserCourseInfosImpl infos = new UserCourseInfosImpl(); + infos.setIdentity(identity); + infos.setLastModified(new Date()); + infos.setInitialLaunch(prop.initialLaunch); + infos.setRecentLaunch(prop.recentLaunch); + infos.setVisit(1); + infos.setResource(resource); + dbInstance.saveObject(infos); + } + + private Set<SimpleProp> getLaunchProperties() { + + try { + StringBuilder query = new StringBuilder(); + query.append("select p.resourceTypeId, p.identity.key, p.name, p.stringValue from ").append(Property.class.getName()).append(" as p "); + query.append(" where p.resourceTypeName='CourseModule' and p.name in ('initialCourseLaunchDate','recentCourseLaunchDate')"); + + DBQuery dbQuery = dbInstance.createQuery(query.toString()); + @SuppressWarnings("unchecked") + List<Object[]> props = dbQuery.list(); + + Calendar cal = Calendar.getInstance(); + Map<SimpleProp, SimpleProp> simpleProps = new HashMap<SimpleProp, SimpleProp>((2 * props.size()) + 1); + for(Object[] prop:props) { + SimpleProp simpleProp = new SimpleProp(); + simpleProp.resourceId = (Long)prop[0]; + simpleProp.identityKey = (Long)prop[1]; + if(simpleProps.containsKey(simpleProp)) { + simpleProp = simpleProps.get(simpleProp); + } + + String name = (String)prop[2]; + try { + long time = Long.valueOf((String)prop[3]); + cal.setTimeInMillis(time); + if(PROPERTY_INITIAL_LAUNCH_DATE.equals(name)) { + simpleProp.initialLaunch = cal.getTime(); + }else if(PROPERTY_RECENT_LAUNCH_DATE.equals(name)) { + simpleProp.recentLaunch = cal.getTime(); + } + simpleProps.put(simpleProp, simpleProp); + } catch(Exception e) { + log.error("", e); + } + } + return simpleProps.keySet(); + } catch (Exception e) { + log.error("", e); + return null; + } + } + + @Override + public String getVersion() { + return VERSION; + } + + private class SimpleProp { + private Long resourceId; + private Long identityKey; + private Date initialLaunch; + private Date recentLaunch; + + @Override + public int hashCode() { + return (resourceId == null ? -1 : resourceId.hashCode()) + + (identityKey == null ? -1 : identityKey.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof SimpleProp) { + SimpleProp prop = (SimpleProp)obj; + return resourceId != null && resourceId.equals(prop.resourceId) + && identityKey != null && identityKey.equals(prop.identityKey); + } + return false; + } + } +} diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml index dd5776dd50a..2384349a336 100644 --- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml @@ -35,6 +35,10 @@ <constructor-arg index="0" value="OLAT_8.0.2" /> <property name="alterDbStatements" value="alter_8_0_x_to_8_0_2.sql" /> </bean> + <bean id="database_upgrade_8_1_0" class="org.olat.upgrade.DatabaseUpgrade"> + <constructor-arg index="0" value="OLAT_8.1.0" /> + <property name="alterDbStatements" value="alter_8_0_x_to_8_1_0.sql" /> + </bean> </list> </property> </bean> diff --git a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml index ddeecd14f9a..baf1d2385f0 100644 --- a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml @@ -37,6 +37,7 @@ <constructor-arg index="0" ref="portfolioModule" /> <property name="portfolioCourseNodeEnabled" value="${course.node.portfolio.enabled}"/> </bean> + <bean id="upgrade_8_1_0" class="org.olat.upgrade.OLATUpgrade_8_1_0"/> </list> </property> </bean> diff --git a/src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql b/src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql new file mode 100644 index 00000000000..8add8d747ab --- /dev/null +++ b/src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql @@ -0,0 +1,78 @@ +-- user view +create view o_bs_identity_short_v as ( + select + ident.id as id_id, + ident.name as id_name, + ident.lastlogin as id_lastlogin, + ident.status as id_status, + us.user_id as us_id, + p_firstname.propvalue as first_name, + p_lastname.propvalue as last_name, + p_email.propvalue as email + from o_bs_identity as ident + inner join o_user as us on (ident.fk_user_id = us.user_id) + left join o_userproperty as p_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName') + left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName') + left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email') +); + +-- assessment tables +-- efficiency statments +create table if not exists o_as_eff_statement ( + id bigint not null, + version mediumint unsigned not null, + lastmodified datetime, + creationdate datetime, + passed bit(0), + score bigint, + total_nodes bigint, + attempted_nodes bigint, + passed_nodes bigint, + course_title varchar(255), + course_short_title varchar(128), + course_repo_key bigint, + statement_xml longtext, + fk_identity bigint, + fk_resource_id bigint, + primary key (id) +); +alter table o_as_eff_statement add index eff_statement_id_cstr (fk_identity), add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id); +create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key); +create index eff_statement_resource_idx on o_as_eff_statement (fk_resource_id); + +-- user to course informations (was property initial and recent launch dates) +create table o_as_user_course_infos ( + id bigint not null, + version mediumint unsigned not null, + creationdate datetime, + lastmodified datetime, + initiallaunchdate datetime, + recentlaunchdate datetime, + visit bigint, + timespend bigint, + fk_identity bigint, + fk_resource_id bigint, + primary key (id) +); +alter table o_as_user_course_infos add index user_course_infos_id_cstr (fk_identity), add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id); +alter table o_as_user_course_infos add index user_course_infos_res_cstr (fk_resource_id), add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id); + +-- assessment results +-- help view +drop view if exists o_gp_contextresource_2_group_v; +create view o_gp_contextresource_2_group_v as ( + select + cg_bg2resource.groupcontextresource_id as groupcontextresource_id, + cg_bgcontext.groupcontext_id as groupcontext_id, + cg_bgroup.group_id as group_id, + cg_bg2resource.oresource_id as oresource_id, + cg_bgcontext.grouptype as grouptype, + cg_bgcontext.defaultcontext as defaultcontext, + cg_bgroup.groupname as groupname, + cg_bgroup.fk_ownergroup as fk_ownergroup, + cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup, + cg_bgroup.fk_waitinggroup as fk_waitinggroup + from o_gp_bgcontextresource_rel as cg_bg2resource + inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id) + inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk) +); diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index c65eff85a4d..8cb40cc21bf 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -907,6 +907,77 @@ create table if not exists o_ac_transaction ( primary key (transaction_id) ); +-- assessment tables +-- efficiency statments +create table if not exists o_as_eff_statement ( + id bigint not null, + version mediumint unsigned not null, + lastmodified datetime, + creationdate datetime, + passed bit(0), + score bigint, + total_nodes bigint, + attempted_nodes bigint, + passed_nodes bigint, + course_title varchar(255), + course_short_title varchar(128), + course_repo_key bigint, + statement_xml longtext, + fk_identity bigint, + fk_resource_id bigint, + primary key (id) +); + +-- user to course informations (was property initial and recent launch dates) +create table o_as_user_course_infos ( + id bigint not null, + version mediumint unsigned not null, + creationdate datetime, + lastmodified datetime, + initiallaunchdate datetime, + recentlaunchdate datetime, + visit bigint, + timespend bigint, + fk_identity bigint, + fk_resource_id bigint, + primary key (id) +); + +-- user view +create view o_bs_identity_short_v as ( + select + ident.id as id_id, + ident.name as id_name, + ident.lastlogin as id_lastlogin, + ident.status as id_status, + us.user_id as us_id, + p_firstname.propvalue as first_name, + p_lastname.propvalue as last_name, + p_email.propvalue as email + from o_bs_identity as ident + inner join o_user as us on (ident.fk_user_id = us.user_id) + left join o_userproperty as p_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName') + left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName') + left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email') +); + +create view o_gp_contextresource_2_group_v as ( + select + cg_bg2resource.groupcontextresource_id as groupcontextresource_id, + cg_bgcontext.groupcontext_id as groupcontext_id, + cg_bgroup.group_id as group_id, + cg_bg2resource.oresource_id as oresource_id, + cg_bgcontext.grouptype as grouptype, + cg_bgcontext.defaultcontext as defaultcontext, + cg_bgroup.groupname as groupname, + cg_bgroup.fk_ownergroup as fk_ownergroup, + cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup, + cg_bgroup.fk_waitinggroup as fk_waitinggroup + from o_gp_bgcontextresource_rel as cg_bg2resource + inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id) + inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk) +); + create index ocl_asset_idx on oc_lock (asset); alter table oc_lock add index FK9E30F4B66115906D (identity_fk), add constraint FK9E30F4B66115906D foreign key (identity_fk) references o_bs_identity (id); @@ -1122,6 +1193,14 @@ alter table o_tag add constraint FK6491FCA5A4FA5DC foreign key (fk_author_id) re alter table o_bs_invitation add constraint FKF26C8375236F27X foreign key (fk_secgroup) references o_bs_secgroup (id); +alter table o_as_eff_statement add index eff_statement_id_cstr (fk_identity), add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id); +create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key); +create index eff_statement_resource_idx on o_as_eff_statement (fk_resource_id); + +alter table o_as_user_course_infos add index user_course_infos_id_cstr (fk_identity), add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id); +alter table o_as_user_course_infos add index user_course_infos_res_cstr (fk_resource_id), add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id); + + insert into hibernate_unique_key values ( 0 ); -- the following are redundant indexes diff --git a/src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql b/src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql new file mode 100644 index 00000000000..d12ae4317a0 --- /dev/null +++ b/src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql @@ -0,0 +1,74 @@ +-- user view +create view o_bs_identity_short_v as ( + select + ident.id as id_id, + ident.name as id_name, + ident.lastlogin as id_lastlogin, + ident.status as id_status, + us.user_id as us_id, + p_firstname.propvalue as first_name, + p_lastname.propvalue as last_name, + p_email.propvalue as email + from o_bs_identity as ident + inner join o_user as us on (ident.fk_user_id = us.user_id) + left join o_userproperty as P_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName') + left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName') + left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email') +); + +-- efficiency statments +create table o_as_eff_statement ( + id int8 not null, + version int4 not null, + lastmodified timestamp, + creationdate timestamp, + passed boolean, + score decimal, + course_title varchar(255), + course_short_title varchar(128), + course_repo_key int8, + statement_xml text, + fk_identity int8, + fk_resource_id int8, + primary key (id) +); +alter table o_as_eff_statement add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id); +create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key); +create index eff_statement_ident_key_idx on o_as_eff_statement (fk_resource_id); + +-- user to course informations (was property initial and recent launch dates) +create table o_as_user_course_infos ( + id bigint not null, + version int4 not null, + creationdate timestamp, + lastmodified timestamp, + initiallaunchdate timestamp, + recentlaunchdate timestamp, + visit int4, + timespend int8, + fk_identity int8, + fk_resource_id int8, + primary key (id) +); +alter table o_as_user_course_infos add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id); +alter table o_as_user_course_infos add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id); + +-- assessment results +-- help view +create view o_gp_contextresource_2_group_v as ( + select + cg_bg2resource.groupcontextresource_id as groupcontextresource_id, + cg_bgcontext.groupcontext_id as groupcontext_id, + cg_bgroup.group_id as group_id, + cg_bg2resource.oresource_id as oresource_id, + cg_bgcontext.grouptype as grouptype, + cg_bgcontext.defaultcontext as defaultcontext, + cg_bgroup.groupname as groupname, + cg_bgroup.fk_ownergroup as fk_ownergroup, + cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup, + cg_bgroup.fk_waitinggroup as fk_waitinggroup + from o_gp_bgcontextresource_rel as cg_bg2resource + inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id) + inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk) +); + diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 9f201646dbe..f7e3ce84440 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -904,6 +904,75 @@ create table o_mark ( primary key (mark_id) ); +-- efficiency statments +create table o_as_eff_statement ( + id int8 not null, + version int4 not null, + lastmodified timestamp, + creationdate timestamp, + passed boolean, + score decimal, + course_title varchar(255), + course_short_title varchar(128), + course_repo_key int8, + statement_xml text, + fk_identity int8, + fk_resource_id int8, + primary key (id) +); + +-- user to course informations (was property initial and recent launch dates) +create table o_as_user_course_infos ( + id bigint not null, + version int4 not null, + creationdate timestamp, + lastmodified timestamp, + initiallaunchdate timestamp, + recentlaunchdate timestamp, + visit int4, + timespend int8, + fk_identity int8, + fk_resource_id int8, + primary key (id) +); + +-- user view +create view o_bs_identity_short_v as ( + select + ident.id as id_id, + ident.name as id_name, + ident.lastlogin as id_lastlogin, + ident.status as id_status, + us.user_id as us_id, + p_firstname.propvalue as first_name, + p_lastname.propvalue as last_name, + p_email.propvalue as email + from o_bs_identity as ident + inner join o_user as us on (ident.fk_user_id = us.user_id) + left join o_userproperty as P_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName') + left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName') + left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email') +); + +-- assessment results +-- help view +create view o_gp_contextresource_2_group_v as ( + select + cg_bg2resource.groupcontextresource_id as groupcontextresource_id, + cg_bgcontext.groupcontext_id as groupcontext_id, + cg_bgroup.group_id as group_id, + cg_bg2resource.oresource_id as oresource_id, + cg_bgcontext.grouptype as grouptype, + cg_bgcontext.defaultcontext as defaultcontext, + cg_bgroup.groupname as groupname, + cg_bgroup.fk_ownergroup as fk_ownergroup, + cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup, + cg_bgroup.fk_waitinggroup as fk_waitinggroup + from o_gp_bgcontextresource_rel as cg_bg2resource + inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id) + inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk) +); + create index userrating_id_idx on o_userrating (resid); create index userrating_name_idx on o_userrating (resname); create index userrating_subpath_idx on o_userrating (ressubpath); @@ -1058,5 +1127,12 @@ alter table o_ac_transaction add constraint trans_ord_ctx foreign key (fk_order_ alter table o_ac_transaction add constraint trans_ord_part_ctx foreign key (fk_order_part_id) references o_ac_order_part (order_part_id); alter table o_ac_transaction add constraint trans_method_ctx foreign key (fk_method_id) references o_ac_method (method_id); +alter table o_as_eff_statement add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id); +create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key); +create index eff_statement_ident_key_idx on o_as_eff_statement (fk_resource_id); + +alter table o_as_user_course_infos add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id); +alter table o_as_user_course_infos add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id); + insert into hibernate_unique_key values ( 0 ); -- GitLab