/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <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 the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <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>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.ims.qti21.manager.archive;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.media.MediaResource;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.User;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Formatter;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.io.ShieldOutputStream;
import org.olat.core.util.openxml.OpenXMLWorkbook;
import org.olat.core.util.openxml.OpenXMLWorkbookResource;
import org.olat.core.util.openxml.OpenXMLWorksheet;
import org.olat.core.util.openxml.OpenXMLWorksheet.Row;
import org.olat.core.util.openxml.workbookstyle.CellStyle;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.archiver.ExportFormat;
import org.olat.course.nodes.CourseNode;
import org.olat.fileresource.FileResourceManager;
import org.olat.ims.qti.export.QTIArchiver;
import org.olat.ims.qti.export.QTIExportFormatter;
import org.olat.ims.qti.export.helper.IdentityAnonymizerCallback;
import org.olat.ims.qti21.AssessmentItemSession;
import org.olat.ims.qti21.AssessmentResponse;
import org.olat.ims.qti21.AssessmentTestSession;
import org.olat.ims.qti21.QTI21Service;
import org.olat.ims.qti21.manager.AssessmentResponseDAO;
import org.olat.ims.qti21.manager.QTI21ServiceImpl;
import org.olat.ims.qti21.manager.archive.interactions.AssociateInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.ChoiceInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.DefaultInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.ExtendedTextInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.GapMatchInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.GraphicAssociateInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.GraphicGapMatchInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.GraphicOrderInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.HotspotInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.HottextInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.InlineChoiceInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.InteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.MatchInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.MediaInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.NoOutputInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.OrderInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.PositionObjectInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.SelectPointInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.SliderInteractionArchive;
import org.olat.ims.qti21.manager.archive.interactions.TextEntryInteractionArchive;
import org.olat.ims.qti21.model.QTI21StatisticSearchParams;
import org.olat.ims.qti21.ui.QTI21RuntimeController;
import org.olat.modules.assessment.AssessmentEntry;
import org.olat.repository.RepositoryEntry;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.AssociateInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.CustomInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.DrawingInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.EndAttemptInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.ExtendedTextInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.GapMatchInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.GraphicAssociateInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.GraphicGapMatchInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.GraphicOrderInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.HotspotInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.HottextInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.InlineChoiceInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.Interaction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.MatchInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.MediaInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.OrderInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.PositionObjectInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.SelectPointInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.SliderInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.TextEntryInteraction;
import uk.ac.ed.ph.jqtiplus.node.item.interaction.UploadInteraction;
import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef;
import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
import uk.ac.ed.ph.jqtiplus.types.Identifier;
/**
*
* Initial date: 22.04.2016<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class QTI21ArchiveFormat {
private static final OLog log = Tracing.createLoggerFor(QTI21ArchiveFormat.class);
private Translator translator;
private ResolvedAssessmentTest resolvedAssessmentTest;
private List<UserPropertyHandler> userPropertyHandlers;
private IdentityAnonymizerCallback anonymizerCallback;
private final QTI21StatisticSearchParams searchParams;
private ExportFormat exportConfig;
private List<ItemInfos> itemInfos;
private final Map<String, InteractionArchive> interactionArchiveMap = new HashMap<>();
private final QTI21Service qtiService;
private final UserManager userManager;
private final AssessmentResponseDAO responseDao;
public QTI21ArchiveFormat(Locale locale, QTI21StatisticSearchParams searchParams) {
this.searchParams = searchParams;
if(searchParams.getArchiveOptions() == null || searchParams.getArchiveOptions().getExportFormat() == null) {
exportConfig = new ExportFormat(true, true, true, true, true);
} else {
exportConfig = searchParams.getArchiveOptions().getExportFormat();
}
userManager = CoreSpringFactory.getImpl(UserManager.class);
qtiService = CoreSpringFactory.getImpl(QTI21ServiceImpl.class);
responseDao = CoreSpringFactory.getImpl(AssessmentResponseDAO.class);
userPropertyHandlers = userManager.getUserPropertyHandlersFor(QTIArchiver.TEST_USER_PROPERTIES, true);
translator = Util.createPackageTranslator(QTI21RuntimeController.class, locale);
translator = Util.createPackageTranslator(QTIExportFormatter.class, locale, translator);
translator = userManager.getPropertyHandlerTranslator(translator);
initInteractionWriters();
}
private void initInteractionWriters() {
interactionArchiveMap.put(AssociateInteraction.QTI_CLASS_NAME, new AssociateInteractionArchive()); //ok
interactionArchiveMap.put(ChoiceInteraction.QTI_CLASS_NAME, new ChoiceInteractionArchive()); //ok
interactionArchiveMap.put(DrawingInteraction.QTI_CLASS_NAME, new DefaultInteractionArchive()); //like file
interactionArchiveMap.put(ExtendedTextInteraction.QTI_CLASS_NAME, new ExtendedTextInteractionArchive()); //ok
interactionArchiveMap.put(GapMatchInteraction.QTI_CLASS_NAME, new GapMatchInteractionArchive()); //ok
interactionArchiveMap.put(GraphicAssociateInteraction.QTI_CLASS_NAME, new GraphicAssociateInteractionArchive()); //ok
interactionArchiveMap.put(GraphicGapMatchInteraction.QTI_CLASS_NAME, new GraphicGapMatchInteractionArchive()); //ok
interactionArchiveMap.put(GraphicOrderInteraction.QTI_CLASS_NAME, new GraphicOrderInteractionArchive()); //ok
interactionArchiveMap.put(HotspotInteraction.QTI_CLASS_NAME, new HotspotInteractionArchive()); //ok
interactionArchiveMap.put(SelectPointInteraction.QTI_CLASS_NAME, new SelectPointInteractionArchive()); //ok
interactionArchiveMap.put(HottextInteraction.QTI_CLASS_NAME, new HottextInteractionArchive()); //ok
interactionArchiveMap.put(MatchInteraction.QTI_CLASS_NAME, new MatchInteractionArchive()); //ok
interactionArchiveMap.put(MediaInteraction.QTI_CLASS_NAME, new MediaInteractionArchive());
interactionArchiveMap.put(OrderInteraction.QTI_CLASS_NAME, new OrderInteractionArchive()); //ok
interactionArchiveMap.put(PositionObjectInteraction.QTI_CLASS_NAME, new PositionObjectInteractionArchive());
interactionArchiveMap.put(SliderInteraction.QTI_CLASS_NAME, new SliderInteractionArchive()); //ok
interactionArchiveMap.put(UploadInteraction.QTI_CLASS_NAME, new DefaultInteractionArchive());
//custom
interactionArchiveMap.put(CustomInteraction.QTI_CLASS_NAME, new NoOutputInteractionArchive()); //ok
//inline
interactionArchiveMap.put(EndAttemptInteraction.QTI_CLASS_NAME, new NoOutputInteractionArchive()); //ok
interactionArchiveMap.put(InlineChoiceInteraction.QTI_CLASS_NAME, new InlineChoiceInteractionArchive()); //ok
interactionArchiveMap.put(TextEntryInteraction.QTI_CLASS_NAME, new TextEntryInteractionArchive()); //ok
}
public boolean hasResults() {
return responseDao.hasResponses(searchParams);
}
/**
*
* @param exportStream
*/
public void exportCourseElement(ZipOutputStream exportStream) {
ICourse course = CourseFactory.loadCourse(searchParams.getCourseEntry());
CourseNode courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent());
String label = StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName())
+ "_" + Formatter.formatDatetimeWithMinutes(new Date())
+ ".xlsx";
exportCourseElement(label, exportStream);
}
public void exportCourseElement(String label, ZipOutputStream exportStream) {
ICourse course = CourseFactory.loadCourse(searchParams.getCourseEntry());
CourseNode courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent());
if("iqself".equals(courseNode.getType())) {
anonymizerCallback = course.getCourseEnvironment().getCoursePropertyManager();
}
export(label, exportStream);
}
public void exportResource(ZipOutputStream exportStream) {
String archiveName = "qti21test_"
+ StringHelper.transformDisplayNameToFileSystemName(searchParams.getTestEntry().getDisplayname())
+ "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + ".xlsx";
export(archiveName, exportStream);
}
public void export(String filename, ZipOutputStream exportStream) {
try {
exportStream.putNextEntry(new ZipEntry(filename));
exportWorkbook(new ShieldOutputStream(exportStream));
exportStream.closeEntry();
} catch (IOException e) {
log.error("", e);
}
}
public void exportWorkbook(OutputStream exportStream) {
RepositoryEntry testEntry = searchParams.getTestEntry();
FileResourceManager frm = FileResourceManager.getInstance();
File unzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource());
resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false, false);
//content
List<AssessmentResponse> responses = responseDao.getResponse(searchParams);
try(OpenXMLWorkbook workbook = new OpenXMLWorkbook(exportStream, 1)) {
//headers
OpenXMLWorksheet exportSheet = workbook.nextWorksheet();
exportSheet.setHeaderRows(2);
writeHeaders_1(exportSheet, workbook);
writeHeaders_2(exportSheet, workbook);
writeData(responses, exportSheet, workbook);
} catch(Exception e) {
log.error("", e);
}
}
public MediaResource exportCourseElement() {
FileResourceManager frm = FileResourceManager.getInstance();
File unzippedDirRoot = frm.unzipFileResource(searchParams.getTestEntry().getOlatResource());
resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false, false);
ICourse course = CourseFactory.loadCourse(searchParams.getCourseEntry());
CourseNode courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent());
String label = courseNode.getType() + "_"
+ StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName())
+ "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis()))
+ ".xlsx";
if("iqself".equals(courseNode.getType())) {
anonymizerCallback = course.getCourseEnvironment().getCoursePropertyManager();
}
//content
final List<AssessmentResponse> responses = responseDao.getResponse(searchParams);
return new OpenXMLWorkbookResource(label) {
@Override
protected void generate(OutputStream out) {
try(OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) {
//headers
OpenXMLWorksheet exportSheet = workbook.nextWorksheet();
exportSheet.setHeaderRows(2);
writeHeaders_1(exportSheet, workbook);
writeHeaders_2(exportSheet, workbook);
writeData(responses, exportSheet, workbook);
} catch (Exception e) {
log.error("", e);
}
}
};
}
private void writeHeaders_1(OpenXMLWorksheet exportSheet, OpenXMLWorkbook workbook) {
//first header
Row header1Row = exportSheet.newRow();
int col = 1;
if(anonymizerCallback != null) {
col += 4;// anonymized name -> test duration
} else {
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
if (userPropertyHandler != null) {
col++;
}
}
col += 5;// homepage -> test duration
}
List<ItemInfos> infos = getItemInfos();
for(int i=0; i<infos.size(); i++) {
int delta = col;
ItemInfos item = infos.get(i);
if (exportConfig.isResponseCols() || exportConfig.isPointCol() || exportConfig.isTimeCols() || exportConfig.isCommentCol()) {
List<Interaction> interactions = item.getInteractions();
for(int j=0; j<interactions.size(); j++) {
Interaction interaction = interactions.get(j);
col = interactionArchiveMap.get(interaction.getQtiClassName())
.writeHeader1(item.getAssessmentItem(), interaction, i, j, header1Row, col, workbook);
}
}
if (!exportConfig.isResponseCols()) {
col -= col - delta;
}
if (exportConfig.isPointCol()) {
col++;
}
if (exportConfig.isCommentCol()) {
col++;
}
if (exportConfig.isTimeCols()) {
col += anonymizerCallback != null ? 1 : 2;
}
}
}
private void writeHeaders_2(OpenXMLWorksheet exportSheet, OpenXMLWorkbook workbook) {
CellStyle headerStyle = workbook.getStyles().getHeaderStyle();
//second header
int col = 0;//reset column counter
Row header2Row = exportSheet.newRow();
String sequentialNumber = translator.translate("column.header.seqnum");
header2Row.addCell(col++, sequentialNumber, headerStyle);
if(anonymizerCallback != null) {
col++;
} else {
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
if (userPropertyHandler == null) {
continue;
}
String header = translator.translate(userPropertyHandler.i18nFormElementLabelKey());
header2Row.addCell(col++, header, headerStyle);
}
// add other user and session information
header2Row.addCell(col++, translator.translate("column.header.homepage"), headerStyle);
}
header2Row.addCell(col++, translator.translate("column.header.assesspoints"), headerStyle);
header2Row.addCell(col++, translator.translate("column.header.passed"), headerStyle);
if (anonymizerCallback == null){
header2Row.addCell(col++, translator.translate("column.header.date"), headerStyle);
}
header2Row.addCell(col++, translator.translate("column.header.duration"), headerStyle);
List<ItemInfos> infos = getItemInfos();
for(int i=0; i<infos.size(); i++) {
ItemInfos info = infos.get(i);
if (exportConfig.isResponseCols()) {
List<Interaction> interactions = info.getInteractions();
for(int j=0; j<interactions.size(); j++) {
Interaction interaction = interactions.get(j);
col = interactionArchiveMap.get(interaction.getQtiClassName())
.writeHeader2(info.getAssessmentItem(), interaction, i, j, header2Row, col, workbook);
}
}
if (exportConfig.isPointCol()) {
header2Row.addCell(col++, translator.translate("item.score"), headerStyle);
}
if (exportConfig.isCommentCol()) {
header2Row.addCell(col++, translator.translate("item.comment"), headerStyle);
}
if (exportConfig.isTimeCols()) {
if (anonymizerCallback == null){
header2Row.addCell(col++, translator.translate("item.start"), headerStyle);
}
header2Row.addCell(col++, translator.translate("item.duration"), headerStyle);
}
}
}
private void writeData(List<AssessmentResponse> responses, OpenXMLWorksheet exportSheet, OpenXMLWorkbook workbook) {
int num = 0;
SessionResponses sessionResponses = null;
int numOfResponses = responses.size();
for(int i=0; i<numOfResponses; i++) {
AssessmentResponse response = responses.get(i);
AssessmentItemSession itemSession = response.getAssessmentItemSession();
AssessmentTestSession testSession = itemSession.getAssessmentTestSession();
if(sessionResponses == null) {
sessionResponses = new SessionResponses(testSession);
} else if(!sessionResponses.getSessionKey().equals(testSession.getKey())) {
writeDataRow(++num, sessionResponses, exportSheet, workbook);
sessionResponses = new SessionResponses(testSession);
}
sessionResponses.addResponse(itemSession, response);
}
if(sessionResponses != null) {
writeDataRow(++num, sessionResponses, exportSheet, workbook);
}
}
private void writeDataRow(int num, SessionResponses responses, OpenXMLWorksheet exportSheet, OpenXMLWorkbook workbook) {
int col = 0;
Row dataRow = exportSheet.newRow();
dataRow.addCell(col++, num, null);//sequence number
AssessmentTestSession testSession = responses.getTestSession();
AssessmentEntry entry = testSession.getAssessmentEntry();
//user properties
if(entry.getIdentity() == null) {
for (UserPropertyHandler userPropertyHandler:userPropertyHandlers) {
if (userPropertyHandler != null) {
if(userPropertyHandlers.get(0) == userPropertyHandler) {
dataRow.addCell(col++, translator.translate("anonym.user"), null);
} else {
col++;
}
}
}
} else if(anonymizerCallback != null) {
String anonymizedName = anonymizerCallback.getAnonymizedUserName(entry.getIdentity());
dataRow.addCell(col++, anonymizedName, null);
} else {
User assessedUser = entry.getIdentity().getUser();
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
if (userPropertyHandler != null) {
String property = userPropertyHandler.getUserProperty(assessedUser, translator.getLocale());
dataRow.addCell(col++, property, null);
}
}
}
//homepage
if(anonymizerCallback == null) {
String homepage;
if(entry.getIdentity() == null) {
homepage = "";
} else {
ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(entry.getIdentity());
homepage = BusinessControlFactory.getInstance().getAsURIString(Collections.singletonList(ce), false);
}
dataRow.addCell(col++, homepage, null);
}
//assesspoints, passed, ipaddress, date, duration
if(testSession.getScore() != null) {
dataRow.addCell(col++, testSession.getScore(), null);
} else {
col++;
}
if(testSession.getPassed() != null) {
dataRow.addCell(col++, testSession.getPassed().toString(), null);
} else {
col++;
}
if(anonymizerCallback == null) {
dataRow.addCell(col++, testSession.getCreationDate(), workbook.getStyles().getDateStyle());
}
dataRow.addCell(col++, toDurationInMilliseconds(testSession.getDuration()), null);
List<ItemInfos> infos = getItemInfos();
for(int i=0; i<infos.size(); i++) {
ItemInfos info = infos.get(i);
AssessmentItemRef itemRef = info.getAssessmentItemRef();
String itemRefIdentifier = itemRef.getIdentifier().toString();
AssessmentItemSession itemSession = responses.getItemSession(itemRefIdentifier);
if (exportConfig.isResponseCols()) {
List<Interaction> interactions = info.getInteractions();
for(int j=0; j<interactions.size(); j++) {
Interaction interaction = interactions.get(j);
AssessmentResponse response = responses
.getResponse(itemRefIdentifier, interaction.getResponseIdentifier());
col = interactionArchiveMap.get(interaction.getQtiClassName())
.writeInteractionData(info.getAssessmentItem(), response, interaction, j, dataRow, col, workbook);
}
}
//score, start, duration
if (itemSession == null) {
if (exportConfig.isPointCol()) {
col++;
}
if (exportConfig.isCommentCol()) {
col++;
}
if (exportConfig.isTimeCols()) {
col += anonymizerCallback != null ? 1 : 2;
}
} else {
if (exportConfig.isPointCol()) {
if(itemSession.getManualScore() != null) {
dataRow.addCell(col++, itemSession.getManualScore(), null);
} else {
dataRow.addCell(col++, itemSession.getScore(), null);
}
}
if (exportConfig.isCommentCol()) {
dataRow.addCell(col++, itemSession.getCoachComment(), null);
}
if (exportConfig.isTimeCols()) {
if (anonymizerCallback == null){
dataRow.addCell(col++, itemSession.getCreationDate(), workbook.getStyles().getTimeStyle());
}
dataRow.addCell(col++, toDurationInMilliseconds(itemSession.getDuration()), null);
}
}
}
}
private Long toDurationInMilliseconds(Long value) {
if(value == null || value.longValue() == 0) return null;
return value.longValue() / 1000l;
}
private List<ItemInfos> getItemInfos() {
if(itemInfos == null) {
itemInfos = new ArrayList<>();
List<AssessmentItemRef> itemRefs = resolvedAssessmentTest.getAssessmentItemRefs();
for(AssessmentItemRef itemRef:itemRefs) {
ResolvedAssessmentItem resolvedItem = resolvedAssessmentTest.getResolvedAssessmentItem(itemRef);
AssessmentItem item = resolvedItem.getRootNodeLookup().extractIfSuccessful();
if(item != null) {
itemInfos.add(new ItemInfos(itemRef, item, item.getItemBody().findInteractions()));
}
}
}
return itemInfos;
}
private static class SessionResponses {
private final AssessmentTestSession testSession;
private final Map<String,AssessmentItemSession> itemSessionsMap = new HashMap<>();
private final Map<String,List<AssessmentResponse>> responsesMap = new HashMap<>();
public SessionResponses(AssessmentTestSession testSession) {
this.testSession = testSession;
}
public AssessmentTestSession getTestSession() {
return testSession;
}
public Long getSessionKey() {
return testSession.getKey();
}
public AssessmentResponse getResponse(String itemRefIdentifier, Identifier responseIdentifier) {
if(responseIdentifier == null) return null;
String responseIdentifierStr = responseIdentifier.toString();
List<AssessmentResponse> itemResponses = responsesMap.get(itemRefIdentifier);
AssessmentResponse response = null;
if(itemResponses != null && itemResponses.size() > 0) {
for(AssessmentResponse itemResponse:itemResponses) {
if(responseIdentifierStr.equals(itemResponse.getResponseIdentifier())) {
response = itemResponse;
break;
}
}
}
return response;
}
public void addResponse(AssessmentItemSession itemSession, AssessmentResponse response) {
String itemIdentifier = itemSession.getAssessmentItemIdentifier();
if(!itemSessionsMap.containsKey(itemIdentifier)) {
itemSessionsMap.put(itemIdentifier, itemSession);
responsesMap.put(itemIdentifier, new ArrayList<>(5));
}
responsesMap.get(itemIdentifier).add(response);
}
public AssessmentItemSession getItemSession(String itemIdentifier) {
return itemSessionsMap.get(itemIdentifier);
}
}
private static class ItemInfos {
private final AssessmentItemRef itemRef;
private final AssessmentItem assessmentItem;
private final List<Interaction> interactions;
public ItemInfos(AssessmentItemRef itemRef, AssessmentItem assessmentItem, List<Interaction> interactions) {
this.itemRef = itemRef;
this.interactions = interactions;
this.assessmentItem = assessmentItem;
}
public AssessmentItemRef getAssessmentItemRef() {
return itemRef;
}
public AssessmentItem getAssessmentItem() {
return assessmentItem;
}
public List<Interaction> getInteractions() {
return interactions;
}
}
}