Index: lams_central/web/qb/collection.jsp =================================================================== diff -u -rf9c66e78afa51f175afcaf22ee81f9b3460afea8 -r95b32ee3c7e813d17c11fe5391bbd5c5a951a64d --- lams_central/web/qb/collection.jsp (.../collection.jsp) (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) +++ lams_central/web/qb/collection.jsp (.../collection.jsp) (revision 95b32ee3c7e813d17c11fe5391bbd5c5a951a64d) @@ -2,48 +2,48 @@ <%@ taglib uri="tags-lams" prefix="lams"%> <%@ taglib uri="tags-fmt" prefix="fmt"%> <%@ taglib uri="tags-core" prefix="c"%> + - Question collections + Collection @@ -54,127 +54,47 @@ - - - - - -
- -
- - - - - + +
+ +
-
- - -
+
+ + + <%-- Build collection title with its name, question count, optional "private" flag and edit button --%> + + + (${questionCount} questions) + + Private + + + <%-- jqGrid placeholder with some useful attributes --%> + +
+
+ +
+ There are no questions in this collection +
+
+
- - <%-- jqGrid placeholder with some useful attributes --%> -
- -
- -
+
+
+
+
+ Create question +
+
+ +
+
+ + + +
+
+ + <%-- Do not display links for collection manipulation for public and private collections --%> + +
- <%-- Do not allow removing questions from public collection --%> - - + +
-
- Transfer questions to -
-
- -
-
- - -
-
- - <%-- Do not display links for collection manipulation for public and private collections --%> - -
-
- + +
+ Share collection with
-
- Share collection with -
- +
- +
+
+
+ + +
+
+
+ Shared with organisations +
+
- - +
-
-
- Shared with organisations +
+
+
-
-
- -
-
-
- -
-
- -
+
+
- - +
+
-
+
- -
- -
- Index: lams_central/web/qb/stats.jsp =================================================================== diff -u -rf9c66e78afa51f175afcaf22ee81f9b3460afea8 -r95b32ee3c7e813d17c11fe5391bbd5c5a951a64d --- lams_central/web/qb/stats.jsp (.../stats.jsp) (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) +++ lams_central/web/qb/stats.jsp (.../stats.jsp) (revision 95b32ee3c7e813d17c11fe5391bbd5c5a951a64d) @@ -2,6 +2,7 @@ <%@ taglib uri="tags-lams" prefix="lams"%> <%@ taglib uri="tags-fmt" prefix="fmt"%> <%@ taglib uri="tags-core" prefix="c"%> + @@ -31,6 +32,23 @@ #usage a { text-decoration: underline; } + + .row { + margin-top: 10px; + } + + .middle-cell { + padding-top: 6px; + display: inline-block; + } + + .middle-buttons { + text-align: center; + } + + .header-column { + font-weight: bold; + } @@ -40,12 +58,62 @@ +
Question - -
-
@@ -117,157 +175,241 @@
- -

Options

- - - - - - - - + + +

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]}% - + # + + Title + + Correct? + + Average selection
(as first choice) +
-
+ + + + ${status.index + 1} + + + + + + + + + + + <%--(${empty stats.answersRaw[option.uid] ? 0 : stats.answersRaw[option.uid]})--%> + ${stats.answersPercent[option.uid]}% + + + + +
+
- Average selection chart + Management
-
+
+
+
+
+ +
+
+ + +
+
+ Transfer questions to +
+
+ +
+
+ + +
+
+
+ +
+
+
+ Existing collections +
+
+
+ + +
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+ Average selection chart +
+
+
+
+
+
Burning questions
- - - - - - - - - - - -
- Question - - Likes -
- - - -
+ + + This question does not have any burning questions + + + + + + + + + + + + + +
+ Question + + Likes +
+ + + +
+
+
- Usage + 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 -
- - - + + + + + + + + + + + + + + + + - - + + + + - - - + + + + - - - - - - - - - - - - - - - - - - -
- - - - + + Organisation + + Lesson + + Activity + + Tool type + + Test participant count + + Difficulty index + + Discrimination index + + Point biserial +
+ + + + + + + + + + + + + + + + + + + --- - - - - ---
+ + + + +
@@ -276,39 +418,46 @@ Previous versions
- - - - - - - - - - - - - -
- # - - Created date - - Created ago -
- v${version.version} - - - - -
+ + + 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/qb/service/IQbService.java =================================================================== diff -u -rf9c66e78afa51f175afcaf22ee81f9b3460afea8 -r95b32ee3c7e813d17c11fe5391bbd5c5a951a64d --- lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision 95b32ee3c7e813d17c11fe5391bbd5c5a951a64d) @@ -76,13 +76,17 @@ List getUserCollections(int userId); + List getUserOwnCollections(int userId); + List getCollectionQuestions(long collectionUid); List getCollectionQuestions(long collectionUid, Integer offset, Integer limit, String orderBy, String orderDirection, String search); - int countCollectionQuestions(long collectionUid, String search); + int getCountCollectionQuestions(long collectionUid, String search); + List getQuestionCollections(long qbQuestionUid); + QbCollection addCollection(int userId, String name); void removeCollection(long collectionUid); @@ -98,9 +102,19 @@ void addQuestionToCollection(long sourceCollectionUid, long targetCollectionUid, Collection excludedQbQuestionUids, boolean copy); - void removeQuestionFromCollection(long collectionUid, long qbQuestionUid); + boolean removeQuestionFromCollection(long collectionUid, long qbQuestionUid); - void removeQuestionFromCollection(long collectionUid, Collection excludedQbQuestionUids); + Collection removeQuestionFromCollection(long collectionUid, Collection excludedQbQuestionUids); + + boolean removeQuestion(long qbQuestionUid); + + boolean removeQuestionPossible(long qbQuestionUid); + + QbCollection getCollection(long collectionUid); + + int getCountQuestionActivities(long qbQuestionUid); + + void changeCollectionName(long collectionUid, String name); void releaseFromCache(Object object); } Index: lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java =================================================================== diff -u -rf9c66e78afa51f175afcaf22ee81f9b3460afea8 -r95b32ee3c7e813d17c11fe5391bbd5c5a951a64d --- lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java (.../QbService.java) (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java (.../QbService.java) (revision 95b32ee3c7e813d17c11fe5391bbd5c5a951a64d) @@ -28,6 +28,7 @@ import org.lamsfoundation.lams.qb.model.QbOption; import org.lamsfoundation.lams.qb.model.QbQuestion; import org.lamsfoundation.lams.qb.model.QbQuestionUnit; +import org.lamsfoundation.lams.qb.model.QbToolQuestion; import org.lamsfoundation.lams.tool.service.ILamsCoreToolService; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.Role; @@ -60,18 +61,18 @@ public List getQbQuestionsByQuestionId(Integer questionId) { return qbDAO.getQbQuestionsByQuestionId(questionId); } - + @Override public QbOption getQbOptionByUid(Long optionUid) { - QbOption option = (QbOption) qbDAO.find(QbOption.class, optionUid); + QbOption option = qbDAO.find(QbOption.class, optionUid); qbDAO.releaseFromCache(option); qbDAO.releaseFromCache(option.getQbQuestion()); return option; } - + @Override public QbQuestionUnit getQbQuestionUnitByUid(Long unitUid) { - QbQuestionUnit unit = (QbQuestionUnit) qbDAO.find(QbQuestionUnit.class, unitUid); + QbQuestionUnit unit = qbDAO.find(QbQuestionUnit.class, unitUid); qbDAO.releaseFromCache(unit); qbDAO.releaseFromCache(unit.getQbQuestion()); return unit; @@ -101,7 +102,7 @@ @Override public QbStatsDTO getQbQuestionStats(long qbQuestionUid) { QbStatsDTO stats = new QbStatsDTO(); - QbQuestion qbQuestion = (QbQuestion) qbDAO.find(QbQuestion.class, qbQuestionUid); + QbQuestion qbQuestion = qbDAO.find(QbQuestion.class, qbQuestionUid); List qbOptions = qbQuestion.getQbOptions(); stats.setQuestion(qbQuestion); Map burningQuestions = qbDAO.getBurningQuestions(qbQuestionUid); @@ -152,17 +153,15 @@ return stats; } - @SuppressWarnings("unchecked") @Override public QbStatsActivityDTO getActivityStatsByContentId(Long toolContentId, Long qbQuestionUid) { - Activity activity = ((List) qbDAO.findByProperty(ToolActivity.class, "toolContentId", toolContentId)) - .get(0); + Activity activity = qbDAO.findByProperty(ToolActivity.class, "toolContentId", toolContentId).get(0); return getActivityStats(activity.getActivityId(), qbQuestionUid); } @Override public QbStatsActivityDTO getActivityStats(Long activityId, Long qbQuestionUid) { - QbQuestion qbQuestion = (QbQuestion) qbDAO.find(QbQuestion.class, qbQuestionUid); + QbQuestion qbQuestion = qbDAO.find(QbQuestion.class, qbQuestionUid); Set correctOptionUids = new HashSet<>(); for (QbOption option : qbQuestion.getQbOptions()) { if (option.isCorrect()) { @@ -175,7 +174,7 @@ @Override public QbStatsActivityDTO getActivityStats(Long activityId, Long qbQuestionUid, Collection correctOptionUids) { - ToolActivity activity = (ToolActivity) qbDAO.find(ToolActivity.class, activityId); + ToolActivity activity = qbDAO.find(ToolActivity.class, activityId); LearningDesign learningDesign = activity.getLearningDesign(); Lesson lesson = learningDesign.getLessons().iterator().next(); Long lessonId = lesson.getLessonId(); @@ -259,19 +258,18 @@ return activityDTO; } - - @Override + + @Override public QbCollection getCollectionByUid(Long collectionUid) { - return (QbCollection) qbDAO.find(QbCollection.class, collectionUid); + return qbDAO.find(QbCollection.class, collectionUid); } @Override public QbCollection getPublicCollection() { - return (QbCollection) qbDAO.find(QbCollection.class, 1L); + return qbDAO.find(QbCollection.class, 1L); } @Override - @SuppressWarnings("unchecked") public QbCollection getUserPrivateCollection(int userId) { Map map = new HashMap<>(); map.put("userId", userId); @@ -280,19 +278,18 @@ if (!result.isEmpty()) { return result.get(0); } - + // is an user does not have a private collection yet, create it QbCollection collection = new QbCollection(); - collection.setName("Private questions"); + collection.setName("My questions"); collection.setUserId(userId); collection.setPersonal(true); qbDAO.insert(collection); return collection; } @Override - @SuppressWarnings("unchecked") - public List getUserCollections(int userId) { + public List getUserOwnCollections(int userId) { Map map = new HashMap<>(); map.put("userId", userId); map.put("personal", false); @@ -311,11 +308,16 @@ } @Override - public int countCollectionQuestions(long collectionUid, String search) { - return qbDAO.countCollectionQuestions(collectionUid, search); + public int getCountCollectionQuestions(long collectionUid, String search) { + return qbDAO.getCountCollectionQuestions(collectionUid, search); } @Override + public List getQuestionCollections(long qbQuestionUid) { + return qbDAO.getQuestionCollections(qbQuestionUid); + } + + @Override public QbCollection addCollection(int userId, String name) { QbCollection collection = new QbCollection(); collection.setName(name); @@ -326,28 +328,49 @@ @Override public void removeCollection(long collectionUid) { - QbCollection collection = (QbCollection) qbDAO.find(QbCollection.class, collectionUid); + if (getCountCollectionQuestions(collectionUid, null) > 0) { + throw new InvalidParameterException("Can not remove a collection with questions"); + } + QbCollection collection = qbDAO.find(QbCollection.class, collectionUid); if (collection.getUserId() == null || collection.isPersonal()) { throw new InvalidParameterException("Attempt to remove a private or the public question bank collection"); } qbDAO.delete(collection); } @Override + public boolean removeQuestion(long qbQuestionUid) { + boolean removeQuestionPossible = removeQuestionPossible(qbQuestionUid); + if (!removeQuestionPossible) { + // if the question is used in a Learning Design, do not allow to remove it + return false; + } + qbDAO.deleteById(QbQuestion.class, qbQuestionUid); + return true; + } + + @Override + public boolean removeQuestionPossible(long qbQuestionUid) { + Map properties = new HashMap<>(); + properties.put("qbQuestion.uid", qbQuestionUid); + return qbDAO.countByProperties(QbToolQuestion.class, properties) == 0; + } + + @Override public Organisation shareCollection(long collectionUid, int organisationId) { - QbCollection collection = (QbCollection) qbDAO.find(QbCollection.class, collectionUid); + QbCollection collection = qbDAO.find(QbCollection.class, collectionUid); if (collection.getUserId() == null || collection.isPersonal()) { throw new InvalidParameterException("Attempt to share a private or the public question bank collection"); } - Organisation organisation = (Organisation) qbDAO.find(Organisation.class, organisationId); + Organisation organisation = qbDAO.find(Organisation.class, organisationId); collection.getOrganisations().add(organisation); qbDAO.update(collection); return organisation; } @Override public void unshareCollection(long collectionUid, int organisationId) { - QbCollection collection = (QbCollection) qbDAO.find(QbCollection.class, collectionUid); + QbCollection collection = qbDAO.find(QbCollection.class, collectionUid); if (collection.getUserId() == null || collection.isPersonal()) { throw new InvalidParameterException("Attempt to unshare a private or the public question bank collection"); } @@ -388,21 +411,33 @@ } @Override - public void removeQuestionFromCollection(long collectionUid, long qbQuestionUid) { + public boolean removeQuestionFromCollection(long collectionUid, long qbQuestionUid) { + Collection collections = getQuestionCollections(qbQuestionUid); + int size = collections.size(); + if (size <= 1) { + // if the question is in its last collection, try to remove it permanently + return removeQuestion(qbQuestionUid); + } qbDAO.removeCollectionQuestion(collectionUid, qbQuestionUid); + return true; } @Override - public void removeQuestionFromCollection(long collectionUid, Collection excludedQbQuestionUids) { + public Collection removeQuestionFromCollection(long collectionUid, Collection excludedQbQuestionUids) { Collection includedUids = qbDAO.getCollectionQuestionUidsExcluded(collectionUid, excludedQbQuestionUids); + Collection retainedQuestionUids = new HashSet<>(); for (Long uid : includedUids) { - qbDAO.removeCollectionQuestion(collectionUid, uid); + boolean deleted = removeQuestionFromCollection(collectionUid, uid); + if (!deleted) { + retainedQuestionUids.add(uid); + } } + return retainedQuestionUids; } @Override public List getShareableWithOrganisations(long collectionUid, int userId) { - QbCollection collection = (QbCollection) qbDAO.find(QbCollection.class, collectionUid); + QbCollection collection = qbDAO.find(QbCollection.class, collectionUid); if (collection.getUserId() == null || collection.isPersonal()) { return null; } @@ -418,18 +453,49 @@ } } if (organisationId != null && roleEntry.getValue().contains(Role.ROLE_AUTHOR)) { - Organisation organisation = (Organisation) qbDAO.find(Organisation.class, organisationId); + Organisation organisation = qbDAO.find(Organisation.class, organisationId); result.add(organisation); } } return result; } - + + @Override public void releaseFromCache(Object object) { - qbDAO.releaseFromCache(object); + qbDAO.releaseFromCache(object); } + @Override + public List getUserCollections(int userId) { + List collections = getUserOwnCollections(userId); + + QbCollection privateCollection = getUserPrivateCollection(userId); + collections.add(privateCollection); + + QbCollection publicCollection = getPublicCollection(); + collections.add(publicCollection); + + return collections; + } + + @Override + public QbCollection getCollection(long collectionUid) { + return qbDAO.find(QbCollection.class, collectionUid); + } + + @Override + public int getCountQuestionActivities(long qbQuestionUid) { + return qbDAO.getCountQuestionActivities(qbQuestionUid); + } + + @Override + public void changeCollectionName(long collectionUid, String name) { + QbCollection collection = getCollection(collectionUid); + collection.setName(name); + qbDAO.update(collection); + } + public void setQbDAO(IQbDAO qbDAO) { this.qbDAO = qbDAO; }