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 3df008b319e6a8546fefe989770a84a2d97c526b..44b46cbd3e5b86ad80043d7ac94e91d1d39374d0 100644 --- a/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionInvalidationController.java +++ b/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionInvalidationController.java @@ -35,11 +35,16 @@ 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.assessment.CourseAssessmentService; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Service; +import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; +import org.olat.modules.grading.GradingAssignment; +import org.olat.modules.grading.GradingAssignmentStatus; +import org.olat.modules.grading.GradingService; import org.olat.repository.RepositoryEntry; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; @@ -62,6 +67,7 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi private final Identity assessedIdentity; private final boolean canUpdateAssessmentEntry; private UserCourseEnvironment assessedUserCourseEnv; + private final GradingAssignment runningAssignment; @Autowired private DB dbInstance; @@ -69,6 +75,10 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi private QTI21Service qtiService; @Autowired private UserManager userManager; + @Autowired + private GradingService gradingService; + @Autowired + private CourseAssessmentService courseAssessmentService; /** * Invalidate the assessment test session linked to a course. @@ -89,6 +99,7 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi this.assessedUserCourseEnv = assessedUserCourseEnv; assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); canUpdateAssessmentEntry = lastSession && (getNextLastSession() != null); + runningAssignment = getRunningGradingAssignment(); initForm(ureq); } @@ -109,15 +120,28 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi this.testEntry = testEntry; this.assessedIdentity = assessedIdentity; canUpdateAssessmentEntry = lastSession && (getNextLastSession() != null); + runningAssignment = null; initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; String fullname = userManager.getUserDisplayName(session.getIdentity()); String text = translate("invalidate.test.confirm.text", new String[]{ fullname }); - ((FormLayoutContainer) formLayout).contextPut("msg", text); + layoutCont.contextPut("msg", text); + + if(runningAssignment != null) { + GradingAssignmentStatus assignmentStatus = runningAssignment.getAssignmentStatus(); + if(assignmentStatus == GradingAssignmentStatus.assigned || assignmentStatus == GradingAssignmentStatus.inProcess) { + String warningText = translate("warning.assignment.inProcess"); + layoutCont.contextPut("assignmentMsg", warningText); + } else if(assignmentStatus == GradingAssignmentStatus.done) { + String warningText = translate("warning.assignment.done"); + layoutCont.contextPut("assignmentMsg", warningText); + } + } } uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); @@ -128,6 +152,19 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi uifactory.addFormSubmitButton("invalidate", formLayout); } } + + private GradingAssignment getRunningGradingAssignment() { + if(courseNode == null) return null; + + if(gradingService.isGradingEnabled(session.getTestEntry(), null)) { + AssessmentEntry assessmentEntry = courseAssessmentService.getAssessmentEntry(courseNode, assessedUserCourseEnv); + GradingAssignment assignment = gradingService.getGradingAssignment(session.getTestEntry(), assessmentEntry); + if(assignment != null && session.getKey().equals(assessmentEntry.getAssessmentId()) && gradingService.hasRecordedTime(assignment)) { + return assignment; + } + } + return null; + } @Override protected void doDispose() { @@ -166,6 +203,16 @@ public class ConfirmAssessmentTestSessionInvalidationController extends FormBasi } } } + + if(runningAssignment != null) { + GradingAssignmentStatus assignmentStatus = runningAssignment.getAssignmentStatus(); + if(assignmentStatus == GradingAssignmentStatus.assigned + || assignmentStatus == GradingAssignmentStatus.inProcess + || assignmentStatus == GradingAssignmentStatus.done) { + gradingService.reopenAssignment(runningAssignment); + } + } + fireEvent(ureq, Event.CHANGED_EVENT); } 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 863b4a72bdb1bc3ab6ef6c513c1077007a8f6a65..cc63a594113bf5647bbd3b067671c4bb24135423 100644 --- a/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionRevalidationController.java +++ b/src/main/java/org/olat/ims/qti21/ui/ConfirmAssessmentTestSessionRevalidationController.java @@ -35,11 +35,16 @@ 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.assessment.CourseAssessmentService; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Service; +import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; +import org.olat.modules.grading.GradingAssignment; +import org.olat.modules.grading.GradingAssignmentStatus; +import org.olat.modules.grading.GradingService; import org.olat.repository.RepositoryEntry; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; @@ -60,6 +65,7 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi private final Identity assessedIdentity; private final boolean canUpdateAssessmentEntry; private UserCourseEnvironment assessedUserCourseEnv; + private final GradingAssignment runningAssignment; @Autowired private DB dbInstance; @@ -67,6 +73,10 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi private QTI21Service qtiService; @Autowired private UserManager userManager; + @Autowired + private GradingService gradingService; + @Autowired + private CourseAssessmentService courseAssessmentService; public ConfirmAssessmentTestSessionRevalidationController(UserRequest ureq, WindowControl wControl, AssessmentTestSession session, IQTESTCourseNode courseNode, UserCourseEnvironment assessedUserCourseEnv) { @@ -76,6 +86,7 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi this.assessedUserCourseEnv = assessedUserCourseEnv; assessedIdentity = assessedUserCourseEnv.getIdentityEnvironment().getIdentity(); canUpdateAssessmentEntry = canBeNextLastSession(); + runningAssignment = getRunningGradingAssignment(); initForm(ureq); } @@ -86,15 +97,28 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi this.testEntry = testEntry; this.assessedIdentity = assessedIdentity; canUpdateAssessmentEntry = canBeNextLastSession(); + runningAssignment = null; initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; String fullname = userManager.getUserDisplayName(session.getIdentity()); String text = translate("revalidate.test.confirm.text", new String[]{ fullname }); - ((FormLayoutContainer) formLayout).contextPut("msg", text); + layoutCont.contextPut("msg", text); + + if(runningAssignment != null) { + GradingAssignmentStatus assignmentStatus = runningAssignment.getAssignmentStatus(); + if(assignmentStatus == GradingAssignmentStatus.assigned || assignmentStatus == GradingAssignmentStatus.inProcess) { + String warningText = translate("warning.assignment.inProcess"); + layoutCont.contextPut("assignmentMsg", warningText); + } else if(assignmentStatus == GradingAssignmentStatus.done) { + String warningText = translate("warning.assignment.done"); + layoutCont.contextPut("assignmentMsg", warningText); + } + } } uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); @@ -128,7 +152,20 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi protected void formCancelled(UserRequest ureq) { fireEvent(ureq, Event.CANCELLED_EVENT); } - + + private GradingAssignment getRunningGradingAssignment() { + if(courseNode == null) return null; + + if(gradingService.isGradingEnabled(session.getTestEntry(), null)) { + AssessmentEntry assessmentEntry = courseAssessmentService.getAssessmentEntry(courseNode, assessedUserCourseEnv); + GradingAssignment assignment = gradingService.getGradingAssignment(session.getTestEntry(), assessmentEntry); + if(assignment != null && session.getKey().equals(assessmentEntry.getAssessmentId()) && gradingService.hasRecordedTime(assignment)) { + return assignment; + } + } + return null; + } + private void doInvalidateSession(UserRequest ureq, boolean updateEntryResults) { session.setCancelled(false); session = qtiService.updateAssessmentTestSession(session); @@ -140,6 +177,16 @@ public class ConfirmAssessmentTestSessionRevalidationController extends FormBasi courseNode.promoteAssessmentTestSession(session, assessedUserCourseEnv, getIdentity(), Role.coach); } } + + if(runningAssignment != null) { + GradingAssignmentStatus assignmentStatus = runningAssignment.getAssignmentStatus(); + if(assignmentStatus == GradingAssignmentStatus.assigned + || assignmentStatus == GradingAssignmentStatus.inProcess + || assignmentStatus == GradingAssignmentStatus.done) { + gradingService.reopenAssignment(runningAssignment); + } + } + fireEvent(ureq, Event.CHANGED_EVENT); } diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/confirm_inval_test_session.html b/src/main/java/org/olat/ims/qti21/ui/_content/confirm_inval_test_session.html index bc1be9a8dc1a998712264983575378060d6970b9..34fb5d5c7a164fe3fe08cdb5be8628f9be9704eb 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_content/confirm_inval_test_session.html +++ b/src/main/java/org/olat/ims/qti21/ui/_content/confirm_inval_test_session.html @@ -1,4 +1,9 @@ +#if($r.isNotEmpty($assignmentMsg)) +<div class="o_error">$r.xssScan($assignmentMsg)</div> +#end +#if($r.isNotEmpty($msg)) <div class="o_warning">$r.xssScan($msg)</div> +#end <div class="o_button_group"> $r.render("cancel") #if($r.available("invalidate.overwrite")) diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/confirm_reval_test_session.html b/src/main/java/org/olat/ims/qti21/ui/_content/confirm_reval_test_session.html index 7c4fb197ac8b32ebc5fbbc827219636514811f89..95cbf6b339e44e9eb96c5dc38a114a84375dfe79 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_content/confirm_reval_test_session.html +++ b/src/main/java/org/olat/ims/qti21/ui/_content/confirm_reval_test_session.html @@ -1,4 +1,9 @@ +#if($r.isNotEmpty($assignmentMsg)) +<div class="o_error">$r.xssScan($assignmentMsg)</div> +#end +#if($r.isNotEmpty($msg)) <div class="o_warning">$r.xssScan($msg)</div> +#end <div class="o_button_group"> $r.render("cancel") #if($r.available("revalidate.overwrite")) diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties index 3d257cf4083d539dc77bbe4c5583425f4b48efd2..05ce41e31d1efeada125d023bd510f404f55dc96 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties @@ -290,9 +290,12 @@ upload.explanation=Datei auf lokalem Computer f\u00FCr \u00DCbertragung w\u00E4h validate.xml.signature=Testquittung validieren validate.xml.signature.file=XML Datei validate.xml.signature.ok=Testquittung und Datei konnte erfolgreich validiert werden. +warning.assignment.done=Korrektur von diesem Test ist abgeschlossen. Wenn dieser Test ung\u00FCltig gemachen wird, werden alle Korrekturen verloren. +warning.assignment.inProcess=Korrektur von diesem Test wurde angefangen. Wenn dieser Test ung\u00FCltig gemachen wird, werden alle Korrekturen verloren. warning.download.log=Es gibt leider kein Logdatei f\u00FCr diesen Test. warning.reset.assessmenttest.data=Die Test-Resultate wurden von einem Administrator oder Kursbesitzer zur\u00FCckgesetzt. Sie k\u00F6nnen den Test nicht fortsetzen und m\u00FCssen ihn erneut starten. warning.reset.test.data.nobody=Es gibt kein Teilnehmer zu zur\u00FCcksetzen warning.suspended.ended.assessmenttest=Sie haben schon den Test unterbrochen oder beendet, wahrscheinlich in einem anderen Fenster. Bitte, diese Fenster jetzt schliessen. warning.xml.signature.notok=Unterschrift und Datei konnte nicht validiert werden. warning.xml.signature.session.not.found=Die Resultaten konnte nicht gefunden werden. + diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties index 26b37a1df898ce11c944272b4529fb8cb82fa009..bae8ed6dd36fad24cbb3e0570165d4393570c17d 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties @@ -290,6 +290,8 @@ upload.explanation=Select a file from your computer to upload it validate.xml.signature=Validate test receipt validate.xml.signature.file=XML file validate.xml.signature.ok=Test receipt and results was successfully validated. +warning.assignment.done=Grading of this test is done. If the test is invalidated, all correction will be lost. +warning.assignment.inProcess=Grading of this test was already started. If the test is invalidated, all correction will be lost. warning.download.log=There is not a log file for this test. warning.reset.assessmenttest.data=The test results were reset by an administrator or course owner. You cannot continue the test and have to restart it. warning.suspended.ended.assessmenttest=You have already suspended or ended this test, probably in an other window. Please close this window. diff --git a/src/main/java/org/olat/modules/grading/GradingService.java b/src/main/java/org/olat/modules/grading/GradingService.java index 3879872a028c412c4efce6a97425a87fca78fe74..2745f3d539257f33262c052c58f938c5bac17717 100644 --- a/src/main/java/org/olat/modules/grading/GradingService.java +++ b/src/main/java/org/olat/modules/grading/GradingService.java @@ -236,6 +236,15 @@ public interface GradingService { public void appendTimeTo(GradingTimeRecordRef record, long addedTime, TimeUnit unit); + /** + * Check if the specified assignment has at least one minute of + * recorded time. + * + * @param assignment The assignment + * @return true if at least one minute was recorded + */ + public boolean hasRecordedTime(GradingAssignment assignment); + public String getCachedCourseElementTitle(RepositoryEntry entry, String subIdenty); /** diff --git a/src/main/java/org/olat/modules/grading/manager/GradingServiceImpl.java b/src/main/java/org/olat/modules/grading/manager/GradingServiceImpl.java index 63ef1c9d25a8593293e69dd007cb5deb261feab4..9d9d0f22353f4d04513703c68194ce06137b4396 100644 --- a/src/main/java/org/olat/modules/grading/manager/GradingServiceImpl.java +++ b/src/main/java/org/olat/modules/grading/manager/GradingServiceImpl.java @@ -1094,6 +1094,11 @@ public class GradingServiceImpl implements GradingService, UserDataDeletable, Re gradingTimeRecordDao.appendTimeInSeconds(record, unit.toSeconds(addedTime)); } + @Override + public boolean hasRecordedTime(GradingAssignment assignment) { + return gradingTimeRecordDao.hasRecordedTime(assignment); + } + @Override public AssessmentEntry loadFullAssessmentEntry(AssessmentEntry assessmentEntry) { QueryBuilder sb = new QueryBuilder(); diff --git a/src/main/java/org/olat/modules/grading/manager/GradingTimeRecordDAO.java b/src/main/java/org/olat/modules/grading/manager/GradingTimeRecordDAO.java index 087678730333eea6ee257377bfb407350d457871..d2261c847e496e34bb3d8f1c60239fa97c7b86e7 100644 --- a/src/main/java/org/olat/modules/grading/manager/GradingTimeRecordDAO.java +++ b/src/main/java/org/olat/modules/grading/manager/GradingTimeRecordDAO.java @@ -94,6 +94,23 @@ public class GradingTimeRecordDAO { return records != null && !records.isEmpty() ? records.get(0) : null; } + public boolean hasRecordedTime(GradingAssignmentRef assignment) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select record from gradingtimerecord as record ") + .append(" where record.assignment.key=:assignmentKey"); + + List<GradingTimeRecord> records = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), GradingTimeRecord.class) + .setParameter("assignmentKey", assignment.getKey()) + .getResultList(); + + long time = 0l; + for(GradingTimeRecord record:records) { + time += record.getTime(); + } + return time > 0l; + } + public void appendTimeInSeconds(GraderToIdentity grader, GradingAssignmentRef assignment, Long addedTime, Date date) { QueryBuilder sb = new QueryBuilder(); sb.append("update gradingtimerecordappender set time=time+:addedTime, lastModified=:now") diff --git a/src/test/java/org/olat/modules/grading/manager/GradingTimeRecordDAOTest.java b/src/test/java/org/olat/modules/grading/manager/GradingTimeRecordDAOTest.java index 99e5ae4b580ba859bb7e613de5e91bbb32975e8c..76871ccfc357ed249d23e17795c489cd33247d0c 100644 --- a/src/test/java/org/olat/modules/grading/manager/GradingTimeRecordDAOTest.java +++ b/src/test/java/org/olat/modules/grading/manager/GradingTimeRecordDAOTest.java @@ -172,4 +172,48 @@ public class GradingTimeRecordDAOTest extends OlatTestCase { GradingTimeRecord reloadedRecord = gradingTimesheetDao.loadByKey(record.getKey()); Assert.assertEquals(102l, reloadedRecord.getTime()); } + + @Test + public void hasRecordedTime() { + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("time-5"); + RepositoryEntry entry = JunitTestHelper.createRandomRepositoryEntry(id); + AssessmentEntry assessment = assessmentEntryDao.createAssessmentEntry(id, null, entry, null, false, entry); + GraderToIdentity grader = gradedToIdentityDao.createRelation(entry, id); + GradingAssignment assignment = gradingAssignmentDao.createGradingAssignment(grader, entry, assessment, new Date(), null); + dbInstance.commit(); + + // create record + GradingTimeRecord record = gradingTimesheetDao.createRecord(grader, assignment, new Date()); + dbInstance.commit(); + // append time + gradingTimesheetDao.appendTimeInSeconds(record, 1l); + dbInstance.commitAndCloseSession(); + + // check + boolean hasRecordedTime = gradingTimesheetDao.hasRecordedTime(assignment); + Assert.assertTrue(hasRecordedTime); + } + + @Test + public void hasNotRecordedTime() { + Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("time-5"); + RepositoryEntry entry = JunitTestHelper.createRandomRepositoryEntry(id); + AssessmentEntry assessment = assessmentEntryDao.createAssessmentEntry(id, null, entry, null, false, entry); + GraderToIdentity grader = gradedToIdentityDao.createRelation(entry, id); + GradingAssignment assignment = gradingAssignmentDao.createGradingAssignment(grader, entry, assessment, new Date(), null); + dbInstance.commitAndCloseSession(); + + // check + boolean hasNotRecordedTime = gradingTimesheetDao.hasRecordedTime(assignment); + Assert.assertFalse(hasNotRecordedTime); + + // create record + GradingTimeRecord record = gradingTimesheetDao.createRecord(grader, assignment, new Date()); + dbInstance.commitAndCloseSession(); + Assert.assertNotNull(record); + + // check + boolean hasStillNotRecordedTime = gradingTimesheetDao.hasRecordedTime(assignment); + Assert.assertFalse(hasStillNotRecordedTime); + } }