/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.course.nodes.iq;
import java.io.File;
import java.util.Date;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.DateChooser;
import org.olat.core.gui.components.form.flexible.elements.SelectionElement;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.StaticTextElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
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.logging.AssertException;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.LocalFolderImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.fileresource.FileResourceManager;
import org.olat.ims.qti.process.AssessmentInstance;
import org.olat.ims.qti.process.QTIHelper;
import org.olat.modules.ModuleConfiguration;
import org.olat.resource.OLATResource;
/**
* Test configuration form.
* Used for configuring Test, Self-test, and Questionnaire(aka Survey).
* <p>
* Initial Date: Mar 3, 2004
*
* @author Mike Stock
*/
public class IQ12EditForm extends FormBasicController {
private static final String[] correctionModeKeys = new String[] { "auto", "manual" };
private SingleSelection correctionModeEl;
private SelectionElement scoreInfo;
private SingleSelection summary;
private SelectionElement showResultsDateDependentButton;
private DateChooser startDateElement;
private DateChooser endDateElement;
private SelectionElement showResultsAfterFinishTest;
private SelectionElement showResultsOnHomePage;
private StaticTextElement minScoreEl, maxScoreEl, cutValueEl;
private ModuleConfiguration modConfig;
private String[] correctionModeValues;
private String configKeyType;
private boolean isAssessment, isSelfTest, isSurvey;
private final boolean hasEssay;
/**
* Constructor for the qti configuration form
* @param ureq
* @param wControl
* @param modConfig
*/
IQ12EditForm(UserRequest ureq, WindowControl wControl, ModuleConfiguration modConfig, boolean hasEssay) {
super (ureq, wControl);
this.modConfig = modConfig;
this.hasEssay = hasEssay;
configKeyType = (String)modConfig.get(IQEditController.CONFIG_KEY_TYPE);
isAssessment = configKeyType.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS);
isSelfTest = configKeyType.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF);
isSurvey = configKeyType.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY);
correctionModeValues = new String[]{
translate("correction.auto"),
translate("correction.manual")
};
initForm(ureq);
}
@Override
protected boolean validateFormLogic (UserRequest ureq) {
boolean allOk = true;
if(summary != null && summary.isEnabled()) {
summary.clearError();
if(!summary.isOneSelected()) {
summary.setErrorKey("form.legende.mandatory", null);
allOk &= false;
}
}
startDateElement.clearError();
endDateElement.clearError();
if (startDateElement.isVisible()) {
if (startDateElement.isEmpty()) {
startDateElement.setErrorKey("qti.form.date.start.error.mandatory", null);
allOk &= false;
} else {
if (startDateElement.getDate() == null) {
startDateElement.setErrorKey("qti.form.date.error.format", null);
allOk &= false;
}
}
if (!endDateElement.isEmpty()) {
if (endDateElement.getDate() == null) {
endDateElement.setErrorKey("qti.form.date.error.format", null);
allOk &= false;
}
if (endDateElement.getDate().before(startDateElement.getDate())) {
endDateElement.setErrorKey("qti.form.date.error.endbeforebegin", null);
allOk &= false;
}
}
}
return allOk;
}
@Override
protected void formOK(UserRequest ureq) {
if(correctionModeEl != null &&correctionModeEl.isOneSelected()) {
modConfig.setStringValue(IQEditController.CONFIG_CORRECTION_MODE, correctionModeEl.getSelectedKey());
}
// Only tests and selftests have summaries and score progress
if (!isSurvey) {
modConfig.set(IQEditController.CONFIG_KEY_SUMMARY, getSummary());
modConfig.set(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS, new Boolean(isShowResultsDateDependent()));
modConfig.set(IQEditController.CONFIG_KEY_RESULTS_START_DATE, getShowResultsStartDate());
modConfig.set(IQEditController.CONFIG_KEY_RESULTS_END_DATE, getShowResultsEndDate());
modConfig.set(IQEditController.CONFIG_KEY_RESULT_ON_FINISH, isShowResultsAfterFinishTest());
modConfig.set(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE, isShowResultsOnHomePage());
modConfig.set(IQEditController.CONFIG_KEY_ENABLESCOREINFO, isEnableScoreInfo());
}
fireEvent(ureq, Event.DONE_EVENT);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
minScoreEl = uifactory.addStaticTextElement("score.min", "", formLayout);
minScoreEl.setVisible(false);
maxScoreEl = uifactory.addStaticTextElement("score.max", "", formLayout);
maxScoreEl.setVisible(false);
cutValueEl = uifactory.addStaticTextElement("score.cut", "", formLayout);
cutValueEl.setVisible(false);
if (isAssessment) {
correctionModeEl = uifactory.addRadiosVertical("correction.mode", "correction.mode", formLayout, correctionModeKeys, correctionModeValues);
String mode = modConfig.getStringValue(IQEditController.CONFIG_CORRECTION_MODE);
boolean selected = false;
for(String correctionModeKey:correctionModeKeys) {
if(correctionModeKey.equals(mode)) {
correctionModeEl.select(correctionModeKey, true);
selected = true;
}
}
if(!selected) {
if(hasEssay) {
correctionModeEl.select(correctionModeKeys[1], true);
} else {
correctionModeEl.select(correctionModeKeys[0], true);
}
}
}
//Show score infos on start page
Boolean bEnableScoreInfos = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_ENABLESCOREINFO);
boolean enableScoreInfos = (bEnableScoreInfos != null) ? bEnableScoreInfos.booleanValue() : true;
scoreInfo = uifactory.addCheckboxesHorizontal("qti_scoreInfo", "qti.form.scoreinfo", formLayout, new String[]{"xx"}, new String[]{null});
scoreInfo.select("xx", enableScoreInfos);
if (isAssessment || isSelfTest) {
scoreInfo.select("xx", enableScoreInfos);
scoreInfo.addActionListener(FormEvent.ONCLICK);
} else {// isSurvey
scoreInfo.setVisible(false);
}
//migration: check if old tests have no summary
String configuredSummary = (String) modConfig.get(IQEditController.CONFIG_KEY_SUMMARY);
boolean noSummary = configuredSummary!=null && configuredSummary.equals(AssessmentInstance.QMD_ENTRY_SUMMARY_NONE) ? true : false;
Boolean showResultOnHomePage = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE);
boolean confEnableShowResultOnHomePage = (showResultOnHomePage != null) ? showResultOnHomePage.booleanValue() : false;
confEnableShowResultOnHomePage = !noSummary && confEnableShowResultOnHomePage;
showResultsOnHomePage = uifactory.addCheckboxesHorizontal("qti_enableResultsOnHomePage", "qti.form.results.onhomepage", formLayout, new String[]{"xx"}, new String[]{null});
showResultsOnHomePage.select("xx", confEnableShowResultOnHomePage);
showResultsOnHomePage.addActionListener(FormEvent.ONCLICK);
showResultsOnHomePage.setVisible(!isSurvey);
Boolean showResultsActive = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS);
boolean showResultsDateDependent = false; // default false
if (showResultsActive != null) {
showResultsDateDependent = showResultsActive.booleanValue();
}
showResultsDateDependentButton = uifactory.addCheckboxesHorizontal("qti_showresult", "qti.form.show.results", formLayout, new String[]{"xx"}, new String[]{null});
if (isAssessment || isSelfTest) {
showResultsDateDependentButton.select("xx", showResultsDateDependent);
showResultsDateDependentButton.addActionListener(FormEvent.ONCLICK);
} else {
showResultsDateDependentButton.setEnabled(false);
}
Date startDate = null;
if(modConfig.get(IQEditController.CONFIG_KEY_RESULTS_START_DATE) instanceof Date) {
startDate = (Date)modConfig.get(IQEditController.CONFIG_KEY_RESULTS_START_DATE);
}
startDateElement = uifactory.addDateChooser("qti_form_start_date", "qti.form.date.start", null, formLayout);
startDateElement.setDateChooserTimeEnabled(true);
startDateElement.setDate(startDate);
startDateElement.setMandatory(true);
Date endDate = null;
if(modConfig.get(IQEditController.CONFIG_KEY_RESULTS_END_DATE) instanceof Date) {
endDate = (Date) modConfig.get(IQEditController.CONFIG_KEY_RESULTS_END_DATE);
}
endDateElement = uifactory.addDateChooser("qti_form_end_date", "qti.form.date.end", null, formLayout);
endDateElement.setDateChooserTimeEnabled(true);
endDateElement.setDate(endDate);
Boolean showResultOnFinish = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_RESULT_ON_FINISH);
boolean confEnableShowResultOnFinish = (showResultOnFinish != null) ? showResultOnFinish.booleanValue() : true;
confEnableShowResultOnFinish = !noSummary && confEnableShowResultOnFinish;
showResultsAfterFinishTest = uifactory.addCheckboxesHorizontal("qti_enableResultsOnFinish", "qti.form.results.onfinish", formLayout, new String[]{"xx"}, new String[]{null});
showResultsAfterFinishTest.select("xx", confEnableShowResultOnFinish);
showResultsAfterFinishTest.addActionListener(FormEvent.ONCLICK);
showResultsAfterFinishTest.setVisible(!isSurvey);
String[] summaryKeys = new String[] {
AssessmentInstance.QMD_ENTRY_SUMMARY_COMPACT,
AssessmentInstance.QMD_ENTRY_SUMMARY_SECTION,
AssessmentInstance.QMD_ENTRY_SUMMARY_DETAILED
};
String[] summaryValues = new String[] {
translate("qti.form.summary.compact"),
translate("qti.form.summary.section"),
translate("qti.form.summary.detailed")
};
summary = uifactory.addRadiosVertical("qti_form_summary", "qti.form.summary", formLayout, summaryKeys, summaryValues);
String confSummary = (String) modConfig.get(IQEditController.CONFIG_KEY_SUMMARY);
if (confSummary == null || noSummary) {
confSummary = AssessmentInstance.QMD_ENTRY_SUMMARY_COMPACT;
}
if (isAssessment || isSelfTest) {
for(String summaryKey:summaryKeys) {
if(summaryKey.equals(confSummary)) {
summary.select(summaryKey, true);
}
}
} else {
summary.setEnabled(false);
}
uifactory.addFormSubmitButton("submit", formLayout);
update();
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
update();
}
private void update() {
summary.setVisible(showResultsAfterFinishTest.isSelected(0) || showResultsOnHomePage.isSelected(0) );
showResultsDateDependentButton.setVisible(showResultsOnHomePage.isSelected(0));
if (!startDateElement.isVisible()) {
startDateElement.setValue("");
}
startDateElement.clearError();
startDateElement.setVisible(
showResultsDateDependentButton.isVisible() &&
showResultsDateDependentButton.isSelected(0)
);
endDateElement.clearError();
if (!endDateElement.isVisible()) endDateElement.setValue("");
endDateElement.setVisible(startDateElement.isVisible());
flc.setDirty(true);
}
/**
* Update the module configuration from the qti file: read min/max/cut values
* @param res
*/
protected void update(OLATResource res) {
FileResourceManager frm = FileResourceManager.getInstance();
File unzippedRoot = frm.unzipFileResource(res);
//with VFS FIXME:pb:c: remove casts to LocalFileImpl and LocalFolderImpl if no longer needed.
VFSContainer vfsUnzippedRoot = new LocalFolderImpl(unzippedRoot);
VFSItem vfsQTI = vfsUnzippedRoot.resolve("qti.xml");
if (vfsQTI==null){
throw new AssertException("qti file did not exist even it should be guaranteed by repositor check-in ");
}
//ensures that InputStream is closed in every case.
Document doc = QTIHelper.getDocument((LocalFileImpl)vfsQTI);
if(doc == null){
//error reading qti file (existence check was made before)
throw new AssertException("qti file could not be read " + ((LocalFileImpl)vfsQTI).getBasefile().getAbsolutePath());
}
// Extract min, max and cut value
Float minValue = null, maxValue = null, cutValue = null;
Element decvar = (Element) doc.selectSingleNode("questestinterop/assessment/outcomes_processing/outcomes/decvar");
if (decvar != null) {
Attribute minval = decvar.attribute("minvalue");
if (minval != null) {
String mv = minval.getValue();
try {
minValue = new Float(Float.parseFloat(mv));
} catch (NumberFormatException e1) {
// if not correct in qti file -> ignore
}
}
Attribute maxval = decvar.attribute("maxvalue");
if (maxval != null) {
String mv = maxval.getValue();
try {
maxValue = new Float(Float.parseFloat(mv));
} catch (NumberFormatException e1) {
// if not correct in qti file -> ignore
}
}
Attribute cutval = decvar.attribute("cutvalue");
if (cutval != null) {
String cv = cutval.getValue();
try {
cutValue = new Float(Float.parseFloat(cv));
} catch (NumberFormatException e1) {
// if not correct in qti file -> ignore
}
}
}
// Put values to module configuration
minScoreEl.setValue(minValue == null ? "" : AssessmentHelper.getRoundedScore(minValue));
minScoreEl.setVisible(minValue != null);
maxScoreEl.setValue(maxValue == null ? "" : AssessmentHelper.getRoundedScore(maxValue));
maxScoreEl.setVisible(maxValue != null);
cutValueEl.setValue(cutValue == null ? "" : AssessmentHelper.getRoundedScore(cutValue));
cutValueEl.setVisible(cutValue != null);
}
/**
* @return summary type: compact or detailed
*/
private String getSummary() { return summary.getSelectedKey();}
/**
*
* @return true is the results are shown date dependent
*/
private boolean isShowResultsDateDependent() { return showResultsDateDependentButton.isSelected(0); }
/**
*
* @return Returns the start date for the result visibility.
*/
private Date getShowResultsStartDate() { return startDateElement.getDate(); }
/**
*
* @return Returns the end date for the result visibility.
*/
private Date getShowResultsEndDate() { return endDateElement.getDate(); }
/**
*
* @return Returns true if the results are shown after test finished.
*/
private boolean isShowResultsAfterFinishTest() { return showResultsAfterFinishTest.isSelected(0); }
/**
*
* @return Returns true if the results are shown on the test home page.
*/
private boolean isShowResultsOnHomePage() { return showResultsOnHomePage.isSelected(0); }
/**
* @return true: score-info on start-page is enabled
*/
private boolean isEnableScoreInfo() { return scoreInfo.isSelected(0); }
@Override
protected void doDispose() {
//
}
}