Index: lams_admin/web/WEB-INF/tags/OutcomeAuthor.tag =================================================================== diff -u -rba2b4e0d8f708d647ab75bf4db71559dfb249e07 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_admin/web/WEB-INF/tags/OutcomeAuthor.tag (.../OutcomeAuthor.tag) (revision ba2b4e0d8f708d647ab75bf4db71559dfb249e07) +++ lams_admin/web/WEB-INF/tags/OutcomeAuthor.tag (.../OutcomeAuthor.tag) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -16,6 +16,7 @@ <%@ attribute name="lessonId" required="false" rtexprvalue="true" %> <%@ attribute name="toolContentId" required="false" rtexprvalue="true" %> <%@ attribute name="itemId" required="false" rtexprvalue="true" %> +<%@ attribute name="qbQuestionId" required="false" rtexprvalue="true" %> <%-- Support for multiple tags on one page --%> @@ -43,7 +44,8 @@ var outcomeData${outcomeTagId} = { lessonId : '${lessonId}', toolContentId : '${toolContentId}', - itemId : '${itemId}' + itemId : '${itemId}', + qbQuestionId : '${qbQuestionId}' }, // keep mapped outcome IDs for search result filtering outcomeMappingIds${outcomeTagId} = [], @@ -56,7 +58,7 @@ $(document).ready(function(){ $('#outcomeSearchInput${outcomeTagId}').autocomplete({ - 'source' : "outcome/outcomeSearch.do?organisationIds=${organisations}", + 'source' : "outcome/outcomeSearch.do", 'delay' : 700, 'minLength' : 2, 'response' : function(event, ui) { @@ -91,6 +93,7 @@ var input = $(this); $.ajax({ 'url' : 'outcome/outcomeMap.do', + 'dataType' : 'text', 'data': $.extend({ 'outcomeId' : ui.item.value, // if value is null, then it is a new outcome to create; remove ' [create new]' part before sending @@ -116,9 +119,11 @@ * Loads existing mappings */ function refreshOutcomeMappings(outcomeTagId) { + var outcomeData = outcomeData${outcomeTagId}; + $.ajax({ 'url' : 'outcome/outcomeGetMappings.do', - 'data' : outcomeData${outcomeTagId}, + 'data' : outcomeData, 'cache' : false, 'dataType' : 'json', 'success' : function(outcomeMappings) { @@ -134,13 +139,17 @@ // cache already mapped outcomes outcomeMappingIds${outcomeTagId}.push(this.outcomeId); // add a label with outcome information - var outcomeButton = $(' - - - - - -
-
-
- Existing collections -
-
-
- - -
-
-
- -
-
- - - -
-
-
- - - - - -
-
- Average selection chart -
-
-
-
-
- -
-
- Burning questions -
-
- - - This question does not have any burning questions - - - - - - - - - - - - - -
- Question - - Likes -
- - - -
-
-
-
-
- -
-
- Usage in lessons -
-
- - - This question is not used in any lesson - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Organisation - - Lesson - - Activity - - Tool type - - Test participant count - - Difficulty index - - Discrimination index - - Point biserial -
- - - - - - - - - - - - - - - - - - - ---
-
-
-
-
- -
-
- Previous versions -
-
- - - This question does not have any previous versions - - - - - - - - - - - - - - - -
- # - - Created date - - Created ago -
- v${version.version} - - - - -
-
-
-
-
- - - - - - - +<%@ 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]}% +
+
+
+
+ +
+
+ Collections +
+
+
+ +
+
+ Transfer questions to +
+
+ +
+
+ + +
+
+
+ +
+
+
+ Existing collections +
+
+
+ + +
+
+
+ +
+
+ + + +
+
+
+
+
+
+ + + + +
+
+ Average selection chart +
+
+
+
+
+ +
+
+ Burning questions +
+
+ + + This question does not have any burning questions + + + + + + + + + + + + + +
+ Question + + Likes +
+ + + +
+
+
+
+
+ +
+
+ Usage in lessons +
+
+ + + This question is not used in any lesson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Organisation + + Lesson + + Activity + + Tool type + + Test participant count + + Difficulty index + + Discrimination index + + Point biserial +
+ + + + + + + + + + + + + + + + + + + ---
+
+
+
+
+ +
+
+ Previous versions +
+
+ + + This question does not have any previous versions + + + + + + + + + + + + + + + +
+ # + + Created date + + Created ago +
+ v${version.version} + + + + +
+
+
+
+
+
+ + + + + +
\ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/commonContext.xml =================================================================== diff -u -r394f403c289f0fd7808c228840bead5c4e7d5d32 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision 394f403c289f0fd7808c228840bead5c4e7d5d32) +++ lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,242 +1,242 @@ - - - - - - - - - - - - - - - - org.lamsfoundation.lams.comments - org.lamsfoundation.lams.config - org.lamsfoundation.lams.contentrepository - org.lamsfoundation.lams.events - org.lamsfoundation.lams.gradebook - org.lamsfoundation.lams.integration - org.lamsfoundation.lams.learningdesign - org.lamsfoundation.lams.lesson - org.lamsfoundation.lams.logevent - org.lamsfoundation.lams.outcome - org.lamsfoundation.lams.planner - org.lamsfoundation.lams.policies - org.lamsfoundation.lams.qb - org.lamsfoundation.lams.themes - org.lamsfoundation.lams.timezone - org.lamsfoundation.lams.tool - org.lamsfoundation.lams.usermanagement - org.lamsfoundation.lams.workspace - org.lamsfoundation.lams.**.model - - - - - - - - - - - - - true - - - PROPAGATION_REQUIRES_NEW - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - + + + + + + + + + + + + + + + + org.lamsfoundation.lams.comments + org.lamsfoundation.lams.config + org.lamsfoundation.lams.contentrepository + org.lamsfoundation.lams.events + org.lamsfoundation.lams.gradebook + org.lamsfoundation.lams.integration + org.lamsfoundation.lams.learningdesign + org.lamsfoundation.lams.lesson + org.lamsfoundation.lams.logevent + org.lamsfoundation.lams.outcome + org.lamsfoundation.lams.planner + org.lamsfoundation.lams.policies + org.lamsfoundation.lams.qb + org.lamsfoundation.lams.themes + org.lamsfoundation.lams.timezone + org.lamsfoundation.lams.tool + org.lamsfoundation.lams.usermanagement + org.lamsfoundation.lams.workspace + org.lamsfoundation.lams.**.model + + + + + + + + + + + + + true + + + PROPAGATION_REQUIRES_NEW + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + @@ -260,266 +260,266 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED,+java.lang.Exception - PROPAGATION_REQUIRED,+java.lang.Exception - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED,+java.lang.Exception + PROPAGATION_REQUIRED,+java.lang.Exception + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - org.springframework.scheduling.quartz.LocalDataSourceJobStore - org.quartz.impl.jdbcjobstore.StdJDBCDelegate - lams_qtz_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - - - - - - - true - - - PROPAGATION_REQUIRED - PROPAGATION_REQUIRED - - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED,readOnly - - - - + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + org.springframework.scheduling.quartz.LocalDataSourceJobStore + org.quartz.impl.jdbcjobstore.StdJDBCDelegate + lams_qtz_ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + + + + true + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + + + + + + + true + + + + + + + + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED,readOnly + + + + @@ -541,190 +541,192 @@ - - - - - - - - - - - - - true - - - - - - - - PROPAGATION_REQUIRED - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + true + + + + + + + + PROPAGATION_REQUIRED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: lams_common/src/java/org/lamsfoundation/lams/dao/hibernate/LAMSBaseDAO.java =================================================================== diff -u -r48f344a789c9af4dcca102b6b5914333e9cbd17a -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/dao/hibernate/LAMSBaseDAO.java (.../LAMSBaseDAO.java) (revision 48f344a789c9af4dcca102b6b5914333e9cbd17a) +++ lams_common/src/java/org/lamsfoundation/lams/dao/hibernate/LAMSBaseDAO.java (.../LAMSBaseDAO.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,604 +1,604 @@ -package org.lamsfoundation.lams.dao.hibernate; - -import java.io.Serializable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Root; - -import org.apache.log4j.Logger; -import org.hibernate.Hibernate; -import org.hibernate.HibernateException; -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.query.Query; -import org.lamsfoundation.lams.dao.IBaseDAO; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Repository; - -@Repository -public class LAMSBaseDAO implements IBaseDAO { - - private static class Qv { - - String queryString; - Object[] values; - - Qv(String queryString, Object[] values) { - super(); - - this.queryString = queryString; - this.values = values; - } - } - - private static final String SELECT = "from "; - private static final String DELETE = "delete "; - private static final String WHERE = " where "; - private static final String AND = " and "; - private static final String SPACE = " "; - private static final String SPOT = "."; - private static final String EQUAL_TO_WHAT = "=?"; - private static final String LIKE_WHAT = " like ?"; - - private static Logger log = Logger.getLogger(LAMSBaseDAO.class); - - @Autowired - @Qualifier("coreSessionFactory") - private SessionFactory sessionFactory; - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#insert(java.lang.Object) - */ - @Override - public void insert(Object object) { - getSession().save(object); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#update(java.lang.Object) - */ - @Override - public void update(Object object) { - getSession().update(object); - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#insertOrUpdate(java.lang.Object) - */ - @Override - public void insertOrUpdate(Object object) { - getSession().saveOrUpdate(object); - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#insertOrUpdateAll(java.util.Collection - * ) - */ - @Override - public void insertOrUpdateAll(Collection objects) { - if (objects != null) { - for (Object object : objects) { - getSession().saveOrUpdate(object); - } - } - } - - @Override - public void update(String queryString) { - doBulkUpdate(queryString); - } - - @Override - public void update(String queryString, Object value) { - doBulkUpdate(queryString, value); - } - - @Override - public void update(String queryString, Object[] values) { - doBulkUpdate(queryString, values); - } - - @Override - public void update(Class clazz, String propertyToChange, Object newValue, String conditionProperty, - Object conditionValue) { - // TODO implement me - } - - @Override - public void update(Class clazz, String propertyToChange, Object newValue, Map conditions) { - // TODO implement me - } - - @Override - public void update(Class clazz, Map newValues, String conditionProperty, Object conditionValue) { - // TODO implement me - } - - @Override - public void update(Class clazz, Map newValues, Map conditions) { - // TODO implement me - } - - @Override - public void updateAnythingLike(Class clazz, Object newValues, Object conditions) { - // TODO implement me - } - - @Override - public void flush() { - getSession().flush(); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#delete(java.lang.Object) - */ - @Override - public void delete(Object object) { - getSession().delete(object); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#deleteAll(java.lang.Class) - */ - @Override - public void deleteAll(Class clazz) { - String queryString = buildQueryString(clazz, DELETE); - doBulkUpdate(queryString); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#deleteAll(java.util.Collection) - */ - @Override - public void deleteAll(Collection objects) { - doDeleteAll(objects); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#deleteById(java.lang.Class, - * java.io.Serializable) - */ - @Override - public void deleteById(Class clazz, Serializable id) { - delete(find(clazz, id)); - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#deleteByProperty(java.lang.Class, - * java.lang.String, java.lang.Object) - */ - @Override - public void deleteByProperty(Class clazz, String name, Object value) { - String queryString = buildQueryString(clazz, name, DELETE); - doBulkUpdate(queryString, value); - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#deleteByProperties(java.lang.Class, - * java.util.Map) - */ - @Override - public void deleteByProperties(Class clazz, Map properties) { - Qv qv = buildQueryString(clazz, properties, DELETE, EQUAL_TO_WHAT); - doBulkUpdate(qv.queryString, qv.values); - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#deleteAnythingLike(java.lang.Object) - */ - @Override - public void deleteAnythingLike(Object object) { - try { - Qv qv = buildQueryString(object, DELETE); - doBulkUpdate(qv.queryString, qv.values); - } catch (Exception e) { - log.debug(e); - } - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#find(java.lang.Class, - * java.io.Serializable) - */ - @Override - public T find(Class clazz, Serializable id) { - return getSession().get(clazz, id); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#findAll(java.lang.Class) - */ - @Override - public List findAll(Class clazz) { - return loadAll(clazz); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#findByProperty(java.lang.Class, - * java.lang.String, java.lang.Object) - */ - @Override - public List findByProperty(Class clazz, String name, Object value) { - String queryString = buildQueryString(clazz, name, SELECT); - return doFind(queryString, value); - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#findByProperties(java.lang.Class, - * java.util.Map) - */ - @SuppressWarnings("unchecked") - @Override - public List findByProperties(Class clazz, Map properties) { - Qv qv = buildQueryString(clazz, properties, SELECT, EQUAL_TO_WHAT); - return (List) doFind(qv.queryString, qv.values); - } - - private String buildQueryString(Class clazz, String operation) { - StringBuilder queryString = new StringBuilder(operation).append(clazz.getSimpleName()); - // log.debug(queryString); - return queryString.toString(); - } - - private String buildQueryString(Class clazz, String name, String operation) { - String clazzName = clazz.getSimpleName(); - String objName = createObjectName(clazzName); - StringBuilder queryString = new StringBuilder(operation).append(clazzName).append(SPACE).append(objName) - .append(WHERE).append(objName).append(SPOT).append(name).append(EQUAL_TO_WHAT); - // log.debug(queryString); - return queryString.toString(); - } - - @Override - public List find(String queryString) { - return doFind(queryString); - } - - @Override - public List find(String queryString, Object value) { - return doFind(queryString, value); - } - - @Override - public List find(String queryString, Object[] values) { - return doFind(queryString, values); - } - - @Override - public List findByNamedQuery(String queryName) { - return doFindByNamedQuery(queryName); - } - - @Override - public List findByNamedQuery(String queryName, Object value) { - return doFindByNamedQuery(queryName, value); - } - - @Override - public List findByNamedQuery(String queryName, Object[] values) { - return doFindByNamedQuery(queryName, values); - } - - @Override - public List searchByStringProperty(Class clazz, String name, String pattern) { - // TODO implement me - return null; - } - - @SuppressWarnings("unchecked") - @Override - public List searchByStringProperties(Class clazz, Map properties) { - Map p = new HashMap<>(); - for (Map.Entry entry : properties.entrySet()) { - p.put(entry.getKey(), entry.getValue()); - } - Qv qv = buildQueryString(clazz, p, SELECT, LIKE_WHAT); - return (List) doFind(qv.queryString, qv.values); - } - - @Override - public List searchByNumberSpan(Class clazz, String name, Integer min, Boolean minIncluded, Integer max, - Boolean maxIncluded) { - // TODO implement me - return null; - } - - private Qv buildQueryString(Class clazz, Map properties, String operation, String condition) { - String clazzName = clazz.getSimpleName(); - String objName = createObjectName(clazzName); - StringBuilder queryString = new StringBuilder(operation).append(clazzName).append(SPACE).append(objName) - .append(WHERE); - Object[] values = new Object[properties.size()]; - int i = 0; - for (Map.Entry entry : properties.entrySet()) { - queryString.append(objName).append(SPOT).append(entry.getKey()).append(condition); - if (i != properties.size() - 1) { - queryString.append(AND); - } - values[i] = entry.getValue(); - i++; - } - // log.debug(queryString); - return new Qv(queryString.toString(), values); - } - - private Qv buildQueryString(Object obj, String operation) throws Exception { - String clazzName = obj.getClass().getSimpleName(); - String objName = createObjectName(clazzName); - StringBuilder queryString = new StringBuilder(operation).append(clazzName).append(SPACE).append(objName) - .append(WHERE); - Field[] fields = obj.getClass().getDeclaredFields(); - List values = new ArrayList<>(); - for (int i = 0; i < fields.length; i++) { - String name = fields[i].getName(); - Method readMethod = getReadMethod(fields[i], name, obj.getClass()); - Object value = readMethod.invoke(obj); - if (value != null) { - queryString.append(objName).append(SPOT).append(name).append(EQUAL_TO_WHAT); - if (i != fields.length - 1) { - queryString.append(AND); - } - values.add(value); - } - } - // log.debug(queryString); - return new Qv(queryString.toString(), values.toArray()); - } - - private Method getReadMethod(Field field, String fieldName, Class clazz) throws Exception { - String convertedName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); - if (field.getType().getSimpleName().equals("Boolean")) { - return clazz.getDeclaredMethod("is" + convertedName); - } else { - return clazz.getDeclaredMethod("get" + convertedName); - } - - } - - private String createObjectName(String clazzName) { - return clazzName.substring(0, 1).toLowerCase(); - } - - @Override - public void initialize(Object proxy) { - Hibernate.initialize(proxy); - } - - /* - * (non-Javadoc) - * - * @see org.lamsfoundation.lams.dao.IBaseDAO#countAll(java.lang.Class) - */ - @Override - public long countAll(Class clazz) { - String query = "select count(*) from " + clazz.getSimpleName(); - - List list = doFind(query); - - if (list != null && list.size() > 0) { - return (Long) list.get(0); - } else { - return 0; - } - } - - /* - * (non-Javadoc) - * - * @see - * org.lamsfoundation.lams.dao.IBaseDAO#countByProperties(java.lang.Class, - * java.util.Map) - */ - @Override - public long countByProperties(Class clazz, Map properties) { - Qv qv = buildQueryString(clazz, properties, SELECT, EQUAL_TO_WHAT); - String query = "select count(*) " + qv.queryString; - - List list = doFind(query, qv.values); - - if (list != null && list.size() > 0) { - return (Long) list.get(0); - } else { - return 0; - } - } - - protected Session getSession() { - return sessionFactory.getCurrentSession(); - } - - public void setSessionFactory(SessionFactory sessionFactory) { - this.sessionFactory = sessionFactory; - } - - public SessionFactory getSessionFactory() { - return sessionFactory; - } - - public List doFind(final String queryString, final Object... values) { - Query queryObject = convertLegacyStyleParameters(queryString, values); - return queryObject.list(); - } - - private Query convertLegacyStyleParameters(final String queryString, final Object... values) { - Query queryObject = null; - if (values == null) { - queryObject = getSession().createQuery(queryString); - } else { - // replace all the current ? with :P1, :P2, etc - String[] parts = Pattern.compile("\\?").split(queryString, 0); - StringBuilder bldr = new StringBuilder(parts[0]); - int i = 1; - if (parts.length > 1) { - for (; i < parts.length; i++) { - bldr.append(":P").append(i).append(" ").append(parts[i]); - } - } - if (queryString.endsWith("?")) { - bldr.append(" :P").append(i).append(" "); - } - queryObject = getSession().createQuery(bldr.toString()); - for (i = 0; i < values.length; i++) { - queryObject.setParameter("P" + Integer.toString(i + 1), values[i]); - } - } - return queryObject; - } - - public int doBulkUpdate(final String queryString, final Object... values) { - Query queryObject = convertLegacyStyleParameters(queryString, values); - return queryObject.executeUpdate(); - } - - public List doFindByNamedQuery(final String queryName, final Object... values) { - Query queryObject = getSession().getNamedQuery(queryName); - if (values != null) { - for (int i = 0; i < values.length; i++) { - queryObject.setParameter(i, values[i]); - } - } - return queryObject.list(); - } - - public List loadAll(final Class entityClass) { - CriteriaBuilder builder = getSession().getCriteriaBuilder(); - CriteriaQuery query = builder.createQuery(entityClass); - Root variableRoot = query.from(entityClass); - query.select(variableRoot); - return getSession().createQuery(query).getResultList(); - - } - - public void doDeleteAll(final Collection entities) { - for (Object entity : entities) { - getSession().delete(entity); - } - } - - public List doFindByNamedParam(final String queryString, final String[] paramNames, final Object[] values) { - - if (paramNames.length != values.length) { - throw new IllegalArgumentException("Length of paramNames array must match length of values array"); - } - Query queryObject = getSession().createQuery(queryString); - if (values != null) { - for (int i = 0; i < values.length; i++) { - applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); - } - } - return queryObject.list(); - } - - private void applyNamedParameterToQuery(Query queryObject, String paramName, Object value) - throws HibernateException { - - if (value instanceof Collection) { - queryObject.setParameterList(paramName, (Collection) value); - } else if (value instanceof Object[]) { - queryObject.setParameterList(paramName, (Object[]) value); - } else { - queryObject.setParameter(paramName, value); - } - } - - public List doFindByNamedQueryAndNamedParam(String queryName, String paramName, Object value) { - - return doFindByNamedQueryAndNamedParam(queryName, new String[] { paramName }, new Object[] { value }); - } - - public List doFindByNamedQueryAndNamedParam(final String queryName, final String[] paramNames, - final Object[] values) { - - if (values != null && (paramNames == null || paramNames.length != values.length)) { - throw new IllegalArgumentException("Length of paramNames array must match length of values array"); - } - Query queryObject = getSession().getNamedQuery(queryName); - if (values != null) { - for (int i = 0; i < values.length; i++) { - applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); - } - } - - return queryObject.list(); - } - - /** - * @see com.edgenius.paradise.dao.DAO#saveObject(java.lang.Object) - */ - public void saveObject(Object o) { - getSession().saveOrUpdate(o); - } - - /** - * @see com.edgenius.paradise.dao.DAO#getObject(java.lang.Class, java.io.Serializable) - */ - public Object getObject(Class clazz, Serializable id) { - Object o = getSession().get(clazz, id); - return o; - } - - /** - * @see com.edgenius.paradise.dao.DAO#getObjects(java.lang.Class) - */ - public List getObjects(Class clazz) { - return loadAll(clazz); - } - - /** - * @see com.edgenius.paradise.dao.DAO#removeObject(java.lang.Class, java.io.Serializable) - */ - public void removeObject(Class clazz, Serializable id) { - getSession().delete(getObject(clazz, id)); - } - - @Override - public void releaseFromCache(Object o) { - getSessionFactory().getCurrentSession().evict(o); - } -} +package org.lamsfoundation.lams.dao.hibernate; + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.apache.log4j.Logger; +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.query.Query; +import org.lamsfoundation.lams.dao.IBaseDAO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Repository; + +@Repository +public class LAMSBaseDAO implements IBaseDAO { + + private static class Qv { + + String queryString; + Object[] values; + + Qv(String queryString, Object[] values) { + super(); + + this.queryString = queryString; + this.values = values; + } + } + + private static final String SELECT = "from "; + private static final String DELETE = "delete "; + private static final String WHERE = " where "; + private static final String AND = " and "; + private static final String SPACE = " "; + private static final String SPOT = "."; + private static final String EQUAL_TO_WHAT = "=?"; + private static final String LIKE_WHAT = " like ?"; + + private static Logger log = Logger.getLogger(LAMSBaseDAO.class); + + @Autowired + @Qualifier("coreSessionFactory") + private SessionFactory sessionFactory; + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#insert(java.lang.Object) + */ + @Override + public void insert(Object object) { + getSession().save(object); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#update(java.lang.Object) + */ + @Override + public void update(Object object) { + getSession().update(object); + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#insertOrUpdate(java.lang.Object) + */ + @Override + public void insertOrUpdate(Object object) { + getSession().saveOrUpdate(object); + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#insertOrUpdateAll(java.util.Collection + * ) + */ + @Override + public void insertOrUpdateAll(Collection objects) { + if (objects != null) { + for (Object object : objects) { + getSession().saveOrUpdate(object); + } + } + } + + @Override + public void update(String queryString) { + doBulkUpdate(queryString); + } + + @Override + public void update(String queryString, Object value) { + doBulkUpdate(queryString, value); + } + + @Override + public void update(String queryString, Object[] values) { + doBulkUpdate(queryString, values); + } + + @Override + public void update(Class clazz, String propertyToChange, Object newValue, String conditionProperty, + Object conditionValue) { + // TODO implement me + } + + @Override + public void update(Class clazz, String propertyToChange, Object newValue, Map conditions) { + // TODO implement me + } + + @Override + public void update(Class clazz, Map newValues, String conditionProperty, Object conditionValue) { + // TODO implement me + } + + @Override + public void update(Class clazz, Map newValues, Map conditions) { + // TODO implement me + } + + @Override + public void updateAnythingLike(Class clazz, Object newValues, Object conditions) { + // TODO implement me + } + + @Override + public void flush() { + getSession().flush(); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#delete(java.lang.Object) + */ + @Override + public void delete(Object object) { + getSession().delete(object); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#deleteAll(java.lang.Class) + */ + @Override + public void deleteAll(Class clazz) { + String queryString = buildQueryString(clazz, DELETE); + doBulkUpdate(queryString); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#deleteAll(java.util.Collection) + */ + @Override + public void deleteAll(Collection objects) { + doDeleteAll(objects); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#deleteById(java.lang.Class, + * java.io.Serializable) + */ + @Override + public void deleteById(Class clazz, Serializable id) { + delete(find(clazz, id)); + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#deleteByProperty(java.lang.Class, + * java.lang.String, java.lang.Object) + */ + @Override + public void deleteByProperty(Class clazz, String name, Object value) { + String queryString = buildQueryString(clazz, name, DELETE); + doBulkUpdate(queryString, value); + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#deleteByProperties(java.lang.Class, + * java.util.Map) + */ + @Override + public void deleteByProperties(Class clazz, Map properties) { + Qv qv = buildQueryString(clazz, properties, DELETE, EQUAL_TO_WHAT); + doBulkUpdate(qv.queryString, qv.values); + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#deleteAnythingLike(java.lang.Object) + */ + @Override + public void deleteAnythingLike(Object object) { + try { + Qv qv = buildQueryString(object, DELETE); + doBulkUpdate(qv.queryString, qv.values); + } catch (Exception e) { + log.debug(e); + } + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#find(java.lang.Class, + * java.io.Serializable) + */ + @Override + public T find(Class clazz, Serializable id) { + return getSession().get(clazz, id); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#findAll(java.lang.Class) + */ + @Override + public List findAll(Class clazz) { + return loadAll(clazz); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#findByProperty(java.lang.Class, + * java.lang.String, java.lang.Object) + */ + @Override + public List findByProperty(Class clazz, String name, Object value) { + String queryString = buildQueryString(clazz, name, SELECT); + return doFind(queryString, value); + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#findByProperties(java.lang.Class, + * java.util.Map) + */ + @SuppressWarnings("unchecked") + @Override + public List findByProperties(Class clazz, Map properties) { + Qv qv = buildQueryString(clazz, properties, SELECT, EQUAL_TO_WHAT); + return doFind(qv.queryString, qv.values); + } + + private String buildQueryString(Class clazz, String operation) { + StringBuilder queryString = new StringBuilder(operation).append(clazz.getSimpleName()); + // log.debug(queryString); + return queryString.toString(); + } + + private String buildQueryString(Class clazz, String name, String operation) { + String clazzName = clazz.getSimpleName(); + String objName = createObjectName(clazzName); + StringBuilder queryString = new StringBuilder(operation).append(clazzName).append(SPACE).append(objName) + .append(WHERE).append(objName).append(SPOT).append(name).append(EQUAL_TO_WHAT); + // log.debug(queryString); + return queryString.toString(); + } + + @Override + public List find(String queryString) { + return doFind(queryString); + } + + @Override + public List find(String queryString, Object value) { + return doFind(queryString, value); + } + + @Override + public List find(String queryString, Object[] values) { + return doFind(queryString, values); + } + + @Override + public List findByNamedQuery(String queryName) { + return doFindByNamedQuery(queryName); + } + + @Override + public List findByNamedQuery(String queryName, Object value) { + return doFindByNamedQuery(queryName, value); + } + + @Override + public List findByNamedQuery(String queryName, Object[] values) { + return doFindByNamedQuery(queryName, values); + } + + @Override + public List searchByStringProperty(Class clazz, String name, String pattern) { + // TODO implement me + return null; + } + + @SuppressWarnings("unchecked") + @Override + public List searchByStringProperties(Class clazz, Map properties) { + Map p = new HashMap<>(); + for (Map.Entry entry : properties.entrySet()) { + p.put(entry.getKey(), entry.getValue()); + } + Qv qv = buildQueryString(clazz, p, SELECT, LIKE_WHAT); + return doFind(qv.queryString, qv.values); + } + + @Override + public List searchByNumberSpan(Class clazz, String name, Integer min, Boolean minIncluded, Integer max, + Boolean maxIncluded) { + // TODO implement me + return null; + } + + private Qv buildQueryString(Class clazz, Map properties, String operation, String condition) { + String clazzName = clazz.getSimpleName(); + String objName = createObjectName(clazzName); + StringBuilder queryString = new StringBuilder(operation).append(clazzName).append(SPACE).append(objName) + .append(WHERE); + Object[] values = new Object[properties.size()]; + int i = 0; + for (Map.Entry entry : properties.entrySet()) { + queryString.append(objName).append(SPOT).append(entry.getKey()).append(condition); + if (i != properties.size() - 1) { + queryString.append(AND); + } + values[i] = entry.getValue(); + i++; + } + // log.debug(queryString); + return new Qv(queryString.toString(), values); + } + + private Qv buildQueryString(Object obj, String operation) throws Exception { + String clazzName = obj.getClass().getSimpleName(); + String objName = createObjectName(clazzName); + StringBuilder queryString = new StringBuilder(operation).append(clazzName).append(SPACE).append(objName) + .append(WHERE); + Field[] fields = obj.getClass().getDeclaredFields(); + List values = new ArrayList<>(); + for (int i = 0; i < fields.length; i++) { + String name = fields[i].getName(); + Method readMethod = getReadMethod(fields[i], name, obj.getClass()); + Object value = readMethod.invoke(obj); + if (value != null) { + queryString.append(objName).append(SPOT).append(name).append(EQUAL_TO_WHAT); + if (i != fields.length - 1) { + queryString.append(AND); + } + values.add(value); + } + } + // log.debug(queryString); + return new Qv(queryString.toString(), values.toArray()); + } + + private Method getReadMethod(Field field, String fieldName, Class clazz) throws Exception { + String convertedName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + if (field.getType().getSimpleName().equals("Boolean")) { + return clazz.getDeclaredMethod("is" + convertedName); + } else { + return clazz.getDeclaredMethod("get" + convertedName); + } + + } + + private String createObjectName(String clazzName) { + return clazzName.substring(0, 1).toLowerCase(); + } + + @Override + public void initialize(Object proxy) { + Hibernate.initialize(proxy); + } + + /* + * (non-Javadoc) + * + * @see org.lamsfoundation.lams.dao.IBaseDAO#countAll(java.lang.Class) + */ + @Override + public long countAll(Class clazz) { + String query = "select count(*) from " + clazz.getSimpleName(); + + List list = doFind(query); + + if (list != null && list.size() > 0) { + return (Long) list.get(0); + } else { + return 0; + } + } + + /* + * (non-Javadoc) + * + * @see + * org.lamsfoundation.lams.dao.IBaseDAO#countByProperties(java.lang.Class, + * java.util.Map) + */ + @Override + public long countByProperties(Class clazz, Map properties) { + Qv qv = buildQueryString(clazz, properties, SELECT, EQUAL_TO_WHAT); + String query = "select count(*) " + qv.queryString; + + List list = doFind(query, qv.values); + + if (list != null && list.size() > 0) { + return (Long) list.get(0); + } else { + return 0; + } + } + + protected Session getSession() { + return sessionFactory.getCurrentSession(); + } + + public void setSessionFactory(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } + + public SessionFactory getSessionFactory() { + return sessionFactory; + } + + public List doFind(final String queryString, final Object... values) { + Query queryObject = convertLegacyStyleParameters(queryString, values); + return queryObject.list(); + } + + private Query convertLegacyStyleParameters(final String queryString, final Object... values) { + Query queryObject = null; + if (values == null) { + queryObject = getSession().createQuery(queryString); + } else { + // replace all the current ? with :P1, :P2, etc + String[] parts = Pattern.compile("\\?").split(queryString, 0); + StringBuilder bldr = new StringBuilder(parts[0]); + int i = 1; + if (parts.length > 1) { + for (; i < parts.length; i++) { + bldr.append(":P").append(i).append(" ").append(parts[i]); + } + } + if (queryString.endsWith("?")) { + bldr.append(" :P").append(i).append(" "); + } + queryObject = getSession().createQuery(bldr.toString()); + for (i = 0; i < values.length; i++) { + queryObject.setParameter("P" + Integer.toString(i + 1), values[i]); + } + } + return queryObject; + } + + public int doBulkUpdate(final String queryString, final Object... values) { + Query queryObject = convertLegacyStyleParameters(queryString, values); + return queryObject.executeUpdate(); + } + + public List doFindByNamedQuery(final String queryName, final Object... values) { + Query queryObject = getSession().getNamedQuery(queryName); + if (values != null) { + for (int i = 0; i < values.length; i++) { + queryObject.setParameter(i, values[i]); + } + } + return queryObject.list(); + } + + public List loadAll(final Class entityClass) { + CriteriaBuilder builder = getSession().getCriteriaBuilder(); + CriteriaQuery query = builder.createQuery(entityClass); + Root variableRoot = query.from(entityClass); + query.select(variableRoot); + return getSession().createQuery(query).getResultList(); + + } + + public void doDeleteAll(final Collection entities) { + for (Object entity : entities) { + getSession().delete(entity); + } + } + + public List doFindByNamedParam(final String queryString, final String[] paramNames, final Object[] values) { + + if (paramNames.length != values.length) { + throw new IllegalArgumentException("Length of paramNames array must match length of values array"); + } + Query queryObject = getSession().createQuery(queryString); + if (values != null) { + for (int i = 0; i < values.length; i++) { + applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); + } + } + return queryObject.list(); + } + + private void applyNamedParameterToQuery(Query queryObject, String paramName, Object value) + throws HibernateException { + + if (value instanceof Collection) { + queryObject.setParameterList(paramName, (Collection) value); + } else if (value instanceof Object[]) { + queryObject.setParameterList(paramName, (Object[]) value); + } else { + queryObject.setParameter(paramName, value); + } + } + + public List doFindByNamedQueryAndNamedParam(String queryName, String paramName, Object value) { + + return doFindByNamedQueryAndNamedParam(queryName, new String[] { paramName }, new Object[] { value }); + } + + public List doFindByNamedQueryAndNamedParam(final String queryName, final String[] paramNames, + final Object[] values) { + + if (values != null && (paramNames == null || paramNames.length != values.length)) { + throw new IllegalArgumentException("Length of paramNames array must match length of values array"); + } + Query queryObject = getSession().getNamedQuery(queryName); + if (values != null) { + for (int i = 0; i < values.length; i++) { + applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); + } + } + + return queryObject.list(); + } + + /** + * @see com.edgenius.paradise.dao.DAO#saveObject(java.lang.Object) + */ + public void saveObject(Object o) { + getSession().saveOrUpdate(o); + } + + /** + * @see com.edgenius.paradise.dao.DAO#getObject(java.lang.Class, java.io.Serializable) + */ + public Object getObject(Class clazz, Serializable id) { + Object o = getSession().get(clazz, id); + return o; + } + + /** + * @see com.edgenius.paradise.dao.DAO#getObjects(java.lang.Class) + */ + public List getObjects(Class clazz) { + return loadAll(clazz); + } + + /** + * @see com.edgenius.paradise.dao.DAO#removeObject(java.lang.Class, java.io.Serializable) + */ + public void removeObject(Class clazz, Serializable id) { + getSession().delete(getObject(clazz, id)); + } + + @Override + public void releaseFromCache(Object o) { + getSessionFactory().getCurrentSession().evict(o); + } +} Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190703.sql =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190703.sql (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190703.sql (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -0,0 +1,16 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS=0; +----------------------Put all sql statements below here------------------------- + +-- LDEV-4834 Add Learning Outcomes to QB questions +ALTER TABLE lams_outcome_mapping ADD COLUMN qb_question_id INT, + ADD CONSTRAINT FK_lams_outcome_mapping_4 FOREIGN KEY (qb_question_id) + REFERENCES lams_qb_question (question_id) ON DELETE CASCADE ON UPDATE CASCADE; + +----------------------Put all sql statements above here------------------------- + +-- If there were no errors, commit and restore autocommit to on +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/outcome/OutcomeMapping.java =================================================================== diff -u -r1ee503e3d0e0228ea8a45025fddf15d9623c0377 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/outcome/OutcomeMapping.java (.../OutcomeMapping.java) (revision 1ee503e3d0e0228ea8a45025fddf15d9623c0377) +++ lams_common/src/java/org/lamsfoundation/lams/outcome/OutcomeMapping.java (.../OutcomeMapping.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,77 +1,102 @@ -package org.lamsfoundation.lams.outcome; - -import java.io.Serializable; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -@Entity -@Table(name = "lams_outcome_mapping") -public class OutcomeMapping implements Serializable { - private static final long serialVersionUID = -2195345501533401085L; - - @Id - @Column(name = "mapping_id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long mappingId; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "outcome_id") - private Outcome outcome; - - @Column(name = "lesson_id") - private Long lessonId; - - @Column(name = "tool_content_id") - private Long toolContentId; - - @Column(name = "item_id") - private Long itemId; - - public Long getMappingId() { - return mappingId; - } - - public void setMappingId(Long mappingId) { - this.mappingId = mappingId; - } - - public Outcome getOutcome() { - return outcome; - } - - public void setOutcome(Outcome outcome) { - this.outcome = outcome; - } - - public Long getLessonId() { - return lessonId; - } - - public void setLessonId(Long lessonId) { - this.lessonId = lessonId; - } - - public Long getToolContentId() { - return toolContentId; - } - - public void setToolContentId(Long toolContentId) { - this.toolContentId = toolContentId; - } - - public Long getItemId() { - return itemId; - } - - public void setItemId(Long itemId) { - this.itemId = itemId; - } +package org.lamsfoundation.lams.outcome; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +@Entity +@Table(name = "lams_outcome_mapping") +public class OutcomeMapping implements Serializable { + private static final long serialVersionUID = -2195345501533401085L; + + @Id + @Column(name = "mapping_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long mappingId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "outcome_id") + private Outcome outcome; + + @Column(name = "lesson_id") + private Long lessonId; + + @Column(name = "tool_content_id") + private Long toolContentId; + + @Column(name = "item_id") + private Long itemId; + + @Column(name = "qb_question_id") + private Integer qbQuestionId; + + public Long getMappingId() { + return mappingId; + } + + public void setMappingId(Long mappingId) { + this.mappingId = mappingId; + } + + public Outcome getOutcome() { + return outcome; + } + + public void setOutcome(Outcome outcome) { + this.outcome = outcome; + } + + public Long getLessonId() { + return lessonId; + } + + public void setLessonId(Long lessonId) { + this.lessonId = lessonId; + } + + public Long getToolContentId() { + return toolContentId; + } + + public void setToolContentId(Long toolContentId) { + this.toolContentId = toolContentId; + } + + public Long getItemId() { + return itemId; + } + + public void setItemId(Long itemId) { + this.itemId = itemId; + } + + public Integer getQbQuestionId() { + return qbQuestionId; + } + + public void setQbQuestionId(Integer qbQuestionId) { + this.qbQuestionId = qbQuestionId; + } + + @Override + public boolean equals(Object o) { + OutcomeMapping other = (OutcomeMapping) o; + return new EqualsBuilder().append(mappingId, other.mappingId).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(mappingId).toHashCode(); + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/outcome/dao/IOutcomeDAO.java =================================================================== diff -u -r7ba17e82a4cd08db748536b39aef80a4f1c03027 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/outcome/dao/IOutcomeDAO.java (.../IOutcomeDAO.java) (revision 7ba17e82a4cd08db748536b39aef80a4f1c03027) +++ lams_common/src/java/org/lamsfoundation/lams/outcome/dao/IOutcomeDAO.java (.../IOutcomeDAO.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -35,7 +35,7 @@ List getOutcomesSortedByName(String search); - List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId); + List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId, Integer qbQuestionId); List getScalesSortedByName(); Index: lams_common/src/java/org/lamsfoundation/lams/outcome/dao/hibernate/OutcomeDAO.java =================================================================== diff -u -r7ba17e82a4cd08db748536b39aef80a4f1c03027 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/outcome/dao/hibernate/OutcomeDAO.java (.../OutcomeDAO.java) (revision 7ba17e82a4cd08db748536b39aef80a4f1c03027) +++ lams_common/src/java/org/lamsfoundation/lams/outcome/dao/hibernate/OutcomeDAO.java (.../OutcomeDAO.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -22,9 +22,14 @@ package org.lamsfoundation.lams.outcome.dao.hibernate; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.hibernate.query.Query; @@ -34,15 +39,21 @@ import org.lamsfoundation.lams.outcome.OutcomeResult; import org.lamsfoundation.lams.outcome.OutcomeScale; import org.lamsfoundation.lams.outcome.dao.IOutcomeDAO; +import org.lamsfoundation.lams.qb.dao.IQbDAO; +import org.lamsfoundation.lams.qb.model.QbQuestion; import org.springframework.stereotype.Repository; @Repository public class OutcomeDAO extends LAMSBaseDAO implements IOutcomeDAO { + private IQbDAO qbDAO; + private static final String FIND_OUTCOMES_SORTED_BY_NAME = "FROM Outcome o ? ORDER BY o.name, o.code"; private static final String FIND_SCALES_SORTED_BY_NAME = "FROM OutcomeScale o ORDER BY o.name, o.code"; + private static final String FIND_OUTCOME_MAPPINGS_BY_QUESTION_ID = "FROM OutcomeMapping m WHERE m.qbQuestionId IN :qbQuestionIds"; + /** * Finds all global outcomes and ones for the given organisation */ @@ -71,19 +82,41 @@ } @Override - @SuppressWarnings("unchecked") - public List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId) { + public List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId, + Integer qbQuestionId) { + List result = new ArrayList<>(); Map properties = new HashMap<>(); + + Set qbQuestionIds = new HashSet<>(); if (lessonId != null) { properties.put("lessonId", lessonId); } if (toolContentId != null) { properties.put("toolContentId", toolContentId); + + Collection questions = qbDAO.getQuestionsByToolContentId(toolContentId); + qbQuestionIds.addAll( + questions.stream().collect(Collectors.mapping(QbQuestion::getQuestionId, Collectors.toSet()))); } if (itemId != null) { properties.put("itemId", itemId); } - return findByProperties(OutcomeMapping.class, properties); + // find mappings bound to the given lesson/activity/item + if (!properties.isEmpty()) { + result.addAll(findByProperties(OutcomeMapping.class, properties)); + } + + // find mappings bound to an activity via its QB questions + if (qbQuestionId != null) { + qbQuestionIds.add(qbQuestionId); + } + if (!qbQuestionIds.isEmpty()) { + Query query = getSession() + .createQuery(FIND_OUTCOME_MAPPINGS_BY_QUESTION_ID, OutcomeMapping.class) + .setParameter("qbQuestionIds", qbQuestionIds); + result.addAll(query.getResultList()); + } + return result; } /** @@ -96,7 +129,6 @@ } @Override - @SuppressWarnings("unchecked") public List getOutcomeResults(Integer userId, Long lessonId, Long toolContentId, Long itemId) { Map properties = new HashMap<>(); if (lessonId != null) { @@ -115,12 +147,15 @@ } @Override - @SuppressWarnings("unchecked") public OutcomeResult getOutcomeResult(Integer userId, Long mappingId) { Map properties = new HashMap<>(); properties.put("user.userId", userId); properties.put("mapping.mappingId", mappingId); List result = findByProperties(OutcomeResult.class, properties); return result.isEmpty() ? null : result.get(0); } + + public void setQbDAO(IQbDAO qbDAO) { + this.qbDAO = qbDAO; + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/outcome/service/IOutcomeService.java =================================================================== diff -u -r81e4f5489c431c5e9ff66769f43ebe9b14d36c1a -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/outcome/service/IOutcomeService.java (.../IOutcomeService.java) (revision 81e4f5489c431c5e9ff66769f43ebe9b14d36c1a) +++ lams_common/src/java/org/lamsfoundation/lams/outcome/service/IOutcomeService.java (.../IOutcomeService.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,50 +1,51 @@ -package org.lamsfoundation.lams.outcome.service; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.List; - -import org.lamsfoundation.lams.outcome.Outcome; -import org.lamsfoundation.lams.outcome.OutcomeMapping; -import org.lamsfoundation.lams.outcome.OutcomeResult; -import org.lamsfoundation.lams.outcome.OutcomeScale; -import org.lamsfoundation.lams.util.ExcelCell; -import org.springframework.web.multipart.MultipartFile; - -public interface IOutcomeService { - static final long DEFAULT_SCALE_ID = 1; - - // just a hardcoded, random number - static final String OUTCOME_CONTENT_FOLDER_ID = "outcomeo-utco-meou-tcom-eoutcomeoutc"; - - List getOutcomes(); - - List getOutcomes(String search); - - List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId); - - long countOutcomeMappings(Long outcomeId); - - long countScaleUse(Long scaleId); - - List getScales(); - - List getOutcomeResults(Integer userId, Long lessonId, Long toolContentId, Long itemId); - - OutcomeResult getOutcomeResult(Integer userId, Long mappingId); - - OutcomeScale getDefaultScale(); - - boolean isDefaultScale(Long scaleId); - - void copyOutcomeMappings(Long sourceLessonId, Long sourceToolContentId, Long sourceItemId, Long targetLessonId, - Long targetToolContentId, Long targetItemId); - - LinkedHashMap exportScales(); - - LinkedHashMap exportOutcomes(); - - int importScales(MultipartFile fileItem) throws IOException; - - int importOutcomes(MultipartFile fileItem) throws IOException; +package org.lamsfoundation.lams.outcome.service; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; + +import org.lamsfoundation.lams.outcome.Outcome; +import org.lamsfoundation.lams.outcome.OutcomeMapping; +import org.lamsfoundation.lams.outcome.OutcomeResult; +import org.lamsfoundation.lams.outcome.OutcomeScale; +import org.lamsfoundation.lams.util.ExcelCell; +import org.springframework.web.multipart.MultipartFile; + +public interface IOutcomeService { + static final long DEFAULT_SCALE_ID = 1; + + // just a hardcoded, random number + static final String OUTCOME_CONTENT_FOLDER_ID = "outcomeo-utco-meou-tcom-eoutcomeoutc"; + + List getOutcomes(); + + List getOutcomes(String search); + + List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId, Integer qbQuestionId); + + long countOutcomeMappings(Long outcomeId); + + long countScaleUse(Long scaleId); + + List getScales(); + + List getOutcomeResults(Integer userId, Long lessonId, Long toolContentId, Long itemId); + + OutcomeResult getOutcomeResult(Integer userId, Long mappingId); + + OutcomeScale getDefaultScale(); + + boolean isDefaultScale(Long scaleId); + + void copyOutcomeMappings(Long sourceLessonId, Long sourceToolContentId, Long sourceItemId, + Integer sourceQbQuestionId, Long targetLessonId, Long targetToolContentId, Long targetItemId, + Integer targetQbQuestionId); + + LinkedHashMap exportScales(); + + LinkedHashMap exportOutcomes(); + + int importScales(MultipartFile fileItem) throws IOException; + + int importOutcomes(MultipartFile fileItem) throws IOException; } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/outcome/service/OutcomeService.java =================================================================== diff -u -r81e4f5489c431c5e9ff66769f43ebe9b14d36c1a -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/outcome/service/OutcomeService.java (.../OutcomeService.java) (revision 81e4f5489c431c5e9ff66769f43ebe9b14d36c1a) +++ lams_common/src/java/org/lamsfoundation/lams/outcome/service/OutcomeService.java (.../OutcomeService.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,305 +1,307 @@ -package org.lamsfoundation.lams.outcome.service; - -import java.io.IOException; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpSession; - -import org.apache.log4j.Logger; -import org.apache.poi.hssf.usermodel.HSSFCell; -import org.apache.poi.hssf.usermodel.HSSFRow; -import org.apache.poi.hssf.usermodel.HSSFSheet; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.lamsfoundation.lams.outcome.Outcome; -import org.lamsfoundation.lams.outcome.OutcomeMapping; -import org.lamsfoundation.lams.outcome.OutcomeResult; -import org.lamsfoundation.lams.outcome.OutcomeScale; -import org.lamsfoundation.lams.outcome.OutcomeScaleItem; -import org.lamsfoundation.lams.outcome.dao.IOutcomeDAO; -import org.lamsfoundation.lams.usermanagement.User; -import org.lamsfoundation.lams.usermanagement.dto.UserDTO; -import org.lamsfoundation.lams.util.ExcelCell; -import org.lamsfoundation.lams.util.MessageService; -import org.lamsfoundation.lams.web.session.SessionManager; -import org.lamsfoundation.lams.web.util.AttributeNames; -import org.springframework.web.multipart.MultipartFile; - -public class OutcomeService implements IOutcomeService { - private IOutcomeDAO outcomeDAO; - private MessageService messageService; - - private static Logger log = Logger.getLogger(OutcomeService.class); - - @Override - public List getOutcomes() { - return outcomeDAO.getOutcomesSortedByName(); - } - - @Override - public List getScales() { - return outcomeDAO.getScalesSortedByName(); - } - - @Override - public List getOutcomes(String search) { - return outcomeDAO.getOutcomesSortedByName(search); - } - - @Override - public List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId) { - return outcomeDAO.getOutcomeMappings(lessonId, toolContentId, itemId); - } - - @Override - public long countOutcomeMappings(Long outcomeId) { - Map properties = new HashMap<>(); - properties.put("outcome.outcomeId", outcomeId); - return outcomeDAO.countByProperties(OutcomeMapping.class, properties); - } - - @Override - public long countScaleUse(Long scaleId) { - Map properties = new HashMap<>(); - properties.put("scale.scaleId", scaleId); - return outcomeDAO.countByProperties(Outcome.class, properties); - } - - @Override - public List getOutcomeResults(Integer userId, Long lessonId, Long toolContentId, Long itemId) { - return outcomeDAO.getOutcomeResults(userId, lessonId, toolContentId, itemId); - } - - @Override - public OutcomeResult getOutcomeResult(Integer userId, Long mappingId) { - return outcomeDAO.getOutcomeResult(userId, mappingId); - } - - @Override - public OutcomeScale getDefaultScale() { - return (OutcomeScale) outcomeDAO.find(OutcomeScale.class, DEFAULT_SCALE_ID); - } - - @Override - public boolean isDefaultScale(Long scaleId) { - return scaleId != null && DEFAULT_SCALE_ID == scaleId; - } - - @Override - public void copyOutcomeMappings(Long sourceLessonId, Long sourceToolContentId, Long sourceItemId, - Long targetLessonId, Long targetToolContentId, Long targetItemId) { - List sourceMappings = getOutcomeMappings(sourceLessonId, sourceToolContentId, sourceItemId); - for (OutcomeMapping sourceMapping : sourceMappings) { - OutcomeMapping targetMapping = new OutcomeMapping(); - targetMapping.setOutcome(sourceMapping.getOutcome()); - targetMapping.setLessonId(targetLessonId); - targetMapping.setToolContentId(targetToolContentId); - targetMapping.setItemId(targetItemId); - outcomeDAO.insert(targetMapping); - } - } - - @Override - public LinkedHashMap exportScales() { - LinkedHashMap dataToExport = new LinkedHashMap<>(); - - // The entire data list - List rowList = new LinkedList<>(); - ExcelCell[] row = new ExcelCell[4]; - row[0] = new ExcelCell(messageService.getMessage("outcome.manage.add.name"), true); - row[1] = new ExcelCell(messageService.getMessage("outcome.manage.add.code"), true); - row[2] = new ExcelCell(messageService.getMessage("outcome.manage.add.description"), true); - row[3] = new ExcelCell(messageService.getMessage("scale.manage.add.value"), true); - rowList.add(row); - - List scales = getScales(); - for (OutcomeScale scale : scales) { - row = new ExcelCell[4]; - row[0] = new ExcelCell(scale.getName(), false); - row[1] = new ExcelCell(scale.getCode(), false); - row[2] = new ExcelCell(scale.getDescription(), false); - row[3] = new ExcelCell(scale.getItemString(), false); - rowList.add(row); - } - - ExcelCell[][] data = rowList.toArray(new ExcelCell[][] {}); - dataToExport.put(messageService.getMessage("scale.title"), data); - return dataToExport; - } - - @Override - public LinkedHashMap exportOutcomes() { - LinkedHashMap dataToExport = new LinkedHashMap<>(); - - // The entire data list - List rowList = new LinkedList<>(); - ExcelCell[] row = new ExcelCell[4]; - row[0] = new ExcelCell(messageService.getMessage("outcome.manage.add.name"), true); - row[1] = new ExcelCell(messageService.getMessage("outcome.manage.add.code"), true); - row[2] = new ExcelCell(messageService.getMessage("outcome.manage.add.description"), true); - row[3] = new ExcelCell(messageService.getMessage("outcome.manage.add.scale"), true); - rowList.add(row); - - List outcomes = getOutcomes(); - for (Outcome outcome : outcomes) { - row = new ExcelCell[4]; - row[0] = new ExcelCell(outcome.getName(), false); - row[1] = new ExcelCell(outcome.getCode(), false); - row[2] = new ExcelCell(outcome.getDescription(), false); - row[3] = new ExcelCell(outcome.getScale().getName(), false); - rowList.add(row); - } - - ExcelCell[][] data = rowList.toArray(new ExcelCell[][] {}); - dataToExport.put(messageService.getMessage("index.outcome.manage"), data); - return dataToExport; - } - - @Override - @SuppressWarnings("unchecked") - public int importScales(MultipartFile fileItem) throws IOException { - int counter = 0; - POIFSFileSystem fs = new POIFSFileSystem(fileItem.getInputStream()); - try (HSSFWorkbook wb = new HSSFWorkbook(fs)) { - HSSFSheet sheet = wb.getSheetAt(0); - int startRow = sheet.getFirstRowNum(); - int endRow = sheet.getLastRowNum(); - User user = null; - - // make import work with files with header ("exported on") or without (pure data) - HSSFRow row = sheet.getRow(startRow); - HSSFCell cell = row.getCell(0); - String header = cell.getStringCellValue(); - startRow += "name".equalsIgnoreCase(header) ? 1 : 5; - - for (int i = startRow; i < (endRow + 1); i++) { - row = sheet.getRow(i); - cell = row.getCell(0); - String name = cell.getStringCellValue(); - cell = row.getCell(1); - String code = cell.getStringCellValue(); - List foundScales = outcomeDAO.findByProperty(OutcomeScale.class, "name", name); - foundScales.addAll(outcomeDAO.findByProperty(OutcomeScale.class, "code", code)); - if (!foundScales.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Skipping an outcome scale with existing name \"" + name + "\" or code \"" + code - + "\""); - } - continue; - } - - cell = row.getCell(2); - String description = cell == null ? null : cell.getStringCellValue(); - cell = row.getCell(3); - String itemsString = cell.getStringCellValue(); - - OutcomeScale scale = new OutcomeScale(); - scale.setName(name); - scale.setCode(code); - scale.setDescription(description); - if (user == null) { - UserDTO userDTO = OutcomeService.getUserDTO(); - user = (User) outcomeDAO.find(User.class, userDTO.getUserID()); - } - scale.setCreateBy(user); - scale.setCreateDateTime(new Date()); - outcomeDAO.insert(scale); - - List items = OutcomeScale.parseItems(itemsString); - int value = 0; - for (String itemString : items) { - OutcomeScaleItem item = new OutcomeScaleItem(); - item.setName(itemString); - item.setValue(value++); - item.setScale(scale); - outcomeDAO.insert(item); - } - - counter++; - } - } - return counter; - } - - @Override - @SuppressWarnings("unchecked") - public int importOutcomes(MultipartFile fileItem) throws IOException { - int counter = 0; - POIFSFileSystem fs = new POIFSFileSystem(fileItem.getInputStream()); - try (HSSFWorkbook wb = new HSSFWorkbook(fs)) { - HSSFSheet sheet = wb.getSheetAt(0); - int startRow = sheet.getFirstRowNum(); - int endRow = sheet.getLastRowNum(); - User user = null; - - // make import work with files with header ("exported on") or without (pure data) - HSSFRow row = sheet.getRow(startRow); - HSSFCell cell = row.getCell(0); - String header = cell.getStringCellValue(); - startRow += "name".equalsIgnoreCase(header) ? 1 : 5; - - for (int i = startRow; i < (endRow + 1); i++) { - row = sheet.getRow(i); - cell = row.getCell(0); - String name = cell.getStringCellValue(); - cell = row.getCell(1); - String code = cell.getStringCellValue(); - List foundOutcomes = outcomeDAO.findByProperty(Outcome.class, "name", name); - foundOutcomes.addAll(outcomeDAO.findByProperty(Outcome.class, "code", code)); - if (!foundOutcomes.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Skipping an outcome with existing name \"" + name + "\" or code \"" + code + "\""); - } - continue; - } - cell = row.getCell(3); - String scaleName = cell.getStringCellValue(); - List foundScales = outcomeDAO.findByProperty(OutcomeScale.class, "name", scaleName); - OutcomeScale scale = foundScales.isEmpty() ? null : foundScales.get(0); - if (scale == null) { - if (log.isDebugEnabled()) { - log.debug("Skipping an outcome with missing scale with name: " + scaleName); - } - continue; - } - - cell = row.getCell(2); - String description = cell == null ? null : cell.getStringCellValue(); - - Outcome outcome = new Outcome(); - outcome.setName(name); - outcome.setCode(code); - outcome.setDescription(description); - outcome.setScale(scale); - if (user == null) { - UserDTO userDTO = OutcomeService.getUserDTO(); - user = (User) outcomeDAO.find(User.class, userDTO.getUserID()); - } - outcome.setCreateBy(user); - outcome.setCreateDateTime(new Date()); - outcomeDAO.insert(outcome); - - counter++; - } - } - return counter; - } - - private static UserDTO getUserDTO() { - HttpSession ss = SessionManager.getSession(); - return (UserDTO) ss.getAttribute(AttributeNames.USER); - } - - public void setOutcomeDAO(IOutcomeDAO outcomeDAO) { - this.outcomeDAO = outcomeDAO; - } - - public void setMessageService(MessageService messageService) { - this.messageService = messageService; - } +package org.lamsfoundation.lams.outcome.service; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpSession; + +import org.apache.log4j.Logger; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.lamsfoundation.lams.outcome.Outcome; +import org.lamsfoundation.lams.outcome.OutcomeMapping; +import org.lamsfoundation.lams.outcome.OutcomeResult; +import org.lamsfoundation.lams.outcome.OutcomeScale; +import org.lamsfoundation.lams.outcome.OutcomeScaleItem; +import org.lamsfoundation.lams.outcome.dao.IOutcomeDAO; +import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.util.ExcelCell; +import org.lamsfoundation.lams.util.MessageService; +import org.lamsfoundation.lams.web.session.SessionManager; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.springframework.web.multipart.MultipartFile; + +public class OutcomeService implements IOutcomeService { + private IOutcomeDAO outcomeDAO; + private MessageService messageService; + + private static Logger log = Logger.getLogger(OutcomeService.class); + + @Override + public List getOutcomes() { + return outcomeDAO.getOutcomesSortedByName(); + } + + @Override + public List getScales() { + return outcomeDAO.getScalesSortedByName(); + } + + @Override + public List getOutcomes(String search) { + return outcomeDAO.getOutcomesSortedByName(search); + } + + @Override + public List getOutcomeMappings(Long lessonId, Long toolContentId, Long itemId, + Integer qbQuestionId) { + return outcomeDAO.getOutcomeMappings(lessonId, toolContentId, itemId, qbQuestionId); + } + + @Override + public long countOutcomeMappings(Long outcomeId) { + Map properties = new HashMap<>(); + properties.put("outcome.outcomeId", outcomeId); + return outcomeDAO.countByProperties(OutcomeMapping.class, properties); + } + + @Override + public long countScaleUse(Long scaleId) { + Map properties = new HashMap<>(); + properties.put("scale.scaleId", scaleId); + return outcomeDAO.countByProperties(Outcome.class, properties); + } + + @Override + public List getOutcomeResults(Integer userId, Long lessonId, Long toolContentId, Long itemId) { + return outcomeDAO.getOutcomeResults(userId, lessonId, toolContentId, itemId); + } + + @Override + public OutcomeResult getOutcomeResult(Integer userId, Long mappingId) { + return outcomeDAO.getOutcomeResult(userId, mappingId); + } + + @Override + public OutcomeScale getDefaultScale() { + return outcomeDAO.find(OutcomeScale.class, DEFAULT_SCALE_ID); + } + + @Override + public boolean isDefaultScale(Long scaleId) { + return scaleId != null && DEFAULT_SCALE_ID == scaleId; + } + + @Override + public void copyOutcomeMappings(Long sourceLessonId, Long sourceToolContentId, Long sourceItemId, + Integer sourceQbQuestionId, Long targetLessonId, Long targetToolContentId, Long targetItemId, + Integer targetQbQuestionId) { + List sourceMappings = getOutcomeMappings(sourceLessonId, sourceToolContentId, sourceItemId, + sourceQbQuestionId); + for (OutcomeMapping sourceMapping : sourceMappings) { + OutcomeMapping targetMapping = new OutcomeMapping(); + targetMapping.setOutcome(sourceMapping.getOutcome()); + targetMapping.setLessonId(targetLessonId); + targetMapping.setToolContentId(targetToolContentId); + targetMapping.setItemId(targetItemId); + targetMapping.setQbQuestionId(targetQbQuestionId); + outcomeDAO.insert(targetMapping); + } + } + + @Override + public LinkedHashMap exportScales() { + LinkedHashMap dataToExport = new LinkedHashMap<>(); + + // The entire data list + List rowList = new LinkedList<>(); + ExcelCell[] row = new ExcelCell[4]; + row[0] = new ExcelCell(messageService.getMessage("outcome.manage.add.name"), true); + row[1] = new ExcelCell(messageService.getMessage("outcome.manage.add.code"), true); + row[2] = new ExcelCell(messageService.getMessage("outcome.manage.add.description"), true); + row[3] = new ExcelCell(messageService.getMessage("scale.manage.add.value"), true); + rowList.add(row); + + List scales = getScales(); + for (OutcomeScale scale : scales) { + row = new ExcelCell[4]; + row[0] = new ExcelCell(scale.getName(), false); + row[1] = new ExcelCell(scale.getCode(), false); + row[2] = new ExcelCell(scale.getDescription(), false); + row[3] = new ExcelCell(scale.getItemString(), false); + rowList.add(row); + } + + ExcelCell[][] data = rowList.toArray(new ExcelCell[][] {}); + dataToExport.put(messageService.getMessage("scale.title"), data); + return dataToExport; + } + + @Override + public LinkedHashMap exportOutcomes() { + LinkedHashMap dataToExport = new LinkedHashMap<>(); + + // The entire data list + List rowList = new LinkedList<>(); + ExcelCell[] row = new ExcelCell[4]; + row[0] = new ExcelCell(messageService.getMessage("outcome.manage.add.name"), true); + row[1] = new ExcelCell(messageService.getMessage("outcome.manage.add.code"), true); + row[2] = new ExcelCell(messageService.getMessage("outcome.manage.add.description"), true); + row[3] = new ExcelCell(messageService.getMessage("outcome.manage.add.scale"), true); + rowList.add(row); + + List outcomes = getOutcomes(); + for (Outcome outcome : outcomes) { + row = new ExcelCell[4]; + row[0] = new ExcelCell(outcome.getName(), false); + row[1] = new ExcelCell(outcome.getCode(), false); + row[2] = new ExcelCell(outcome.getDescription(), false); + row[3] = new ExcelCell(outcome.getScale().getName(), false); + rowList.add(row); + } + + ExcelCell[][] data = rowList.toArray(new ExcelCell[][] {}); + dataToExport.put(messageService.getMessage("index.outcome.manage"), data); + return dataToExport; + } + + @Override + public int importScales(MultipartFile fileItem) throws IOException { + int counter = 0; + POIFSFileSystem fs = new POIFSFileSystem(fileItem.getInputStream()); + try (HSSFWorkbook wb = new HSSFWorkbook(fs)) { + HSSFSheet sheet = wb.getSheetAt(0); + int startRow = sheet.getFirstRowNum(); + int endRow = sheet.getLastRowNum(); + User user = null; + + // make import work with files with header ("exported on") or without (pure data) + HSSFRow row = sheet.getRow(startRow); + HSSFCell cell = row.getCell(0); + String header = cell.getStringCellValue(); + startRow += "name".equalsIgnoreCase(header) ? 1 : 5; + + for (int i = startRow; i < (endRow + 1); i++) { + row = sheet.getRow(i); + cell = row.getCell(0); + String name = cell.getStringCellValue(); + cell = row.getCell(1); + String code = cell.getStringCellValue(); + List foundScales = outcomeDAO.findByProperty(OutcomeScale.class, "name", name); + foundScales.addAll(outcomeDAO.findByProperty(OutcomeScale.class, "code", code)); + if (!foundScales.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Skipping an outcome scale with existing name \"" + name + "\" or code \"" + code + + "\""); + } + continue; + } + + cell = row.getCell(2); + String description = cell == null ? null : cell.getStringCellValue(); + cell = row.getCell(3); + String itemsString = cell.getStringCellValue(); + + OutcomeScale scale = new OutcomeScale(); + scale.setName(name); + scale.setCode(code); + scale.setDescription(description); + if (user == null) { + UserDTO userDTO = OutcomeService.getUserDTO(); + user = outcomeDAO.find(User.class, userDTO.getUserID()); + } + scale.setCreateBy(user); + scale.setCreateDateTime(new Date()); + outcomeDAO.insert(scale); + + List items = OutcomeScale.parseItems(itemsString); + int value = 0; + for (String itemString : items) { + OutcomeScaleItem item = new OutcomeScaleItem(); + item.setName(itemString); + item.setValue(value++); + item.setScale(scale); + outcomeDAO.insert(item); + } + + counter++; + } + } + return counter; + } + + @Override + public int importOutcomes(MultipartFile fileItem) throws IOException { + int counter = 0; + POIFSFileSystem fs = new POIFSFileSystem(fileItem.getInputStream()); + try (HSSFWorkbook wb = new HSSFWorkbook(fs)) { + HSSFSheet sheet = wb.getSheetAt(0); + int startRow = sheet.getFirstRowNum(); + int endRow = sheet.getLastRowNum(); + User user = null; + + // make import work with files with header ("exported on") or without (pure data) + HSSFRow row = sheet.getRow(startRow); + HSSFCell cell = row.getCell(0); + String header = cell.getStringCellValue(); + startRow += "name".equalsIgnoreCase(header) ? 1 : 5; + + for (int i = startRow; i < (endRow + 1); i++) { + row = sheet.getRow(i); + cell = row.getCell(0); + String name = cell.getStringCellValue(); + cell = row.getCell(1); + String code = cell.getStringCellValue(); + List foundOutcomes = outcomeDAO.findByProperty(Outcome.class, "name", name); + foundOutcomes.addAll(outcomeDAO.findByProperty(Outcome.class, "code", code)); + if (!foundOutcomes.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Skipping an outcome with existing name \"" + name + "\" or code \"" + code + "\""); + } + continue; + } + cell = row.getCell(3); + String scaleName = cell.getStringCellValue(); + List foundScales = outcomeDAO.findByProperty(OutcomeScale.class, "name", scaleName); + OutcomeScale scale = foundScales.isEmpty() ? null : foundScales.get(0); + if (scale == null) { + if (log.isDebugEnabled()) { + log.debug("Skipping an outcome with missing scale with name: " + scaleName); + } + continue; + } + + cell = row.getCell(2); + String description = cell == null ? null : cell.getStringCellValue(); + + Outcome outcome = new Outcome(); + outcome.setName(name); + outcome.setCode(code); + outcome.setDescription(description); + outcome.setScale(scale); + if (user == null) { + UserDTO userDTO = OutcomeService.getUserDTO(); + user = outcomeDAO.find(User.class, userDTO.getUserID()); + } + outcome.setCreateBy(user); + outcome.setCreateDateTime(new Date()); + outcomeDAO.insert(outcome); + + counter++; + } + } + return counter; + } + + private static UserDTO getUserDTO() { + HttpSession ss = SessionManager.getSession(); + return (UserDTO) ss.getAttribute(AttributeNames.USER); + } + + public void setOutcomeDAO(IOutcomeDAO outcomeDAO) { + this.outcomeDAO = outcomeDAO; + } + + public void setMessageService(MessageService messageService) { + this.messageService = messageService; + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java =================================================================== diff -u -r0b1e74374b821758fdda7835d8d283bf84fa8db0 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java (.../IQbDAO.java) (revision 0b1e74374b821758fdda7835d8d283bf84fa8db0) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java (.../IQbDAO.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,69 +1,71 @@ -package org.lamsfoundation.lams.qb.dao; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.lamsfoundation.lams.dao.IBaseDAO; -import org.lamsfoundation.lams.learningdesign.ToolActivity; -import org.lamsfoundation.lams.qb.model.QbCollection; -import org.lamsfoundation.lams.qb.model.QbQuestion; - -public interface IQbDAO extends IBaseDAO { - - /** - * @param qbQuestionUid - * @return QbQuestion object with the specified uid - */ - QbQuestion getQuestionByUid(Long qbQuestionUid); - - /** - * @param questionId - * @return questions sharing the same questionId - */ - List getQuestionsByQuestionId(Integer questionId); - - // 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); - - int getCountQuestionActivitiesByUid(long qbQuestionUid); - - int getCountQuestionActivitiesByQuestionId(int qbQuestionId); - - List getQuestionVersions(long qbQuestionUid); - - Map getAnswerStatsForQuestion(long qbQuestionUid); - - Map getAnswersForActivity(long activityId, long qbQuestionUid); - - Map getBurningQuestions(long qbQuestionUid); - - List getPagedQuestions(Integer questionType, int page, int size, String sortBy, String sortOrder, - String searchString); - - int getCountQuestions(Integer questionType, String searchString); - - List getCollectionQuestions(long collectionUid); - - List getCollectionQuestions(long collectionUid, Integer offset, Integer limit, String orderBy, - String orderDirection, String search); - - List getQuestionCollectionsByUid(long qbQuestionUid); - - List getQuestionCollectionsByQuestionId(int qbQuestionId); - - int getCountCollectionQuestions(long collectionUid, String search); - - void addCollectionQuestion(long collectionUid, int qbQuestionId); - - void removeCollectionQuestion(long collectionUid, int qbQuestionId); - - Set getCollectionQuestionIdsExcluded(long collectionUid, Collection qbQuestionIds); - +package org.lamsfoundation.lams.qb.dao; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.lamsfoundation.lams.dao.IBaseDAO; +import org.lamsfoundation.lams.learningdesign.ToolActivity; +import org.lamsfoundation.lams.qb.model.QbCollection; +import org.lamsfoundation.lams.qb.model.QbQuestion; + +public interface IQbDAO extends IBaseDAO { + + /** + * @param qbQuestionUid + * @return QbQuestion object with the specified uid + */ + QbQuestion getQuestionByUid(Long qbQuestionUid); + + /** + * @param questionId + * @return questions sharing the same questionId + */ + List getQuestionsByQuestionId(Integer questionId); + + List getQuestionsByToolContentId(long toolContentId); + + // 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); + + int getCountQuestionActivitiesByUid(long qbQuestionUid); + + int getCountQuestionActivitiesByQuestionId(int qbQuestionId); + + List getQuestionVersions(long qbQuestionUid); + + Map getAnswerStatsForQuestion(long qbQuestionUid); + + Map getAnswersForActivity(long activityId, long qbQuestionUid); + + Map getBurningQuestions(long qbQuestionUid); + + List getPagedQuestions(Integer questionType, int page, int size, String sortBy, String sortOrder, + String searchString); + + int getCountQuestions(Integer questionType, String searchString); + + List getCollectionQuestions(long collectionUid); + + List getCollectionQuestions(long collectionUid, Integer offset, Integer limit, String orderBy, + String orderDirection, String search); + + List getQuestionCollectionsByUid(long qbQuestionUid); + + List getQuestionCollectionsByQuestionId(int qbQuestionId); + + int getCountCollectionQuestions(long collectionUid, String search); + + void addCollectionQuestion(long collectionUid, int qbQuestionId); + + void removeCollectionQuestion(long collectionUid, int qbQuestionId); + + Set getCollectionQuestionIdsExcluded(long collectionUid, Collection qbQuestionIds); + } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java =================================================================== diff -u -r0b1e74374b821758fdda7835d8d283bf84fa8db0 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java (.../QbDAO.java) (revision 0b1e74374b821758fdda7835d8d283bf84fa8db0) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java (.../QbDAO.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,366 +1,375 @@ -package org.lamsfoundation.lams.qb.dao.hibernate; - -import java.math.BigInteger; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.Query; - -import org.apache.commons.lang.StringUtils; -import org.hibernate.query.NativeQuery; -import org.hibernate.type.IntegerType; -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.QbCollection; -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_BY_QB_QUESTION = "SELECT COALESCE(a.qb_option_uid, aa.question_option_uid) AS opt, COUNT(a.answer_uid) " - + "FROM lams_qb_tool_question AS tq JOIN lams_qb_tool_answer AS a USING (tool_question_uid) " - + "LEFT JOIN tl_lascrt11_answer_log AS sa ON a.answer_uid = sa.uid " - + "LEFT JOIN tl_lascrt11_session AS ss ON sa.session_id = ss.session_id " - + "LEFT JOIN tl_lascrt11_user AS su ON ss.uid = su.session_uid " - + "LEFT JOIN tl_laasse10_option_answer AS aa ON a.answer_uid = aa.question_result_uid AND aa.answer_boolean = 1 " - + "WHERE tq.qb_question_uid = :qbQuestionUid GROUP BY opt HAVING opt IS NOT NULL"; - - private static final String FIND_ANSWERS_BY_ACTIVITY = "SELECT COALESCE(mcu.que_usr_id, su.user_id, au.user_id), " - + "COALESCE(a.qb_option_uid, aa.question_option_uid) AS opt " - + "FROM lams_learning_activity AS act JOIN lams_qb_tool_question AS tq USING (tool_content_id) " - + "JOIN lams_qb_tool_answer AS a USING (tool_question_uid) " - + "LEFT JOIN tl_lamc11_usr_attempt AS mca ON a.answer_uid = mca.uid " - + "LEFT JOIN tl_lamc11_que_usr AS mcu ON mca.que_usr_id = mcu.uid " - + "LEFT JOIN tl_lascrt11_answer_log AS sa ON a.answer_uid = sa.uid " - + "LEFT JOIN tl_lascrt11_session AS ss ON sa.session_id = ss.session_id " - + "LEFT JOIN tl_lascrt11_user AS su ON ss.uid = su.session_uid " - + "LEFT JOIN tl_laasse10_option_answer AS aa ON a.answer_uid = aa.question_result_uid AND aa.answer_boolean = 1 " - + "LEFT JOIN tl_laasse10_question_result AS aq ON a.answer_uid = aq.uid " - + "LEFT JOIN tl_laasse10_assessment_result AS ar ON aq.result_uid = ar.uid " - + "LEFT JOIN tl_laasse10_user AS au ON ar.user_uid = au.uid " - + "WHERE act.activity_id = :activityId AND tq.qb_question_uid = :qbQuestionUid HAVING opt IS NOT NULL"; - - private static final String FIND_BURNING_QUESTIONS = "SELECT b.question, COUNT(bl.uid) FROM ScratchieBurningQuestion b LEFT OUTER JOIN " - + "BurningQuestionLike AS bl ON bl.burningQuestion = b WHERE b.scratchieItem.qbQuestion.uid = :qbQuestionUid " - + "GROUP BY b.question ORDER BY COUNT(bl.uid) DESC"; - - private static final String FIND_COLLECTION_QUESTIONS = "SELECT q.* FROM lams_qb_collection_question AS cq " - + "JOIN lams_qb_question AS q ON cq.qb_question_id = q.question_id WHERE " - + "q.version = (SELECT MAX(version) FROM lams_qb_question WHERE question_id = q.question_id) " - + "AND cq.collection_uid = :collectionUid"; - - private static final String FIND_QUESTION_COLLECTIONS_BY_UID = "SELECT c.* FROM lams_qb_collection_question AS cq " - + "JOIN lams_qb_collection AS c ON cq.collection_uid = c.uid JOIN lams_qb_question AS q ON cq.qb_question_id = q.question_id " - + "WHERE q.uid = :qbQuestionUid"; - - private static final String FIND_QUESTION_COLLECTIONS_BY_QUESTION_ID = "SELECT c.* FROM lams_qb_collection_question AS cq " - + "JOIN lams_qb_collection AS c ON cq.collection_uid = c.uid WHERE cq.qb_question_id = :qbQuestionId"; - - private static final String ADD_COLLECTION_QUESTION = "INSERT INTO lams_qb_collection_question VALUES (:collectionUid, :qbQuestionId)"; - - private static final String EXISTS_COLLECTION_QUESTION = "SELECT 1 FROM lams_qb_collection_question WHERE collection_uid = :collectionUid " - + "AND qb_question_id = :qbQuestionId"; - - private static final String REMOVE_COLLECTION_QUESTION = "DELETE FROM lams_qb_collection_question WHERE collection_uid = :collectionUid " - + "AND qb_question_id = :qbQuestionId"; - - private static final String FIND_COLLECTION_QUESTIONS_EXCLUDED = "SELECT qb_question_id FROM lams_qb_collection_question " - + "WHERE collection_uid = :collectionUid AND qb_question_id NOT IN :qbQuestionIds"; - - @Override - public QbQuestion getQuestionByUid(Long qbQuestionUid) { - return this.find(QbQuestion.class, qbQuestionUid); - } - - @SuppressWarnings("unchecked") - @Override - public List getQuestionsByQuestionId(Integer questionId) { - final String FIND_QUESTIONS_BY_QUESTION_ID = "FROM " + QbQuestion.class.getName() - + " WHERE questionId = :questionId ORDER BY version DESC"; - - Query q = getSession().createQuery(FIND_QUESTIONS_BY_QUESTION_ID, QbQuestion.class); - q.setParameter("questionId", questionId); - return q.getResultList(); - } - - @Override - public int getMaxQuestionId() { - Object result = this.getSession().createQuery(FIND_MAX_QUESTION_ID).uniqueResult(); - Integer max = (Integer) result; - return max == null ? 1 : max + 1; - } - - @Override - public int getMaxQuestionVersion(Integer qbQuestionId) { - Object result = this.getSession().createQuery(FIND_MAX_VERSION).setParameter("questionId", qbQuestionId) - .uniqueResult(); - 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 - public int getCountQuestionActivitiesByUid(long qbQuestionUid) { - return ((Long) getSession().createQuery(FIND_QUESTION_ACTIVITIES.replace("SELECT a", "SELECT COUNT(a)")) - .setParameter("qbQuestionUid", qbQuestionUid).getSingleResult()).intValue(); - } - - @Override - public int getCountQuestionActivitiesByQuestionId(int qbQuestionId) { - return ((Long) getSession() - .createQuery(FIND_QUESTION_ACTIVITIES.replace("SELECT a", "SELECT COUNT(a)") - .replace("uid = :qbQuestionUid", "questionId = :qbQuestionId")) - .setParameter("qbQuestionId", qbQuestionId).getSingleResult()).intValue(); - } - - @Override - @SuppressWarnings("unchecked") - public List getQuestionVersions(long qbQuestionUid) { - return this.getSession().createQuery(FIND_QUESTION_VERSIONS).setParameter("qbQuestionUid", qbQuestionUid) - .list(); - } - - @Override - @SuppressWarnings("unchecked") - public Map getAnswerStatsForQuestion(long qbQuestionUid) { - List result = this.getSession().createSQLQuery(FIND_ANSWER_STATS_BY_QB_QUESTION) - .setParameter("qbQuestionUid", qbQuestionUid).list(); - Map map = new HashMap<>(result.size()); - for (Object[] answerStat : result) { - map.put(((BigInteger) answerStat[0]).longValue(), ((BigInteger) answerStat[1]).longValue()); - } - return map; - } - - @SuppressWarnings("unchecked") - @Override - public List getPagedQuestions(Integer questionType, int page, int size, String sortBy, String sortOrder, - String searchString) { - //we sort of strip out HTML tags from the search by using REGEXP_REPLACE which skips all the content between < > - final String SELECT_QUESTIONS = "SELECT DISTINCT question.* " + " FROM lams_qb_question question " - + " LEFT OUTER JOIN lams_qb_option qboption " + " ON qboption.qb_question_uid = question.uid " - + " LEFT JOIN ("//help finding questions with the max available version - + " SELECT biggerQuestion.* FROM lams_qb_question biggerQuestion " - + " LEFT OUTER JOIN lams_qb_option qboption1 " - + " ON qboption1.qb_question_uid = biggerQuestion.uid " - + " WHERE biggerQuestion.type = :questionType " - + " AND (REGEXP_REPLACE(biggerQuestion.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" - + " OR biggerQuestion.name LIKE CONCAT('%', :searchString, '%') " - + " OR REGEXP_REPLACE(qboption1.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) " - + ") AS biggerQuestion ON question.question_id = biggerQuestion.question_id AND question.version < biggerQuestion.version " - + " WHERE biggerQuestion.version is NULL " + " AND question.type = :questionType " - + " AND (REGEXP_REPLACE(question.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" - + " OR question.name LIKE CONCAT('%', :searchString, '%') " - + " OR REGEXP_REPLACE(qboption.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) "; - final String ORDER_BY_NAME = "ORDER BY question.name "; - final String ORDER_BY_SMTH_ELSE = "ORDER BY question.question_id "; - - //TODO check the following query with real data. and see maybe it's better than the current (it's unlikely though) [https://stackoverflow.com/a/28090544/10331386 and https://stackoverflow.com/a/612268/10331386] -// SELECT t1.* -// FROM lams_qb_question t1 -// INNER JOIN -// ( -// SELECT `question_id`, MAX(version) AS max_version -// FROM lams_qb_question as t3 -// LEFT OUTER JOIN lams_qb_option qboption -// ON qboption.qb_question_uid = t3.uid -// WHERE REGEXP_REPLACE(qboption.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%') -// GROUP BY `question_id` -// ) t2 -// ON t1.`question_id` = t2.`question_id` AND t1.version = t2.max_version; - - StringBuilder bldr = new StringBuilder(SELECT_QUESTIONS); - if ("smth_else".equalsIgnoreCase(sortBy)) { - bldr.append(ORDER_BY_SMTH_ELSE); - } else { - bldr.append(ORDER_BY_NAME); - } - bldr.append(sortOrder); - - NativeQuery query = getSession().createNativeQuery(bldr.toString()); - query.setParameter("questionType", questionType); - // support for custom search from a toolbar - searchString = searchString == null ? "" : searchString; - query.setParameter("searchString", searchString); - query.setFirstResult(page * size); - query.setMaxResults(size); - query.addEntity(QbQuestion.class); - List queryResults = (List) query.list(); - - return queryResults; - } - - @Override - public int getCountQuestions(Integer questionType, String searchString) { - final String SELECT_QUESTIONS = "SELECT COUNT(DISTINCT question.uid) count " - + " FROM lams_qb_question question " + " LEFT OUTER JOIN lams_qb_option qboption " - + " ON qboption.qb_question_uid = question.uid " + " LEFT JOIN ("//help finding questions with the max available version - + " SELECT biggerQuestion.* FROM lams_qb_question biggerQuestion " - + " LEFT OUTER JOIN lams_qb_option qboption1 " - + " ON qboption1.qb_question_uid = biggerQuestion.uid " - + " WHERE biggerQuestion.type = :questionType " - + " AND (REGEXP_REPLACE(biggerQuestion.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" - + " OR biggerQuestion.name LIKE CONCAT('%', :searchString, '%') " - + " OR REGEXP_REPLACE(qboption1.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) " - + ") AS biggerQuestion ON question.question_id = biggerQuestion.question_id AND question.version < biggerQuestion.version " - + " WHERE biggerQuestion.version is NULL " + " AND question.type = :questionType " - + " AND (REGEXP_REPLACE(question.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" - + " OR question.name LIKE CONCAT('%', :searchString, '%') " - + " OR REGEXP_REPLACE(qboption.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) "; - - Query query = getSession().createNativeQuery(SELECT_QUESTIONS).addScalar("count", IntegerType.INSTANCE); - ; - query.setParameter("questionType", questionType); - // support for custom search from a toolbar - searchString = searchString == null ? "" : searchString; - query.setParameter("searchString", searchString); - int result = (int) query.getSingleResult(); - return result; - } - - @Override - @SuppressWarnings("unchecked") - public Map getAnswersForActivity(long activityId, long qbQuestionUid) { - List result = this.getSession().createSQLQuery(FIND_ANSWERS_BY_ACTIVITY) - .setParameter("activityId", activityId).setParameter("qbQuestionUid", qbQuestionUid).list(); - Map map = new HashMap<>(result.size()); - for (Object[] answerStat : result) { - map.put(((BigInteger) answerStat[0]).intValue(), ((BigInteger) answerStat[1]).longValue()); - } - return map; - } - - @Override - @SuppressWarnings("unchecked") - public Map getBurningQuestions(long qbQuestionUid) { - List result = this.getSession().createQuery(FIND_BURNING_QUESTIONS) - .setParameter("qbQuestionUid", qbQuestionUid).list(); - Map map = new LinkedHashMap<>(result.size()); - for (Object[] burningQuestion : result) { - map.put((String) burningQuestion[0], (Long) burningQuestion[1]); - } - return map; - } - - @Override - public List getCollectionQuestions(long collectionUid) { - return getCollectionQuestions(collectionUid, null, null, null, null, null); - } - - @SuppressWarnings("unchecked") - @Override - public List getQuestionCollectionsByUid(long qbQuestionUid) { - return getSession().createNativeQuery(FIND_QUESTION_COLLECTIONS_BY_UID) - .setParameter("qbQuestionUid", qbQuestionUid).addEntity(QbCollection.class).list(); - } - - @SuppressWarnings("unchecked") - @Override - public List getQuestionCollectionsByQuestionId(int qbQuestionId) { - return getSession().createNativeQuery(FIND_QUESTION_COLLECTIONS_BY_QUESTION_ID) - .setParameter("qbQuestionId", qbQuestionId).addEntity(QbCollection.class).list(); - } - - @SuppressWarnings("unchecked") - @Override - public List getCollectionQuestions(long collectionUid, Integer offset, Integer limit, String orderBy, - String orderDirection, String search) { - Query query = prepareCollectionQuestionsQuery(collectionUid, orderBy, orderDirection, search, false); - if (offset != null) { - query.setFirstResult(offset); - } - if (limit != null) { - query.setMaxResults(limit); - } - return query.getResultList(); - } - - @Override - public int getCountCollectionQuestions(long collectionUid, String search) { - Query query = prepareCollectionQuestionsQuery(collectionUid, null, null, search, true); - return ((BigInteger) query.getSingleResult()).intValue(); - } - - @Override - public void addCollectionQuestion(long collectionUid, int qbQuestionId) { - if (!questionInCollectionExists(collectionUid, qbQuestionId)) { - getSession().createNativeQuery(ADD_COLLECTION_QUESTION).setParameter("collectionUid", collectionUid) - .setParameter("qbQuestionId", qbQuestionId).executeUpdate(); - } - } - - @Override - public void removeCollectionQuestion(long collectionUid, int qbQuestionId) { - getSession().createNativeQuery(REMOVE_COLLECTION_QUESTION).setParameter("collectionUid", collectionUid) - .setParameter("qbQuestionId", qbQuestionId).executeUpdate(); - } - - @SuppressWarnings("unchecked") - @Override - public Set getCollectionQuestionIdsExcluded(long collectionUid, Collection qbQuestionIds) { - List queryResult = getSession().createNativeQuery(FIND_COLLECTION_QUESTIONS_EXCLUDED) - .setParameter("collectionUid", collectionUid).setParameterList("qbQuestionIds", qbQuestionIds) - .getResultList(); - Set result = new HashSet<>(); - for (BigInteger questionId : queryResult) { - result.add(questionId.intValue()); - } - return result; - } - - private boolean questionInCollectionExists(long collectionUid, int qbQuestionId) { - return !getSession().createNativeQuery(EXISTS_COLLECTION_QUESTION).setParameter("collectionUid", collectionUid) - .setParameter("qbQuestionId", qbQuestionId).getResultList().isEmpty(); - } - - private Query prepareCollectionQuestionsQuery(long collectionUid, String orderBy, String orderDirection, - String search, boolean isCount) { - StringBuilder queryBuilder = new StringBuilder(FIND_COLLECTION_QUESTIONS); - - if (StringUtils.isNotBlank(search)) { - queryBuilder.append(" AND (q.name LIKE :search OR q.description LIKE :search)"); - } - - if (!isCount && StringUtils.isNotBlank(orderBy)) { - queryBuilder.append(" ORDER BY ").append(orderBy); - if (StringUtils.isNotBlank(orderDirection)) { - queryBuilder.append(" ").append(orderDirection); - } - } - - String queryText = queryBuilder.toString(); - if (isCount) { - queryText = queryText.replace("q.*", "COUNT(*)"); - } - - Query query = isCount ? getSession().createNativeQuery(queryText) - : getSession().createNativeQuery(queryText, QbQuestion.class); - query.setParameter("collectionUid", collectionUid); - if (StringUtils.isNotBlank(search)) { - query.setParameter("search", "%" + search.trim() + "%"); - } - - return query; - } +package org.lamsfoundation.lams.qb.dao.hibernate; + +import java.math.BigInteger; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.Query; + +import org.apache.commons.lang.StringUtils; +import org.hibernate.query.NativeQuery; +import org.hibernate.type.IntegerType; +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.QbCollection; +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_BY_QB_QUESTION = "SELECT COALESCE(a.qb_option_uid, aa.question_option_uid) AS opt, COUNT(a.answer_uid) " + + "FROM lams_qb_tool_question AS tq JOIN lams_qb_tool_answer AS a USING (tool_question_uid) " + + "LEFT JOIN tl_lascrt11_answer_log AS sa ON a.answer_uid = sa.uid " + + "LEFT JOIN tl_lascrt11_session AS ss ON sa.session_id = ss.session_id " + + "LEFT JOIN tl_lascrt11_user AS su ON ss.uid = su.session_uid " + + "LEFT JOIN tl_laasse10_option_answer AS aa ON a.answer_uid = aa.question_result_uid AND aa.answer_boolean = 1 " + + "WHERE tq.qb_question_uid = :qbQuestionUid GROUP BY opt HAVING opt IS NOT NULL"; + + private static final String FIND_ANSWERS_BY_ACTIVITY = "SELECT COALESCE(mcu.que_usr_id, su.user_id, au.user_id), " + + "COALESCE(a.qb_option_uid, aa.question_option_uid) AS opt " + + "FROM lams_learning_activity AS act JOIN lams_qb_tool_question AS tq USING (tool_content_id) " + + "JOIN lams_qb_tool_answer AS a USING (tool_question_uid) " + + "LEFT JOIN tl_lamc11_usr_attempt AS mca ON a.answer_uid = mca.uid " + + "LEFT JOIN tl_lamc11_que_usr AS mcu ON mca.que_usr_id = mcu.uid " + + "LEFT JOIN tl_lascrt11_answer_log AS sa ON a.answer_uid = sa.uid " + + "LEFT JOIN tl_lascrt11_session AS ss ON sa.session_id = ss.session_id " + + "LEFT JOIN tl_lascrt11_user AS su ON ss.uid = su.session_uid " + + "LEFT JOIN tl_laasse10_option_answer AS aa ON a.answer_uid = aa.question_result_uid AND aa.answer_boolean = 1 " + + "LEFT JOIN tl_laasse10_question_result AS aq ON a.answer_uid = aq.uid " + + "LEFT JOIN tl_laasse10_assessment_result AS ar ON aq.result_uid = ar.uid " + + "LEFT JOIN tl_laasse10_user AS au ON ar.user_uid = au.uid " + + "WHERE act.activity_id = :activityId AND tq.qb_question_uid = :qbQuestionUid HAVING opt IS NOT NULL"; + + private static final String FIND_BURNING_QUESTIONS = "SELECT b.question, COUNT(bl.uid) FROM ScratchieBurningQuestion b LEFT OUTER JOIN " + + "BurningQuestionLike AS bl ON bl.burningQuestion = b WHERE b.scratchieItem.qbQuestion.uid = :qbQuestionUid " + + "GROUP BY b.question ORDER BY COUNT(bl.uid) DESC"; + + private static final String FIND_COLLECTION_QUESTIONS = "SELECT q.* FROM lams_qb_collection_question AS cq " + + "JOIN lams_qb_question AS q ON cq.qb_question_id = q.question_id WHERE " + + "q.version = (SELECT MAX(version) FROM lams_qb_question WHERE question_id = q.question_id) " + + "AND cq.collection_uid = :collectionUid"; + + private static final String FIND_QUESTION_COLLECTIONS_BY_UID = "SELECT c.* FROM lams_qb_collection_question AS cq " + + "JOIN lams_qb_collection AS c ON cq.collection_uid = c.uid JOIN lams_qb_question AS q ON cq.qb_question_id = q.question_id " + + "WHERE q.uid = :qbQuestionUid"; + + private static final String FIND_QUESTION_COLLECTIONS_BY_QUESTION_ID = "SELECT c.* FROM lams_qb_collection_question AS cq " + + "JOIN lams_qb_collection AS c ON cq.collection_uid = c.uid WHERE cq.qb_question_id = :qbQuestionId"; + + private static final String ADD_COLLECTION_QUESTION = "INSERT INTO lams_qb_collection_question VALUES (:collectionUid, :qbQuestionId)"; + + private static final String EXISTS_COLLECTION_QUESTION = "SELECT 1 FROM lams_qb_collection_question WHERE collection_uid = :collectionUid " + + "AND qb_question_id = :qbQuestionId"; + + private static final String REMOVE_COLLECTION_QUESTION = "DELETE FROM lams_qb_collection_question WHERE collection_uid = :collectionUid " + + "AND qb_question_id = :qbQuestionId"; + + private static final String FIND_COLLECTION_QUESTIONS_EXCLUDED = "SELECT qb_question_id FROM lams_qb_collection_question " + + "WHERE collection_uid = :collectionUid AND qb_question_id NOT IN :qbQuestionIds"; + + private static final String FIND_QUESTIONS_BY_TOOL_CONTENT_ID = "SELECT tq.qbQuestion FROM QbToolQuestion AS tq " + + "WHERE tq.toolContentId = :toolContentId"; + + @Override + public QbQuestion getQuestionByUid(Long qbQuestionUid) { + return this.find(QbQuestion.class, qbQuestionUid); + } + + @SuppressWarnings("unchecked") + @Override + public List getQuestionsByQuestionId(Integer questionId) { + final String FIND_QUESTIONS_BY_QUESTION_ID = "FROM " + QbQuestion.class.getName() + + " WHERE questionId = :questionId ORDER BY version DESC"; + + Query q = getSession().createQuery(FIND_QUESTIONS_BY_QUESTION_ID, QbQuestion.class); + q.setParameter("questionId", questionId); + return q.getResultList(); + } + + @Override + public List getQuestionsByToolContentId(long toolContentId) { + return getSession().createQuery(FIND_QUESTIONS_BY_TOOL_CONTENT_ID, QbQuestion.class) + .setParameter("toolContentId", toolContentId).getResultList(); + } + + @Override + public int getMaxQuestionId() { + Object result = this.getSession().createQuery(FIND_MAX_QUESTION_ID).uniqueResult(); + Integer max = (Integer) result; + return max == null ? 1 : max + 1; + } + + @Override + public int getMaxQuestionVersion(Integer qbQuestionId) { + Object result = this.getSession().createQuery(FIND_MAX_VERSION).setParameter("questionId", qbQuestionId) + .uniqueResult(); + 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 + public int getCountQuestionActivitiesByUid(long qbQuestionUid) { + return ((Long) getSession().createQuery(FIND_QUESTION_ACTIVITIES.replace("SELECT a", "SELECT COUNT(a)")) + .setParameter("qbQuestionUid", qbQuestionUid).getSingleResult()).intValue(); + } + + @Override + public int getCountQuestionActivitiesByQuestionId(int qbQuestionId) { + return ((Long) getSession() + .createQuery(FIND_QUESTION_ACTIVITIES.replace("SELECT a", "SELECT COUNT(a)") + .replace("uid = :qbQuestionUid", "questionId = :qbQuestionId")) + .setParameter("qbQuestionId", qbQuestionId).getSingleResult()).intValue(); + } + + @Override + @SuppressWarnings("unchecked") + public List getQuestionVersions(long qbQuestionUid) { + return this.getSession().createQuery(FIND_QUESTION_VERSIONS).setParameter("qbQuestionUid", qbQuestionUid) + .list(); + } + + @Override + @SuppressWarnings("unchecked") + public Map getAnswerStatsForQuestion(long qbQuestionUid) { + List result = this.getSession().createSQLQuery(FIND_ANSWER_STATS_BY_QB_QUESTION) + .setParameter("qbQuestionUid", qbQuestionUid).list(); + Map map = new HashMap<>(result.size()); + for (Object[] answerStat : result) { + map.put(((BigInteger) answerStat[0]).longValue(), ((BigInteger) answerStat[1]).longValue()); + } + return map; + } + + @SuppressWarnings("unchecked") + @Override + public List getPagedQuestions(Integer questionType, int page, int size, String sortBy, String sortOrder, + String searchString) { + //we sort of strip out HTML tags from the search by using REGEXP_REPLACE which skips all the content between < > + final String SELECT_QUESTIONS = "SELECT DISTINCT question.* " + " FROM lams_qb_question question " + + " LEFT OUTER JOIN lams_qb_option qboption " + " ON qboption.qb_question_uid = question.uid " + + " LEFT JOIN ("//help finding questions with the max available version + + " SELECT biggerQuestion.* FROM lams_qb_question biggerQuestion " + + " LEFT OUTER JOIN lams_qb_option qboption1 " + + " ON qboption1.qb_question_uid = biggerQuestion.uid " + + " WHERE biggerQuestion.type = :questionType " + + " AND (REGEXP_REPLACE(biggerQuestion.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" + + " OR biggerQuestion.name LIKE CONCAT('%', :searchString, '%') " + + " OR REGEXP_REPLACE(qboption1.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) " + + ") AS biggerQuestion ON question.question_id = biggerQuestion.question_id AND question.version < biggerQuestion.version " + + " WHERE biggerQuestion.version is NULL " + " AND question.type = :questionType " + + " AND (REGEXP_REPLACE(question.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" + + " OR question.name LIKE CONCAT('%', :searchString, '%') " + + " OR REGEXP_REPLACE(qboption.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) "; + final String ORDER_BY_NAME = "ORDER BY question.name "; + final String ORDER_BY_SMTH_ELSE = "ORDER BY question.question_id "; + + //TODO check the following query with real data. and see maybe it's better than the current (it's unlikely though) [https://stackoverflow.com/a/28090544/10331386 and https://stackoverflow.com/a/612268/10331386] +// SELECT t1.* +// FROM lams_qb_question t1 +// INNER JOIN +// ( +// SELECT `question_id`, MAX(version) AS max_version +// FROM lams_qb_question as t3 +// LEFT OUTER JOIN lams_qb_option qboption +// ON qboption.qb_question_uid = t3.uid +// WHERE REGEXP_REPLACE(qboption.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%') +// GROUP BY `question_id` +// ) t2 +// ON t1.`question_id` = t2.`question_id` AND t1.version = t2.max_version; + + StringBuilder bldr = new StringBuilder(SELECT_QUESTIONS); + if ("smth_else".equalsIgnoreCase(sortBy)) { + bldr.append(ORDER_BY_SMTH_ELSE); + } else { + bldr.append(ORDER_BY_NAME); + } + bldr.append(sortOrder); + + NativeQuery query = getSession().createNativeQuery(bldr.toString()); + query.setParameter("questionType", questionType); + // support for custom search from a toolbar + searchString = searchString == null ? "" : searchString; + query.setParameter("searchString", searchString); + query.setFirstResult(page * size); + query.setMaxResults(size); + query.addEntity(QbQuestion.class); + List queryResults = (List) query.list(); + + return queryResults; + } + + @Override + public int getCountQuestions(Integer questionType, String searchString) { + final String SELECT_QUESTIONS = "SELECT COUNT(DISTINCT question.uid) count " + + " FROM lams_qb_question question " + " LEFT OUTER JOIN lams_qb_option qboption " + + " ON qboption.qb_question_uid = question.uid " + " LEFT JOIN ("//help finding questions with the max available version + + " SELECT biggerQuestion.* FROM lams_qb_question biggerQuestion " + + " LEFT OUTER JOIN lams_qb_option qboption1 " + + " ON qboption1.qb_question_uid = biggerQuestion.uid " + + " WHERE biggerQuestion.type = :questionType " + + " AND (REGEXP_REPLACE(biggerQuestion.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" + + " OR biggerQuestion.name LIKE CONCAT('%', :searchString, '%') " + + " OR REGEXP_REPLACE(qboption1.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) " + + ") AS biggerQuestion ON question.question_id = biggerQuestion.question_id AND question.version < biggerQuestion.version " + + " WHERE biggerQuestion.version is NULL " + " AND question.type = :questionType " + + " AND (REGEXP_REPLACE(question.description, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')" + + " OR question.name LIKE CONCAT('%', :searchString, '%') " + + " OR REGEXP_REPLACE(qboption.name, '<[^>]*>+', '') LIKE CONCAT('%', :searchString, '%')) "; + + Query query = getSession().createNativeQuery(SELECT_QUESTIONS).addScalar("count", IntegerType.INSTANCE); + ; + query.setParameter("questionType", questionType); + // support for custom search from a toolbar + searchString = searchString == null ? "" : searchString; + query.setParameter("searchString", searchString); + int result = (int) query.getSingleResult(); + return result; + } + + @Override + @SuppressWarnings("unchecked") + public Map getAnswersForActivity(long activityId, long qbQuestionUid) { + List result = this.getSession().createSQLQuery(FIND_ANSWERS_BY_ACTIVITY) + .setParameter("activityId", activityId).setParameter("qbQuestionUid", qbQuestionUid).list(); + Map map = new HashMap<>(result.size()); + for (Object[] answerStat : result) { + map.put(((BigInteger) answerStat[0]).intValue(), ((BigInteger) answerStat[1]).longValue()); + } + return map; + } + + @Override + @SuppressWarnings("unchecked") + public Map getBurningQuestions(long qbQuestionUid) { + List result = this.getSession().createQuery(FIND_BURNING_QUESTIONS) + .setParameter("qbQuestionUid", qbQuestionUid).list(); + Map map = new LinkedHashMap<>(result.size()); + for (Object[] burningQuestion : result) { + map.put((String) burningQuestion[0], (Long) burningQuestion[1]); + } + return map; + } + + @Override + public List getCollectionQuestions(long collectionUid) { + return getCollectionQuestions(collectionUid, null, null, null, null, null); + } + + @SuppressWarnings("unchecked") + @Override + public List getQuestionCollectionsByUid(long qbQuestionUid) { + return getSession().createNativeQuery(FIND_QUESTION_COLLECTIONS_BY_UID) + .setParameter("qbQuestionUid", qbQuestionUid).addEntity(QbCollection.class).list(); + } + + @SuppressWarnings("unchecked") + @Override + public List getQuestionCollectionsByQuestionId(int qbQuestionId) { + return getSession().createNativeQuery(FIND_QUESTION_COLLECTIONS_BY_QUESTION_ID) + .setParameter("qbQuestionId", qbQuestionId).addEntity(QbCollection.class).list(); + } + + @SuppressWarnings("unchecked") + @Override + public List getCollectionQuestions(long collectionUid, Integer offset, Integer limit, String orderBy, + String orderDirection, String search) { + Query query = prepareCollectionQuestionsQuery(collectionUid, orderBy, orderDirection, search, false); + if (offset != null) { + query.setFirstResult(offset); + } + if (limit != null) { + query.setMaxResults(limit); + } + return query.getResultList(); + } + + @Override + public int getCountCollectionQuestions(long collectionUid, String search) { + Query query = prepareCollectionQuestionsQuery(collectionUid, null, null, search, true); + return ((BigInteger) query.getSingleResult()).intValue(); + } + + @Override + public void addCollectionQuestion(long collectionUid, int qbQuestionId) { + if (!questionInCollectionExists(collectionUid, qbQuestionId)) { + getSession().createNativeQuery(ADD_COLLECTION_QUESTION).setParameter("collectionUid", collectionUid) + .setParameter("qbQuestionId", qbQuestionId).executeUpdate(); + } + } + + @Override + public void removeCollectionQuestion(long collectionUid, int qbQuestionId) { + getSession().createNativeQuery(REMOVE_COLLECTION_QUESTION).setParameter("collectionUid", collectionUid) + .setParameter("qbQuestionId", qbQuestionId).executeUpdate(); + } + + @SuppressWarnings("unchecked") + @Override + public Set getCollectionQuestionIdsExcluded(long collectionUid, Collection qbQuestionIds) { + List queryResult = getSession().createNativeQuery(FIND_COLLECTION_QUESTIONS_EXCLUDED) + .setParameter("collectionUid", collectionUid).setParameterList("qbQuestionIds", qbQuestionIds) + .getResultList(); + Set result = new HashSet<>(); + for (BigInteger questionId : queryResult) { + result.add(questionId.intValue()); + } + return result; + } + + private boolean questionInCollectionExists(long collectionUid, int qbQuestionId) { + return !getSession().createNativeQuery(EXISTS_COLLECTION_QUESTION).setParameter("collectionUid", collectionUid) + .setParameter("qbQuestionId", qbQuestionId).getResultList().isEmpty(); + } + + private Query prepareCollectionQuestionsQuery(long collectionUid, String orderBy, String orderDirection, + String search, boolean isCount) { + StringBuilder queryBuilder = new StringBuilder(FIND_COLLECTION_QUESTIONS); + + if (StringUtils.isNotBlank(search)) { + queryBuilder.append(" AND (q.name LIKE :search OR q.description LIKE :search)"); + } + + if (!isCount && StringUtils.isNotBlank(orderBy)) { + queryBuilder.append(" ORDER BY ").append(orderBy); + if (StringUtils.isNotBlank(orderDirection)) { + queryBuilder.append(" ").append(orderDirection); + } + } + + String queryText = queryBuilder.toString(); + if (isCount) { + queryText = queryText.replace("q.*", "COUNT(*)"); + } + + Query query = isCount ? getSession().createNativeQuery(queryText) + : getSession().createNativeQuery(queryText, QbQuestion.class); + query.setParameter("collectionUid", collectionUid); + if (StringUtils.isNotBlank(search)) { + query.setParameter("search", "%" + search.trim() + "%"); + } + + return query; + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/model/QbQuestion.java =================================================================== diff -u -re6b461918b2b8c7e791407b868c5f5b1991c7744 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_common/src/java/org/lamsfoundation/lams/qb/model/QbQuestion.java (.../QbQuestion.java) (revision e6b461918b2b8c7e791407b868c5f5b1991c7744) +++ lams_common/src/java/org/lamsfoundation/lams/qb/model/QbQuestion.java (.../QbQuestion.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -1,416 +1,425 @@ -package org.lamsfoundation.lams.qb.model; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.Table; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.EqualsBuilder; -import org.apache.commons.lang.builder.HashCodeBuilder; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; - -/** - * A question in Question Bank. - * - * @author Marcin Cieslak - */ -@Entity -@Table(name = "lams_qb_question") -public class QbQuestion implements Serializable, Cloneable { - private static final long serialVersionUID = -6287273838239262151L; - - // questions can be of different type - // not all tools can produce/consume all question types - public static final int TYPE_MULTIPLE_CHOICE = 1; - public static final int TYPE_MATCHING_PAIRS = 2; - public static final int TYPE_SHORT_ANSWER = 3; - public static final int TYPE_NUMERICAL = 4; - public static final int TYPE_TRUE_FALSE = 5; - public static final int TYPE_ESSAY = 6; - public static final int TYPE_ORDERING = 7; - public static final int TYPE_MARK_HEDGING = 8; - - // primary key - // another candidate is questionId + version, but single uid can be searched faster - @Id - @Column - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long uid; - - // one of question types - @Column - private Integer type; - - // "tracking ID" for a question - // multiple versions can share the same question ID so their stats are aggregated - @Column(name = "question_id") - private Integer questionId; - - // the same question can have multiple versions - @Column - private Integer version = 1; - - @Column(name = "create_date") - private Date createDate = new Date(); - - // text of the question - @Column - private String name; - - @Column - private String description; - - @Column(name = "max_mark") - private Integer maxMark; - - @Column - private String feedback; - - @Column(name = "penalty_factor") - private float penaltyFactor; - - @Column(name = "answer_required") - private boolean answerRequired; - - @Column(name = "multiple_answers_allowed") - private boolean multipleAnswersAllowed; - - @Column(name = "incorrect_answer_nullifies_mark") - private boolean incorrectAnswerNullifiesMark; - - @Column(name = "feedback_on_correct") - private String feedbackOnCorrect; - - @Column(name = "feedback_on_partially_correct") - private String feedbackOnPartiallyCorrect; - - @Column(name = "feedback_on_incorrect") - private String feedbackOnIncorrect; - - // only one of shuffle and prefixAnswersWithLetters should be on. Both may be off - @Column - private boolean shuffle; - - @Column(name = "prefix_answers_with_letters") - private boolean prefixAnswersWithLetters; - - @Column(name = "case_sensitive") - private boolean caseSensitive; - - @Column(name = "correct_answer") - private boolean correctAnswer; - - @Column(name = "allow_rich_editor") - private boolean allowRichEditor; - - // only for essay type of question - @Column(name = "max_words_limit") - private int maxWordsLimit; - - // only for essay type of question - @Column(name = "min_words_limit") - private int minWordsLimit; - - // only for hedging maxMark type of question - @Column(name = "hedging_justification_enabled") - private boolean hedgingJustificationEnabled; - - @OneToMany(mappedBy = "qbQuestion", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) - @Fetch(value = FetchMode.SUBSELECT) - private List qbOptions = new ArrayList<>(); - - @OneToMany(mappedBy = "qbQuestion", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) - @Fetch(value = FetchMode.SUBSELECT) - private List units = new ArrayList<>(); - - // compares if current question data and the other one (probably modified with new data) are the same - // it detects if question is the same or should another question/version be created - public boolean isModified(QbQuestion modifiedQuestion) { - return !equals(modifiedQuestion); - } - - // checks if important parts of another question are the same as current question's - @Override - public boolean equals(Object o) { - QbQuestion other = (QbQuestion) o; - // options are also checked if they are equal - return new EqualsBuilder().append(name, other.name).append(description, other.description) - .append(feedback, other.feedback).append(maxMark, other.maxMark) - .append(qbOptions.toArray(), other.getQbOptions().toArray()) - .append(units.toArray(), other.getUnits().toArray()).isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder().append(name).append(description).append(feedback).append(maxMark).toHashCode(); - } - - @Override - public QbQuestion clone() { - QbQuestion clone = null; - try { - clone = (QbQuestion) super.clone(); - } catch (CloneNotSupportedException e) { - // it should never happen - e.printStackTrace(); - } - // make a deep copy of options - List optionsClone = new ArrayList<>(qbOptions.size()); - clone.setQbOptions(optionsClone); - for (QbOption option : qbOptions) { - QbOption optionClone = option.clone(); - optionClone.setQbQuestion(clone); - optionsClone.add(optionClone); - } - // make a deep copy of units - List unitsClone = new ArrayList<>(units.size()); - clone.setUnits(unitsClone); - for (QbQuestionUnit unit : units) { - QbQuestionUnit unitClone = unit.clone(); - unitClone.setQbQuestion(clone); - unitsClone.add(unitClone); - } - return clone; - } - - public void clearID() { - this.uid = null; - for (QbOption option : qbOptions) { - option.uid = null; - } - for (QbQuestionUnit unit : units) { - unit.uid = null; - } - } - - public Long getUid() { - return uid; - } - - public Integer getType() { - return type; - } - - public void setType(Integer type) { - this.type = type; - } - - public Integer getQuestionId() { - return questionId; - } - - public void setQuestionId(Integer questionId) { - this.questionId = questionId; - } - - public Integer getVersion() { - return version; - } - - public void setVersion(Integer version) { - this.version = version; - } - - public Date getCreateDate() { - return createDate; - } - - public void setCreateDate(Date createDate) { - this.createDate = createDate; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = StringUtils.isBlank(name) ? null : name.trim(); - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Integer getMaxMark() { - return maxMark; - } - - public void setMaxMark(Integer maxMark) { - this.maxMark = maxMark; - } - - public String getFeedback() { - return feedback; - } - - public void setFeedback(String feedback) { - this.feedback = StringUtils.isBlank(feedback) ? null : feedback.trim(); - } - - - public float getPenaltyFactor() { - return penaltyFactor; - } - - public void setPenaltyFactor(float penaltyFactor) { - this.penaltyFactor = penaltyFactor; - } - - public boolean isAnswerRequired() { - return answerRequired; - } - - public void setAnswerRequired(boolean answerRequired) { - this.answerRequired = answerRequired; - } - - public boolean isMultipleAnswersAllowed() { - return multipleAnswersAllowed; - } - - public void setMultipleAnswersAllowed(boolean multipleAnswersAllowed) { - this.multipleAnswersAllowed = multipleAnswersAllowed; - } - - public boolean isIncorrectAnswerNullifiesMark() { - return incorrectAnswerNullifiesMark; - } - - public void setIncorrectAnswerNullifiesMark(boolean incorrectAnswerNullifiesMark) { - this.incorrectAnswerNullifiesMark = incorrectAnswerNullifiesMark; - } - - public String getFeedbackOnCorrect() { - return feedbackOnCorrect; - } - - public void setFeedbackOnCorrect(String feedbackOnCorrect) { - this.feedbackOnCorrect = feedbackOnCorrect; - } - - public String getFeedbackOnPartiallyCorrect() { - return feedbackOnPartiallyCorrect; - } - - public void setFeedbackOnPartiallyCorrect(String feedbackOnPartiallyCorrect) { - this.feedbackOnPartiallyCorrect = feedbackOnPartiallyCorrect; - } - - public String getFeedbackOnIncorrect() { - return feedbackOnIncorrect; - } - - public void setFeedbackOnIncorrect(String feedbackOnIncorrect) { - this.feedbackOnIncorrect = feedbackOnIncorrect; - } - - public boolean isShuffle() { - return shuffle; - } - - public void setShuffle(boolean shuffle) { - this.shuffle = shuffle; - } - - public boolean isCaseSensitive() { - return caseSensitive; - } - - public void setCaseSensitive(boolean caseSensitive) { - this.caseSensitive = caseSensitive; - } - - public boolean getCorrectAnswer() { - return correctAnswer; - } - - public void setCorrectAnswer(boolean correctAnswer) { - this.correctAnswer = correctAnswer; - } - - public boolean isAllowRichEditor() { - return allowRichEditor; - } - - public void setAllowRichEditor(boolean allowRichEditor) { - this.allowRichEditor = allowRichEditor; - } - - /** - * maxWordsLimit set in author. Used only for essay type of questions - */ - public int getMaxWordsLimit() { - return maxWordsLimit; - } - - /** - * @param maxWordsLimit - * set in author. Used only for essay type of questions - */ - public void setMaxWordsLimit(int maxWordsLimit) { - this.maxWordsLimit = maxWordsLimit; - } - - /** - * minWordsLimit set in author. Used only for essay type of questions - */ - public int getMinWordsLimit() { - return minWordsLimit; - } - - /** - * @param minWordsLimit - * set in author. Used only for essay type of questions - */ - public void setMinWordsLimit(int minWordsLimit) { - this.minWordsLimit = minWordsLimit; - } - - public boolean isHedgingJustificationEnabled() { - return hedgingJustificationEnabled; - } - - public void setHedgingJustificationEnabled(boolean hedgingJustificationEnabled) { - this.hedgingJustificationEnabled = hedgingJustificationEnabled; - } - - public boolean isPrefixAnswersWithLetters() { - return prefixAnswersWithLetters; - } - - public void setPrefixAnswersWithLetters(boolean prefixAnswersWithLetters) { - this.prefixAnswersWithLetters = prefixAnswersWithLetters; - } - - public List getQbOptions() { - return qbOptions; - } - - public void setQbOptions(List options) { - this.qbOptions = options; - } - - public List getUnits() { - return units; - } - - public void setUnits(List units) { - this.units = units; - } +package org.lamsfoundation.lams.qb.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.lamsfoundation.lams.outcome.Outcome; + +/** + * A question in Question Bank. + * + * @author Marcin Cieslak + */ +@Entity +@Table(name = "lams_qb_question") +public class QbQuestion implements Serializable, Cloneable { + private static final long serialVersionUID = -6287273838239262151L; + + // questions can be of different type + // not all tools can produce/consume all question types + public static final int TYPE_MULTIPLE_CHOICE = 1; + public static final int TYPE_MATCHING_PAIRS = 2; + public static final int TYPE_SHORT_ANSWER = 3; + public static final int TYPE_NUMERICAL = 4; + public static final int TYPE_TRUE_FALSE = 5; + public static final int TYPE_ESSAY = 6; + public static final int TYPE_ORDERING = 7; + public static final int TYPE_MARK_HEDGING = 8; + + // primary key + // another candidate is questionId + version, but single uid can be searched faster + @Id + @Column + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long uid; + + // one of question types + @Column + private Integer type; + + // "tracking ID" for a question + // multiple versions can share the same question ID so their stats are aggregated + @Column(name = "question_id") + private Integer questionId; + + // the same question can have multiple versions + @Column + private Integer version = 1; + + @Column(name = "create_date") + private Date createDate = new Date(); + + // text of the question + @Column + private String name; + + @Column + private String description; + + @Column(name = "max_mark") + private Integer maxMark; + + @Column + private String feedback; + + @Column(name = "penalty_factor") + private float penaltyFactor; + + @Column(name = "answer_required") + private boolean answerRequired; + + @Column(name = "multiple_answers_allowed") + private boolean multipleAnswersAllowed; + + @Column(name = "incorrect_answer_nullifies_mark") + private boolean incorrectAnswerNullifiesMark; + + @Column(name = "feedback_on_correct") + private String feedbackOnCorrect; + + @Column(name = "feedback_on_partially_correct") + private String feedbackOnPartiallyCorrect; + + @Column(name = "feedback_on_incorrect") + private String feedbackOnIncorrect; + + // only one of shuffle and prefixAnswersWithLetters should be on. Both may be off + @Column + private boolean shuffle; + + @Column(name = "prefix_answers_with_letters") + private boolean prefixAnswersWithLetters; + + @Column(name = "case_sensitive") + private boolean caseSensitive; + + @Column(name = "correct_answer") + private boolean correctAnswer; + + @Column(name = "allow_rich_editor") + private boolean allowRichEditor; + + // only for essay type of question + @Column(name = "max_words_limit") + private int maxWordsLimit; + + // only for essay type of question + @Column(name = "min_words_limit") + private int minWordsLimit; + + // only for hedging maxMark type of question + @Column(name = "hedging_justification_enabled") + private boolean hedgingJustificationEnabled; + + @OneToMany(mappedBy = "qbQuestion", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) + @Fetch(FetchMode.SUBSELECT) + private List qbOptions = new ArrayList<>(); + + @OneToMany(mappedBy = "qbQuestion", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) + @Fetch(FetchMode.SUBSELECT) + private List units = new ArrayList<>(); + + // read-only collection of learning outcomes + @ManyToMany(fetch = FetchType.LAZY) + @Fetch(FetchMode.SUBSELECT) + @JoinTable(name = "lams_outcome_mapping", joinColumns = @JoinColumn(name = "qb_question_id", referencedColumnName = "question_id", updatable = false, insertable = false), inverseJoinColumns = @JoinColumn(name = "outcome_id")) + private List outcomes = new ArrayList<>(); + + // compares if current question data and the other one (probably modified with new data) are the same + // it detects if question is the same or should another question/version be created + public boolean isModified(QbQuestion modifiedQuestion) { + return !equals(modifiedQuestion); + } + + // checks if important parts of another question are the same as current question's + @Override + public boolean equals(Object o) { + QbQuestion other = (QbQuestion) o; + // options are also checked if they are equal + return new EqualsBuilder().append(name, other.name).append(description, other.description) + .append(feedback, other.feedback).append(maxMark, other.maxMark) + .append(qbOptions.toArray(), other.getQbOptions().toArray()) + .append(units.toArray(), other.getUnits().toArray()).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(name).append(description).append(feedback).append(maxMark).toHashCode(); + } + + @Override + public QbQuestion clone() { + QbQuestion clone = null; + try { + clone = (QbQuestion) super.clone(); + } catch (CloneNotSupportedException e) { + // it should never happen + e.printStackTrace(); + } + // make a deep copy of options + List optionsClone = new ArrayList<>(qbOptions.size()); + clone.setQbOptions(optionsClone); + for (QbOption option : qbOptions) { + QbOption optionClone = option.clone(); + optionClone.setQbQuestion(clone); + optionsClone.add(optionClone); + } + // make a deep copy of units + List unitsClone = new ArrayList<>(units.size()); + clone.setUnits(unitsClone); + for (QbQuestionUnit unit : units) { + QbQuestionUnit unitClone = unit.clone(); + unitClone.setQbQuestion(clone); + unitsClone.add(unitClone); + } + return clone; + } + + public void clearID() { + this.uid = null; + for (QbOption option : qbOptions) { + option.uid = null; + } + for (QbQuestionUnit unit : units) { + unit.uid = null; + } + } + + public Long getUid() { + return uid; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Integer getQuestionId() { + return questionId; + } + + public void setQuestionId(Integer questionId) { + this.questionId = questionId; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = StringUtils.isBlank(name) ? null : name.trim(); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getMaxMark() { + return maxMark; + } + + public void setMaxMark(Integer maxMark) { + this.maxMark = maxMark; + } + + public String getFeedback() { + return feedback; + } + + public void setFeedback(String feedback) { + this.feedback = StringUtils.isBlank(feedback) ? null : feedback.trim(); + } + + public float getPenaltyFactor() { + return penaltyFactor; + } + + public void setPenaltyFactor(float penaltyFactor) { + this.penaltyFactor = penaltyFactor; + } + + public boolean isAnswerRequired() { + return answerRequired; + } + + public void setAnswerRequired(boolean answerRequired) { + this.answerRequired = answerRequired; + } + + public boolean isMultipleAnswersAllowed() { + return multipleAnswersAllowed; + } + + public void setMultipleAnswersAllowed(boolean multipleAnswersAllowed) { + this.multipleAnswersAllowed = multipleAnswersAllowed; + } + + public boolean isIncorrectAnswerNullifiesMark() { + return incorrectAnswerNullifiesMark; + } + + public void setIncorrectAnswerNullifiesMark(boolean incorrectAnswerNullifiesMark) { + this.incorrectAnswerNullifiesMark = incorrectAnswerNullifiesMark; + } + + public String getFeedbackOnCorrect() { + return feedbackOnCorrect; + } + + public void setFeedbackOnCorrect(String feedbackOnCorrect) { + this.feedbackOnCorrect = feedbackOnCorrect; + } + + public String getFeedbackOnPartiallyCorrect() { + return feedbackOnPartiallyCorrect; + } + + public void setFeedbackOnPartiallyCorrect(String feedbackOnPartiallyCorrect) { + this.feedbackOnPartiallyCorrect = feedbackOnPartiallyCorrect; + } + + public String getFeedbackOnIncorrect() { + return feedbackOnIncorrect; + } + + public void setFeedbackOnIncorrect(String feedbackOnIncorrect) { + this.feedbackOnIncorrect = feedbackOnIncorrect; + } + + public boolean isShuffle() { + return shuffle; + } + + public void setShuffle(boolean shuffle) { + this.shuffle = shuffle; + } + + public boolean isCaseSensitive() { + return caseSensitive; + } + + public void setCaseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + public boolean getCorrectAnswer() { + return correctAnswer; + } + + public void setCorrectAnswer(boolean correctAnswer) { + this.correctAnswer = correctAnswer; + } + + public boolean isAllowRichEditor() { + return allowRichEditor; + } + + public void setAllowRichEditor(boolean allowRichEditor) { + this.allowRichEditor = allowRichEditor; + } + + /** + * maxWordsLimit set in author. Used only for essay type of questions + */ + public int getMaxWordsLimit() { + return maxWordsLimit; + } + + /** + * @param maxWordsLimit + * set in author. Used only for essay type of questions + */ + public void setMaxWordsLimit(int maxWordsLimit) { + this.maxWordsLimit = maxWordsLimit; + } + + /** + * minWordsLimit set in author. Used only for essay type of questions + */ + public int getMinWordsLimit() { + return minWordsLimit; + } + + /** + * @param minWordsLimit + * set in author. Used only for essay type of questions + */ + public void setMinWordsLimit(int minWordsLimit) { + this.minWordsLimit = minWordsLimit; + } + + public boolean isHedgingJustificationEnabled() { + return hedgingJustificationEnabled; + } + + public void setHedgingJustificationEnabled(boolean hedgingJustificationEnabled) { + this.hedgingJustificationEnabled = hedgingJustificationEnabled; + } + + public boolean isPrefixAnswersWithLetters() { + return prefixAnswersWithLetters; + } + + public void setPrefixAnswersWithLetters(boolean prefixAnswersWithLetters) { + this.prefixAnswersWithLetters = prefixAnswersWithLetters; + } + + public List getQbOptions() { + return qbOptions; + } + + public void setQbOptions(List options) { + this.qbOptions = options; + } + + public List getUnits() { + return units; + } + + public void setUnits(List units) { + this.units = units; + } } \ No newline at end of file Index: lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java =================================================================== diff -u -r66cee69cb7746aa2567532847ae9743a82f8f24c -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java (.../GradebookService.java) (revision 66cee69cb7746aa2567532847ae9743a82f8f24c) +++ lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java (.../GradebookService.java) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -203,7 +203,7 @@ } List outcomeMappings = outcomeService.getOutcomeMappings(null, activity.getToolContentId(), - null); + null, null); if (!outcomeMappings.isEmpty()) { ArrayNode outcomeMappingsJSON = JsonNodeFactory.instance.arrayNode(); for (OutcomeMapping outcomeMapping : outcomeMappings) { Index: lams_gradebook/web/WEB-INF/tags/OutcomeAuthor.tag =================================================================== diff -u -rba2b4e0d8f708d647ab75bf4db71559dfb249e07 -r67db3e1b2ada0d59ff98c807a54282783797c5e6 --- lams_gradebook/web/WEB-INF/tags/OutcomeAuthor.tag (.../OutcomeAuthor.tag) (revision ba2b4e0d8f708d647ab75bf4db71559dfb249e07) +++ lams_gradebook/web/WEB-INF/tags/OutcomeAuthor.tag (.../OutcomeAuthor.tag) (revision 67db3e1b2ada0d59ff98c807a54282783797c5e6) @@ -16,6 +16,7 @@ <%@ attribute name="lessonId" required="false" rtexprvalue="true" %> <%@ attribute name="toolContentId" required="false" rtexprvalue="true" %> <%@ attribute name="itemId" required="false" rtexprvalue="true" %> +<%@ attribute name="qbQuestionId" required="false" rtexprvalue="true" %> <%-- Support for multiple tags on one page --%> @@ -43,7 +44,8 @@ var outcomeData${outcomeTagId} = { lessonId : '${lessonId}', toolContentId : '${toolContentId}', - itemId : '${itemId}' + itemId : '${itemId}', + qbQuestionId : '${qbQuestionId}' }, // keep mapped outcome IDs for search result filtering outcomeMappingIds${outcomeTagId} = [], @@ -56,7 +58,7 @@ $(document).ready(function(){ $('#outcomeSearchInput${outcomeTagId}').autocomplete({ - 'source' : "outcome/outcomeSearch.do?organisationIds=${organisations}", + 'source' : "outcome/outcomeSearch.do", 'delay' : 700, 'minLength' : 2, 'response' : function(event, ui) { @@ -91,6 +93,7 @@ var input = $(this); $.ajax({ 'url' : 'outcome/outcomeMap.do', + 'dataType' : 'text', 'data': $.extend({ 'outcomeId' : ui.item.value, // if value is null, then it is a new outcome to create; remove ' [create new]' part before sending @@ -116,9 +119,11 @@ * Loads existing mappings */ function refreshOutcomeMappings(outcomeTagId) { + var outcomeData = outcomeData${outcomeTagId}; + $.ajax({ 'url' : 'outcome/outcomeGetMappings.do', - 'data' : outcomeData${outcomeTagId}, + 'data' : outcomeData, 'cache' : false, 'dataType' : 'json', 'success' : function(outcomeMappings) { @@ -134,13 +139,17 @@ // cache already mapped outcomes outcomeMappingIds${outcomeTagId}.push(this.outcomeId); // add a label with outcome information - var outcomeButton = $('