/**
* <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 static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.ASSESSMENT_ID;
import static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.ATTEMPTS;
import static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.COACH_COMMENT;
import static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.COMMENT;
import static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.FULLY_ASSESSED;
import static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.PASSED;
import static org.olat.upgrade.legacy.NewCachePersistingAssessmentManager.SCORE;
import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.olat.admin.user.imp.TransientIdentity;
import org.olat.basesecurity.GroupRoles;
import org.olat.basesecurity.IdentityRef;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
import org.olat.core.commons.persistence.DB;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.id.IdentityEnvironment;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.Roles;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.logging.activity.LoggingObject;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.cache.CacheWrapper;
import org.olat.core.util.io.SystemFileFilter;
import org.olat.core.util.nodes.INode;
import org.olat.core.util.resource.OresHelper;
import org.olat.core.util.tree.TreeVisitor;
import org.olat.core.util.tree.Visitor;
import org.olat.course.CorruptedCourseException;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.ICourse;
import org.olat.course.assessment.model.UserEfficiencyStatementLight;
import org.olat.course.nodes.AssessableCourseNode;
import org.olat.course.nodes.BasicLTICourseNode;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.GTACourseNode;
import org.olat.course.nodes.IQTESTCourseNode;
import org.olat.course.nodes.MSCourseNode;
import org.olat.course.nodes.PortfolioCourseNode;
import org.olat.course.nodes.STCourseNode;
import org.olat.course.nodes.ScormCourseNode;
import org.olat.course.nodes.TACourseNode;
import org.olat.course.nodes.gta.GTAManager;
import org.olat.course.nodes.gta.Task;
import org.olat.course.nodes.ta.DropboxController;
import org.olat.course.nodes.ta.ReturnboxController;
import org.olat.course.nodes.ta.StatusForm;
import org.olat.course.nodes.ta.StatusManager;
import org.olat.course.nodes.ta.TaskController;
import org.olat.course.run.scoring.ScoreCalculator;
import org.olat.course.run.userview.UserCourseEnvironmentImpl;
import org.olat.course.tree.CourseEditorTreeNode;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupService;
import org.olat.group.model.SearchBusinessGroupParams;
import org.olat.ims.qti.QTIModule;
import org.olat.ims.qti.QTIResultManager;
import org.olat.ims.qti.QTIResultSet;
import org.olat.ims.qti.editor.QTIEditorPackage;
import org.olat.ims.qti.editor.QTIEditorPackageImpl;
import org.olat.ims.qti.editor.beecom.objects.Assessment;
import org.olat.ims.qti.editor.beecom.objects.Item;
import org.olat.ims.qti.editor.beecom.objects.Section;
import org.olat.ims.qti.fileresource.TestFileResource;
import org.olat.ims.qti.process.FilePersister;
import org.olat.modules.assessment.AssessmentEntry;
import org.olat.modules.assessment.model.AssessmentEntryImpl;
import org.olat.modules.assessment.model.AssessmentEntryStatus;
import org.olat.modules.portfolio.PortfolioV2Module;
import org.olat.modules.scorm.assessment.CmiData;
import org.olat.modules.scorm.assessment.ScormAssessmentManager;
import org.olat.portfolio.PortfolioModule;
import org.olat.portfolio.manager.EPFrontendManager;
import org.olat.portfolio.model.structel.EPStructureElement;
import org.olat.portfolio.model.structel.PortfolioStructureMap;
import org.olat.portfolio.model.structel.StructureStatusEnum;
import org.olat.properties.Property;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryRef;
import org.olat.repository.RepositoryManager;
import org.olat.repository.RepositoryService;
import org.olat.repository.model.SearchRepositoryEntryParameters;
import org.olat.upgrade.legacy.NewCacheKey;
import org.olat.upgrade.legacy.NewCachePersistingAssessmentManager;
import org.springframework.beans.factory.annotation.Autowired;
import de.bps.onyx.plugin.OnyxModule;
/**
*
* Initial date: 27.03.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class OLATUpgrade_11_0_0 extends OLATUpgrade {
private static final int BATCH_SIZE = 50;
private static final String ASSESSMENT_DATAS = "ASSESSMENT PROPERTY TABLE";
private static final String EFFICIENCY_STATEMENT_DATAS = "EFFICIENCY STATEMENT TABLE";
private static final String PORTFOLIO_SETTINGS = "PORTFOLIO SETTINGS";
private static final String VERSION = "OLAT_11.0.0";
private final Map<Long,Boolean> qtiEssayMap = new HashMap<>();
@Autowired
private DB dbInstance;
@Autowired
private GTAManager gtaManager;
@Autowired
private EPFrontendManager ePFMgr;
@Autowired
private QTIResultManager qtiResultManager;
@Autowired
private RepositoryService repositoryService;
@Autowired
private RepositoryManager repositoryManager;
@Autowired
private BusinessGroupService businessGroupService;
@Autowired
private PortfolioModule portfolioModule;
@Autowired
private PortfolioV2Module portfolioV2Module;
public OLATUpgrade_11_0_0() {
super();
}
@Override
public String getVersion() {
return VERSION;
}
@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;
}
boolean allOk = true;
allOk &= upgradeEfficiencyStatementTable(upgradeManager, uhd);
allOk &= upgradeAssessmentPropertyTable(upgradeManager, uhd);
allOk &= upgradePortfolioSettings(upgradeManager, uhd);
uhd.setInstallationComplete(allOk);
upgradeManager.setUpgradesHistory(uhd, VERSION);
if(allOk) {
log.audit("Finished OLATUpgrade_11_0_0 successfully!");
} else {
log.audit("OLATUpgrade_11_0_0 not finished, try to restart OpenOLAT!");
}
return allOk;
}
private boolean upgradePortfolioSettings(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
boolean allOk = true;
if (!uhd.getBooleanDataValue(PORTFOLIO_SETTINGS)) {
if(portfolioModule.isEnabled()) {
portfolioV2Module.setEnabled(true);
boolean hasMaps = hasMap();
if(!hasMaps) {
portfolioModule.setEnabled(false);
}
} else {
boolean hasBinder = hasBinder();
if(!hasBinder) {
portfolioV2Module.setEnabled(false);
}
}
uhd.setBooleanDataValue(PORTFOLIO_SETTINGS, allOk);
upgradeManager.setUpgradesHistory(uhd, VERSION);
}
return allOk;
}
private boolean hasMap() {
try {
StringBuilder sb = new StringBuilder();
sb.append("select stEl.key from ").append(EPStructureElement.class.getName()).append(" stEl ");
List<Long> count = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Long.class)
.setFirstResult(0)
.setMaxResults(1)
.getResultList();
return count != null && count.size() > 0 && count.get(0) != null && count.get(0) >= 0;
} catch (Exception e) {
log.error("", e);
return true;
}
}
private boolean hasBinder() {
try {
StringBuilder sb = new StringBuilder();
sb.append("select binder.key from pfbinder as binder");
List<Long> count = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Long.class)
.setFirstResult(0)
.setMaxResults(1)
.getResultList();
return count != null && count.size() > 0 && count.get(0) != null && count.get(0) >= 0;
} catch (Exception e) {
log.error("", e);
return true;
}
}
private boolean upgradeEfficiencyStatementTable(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
boolean allOk = true;
if (!uhd.getBooleanDataValue(EFFICIENCY_STATEMENT_DATAS)) {
int counter = 0;
final Roles roles = new Roles(true, true, true, true, false, true, false);
final SearchRepositoryEntryParameters params = new SearchRepositoryEntryParameters();
params.setRoles(roles);
params.setResourceTypes(Collections.singletonList("CourseModule"));
List<RepositoryEntry> courses;
do {
courses = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, 50, true);
for(RepositoryEntry course:courses) {
try {
convertUserEfficiencyStatemen(course);
} catch (CorruptedCourseException e) {
log.error("Corrupted course: " + course.getKey(), e);
}
}
counter += courses.size();
log.audit("Efficiency statement data migration processed: " + courses.size() + ", total courses processed (" + counter + ")");
dbInstance.commitAndCloseSession();
} while(courses.size() == BATCH_SIZE);
uhd.setBooleanDataValue(EFFICIENCY_STATEMENT_DATAS, allOk);
upgradeManager.setUpgradesHistory(uhd, VERSION);
}
return allOk;
}
private boolean upgradeAssessmentPropertyTable(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
boolean allOk = true;
if (!uhd.getBooleanDataValue(ASSESSMENT_DATAS)) {
int counter = 0;
final Roles roles = new Roles(true, true, true, true, false, true, false);
final SearchRepositoryEntryParameters params = new SearchRepositoryEntryParameters();
params.setRoles(roles);
params.setResourceTypes(Collections.singletonList("CourseModule"));
List<RepositoryEntry> courses;
do {
courses = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, 50, true);
for(RepositoryEntry course:courses) {
try {
allOk &= processCourseAssessmentData(course);
} catch (CorruptedCourseException e) {
log.error("Corrupted course: " + course.getKey(), e);
}
}
counter += courses.size();
log.audit("Assessment data migration processed: " + courses.size() + ", total courses processed (" + counter + ")");
dbInstance.commitAndCloseSession();
} while(courses.size() == BATCH_SIZE);
uhd.setBooleanDataValue(ASSESSMENT_DATAS, allOk);
upgradeManager.setUpgradesHistory(uhd, VERSION);
}
return allOk;
}
private void convertUserEfficiencyStatemen(RepositoryEntry courseEntry) {
try {
final ICourse course = CourseFactory.loadCourse(courseEntry);
CourseNode rootNode = course.getRunStructure().getRootNode();
Set<Long> identityKeys = new HashSet<>(loadIdentityKeyOfAssessmentEntries(courseEntry, rootNode.getIdent()));
int count = 0;
List<UserEfficiencyStatementLight> statements = getUserEfficiencyStatements(courseEntry);
for(UserEfficiencyStatementLight statement:statements) {
Identity identity = statement.getIdentity();
if(!identityKeys.contains(identity.getKey())) {
AssessmentEntry entry = createAssessmentEntry(identity, null, course, courseEntry, rootNode.getIdent());
if(statement.getScore() != null) {
entry.setScore(new BigDecimal(statement.getScore()));
}
if(statement.getPassed() != null) {
entry.setPassed(statement.getPassed());
}
dbInstance.getCurrentEntityManager().persist(entry);
if(count++ % 25 == 0) {
dbInstance.commitAndCloseSession();
}
}
}
} catch (Exception e) {
log.error("Error with " + courseEntry.getKey() + " " + courseEntry, e);
}
dbInstance.commitAndCloseSession();
}
public List<UserEfficiencyStatementLight> getUserEfficiencyStatements(RepositoryEntryRef courseRepoEntry) {
StringBuilder sb = new StringBuilder();
sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ")
.append(" inner join fetch statement.identity as ident")
.append(" where statement.courseRepoKey=:repoKey");
return dbInstance.getCurrentEntityManager()
.createQuery(sb.toString(), UserEfficiencyStatementLight.class)
.setParameter("repoKey", courseRepoEntry.getKey())
.getResultList();
}
// select count(*) from o_property where name in ('SCORE','PASSED','ATTEMPTS','COMMENT','COACH_COMMENT','ASSESSMENT_ID','FULLY_ASSESSED');
private boolean processCourseAssessmentData(RepositoryEntry courseEntry) {
boolean allOk = true;
try {
final Long courseResourceId = courseEntry.getOlatResource().getResourceableId();
final ICourse course = CourseFactory.loadCourse(courseEntry);
//load all assessable identities
List<Identity> assessableIdentities = getAllAssessableIdentities(course, courseEntry);
Map<AssessmentDataKey,AssessmentEntryImpl> curentNodeAssessmentMap = new HashMap<>();
{//load already migrated data
List<AssessmentEntryImpl> currentNodeAssessmentList = loadAssessmentEntries(courseEntry);
for(AssessmentEntryImpl currentNodeAssessment:currentNodeAssessmentList) {
AssessmentDataKey key = new AssessmentDataKey(currentNodeAssessment.getIdentity().getKey(), courseResourceId, currentNodeAssessment.getSubIdent());
curentNodeAssessmentMap.put(key, currentNodeAssessment);
}
}
Map<AssessmentDataKey,AssessmentEntryImpl> nodeAssessmentMap = new HashMap<>();
{//processed properties
List<Property> courseProperties = loadAssessmentProperties(courseEntry);
for(Property property:courseProperties) {
String propertyCategory = property.getCategory();
if(StringHelper.containsNonWhitespace(propertyCategory)) {
int nodeIdentIndex = propertyCategory.indexOf("::");
if(nodeIdentIndex > 0) {
String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2);
AssessmentDataKey key = new AssessmentDataKey(property.getIdentity().getKey(), property.getResourceTypeId(), nodeIdent);
if(curentNodeAssessmentMap.containsKey(key)) {
continue;
}
AssessmentEntryImpl nodeAssessment;
if(nodeAssessmentMap.containsKey(key)) {
nodeAssessment = nodeAssessmentMap.get(key);
if(nodeAssessment.getCreationDate().after(property.getCreationDate())) {
nodeAssessment.setCreationDate(property.getCreationDate());
}
if(nodeAssessment.getLastModified().before(property.getLastModified())) {
nodeAssessment.setLastModified(property.getLastModified());
}
} else {
nodeAssessment = createAssessmentEntry(property.getIdentity(), property, course, courseEntry, nodeIdent);
}
copyAssessmentProperty(property, nodeAssessment, course);
nodeAssessmentMap.put(key, nodeAssessment);
}
}
}
}
//check the transient qti ser
CourseNode rootNode = course.getRunStructure().getRootNode();
new TreeVisitor(new Visitor() {
@Override
public void visit(INode node) {
if(node instanceof AssessableCourseNode) {
processNonPropertiesStates(assessableIdentities, (AssessableCourseNode)node, course, courseEntry,
nodeAssessmentMap, curentNodeAssessmentMap);
}
}
}, rootNode, true).visitAll();
dbInstance.commitAndCloseSession();
int count = 0;
for(AssessmentEntryImpl courseNodeAssessment:nodeAssessmentMap.values()) {
dbInstance.getCurrentEntityManager().persist(courseNodeAssessment);
if(++count % 50 == 0) {
dbInstance.commit();
}
}
dbInstance.commitAndCloseSession();
allOk = verifyCourseAssessmentData(assessableIdentities, courseEntry);
dbInstance.commitAndCloseSession();
if(allOk) {
List<STCourseNode> nodes = hasAssessableSTCourseNode(course);
if(nodes.size() > 0) {
log.info("Has assessables ST nodes");
for(Identity identity:assessableIdentities) {
IdentityEnvironment identityEnv = new IdentityEnvironment(identity, null);
UserCourseEnvironmentImpl userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment());
userCourseEnv.getScoreAccounting().evaluateAll(true);
dbInstance.commit();
}
}
}
} catch (Exception e) {
log.error("", e);
}
return allOk;
}
private void processNonPropertiesStates(List<Identity> assessableIdentities, AssessableCourseNode cNode,
ICourse course, RepositoryEntry courseEntry, Map<AssessmentDataKey,AssessmentEntryImpl> nodeAssessmentMap,
Map<AssessmentDataKey,AssessmentEntryImpl> curentNodeAssessmentMap) {
if(cNode instanceof IQTESTCourseNode) {
processNonPropertiesIQTESTStates(assessableIdentities, (IQTESTCourseNode)cNode, course, courseEntry, nodeAssessmentMap, curentNodeAssessmentMap);
} else if(cNode instanceof TACourseNode) {
processNonPropertiesTAStates(assessableIdentities, (TACourseNode)cNode, course, courseEntry, nodeAssessmentMap, curentNodeAssessmentMap);
}
}
/**
* Find if someone dropped a file in a Task element without task assignment, or has a returned
* document.
*
* @param assessableIdentities
* @param tNode
* @param course
* @param courseEntry
* @param nodeAssessmentMap
*/
private void processNonPropertiesTAStates(List<Identity> assessableIdentities, TACourseNode tNode,
ICourse course, RepositoryEntry courseEntry, Map<AssessmentDataKey,AssessmentEntryImpl> nodeAssessmentMap,
Map<AssessmentDataKey,AssessmentEntryImpl> curentNodeAssessmentMap) {
for(Identity assessedIdentity:assessableIdentities) {
AssessmentDataKey key = new AssessmentDataKey(assessedIdentity, course.getResourceableId(), tNode.getIdent());
if(curentNodeAssessmentMap.containsKey(key)) {
continue;
}
AssessmentEntryImpl nodeAssessment;
if(!nodeAssessmentMap.containsKey(key)) {
nodeAssessment = createAssessmentEntry(assessedIdentity, null, course, courseEntry, tNode.getIdent());
nodeAssessmentMap.put(key, nodeAssessment);
String dropbox = DropboxController.getDropboxPathRelToFolderRoot(course.getCourseEnvironment(), tNode) + File.separator + assessedIdentity.getName();
OlatRootFolderImpl dropBox = new OlatRootFolderImpl(dropbox, null);
if (dropBox.getBasefile().exists() && dropBox.getBasefile().listFiles(SystemFileFilter.FILES_ONLY).length > 0) {
nodeAssessment.setAssessmentStatus(AssessmentEntryStatus.inProgress);
} else {
String returnbox = ReturnboxController.getReturnboxPathRelToFolderRoot(course.getCourseEnvironment(), tNode) + File.separator + assessedIdentity.getName();
OlatRootFolderImpl returnBox = new OlatRootFolderImpl(returnbox, null);
if (returnBox.getBasefile().exists() && returnBox.getBasefile().listFiles(SystemFileFilter.FILES_ONLY).length > 0) {
nodeAssessment.setAssessmentStatus(AssessmentEntryStatus.inProgress);
}
}
}
}
}
/**
* Find if someone has started a test without getting a score, passed status...
*
* @param assessableIdentities
* @param iqNode
* @param course
* @param courseEntry
* @param nodeAssessmentMap
*/
private void processNonPropertiesIQTESTStates(List<Identity> assessableIdentities, IQTESTCourseNode iqNode,
ICourse course, RepositoryEntry courseEntry, Map<AssessmentDataKey,AssessmentEntryImpl> nodeAssessmentMap,
Map<AssessmentDataKey,AssessmentEntryImpl> curentNodeAssessmentMap) {
for(Identity assessedIdentity:assessableIdentities) {
if(iqTestPersisterExists(assessedIdentity, iqNode, course)) {
AssessmentDataKey key = new AssessmentDataKey(assessedIdentity, course.getResourceableId(), iqNode.getIdent());
if(curentNodeAssessmentMap.containsKey(key)) {
continue;
}
AssessmentEntryImpl nodeAssessment;
if(nodeAssessmentMap.containsKey(key)) {
nodeAssessment = nodeAssessmentMap.get(key);
} else {
nodeAssessment = createAssessmentEntry(assessedIdentity, null, course, courseEntry, iqNode.getIdent());
nodeAssessmentMap.put(key, nodeAssessment);
Long courseResourceableId = course.getResourceableId();
String resourcePath = courseResourceableId + File.separator + iqNode.getIdent();
FilePersister qtiPersister = new FilePersister(assessedIdentity, resourcePath);
nodeAssessment.setCreationDate(qtiPersister.getLastModified());
nodeAssessment.setLastModified(qtiPersister.getLastModified());
}
nodeAssessment.setAssessmentStatus(AssessmentEntryStatus.inProgress);
}
}
}
private List<STCourseNode> hasAssessableSTCourseNode(ICourse course) {
List<STCourseNode> assessableSTNodes = new ArrayList<>();
CourseNode rootNode = course.getRunStructure().getRootNode();
new TreeVisitor(new Visitor() {
@Override
public void visit(INode node) {
if(node instanceof STCourseNode) {
STCourseNode stNode = (STCourseNode)node;
ScoreCalculator calculator = stNode.getScoreCalculator();
if(StringHelper.containsNonWhitespace(calculator.getPassedExpression())) {
assessableSTNodes.add(stNode);
} else if(StringHelper.containsNonWhitespace(calculator.getScoreExpression())) {
assessableSTNodes.add(stNode);
}
}
}
}, rootNode, true).visitAll();
return assessableSTNodes;
}
/**
* The method compare the content of the cache used in assessment tool
* with the migrated values and vice versa.
*
* @param courseEntry
* @return
*/
private boolean verifyCourseAssessmentData(List<Identity> assessableIdentities, RepositoryEntry courseEntry) {
//load the cache and fill it with the same amount of datas as in assessment tool
final ICourse course = CourseFactory.loadCourse(courseEntry);
final Long courseResourceableId = course.getResourceableId();
StaticCacheWrapper cache = new StaticCacheWrapper();
NewCachePersistingAssessmentManager assessmentManager = new NewCachePersistingAssessmentManager(course, cache);
assessmentManager.preloadCache(assessableIdentities);
dbInstance.commitAndCloseSession();
Set<Identity> assessableIdentitySet = new HashSet<>(assessableIdentities);
List<AssessmentEntryImpl> nodeAssessments = loadAssessmentEntries(courseEntry);
Map<AssessmentDataKey, AssessmentEntryImpl> nodeAssessmentMap = new HashMap<>();
for(AssessmentEntryImpl nodeAssessment:nodeAssessments) {
if(!assessableIdentitySet.contains(nodeAssessment.getIdentity())) {
assessmentManager.preloadCache(nodeAssessment.getIdentity());
}
if(nodeAssessment.getIdentity() != null && nodeAssessment.getRepositoryEntry() != null
&& nodeAssessment.getRepositoryEntry().getOlatResource() != null) {
nodeAssessmentMap.put(new AssessmentDataKey(nodeAssessment), nodeAssessment);
}
}
dbInstance.commitAndCloseSession();
//compare the value in CourseNodeAssessment with the content of the cache
boolean allOk = true;
for(AssessmentEntryImpl nodeAssessment:nodeAssessments) {
allOk &= compareCourseNodeAssessment(nodeAssessment, assessmentManager, course, courseEntry);
}
dbInstance.commitAndCloseSession();
//compare the content of the cache with the CourseNodeAssessments
for(NewCacheKey cacheKey:cache.getKeys()) {
Map<String,Serializable> dataMap = cache.get(cacheKey);
Long identityKey = cacheKey.getIdentityKey();
for(Map.Entry<String, Serializable> data:dataMap.entrySet()) {
String key = data.getKey();
int index = key.indexOf("_");
if(index > 0 && !key.equals("LAST_MODIFIED")) {
String nodeIdent = key.substring(0, index);
String dataType = key.substring(index + 1);
AssessmentDataKey assessmentDataKey = new AssessmentDataKey(identityKey, courseResourceableId, nodeIdent);
AssessmentEntryImpl nodeAssessment = nodeAssessmentMap.get(assessmentDataKey);
allOk &= compareProperty(dataType, data.getValue(), nodeAssessment, courseEntry, nodeIdent, identityKey);
}
}
}
dbInstance.commitAndCloseSession();
if(!allOk) {
log.error("Critical error during course verification: " + courseEntry.getDisplayname() + "(" + courseEntry.getKey() + ")");
}
return allOk;
}
private boolean compareProperty(String dataType, Serializable value, AssessmentEntryImpl nodeAssessment, RepositoryEntry courseEntry, String nodeIdent, Long identityKey) {
boolean allOk = true;
if(nodeAssessment == null) {
log.audit("ERROR nodeAssessment not found: " + getErrorAt(courseEntry, nodeIdent, identityKey));
allOk = false;
} else if(ATTEMPTS.equals(dataType)) {
if((value == null && nodeAssessment.getAttempts() == null) ||
(value != null && nodeAssessment.getAttempts() != null && value.equals(nodeAssessment.getAttempts()))) {
//ok
} else {
log.audit("ERROR number of attempts: " + value + " / " + nodeAssessment.getAttempts() + getErrorAt(courseEntry, nodeIdent, identityKey));
allOk &= false;
}
} else if(PASSED.equals(dataType)) {
if((value == null && nodeAssessment.getPassed() == null) ||
(value != null && nodeAssessment.getPassed() != null && value.equals(nodeAssessment.getPassed()))) {
//ok
} else {
log.audit("ERROR passed: " + value + " / " + nodeAssessment.getPassed() + getErrorAt(courseEntry, nodeIdent, identityKey));
allOk &= false;
}
} else if(FULLY_ASSESSED.equals(dataType)) {
if((value == null && nodeAssessment.getFullyAssessed() == null) ||
(value != null && nodeAssessment.getFullyAssessed() != null && value.equals(nodeAssessment.getFullyAssessed()))) {
//ok
} else {
log.audit("ERROR fullyAssessed: " + value + " / " + nodeAssessment.getFullyAssessed() + getErrorAt(courseEntry, nodeIdent, identityKey));
allOk &= false;
}
} else if(SCORE.equals(dataType)) {
if((value == null && nodeAssessment.getScore() == null) ||
(value instanceof Float && nodeAssessment.getScore() != null && Math.abs(((Float)value).floatValue() - nodeAssessment.getScore().floatValue()) < 0.00001f)) {
//ok
} else {
log.audit("ERROR score: " + value + " / " + nodeAssessment.getScore() + getErrorAt(courseEntry, nodeIdent, identityKey));
allOk &= false;
}
}
return allOk;
}
private String getErrorAt(RepositoryEntry courseEntry, String nodeIdent, Long identityKey) {
return " at course : " + courseEntry.getDisplayname() + " (" + courseEntry.getKey() + ")"
+ " at node: " + nodeIdent + " for identity: " + identityKey;
}
private boolean compareCourseNodeAssessment(AssessmentEntryImpl entry, NewCachePersistingAssessmentManager assessmentManager,
ICourse course, RepositoryEntry courseEntry) {
CourseNode node = course.getRunStructure().getNode(entry.getSubIdent());
if(node == null) {
CourseEditorTreeNode editorNode = course.getEditorTreeModel().getCourseEditorNodeById(entry.getSubIdent());
if(editorNode != null) {
node = editorNode.getCourseNode();
}
}
boolean allOk = true;
if(node instanceof AssessableCourseNode && !(node instanceof STCourseNode)) {
Identity assessedIdentity = entry.getIdentity();
Integer attempts = assessmentManager.getNodeAttempts(node, assessedIdentity);
if((attempts == null && entry.getAttempts() == null) ||
(attempts != null && entry.getAttempts() != null && attempts.equals(entry.getAttempts()))) {
//ok
} else {
log.audit("ERROR number of attempts: " + attempts + " / " + entry.getAttempts() + getErrorAt(courseEntry, node));
allOk &= false;
}
Boolean passed = assessmentManager.getNodePassed(node, assessedIdentity);
if((passed == null && entry.getPassed() == null) ||
(passed != null && entry.getPassed() != null && passed.equals(entry.getPassed()))) {
//ok
} else {
log.audit("ERROR passed: " + passed + " / " + entry.getPassed() + getErrorAt(courseEntry, node));
allOk &= false;
}
Boolean fullyAssessed = assessmentManager.getNodeFullyAssessed(node, assessedIdentity);
if((fullyAssessed == null && entry.getFullyAssessed() == null) ||
(fullyAssessed != null && entry.getFullyAssessed() != null && fullyAssessed.equals(entry.getFullyAssessed()))) {
//ok
} else {
log.audit("ERROR fullyAssessed: " + fullyAssessed + " / " + entry.getFullyAssessed() + getErrorAt(courseEntry, node));
allOk &= false;
}
Float score = assessmentManager.getNodeScore(node, assessedIdentity);
if((score == null && entry.getScore() == null) ||
(score != null && entry.getScore() != null && Math.abs(score.floatValue() - entry.getScore().floatValue()) < 0.00001f)) {
//ok
} else {
log.audit("ERROR score: " + score + " / " + entry.getScore() + getErrorAt(courseEntry, node));
allOk &= false;
}
}
return allOk;
}
private String getErrorAt(RepositoryEntry courseEntry, CourseNode courseNode) {
return " at course : " + courseEntry.getDisplayname() + " (" + courseEntry.getKey() + ")"
+ " at node: " + courseNode.getShortTitle() + " (" + courseNode.getIdent() + ")";
}
/**
* Return the same amount of user as in the assessment tool.
* @param entry
* @return
*/
private List<Identity> getAllAssessableIdentities(ICourse course, RepositoryEntry entry) {
Set<Identity> duplicateKiller = new HashSet<>();
List<Identity> assessableIdentities = new ArrayList<>();
SearchBusinessGroupParams params = new SearchBusinessGroupParams();
List<BusinessGroup> coachedGroups = businessGroupService.findBusinessGroups(params, entry, 0, -1);
List<Identity> participants = businessGroupService.getMembers(coachedGroups, GroupRoles.participant.name());
for(Identity participant:participants) {
if(!duplicateKiller.contains(participant)) {
assessableIdentities.add(participant);
duplicateKiller.add(participant);
}
}
List<Identity> courseParticipants = repositoryService.getMembers(entry, GroupRoles.participant.name());
for(Identity participant:courseParticipants) {
if(!duplicateKiller.contains(participant)) {
assessableIdentities.add(participant);
duplicateKiller.add(participant);
}
}
List<Identity> assessedUsers = getAllIdentitiesWithCourseAssessmentData(course.getResourceableId());
for(Identity assessedUser:assessedUsers) {
if(!duplicateKiller.contains(assessedUser)) {
assessableIdentities.add(assessedUser);
duplicateKiller.add(assessedUser);
}
}
return assessableIdentities;
}
private List<Identity> getAllIdentitiesWithCourseAssessmentData(Long resourceId) {
StringBuilder query = new StringBuilder();
query.append("select p.identity from ")
.append(Property.class.getName()).append(" as p")
.append(" where p.resourceTypeName = 'CourseModule'")
.append(" and p.resourceTypeId = :resid")
.append(" and p.identity is not null")
.append(" and p.name in ('").append(SCORE).append("','").append(PASSED).append("')");
return dbInstance.getCurrentEntityManager()
.createQuery(query.toString(), Identity.class)
.setParameter("resid", resourceId)
.getResultList();
}
private AssessmentEntryImpl createAssessmentEntry(Identity assessedIdentity, Property property, ICourse course, RepositoryEntry courseEntry, String nodeIdent) {
AssessmentEntryImpl entry = new AssessmentEntryImpl();
if(property == null) {
entry.setCreationDate(new Date());
entry.setLastModified(entry.getCreationDate());
} else {
entry.setCreationDate(property.getCreationDate());
entry.setLastModified(property.getLastModified());
}
entry.setIdentity(assessedIdentity);
entry.setRepositoryEntry(courseEntry);
entry.setSubIdent(nodeIdent);
entry.setAttempts(new Integer(0));
entry.setUserVisibility(Boolean.TRUE);
CourseNode courseNode = course.getRunStructure().getNode(nodeIdent);
if(courseNode != null) {
if(courseNode.needsReferenceToARepositoryEntry()) {
RepositoryEntry referenceEntry = courseNode.getReferencedRepositoryEntry();
entry.setReferenceEntry(referenceEntry);
}
if(courseNode instanceof GTACourseNode) {
processAssessmentPropertyForGTA(assessedIdentity, entry, (GTACourseNode)courseNode, courseEntry);
} else if(courseNode instanceof TACourseNode) {
processAssessmentPropertyForTA(assessedIdentity, entry, (TACourseNode)courseNode, course);
} else if(courseNode instanceof IQTESTCourseNode) {
processAssessmentPropertyForIQTEST(assessedIdentity, entry, (IQTESTCourseNode)courseNode, course);
} else if(courseNode instanceof PortfolioCourseNode) {
processAssessmentPropertyForPortfolio(assessedIdentity, entry, (PortfolioCourseNode)courseNode, course);
} else if(courseNode instanceof MSCourseNode) {
entry.setAssessmentStatus(AssessmentEntryStatus.inReview);
} else if(courseNode instanceof BasicLTICourseNode) {
processAssessmentPropertyForBasicLTI(assessedIdentity, entry, (BasicLTICourseNode)courseNode, course);
} else if(courseNode instanceof ScormCourseNode) {
String username = assessedIdentity.getName();
Map<Date, List<CmiData>> rawDatas = ScormAssessmentManager.getInstance()
.visitScoDatasMultiResults(username, course.getCourseEnvironment(), (ScormCourseNode)courseNode);
if(rawDatas != null && rawDatas.size() > 0) {
entry.setAssessmentStatus(AssessmentEntryStatus.inProgress);
} else {
entry.setAssessmentStatus(AssessmentEntryStatus.notStarted);
}
}
}
return entry;
}
private void processAssessmentPropertyForBasicLTI(Identity assessedIdentity, AssessmentEntryImpl entry, BasicLTICourseNode cNode, ICourse course) {
List<LoggingObject> objects = getLoggingObject(assessedIdentity, cNode, course);
if(objects.size() > 0) {
entry.setAssessmentStatus(AssessmentEntryStatus.inProgress);
} else {
entry.setAssessmentStatus(AssessmentEntryStatus.notStarted);
}
}
/**
* Use the log of the navigation handler of the course to know if the user
* launched the specified course element.
*
* @param user
* @param courseNode
* @param course
* @return
*/
private List<LoggingObject> getLoggingObject(IdentityRef user, CourseNode courseNode, ICourse course) {
StringBuilder query = new StringBuilder();
query.append("select log from ").append(LoggingObject.class.getName()).append(" log")
.append(" where log.userId=:userId and sourceclass='org.olat.course.run.navigation.NavigationHandler'")
.append(" and parentResType='CourseModule' and parentResId=:courseId")
.append(" and targetResId=:targetResId")
.append(" and actionVerb='launch' and actionObject='node'");
return dbInstance.getCurrentEntityManager()
.createQuery(query.toString(), LoggingObject.class)
.setParameter("userId", user.getKey())
.setParameter("courseId", course.getResourceableId().toString())
.setParameter("targetResId", courseNode.getIdent())
.getResultList();
}
/**
* If a QTI ser is found, the test is in progress. If not, check if some result set is available
* and if the test has essay to set the status as inReview or done.
*
* @param assessedIdentity
* @param entry
* @param cNode
* @param course
*/
private void processAssessmentPropertyForIQTEST(Identity assessedIdentity, AssessmentEntryImpl entry, IQTESTCourseNode cNode, ICourse course) {
entry.setAssessmentStatus(AssessmentEntryStatus.notStarted);
if(iqTestPersisterExists(assessedIdentity, cNode, course)) {
entry.setAssessmentStatus(AssessmentEntryStatus.inProgress);
} else {
RepositoryEntry ref = cNode.getReferencedRepositoryEntry();
if(ref != null) {
Long courseResourceableId = course.getResourceableId();
List<QTIResultSet> resultSets = qtiResultManager.getResultSets(courseResourceableId, cNode.getIdent(), ref.getKey(), assessedIdentity);
if(resultSets.size() > 0) {
if(OnyxModule.isOnyxTest(ref.getOlatResource())) {
//make it later with the flag fully assessed
entry.setAssessmentStatus(AssessmentEntryStatus.inProgress);
} else if(checkEssay(ref)) {
entry.setAssessmentStatus(AssessmentEntryStatus.inReview);
} else {
entry.setAssessmentStatus(AssessmentEntryStatus.done);
}
}
}
}
}
private boolean iqTestPersisterExists(Identity assessedIdentity, IQTESTCourseNode cNode, ICourse course) {
Long courseResourceableId = course.getResourceableId();
String resourcePath = courseResourceableId + File.separator + cNode.getIdent();
FilePersister qtiPersister = new FilePersister(assessedIdentity, resourcePath);
return qtiPersister.exists();
}
private boolean checkEssay(RepositoryEntry testEntry) {
if(qtiEssayMap.containsKey(testEntry.getKey())) {
return qtiEssayMap.get(testEntry.getKey()).booleanValue();
}
TestFileResource fr = new TestFileResource();
fr.overrideResourceableId(testEntry.getOlatResource().getResourceableId());
TransientIdentity pseudoIdentity = new TransientIdentity();
pseudoIdentity.setName("transient");
Translator translator = Util.createPackageTranslator(QTIModule.class, Locale.ENGLISH);
try {
QTIEditorPackage qtiPackage = new QTIEditorPackageImpl(pseudoIdentity, fr, null, translator);
if(qtiPackage.getQTIDocument() != null && qtiPackage.getQTIDocument().getAssessment() != null) {
Assessment ass = qtiPackage.getQTIDocument().getAssessment();
//Sections with their Items
List<Section> sections = ass.getSections();
for (Section section:sections) {
List<Item> items = section.getItems();
for (Item item:items) {
String ident = item.getIdent();
if(ident != null && ident.startsWith("QTIEDIT:ESSAY")) {
qtiEssayMap.put(testEntry.getKey(), Boolean.TRUE);
return true;
}
}
}
}
} catch (OLATRuntimeException e) {
log.warn("QTI without content in repository entry: " + testEntry.getKey(), e);
}
qtiEssayMap.put(testEntry.getKey(), Boolean.FALSE);
return false;
}
private void processAssessmentPropertyForPortfolio(Identity assessedIdentity, AssessmentEntryImpl entry, PortfolioCourseNode cNode, ICourse course) {
entry.setAssessmentStatus(AssessmentEntryStatus.notStarted);
Long courseResId = course.getCourseEnvironment().getCourseResourceableId();
RepositoryEntry mapEntry = cNode.getReferencedRepositoryEntry();
if(mapEntry != null) {
PortfolioStructureMap template = (PortfolioStructureMap) ePFMgr.loadPortfolioStructure(mapEntry.getOlatResource());
if(template != null) {
OLATResourceable courseOres = OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId);
PortfolioStructureMap copy = ePFMgr.loadPortfolioStructureMap(assessedIdentity, template, courseOres, cNode.getIdent(), null);
if(copy != null) {
String status = copy.getStatus();
if(StructureStatusEnum.CLOSED.equals(status)) {
entry.setAssessmentStatus(AssessmentEntryStatus.inReview);
} else {
entry.setAssessmentStatus(AssessmentEntryStatus.inProgress);
}
}
}
}
}
/**
* Search first for the status of the task, if not found, see if some documents
* where dropped or returned, in this case, the status is in review, if not
* the status is not started.
*
* @param assessedIdentity
* @param entry
* @param tNode
* @param course
*/
private void processAssessmentPropertyForTA(Identity assessedIdentity, AssessmentEntryImpl entry, TACourseNode tNode, ICourse course) {
List<Property> samples = course.getCourseEnvironment().getCoursePropertyManager()
.findCourseNodeProperties(tNode, assessedIdentity, null, TaskController.PROP_ASSIGNED);
if (samples.size() > 0) {
String details = samples.get(0).getStringValue();
entry.setDetails(details);
}
Property statusProperty = course.getCourseEnvironment().getCoursePropertyManager()
.findCourseNodeProperty(tNode, assessedIdentity, null, StatusManager.PROPERTY_KEY_STATUS);
AssessmentEntryStatus assessmentStatus = null;
if (statusProperty != null) {
String status = statusProperty.getStringValue();
if(status != null) {
switch(status) {
case StatusForm.STATUS_VALUE_NOT_OK: assessmentStatus = AssessmentEntryStatus.inProgress; break;
case StatusForm.STATUS_VALUE_OK: assessmentStatus = AssessmentEntryStatus.done; break;
case StatusForm.STATUS_VALUE_WORKING_ON: assessmentStatus = AssessmentEntryStatus.inProgress; break;
case StatusForm.STATUS_VALUE_UNDEFINED: assessmentStatus = AssessmentEntryStatus.inProgress; break;
}
}
}
if(assessmentStatus == null) {
String dropbox = DropboxController.getDropboxPathRelToFolderRoot(course.getCourseEnvironment(), tNode) + File.separator + assessedIdentity.getName();
OlatRootFolderImpl dropBox = new OlatRootFolderImpl(dropbox, null);
boolean hasDropped = (dropBox.getBasefile().exists() && dropBox.getBasefile().listFiles(SystemFileFilter.FILES_ONLY).length > 0);
String returnbox = ReturnboxController.getReturnboxPathRelToFolderRoot(course.getCourseEnvironment(), tNode) + File.separator + assessedIdentity.getName();
OlatRootFolderImpl returnBox = new OlatRootFolderImpl(returnbox, null);
boolean hasReturned = (returnBox.getBasefile().exists() && returnBox.getBasefile().listFiles(SystemFileFilter.FILES_ONLY).length > 0);
if(hasReturned || hasDropped) {
assessmentStatus = AssessmentEntryStatus.inReview;
} else {
assessmentStatus = AssessmentEntryStatus.notStarted;
}
}
entry.setAssessmentStatus(assessmentStatus);
}
private void processAssessmentPropertyForGTA(Identity assessedIdentity, AssessmentEntryImpl entry, GTACourseNode cNode, RepositoryEntry courseEntry) {
List<Task> tasks = gtaManager.getTasks(assessedIdentity, courseEntry, cNode);
if(tasks != null && tasks.size() > 0) {
Task task = tasks.get(0);
AssessmentEntryStatus status = gtaManager.convertToAssessmentEntrystatus(task, cNode);
entry.setStatus(status.name());
String details = gtaManager.getDetails(assessedIdentity, courseEntry, cNode);
entry.setDetails(details);
}
}
private void copyAssessmentProperty(Property property, AssessmentEntryImpl nodeAssessment, ICourse course) {
String propertyName = property.getName();
if (propertyName.equals(ATTEMPTS)) {
if(property.getLongValue() != null) {
nodeAssessment.setAttempts(property.getLongValue().intValue());
}
} else if (propertyName.equals(SCORE)) {
if(property.getFloatValue() != null) {
BigDecimal score = new BigDecimal(Float.toString(property.getFloatValue().floatValue()));
nodeAssessment.setScore(score);
postCopyPassedScore(nodeAssessment, course);
}
} else if (propertyName.equals(PASSED)) {
if(StringHelper.containsNonWhitespace(property.getStringValue())) {
nodeAssessment.setPassed(new Boolean(property.getStringValue()));
postCopyPassedScore(nodeAssessment, course);
}
} else if(propertyName.equals(FULLY_ASSESSED)) {
if(StringHelper.containsNonWhitespace(property.getStringValue())) {
Boolean fullyAssessed = new Boolean(property.getStringValue());
nodeAssessment.setFullyAssessed(fullyAssessed);
if(nodeAssessment.getStatus() == null
|| nodeAssessment.getAssessmentStatus() == AssessmentEntryStatus.notStarted
|| nodeAssessment.getAssessmentStatus() == AssessmentEntryStatus.inProgress) {
if(fullyAssessed.booleanValue()) {
nodeAssessment.setAssessmentStatus(AssessmentEntryStatus.done);
} else {
nodeAssessment.setAssessmentStatus(AssessmentEntryStatus.inProgress);
}
}
}
} else if (propertyName.equals(ASSESSMENT_ID)) {
nodeAssessment.setAssessmentId(property.getLongValue());
} else if (propertyName.equals(COMMENT)) {
nodeAssessment.setComment(property.getTextValue());
} else if(propertyName.equals(COACH_COMMENT)) {
nodeAssessment.setCoachComment(property.getTextValue());
} else if(propertyName.equals(TaskController.PROP_ASSIGNED)) {
nodeAssessment.setDetails(property.getStringValue());
}
}
/**
* Used if a passed or score value was set.
* @param nodeAssessment
* @param course
*/
private void postCopyPassedScore(AssessmentEntry entry, ICourse course) {
String nodeIdent = entry.getSubIdent();
CourseNode courseNode = course.getRunStructure().getNode(nodeIdent);
if(courseNode instanceof GTACourseNode) {
//
} else if(courseNode instanceof TACourseNode) {
entry.setAssessmentStatus(AssessmentEntryStatus.done);
} else if(courseNode instanceof IQTESTCourseNode) {
//
} else if(courseNode instanceof PortfolioCourseNode) {
entry.setAssessmentStatus(AssessmentEntryStatus.done);
} else if(courseNode instanceof MSCourseNode) {
entry.setAssessmentStatus(AssessmentEntryStatus.done);
} else if(courseNode instanceof BasicLTICourseNode) {
entry.setAssessmentStatus(AssessmentEntryStatus.done);
} else if(courseNode instanceof ScormCourseNode) {
entry.setAssessmentStatus(AssessmentEntryStatus.done);
}
}
private List<AssessmentEntryImpl> loadAssessmentEntries(RepositoryEntry courseEntry) {
String sb = "select data from assessmententry data where data.repositoryEntry.key=:courseEntryKey";
return dbInstance.getCurrentEntityManager()
.createQuery(sb, AssessmentEntryImpl.class)
.setParameter("courseEntryKey", courseEntry.getKey())
.getResultList();
}
private List<Long> loadIdentityKeyOfAssessmentEntries(RepositoryEntry courseEntry, String subIdent) {
String sb = "select data.identity.key from assessmententry data where data.repositoryEntry.key=:courseEntryKey and data.subIdent=:subIdent";
return dbInstance.getCurrentEntityManager()
.createQuery(sb, Long.class)
.setParameter("courseEntryKey", courseEntry.getKey())
.setParameter("subIdent", subIdent)
.getResultList();
}
private List<Property> loadAssessmentProperties(RepositoryEntry course) {
StringBuilder sb = new StringBuilder();
sb.append("from org.olat.properties.Property as p")
.append(" inner join fetch p.identity as ident ")
.append(" inner join fetch ident.user as user ")
.append(" where p.resourceTypeId = :restypeid and p.resourceTypeName = :restypename")
.append(" and p.name in ('")
.append(ATTEMPTS).append("','")
.append(SCORE).append("','")
.append(FULLY_ASSESSED).append("','")
.append(PASSED).append("','")
.append(ASSESSMENT_ID).append("','")
.append(COMMENT).append("','")
.append(COACH_COMMENT).append("','")
.append(TaskController.PROP_ASSIGNED).append("','")
.append(StatusManager.PROPERTY_KEY_STATUS).append("','")
.append("')");
return dbInstance.getCurrentEntityManager()
.createQuery(sb.toString(), Property.class)
.setParameter("restypename", course.getOlatResource().getResourceableTypeName())
.setParameter("restypeid", course.getOlatResource().getResourceableId())
.getResultList();
}
private static class AssessmentDataKey {
private final Long courseId;
private final Long identityKey;
private final String courseNodeIdent;
public AssessmentDataKey(Identity identity, Long courseOresId, String courseNodeIdent) {
this.courseId = courseOresId;
this.identityKey = identity.getKey();
this.courseNodeIdent = courseNodeIdent;
}
public AssessmentDataKey(Long identityKey, Long courseOresId, String courseNodeIdent) {
this.courseId = courseOresId;
this.identityKey = identityKey;
this.courseNodeIdent = courseNodeIdent;
}
public AssessmentDataKey(AssessmentEntry nodeAssessment) {
this.courseId = nodeAssessment.getRepositoryEntry().getOlatResource().getResourceableId();
this.identityKey = nodeAssessment.getIdentity().getKey();
this.courseNodeIdent = nodeAssessment.getSubIdent();
}
@Override
public int hashCode() {
return (courseId == null ? 32876 : courseId.hashCode())
+ (identityKey == null ? 7525 : identityKey.hashCode())
+ (courseNodeIdent == null ? 39841 : courseNodeIdent.hashCode());
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj instanceof AssessmentDataKey) {
AssessmentDataKey key = (AssessmentDataKey)obj;
return courseId != null && courseId.equals(key.courseId)
&& identityKey != null && identityKey.equals(key.identityKey)
&& courseNodeIdent != null && courseNodeIdent.equals(key.courseNodeIdent);
}
return false;
}
}
private static class StaticCacheWrapper implements CacheWrapper<NewCacheKey,HashMap<String, Serializable>> {
private ConcurrentHashMap<NewCacheKey,HashMap<String, Serializable>> map = new ConcurrentHashMap<>();
@Override
public boolean containsKey(NewCacheKey key) {
return map.containsKey(key);
}
@Override
public HashMap<String, Serializable> get(NewCacheKey key) {
return map.get(key);
}
@Override
public HashMap<String, Serializable> update(NewCacheKey key, HashMap<String, Serializable> value) {
return map.put(key, value);
}
@Override
public HashMap<String, Serializable> put(NewCacheKey key, HashMap<String, Serializable> value) {
return map.put(key, value);
}
@Override
public HashMap<String, Serializable> putIfAbsent(NewCacheKey key, HashMap<String, Serializable> value) {
return map.putIfAbsent(key, value);
}
@Override
public HashMap<String, Serializable> replace(NewCacheKey key, HashMap<String, Serializable> value) {
return map.replace(key, value);
}
@Override
public HashMap<String, Serializable> computeIfAbsent(NewCacheKey key,
Function<? super NewCacheKey, ? extends HashMap<String, Serializable>> mappingFunction) {
return map.computeIfAbsent(key, mappingFunction);
}
@Override
public List<NewCacheKey> getKeys() {
return new ArrayList<>(map.keySet());
}
@Override
public HashMap<String, Serializable> remove(NewCacheKey key) {
return map.remove(key);
}
@Override
public int size() {
return map.size();
}
@Override
public Iterator<NewCacheKey> iterateKeys() {
return map.keySet().iterator();
}
@Override
public void addListener(Object obj) {
//
}
}
}