use of uk.ac.ed.ph.jqtiplus.types.ResponseData in project OpenOLAT by OpenOLAT.
the class AssessmentItemDisplayController method handleResponses.
public void handleResponses(UserRequest ureq, Map<Identifier, ResponseInput> stringResponseMap, Map<Identifier, ResponseInput> fileResponseMap, String candidateComment) {
/* Retrieve current JQTI state and set up JQTI controller */
NotificationRecorder notificationRecorder = new NotificationRecorder(NotificationLevel.INFO);
ItemSessionState itemSessionState = itemSessionController.getItemSessionState();
/* Make sure an attempt is allowed */
if (itemSessionState.isEnded()) {
candidateAuditLogger.logAndThrowCandidateException(candidateSession, CandidateExceptionReason.RESPONSES_NOT_EXPECTED, null);
logError("RESPONSES_NOT_EXPECTED", null);
return;
}
/* Build response map in required format for JQTI+.
* NB: The following doesn't test for duplicate keys in the two maps. I'm not sure
* it's worth the effort.
*/
final Map<Identifier, ResponseData> responseDataMap = new HashMap<>();
// final Map<Identifier, CandidateFileSubmission> fileSubmissionMap = new HashMap<>();
final Map<Identifier, AssessmentResponse> assessmentResponseDataMap = new HashMap<>();
if (stringResponseMap != null) {
for (final Entry<Identifier, ResponseInput> stringResponseEntry : stringResponseMap.entrySet()) {
Identifier identifier = stringResponseEntry.getKey();
ResponseInput responseData = stringResponseEntry.getValue();
if (responseData instanceof StringInput) {
responseDataMap.put(identifier, new StringResponseData(((StringInput) responseData).getResponseData()));
} else if (responseData instanceof Base64Input) {
// TODO
} else if (responseData instanceof FileInput) {
}
}
}
if (fileResponseMap != null) {
for (final Entry<Identifier, ResponseInput> fileResponseEntry : fileResponseMap.entrySet()) {
final Identifier identifier = fileResponseEntry.getKey();
final FileInput multipartFile = (FileInput) fileResponseEntry.getValue();
if (!multipartFile.isEmpty()) {
// final CandidateFileSubmission fileSubmission = candidateUploadService.importFileSubmission(candidateSession, multipartFile);
File storedFile = qtiService.importFileSubmission(candidateSession, multipartFile.getMultipartFileInfos());
final FileResponseData fileResponseData = new FileResponseData(storedFile, multipartFile.getContentType(), multipartFile.getFileName());
responseDataMap.put(identifier, fileResponseData);
assessmentResponseDataMap.put(identifier, new AssessmentResponseData(identifier, fileResponseData));
// fileSubmissionMap.put(identifier, fileSubmission);
}
}
}
/* Submit comment (if provided)
* NB: Do this first in case next actions end the item session.
*/
final Date timestamp = ureq.getRequestTimestamp();
if (candidateComment != null) {
try {
itemSessionController.setCandidateComment(timestamp, candidateComment);
} catch (final QtiCandidateStateException e) {
candidateAuditLogger.logAndThrowCandidateException(candidateSession, CandidateExceptionReason.CANDIDATE_COMMENT_FORBIDDEN, e);
logError("CANDIDATE_COMMENT_FORBIDDEN", null);
return;
} catch (final RuntimeException e) {
logError("", e);
// handleExplosion(e, candidateSession);
return;
}
}
/* Attempt to bind responses */
boolean allResponsesValid = false, allResponsesBound = false;
try {
itemSessionController.bindResponses(timestamp, responseDataMap);
/* Note any responses that failed to bind */
final Set<Identifier> badResponseIdentifiers = itemSessionState.getUnboundResponseIdentifiers();
allResponsesBound = badResponseIdentifiers.isEmpty();
/* Now validate the responses according to any constraints specified by the interactions */
if (allResponsesBound) {
final Set<Identifier> invalidResponseIdentifiers = itemSessionState.getInvalidResponseIdentifiers();
allResponsesValid = invalidResponseIdentifiers.isEmpty();
}
/* (We commit responses immediately here) */
itemSessionController.commitResponses(timestamp);
/* Invoke response processing (only if responses are valid) */
if (allResponsesValid) {
itemSessionController.performResponseProcessing(timestamp);
}
} catch (final QtiCandidateStateException e) {
candidateAuditLogger.logAndThrowCandidateException(candidateSession, CandidateExceptionReason.RESPONSES_NOT_EXPECTED, null);
logError("RESPONSES_NOT_EXPECTED", e);
return;
} catch (final RuntimeException e) {
logError("", e);
// handleExplosion(e, candidateSession);
return;
}
/* Record resulting attempt and event */
final CandidateItemEventType eventType = allResponsesBound ? (allResponsesValid ? CandidateItemEventType.ATTEMPT_VALID : CandidateItemEventType.RESPONSE_INVALID) : CandidateItemEventType.RESPONSE_BAD;
final CandidateEvent candidateEvent = qtiService.recordCandidateItemEvent(candidateSession, null, entry, eventType, itemSessionState, notificationRecorder);
candidateAuditLogger.logCandidateEvent(candidateEvent, assessmentResponseDataMap);
lastEvent = candidateEvent;
/* Record current result state, or finish session */
updateSessionFinishedStatus(ureq);
}
use of uk.ac.ed.ph.jqtiplus.types.ResponseData in project OpenOLAT by OpenOLAT.
the class AssessmentTestDisplayController method handleResponse.
// public CandidateSession handleResponses(final CandidateSessionContext candidateSessionContext,
// final Map<Identifier, StringResponseData> stringResponseMap,
// final Map<Identifier, MultipartFile> fileResponseMap,
// final String candidateComment)
private void handleResponse(UserRequest ureq, Map<Identifier, ResponseInput> stringResponseMap, Map<Identifier, ResponseInput> fileResponseMap, String candidateComment) {
NotificationRecorder notificationRecorder = new NotificationRecorder(NotificationLevel.INFO);
TestSessionState testSessionState = testSessionController.getTestSessionState();
TestPlanNodeKey currentItemKey = testSessionState.getCurrentItemKey();
if (currentItemKey == null && getLastEvent() != null && getLastEvent().getTestEventType() == CandidateTestEventType.REVIEW_ITEM) {
// someone try to send the form in review with tab / return
return;
}
if (!qtiWorksCtrl.validatePresentedItem(currentItemKey)) {
logError("Response send by browser doesn't match current item key", null);
ServletUtil.printOutRequestParameters(ureq.getHttpReq());
if (candidateSession != null && candidateSession.getFinishTime() != null) {
showWarning("error.test.closed");
} else {
showWarning("error.reload.question");
}
// this is not the right node in the plan
return;
}
final Map<Identifier, File> fileSubmissionMap = new HashMap<>();
final Map<Identifier, ResponseData> responseDataMap = new HashMap<>();
if (stringResponseMap != null) {
for (final Entry<Identifier, ResponseInput> responseEntry : stringResponseMap.entrySet()) {
final Identifier identifier = responseEntry.getKey();
final ResponseInput responseData = responseEntry.getValue();
if (responseData instanceof StringInput) {
responseDataMap.put(identifier, new StringResponseData(((StringInput) responseData).getResponseData()));
} else if (responseData instanceof Base64Input) {
// only used from drawing interaction
Base64Input fileInput = (Base64Input) responseData;
String filename = "submitted_image.png";
File storedFile = qtiService.importFileSubmission(candidateSession, filename, fileInput.getResponseData());
responseDataMap.put(identifier, new FileResponseData(storedFile, fileInput.getContentType(), storedFile.getName()));
fileSubmissionMap.put(identifier, storedFile);
} else if (responseData instanceof FileInput) {
FileInput fileInput = (FileInput) responseData;
File storedFile = qtiService.importFileSubmission(candidateSession, fileInput.getMultipartFileInfos());
responseDataMap.put(identifier, new FileResponseData(storedFile, fileInput.getContentType(), storedFile.getName()));
fileSubmissionMap.put(identifier, storedFile);
}
}
}
ParentPartItemRefs parentParts = getParentSection(currentItemKey);
String assessmentItemIdentifier = currentItemKey.getIdentifier().toString();
AssessmentItemSession itemSession = qtiService.getOrCreateAssessmentItemSession(candidateSession, parentParts, assessmentItemIdentifier);
if (fileResponseMap != null) {
for (Entry<Identifier, ResponseInput> fileResponseEntry : fileResponseMap.entrySet()) {
Identifier identifier = fileResponseEntry.getKey();
FileInput multipartFile = (FileInput) fileResponseEntry.getValue();
if (!multipartFile.isEmpty()) {
File storedFile = qtiService.importFileSubmission(candidateSession, multipartFile.getMultipartFileInfos());
responseDataMap.put(identifier, new FileResponseData(storedFile, multipartFile.getContentType(), storedFile.getName()));
fileSubmissionMap.put(identifier, storedFile);
}
}
}
Map<Identifier, AssessmentResponse> candidateResponseMap = qtiService.getAssessmentResponses(itemSession);
for (Entry<Identifier, ResponseData> responseEntry : responseDataMap.entrySet()) {
Identifier responseIdentifier = responseEntry.getKey();
ResponseData responseData = responseEntry.getValue();
AssessmentResponse candidateItemResponse;
if (candidateResponseMap.containsKey(responseIdentifier)) {
candidateItemResponse = candidateResponseMap.get(responseIdentifier);
} else {
candidateItemResponse = qtiService.createAssessmentResponse(candidateSession, itemSession, responseIdentifier.toString(), ResponseLegality.VALID, responseData.getType());
}
switch(responseData.getType()) {
case STRING:
{
List<String> data = ((StringResponseData) responseData).getResponseData();
String stringuifiedResponse = ResponseFormater.format(data);
candidateItemResponse.setStringuifiedResponse(stringuifiedResponse);
break;
}
case FILE:
{
if (fileSubmissionMap.get(responseIdentifier) != null) {
File storedFile = fileSubmissionMap.get(responseIdentifier);
candidateItemResponse.setStringuifiedResponse(storedFile.getName());
}
break;
}
default:
throw new OLATRuntimeException("Unexpected switch case: " + responseData.getType());
}
candidateResponseMap.put(responseIdentifier, candidateItemResponse);
}
boolean allResponsesValid = true;
boolean allResponsesBound = true;
final Date timestamp = ureq.getRequestTimestamp();
if (candidateComment != null) {
testSessionController.setCandidateCommentForCurrentItem(timestamp, candidateComment);
}
/* Attempt to bind responses (and maybe perform RP & OP) */
testSessionController.handleResponsesToCurrentItem(timestamp, responseDataMap);
/* Classify this event */
final SubmissionMode submissionMode = testSessionController.getCurrentTestPart().getSubmissionMode();
final CandidateItemEventType candidateItemEventType;
if (allResponsesValid) {
candidateItemEventType = submissionMode == SubmissionMode.INDIVIDUAL ? CandidateItemEventType.ATTEMPT_VALID : CandidateItemEventType.RESPONSE_VALID;
} else {
candidateItemEventType = allResponsesBound ? CandidateItemEventType.RESPONSE_INVALID : CandidateItemEventType.RESPONSE_BAD;
}
/* Record resulting event */
final CandidateEvent candidateEvent = qtiService.recordCandidateTestEvent(candidateSession, testEntry, entry, CandidateTestEventType.ITEM_EVENT, candidateItemEventType, currentItemKey, testSessionState, notificationRecorder);
candidateAuditLogger.logCandidateEvent(candidateEvent, candidateResponseMap);
this.lastEvent = candidateEvent;
/* Record current result state */
AssessmentResult assessmentResult = computeAndRecordTestAssessmentResult(timestamp, testSessionState, false);
ItemSessionState itemSessionState = testSessionState.getCurrentItemSessionState();
long itemDuration = itemSessionState.getDurationAccumulated();
itemSession.setDuration(itemDuration);
ItemResult itemResult = assessmentResult.getItemResult(assessmentItemIdentifier);
collectOutcomeVariablesForItemSession(itemResult, itemSession);
/* Persist CandidateResponse entities */
qtiService.recordTestAssessmentResponses(itemSession, candidateResponseMap.values());
/* Save any change to session state */
candidateSession = qtiService.updateAssessmentTestSession(candidateSession);
addToHistory(ureq, this);
checkConcurrentExit(ureq);
}
use of uk.ac.ed.ph.jqtiplus.types.ResponseData in project OpenOLAT by OpenOLAT.
the class AssessmentObjectComponentRenderer method renderExtendedTextBox.
protected void renderExtendedTextBox(AssessmentRenderer renderer, StringOutput sb, AssessmentObjectComponent component, AssessmentItem assessmentItem, ItemSessionState itemSessionState, ExtendedTextInteraction interaction) {
ResponseData responseInput = getResponseInput(itemSessionState, interaction.getResponseIdentifier());
ResponseDeclaration responseDeclaration = getResponseDeclaration(assessmentItem, interaction.getResponseIdentifier());
Cardinality cardinality = responseDeclaration == null ? null : responseDeclaration.getCardinality();
if (cardinality != null && (cardinality.isRecord() || cardinality.isSingle())) {
String responseInputString = extractSingleCardinalityResponseInput(responseInput);
renderExtendedTextBox(renderer, sb, component, assessmentItem, itemSessionState, interaction, responseInputString);
} else {
if (interaction.getMaxStrings() != null) {
int maxStrings = interaction.getMaxStrings().intValue();
for (int i = 0; i < maxStrings; i++) {
String responseInputString = extractResponseInputAt(responseInput, i);
renderExtendedTextBox(renderer, sb, component, assessmentItem, itemSessionState, interaction, responseInputString);
}
} else {
// <xsl:with-param name="stringsCount" select="if (exists($responseValue)) then max(($minStrings, qw:get-cardinality-size($responseValue))) else $minStrings"/>
int stringCounts = interaction.getMinStrings();
Value responseValue = AssessmentRenderFunctions.getResponseValue(assessmentItem, itemSessionState, interaction.getResponseIdentifier(), renderer.isSolutionMode());
if (exists(responseValue)) {
stringCounts = java.lang.Math.max(interaction.getMinStrings(), getCardinalitySize(responseValue));
}
for (int i = 0; i < stringCounts; i++) {
String responseInputString = extractResponseInputAt(responseInput, i);
renderExtendedTextBox(renderer, sb, component, assessmentItem, itemSessionState, interaction, responseInputString);
}
}
}
}
use of uk.ac.ed.ph.jqtiplus.types.ResponseData in project openolat by klemens.
the class AssessmentItemDisplayController method handleResponses.
public void handleResponses(UserRequest ureq, Map<Identifier, ResponseInput> stringResponseMap, Map<Identifier, ResponseInput> fileResponseMap, String candidateComment) {
/* Retrieve current JQTI state and set up JQTI controller */
NotificationRecorder notificationRecorder = new NotificationRecorder(NotificationLevel.INFO);
ItemSessionState itemSessionState = itemSessionController.getItemSessionState();
/* Make sure an attempt is allowed */
if (itemSessionState.isEnded()) {
candidateAuditLogger.logAndThrowCandidateException(candidateSession, CandidateExceptionReason.RESPONSES_NOT_EXPECTED, null);
logError("RESPONSES_NOT_EXPECTED", null);
return;
}
/* Build response map in required format for JQTI+.
* NB: The following doesn't test for duplicate keys in the two maps. I'm not sure
* it's worth the effort.
*/
final Map<Identifier, ResponseData> responseDataMap = new HashMap<>();
// final Map<Identifier, CandidateFileSubmission> fileSubmissionMap = new HashMap<>();
final Map<Identifier, AssessmentResponse> assessmentResponseDataMap = new HashMap<>();
if (stringResponseMap != null) {
for (final Entry<Identifier, ResponseInput> stringResponseEntry : stringResponseMap.entrySet()) {
Identifier identifier = stringResponseEntry.getKey();
ResponseInput responseData = stringResponseEntry.getValue();
if (responseData instanceof StringInput) {
responseDataMap.put(identifier, new StringResponseData(((StringInput) responseData).getResponseData()));
} else if (responseData instanceof Base64Input) {
// TODO
} else if (responseData instanceof FileInput) {
}
}
}
if (fileResponseMap != null) {
for (final Entry<Identifier, ResponseInput> fileResponseEntry : fileResponseMap.entrySet()) {
final Identifier identifier = fileResponseEntry.getKey();
final FileInput multipartFile = (FileInput) fileResponseEntry.getValue();
if (!multipartFile.isEmpty()) {
// final CandidateFileSubmission fileSubmission = candidateUploadService.importFileSubmission(candidateSession, multipartFile);
File storedFile = qtiService.importFileSubmission(candidateSession, multipartFile.getMultipartFileInfos());
final FileResponseData fileResponseData = new FileResponseData(storedFile, multipartFile.getContentType(), multipartFile.getFileName());
responseDataMap.put(identifier, fileResponseData);
assessmentResponseDataMap.put(identifier, new AssessmentResponseData(identifier, fileResponseData));
// fileSubmissionMap.put(identifier, fileSubmission);
}
}
}
/* Submit comment (if provided)
* NB: Do this first in case next actions end the item session.
*/
final Date timestamp = ureq.getRequestTimestamp();
if (candidateComment != null) {
try {
itemSessionController.setCandidateComment(timestamp, candidateComment);
} catch (final QtiCandidateStateException e) {
candidateAuditLogger.logAndThrowCandidateException(candidateSession, CandidateExceptionReason.CANDIDATE_COMMENT_FORBIDDEN, e);
logError("CANDIDATE_COMMENT_FORBIDDEN", null);
return;
} catch (final RuntimeException e) {
logError("", e);
// handleExplosion(e, candidateSession);
return;
}
}
/* Attempt to bind responses */
boolean allResponsesValid = false, allResponsesBound = false;
try {
itemSessionController.bindResponses(timestamp, responseDataMap);
/* Note any responses that failed to bind */
final Set<Identifier> badResponseIdentifiers = itemSessionState.getUnboundResponseIdentifiers();
allResponsesBound = badResponseIdentifiers.isEmpty();
/* Now validate the responses according to any constraints specified by the interactions */
if (allResponsesBound) {
final Set<Identifier> invalidResponseIdentifiers = itemSessionState.getInvalidResponseIdentifiers();
allResponsesValid = invalidResponseIdentifiers.isEmpty();
}
/* (We commit responses immediately here) */
itemSessionController.commitResponses(timestamp);
/* Invoke response processing (only if responses are valid) */
if (allResponsesValid) {
itemSessionController.performResponseProcessing(timestamp);
}
} catch (final QtiCandidateStateException e) {
candidateAuditLogger.logAndThrowCandidateException(candidateSession, CandidateExceptionReason.RESPONSES_NOT_EXPECTED, null);
logError("RESPONSES_NOT_EXPECTED", e);
return;
} catch (final RuntimeException e) {
logError("", e);
// handleExplosion(e, candidateSession);
return;
}
/* Record resulting attempt and event */
final CandidateItemEventType eventType = allResponsesBound ? (allResponsesValid ? CandidateItemEventType.ATTEMPT_VALID : CandidateItemEventType.RESPONSE_INVALID) : CandidateItemEventType.RESPONSE_BAD;
final CandidateEvent candidateEvent = qtiService.recordCandidateItemEvent(candidateSession, null, entry, eventType, itemSessionState, notificationRecorder);
candidateAuditLogger.logCandidateEvent(candidateEvent, assessmentResponseDataMap);
lastEvent = candidateEvent;
/* Record current result state, or finish session */
updateSessionFinishedStatus(ureq);
}
use of uk.ac.ed.ph.jqtiplus.types.ResponseData in project openolat by klemens.
the class AssessmentObjectComponentRenderer method renderExtendedTextBox.
protected void renderExtendedTextBox(AssessmentRenderer renderer, StringOutput sb, AssessmentObjectComponent component, AssessmentItem assessmentItem, ItemSessionState itemSessionState, ExtendedTextInteraction interaction) {
ResponseData responseInput = getResponseInput(itemSessionState, interaction.getResponseIdentifier());
ResponseDeclaration responseDeclaration = getResponseDeclaration(assessmentItem, interaction.getResponseIdentifier());
Cardinality cardinality = responseDeclaration == null ? null : responseDeclaration.getCardinality();
if (cardinality != null && (cardinality.isRecord() || cardinality.isSingle())) {
String responseInputString = extractSingleCardinalityResponseInput(responseInput);
renderExtendedTextBox(renderer, sb, component, assessmentItem, itemSessionState, interaction, responseInputString);
} else {
if (interaction.getMaxStrings() != null) {
int maxStrings = interaction.getMaxStrings().intValue();
for (int i = 0; i < maxStrings; i++) {
String responseInputString = extractResponseInputAt(responseInput, i);
renderExtendedTextBox(renderer, sb, component, assessmentItem, itemSessionState, interaction, responseInputString);
}
} else {
// <xsl:with-param name="stringsCount" select="if (exists($responseValue)) then max(($minStrings, qw:get-cardinality-size($responseValue))) else $minStrings"/>
int stringCounts = interaction.getMinStrings();
Value responseValue = AssessmentRenderFunctions.getResponseValue(assessmentItem, itemSessionState, interaction.getResponseIdentifier(), renderer.isSolutionMode());
if (exists(responseValue)) {
stringCounts = java.lang.Math.max(interaction.getMinStrings(), getCardinalitySize(responseValue));
}
for (int i = 0; i < stringCounts; i++) {
String responseInputString = extractResponseInputAt(responseInput, i);
renderExtendedTextBox(renderer, sb, component, assessmentItem, itemSessionState, interaction, responseInputString);
}
}
}
}
Aggregations