Index: lams_common/src/java/org/lamsfoundation/lams/commonContext.xml
===================================================================
diff -u -r292df3a6fb3cce2b31c9d9ee33c61bbe931e58ab -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision 292df3a6fb3cce2b31c9d9ee33c61bbe931e58ab)
+++ lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -497,7 +497,7 @@
-
+
true
@@ -727,6 +727,11 @@
+
+
+
+
+
Index: lams_common/src/java/org/lamsfoundation/lams/logevent/dao/ILearnerInteractionDAO.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/logevent/dao/ILearnerInteractionDAO.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/logevent/dao/ILearnerInteractionDAO.java (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -0,0 +1,35 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.logevent.dao;
+
+import java.util.Map;
+
+import org.lamsfoundation.lams.dao.IBaseDAO;
+import org.lamsfoundation.lams.logevent.LearnerInteractionEvent;
+
+public interface ILearnerInteractionDAO extends IBaseDAO {
+ /**
+ * Returns a map of QbToolQuestion UID -> learner interaction event with the question the first time
+ */
+ Map getFirstLearnerInteractions(long toolContentId, int userId);
+}
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/logevent/dao/hibernate/LearnerInteractionDAO.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/logevent/dao/hibernate/LearnerInteractionDAO.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/logevent/dao/hibernate/LearnerInteractionDAO.java (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -0,0 +1,22 @@
+package org.lamsfoundation.lams.logevent.dao.hibernate;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO;
+import org.lamsfoundation.lams.logevent.LearnerInteractionEvent;
+import org.lamsfoundation.lams.logevent.dao.ILearnerInteractionDAO;
+
+public class LearnerInteractionDAO extends LAMSBaseDAO implements ILearnerInteractionDAO {
+ private static final String FIND_FIRST_LEARNER_INTERACTIONS = "SELECT i.* FROM lams_learner_interaction_event AS i "
+ + "JOIN lams_qb_tool_question AS q ON i.qb_tool_question_uid = q.tool_question_uid "
+ + "WHERE q.tool_content_id = :toolContentId AND i.user_id = :userId GROUP BY i.qb_tool_question_uid";
+
+ @Override
+ public Map getFirstLearnerInteractions(long toolContentId, int userId) {
+ return getSession().createNativeQuery(FIND_FIRST_LEARNER_INTERACTIONS, LearnerInteractionEvent.class)
+ .setParameter("toolContentId", toolContentId).setParameter("userId", userId).list().stream()
+ .collect(Collectors.toMap(LearnerInteractionEvent::getQbToolQuestionUid, Function.identity()));
+ }
+}
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/logevent/service/ILearnerInteractionService.java
===================================================================
diff -u -r292df3a6fb3cce2b31c9d9ee33c61bbe931e58ab -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_common/src/java/org/lamsfoundation/lams/logevent/service/ILearnerInteractionService.java (.../ILearnerInteractionService.java) (revision 292df3a6fb3cce2b31c9d9ee33c61bbe931e58ab)
+++ lams_common/src/java/org/lamsfoundation/lams/logevent/service/ILearnerInteractionService.java (.../ILearnerInteractionService.java) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -1,7 +1,11 @@
package org.lamsfoundation.lams.logevent.service;
+import java.util.Map;
+
import org.lamsfoundation.lams.logevent.LearnerInteractionEvent;
public interface ILearnerInteractionService {
void saveEvent(LearnerInteractionEvent event);
+
+ Map getFirstLearnerInteractions(long toolContentId, int userId);
}
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/logevent/service/LearnerInteractionService.java
===================================================================
diff -u -r292df3a6fb3cce2b31c9d9ee33c61bbe931e58ab -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_common/src/java/org/lamsfoundation/lams/logevent/service/LearnerInteractionService.java (.../LearnerInteractionService.java) (revision 292df3a6fb3cce2b31c9d9ee33c61bbe931e58ab)
+++ lams_common/src/java/org/lamsfoundation/lams/logevent/service/LearnerInteractionService.java (.../LearnerInteractionService.java) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -1,18 +1,28 @@
package org.lamsfoundation.lams.logevent.service;
-import org.lamsfoundation.lams.dao.IBaseDAO;
+import java.util.Map;
+
import org.lamsfoundation.lams.logevent.LearnerInteractionEvent;
+import org.lamsfoundation.lams.logevent.dao.ILearnerInteractionDAO;
public class LearnerInteractionService implements ILearnerInteractionService {
- private IBaseDAO baseDAO;
+ private ILearnerInteractionDAO learnerInteractionDAO;
@Override
public void saveEvent(LearnerInteractionEvent event) {
- baseDAO.insert(event);
+ learnerInteractionDAO.insert(event);
}
- public void setBaseDAO(IBaseDAO baseDAO) {
- this.baseDAO = baseDAO;
+ /**
+ * Returns a map of QbToolQuestion UID -> learner interaction event with the question the first time
+ */
+ @Override
+ public Map getFirstLearnerInteractions(long toolContentId, int userId) {
+ return learnerInteractionDAO.getFirstLearnerInteractions(toolContentId, userId);
}
+
+ public void setLearnerInteractionDAO(ILearnerInteractionDAO learnerInteractionDAO) {
+ this.learnerInteractionDAO = learnerInteractionDAO;
+ }
}
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/util/FileUtil.java
===================================================================
diff -u -r9367000b12e2272e6e0756d91ae1fdd5d7d4a220 -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_common/src/java/org/lamsfoundation/lams/util/FileUtil.java (.../FileUtil.java) (revision 9367000b12e2272e6e0756d91ae1fdd5d7d4a220)
+++ lams_common/src/java/org/lamsfoundation/lams/util/FileUtil.java (.../FileUtil.java) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -34,6 +34,7 @@
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
@@ -81,6 +82,8 @@
public static final String ENCODING_UTF_8 = "UTF8";
public static final SimpleDateFormat EXPORT_TO_SPREADSHEET_TITLE_DATE_FORMAT = new SimpleDateFormat(
"dd/MM/yyyy HH:mm:ss");
+ public static final DateTimeFormatter EXPORT_TO_SPREADSHEET_TITLE_DATE_FORMATTER = DateTimeFormatter
+ .ofPattern("dd/MM/yyyy HH:mm:ss");
public static final SimpleDateFormat EXPORT_TO_SPREADSHEET_CELL_DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy");
public static final String LAMS_WWW_SECURE_DIR = "secure";
Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/assessmentApplicationContext.xml
===================================================================
diff -u -rd9a9f033cd1e6050279d05ef7cbca24f243ecf6a -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/assessmentApplicationContext.xml (.../assessmentApplicationContext.xml) (revision d9a9f033cd1e6050279d05ef7cbca24f243ecf6a)
+++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/assessmentApplicationContext.xml (.../assessmentApplicationContext.xml) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -128,6 +128,9 @@
+
+
+
Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java
===================================================================
diff -u -rdac907ce79fde14b5b168324b3a2da18404d6dcb -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision dac907ce79fde14b5b168324b3a2da18404d6dcb)
+++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -65,6 +65,8 @@
import org.lamsfoundation.lams.learningdesign.service.ImportToolContentException;
import org.lamsfoundation.lams.lesson.Lesson;
import org.lamsfoundation.lams.lesson.service.ILessonService;
+import org.lamsfoundation.lams.logevent.LearnerInteractionEvent;
+import org.lamsfoundation.lams.logevent.service.ILearnerInteractionService;
import org.lamsfoundation.lams.logevent.service.ILogEventService;
import org.lamsfoundation.lams.notebook.model.NotebookEntry;
import org.lamsfoundation.lams.notebook.service.CoreNotebookConstants;
@@ -129,6 +131,7 @@
import org.lamsfoundation.lams.util.JsonUtil;
import org.lamsfoundation.lams.util.MessageService;
import org.lamsfoundation.lams.util.NumberUtil;
+import org.lamsfoundation.lams.util.WebUtil;
import org.lamsfoundation.lams.util.excel.ExcelCell;
import org.lamsfoundation.lams.util.excel.ExcelRow;
import org.lamsfoundation.lams.util.excel.ExcelSheet;
@@ -188,6 +191,8 @@
private IOutcomeService outcomeService;
+ private ILearnerInteractionService learnerInteractionService;
+
// *******************************************************************************
// Service method
// *******************************************************************************
@@ -1904,7 +1909,7 @@
}
} else {
- userResultRow.addCell(AssessmentEscapeUtils.printResponsesForExcelExport(questionResult));
+ AssessmentEscapeUtils.addResponseCellForExcelExport(questionResult, userResultRow, false);
if (doSummaryTable) {
summaryNACount = updateSummaryCounts(question, questionResult, summaryOfAnswers,
@@ -1963,173 +1968,340 @@
}
}
+ {
+ // ------------------------------------------------------------------
+ // -------------- Third tab: User Summary ---------------------------
+
+ ExcelSheet userSummarySheet = new ExcelSheet(getMessage("label.export.summary.by.user"));
+ sheets.add(userSummarySheet);
+
+ // Create the question summary
+ ExcelRow userSummaryTitle = userSummarySheet.initRow();
+ userSummaryTitle.addCell(getMessage("label.export.user.summary"), true);
+
+ ExcelRow summaryRowTitle = userSummarySheet.initRow();
+ summaryRowTitle.addCell("#", true, ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+ summaryRowTitle.addCell(getMessage("label.monitoring.question.summary.question"), true,
+ ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+ summaryRowTitle.addCell(getMessage("label.authoring.basic.list.header.type"), true,
+ ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+ summaryRowTitle.addCell(getMessage("label.authoring.basic.penalty.factor"), true,
+ ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+ summaryRowTitle.addCell(getMessage("label.monitoring.question.summary.default.mark"), true,
+ ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+ summaryRowTitle.addCell(getMessage("label.monitoring.question.summary.average.mark"), true,
+ ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+
+ Float totalGradesPossible = 0F;
+ Float totalAverage = 0F;
+ int questionIndex = 1;
+ if (assessment.getQuestionReferences() != null) {
+ Set questionReferences = new TreeSet<>(new SequencableComparator());
+ questionReferences.addAll(assessment.getQuestionReferences());
+
+ int randomQuestionsCount = 1;
+ for (QuestionReference questionReference : questionReferences) {
+
+ String title;
+ String questionType;
+ Float penaltyFactor;
+ Float averageMark = null;
+ if (questionReference.isRandomQuestion()) {
+
+ title = getMessage("label.authoring.basic.type.random.question") + randomQuestionsCount++;
+ questionType = getMessage("label.authoring.basic.type.random.question");
+ penaltyFactor = null;
+ averageMark = null;
+ } else {
+
+ AssessmentQuestion question = questionReference.getQuestion();
+ title = question.getQbQuestion().getName();
+ questionType = AssessmentServiceImpl.getQuestionTypeLanguageLabel(question.getType());
+ penaltyFactor = question.getQbQuestion().getPenaltyFactor();
+
+ QuestionSummary questionSummary = questionSummaries.get(question.getUid());
+ if (questionSummary != null) {
+ averageMark = questionSummary.getAverageMark();
+ totalAverage += questionSummary.getAverageMark();
+ }
+ }
+
+ int maxGrade = questionReference.getMaxMark();
+ totalGradesPossible += maxGrade;
+
+ ExcelRow questCellRow = userSummarySheet.initRow();
+ questCellRow.addCell(questionIndex++);
+ questCellRow.addCell(title);
+ questCellRow.addCell(questionType);
+ questCellRow.addCell(penaltyFactor);
+ questCellRow.addCell(maxGrade);
+ questCellRow.addCell(averageMark);
+ }
+
+ if (totalGradesPossible.floatValue() > 0) {
+ ExcelRow totalCellRow = userSummarySheet.initRow();
+ totalCellRow.addEmptyCells(2);
+ totalCellRow.addCell(getMessage("label.monitoring.summary.total"), true);
+ totalCellRow.addCell(totalGradesPossible);
+ totalCellRow.addCell(totalAverage);
+ }
+ userSummarySheet.addEmptyRow();
+ }
+
+ if (sessionDtos != null) {
+ List assessmentResults = assessmentResultDao
+ .getLastFinishedAssessmentResults(assessment.getContentId());
+ Map userUidToResultMap = new HashMap<>();
+ for (AssessmentResult assessmentResult : assessmentResults) {
+ userUidToResultMap.put(assessmentResult.getUser().getUid(), assessmentResult);
+ }
+
+ for (SessionDTO sessionDTO : sessionDtos) {
+ userSummarySheet.addEmptyRow();
+
+ ExcelRow sessionTitle = userSummarySheet.initRow();
+ sessionTitle.addCell(sessionDTO.getSessionName(), true);
+
+ AssessmentSession assessmentSession = getSessionBySessionId(sessionDTO.getSessionId());
+ Set assessmentUsers = assessmentSession.getAssessmentUsers();
+ if (assessmentUsers != null) {
+ for (AssessmentUser assessmentUser : assessmentUsers) {
+ ExcelRow userTitleRow = userSummarySheet.initRow();
+ userTitleRow.addCell(getMessage("label.export.user.id"), true);
+ userTitleRow.addCell(getMessage("label.monitoring.user.summary.full.name"), true);
+ userTitleRow.addCell(getMessage("label.export.date.attempted"), true);
+ userTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), true);
+ userTitleRow.addCell(getMessage("label.authoring.basic.option.answer"), true);
+ if (assessment.isEnableConfidenceLevels()) {
+ userTitleRow.addCell(getMessage("label.confidence"), true);
+ }
+ userTitleRow.addCell(getMessage("label.export.mark"), true);
+
+ if (assessment.isAllowAnswerJustification()) {
+ userTitleRow.addCell(getMessage("label.answer.justification"), true);
+ }
+
+ AssessmentResult assessmentResult = userUidToResultMap.get(assessmentUser.getUid());
+ if (assessmentResult != null) {
+ Set questionResults = assessmentResult.getQuestionResults();
+ if (questionResults != null) {
+ for (AssessmentQuestionResult questionResult : questionResults) {
+ ExcelRow userResultRow = userSummarySheet.initRow();
+ userResultRow.addCell(assessmentUser.getLoginName());
+ userResultRow.addCell(assessmentUser.getFullName());
+ userResultRow.addCell(assessmentResult.getStartDate());
+ userResultRow.addCell(questionResult.getQbQuestion().getName());
+
+ AssessmentEscapeUtils.addResponseCellForExcelExport(questionResult,
+ userResultRow, false);
+
+ if (assessment.isEnableConfidenceLevels()) {
+ String confidenceLevel = null;
+
+ switch (assessment.getConfidenceLevelsType()) {
+ case 2:
+ confidenceLevel = new String[] { getMessage("label.not.confident"),
+ getMessage("label.confident"),
+ getMessage("label.very.confident") }[questionResult
+ .getConfidenceLevel() / 5];
+ break;
+ case 3:
+ confidenceLevel = new String[] { getMessage("label.not.sure"),
+ getMessage("label.sure"),
+ getMessage("label.very.sure") }[questionResult
+ .getConfidenceLevel() / 5];
+ break;
+ default:
+ confidenceLevel = questionResult.getConfidenceLevel() * 10 + "%";
+ }
+
+ userResultRow.addCell(confidenceLevel);
+ }
+
+ userResultRow.addCell(questionResult.getMark());
+
+ if (assessment.isAllowAnswerJustification()) {
+ userResultRow.addCell(AssessmentEscapeUtils
+ .escapeStringForExcelExport(questionResult.getJustification()));
+ }
+ }
+ }
+
+ ExcelRow userTotalRow = userSummarySheet.initRow();
+ userTotalRow.addEmptyCells(assessment.isEnableConfidenceLevels() ? 5 : 4);
+ userTotalRow.addCell(getMessage("label.monitoring.summary.total"), true);
+ userTotalRow.addCell(assessmentResult.getGrade());
+ userSummarySheet.addEmptyRow();
+ }
+ }
+ }
+ }
+ }
+ }
// ------------------------------------------------------------------
- // -------------- Third tab: User Summary ---------------------------
+ // -------------- Third tab: Learner summary ---------------------------
ExcelSheet userSummarySheet = new ExcelSheet(getMessage("label.export.summary.by.user"));
sheets.add(userSummarySheet);
- // Create the question summary
- ExcelRow userSummaryTitle = userSummarySheet.initRow();
- userSummaryTitle.addCell(getMessage("label.export.user.summary"), true);
+ if (sessionDtos != null && assessment.getQuestionReferences() != null) {
+ // if there are multiple session, then the activity has to be grouped
+ boolean isActivityGrouped = sessionDtos.size() > 1;
- ExcelRow summaryRowTitle = userSummarySheet.initRow();
- summaryRowTitle.addCell("#", true, ExcelCell.BORDER_STYLE_BOTTOM_THIN);
- summaryRowTitle.addCell(getMessage("label.monitoring.question.summary.question"), true,
- ExcelCell.BORDER_STYLE_BOTTOM_THIN);
- summaryRowTitle.addCell(getMessage("label.authoring.basic.list.header.type"), true,
- ExcelCell.BORDER_STYLE_BOTTOM_THIN);
- summaryRowTitle.addCell(getMessage("label.authoring.basic.penalty.factor"), true,
- ExcelCell.BORDER_STYLE_BOTTOM_THIN);
- summaryRowTitle.addCell(getMessage("label.monitoring.question.summary.default.mark"), true,
- ExcelCell.BORDER_STYLE_BOTTOM_THIN);
- summaryRowTitle.addCell(getMessage("label.monitoring.question.summary.average.mark"), true,
- ExcelCell.BORDER_STYLE_BOTTOM_THIN);
+ // Row with just "Questions" header
+ ExcelRow userSummaryTitle = userSummarySheet.initRow();
+ // if there is no grouping, then we skip "Group" column
+ int questionLeftPadding = isActivityGrouped ? 3 : 2;
+ userSummaryTitle.addEmptyCells(questionLeftPadding);
+ userSummaryTitle.addCell("Questions", true, ExcelCell.BORDER_STYLE_LEFT_THIN);
- Float totalGradesPossible = 0F;
- Float totalAverage = 0F;
- int questionIndex = 1;
- if (assessment.getQuestionReferences() != null) {
+ // Row with question titles
+ ExcelRow questionTitlesRow = userSummarySheet.initRow();
+ questionTitlesRow.addEmptyCells(questionLeftPadding);
+
Set questionReferences = new TreeSet<>(new SequencableComparator());
questionReferences.addAll(assessment.getQuestionReferences());
- int randomQuestionsCount = 1;
+ // print out all question titles
for (QuestionReference questionReference : questionReferences) {
+ AssessmentQuestion question = questionReference.getQuestion();
+ String title = question.getQbQuestion().getName();
+ // leave pure text of title
+ title = WebUtil.removeHTMLtags(title).strip();
- String title;
- String questionType;
- Float penaltyFactor;
- Float averageMark = null;
- if (questionReference.isRandomQuestion()) {
+ // shorten long title
+ if (title.length() > 80) {
+ title = title.substring(0, 80) + "...";
+ }
+ questionTitlesRow.addCell(title, true, ExcelCell.BORDER_STYLE_LEFT_THIN);
- title = getMessage("label.authoring.basic.type.random.question") + randomQuestionsCount++;
- questionType = getMessage("label.authoring.basic.type.random.question");
- penaltyFactor = null;
- averageMark = null;
- } else {
-
- AssessmentQuestion question = questionReference.getQuestion();
- title = question.getQbQuestion().getName();
- questionType = AssessmentServiceImpl.getQuestionTypeLanguageLabel(question.getType());
- penaltyFactor = question.getQbQuestion().getPenaltyFactor();
-
- QuestionSummary questionSummary = questionSummaries.get(question.getUid());
- if (questionSummary != null) {
- averageMark = questionSummary.getAverageMark();
- totalAverage += questionSummary.getAverageMark();
- }
+ int columnShift = 1;
+ // currently only MCQ and True/False questions have learner interaction logged
+ // for other question types, do not include the column
+ boolean addAnsweredDateColumn = QbQuestion.TYPE_MULTIPLE_CHOICE == question.getType()
+ || QbQuestion.TYPE_TRUE_FALSE == question.getType();
+ if (addAnsweredDateColumn) {
+ columnShift++;
}
+ if (assessment.isEnableConfidenceLevels()) {
+ columnShift++;
+ }
+ questionTitlesRow.addEmptyCells(columnShift);
+ userSummarySheet.addMergedCells(5, questionLeftPadding, questionLeftPadding + columnShift);
- int maxGrade = questionReference.getMaxMark();
- totalGradesPossible += maxGrade;
+ questionLeftPadding += columnShift + 1;
+ }
+ questionTitlesRow.addCell("", ExcelCell.BORDER_STYLE_LEFT_THIN);
- ExcelRow questCellRow = userSummarySheet.initRow();
- questCellRow.addCell(questionIndex++);
- questCellRow.addCell(title);
- questCellRow.addCell(questionType);
- questCellRow.addCell(penaltyFactor);
- questCellRow.addCell(maxGrade);
- questCellRow.addCell(averageMark);
+ // Row with column header below question titles
+ ExcelRow userSummaryUserHeadersRow = userSummarySheet.initRow();
+ if (isActivityGrouped) {
+ userSummaryUserHeadersRow.addCell("Group", true);
}
+ userSummaryUserHeadersRow.addCell("User name", true);
+ userSummaryUserHeadersRow.addCell("Full name", true);
- if (totalGradesPossible.floatValue() > 0) {
- ExcelRow totalCellRow = userSummarySheet.initRow();
- totalCellRow.addEmptyCells(2);
- totalCellRow.addCell(getMessage("label.monitoring.summary.total"), true);
- totalCellRow.addCell(totalGradesPossible);
- totalCellRow.addCell(totalAverage);
+ for (QuestionReference questionReference : questionReferences) {
+ userSummaryUserHeadersRow.addCell("Score", ExcelCell.BORDER_STYLE_LEFT_THIN);
+ userSummaryUserHeadersRow.addCell("Answer");
+
+ AssessmentQuestion question = questionReference.getQuestion();
+ boolean addAnsweredDateColumn = QbQuestion.TYPE_MULTIPLE_CHOICE == question.getType()
+ || QbQuestion.TYPE_TRUE_FALSE == question.getType();
+ if (addAnsweredDateColumn) {
+ userSummaryUserHeadersRow.addCell("Date time");
+ }
+ if (assessment.isEnableConfidenceLevels()) {
+ userSummaryUserHeadersRow.addCell("Confidence");
+ }
}
- userSummarySheet.addEmptyRow();
- }
- if (sessionDtos != null) {
+ // a single column at the end of previous headers
+ userSummaryUserHeadersRow.addCell("Total", ExcelCell.BORDER_STYLE_LEFT_THIN);
+
List assessmentResults = assessmentResultDao
.getLastFinishedAssessmentResults(assessment.getContentId());
- Map userUidToResultMap = new HashMap<>();
- for (AssessmentResult assessmentResult : assessmentResults) {
- userUidToResultMap.put(assessmentResult.getUser().getUid(), assessmentResult);
- }
+ Map userUidToResultMap = assessmentResults.stream()
+ .collect(Collectors.toMap(r -> r.getUser().getUid(), r -> r));
for (SessionDTO sessionDTO : sessionDtos) {
- userSummarySheet.addEmptyRow();
-
- ExcelRow sessionTitle = userSummarySheet.initRow();
- sessionTitle.addCell(sessionDTO.getSessionName(), true);
-
AssessmentSession assessmentSession = getSessionBySessionId(sessionDTO.getSessionId());
Set assessmentUsers = assessmentSession.getAssessmentUsers();
- if (assessmentUsers != null) {
- for (AssessmentUser assessmentUser : assessmentUsers) {
- ExcelRow userTitleRow = userSummarySheet.initRow();
- userTitleRow.addCell(getMessage("label.export.user.id"), true);
- userTitleRow.addCell(getMessage("label.monitoring.user.summary.full.name"), true);
- userTitleRow.addCell(getMessage("label.export.date.attempted"), true);
- userTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), true);
- userTitleRow.addCell(getMessage("label.authoring.basic.option.answer"), true);
- if (assessment.isEnableConfidenceLevels()) {
- userTitleRow.addCell(getMessage("label.confidence"), true);
- }
- userTitleRow.addCell(getMessage("label.export.mark"), true);
+ for (AssessmentUser assessmentUser : assessmentUsers) {
+ ExcelRow userResultRow = userSummarySheet.initRow();
+ if (isActivityGrouped) {
+ userResultRow.addCell(sessionDTO.getSessionName());
+ }
+ userResultRow.addCell(assessmentUser.getLoginName());
+ userResultRow.addCell(assessmentUser.getFullName());
- if (assessment.isAllowAnswerJustification()) {
- userTitleRow.addCell(getMessage("label.answer.justification"), true);
- }
+ AssessmentResult assessmentResult = userUidToResultMap.get(assessmentUser.getUid());
+ if (assessmentResult == null) {
+ continue;
+ }
- AssessmentResult assessmentResult = userUidToResultMap.get(assessmentUser.getUid());
- if (assessmentResult != null) {
- Set questionResults = assessmentResult.getQuestionResults();
- if (questionResults != null) {
- for (AssessmentQuestionResult questionResult : questionResults) {
- ExcelRow userResultRow = userSummarySheet.initRow();
- userResultRow.addCell(assessmentUser.getLoginName());
- userResultRow.addCell(assessmentUser.getFullName());
- userResultRow.addCell(assessmentResult.getStartDate());
- userResultRow.addCell(questionResult.getQbQuestion().getName());
- userResultRow.addCell(
- AssessmentEscapeUtils.printResponsesForExcelExport(questionResult));
- if (assessment.isEnableConfidenceLevels()) {
- String confidenceLevel = null;
+ Set questionResults = assessmentResult.getQuestionResults();
+ if (questionResults == null) {
+ continue;
+ }
- switch (assessment.getConfidenceLevelsType()) {
- case 2:
- confidenceLevel = new String[] { getMessage("label.not.confident"),
- getMessage("label.confident"),
- getMessage("label.very.confident") }[questionResult
- .getConfidenceLevel() / 5];
- break;
- case 3:
- confidenceLevel = new String[] { getMessage("label.not.sure"),
- getMessage("label.sure"),
- getMessage("label.very.sure") }[questionResult
- .getConfidenceLevel() / 5];
- break;
- default:
- confidenceLevel = questionResult.getConfidenceLevel() * 10 + "%";
- }
+ // get information when a learner started interaction with given questions
+ Map learnerInteractions = learnerInteractionService
+ .getFirstLearnerInteractions(assessment.getContentId(),
+ assessmentUser.getUserId().intValue());
- userResultRow.addCell(confidenceLevel);
- }
+ for (AssessmentQuestionResult questionResult : questionResults) {
+ // mark
+ userResultRow.addCell(questionResult.getMark(), ExcelCell.BORDER_STYLE_LEFT_THIN);
- userResultRow.addCell(questionResult.getMark());
+ // option chosen or full answer
+ AssessmentEscapeUtils.addResponseCellForExcelExport(questionResult, userResultRow, true);
- if (assessment.isAllowAnswerJustification()) {
- userResultRow.addCell(AssessmentEscapeUtils
- .escapeStringForExcelExport(questionResult.getJustification()));
- }
- }
+ // learner interaction
+ QbQuestion question = questionResult.getQbQuestion();
+ boolean addAnsweredDateColumn = QbQuestion.TYPE_MULTIPLE_CHOICE == question.getType()
+ || QbQuestion.TYPE_TRUE_FALSE == question.getType();
+ if (addAnsweredDateColumn) {
+ LearnerInteractionEvent interaction = learnerInteractions
+ .get(questionResult.getQbToolQuestion().getUid());
+ if (interaction == null) {
+ userResultRow.addEmptyCell();
+ } else {
+ userResultRow.addCell(interaction.getOccuredDateTime()
+ .format(FileUtil.EXPORT_TO_SPREADSHEET_TITLE_DATE_FORMATTER));
}
+ }
- ExcelRow userTotalRow = userSummarySheet.initRow();
- userTotalRow.addEmptyCells(assessment.isEnableConfidenceLevels() ? 5 : 4);
- userTotalRow.addCell(getMessage("label.monitoring.summary.total"), true);
- userTotalRow.addCell(assessmentResult.getGrade());
- userSummarySheet.addEmptyRow();
+ // confidence level
+ if (assessment.isEnableConfidenceLevels()) {
+ String confidenceLevel = null;
+
+ switch (assessment.getConfidenceLevelsType()) {
+ case 2:
+ confidenceLevel = new String[] { getMessage("label.not.confident"),
+ getMessage("label.confident"),
+ getMessage("label.very.confident") }[questionResult.getConfidenceLevel()
+ / 5];
+ break;
+ case 3:
+ confidenceLevel = new String[] { getMessage("label.not.sure"),
+ getMessage("label.sure"),
+ getMessage("label.very.sure") }[questionResult.getConfidenceLevel() / 5];
+ break;
+ default:
+ confidenceLevel = questionResult.getConfidenceLevel() * 10 + "%";
+ }
+
+ userResultRow.addCell(confidenceLevel);
}
}
+ userResultRow.addCell(assessmentResult.getGrade(), ExcelCell.BORDER_STYLE_LEFT_THIN);
+
+ userSummarySheet.addEmptyRow();
+ userSummarySheet.addEmptyRow();
}
}
}
-
return sheets;
}
@@ -3389,6 +3561,10 @@
this.assessmentOutputFactory = assessmentOutputFactory;
}
+ public void setLearnerInteractionService(ILearnerInteractionService learnerInteractionService) {
+ this.learnerInteractionService = learnerInteractionService;
+ }
+
@Override
public Class>[] getSupportedToolOutputDefinitionClasses(int definitionType) {
return getAssessmentOutputFactory().getSupportedDefinitionClasses(definitionType);
@@ -3641,7 +3817,7 @@
if (collectionUUIDs != null) {
addToCollection &= !collectionUUIDs.contains(uuid);
}
-
+
int isModified = qbQuestion.isQbQuestionModified(oldQbQuestion);
if (isModified == IQbService.QUESTION_MODIFIED_VERSION_BUMP) {
qbQuestion.clearID();
Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/AssessmentEscapeUtils.java
===================================================================
diff -u -r7089187749475a10fc520379a3b4689077139d32 -rbfad24196397ef8759dd17934284e5dfdbc45ff6
--- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/AssessmentEscapeUtils.java (.../AssessmentEscapeUtils.java) (revision 7089187749475a10fc520379a3b4689077139d32)
+++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/AssessmentEscapeUtils.java (.../AssessmentEscapeUtils.java) (revision bfad24196397ef8759dd17934284e5dfdbc45ff6)
@@ -27,6 +27,7 @@
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
+import org.apache.poi.ss.usermodel.IndexedColors;
import org.lamsfoundation.lams.qb.model.QbOption;
import org.lamsfoundation.lams.qb.model.QbQuestion;
import org.lamsfoundation.lams.tool.assessment.dto.AssessmentResultDTO;
@@ -37,6 +38,7 @@
import org.lamsfoundation.lams.tool.assessment.dto.UserSummaryItem;
import org.lamsfoundation.lams.tool.assessment.model.AssessmentOptionAnswer;
import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionResult;
+import org.lamsfoundation.lams.util.excel.ExcelRow;
public class AssessmentEscapeUtils {
@@ -228,33 +230,43 @@
/**
* Used only for excell export (for getUserSummaryData() method).
*/
- public static Object printResponsesForExcelExport(AssessmentQuestionResult questionResult) {
- Object ret = null;
+ public static void addResponseCellForExcelExport(AssessmentQuestionResult questionResult, ExcelRow row,
+ boolean useLettersForMcq) {
+ if (questionResult == null) {
+ row.addEmptyCell();
+ return;
+ }
- if (questionResult != null) {
- switch (questionResult.getQbQuestion().getType()) {
- case QbQuestion.TYPE_ESSAY:
- String answer = questionResult.getAnswer();
- return AssessmentEscapeUtils.escapeStringForExcelExport(answer);
- case QbQuestion.TYPE_MATCHING_PAIRS:
- return AssessmentEscapeUtils.getOptionResponse(questionResult, QbQuestion.TYPE_MATCHING_PAIRS);
- case QbQuestion.TYPE_MULTIPLE_CHOICE:
- return AssessmentEscapeUtils.getOptionResponse(questionResult, QbQuestion.TYPE_MULTIPLE_CHOICE);
- case QbQuestion.TYPE_NUMERICAL:
- return questionResult.getAnswer();
- case QbQuestion.TYPE_ORDERING:
- return AssessmentEscapeUtils.getOptionResponse(questionResult, QbQuestion.TYPE_ORDERING);
- case QbQuestion.TYPE_VERY_SHORT_ANSWERS:
- return questionResult.getAnswer();
- case QbQuestion.TYPE_TRUE_FALSE:
- return questionResult.getAnswerBoolean();
- case QbQuestion.TYPE_MARK_HEDGING:
- //taken care beforehand
- default:
- return null;
- }
+ Object value = null;
+ switch (questionResult.getQbQuestion().getType()) {
+ case QbQuestion.TYPE_ESSAY:
+ value = AssessmentEscapeUtils.escapeStringForExcelExport(questionResult.getAnswer());
+ break;
+ case QbQuestion.TYPE_MATCHING_PAIRS:
+ AssessmentEscapeUtils.getOptionResponse(questionResult, QbQuestion.TYPE_MATCHING_PAIRS, row,
+ useLettersForMcq);
+ return;
+ case QbQuestion.TYPE_MULTIPLE_CHOICE:
+ AssessmentEscapeUtils.getOptionResponse(questionResult, QbQuestion.TYPE_MULTIPLE_CHOICE, row,
+ useLettersForMcq);
+ return;
+ case QbQuestion.TYPE_NUMERICAL:
+ value = questionResult.getAnswer();
+ break;
+ case QbQuestion.TYPE_ORDERING:
+ AssessmentEscapeUtils.getOptionResponse(questionResult, QbQuestion.TYPE_ORDERING, row,
+ useLettersForMcq);
+ return;
+ case QbQuestion.TYPE_VERY_SHORT_ANSWERS:
+ value = questionResult.getAnswer();
+ break;
+ case QbQuestion.TYPE_TRUE_FALSE:
+ value = questionResult.getAnswerBoolean();
+ break;
+ case QbQuestion.TYPE_MARK_HEDGING:
+ //taken care beforehand
}
- return ret;
+ row.addCell(value);
}
public static String escapeStringForExcelExport(String input) {
@@ -264,26 +276,37 @@
/**
* Used only for excell export (for getUserSummaryData() method).
*/
- private static String getOptionResponse(AssessmentQuestionResult questionResult, int type) {
+ private static void getOptionResponse(AssessmentQuestionResult questionResult, int type, ExcelRow row,
+ boolean useLettersForMcq) {
StringBuilder sb = new StringBuilder();
//whether there is a need to remove last comma
boolean trimLastComma = false;
+ boolean highlightCell = false;
List options = questionResult.getQbQuestion().getQbOptions();
Set optionAnswers = questionResult.getOptionAnswers();
if (optionAnswers != null) {
-
if (type == QbQuestion.TYPE_MULTIPLE_CHOICE) {
- for (AssessmentOptionAnswer optionAnswer : optionAnswers) {
- if (optionAnswer.getAnswerBoolean()) {
- for (QbOption option : options) {
- if (option.getUid().equals(optionAnswer.getOptionUid())) {
- sb.append(option.getName() + ", ");
+ highlightCell = useLettersForMcq;
+ int letter = 'A';
+ for (QbOption option : options) {
+ for (AssessmentOptionAnswer optionAnswer : optionAnswers) {
+ if (option.getUid().equals(optionAnswer.getOptionUid())) {
+ if (optionAnswer.getAnswerBoolean()) {
+ // either we display full answers or just letters of chosen options
+ sb.append((useLettersForMcq ? String.valueOf((char) letter) : option.getName()) + ", ");
trimLastComma = true;
+
+ // if any answer is wrong, we do not highlight correct answer
+ if (option.getMaxMark() <= 0) {
+ highlightCell = false;
+ }
}
+ break;
}
}
+ letter++;
}
} else if (type == QbQuestion.TYPE_ORDERING) {
@@ -327,7 +350,6 @@
ret = ret.substring(0, ret.lastIndexOf(","));
}
- return ret;
+ row.addCell(ret, highlightCell ? IndexedColors.GREEN : IndexedColors.AUTOMATIC);
}
-
-}
+}
\ No newline at end of file