diff --git a/src/main/java/org/olat/core/commons/controllers/linkchooser/CustomLinkChooserController.java b/src/main/java/org/olat/core/commons/controllers/linkchooser/CustomLinkChooserController.java index df7070fa1642d753f408eacb371cae41ac325bdd..3ce1b1805a2aa23e0dcf3830e6bc57f183760cdd 100644 --- a/src/main/java/org/olat/core/commons/controllers/linkchooser/CustomLinkChooserController.java +++ b/src/main/java/org/olat/core/commons/controllers/linkchooser/CustomLinkChooserController.java @@ -31,6 +31,7 @@ import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.tree.MenuTree; +import org.olat.core.gui.components.tree.TreeEvent; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; @@ -68,7 +69,7 @@ public class CustomLinkChooserController extends BasicController { mainVC.put("internalLinkTree", jumpInSelectionTree); selectButton = LinkFactory.createButton("selectfile", mainVC, this); - selectButton.setElementCssClass("btn btn-primary"); + selectButton.setCustomEnabledLinkCSS("btn btn-default"); cancelButton = LinkFactory.createButton("cancel", mainVC, this); putInitialPanel(mainVC); @@ -84,8 +85,19 @@ public class CustomLinkChooserController extends BasicController { if(source == cancelButton) { fireEvent(ureq, Event.CANCELLED_EVENT); } else if(selectButton == source) { - String url = customLinkTreeModel.getInternalLinkUrlFor(jumpInSelectionTree.getSelectedNode().getIdent()); - fireEvent(ureq, new URLChoosenEvent(url)); + if(jumpInSelectionTree.getSelectedNode() != null) { + String url = customLinkTreeModel.getInternalLinkUrlFor(jumpInSelectionTree.getSelectedNode().getIdent()); + fireEvent(ureq, new URLChoosenEvent(url)); + } + } else if (source == jumpInSelectionTree) { + TreeEvent te = (TreeEvent) event; + if (te.getCommand().equals(MenuTree.COMMAND_TREENODE_CLICKED)) { + if(jumpInSelectionTree.getSelectedNode() != null) { + selectButton.setCustomEnabledLinkCSS("btn btn-default o_button_dirty"); + } else { + selectButton.setCustomEnabledLinkCSS("btn btn-default"); + } + } } } } \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java index 0422a92067085903be62790591e8635b02882087..6c7dac156eb3ab0168722a51b6cd19f471a54bba 100644 --- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java @@ -72,9 +72,11 @@ import org.olat.ims.qti.export.QTIExportFormatterCSVType1; import org.olat.ims.qti.export.QTIExportManager; import org.olat.ims.qti.fileresource.TestFileResource; import org.olat.ims.qti.process.AssessmentInstance; +import org.olat.ims.qti.process.FilePersister; import org.olat.ims.qti.statistics.QTIStatisticResourceResult; import org.olat.ims.qti.statistics.QTIStatisticSearchParams; import org.olat.ims.qti.statistics.QTIType; +import org.olat.ims.qti.statistics.ui.QTI12PullTestsToolController; import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController; import org.olat.modules.ModuleConfiguration; import org.olat.repository.RepositoryEntry; @@ -147,8 +149,22 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As CourseEnvironment courseEnv, AssessmentToolOptions options) { List<Controller> tools = new ArrayList<>(); tools.add(new QTI12StatisticsToolController(ureq, wControl, stackPanel, courseEnv, options, this)); + if(options.getGroup() == null && options.getIdentities() != null && options.getIdentities().size() > 0) { + for(Identity assessedIdentity:options.getIdentities()) { + if(isTestRunning(assessedIdentity, courseEnv)) { + tools.add(new QTI12PullTestsToolController(ureq, wControl, courseEnv, options, this)); + break; + } + } + } return tools; } + + public boolean isTestRunning(Identity assessedIdentity, CourseEnvironment courseEnv) { + String resourcePath = courseEnv.getCourseResourceableId() + File.separator + getIdent(); + FilePersister qtiPersister = new FilePersister(assessedIdentity, resourcePath); + return qtiPersister.exists(); + } @Override public StatisticResourceResult createStatisticNodeResult(UserRequest ureq, WindowControl wControl, diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java new file mode 100644 index 0000000000000000000000000000000000000000..f3053c1a10775615ce2a572e37be8cf5849d4985 --- /dev/null +++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java @@ -0,0 +1,170 @@ +package org.olat.ims.qti.statistics.ui; + +import java.io.File; +import java.util.List; + +import org.dom4j.Document; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; +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.BasicController; +import org.olat.core.gui.control.generic.dtabs.Activateable2; +import org.olat.core.gui.control.generic.modal.DialogBoxController; +import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; +import org.olat.core.id.Identity; +import org.olat.core.id.context.ContextEntry; +import org.olat.core.id.context.StateEntry; +import org.olat.core.util.StringHelper; +import org.olat.core.util.Util; +import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.i18n.I18nModule; +import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.assessment.AssessmentHelper; +import org.olat.course.nodes.AssessmentToolOptions; +import org.olat.course.nodes.IQTESTCourseNode; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.run.scoring.ScoreEvaluation; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.ims.qti.QTIResultManager; +import org.olat.ims.qti.container.AssessmentContext; +import org.olat.ims.qti.process.AssessmentFactory; +import org.olat.ims.qti.process.AssessmentInstance; +import org.olat.ims.qti.process.FilePersister; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.iq.IQManager; +import org.olat.modules.iq.IQRetrievedEvent; +import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 07.07.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI12PullTestsToolController extends BasicController implements Activateable2 { + + private final Link pullButton; + private DialogBoxController retrieveConfirmationCtr; + + private final IQTESTCourseNode courseNode; + private final CourseEnvironment courseEnv; + private final List<Identity> assessedIdentities; + + @Autowired + private IQManager iqm; + @Autowired + private UserManager userManager; + + public QTI12PullTestsToolController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, + AssessmentToolOptions asOptions, IQTESTCourseNode courseNode) { + super(ureq, wControl); + setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); + + this.courseEnv = courseEnv; + this.courseNode = courseNode; + this.assessedIdentities = asOptions.getIdentities(); + + pullButton = LinkFactory.createButton("menu.pull.tests.title", null, this); + pullButton.setTranslator(getTranslator()); + putInitialPanel(pullButton); + getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout + } + + @Override + protected void doDispose() { + // + } + + @Override + public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { + // + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + if(pullButton == source) { + confirmPull(ureq); + } + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(retrieveConfirmationCtr == source) { + if(DialogBoxUIFactory.isYesEvent(event)) { + doRetrieveTests(); + } + removeAsListenerAndDispose(retrieveConfirmationCtr); + retrieveConfirmationCtr = null; + } + } + + private void confirmPull(UserRequest ureq) { + int count = 0; + StringBuilder fullnames = new StringBuilder(256); + for(Identity assessedIdentity:assessedIdentities) { + if(courseNode.isTestRunning(assessedIdentity, courseEnv)) { + if(fullnames.length() > 0) fullnames.append(", "); + String name = userManager.getUserDisplayName(assessedIdentity); + if(StringHelper.containsNonWhitespace(name)) { + fullnames.append(name); + count++; + } + } + } + + if(count == 0) { + showInfo("retrievetest.nothing.todo"); + } else if(count == 1) { + String title = translate("retrievetest.confirm.title"); + String text = translate("retrievetest.confirm.text", new String[]{ fullnames.toString() }); + retrieveConfirmationCtr = activateYesNoDialog(ureq, title, text, retrieveConfirmationCtr); + } else { + String title = translate("retrievetest.confirm.title"); + String text = translate("retrievetest.confirm.text.plural", new String[]{ fullnames.toString() }); + retrieveConfirmationCtr = activateYesNoDialog(ureq, title, text, retrieveConfirmationCtr); + } + } + + private void doRetrieveTests() { + ICourse course = CourseFactory.loadCourse(courseEnv.getCourseResourceableId()); + for(Identity assessedIdentity:assessedIdentities) { + if(courseNode.isTestRunning(assessedIdentity, courseEnv)) { + IQRetrievedEvent retrieveEvent = new IQRetrievedEvent(assessedIdentity, courseEnv.getCourseResourceableId(), courseNode.getIdent()); + CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(retrieveEvent, retrieveEvent); + retrieveTest(assessedIdentity, course); + } + } + } + + private void retrieveTest(Identity assessedIdentity, ICourse course) { + ModuleConfiguration modConfig = courseNode.getModuleConfiguration(); + + String resourcePathInfo = courseEnv.getCourseResourceableId() + File.separator + courseNode.getIdent(); + AssessmentInstance ai = AssessmentFactory.createAssessmentInstance(assessedIdentity, "", modConfig, false, courseEnv.getCourseResourceableId(), courseNode.getIdent(), resourcePathInfo, null); + //close the test + ai.stop(); + //persist the results + iqm.persistResults(ai); + + //reporting + Document docResReporting = iqm.getResultsReporting(ai, assessedIdentity, I18nModule.getDefaultLocale()); + FilePersister.createResultsReporting(docResReporting, assessedIdentity, ai.getFormattedType(), ai.getAssessID()); + + //olat results + AssessmentContext ac = ai.getAssessmentContext(); + Float score = new Float(ac.getScore()); + Boolean passed = new Boolean(ac.isPassed()); + ScoreEvaluation sceval = new ScoreEvaluation(score, passed, Boolean.FALSE, new Long(ai.getAssessID())); + UserCourseEnvironment userCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course); + courseNode.updateUserScoreEvaluation(sceval, userCourseEnv, assessedIdentity, true); + + //cleanup + ai.cleanUp(); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties index 055cf0e2844017890df06aa4d5c918d77eec877d..ffb723fc72d9a4f236033a762e4ce8ff8a03139c 100644 --- a/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties @@ -1,5 +1,6 @@ menu.title=Test Statistiken +menu.pull.tests.title=Tests einziehen splash.choosetest=Wählen Sie einen Test aus splash.notenoughresults=Für diesen Test/Umfrage gibt es noch zu wenig Resultate ({0} Teilnehmer) @@ -80,4 +81,6 @@ fig.avg=Mittelwert fig.span=Spannweite fig.median=Median print=Drucken -section=Sektion \ No newline at end of file +section=Sektion +retrievetest.confirm.text.plural=Wollen Sie wirklich die Tests von "{0}" einziehen? +retrievetest.nothing.todo=Es gibt zurzeit kein Test dass Sie einziehen k\u00F6nnen. \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_en.properties index 011c14f00fe90574a52b1509c0a02baa565c1141..fa59a3d47a2f9bb3c6bf5f62e9d3699cfc8a831a 100644 --- a/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_en.properties @@ -63,7 +63,10 @@ fig.testId=Test-ID fig.title=Key figures fig.wronganswers=Number of wrong answers menu.title=Test Statistics +menu.pull.tests.title=Pull tests print=Print +retrievetest.confirm.text.plural=Do you really want to pull the tests of "{0}"? +retrievetest.nothing.todo=There isn't tests you can pull at the moment. solution=Solution splash.choosetest=Please choose a Test splash.notenoughresults=We don't have enough data for the selected test/survey. ({0} Participant(s) so far...)