Index: lams_central/src/java/org/lamsfoundation/lams/web/qb/QbStatsController.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/qb/QbStatsController.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/qb/QbStatsController.java (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -0,0 +1,54 @@ +/**************************************************************** + * 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.web.qb; + +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.qb.dto.QbStatsDTO; +import org.lamsfoundation.lams.qb.service.IQbService; +import org.lamsfoundation.lams.util.MessageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +@RequestMapping("/qb/stats") +public class QbStatsController { + private static Logger log = Logger.getLogger(QbStatsController.class); + + @Autowired + @Qualifier("centralMessageService") + private MessageService messageService; + + @Autowired + private IQbService qbService; + + @RequestMapping("/show") + public String showStats(@RequestParam long qbQuestionUid, Model model) throws Exception { + QbStatsDTO stats = qbService.getStats(qbQuestionUid); + model.addAttribute("stats", stats); + return "qb/stats"; + } +} \ No newline at end of file Index: lams_central/web/qb/stats.jsp =================================================================== diff -u --- lams_central/web/qb/stats.jsp (revision 0) +++ lams_central/web/qb/stats.jsp (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -0,0 +1,225 @@ +<%@ page contentType="text/html; charset=utf-8" language="java"%> +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> +<%@ taglib uri="tags-core" prefix="c"%> + + + + + Question statistics + + + + + + + + + + + + + + + + +
+
+ Question +
+
+
+
+
+ Version: +
+
+ +
+
+
+
+ Title: +
+
+ +
+
+
+
+ Description: +
+
+ +
+
+
+
+ Feedback: +
+
+ +
+
+
+
+ Mark: +
+
+ +
+
+
+ +

Options

+ + + + + + + + + + + + + + + +
+ # + + Title + + Correct? + + Average selection
(as first choice) +
+ ${status.index + 1} + + + + + + + + <%--(${empty stats.answersRaw[option.uid] ? 0 : stats.answersRaw[option.uid]})--%> + ${stats.answersPercent[option.uid]}% +
+
+
+ +
+
+ Average selection chart +
+
+
+
+ +
+
+ Usage +
+
+ + + + + + + + + + + + + + + + +
+ Organisation + + Lesson + + Activity + + Tool type +
+ + + + + + + +
+
+
+ +
+
+ Previous versions +
+
+ + + + + + + + + + + + + +
+ # + + Created date + + Created ago +
+ v${version.version} + + + + +
+
+
+
+ +
\ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java =================================================================== diff -u -r2e9ee1c2451a05981f05e9edf89bb3356cf2b147 -r75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed --- lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java (.../IQbDAO.java) (revision 2e9ee1c2451a05981f05e9edf89bb3356cf2b147) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java (.../IQbDAO.java) (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -1,11 +1,22 @@ package org.lamsfoundation.lams.qb.dao; +import java.util.List; +import java.util.Map; + import org.lamsfoundation.lams.dao.IBaseDAO; +import org.lamsfoundation.lams.learningdesign.ToolActivity; +import org.lamsfoundation.lams.qb.model.QbQuestion; public interface IQbDAO extends IBaseDAO { // finds next question ID for Question Bank question int getMaxQuestionId(); // finds next version for given question ID for Question Bank question int getMaxQuestionVersion(Integer qbQuestionId); + + List getQuestionActivities(long qbQuestionUid); + + List getQuestionVersions(long qbQuestionUid); + + Map getAnswerStats(long qbQuestionUid); } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java =================================================================== diff -u -r2e9ee1c2451a05981f05e9edf89bb3356cf2b147 -r75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed --- lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java (.../QbDAO.java) (revision 2e9ee1c2451a05981f05e9edf89bb3356cf2b147) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java (.../QbDAO.java) (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -1,12 +1,24 @@ package org.lamsfoundation.lams.qb.dao.hibernate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO; +import org.lamsfoundation.lams.learningdesign.ToolActivity; import org.lamsfoundation.lams.qb.dao.IQbDAO; +import org.lamsfoundation.lams.qb.model.QbQuestion; public class QbDAO extends LAMSBaseDAO implements IQbDAO { private static final String FIND_MAX_QUESTION_ID = "SELECT MAX(questionId) FROM QbQuestion"; private static final String FIND_MAX_VERSION = "SELECT MAX(version) FROM QbQuestion AS q WHERE q.questionId = :questionId"; + private static final String FIND_QUESTION_ACTIVITIES = "SELECT a FROM ToolActivity AS a, QbToolQuestion AS q " + + "WHERE a.toolContentId = q.toolContentId AND a.learningDesign.lessons IS NOT EMPTY AND q.qbQuestion.uid = :qbQuestionUid"; + private static final String FIND_QUESTION_VERSIONS = "SELECT q FROM QbQuestion AS q, QbQuestion AS r " + + "WHERE q.questionId = r.questionId AND q.uid <> r.uid AND r.uid = :qbQuestionUid"; + private static final String FIND_ANSWER_STATS = "SELECT a.qbOption.uid, COUNT(a.uid) FROM QbToolAnswer AS a " + + "WHERE a.qbOption.qbQuestion.uid = :qbQuestionUid GROUP BY a.qbOption.uid"; @Override public int getMaxQuestionId() { @@ -22,4 +34,30 @@ Integer max = (Integer) result; return max == null ? 1 : max + 1; } + + @Override + @SuppressWarnings("unchecked") + public List getQuestionActivities(long qbQuestionUid) { + return this.getSession().createQuery(FIND_QUESTION_ACTIVITIES).setParameter("qbQuestionUid", qbQuestionUid) + .list(); + } + + @Override + @SuppressWarnings("unchecked") + public List getQuestionVersions(long qbQuestionUid) { + return this.getSession().createQuery(FIND_QUESTION_VERSIONS).setParameter("qbQuestionUid", qbQuestionUid) + .list(); + } + + @Override + @SuppressWarnings("unchecked") + public Map getAnswerStats(long qbQuestionUid) { + List result = this.getSession().createQuery(FIND_ANSWER_STATS) + .setParameter("qbQuestionUid", qbQuestionUid).list(); + Map map = new HashMap<>(result.size()); + for (Object[] answerStat : result) { + map.put((Long) answerStat[0], (Long) answerStat[1]); + } + return map; + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/dto/QbStatsDTO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/qb/dto/QbStatsDTO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dto/QbStatsDTO.java (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -0,0 +1,64 @@ +package org.lamsfoundation.lams.qb.dto; + +import java.util.List; +import java.util.Map; + +import org.lamsfoundation.lams.learningdesign.ToolActivity; +import org.lamsfoundation.lams.qb.model.QbQuestion; + +public class QbStatsDTO { + private QbQuestion question; + private Map answersRaw; + private Map answersPercent; + private String answersJSON; + private List activities; + private List versions; + + public QbQuestion getQuestion() { + return question; + } + + public void setQuestion(QbQuestion question) { + this.question = question; + } + + public Map getAnswersRaw() { + return answersRaw; + } + + public void setAnswersRaw(Map answers) { + this.answersRaw = answers; + } + + public Map getAnswersPercent() { + return answersPercent; + } + + public void setAnswersPercent(Map answersPercent) { + this.answersPercent = answersPercent; + } + + public String getAnswersJSON() { + return answersJSON; + } + + public void setAnswersJSON(String answersJSON) { + this.answersJSON = answersJSON; + } + + public List getActivities() { + return activities; + } + + public void setActivities(List activities) { + this.activities = activities; + } + + public List getVersions() { + return versions; + } + + public void setVersions(List versions) { + this.versions = versions; + } +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java =================================================================== diff -u -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 -r75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed --- lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -1,9 +1,11 @@ package org.lamsfoundation.lams.qb.service; +import org.lamsfoundation.lams.qb.dto.QbStatsDTO; + public interface IQbService { // statuses of comparing QB question coming from authoring with data existing in DB - + // no change detected static final int QUESTION_MODIFIED_NONE = 0; // change is minor, so the original question will be modified @@ -18,4 +20,6 @@ // finds next version for given question ID for Question Bank question int getMaxQuestionVersion(Integer qbQuestionId); + + QbStatsDTO getStats(long qbQuestionUid); } Index: lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java =================================================================== diff -u -r2e9ee1c2451a05981f05e9edf89bb3356cf2b147 -r75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed --- lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java (.../QbService.java) (revision 2e9ee1c2451a05981f05e9edf89bb3356cf2b147) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java (.../QbService.java) (revision 75e43eb7dffa6bf6eb2a11bb1e616c53bdbd76ed) @@ -1,7 +1,18 @@ package org.lamsfoundation.lams.qb.service; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.lamsfoundation.lams.qb.dao.IQbDAO; +import org.lamsfoundation.lams.qb.dto.QbStatsDTO; +import org.lamsfoundation.lams.qb.model.QbOption; +import org.lamsfoundation.lams.qb.model.QbQuestion; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + public class QbService implements IQbService { private IQbDAO qbDAO; @@ -16,6 +27,39 @@ return qbDAO.getMaxQuestionVersion(qbQuestionId); } + @Override + public QbStatsDTO getStats(long qbQuestionUid) { + QbStatsDTO stats = new QbStatsDTO(); + stats.setQuestion((QbQuestion) qbDAO.find(QbQuestion.class, qbQuestionUid)); + stats.setActivities(qbDAO.getQuestionActivities(qbQuestionUid)); + stats.setVersions(qbDAO.getQuestionVersions(qbQuestionUid)); + Map answersRaw = qbDAO.getAnswerStats(qbQuestionUid); + stats.setAnswersRaw(answersRaw); + + ArrayNode answersJSON = JsonNodeFactory.instance.arrayNode(); + double total = 0; + for (Long answerCount : answersRaw.values()) { + total += answerCount; + } + Map answerPercent = new HashMap<>(); + List qbOptions = stats.getQuestion().getQbOptions(); + for (int answerIndex = 0; answerIndex < qbOptions.size(); answerIndex++) { + QbOption option = qbOptions.get(answerIndex); + Long answerCount = answersRaw.get(option.getUid()); + int value = answerCount == null ? 0 : Long.valueOf(Math.round(answerCount / total * 100)).intValue(); + answerPercent.put(option.getUid(), value); + + ObjectNode answerJSON = JsonNodeFactory.instance.objectNode(); + answerJSON.put("name", (answerIndex + 1) + ". " + + (option.getName().length() > 20 ? option.getName().substring(0, 20) + "..." : option.getName())); + answerJSON.put("value", value); + answersJSON.add(answerJSON); + } + stats.setAnswersPercent(answerPercent); + stats.setAnswersJSON(answersJSON.toString()); + return stats; + } + public void setQbDAO(IQbDAO qbDAO) { this.qbDAO = qbDAO; }