Skip to content
Snippets Groups Projects
Commit 50bcc77a authored by srosse's avatar srosse
Browse files

OO-3338: add section with their score in the Excel export for QTI 2.1

parent 0c47f16b
No related branches found
No related tags found
No related merge requests found
......@@ -48,7 +48,7 @@ public class OpenXMLWorkbookStyles {
private List<CellStyle> cellXfs = new ArrayList<>();
private final Font standardFont, boldFont;
private final Fill noneFile, gray125Fill, correctFill;
private final Fill noneFile, gray125Fill, lightGrayFill, correctFill;
private final Border noBorder, borderRight;
/**
......@@ -62,6 +62,7 @@ public class OpenXMLWorkbookStyles {
private final CellStyle headerStyle;
private final CellStyle correctStyle;
private final CellStyle percentStyle;
private final CellStyle lightGrayStyle;
public OpenXMLWorkbookStyles() {
standardFont = new Font(fonts.size(), "12", "1", "Calibri", "2", "minor", FontStyle.none);
......@@ -73,6 +74,8 @@ public class OpenXMLWorkbookStyles {
fills.add(noneFile);
gray125Fill = new Fill(fills.size(), "gray125");
fills.add(gray125Fill);
lightGrayFill = new Fill(fills.size(), "solid", "EFEFEFEF", "64");
fills.add(lightGrayFill);
correctFill = new Fill(fills.size(), "solid", "FFC3FFC0", "64");
fills.add(correctFill);
......@@ -97,6 +100,8 @@ public class OpenXMLWorkbookStyles {
cellXfs.add(correctStyle);
percentStyle = new CellStyle(cellXfs.size(), PERCENT_FORMAT, standardFont, noneFile, borderRight, null, "1");
cellXfs.add(percentStyle);
lightGrayStyle = new CellStyle(cellXfs.size(), "0", standardFont, lightGrayFill, borderRight, null, null);
cellXfs.add(lightGrayStyle);
}
public CellStyle getBorderRightStyle() {
......@@ -135,6 +140,10 @@ public class OpenXMLWorkbookStyles {
return percentStyle;
}
public CellStyle getLightGrayStyle() {
return lightGrayStyle;
}
public List<Font> getFonts() {
return fonts;
}
......
......@@ -22,6 +22,7 @@ package org.olat.ims.qti21.manager.archive;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
......@@ -117,6 +118,10 @@ 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.node.test.AssessmentSection;
import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
import uk.ac.ed.ph.jqtiplus.node.test.SectionPart;
import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
import uk.ac.ed.ph.jqtiplus.types.Identifier;
......@@ -140,8 +145,9 @@ public class QTI21ArchiveFormat {
private final QTI21StatisticSearchParams searchParams;
private ExportFormat exportConfig;
private int numOfSections;
private CourseNode courseNode;
private List<ItemInfos> itemInfos;
private List<AbstractInfos> elementInfos;
private final Map<String, InteractionArchive> interactionArchiveMap = new HashMap<>();
private final QTI21Service qtiService;
......@@ -332,30 +338,39 @@ public class QTI21ArchiveFormat {
header1Row.addCell(col++, translator.translate("archive.table.header.test"), headerStyle);
col += 5;
List<ItemInfos> infos = getItemInfos();
List<AbstractInfos> 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);
AbstractInfos info = infos.get(i);
if(info instanceof ItemInfos) {
ItemInfos item = (ItemInfos)info;
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;
}
} else if(numOfSections > 1 && info instanceof SectionInfos) {
SectionInfos section = (SectionInfos)info;
if(!section.getItemInfos().isEmpty()) {
String sectionTitle = translator.translate("archive.table.header.section", new String[] { section.getAssessmentSection().getTitle() });
header1Row.addCell(col++, sectionTitle, headerStyle);
}
}
if (!exportConfig.isResponseCols()) {
col -= col - delta;
}
if (exportConfig.isPointCol()) {
col++;
}
if (exportConfig.isCommentCol()) {
col++;
}
if (exportConfig.isTimeCols()) {
col += anonymizerCallback != null ? 1 : 2;
}
}
}
......@@ -402,28 +417,36 @@ public class QTI21ArchiveFormat {
}
header2Row.addCell(col++, translator.translate("column.header.duration"), headerStyle);
List<ItemInfos> infos = getItemInfos();
List<AbstractInfos> 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);
AbstractInfos info = infos.get(i);
if(info instanceof ItemInfos) {
ItemInfos item = (ItemInfos)info;
if (exportConfig.isResponseCols()) {
List<Interaction> interactions = item.getInteractions();
for(int j=0; j<interactions.size(); j++) {
Interaction interaction = interactions.get(j);
col = interactionArchiveMap.get(interaction.getQtiClassName())
.writeHeader2(item.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);
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);
}
} else if(numOfSections > 1 && info instanceof SectionInfos) {
SectionInfos section = (SectionInfos)info;
if(!section.getItemInfos().isEmpty()) {
header2Row.addCell(col++, translator.translate("archive.table.header.points"), headerStyle);
}
header2Row.addCell(col++, translator.translate("item.duration"), headerStyle);
}
}
}
......@@ -552,74 +575,126 @@ public class QTI21ArchiveFormat {
}
dataRow.addCell(col++, toDurationInMilliseconds(testSession.getDuration()), null);
List<ItemInfos> infos = getItemInfos();
List<AbstractInfos> 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);
AbstractInfos info = infos.get(i);
if(info instanceof ItemInfos) {
ItemInfos item = (ItemInfos)info;
AssessmentItemRef itemRef = item.getAssessmentItemRef();
String itemRefIdentifier = itemRef.getIdentifier().toString();
AssessmentItemSession itemSession = responses.getItemSession(itemRefIdentifier);
if (exportConfig.isResponseCols()) {
List<Interaction> interactions = item.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(item.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);
//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);
}
}
if (exportConfig.isCommentCol()) {
dataRow.addCell(col++, itemSession.getCoachComment(), null);
}
if (exportConfig.isTimeCols()) {
if (anonymizerCallback == null){
dataRow.addCell(col++, itemSession.getCreationDate(), workbook.getStyles().getTimeStyle());
} else if(numOfSections > 1 && info instanceof SectionInfos) {
SectionInfos section = (SectionInfos)info;
if(!section.getItemInfos().isEmpty()) {
BigDecimal score = calculateSectionScore(responses, section);
if(score != null) {
dataRow.addCell(col++, score, workbook.getStyles().getLightGrayStyle());
} else {
col++;
}
dataRow.addCell(col++, toDurationInMilliseconds(itemSession.getDuration()), null);
}
}
}
}
private BigDecimal calculateSectionScore(SessionResponses responses, SectionInfos section) {
BigDecimal sectionScore = BigDecimal.valueOf(0l);
for(ItemInfos item:section.getItemInfos()) {
AssessmentItemRef itemRef = item.getAssessmentItemRef();
String itemRefIdentifier = itemRef.getIdentifier().toString();
AssessmentItemSession itemSession = responses.getItemSession(itemRefIdentifier);
if(itemSession.getManualScore() != null) {
sectionScore = sectionScore.add(itemSession.getManualScore());
} else if(itemSession.getScore() != null){
sectionScore = sectionScore.add(itemSession.getScore());
}
}
return sectionScore;
}
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) {
private List<AbstractInfos> getItemInfos() {
if(elementInfos == null) {
numOfSections = 0;
elementInfos = new ArrayList<>();
AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractAssumingSuccessful();
for(TestPart part:assessmentTest.getTestParts()) {
for(AssessmentSection section:part.getAssessmentSections()) {
collectElementInfos(section);
}
}
}
return elementInfos;
}
private void collectElementInfos(AssessmentSection section) {
numOfSections++;
SectionInfos sectionInfos = new SectionInfos(section);
elementInfos.add(sectionInfos);
List<SectionPart> parts = section.getChildAbstractParts();
for(SectionPart part:parts) {
if(part instanceof AssessmentItemRef) {
AssessmentItemRef itemRef = (AssessmentItemRef)part;
ResolvedAssessmentItem resolvedItem = resolvedAssessmentTest.getResolvedAssessmentItem(itemRef);
AssessmentItem item = resolvedItem.getRootNodeLookup().extractIfSuccessful();
if(item != null) {
itemInfos.add(new ItemInfos(itemRef, item, item.getItemBody().findInteractions()));
ItemInfos itemInfo = new ItemInfos(itemRef, item, item.getItemBody().findInteractions());
elementInfos.add(itemInfo);
sectionInfos.getItemInfos().add(itemInfo);
}
} else if(part instanceof AssessmentSection) {
collectElementInfos((AssessmentSection)part);
}
}
return itemInfos;
}
private static class SessionResponses {
......@@ -667,7 +742,28 @@ public class QTI21ArchiveFormat {
}
}
private static class ItemInfos {
private static class AbstractInfos {
//
}
private static class SectionInfos extends AbstractInfos {
private final AssessmentSection section;
private final List<ItemInfos> itemInfos = new ArrayList<>();
public SectionInfos(AssessmentSection section) {
this.section = section;
}
public AssessmentSection getAssessmentSection() {
return section;
}
public List<ItemInfos> getItemInfos() {
return itemInfos;
}
}
private static class ItemInfos extends AbstractInfos {
private final AssessmentItemRef itemRef;
private final AssessmentItem assessmentItem;
......
......@@ -16,6 +16,7 @@ archive.table.header.node=Kurs
archive.table.header.node.passed=Kursbaustein bestanden
archive.table.header.node.points=Kursbaustein Punkte
archive.table.header.points=$\:table.header.score
archive.table.header.section=Sektion "{0}"
archive.table.header.test=Test
assessment.comment.legend=Pers\u00F6nliche Notizen
assessment.comment.legend.help=Sie k\u00F6nnen ein Kommentar hier schreiebn. Diese Notizen sind privat und werden nicht in einem Pr\u00FCfung ber\u00FCcksichtigt.
......
......@@ -16,6 +16,7 @@ archive.table.header.node=Course
archive.table.header.node.passed=Passed course element
archive.table.header.node.points=Score course element
archive.table.header.points=$\:table.header.score
archive.table.header.section=Section "{0}"
archive.table.header.test=Test
assessment.comment.legend=Personal notes
assessment.comment.legend.help=Please use the following text box if you need to provide any additional information, comments or feedback during this test. This notice is private and will be taken in account for an exam.
......
......@@ -16,6 +16,7 @@ archive.table.header.node=Cours
archive.table.header.node.passed=Element de cours r\u00E9ussi
archive.table.header.node.points=Points \u00E9l\u00E9ment de cours
archive.table.header.points=$\:table.header.score
archive.table.header.section=Sektion "{0}"
archive.table.header.test=Test
assessment.comment.legend=Notes personelles
assessment.comment.legend.help=Vous pouvez ajoutez un commentaire dans le champ de texte ci-dessous. Cette note est priv\u00E9e et il n'en sera pas tenu compte dans un test.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment