diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java index 49851d1a6ad092aa529201f26261434d7cc91800..e2d4d6b97d2832944b71bcbfc46ada5aed11b2cf 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Service.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java @@ -438,6 +438,14 @@ public interface QTI21Service { */ public AssessmentTestSession pullSession(AssessmentTestSession candidateSession, DigitalSignatureOptions signatureOptions, Identity actor); + /** + * Update the assessment entry if the test is done within a test repository entry. + * + * @param candidateSession The assessment test tession. + * @return The updated assessment entry + */ + public AssessmentEntry updateAssessmentEntry(AssessmentTestSession candidateSession); + /** * Sign the assessment result. Be careful, the file must not be changed * after that! diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java index 226c4245aae97c6061f3e0e09d3e2dc8d78f8b0d..d671087fe599999d5caa45a38b654657dd8fbb24 100644 --- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java +++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java @@ -105,6 +105,7 @@ import org.olat.ims.qti21.model.audit.CandidateEvent; import org.olat.ims.qti21.model.audit.CandidateItemEventType; import org.olat.ims.qti21.model.audit.CandidateTestEventType; import org.olat.ims.qti21.model.jpa.AssessmentTestSessionStatistics; +import org.olat.ims.qti21.model.xml.QtiNodesExtractor; import org.olat.ims.qti21.ui.event.DeleteAssessmentTestSessionEvent; import org.olat.ims.qti21.ui.event.RetrieveAssessmentTestSessionEvent; import org.olat.modules.assessment.AssessmentEntry; @@ -133,6 +134,7 @@ import uk.ac.ed.ph.jqtiplus.node.result.AssessmentResult; import uk.ac.ed.ph.jqtiplus.node.result.ItemResult; import uk.ac.ed.ph.jqtiplus.node.result.ItemVariable; import uk.ac.ed.ph.jqtiplus.node.result.OutcomeVariable; +import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; import uk.ac.ed.ph.jqtiplus.notification.NotificationRecorder; import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader; import uk.ac.ed.ph.jqtiplus.reading.QtiObjectReadResult; @@ -1215,6 +1217,34 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia } } + @Override + public AssessmentEntry updateAssessmentEntry(AssessmentTestSession candidateSession) { + Identity assessedIdentity = candidateSession.getIdentity(); + RepositoryEntry testEntry = candidateSession.getTestEntry(); + + File unzippedDirRoot = FileResourceManager.getInstance().unzipFileResource(testEntry.getOlatResource()); + ResolvedAssessmentTest resolvedAssessmentTest = loadAndResolveAssessmentTest(unzippedDirRoot, false, false); + AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful(); + + AssessmentEntry assessmentEntry = assessmentEntryDao.loadAssessmentEntry(assessedIdentity, testEntry, null, testEntry); + BigDecimal finalScore = candidateSession.getFinalScore(); + assessmentEntry.setScore(finalScore); + assessmentEntry.setAssessmentId(candidateSession.getKey()); + + Double cutValue = QtiNodesExtractor.extractCutValue(assessmentTest); + + Boolean passed = assessmentEntry.getPassed(); + if(candidateSession.getManualScore() != null && finalScore != null && cutValue != null) { + boolean calculated = finalScore.compareTo(BigDecimal.valueOf(cutValue.doubleValue())) >= 0; + passed = Boolean.valueOf(calculated); + } else if(candidateSession.getPassed() != null) { + passed = candidateSession.getPassed(); + } + assessmentEntry.setPassed(passed); + assessmentEntry = assessmentEntryDao.updateAssessmentEntry(assessmentEntry); + return assessmentEntry; + } + @Override public AssessmentTestSession pullSession(AssessmentTestSession session, DigitalSignatureOptions signatureOptions, Identity actor) { session = getAssessmentTestSession(session.getKey()); diff --git a/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionInvalidationController.java b/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionInvalidationController.java index 3177361a18997c3f815d28d0a20f24083cd1c334..3df008b319e6a8546fefe989770a84a2d97c526b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionInvalidationController.java +++ b/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionInvalidationController.java @@ -34,6 +34,7 @@ import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; +import org.olat.core.id.Identity; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti21.AssessmentTestSession; @@ -56,9 +57,11 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi private FormLink invalidateButton; private AssessmentTestSession session; - private final IQTESTCourseNode courseNode; + private RepositoryEntry testEntry; + private IQTESTCourseNode courseNode; + private final Identity assessedIdentity; private final boolean canUpdateAssessmentEntry; - private final UserCourseEnvironment assessedUserCourseEnv; + private UserCourseEnvironment assessedUserCourseEnv; @Autowired private DB dbInstance; @@ -67,6 +70,16 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi @Autowired private UserManager userManager; + /** + * Invalidate the assessment test session linked to a course. + * + * @param ureq The user request + * @param wControl The window control + * @param session The assessment test session to invalidate + * @param lastSession If the specified test session is known to be the last session + * @param courseNode The course element + * @param assessedUserCourseEnv The user course environnment of the assessed identity + */ public ConfirmAssessmentTestSessionInvalidationController(UserRequest ureq, WindowControl wControl, AssessmentTestSession session, boolean lastSession, IQTESTCourseNode courseNode, UserCourseEnvironment assessedUserCourseEnv) { @@ -74,6 +87,27 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi this.session = session; this.courseNode = courseNode; this.assessedUserCourseEnv = assessedUserCourseEnv; + assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); + canUpdateAssessmentEntry = lastSession && (getNextLastSession() != null); + initForm(ureq); + } + + /** + * Invalidate the assessment test session of a test entry (without course). + * + * @param ureq The user request + * @param wControl The window control + * @param session The assessment test session to invalidate + * @param lastSession If the specified test session is known to be the last session + * @param testEntry The test repository entry + * @param assessedIdentity The assessed identity + */ + public ConfirmAssessmentTestSessionInvalidationController(UserRequest ureq, WindowControl wControl, + AssessmentTestSession session, boolean lastSession, RepositoryEntry testEntry, Identity assessedIdentity) { + super(ureq, wControl, "confirm_inval_test_session"); + this.session = session; + this.testEntry = testEntry; + this.assessedIdentity = assessedIdentity; canUpdateAssessmentEntry = lastSession && (getNextLastSession() != null); initForm(ureq); } @@ -125,16 +159,25 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi if(updateEntryResults) { AssessmentTestSession promotedSession = getNextLastSession(); if(promotedSession != null) { - courseNode.promoteAssessmentTestSession(promotedSession, assessedUserCourseEnv, getIdentity(), Role.coach); + if(courseNode == null) { + qtiService.updateAssessmentEntry(promotedSession); + } else { + courseNode.promoteAssessmentTestSession(promotedSession, assessedUserCourseEnv, getIdentity(), Role.coach); + } } } fireEvent(ureq, Event.CHANGED_EVENT); } private AssessmentTestSession getNextLastSession() { - RepositoryEntry courseEntry = assessedUserCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); - List<AssessmentTestSession> sessions = qtiService.getAssessmentTestSessions(courseEntry, courseNode.getIdent(), - assessedUserCourseEnv.getIdentityEnvironment().getIdentity(), true); + List<AssessmentTestSession> sessions; + if(courseNode == null) { + sessions = qtiService.getAssessmentTestSessions(testEntry, null, assessedIdentity, true); + } else { + RepositoryEntry courseEntry = assessedUserCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + sessions = qtiService.getAssessmentTestSessions(courseEntry, courseNode.getIdent(), assessedIdentity, true); + } + sessions.remove(session); if(!sessions.isEmpty()) { Collections.sort(sessions, new AssessmentTestSessionComparator()); diff --git a/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionRevalidationController.java b/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionRevalidationController.java index df451157964753e375292eacb8ea78c298f8dc40..863b4a72bdb1bc3ab6ef6c513c1077007a8f6a65 100644 --- a/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionRevalidationController.java +++ b/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionRevalidationController.java @@ -34,6 +34,7 @@ import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; +import org.olat.core.id.Identity; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti21.AssessmentTestSession; @@ -54,9 +55,11 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi private FormLink revalidateButton; private AssessmentTestSession session; - private final IQTESTCourseNode courseNode; + private RepositoryEntry testEntry; + private IQTESTCourseNode courseNode; + private final Identity assessedIdentity; private final boolean canUpdateAssessmentEntry; - private final UserCourseEnvironment assessedUserCourseEnv; + private UserCourseEnvironment assessedUserCourseEnv; @Autowired private DB dbInstance; @@ -71,6 +74,17 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi this.session = session; this.courseNode = courseNode; this.assessedUserCourseEnv = assessedUserCourseEnv; + assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); + canUpdateAssessmentEntry = canBeNextLastSession(); + initForm(ureq); + } + + public ConfirmAssessmentTestSessionRevalidationController(UserRequest ureq, WindowControl wControl, + AssessmentTestSession session, RepositoryEntry testEntry, Identity assessedIdentity) { + super(ureq, wControl, "confirm_reval_test_session"); + this.session = session; + this.testEntry = testEntry; + this.assessedIdentity = assessedIdentity; canUpdateAssessmentEntry = canBeNextLastSession(); initForm(ureq); } @@ -120,15 +134,23 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi session = qtiService.updateAssessmentTestSession(session); dbInstance.commit(); if(updateEntryResults) { - courseNode.promoteAssessmentTestSession(session, assessedUserCourseEnv, getIdentity(), Role.coach); + if(courseNode == null) { + qtiService.updateAssessmentEntry(session); + } else { + courseNode.promoteAssessmentTestSession(session, assessedUserCourseEnv, getIdentity(), Role.coach); + } } fireEvent(ureq, Event.CHANGED_EVENT); } private boolean canBeNextLastSession() { - RepositoryEntry courseEntry = assessedUserCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); - List<AssessmentTestSession> sessions = qtiService.getAssessmentTestSessions(courseEntry, courseNode.getIdent(), - assessedUserCourseEnv.getIdentityEnvironment().getIdentity(), true); + List<AssessmentTestSession> sessions; + if(courseNode == null) { + sessions = qtiService.getAssessmentTestSessions(testEntry, null, assessedIdentity, true); + } else { + RepositoryEntry courseEntry = assessedUserCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + sessions = qtiService.getAssessmentTestSessions(courseEntry, courseNode.getIdent(), assessedIdentity, true); + } if(sessions.isEmpty()) { return true; } diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java index 2833cd00c4a628800e83bd20cd8aa7343d8425f3..4027ebb0bab484cf79ba2ad312ba76d5e5d0377f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -87,8 +87,6 @@ import org.olat.ims.qti21.ui.assessment.CorrectionIdentityAssessmentItemListCont import org.olat.ims.qti21.ui.assessment.CorrectionOverviewModel; import org.olat.ims.qti21.ui.components.AssessmentTestSessionDetailsNumberRenderer; import org.olat.modules.ModuleConfiguration; -import org.olat.modules.assessment.AssessmentEntry; -import org.olat.modules.assessment.AssessmentService; import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentEntryStatus; @@ -156,8 +154,6 @@ public class QTI21AssessmentDetailsController extends FormBasicController { protected QTI21Service qtiService; @Autowired private RepositoryManager repositoryManager; - @Autowired - private AssessmentService assessmentService; /** * The constructor used by the assessment tool of the course. @@ -481,10 +477,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { } private void doUpdateEntry(AssessmentTestSession session) { - AssessmentEntry assessmentEntry = assessmentService.loadAssessmentEntry(assessedIdentity, entry, null, entry); - assessmentEntry.setScore(session.getFinalScore()); - assessmentEntry.setAssessmentId(session.getKey()); - assessmentService.updateAssessmentEntry(assessmentEntry); + qtiService.updateAssessmentEntry(session); } private void doResetData(UserRequest ureq) { @@ -578,8 +571,13 @@ public class QTI21AssessmentDetailsController extends FormBasicController { AssessmentTestSession lastSession = tableModel.getLastTestSession(); boolean isLastSession = session.equals(lastSession); - invalidateConfirmationCtr = new ConfirmAssessmentTestSessionInvalidationController(ureq, getWindowControl(), + if(courseNode == null) { + invalidateConfirmationCtr = new ConfirmAssessmentTestSessionInvalidationController(ureq, getWindowControl(), + session, isLastSession, session.getTestEntry(), assessedIdentity); + } else { + invalidateConfirmationCtr = new ConfirmAssessmentTestSessionInvalidationController(ureq, getWindowControl(), session, isLastSession, courseNode, assessedUserCourseEnv); + } listenTo(invalidateConfirmationCtr); String title = translate("invalidate.test.confirm.title"); @@ -592,8 +590,13 @@ public class QTI21AssessmentDetailsController extends FormBasicController { private void confirmRevalidateTestSession(UserRequest ureq, AssessmentTestSession session) { if(guardModalController(revalidateConfirmationCtr)) return; - revalidateConfirmationCtr = new ConfirmAssessmentTestSessionRevalidationController(ureq, getWindowControl(), + if(courseNode == null) { + revalidateConfirmationCtr = new ConfirmAssessmentTestSessionRevalidationController(ureq, getWindowControl(), + session, entry, assessedIdentity); + } else { + revalidateConfirmationCtr = new ConfirmAssessmentTestSessionRevalidationController(ureq, getWindowControl(), session, courseNode, assessedUserCourseEnv); + } listenTo(revalidateConfirmationCtr); String title = translate("revalidate.test.confirm.title");